diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-11-28 08:45:31 +0100 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-11-28 08:45:31 +0100 |
commit | 1336a7c91e596c423a49d1194ea42d98bca0d958 (patch) | |
tree | bc20362a07d6e4203533576e62aaa8729e44cb3b | |
parent | 766f2d101fd4d91ab470b79fdf93cbc2fc72c515 (diff) | |
download | golang-1336a7c91e596c423a49d1194ea42d98bca0d958.tar.gz |
Imported Upstream version 60.3upstream/60.3
-rw-r--r-- | doc/devel/release.html | 5 | ||||
-rw-r--r-- | src/pkg/Makefile | 1 | ||||
-rw-r--r-- | src/pkg/exp/datafmt/Makefile | 12 | ||||
-rw-r--r-- | src/pkg/exp/datafmt/datafmt.go | 710 | ||||
-rw-r--r-- | src/pkg/exp/datafmt/datafmt_test.go | 330 | ||||
-rw-r--r-- | src/pkg/exp/datafmt/parser.go | 368 | ||||
-rw-r--r-- | src/pkg/fmt/fmt_test.go | 16 | ||||
-rw-r--r-- | src/pkg/fmt/print.go | 247 | ||||
-rw-r--r-- | src/pkg/reflect/all_test.go | 115 | ||||
-rw-r--r-- | src/pkg/reflect/deepequal.go | 2 | ||||
-rw-r--r-- | src/pkg/reflect/value.go | 31 | ||||
-rw-r--r-- | test/interface/fake.go | 40 |
12 files changed, 291 insertions, 1586 deletions
diff --git a/doc/devel/release.html b/doc/devel/release.html index d6de1d71c..984e67493 100644 --- a/doc/devel/release.html +++ b/doc/devel/release.html @@ -110,6 +110,11 @@ r60.2 a memory leak involving maps. </p> +<p> +r60.3 fixes a +<a href="http://code.google.com/p/go/source/detail?r=01fa62f5e4e5">reflect bug</a>. +</p> + <h2 id="r59">r59 (released 2011/08/01)</h2> <p> diff --git a/src/pkg/Makefile b/src/pkg/Makefile index 991e3cbde..44d7f4367 100644 --- a/src/pkg/Makefile +++ b/src/pkg/Makefile @@ -77,7 +77,6 @@ DIRS=\ encoding/hex\ encoding/pem\ exec\ - exp/datafmt\ exp/gui\ exp/gui/x11\ exp/norm\ diff --git a/src/pkg/exp/datafmt/Makefile b/src/pkg/exp/datafmt/Makefile deleted file mode 100644 index aa9453897..000000000 --- a/src/pkg/exp/datafmt/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=exp/datafmt -GOFILES=\ - datafmt.go\ - parser.go\ - -include ../../../Make.pkg diff --git a/src/pkg/exp/datafmt/datafmt.go b/src/pkg/exp/datafmt/datafmt.go deleted file mode 100644 index 6d7e76442..000000000 --- a/src/pkg/exp/datafmt/datafmt.go +++ /dev/null @@ -1,710 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* Package datafmt implements syntax-directed, type-driven formatting - of arbitrary data structures. Formatting a data structure consists of - two phases: first, a parser reads a format specification and builds a - "compiled" format. Then, the format can be applied repeatedly to - arbitrary values. Applying a format to a value evaluates to a []byte - containing the formatted value bytes, or nil. - - A format specification is a set of package declarations and format rules: - - Format = [ Entry { ";" Entry } [ ";" ] ] . - Entry = PackageDecl | FormatRule . - - (The syntax of a format specification is presented in the same EBNF - notation as used in the Go language specification. The syntax of white - space, comments, identifiers, and string literals is the same as in Go.) - - A package declaration binds a package name (such as 'ast') to a - package import path (such as '"go/ast"'). Each package used (in - a type name, see below) must be declared once before use. - - PackageDecl = PackageName ImportPath . - PackageName = identifier . - ImportPath = string . - - A format rule binds a rule name to a format expression. A rule name - may be a type name or one of the special names 'default' or '/'. - A type name may be the name of a predeclared type (for example, 'int', - 'float32', etc.), the package-qualified name of a user-defined type - (for example, 'ast.MapType'), or an identifier indicating the structure - of unnamed composite types ('array', 'chan', 'func', 'interface', 'map', - or 'ptr'). Each rule must have a unique name; rules can be declared in - any order. - - FormatRule = RuleName "=" Expression . - RuleName = TypeName | "default" | "/" . - TypeName = [ PackageName "." ] identifier . - - To format a value, the value's type name is used to select the format rule - (there is an override mechanism, see below). The format expression of the - selected rule specifies how the value is formatted. Each format expression, - when applied to a value, evaluates to a byte sequence or nil. - - In its most general form, a format expression is a list of alternatives, - each of which is a sequence of operands: - - Expression = [ Sequence ] { "|" [ Sequence ] } . - Sequence = Operand { Operand } . - - The formatted result produced by an expression is the result of the first - alternative sequence that evaluates to a non-nil result; if there is no - such alternative, the expression evaluates to nil. The result produced by - an operand sequence is the concatenation of the results of its operands. - If any operand in the sequence evaluates to nil, the entire sequence - evaluates to nil. - - There are five kinds of operands: - - Operand = Literal | Field | Group | Option | Repetition . - - Literals evaluate to themselves, with two substitutions. First, - %-formats expand in the manner of fmt.Printf, with the current value - passed as the parameter. Second, the current indentation (see below) - is inserted after every newline or form feed character. - - Literal = string . - - This table shows string literals applied to the value 42 and the - corresponding formatted result: - - "foo" foo - "%x" 2a - "x = %d" x = 42 - "%#x = %d" 0x2a = 42 - - A field operand is a field name optionally followed by an alternate - rule name. The field name may be an identifier or one of the special - names @ or *. - - Field = FieldName [ ":" RuleName ] . - FieldName = identifier | "@" | "*" . - - If the field name is an identifier, the current value must be a struct, - and there must be a field with that name in the struct. The same lookup - rules apply as in the Go language (for instance, the name of an anonymous - field is the unqualified type name). The field name denotes the field - value in the struct. If the field is not found, formatting is aborted - and an error message is returned. (TODO consider changing the semantics - such that if a field is not found, it evaluates to nil). - - The special name '@' denotes the current value. - - The meaning of the special name '*' depends on the type of the current - value: - - array, slice types array, slice element (inside {} only, see below) - interfaces value stored in interface - pointers value pointed to by pointer - - (Implementation restriction: channel, function and map types are not - supported due to missing reflection support). - - Fields are evaluated as follows: If the field value is nil, or an array - or slice element does not exist, the result is nil (see below for details - on array/slice elements). If the value is not nil the field value is - formatted (recursively) using the rule corresponding to its type name, - or the alternate rule name, if given. - - The following example shows a complete format specification for a - struct 'myPackage.Point'. Assume the package - - package myPackage // in directory myDir/myPackage - type Point struct { - name string; - x, y int; - } - - Applying the format specification - - myPackage "myDir/myPackage"; - int = "%d"; - hexInt = "0x%x"; - string = "---%s---"; - myPackage.Point = name "{" x ", " y:hexInt "}"; - - to the value myPackage.Point{"foo", 3, 15} results in - - ---foo---{3, 0xf} - - Finally, an operand may be a grouped, optional, or repeated expression. - A grouped expression ("group") groups a more complex expression (body) - so that it can be used in place of a single operand: - - Group = "(" [ Indentation ">>" ] Body ")" . - Indentation = Expression . - Body = Expression . - - A group body may be prefixed by an indentation expression followed by '>>'. - The indentation expression is applied to the current value like any other - expression and the result, if not nil, is appended to the current indentation - during the evaluation of the body (see also formatting state, below). - - An optional expression ("option") is enclosed in '[]' brackets. - - Option = "[" Body "]" . - - An option evaluates to its body, except that if the body evaluates to nil, - the option expression evaluates to an empty []byte. Thus an option's purpose - is to protect the expression containing the option from a nil operand. - - A repeated expression ("repetition") is enclosed in '{}' braces. - - Repetition = "{" Body [ "/" Separator ] "}" . - Separator = Expression . - - A repeated expression is evaluated as follows: The body is evaluated - repeatedly and its results are concatenated until the body evaluates - to nil. The result of the repetition is the (possibly empty) concatenation, - but it is never nil. An implicit index is supplied for the evaluation of - the body: that index is used to address elements of arrays or slices. If - the corresponding elements do not exist, the field denoting the element - evaluates to nil (which in turn may terminate the repetition). - - The body of a repetition may be followed by a '/' and a "separator" - expression. If the separator is present, it is invoked between repetitions - of the body. - - The following example shows a complete format specification for formatting - a slice of unnamed type. Applying the specification - - int = "%b"; - array = { * / ", " }; // array is the type name for an unnamed slice - - to the value '[]int{2, 3, 5, 7}' results in - - 10, 11, 101, 111 - - Default rule: If a format rule named 'default' is present, it is used for - formatting a value if no other rule was found. A common default rule is - - default = "%v" - - to provide default formatting for basic types without having to specify - a specific rule for each basic type. - - Global separator rule: If a format rule named '/' is present, it is - invoked with the current value between literals. If the separator - expression evaluates to nil, it is ignored. - - For instance, a global separator rule may be used to punctuate a sequence - of values with commas. The rules: - - default = "%v"; - / = ", "; - - will format an argument list by printing each one in its default format, - separated by a comma and a space. -*/ -package datafmt - -import ( - "bytes" - "fmt" - "go/token" - "io" - "os" - "reflect" - "runtime" -) - -// ---------------------------------------------------------------------------- -// Format representation - -// Custom formatters implement the Formatter function type. -// A formatter is invoked with the current formatting state, the -// value to format, and the rule name under which the formatter -// was installed (the same formatter function may be installed -// under different names). The formatter may access the current state -// to guide formatting and use State.Write to append to the state's -// output. -// -// A formatter must return a boolean value indicating if it evaluated -// to a non-nil value (true), or a nil value (false). -// -type Formatter func(state *State, value interface{}, ruleName string) bool - -// A FormatterMap is a set of custom formatters. -// It maps a rule name to a formatter function. -// -type FormatterMap map[string]Formatter - -// A parsed format expression is built from the following nodes. -// -type ( - expr interface{} - - alternatives []expr // x | y | z - - sequence []expr // x y z - - literal [][]byte // a list of string segments, possibly starting with '%' - - field struct { - fieldName string // including "@", "*" - ruleName string // "" if no rule name specified - } - - group struct { - indent, body expr // (indent >> body) - } - - option struct { - body expr // [body] - } - - repetition struct { - body, separator expr // {body / separator} - } - - custom struct { - ruleName string - fun Formatter - } -) - -// A Format is the result of parsing a format specification. -// The format may be applied repeatedly to format values. -// -type Format map[string]expr - -// ---------------------------------------------------------------------------- -// Formatting - -// An application-specific environment may be provided to Format.Apply; -// the environment is available inside custom formatters via State.Env(). -// Environments must implement copying; the Copy method must return an -// complete copy of the receiver. This is necessary so that the formatter -// can save and restore an environment (in case of an absent expression). -// -// If the Environment doesn't change during formatting (this is under -// control of the custom formatters), the Copy function can simply return -// the receiver, and thus can be very light-weight. -// -type Environment interface { - Copy() Environment -} - -// State represents the current formatting state. -// It is provided as argument to custom formatters. -// -type State struct { - fmt Format // format in use - env Environment // user-supplied environment - errors chan os.Error // not chan *Error (errors <- nil would be wrong!) - hasOutput bool // true after the first literal has been written - indent bytes.Buffer // current indentation - output bytes.Buffer // format output - linePos token.Position // position of line beginning (Column == 0) - default_ expr // possibly nil - separator expr // possibly nil -} - -func newState(fmt Format, env Environment, errors chan os.Error) *State { - s := new(State) - s.fmt = fmt - s.env = env - s.errors = errors - s.linePos = token.Position{Line: 1} - - // if we have a default rule, cache its expression for fast access - if x, found := fmt["default"]; found { - s.default_ = x - } - - // if we have a global separator rule, cache its expression for fast access - if x, found := fmt["/"]; found { - s.separator = x - } - - return s -} - -// Env returns the environment passed to Format.Apply. -func (s *State) Env() interface{} { return s.env } - -// LinePos returns the position of the current line beginning -// in the state's output buffer. Line numbers start at 1. -// -func (s *State) LinePos() token.Position { return s.linePos } - -// Pos returns the position of the next byte to be written to the -// output buffer. Line numbers start at 1. -// -func (s *State) Pos() token.Position { - offs := s.output.Len() - return token.Position{Line: s.linePos.Line, Column: offs - s.linePos.Offset, Offset: offs} -} - -// Write writes data to the output buffer, inserting the indentation -// string after each newline or form feed character. It cannot return an error. -// -func (s *State) Write(data []byte) (int, os.Error) { - n := 0 - i0 := 0 - for i, ch := range data { - if ch == '\n' || ch == '\f' { - // write text segment and indentation - n1, _ := s.output.Write(data[i0 : i+1]) - n2, _ := s.output.Write(s.indent.Bytes()) - n += n1 + n2 - i0 = i + 1 - s.linePos.Offset = s.output.Len() - s.linePos.Line++ - } - } - n3, _ := s.output.Write(data[i0:]) - return n + n3, nil -} - -type checkpoint struct { - env Environment - hasOutput bool - outputLen int - linePos token.Position -} - -func (s *State) save() checkpoint { - saved := checkpoint{nil, s.hasOutput, s.output.Len(), s.linePos} - if s.env != nil { - saved.env = s.env.Copy() - } - return saved -} - -func (s *State) restore(m checkpoint) { - s.env = m.env - s.output.Truncate(m.outputLen) -} - -func (s *State) error(msg string) { - s.errors <- os.NewError(msg) - runtime.Goexit() -} - -// TODO At the moment, unnamed types are simply mapped to the default -// names below. For instance, all unnamed arrays are mapped to -// 'array' which is not really sufficient. Eventually one may want -// to be able to specify rules for say an unnamed slice of T. -// - -func typename(typ reflect.Type) string { - switch typ.Kind() { - case reflect.Array: - return "array" - case reflect.Slice: - return "array" - case reflect.Chan: - return "chan" - case reflect.Func: - return "func" - case reflect.Interface: - return "interface" - case reflect.Map: - return "map" - case reflect.Ptr: - return "ptr" - } - return typ.String() -} - -func (s *State) getFormat(name string) expr { - if fexpr, found := s.fmt[name]; found { - return fexpr - } - - if s.default_ != nil { - return s.default_ - } - - s.error(fmt.Sprintf("no format rule for type: '%s'", name)) - return nil -} - -// eval applies a format expression fexpr to a value. If the expression -// evaluates internally to a non-nil []byte, that slice is appended to -// the state's output buffer and eval returns true. Otherwise, eval -// returns false and the state remains unchanged. -// -func (s *State) eval(fexpr expr, value reflect.Value, index int) bool { - // an empty format expression always evaluates - // to a non-nil (but empty) []byte - if fexpr == nil { - return true - } - - switch t := fexpr.(type) { - case alternatives: - // append the result of the first alternative that evaluates to - // a non-nil []byte to the state's output - mark := s.save() - for _, x := range t { - if s.eval(x, value, index) { - return true - } - s.restore(mark) - } - return false - - case sequence: - // append the result of all operands to the state's output - // unless a nil result is encountered - mark := s.save() - for _, x := range t { - if !s.eval(x, value, index) { - s.restore(mark) - return false - } - } - return true - - case literal: - // write separator, if any - if s.hasOutput { - // not the first literal - if s.separator != nil { - sep := s.separator // save current separator - s.separator = nil // and disable it (avoid recursion) - mark := s.save() - if !s.eval(sep, value, index) { - s.restore(mark) - } - s.separator = sep // enable it again - } - } - s.hasOutput = true - // write literal segments - for _, lit := range t { - if len(lit) > 1 && lit[0] == '%' { - // segment contains a %-format at the beginning - if lit[1] == '%' { - // "%%" is printed as a single "%" - s.Write(lit[1:]) - } else { - // use s instead of s.output to get indentation right - fmt.Fprintf(s, string(lit), value.Interface()) - } - } else { - // segment contains no %-formats - s.Write(lit) - } - } - return true // a literal never evaluates to nil - - case *field: - // determine field value - switch t.fieldName { - case "@": - // field value is current value - - case "*": - // indirection: operation is type-specific - switch v := value; v.Kind() { - case reflect.Array: - if v.Len() <= index { - return false - } - value = v.Index(index) - - case reflect.Slice: - if v.IsNil() || v.Len() <= index { - return false - } - value = v.Index(index) - - case reflect.Map: - s.error("reflection support for maps incomplete") - - case reflect.Ptr: - if v.IsNil() { - return false - } - value = v.Elem() - - case reflect.Interface: - if v.IsNil() { - return false - } - value = v.Elem() - - case reflect.Chan: - s.error("reflection support for chans incomplete") - - case reflect.Func: - s.error("reflection support for funcs incomplete") - - default: - s.error(fmt.Sprintf("error: * does not apply to `%s`", value.Type())) - } - - default: - // value is value of named field - var field reflect.Value - if sval := value; sval.Kind() == reflect.Struct { - field = sval.FieldByName(t.fieldName) - if !field.IsValid() { - // TODO consider just returning false in this case - s.error(fmt.Sprintf("error: no field `%s` in `%s`", t.fieldName, value.Type())) - } - } - value = field - } - - // determine rule - ruleName := t.ruleName - if ruleName == "" { - // no alternate rule name, value type determines rule - ruleName = typename(value.Type()) - } - fexpr = s.getFormat(ruleName) - - mark := s.save() - if !s.eval(fexpr, value, index) { - s.restore(mark) - return false - } - return true - - case *group: - // remember current indentation - indentLen := s.indent.Len() - - // update current indentation - mark := s.save() - s.eval(t.indent, value, index) - // if the indentation evaluates to nil, the state's output buffer - // didn't change - either way it's ok to append the difference to - // the current indentation - s.indent.Write(s.output.Bytes()[mark.outputLen:s.output.Len()]) - s.restore(mark) - - // format group body - mark = s.save() - b := true - if !s.eval(t.body, value, index) { - s.restore(mark) - b = false - } - - // reset indentation - s.indent.Truncate(indentLen) - return b - - case *option: - // evaluate the body and append the result to the state's output - // buffer unless the result is nil - mark := s.save() - if !s.eval(t.body, value, 0) { // TODO is 0 index correct? - s.restore(mark) - } - return true // an option never evaluates to nil - - case *repetition: - // evaluate the body and append the result to the state's output - // buffer until a result is nil - for i := 0; ; i++ { - mark := s.save() - // write separator, if any - if i > 0 && t.separator != nil { - // nil result from separator is ignored - mark := s.save() - if !s.eval(t.separator, value, i) { - s.restore(mark) - } - } - if !s.eval(t.body, value, i) { - s.restore(mark) - break - } - } - return true // a repetition never evaluates to nil - - case *custom: - // invoke the custom formatter to obtain the result - mark := s.save() - if !t.fun(s, value.Interface(), t.ruleName) { - s.restore(mark) - return false - } - return true - } - - panic("unreachable") - return false -} - -// Eval formats each argument according to the format -// f and returns the resulting []byte and os.Error. If -// an error occurred, the []byte contains the partially -// formatted result. An environment env may be passed -// in which is available in custom formatters through -// the state parameter. -// -func (f Format) Eval(env Environment, args ...interface{}) ([]byte, os.Error) { - if f == nil { - return nil, os.NewError("format is nil") - } - - errors := make(chan os.Error) - s := newState(f, env, errors) - - go func() { - for _, v := range args { - fld := reflect.ValueOf(v) - if !fld.IsValid() { - errors <- os.NewError("nil argument") - return - } - mark := s.save() - if !s.eval(s.getFormat(typename(fld.Type())), fld, 0) { // TODO is 0 index correct? - s.restore(mark) - } - } - errors <- nil // no errors - }() - - err := <-errors - return s.output.Bytes(), err -} - -// ---------------------------------------------------------------------------- -// Convenience functions - -// Fprint formats each argument according to the format f -// and writes to w. The result is the total number of bytes -// written and an os.Error, if any. -// -func (f Format) Fprint(w io.Writer, env Environment, args ...interface{}) (int, os.Error) { - data, err := f.Eval(env, args...) - if err != nil { - // TODO should we print partial result in case of error? - return 0, err - } - return w.Write(data) -} - -// Print formats each argument according to the format f -// and writes to standard output. The result is the total -// number of bytes written and an os.Error, if any. -// -func (f Format) Print(args ...interface{}) (int, os.Error) { - return f.Fprint(os.Stdout, nil, args...) -} - -// Sprint formats each argument according to the format f -// and returns the resulting string. If an error occurs -// during formatting, the result string contains the -// partially formatted result followed by an error message. -// -func (f Format) Sprint(args ...interface{}) string { - var buf bytes.Buffer - _, err := f.Fprint(&buf, nil, args...) - if err != nil { - var i interface{} = args - fmt.Fprintf(&buf, "--- Sprint(%s) failed: %v", fmt.Sprint(i), err) - } - return buf.String() -} diff --git a/src/pkg/exp/datafmt/datafmt_test.go b/src/pkg/exp/datafmt/datafmt_test.go deleted file mode 100644 index 87d071659..000000000 --- a/src/pkg/exp/datafmt/datafmt_test.go +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package datafmt - -import ( - "fmt" - "testing" - "go/token" -) - -var fset = token.NewFileSet() - -func parse(t *testing.T, form string, fmap FormatterMap) Format { - f, err := Parse(fset, "", []byte(form), fmap) - if err != nil { - t.Errorf("Parse(%s): %v", form, err) - return nil - } - return f -} - -func verify(t *testing.T, f Format, expected string, args ...interface{}) { - if f == nil { - return // allow other tests to run - } - result := f.Sprint(args...) - if result != expected { - t.Errorf( - "result : `%s`\nexpected: `%s`\n\n", - result, expected) - } -} - -func formatter(s *State, value interface{}, rule_name string) bool { - switch rule_name { - case "/": - fmt.Fprintf(s, "%d %d %d", s.Pos().Line, s.LinePos().Column, s.Pos().Column) - return true - case "blank": - s.Write([]byte{' '}) - return true - case "int": - if value.(int)&1 == 0 { - fmt.Fprint(s, "even ") - } else { - fmt.Fprint(s, "odd ") - } - return true - case "nil": - return false - case "testing.T": - s.Write([]byte("testing.T")) - return true - } - panic("unreachable") - return false -} - -func TestCustomFormatters(t *testing.T) { - fmap0 := FormatterMap{"/": formatter} - fmap1 := FormatterMap{"int": formatter, "blank": formatter, "nil": formatter} - fmap2 := FormatterMap{"testing.T": formatter} - - f := parse(t, `int=`, fmap0) - verify(t, f, ``, 1, 2, 3) - - f = parse(t, `int="#"`, nil) - verify(t, f, `###`, 1, 2, 3) - - f = parse(t, `int="#";string="%s"`, fmap0) - verify(t, f, "#1 0 1#1 0 7#1 0 13\n2 0 0foo2 0 8\n", 1, 2, 3, "\n", "foo", "\n") - - f = parse(t, ``, fmap1) - verify(t, f, `even odd even odd `, 0, 1, 2, 3) - - f = parse(t, `/ =@:blank; float64="#"`, fmap1) - verify(t, f, `# # #`, 0.0, 1.0, 2.0) - - f = parse(t, `float64=@:nil`, fmap1) - verify(t, f, ``, 0.0, 1.0, 2.0) - - f = parse(t, `testing "testing"; ptr=*`, fmap2) - verify(t, f, `testing.T`, t) - - // TODO needs more tests -} - -// ---------------------------------------------------------------------------- -// Formatting of basic and simple composite types - -func check(t *testing.T, form, expected string, args ...interface{}) { - f := parse(t, form, nil) - if f == nil { - return // allow other tests to run - } - result := f.Sprint(args...) - if result != expected { - t.Errorf( - "format : %s\nresult : `%s`\nexpected: `%s`\n\n", - form, result, expected) - } -} - -func TestBasicTypes(t *testing.T) { - check(t, ``, ``) - check(t, `bool=":%v"`, `:true:false`, true, false) - check(t, `int="%b %d %o 0x%x"`, `101010 42 52 0x2a`, 42) - - check(t, `int="%"`, `%`, 42) - check(t, `int="%%"`, `%`, 42) - check(t, `int="**%%**"`, `**%**`, 42) - check(t, `int="%%%%%%"`, `%%%`, 42) - check(t, `int="%%%d%%"`, `%42%`, 42) - - const i = -42 - const is = `-42` - check(t, `int ="%d"`, is, i) - check(t, `int8 ="%d"`, is, int8(i)) - check(t, `int16="%d"`, is, int16(i)) - check(t, `int32="%d"`, is, int32(i)) - check(t, `int64="%d"`, is, int64(i)) - - const u = 42 - const us = `42` - check(t, `uint ="%d"`, us, uint(u)) - check(t, `uint8 ="%d"`, us, uint8(u)) - check(t, `uint16="%d"`, us, uint16(u)) - check(t, `uint32="%d"`, us, uint32(u)) - check(t, `uint64="%d"`, us, uint64(u)) - - const f = 3.141592 - const fs = `3.141592` - check(t, `float64="%g"`, fs, f) - check(t, `float32="%g"`, fs, float32(f)) - check(t, `float64="%g"`, fs, float64(f)) -} - -func TestArrayTypes(t *testing.T) { - var a0 [10]int - check(t, `array="array";`, `array`, a0) - - a1 := [...]int{1, 2, 3} - check(t, `array="array";`, `array`, a1) - check(t, `array={*}; int="%d";`, `123`, a1) - check(t, `array={* / ", "}; int="%d";`, `1, 2, 3`, a1) - check(t, `array={* / *}; int="%d";`, `12233`, a1) - - a2 := []interface{}{42, "foo", 3.14} - check(t, `array={* / ", "}; interface=*; string="bar"; default="%v";`, `42, bar, 3.14`, a2) -} - -func TestChanTypes(t *testing.T) { - var c0 chan int - check(t, `chan="chan"`, `chan`, c0) - - c1 := make(chan int) - go func() { c1 <- 42 }() - check(t, `chan="chan"`, `chan`, c1) - // check(t, `chan=*`, `42`, c1); // reflection support for chans incomplete -} - -func TestFuncTypes(t *testing.T) { - var f0 func() int - check(t, `func="func"`, `func`, f0) - - f1 := func() int { return 42 } - check(t, `func="func"`, `func`, f1) - // check(t, `func=*`, `42`, f1); // reflection support for funcs incomplete -} - -func TestMapTypes(t *testing.T) { - var m0 map[string]int - check(t, `map="map"`, `map`, m0) - - m1 := map[string]int{} - check(t, `map="map"`, `map`, m1) - // check(t, `map=*`, ``, m1); // reflection support for maps incomplete -} - -func TestPointerTypes(t *testing.T) { - var p0 *int - check(t, `ptr="ptr"`, `ptr`, p0) - check(t, `ptr=*`, ``, p0) - check(t, `ptr=*|"nil"`, `nil`, p0) - - x := 99991 - p1 := &x - check(t, `ptr="ptr"`, `ptr`, p1) - check(t, `ptr=*; int="%d"`, `99991`, p1) -} - -func TestDefaultRule(t *testing.T) { - check(t, `default="%v"`, `42foo3.14`, 42, "foo", 3.14) - check(t, `default="%v"; int="%x"`, `abcdef`, 10, 11, 12, 13, 14, 15) - check(t, `default="%v"; int="%x"`, `ab**ef`, 10, 11, "**", 14, 15) - check(t, `default="%x"; int=@:default`, `abcdef`, 10, 11, 12, 13, 14, 15) -} - -func TestGlobalSeparatorRule(t *testing.T) { - check(t, `int="%d"; / ="-"`, `1-2-3-4`, 1, 2, 3, 4) - check(t, `int="%x%x"; / ="*"`, `aa*aa`, 10, 10) -} - -// ---------------------------------------------------------------------------- -// Formatting of a struct - -type T1 struct { - a int -} - -const F1 = `datafmt "datafmt";` + - `int = "%d";` + - `datafmt.T1 = "<" a ">";` - -func TestStruct1(t *testing.T) { check(t, F1, "<42>", T1{42}) } - -// ---------------------------------------------------------------------------- -// Formatting of a struct with an optional field (ptr) - -type T2 struct { - s string - p *T1 -} - -const F2a = F1 + - `string = "%s";` + - `ptr = *;` + - `datafmt.T2 = s ["-" p "-"];` - -const F2b = F1 + - `string = "%s";` + - `ptr = *;` + - `datafmt.T2 = s ("-" p "-" | "empty");` - -func TestStruct2(t *testing.T) { - check(t, F2a, "foo", T2{"foo", nil}) - check(t, F2a, "bar-<17>-", T2{"bar", &T1{17}}) - check(t, F2b, "fooempty", T2{"foo", nil}) -} - -// ---------------------------------------------------------------------------- -// Formatting of a struct with a repetitive field (slice) - -type T3 struct { - s string - a []int -} - -const F3a = `datafmt "datafmt";` + - `default = "%v";` + - `array = *;` + - `datafmt.T3 = s {" " a a / ","};` - -const F3b = `datafmt "datafmt";` + - `int = "%d";` + - `string = "%s";` + - `array = *;` + - `nil = ;` + - `empty = *:nil;` + - `datafmt.T3 = s [a:empty ": " {a / "-"}]` - -func TestStruct3(t *testing.T) { - check(t, F3a, "foo", T3{"foo", nil}) - check(t, F3a, "foo 00, 11, 22", T3{"foo", []int{0, 1, 2}}) - check(t, F3b, "bar", T3{"bar", nil}) - check(t, F3b, "bal: 2-3-5", T3{"bal", []int{2, 3, 5}}) -} - -// ---------------------------------------------------------------------------- -// Formatting of a struct with alternative field - -type T4 struct { - x *int - a []int -} - -const F4a = `datafmt "datafmt";` + - `int = "%d";` + - `ptr = *;` + - `array = *;` + - `nil = ;` + - `empty = *:nil;` + - `datafmt.T4 = "<" (x:empty x | "-") ">" ` - -const F4b = `datafmt "datafmt";` + - `int = "%d";` + - `ptr = *;` + - `array = *;` + - `nil = ;` + - `empty = *:nil;` + - `datafmt.T4 = "<" (a:empty {a / ", "} | "-") ">" ` - -func TestStruct4(t *testing.T) { - x := 7 - check(t, F4a, "<->", T4{nil, nil}) - check(t, F4a, "<7>", T4{&x, nil}) - check(t, F4b, "<->", T4{nil, nil}) - check(t, F4b, "<2, 3, 7>", T4{nil, []int{2, 3, 7}}) -} - -// ---------------------------------------------------------------------------- -// Formatting a struct (documentation example) - -type Point struct { - name string - x, y int -} - -const FPoint = `datafmt "datafmt";` + - `int = "%d";` + - `hexInt = "0x%x";` + - `string = "---%s---";` + - `datafmt.Point = name "{" x ", " y:hexInt "}";` - -func TestStructPoint(t *testing.T) { - p := Point{"foo", 3, 15} - check(t, FPoint, "---foo---{3, 0xf}", p) -} - -// ---------------------------------------------------------------------------- -// Formatting a slice (documentation example) - -const FSlice = `int = "%b";` + - `array = { * / ", " }` - -func TestSlice(t *testing.T) { check(t, FSlice, "10, 11, 101, 111", []int{2, 3, 5, 7}) } - -// TODO add more tests diff --git a/src/pkg/exp/datafmt/parser.go b/src/pkg/exp/datafmt/parser.go deleted file mode 100644 index a2ddd3897..000000000 --- a/src/pkg/exp/datafmt/parser.go +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package datafmt - -import ( - "go/scanner" - "go/token" - "os" - "strconv" - "strings" -) - -// ---------------------------------------------------------------------------- -// Parsing - -type parser struct { - scanner.ErrorVector - scanner scanner.Scanner - file *token.File - pos token.Pos // token position - tok token.Token // one token look-ahead - lit string // token literal - - packs map[string]string // PackageName -> ImportPath - rules map[string]expr // RuleName -> Expression -} - -func (p *parser) next() { - p.pos, p.tok, p.lit = p.scanner.Scan() - switch p.tok { - case token.CHAN, token.FUNC, token.INTERFACE, token.MAP, token.STRUCT: - // Go keywords for composite types are type names - // returned by reflect. Accept them as identifiers. - p.tok = token.IDENT // p.lit is already set correctly - } -} - -func (p *parser) init(fset *token.FileSet, filename string, src []byte) { - p.ErrorVector.Reset() - p.file = fset.AddFile(filename, fset.Base(), len(src)) - p.scanner.Init(p.file, src, p, scanner.AllowIllegalChars) // return '@' as token.ILLEGAL w/o error message - p.next() // initializes pos, tok, lit - p.packs = make(map[string]string) - p.rules = make(map[string]expr) -} - -func (p *parser) error(pos token.Pos, msg string) { - p.Error(p.file.Position(pos), msg) -} - -func (p *parser) errorExpected(pos token.Pos, msg string) { - msg = "expected " + msg - if pos == p.pos { - // the error happened at the current position; - // make the error message more specific - msg += ", found '" + p.tok.String() + "'" - if p.tok.IsLiteral() { - msg += " " + p.lit - } - } - p.error(pos, msg) -} - -func (p *parser) expect(tok token.Token) token.Pos { - pos := p.pos - if p.tok != tok { - p.errorExpected(pos, "'"+tok.String()+"'") - } - p.next() // make progress in any case - return pos -} - -func (p *parser) parseIdentifier() string { - name := p.lit - p.expect(token.IDENT) - return name -} - -func (p *parser) parseTypeName() (string, bool) { - pos := p.pos - name, isIdent := p.parseIdentifier(), true - if p.tok == token.PERIOD { - // got a package name, lookup package - if importPath, found := p.packs[name]; found { - name = importPath - } else { - p.error(pos, "package not declared: "+name) - } - p.next() - name, isIdent = name+"."+p.parseIdentifier(), false - } - return name, isIdent -} - -// Parses a rule name and returns it. If the rule name is -// a package-qualified type name, the package name is resolved. -// The 2nd result value is true iff the rule name consists of a -// single identifier only (and thus could be a package name). -// -func (p *parser) parseRuleName() (string, bool) { - name, isIdent := "", false - switch p.tok { - case token.IDENT: - name, isIdent = p.parseTypeName() - case token.DEFAULT: - name = "default" - p.next() - case token.QUO: - name = "/" - p.next() - default: - p.errorExpected(p.pos, "rule name") - p.next() // make progress in any case - } - return name, isIdent -} - -func (p *parser) parseString() string { - s := "" - if p.tok == token.STRING { - s, _ = strconv.Unquote(p.lit) - // Unquote may fail with an error, but only if the scanner found - // an illegal string in the first place. In this case the error - // has already been reported. - p.next() - return s - } else { - p.expect(token.STRING) - } - return s -} - -func (p *parser) parseLiteral() literal { - s := []byte(p.parseString()) - - // A string literal may contain %-format specifiers. To simplify - // and speed up printing of the literal, split it into segments - // that start with "%" possibly followed by a last segment that - // starts with some other character. - var list []interface{} - i0 := 0 - for i := 0; i < len(s); i++ { - if s[i] == '%' && i+1 < len(s) { - // the next segment starts with a % format - if i0 < i { - // the current segment is not empty, split it off - list = append(list, s[i0:i]) - i0 = i - } - i++ // skip %; let loop skip over char after % - } - } - // the final segment may start with any character - // (it is empty iff the string is empty) - list = append(list, s[i0:]) - - // convert list into a literal - lit := make(literal, len(list)) - for i := 0; i < len(list); i++ { - lit[i] = list[i].([]byte) - } - - return lit -} - -func (p *parser) parseField() expr { - var fname string - switch p.tok { - case token.ILLEGAL: - if p.lit != "@" { - return nil - } - fname = "@" - p.next() - case token.MUL: - fname = "*" - p.next() - case token.IDENT: - fname = p.parseIdentifier() - default: - return nil - } - - var ruleName string - if p.tok == token.COLON { - p.next() - ruleName, _ = p.parseRuleName() - } - - return &field{fname, ruleName} -} - -func (p *parser) parseOperand() (x expr) { - switch p.tok { - case token.STRING: - x = p.parseLiteral() - - case token.LPAREN: - p.next() - x = p.parseExpression() - if p.tok == token.SHR { - p.next() - x = &group{x, p.parseExpression()} - } - p.expect(token.RPAREN) - - case token.LBRACK: - p.next() - x = &option{p.parseExpression()} - p.expect(token.RBRACK) - - case token.LBRACE: - p.next() - x = p.parseExpression() - var div expr - if p.tok == token.QUO { - p.next() - div = p.parseExpression() - } - x = &repetition{x, div} - p.expect(token.RBRACE) - - default: - x = p.parseField() // may be nil - } - - return x -} - -func (p *parser) parseSequence() expr { - var list []interface{} - - for x := p.parseOperand(); x != nil; x = p.parseOperand() { - list = append(list, x) - } - - // no need for a sequence if list.Len() < 2 - switch len(list) { - case 0: - return nil - case 1: - return list[0].(expr) - } - - // convert list into a sequence - seq := make(sequence, len(list)) - for i := 0; i < len(list); i++ { - seq[i] = list[i].(expr) - } - return seq -} - -func (p *parser) parseExpression() expr { - var list []interface{} - - for { - x := p.parseSequence() - if x != nil { - list = append(list, x) - } - if p.tok != token.OR { - break - } - p.next() - } - - // no need for an alternatives if list.Len() < 2 - switch len(list) { - case 0: - return nil - case 1: - return list[0].(expr) - } - - // convert list into a alternatives - alt := make(alternatives, len(list)) - for i := 0; i < len(list); i++ { - alt[i] = list[i].(expr) - } - return alt -} - -func (p *parser) parseFormat() { - for p.tok != token.EOF { - pos := p.pos - - name, isIdent := p.parseRuleName() - switch p.tok { - case token.STRING: - // package declaration - importPath := p.parseString() - - // add package declaration - if !isIdent { - p.error(pos, "illegal package name: "+name) - } else if _, found := p.packs[name]; !found { - p.packs[name] = importPath - } else { - p.error(pos, "package already declared: "+name) - } - - case token.ASSIGN: - // format rule - p.next() - x := p.parseExpression() - - // add rule - if _, found := p.rules[name]; !found { - p.rules[name] = x - } else { - p.error(pos, "format rule already declared: "+name) - } - - default: - p.errorExpected(p.pos, "package declaration or format rule") - p.next() // make progress in any case - } - - if p.tok == token.SEMICOLON { - p.next() - } else { - break - } - } - p.expect(token.EOF) -} - -func remap(p *parser, name string) string { - i := strings.Index(name, ".") - if i >= 0 { - packageName, suffix := name[0:i], name[i:] - // lookup package - if importPath, found := p.packs[packageName]; found { - name = importPath + suffix - } else { - var invalidPos token.Position - p.Error(invalidPos, "package not declared: "+packageName) - } - } - return name -} - -// Parse parses a set of format productions from source src. Custom -// formatters may be provided via a map of formatter functions. If -// there are no errors, the result is a Format and the error is nil. -// Otherwise the format is nil and a non-empty ErrorList is returned. -// -func Parse(fset *token.FileSet, filename string, src []byte, fmap FormatterMap) (Format, os.Error) { - // parse source - var p parser - p.init(fset, filename, src) - p.parseFormat() - - // add custom formatters, if any - for name, form := range fmap { - name = remap(&p, name) - if _, found := p.rules[name]; !found { - p.rules[name] = &custom{name, form} - } else { - var invalidPos token.Position - p.Error(invalidPos, "formatter already declared: "+name) - } - } - - return p.rules, p.GetError(scanner.NoMultiples) -} diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go index 1142c9f8a..38218dfdc 100644 --- a/src/pkg/fmt/fmt_test.go +++ b/src/pkg/fmt/fmt_test.go @@ -61,7 +61,7 @@ type I int func (i I) String() string { return Sprintf("<%d>", int(i)) } type B struct { - i I + I I j int } @@ -83,8 +83,8 @@ func (g G) GoString() string { } type S struct { - f F // a struct field that Formats - g G // a struct field that GoStrings + F F // a struct field that Formats + G G // a struct field that GoStrings } // A type with a String method with pointer receiver for testing %p @@ -332,8 +332,8 @@ var fmttests = []struct { {"%+v", A{1, 2, "a", []int{1, 2}}, `{i:1 j:2 s:a x:[1 2]}`}, // +v on structs with Stringable items - {"%+v", B{1, 2}, `{i:<1> j:2}`}, - {"%+v", C{1, B{2, 3}}, `{i:1 B:{i:<2> j:3}}`}, + {"%+v", B{1, 2}, `{I:<1> j:2}`}, + {"%+v", C{1, B{2, 3}}, `{i:1 B:{I:<2> j:3}}`}, // q on Stringable items {"%s", I(23), `<23>`}, @@ -349,7 +349,7 @@ var fmttests = []struct { {"%#v", uint64(1<<64 - 1), "0xffffffffffffffff"}, {"%#v", 1000000000, "1000000000"}, {"%#v", map[string]int{"a": 1, "b": 2}, `map[string] int{"a":1, "b":2}`}, - {"%#v", map[string]B{"a": {1, 2}, "b": {3, 4}}, `map[string] fmt_test.B{"a":fmt_test.B{i:1, j:2}, "b":fmt_test.B{i:3, j:4}}`}, + {"%#v", map[string]B{"a": {1, 2}, "b": {3, 4}}, `map[string] fmt_test.B{"a":fmt_test.B{I:1, j:2}, "b":fmt_test.B{I:3, j:4}}`}, {"%#v", []string{"a", "b"}, `[]string{"a", "b"}`}, // slices with other formats @@ -384,11 +384,11 @@ var fmttests = []struct { // Formatter {"%x", F(1), "<x=F(1)>"}, {"%x", G(2), "2"}, - {"%+v", S{F(4), G(5)}, "{f:<v=F(4)> g:5}"}, + {"%+v", S{F(4), G(5)}, "{F:<v=F(4)> G:5}"}, // GoStringer {"%#v", G(6), "GoString(6)"}, - {"%#v", S{F(7), G(8)}, "fmt_test.S{f:<v=F(7)>, g:GoString(8)}"}, + {"%#v", S{F(7), G(8)}, "fmt_test.S{F:<v=F(7)>, G:GoString(8)}"}, // %T {"%T", (4 - 3i), "complex128"}, diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go index 738734908..136aebd33 100644 --- a/src/pkg/fmt/print.go +++ b/src/pkg/fmt/print.go @@ -258,10 +258,8 @@ func Sprintln(a ...interface{}) string { // the thing inside the interface, not the interface itself. func getField(v reflect.Value, i int) reflect.Value { val := v.Field(i) - if i := val; i.Kind() == reflect.Interface { - if inter := i.Interface(); inter != nil { - return reflect.ValueOf(inter) - } + if val.Kind() == reflect.Interface && !val.IsNil() { + val = val.Elem() } return val } @@ -288,27 +286,32 @@ func (p *pp) unknownType(v interface{}) { p.buf.WriteByte('?') } -func (p *pp) badVerb(verb int, val interface{}) { +func (p *pp) badVerb(verb int, val interface{}, val1 reflect.Value) { p.add('%') p.add('!') p.add(verb) p.add('(') - if val == nil { - p.buf.Write(nilAngleBytes) - } else { + switch { + case val != nil: p.buf.WriteString(reflect.TypeOf(val).String()) p.add('=') p.printField(val, 'v', false, false, 0) + case val1.IsValid(): + p.buf.WriteString(val1.Type().String()) + p.add('=') + p.printValue(val1, 'v', false, false, 0) + default: + p.buf.Write(nilAngleBytes) } p.add(')') } -func (p *pp) fmtBool(v bool, verb int, value interface{}) { +func (p *pp) fmtBool(v bool, verb int, value interface{}, value1 reflect.Value) { switch verb { case 't', 'v': p.fmt.fmt_boolean(v) default: - p.badVerb(verb, value) + p.badVerb(verb, value, value1) } } @@ -322,7 +325,7 @@ func (p *pp) fmtC(c int64) { p.fmt.pad(p.runeBuf[0:w]) } -func (p *pp) fmtInt64(v int64, verb int, value interface{}) { +func (p *pp) fmtInt64(v int64, verb int, value interface{}, value1 reflect.Value) { switch verb { case 'b': p.fmt.integer(v, 2, signed, ldigits) @@ -336,7 +339,7 @@ func (p *pp) fmtInt64(v int64, verb int, value interface{}) { if 0 <= v && v <= unicode.MaxRune { p.fmt.fmt_qc(v) } else { - p.badVerb(verb, value) + p.badVerb(verb, value, value1) } case 'x': p.fmt.integer(v, 16, signed, ldigits) @@ -345,7 +348,7 @@ func (p *pp) fmtInt64(v int64, verb int, value interface{}) { case 'X': p.fmt.integer(v, 16, signed, udigits) default: - p.badVerb(verb, value) + p.badVerb(verb, value, value1) } } @@ -380,7 +383,7 @@ func (p *pp) fmtUnicode(v int64) { p.fmt.sharp = sharp } -func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) { +func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}, value1 reflect.Value) { switch verb { case 'b': p.fmt.integer(int64(v), 2, unsigned, ldigits) @@ -400,7 +403,7 @@ func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) { if 0 <= v && v <= unicode.MaxRune { p.fmt.fmt_qc(int64(v)) } else { - p.badVerb(verb, value) + p.badVerb(verb, value, value1) } case 'x': p.fmt.integer(int64(v), 16, unsigned, ldigits) @@ -409,11 +412,11 @@ func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) { case 'U': p.fmtUnicode(int64(v)) default: - p.badVerb(verb, value) + p.badVerb(verb, value, value1) } } -func (p *pp) fmtFloat32(v float32, verb int, value interface{}) { +func (p *pp) fmtFloat32(v float32, verb int, value interface{}, value1 reflect.Value) { switch verb { case 'b': p.fmt.fmt_fb32(v) @@ -428,11 +431,11 @@ func (p *pp) fmtFloat32(v float32, verb int, value interface{}) { case 'G': p.fmt.fmt_G32(v) default: - p.badVerb(verb, value) + p.badVerb(verb, value, value1) } } -func (p *pp) fmtFloat64(v float64, verb int, value interface{}) { +func (p *pp) fmtFloat64(v float64, verb int, value interface{}, value1 reflect.Value) { switch verb { case 'b': p.fmt.fmt_fb64(v) @@ -447,33 +450,33 @@ func (p *pp) fmtFloat64(v float64, verb int, value interface{}) { case 'G': p.fmt.fmt_G64(v) default: - p.badVerb(verb, value) + p.badVerb(verb, value, value1) } } -func (p *pp) fmtComplex64(v complex64, verb int, value interface{}) { +func (p *pp) fmtComplex64(v complex64, verb int, value interface{}, value1 reflect.Value) { switch verb { case 'e', 'E', 'f', 'F', 'g', 'G': p.fmt.fmt_c64(v, verb) case 'v': p.fmt.fmt_c64(v, 'g') default: - p.badVerb(verb, value) + p.badVerb(verb, value, value1) } } -func (p *pp) fmtComplex128(v complex128, verb int, value interface{}) { +func (p *pp) fmtComplex128(v complex128, verb int, value interface{}, value1 reflect.Value) { switch verb { case 'e', 'E', 'f', 'F', 'g', 'G': p.fmt.fmt_c128(v, verb) case 'v': p.fmt.fmt_c128(v, 'g') default: - p.badVerb(verb, value) + p.badVerb(verb, value, value1) } } -func (p *pp) fmtString(v string, verb int, goSyntax bool, value interface{}) { +func (p *pp) fmtString(v string, verb int, goSyntax bool, value interface{}, value1 reflect.Value) { switch verb { case 'v': if goSyntax { @@ -490,11 +493,11 @@ func (p *pp) fmtString(v string, verb int, goSyntax bool, value interface{}) { case 'q': p.fmt.fmt_q(v) default: - p.badVerb(verb, value) + p.badVerb(verb, value, value1) } } -func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interface{}) { +func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interface{}, value1 reflect.Value) { if verb == 'v' || verb == 'd' { if goSyntax { p.buf.Write(bytesBytes) @@ -529,7 +532,7 @@ func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interf case 'q': p.fmt.fmt_q(s) default: - p.badVerb(verb, value) + p.badVerb(verb, value, value1) } } @@ -539,12 +542,12 @@ func (p *pp) fmtPointer(field interface{}, value reflect.Value, verb int, goSynt case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: u = value.Pointer() default: - p.badVerb(verb, field) + p.badVerb(verb, field, value) return } if goSyntax { p.add('(') - p.buf.WriteString(reflect.TypeOf(field).String()) + p.buf.WriteString(value.Type().String()) p.add(')') p.add('(') if u == 0 { @@ -590,138 +593,192 @@ func (p *pp) catchPanic(val interface{}, verb int) { } } -func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth int) (wasString bool) { - if field == nil { - if verb == 'T' || verb == 'v' { - p.buf.Write(nilAngleBytes) - } else { - p.badVerb(verb, field) - } - return false - } - - // Special processing considerations. - // %T (the value's type) and %p (its address) are special; we always do them first. - switch verb { - case 'T': - p.printField(reflect.TypeOf(field).String(), 's', false, false, 0) - return false - case 'p': - p.fmtPointer(field, reflect.ValueOf(field), verb, goSyntax) - return false - } +func (p *pp) handleMethods(field interface{}, verb int, plus, goSyntax bool, depth int) (wasString, handled bool) { // Is it a Formatter? if formatter, ok := field.(Formatter); ok { + handled = true + wasString = false defer p.catchPanic(field, verb) formatter.Format(p, verb) - return false // this value is not a string - + return } // Must not touch flags before Formatter looks at them. if plus { p.fmt.plus = false } + // If we're doing Go syntax and the field knows how to supply it, take care of it now. if goSyntax { p.fmt.sharp = false if stringer, ok := field.(GoStringer); ok { + wasString = false + handled = true defer p.catchPanic(field, verb) // Print the result of GoString unadorned. - p.fmtString(stringer.GoString(), 's', false, field) - return false // this value is not a string + p.fmtString(stringer.GoString(), 's', false, field, reflect.Value{}) + return } } else { // Is it a Stringer? if stringer, ok := field.(Stringer); ok { + wasString = false + handled = true defer p.catchPanic(field, verb) p.printField(stringer.String(), verb, plus, false, depth) - return false // this value is not a string + return } } + handled = false + return +} + +func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth int) (wasString bool) { + if field == nil { + if verb == 'T' || verb == 'v' { + p.buf.Write(nilAngleBytes) + } else { + p.badVerb(verb, field, reflect.Value{}) + } + return false + } + + // Special processing considerations. + // %T (the value's type) and %p (its address) are special; we always do them first. + switch verb { + case 'T': + p.printField(reflect.TypeOf(field).String(), 's', false, false, 0) + return false + case 'p': + p.fmtPointer(field, reflect.ValueOf(field), verb, goSyntax) + return false + } + + if wasString, handled := p.handleMethods(field, verb, plus, goSyntax, depth); handled { + return wasString + } // Some types can be done without reflection. switch f := field.(type) { case bool: - p.fmtBool(f, verb, field) + p.fmtBool(f, verb, field, reflect.Value{}) return false case float32: - p.fmtFloat32(f, verb, field) + p.fmtFloat32(f, verb, field, reflect.Value{}) return false case float64: - p.fmtFloat64(f, verb, field) + p.fmtFloat64(f, verb, field, reflect.Value{}) return false case complex64: - p.fmtComplex64(complex64(f), verb, field) + p.fmtComplex64(complex64(f), verb, field, reflect.Value{}) return false case complex128: - p.fmtComplex128(f, verb, field) + p.fmtComplex128(f, verb, field, reflect.Value{}) return false case int: - p.fmtInt64(int64(f), verb, field) + p.fmtInt64(int64(f), verb, field, reflect.Value{}) return false case int8: - p.fmtInt64(int64(f), verb, field) + p.fmtInt64(int64(f), verb, field, reflect.Value{}) return false case int16: - p.fmtInt64(int64(f), verb, field) + p.fmtInt64(int64(f), verb, field, reflect.Value{}) return false case int32: - p.fmtInt64(int64(f), verb, field) + p.fmtInt64(int64(f), verb, field, reflect.Value{}) return false case int64: - p.fmtInt64(f, verb, field) + p.fmtInt64(f, verb, field, reflect.Value{}) return false case uint: - p.fmtUint64(uint64(f), verb, goSyntax, field) + p.fmtUint64(uint64(f), verb, goSyntax, field, reflect.Value{}) return false case uint8: - p.fmtUint64(uint64(f), verb, goSyntax, field) + p.fmtUint64(uint64(f), verb, goSyntax, field, reflect.Value{}) return false case uint16: - p.fmtUint64(uint64(f), verb, goSyntax, field) + p.fmtUint64(uint64(f), verb, goSyntax, field, reflect.Value{}) return false case uint32: - p.fmtUint64(uint64(f), verb, goSyntax, field) + p.fmtUint64(uint64(f), verb, goSyntax, field, reflect.Value{}) return false case uint64: - p.fmtUint64(f, verb, goSyntax, field) + p.fmtUint64(f, verb, goSyntax, field, reflect.Value{}) return false case uintptr: - p.fmtUint64(uint64(f), verb, goSyntax, field) + p.fmtUint64(uint64(f), verb, goSyntax, field, reflect.Value{}) return false case string: - p.fmtString(f, verb, goSyntax, field) + p.fmtString(f, verb, goSyntax, field, reflect.Value{}) return verb == 's' || verb == 'v' case []byte: - p.fmtBytes(f, verb, goSyntax, depth, field) + p.fmtBytes(f, verb, goSyntax, depth, field, reflect.Value{}) return verb == 's' } // Need to use reflection - value := reflect.ValueOf(field) + return p.printReflectValue(reflect.ValueOf(field), verb, plus, goSyntax, depth) +} + +// printValue is like printField but starts with a reflect value, not an interface{} value. +func (p *pp) printValue(value reflect.Value, verb int, plus, goSyntax bool, depth int) (wasString bool) { + if !value.IsValid() { + if verb == 'T' || verb == 'v' { + p.buf.Write(nilAngleBytes) + } else { + p.badVerb(verb, nil, value) + } + return false + } + // Special processing considerations. + // %T (the value's type) and %p (its address) are special; we always do them first. + switch verb { + case 'T': + p.printField(value.Type().String(), 's', false, false, 0) + return false + case 'p': + p.fmtPointer(nil, value, verb, goSyntax) + return false + } + + // Handle values with special methods. + // Call always, even when field == nil, because handleMethods clears p.fmt.plus for us. + var field interface{} + if value.CanInterface() { + field = value.Interface() + } + if wasString, handled := p.handleMethods(field, verb, plus, goSyntax, depth); handled { + return wasString + } + + return p.printReflectValue(value, verb, plus, goSyntax, depth) +} + +// printReflectValue is the fallback for both printField and printValue. +// It uses reflect to print the value. +func (p *pp) printReflectValue(value reflect.Value, verb int, plus, goSyntax bool, depth int) (wasString bool) { BigSwitch: switch f := value; f.Kind() { case reflect.Bool: - p.fmtBool(f.Bool(), verb, field) + p.fmtBool(f.Bool(), verb, nil, value) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - p.fmtInt64(f.Int(), verb, field) + p.fmtInt64(f.Int(), verb, nil, value) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - p.fmtUint64(uint64(f.Uint()), verb, goSyntax, field) + p.fmtUint64(uint64(f.Uint()), verb, goSyntax, nil, value) case reflect.Float32, reflect.Float64: if f.Type().Size() == 4 { - p.fmtFloat32(float32(f.Float()), verb, field) + p.fmtFloat32(float32(f.Float()), verb, nil, value) } else { - p.fmtFloat64(float64(f.Float()), verb, field) + p.fmtFloat64(float64(f.Float()), verb, nil, value) } case reflect.Complex64, reflect.Complex128: if f.Type().Size() == 8 { - p.fmtComplex64(complex64(f.Complex()), verb, field) + p.fmtComplex64(complex64(f.Complex()), verb, nil, value) } else { - p.fmtComplex128(complex128(f.Complex()), verb, field) + p.fmtComplex128(complex128(f.Complex()), verb, nil, value) } case reflect.String: - p.fmtString(f.String(), verb, goSyntax, field) + p.fmtString(f.String(), verb, goSyntax, nil, value) case reflect.Map: if goSyntax { p.buf.WriteString(f.Type().String()) @@ -738,9 +795,9 @@ BigSwitch: p.buf.WriteByte(' ') } } - p.printField(key.Interface(), verb, plus, goSyntax, depth+1) + p.printValue(key, verb, plus, goSyntax, depth+1) p.buf.WriteByte(':') - p.printField(f.MapIndex(key).Interface(), verb, plus, goSyntax, depth+1) + p.printValue(f.MapIndex(key), verb, plus, goSyntax, depth+1) } if goSyntax { p.buf.WriteByte('}') @@ -749,7 +806,7 @@ BigSwitch: } case reflect.Struct: if goSyntax { - p.buf.WriteString(reflect.TypeOf(field).String()) + p.buf.WriteString(value.Type().String()) } p.add('{') v := f @@ -768,20 +825,20 @@ BigSwitch: p.buf.WriteByte(':') } } - p.printField(getField(v, i).Interface(), verb, plus, goSyntax, depth+1) + p.printValue(getField(v, i), verb, plus, goSyntax, depth+1) } p.buf.WriteByte('}') case reflect.Interface: value := f.Elem() if !value.IsValid() { if goSyntax { - p.buf.WriteString(reflect.TypeOf(field).String()) + p.buf.WriteString(value.Type().String()) p.buf.Write(nilParenBytes) } else { p.buf.Write(nilAngleBytes) } } else { - return p.printField(value.Interface(), verb, plus, goSyntax, depth+1) + return p.printValue(value, verb, plus, goSyntax, depth+1) } case reflect.Array, reflect.Slice: // Byte slices are special. @@ -797,11 +854,11 @@ BigSwitch: for i := range bytes { bytes[i] = byte(f.Index(i).Uint()) } - p.fmtBytes(bytes, verb, goSyntax, depth, field) + p.fmtBytes(bytes, verb, goSyntax, depth, nil, value) return verb == 's' } if goSyntax { - p.buf.WriteString(reflect.TypeOf(field).String()) + p.buf.WriteString(value.Type().String()) p.buf.WriteByte('{') } else { p.buf.WriteByte('[') @@ -814,7 +871,7 @@ BigSwitch: p.buf.WriteByte(' ') } } - p.printField(f.Index(i).Interface(), verb, plus, goSyntax, depth+1) + p.printValue(f.Index(i), verb, plus, goSyntax, depth+1) } if goSyntax { p.buf.WriteByte('}') @@ -829,17 +886,17 @@ BigSwitch: switch a := f.Elem(); a.Kind() { case reflect.Array, reflect.Slice: p.buf.WriteByte('&') - p.printField(a.Interface(), verb, plus, goSyntax, depth+1) + p.printValue(a, verb, plus, goSyntax, depth+1) break BigSwitch case reflect.Struct: p.buf.WriteByte('&') - p.printField(a.Interface(), verb, plus, goSyntax, depth+1) + p.printValue(a, verb, plus, goSyntax, depth+1) break BigSwitch } } if goSyntax { p.buf.WriteByte('(') - p.buf.WriteString(reflect.TypeOf(field).String()) + p.buf.WriteString(value.Type().String()) p.buf.WriteByte(')') p.buf.WriteByte('(') if v == 0 { @@ -856,7 +913,7 @@ BigSwitch: } p.fmt0x64(uint64(v), true) case reflect.Chan, reflect.Func, reflect.UnsafePointer: - p.fmtPointer(field, value, verb, goSyntax) + p.fmtPointer(nil, value, verb, goSyntax) default: p.unknownType(f) } diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go index 257278e8f..9a41b504a 100644 --- a/src/pkg/reflect/all_test.go +++ b/src/pkg/reflect/all_test.go @@ -853,13 +853,13 @@ func TestIsNil(t *testing.T) { func TestInterfaceExtraction(t *testing.T) { var s struct { - w io.Writer + W io.Writer } - s.w = os.Stdout + s.W = os.Stdout v := Indirect(ValueOf(&s)).Field(0).Interface() - if v != s.w.(interface{}) { - t.Error("Interface() on interface: ", v, s.w) + if v != s.W.(interface{}) { + t.Error("Interface() on interface: ", v, s.W) } } @@ -1190,18 +1190,18 @@ type D2 struct { } type S0 struct { - a, b, c int + A, B, C int D1 D2 } type S1 struct { - b int + B int S0 } type S2 struct { - a int + A int *S1 } @@ -1216,36 +1216,36 @@ type S1y struct { type S3 struct { S1x S2 - d, e int + D, E int *S1y } type S4 struct { *S4 - a int + A int } var fieldTests = []FTest{ {struct{}{}, "", nil, 0}, - {struct{}{}, "foo", nil, 0}, - {S0{a: 'a'}, "a", []int{0}, 'a'}, - {S0{}, "d", nil, 0}, - {S1{S0: S0{a: 'a'}}, "a", []int{1, 0}, 'a'}, - {S1{b: 'b'}, "b", []int{0}, 'b'}, + {struct{}{}, "Foo", nil, 0}, + {S0{A: 'a'}, "A", []int{0}, 'a'}, + {S0{}, "D", nil, 0}, + {S1{S0: S0{A: 'a'}}, "A", []int{1, 0}, 'a'}, + {S1{B: 'b'}, "B", []int{0}, 'b'}, {S1{}, "S0", []int{1}, 0}, - {S1{S0: S0{c: 'c'}}, "c", []int{1, 2}, 'c'}, - {S2{a: 'a'}, "a", []int{0}, 'a'}, + {S1{S0: S0{C: 'c'}}, "C", []int{1, 2}, 'c'}, + {S2{A: 'a'}, "A", []int{0}, 'a'}, {S2{}, "S1", []int{1}, 0}, - {S2{S1: &S1{b: 'b'}}, "b", []int{1, 0}, 'b'}, - {S2{S1: &S1{S0: S0{c: 'c'}}}, "c", []int{1, 1, 2}, 'c'}, - {S2{}, "d", nil, 0}, + {S2{S1: &S1{B: 'b'}}, "B", []int{1, 0}, 'b'}, + {S2{S1: &S1{S0: S0{C: 'c'}}}, "C", []int{1, 1, 2}, 'c'}, + {S2{}, "D", nil, 0}, {S3{}, "S1", nil, 0}, - {S3{S2: S2{a: 'a'}}, "a", []int{1, 0}, 'a'}, - {S3{}, "b", nil, 0}, - {S3{d: 'd'}, "d", []int{2}, 0}, - {S3{e: 'e'}, "e", []int{3}, 'e'}, - {S4{a: 'a'}, "a", []int{1}, 'a'}, - {S4{}, "b", nil, 0}, + {S3{S2: S2{A: 'a'}}, "A", []int{1, 0}, 'a'}, + {S3{}, "B", nil, 0}, + {S3{D: 'd'}, "D", []int{2}, 0}, + {S3{E: 'e'}, "E", []int{3}, 'e'}, + {S4{A: 'a'}, "A", []int{1}, 'a'}, + {S4{}, "B", nil, 0}, } func TestFieldByIndex(t *testing.T) { @@ -1562,3 +1562,68 @@ func TestTagGet(t *testing.T) { } } } + +type Private struct { + x int + y **int +} + +func (p *Private) m() { +} + +type Public struct { + X int + Y **int +} + +func (p *Public) M() { +} + +func TestUnexported(t *testing.T) { + var pub Public + v := ValueOf(&pub) + isValid(v.Elem().Field(0)) + isValid(v.Elem().Field(1)) + isValid(v.Elem().FieldByName("X")) + isValid(v.Elem().FieldByName("Y")) + isValid(v.Type().Method(0).Func) + isNonNil(v.Elem().Field(0).Interface()) + isNonNil(v.Elem().Field(1).Interface()) + isNonNil(v.Elem().FieldByName("X").Interface()) + isNonNil(v.Elem().FieldByName("Y").Interface()) + isNonNil(v.Type().Method(0).Func.Interface()) + + var priv Private + v = ValueOf(&priv) + isValid(v.Elem().Field(0)) + isValid(v.Elem().Field(1)) + isValid(v.Elem().FieldByName("x")) + isValid(v.Elem().FieldByName("y")) + isValid(v.Type().Method(0).Func) + shouldPanic(func() { v.Elem().Field(0).Interface() }) + shouldPanic(func() { v.Elem().Field(1).Interface() }) + shouldPanic(func() { v.Elem().FieldByName("x").Interface() }) + shouldPanic(func() { v.Elem().FieldByName("y").Interface() }) + shouldPanic(func() { v.Type().Method(0).Func.Interface() }) +} + +func shouldPanic(f func()) { + defer func() { + if recover() == nil { + panic("did not panic") + } + }() + f() +} + +func isNonNil(x interface{}) { + if x == nil { + panic("nil interface") + } +} + +func isValid(v Value) { + if !v.IsValid() { + panic("zero Value") + } +} diff --git a/src/pkg/reflect/deepequal.go b/src/pkg/reflect/deepequal.go index a483135b0..63c28fe20 100644 --- a/src/pkg/reflect/deepequal.go +++ b/src/pkg/reflect/deepequal.go @@ -104,7 +104,7 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool return true default: // Normal equality suffices - return v1.Interface() == v2.Interface() + return valueInterface(v1, false) == valueInterface(v2, false) } panic("Not reached") diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go index d3c510ac2..996ba0c3b 100644 --- a/src/pkg/reflect/value.go +++ b/src/pkg/reflect/value.go @@ -841,14 +841,7 @@ func (v Value) CanInterface() bool { if iv.kind == Invalid { panic(&ValueError{"reflect.Value.CanInterface", iv.kind}) } - // TODO(rsc): Check flagRO too. Decide what to do about asking for - // interface for a value obtained via an unexported field. - // If the field were of a known type, say chan int or *sync.Mutex, - // the caller could interfere with the data after getting the - // interface. But fmt.Print depends on being able to look. - // Now that reflect is more efficient the special cases in fmt - // might be less important. - return v.InternalMethod == 0 + return v.InternalMethod == 0 && iv.flag&flagRO == 0 } // Interface returns v's value as an interface{}. @@ -856,22 +849,28 @@ func (v Value) CanInterface() bool { // (as opposed to Type.Method), Interface cannot return an // interface value, so it panics. func (v Value) Interface() interface{} { - return v.internal().Interface() + return valueInterface(v, true) } -func (iv internalValue) Interface() interface{} { +func valueInterface(v Value, safe bool) interface{} { + iv := v.internal() + return iv.valueInterface(safe) +} + +func (iv internalValue) valueInterface(safe bool) interface{} { if iv.kind == 0 { panic(&ValueError{"reflect.Value.Interface", iv.kind}) } if iv.method { panic("reflect.Value.Interface: cannot create interface value for method with bound receiver") } - /* - if v.flag()&noExport != 0 { - panic("reflect.Value.Interface: cannot return value obtained from unexported struct field") - } - */ + if safe && iv.flag&flagRO != 0 { + // Do not allow access to unexported values via Interface, + // because they might be pointers that should not be + // writable or methods or function that should not be callable. + panic("reflect.Value.Interface: cannot return value obtained from unexported field or method") + } if iv.kind == Interface { // Special case: return the element inside the interface. // Won't recurse further because an interface cannot contain an interface. @@ -1700,7 +1699,7 @@ func convertForAssignment(what string, addr unsafe.Pointer, dst Type, iv interna if addr == nil { addr = unsafe.Pointer(new(interface{})) } - x := iv.Interface() + x := iv.valueInterface(false) if dst.NumMethod() == 0 { *(*interface{})(addr) = x } else { diff --git a/test/interface/fake.go b/test/interface/fake.go index bdc5b9072..ddb832542 100644 --- a/test/interface/fake.go +++ b/test/interface/fake.go @@ -12,20 +12,20 @@ package main import "reflect" type T struct { - f float32 - g float32 + F float32 + G float32 - s string - t string + S string + T string - u uint32 - v uint32 + U uint32 + V uint32 - w uint32 - x uint32 + W uint32 + X uint32 - y uint32 - z uint32 + Y uint32 + Z uint32 } func add(s, t string) string { @@ -40,16 +40,16 @@ func assert(b bool) { func main() { var x T - x.f = 1.0 - x.g = x.f - x.s = add("abc", "def") - x.t = add("abc", "def") - x.u = 1 - x.v = 2 - x.w = 1 << 28 - x.x = 2 << 28 - x.y = 0x12345678 - x.z = x.y + x.F = 1.0 + x.G = x.F + x.S = add("abc", "def") + x.T = add("abc", "def") + x.U = 1 + x.V = 2 + x.W = 1 << 28 + x.X = 2 << 28 + x.Y = 0x12345678 + x.Z = x.Y // check mem and string v := reflect.ValueOf(x) |