| // 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. | |
| // Reflection library. | |
| // Types and parsing of type strings. | |
| // This package implements data ``reflection''. A program can use it to analyze types | |
| // and values it does not know at compile time, such as the values passed in a call | |
| // to a function with a ... parameter. This is achieved by extracting the dynamic | |
| // contents of an interface value. | |
| package reflect | |
| import ( | |
| "utf8"; | |
| "sync"; | |
| "unsafe"; | |
| ) | |
| type Type interface | |
| func ExpandType(name string) Type | |
| func typestrings() string // implemented in C; declared here | |
| // These constants identify what kind of thing a Type represents: an int, struct, etc. | |
| const ( | |
| MissingKind = iota; | |
| ArrayKind; | |
| BoolKind; | |
| ChanKind; | |
| DotDotDotKind; | |
| FloatKind; | |
| Float32Kind; | |
| Float64Kind; | |
| FuncKind; | |
| IntKind; | |
| Int16Kind; | |
| Int32Kind; | |
| Int64Kind; | |
| Int8Kind; | |
| InterfaceKind; | |
| MapKind; | |
| PtrKind; | |
| StringKind; | |
| StructKind; | |
| UintKind; | |
| Uint16Kind; | |
| Uint32Kind; | |
| Uint64Kind; | |
| Uint8Kind; | |
| UintptrKind; | |
| ) | |
| // For sizes and alignments. | |
| type allTypes struct { | |
| xarray []byte; | |
| xbool bool; | |
| xchan chan byte; | |
| xfloat float; | |
| xfloat32 float32; | |
| xfloat64 float64; | |
| xfunc func(); | |
| xint int; | |
| xint16 int16; | |
| xint32 int32; | |
| xint64 int64; | |
| xint8 int8; | |
| xinterface interface {}; | |
| xmap map[byte]byte; | |
| xptr *byte; | |
| xslice []byte; | |
| xstring string; | |
| xuint uint; | |
| xuint16 uint16; | |
| xuint32 uint32; | |
| xuint64 uint64; | |
| xuint8 uint8; | |
| xuintptr uintptr; | |
| } | |
| var x allTypes | |
| const ( | |
| ptrsize = unsafe.Sizeof(&x); | |
| interfacesize = unsafe.Sizeof(x.xinterface); | |
| ) | |
| var missingString = "$missing$" // syntactic name for undefined type names | |
| var dotDotDotString = "..." | |
| // Type is the generic interface to reflection types. Once its Kind is known, | |
| // such as BoolKind, the Type can be narrowed to the appropriate, more | |
| // specific interface, such as BoolType. Such narrowed types still implement | |
| // the Type interface. | |
| type Type interface { | |
| // The kind of thing described: ArrayKind, BoolKind, etc. | |
| Kind() int; | |
| // The name declared for the type ("int", "BoolArray", etc.). | |
| Name() string; | |
| // For a named type, same as Name(); otherwise a representation of the type such as "[]int". | |
| String() string; | |
| // The number of bytes needed to store a value; analogous to unsafe.Sizeof(). | |
| Size() int; | |
| // The alignment of a value of this type when used as a field in a struct. | |
| FieldAlign() int; | |
| } | |
| // Fields and methods common to all types | |
| type commonType struct { | |
| kind int; | |
| str string; | |
| name string; | |
| size int; | |
| } | |
| func (c *commonType) Kind() int { | |
| return c.kind | |
| } | |
| func (c *commonType) Name() string { | |
| return c.name | |
| } | |
| func (c *commonType) String() string { | |
| // If there is a name, show that instead of its expansion. | |
| // This is important for reflection: a named type | |
| // might have methods that the unnamed type does not. | |
| if c.name != "" { | |
| return c.name | |
| } | |
| return c.str | |
| } | |
| func (c *commonType) Size() int { | |
| return c.size | |
| } | |
| // -- Basic | |
| type basicType struct { | |
| commonType; | |
| fieldAlign int; | |
| } | |
| func newBasicType(name string, kind int, size int, fieldAlign int) Type { | |
| return &basicType{ commonType{kind, name, name, size}, fieldAlign } | |
| } | |
| func (t *basicType) FieldAlign() int { | |
| return t.fieldAlign | |
| } | |
| // Prebuilt basic Type objects representing the predeclared basic types. | |
| // Most are self-evident except: | |
| // Missing represents types whose representation cannot be discovered; usually an error. | |
| // DotDotDot represents the pseudo-type of a ... parameter. | |
| var ( | |
| Missing = newBasicType(missingString, MissingKind, 1, 1); | |
| DotDotDot = newBasicType(dotDotDotString, DotDotDotKind, unsafe.Sizeof(x.xinterface), unsafe.Alignof(x.xinterface)); | |
| Bool = newBasicType("bool", BoolKind, unsafe.Sizeof(x.xbool), unsafe.Alignof(x.xbool)); | |
| Int = newBasicType("int", IntKind, unsafe.Sizeof(x.xint), unsafe.Alignof(x.xint)); | |
| Int8 = newBasicType("int8", Int8Kind, unsafe.Sizeof(x.xint8), unsafe.Alignof(x.xint8)); | |
| Int16 = newBasicType("int16", Int16Kind, unsafe.Sizeof(x.xint16), unsafe.Alignof(x.xint16)); | |
| Int32 = newBasicType("int32", Int32Kind, unsafe.Sizeof(x.xint32), unsafe.Alignof(x.xint32)); | |
| Int64 = newBasicType("int64", Int64Kind, unsafe.Sizeof(x.xint64), unsafe.Alignof(x.xint64)); | |
| Uint = newBasicType("uint", UintKind, unsafe.Sizeof(x.xuint), unsafe.Alignof(x.xuint)); | |
| Uint8 = newBasicType("uint8", Uint8Kind, unsafe.Sizeof(x.xuint8), unsafe.Alignof(x.xuint8)); | |
| Uint16 = newBasicType("uint16", Uint16Kind, unsafe.Sizeof(x.xuint16), unsafe.Alignof(x.xuint16)); | |
| Uint32 = newBasicType("uint32", Uint32Kind, unsafe.Sizeof(x.xuint32), unsafe.Alignof(x.xuint32)); | |
| Uint64 = newBasicType("uint64", Uint64Kind, unsafe.Sizeof(x.xuint64), unsafe.Alignof(x.xuint64)); | |
| Uintptr = newBasicType("uintptr", UintptrKind, unsafe.Sizeof(x.xuintptr), unsafe.Alignof(x.xuintptr)); | |
| Float = newBasicType("float", FloatKind, unsafe.Sizeof(x.xfloat), unsafe.Alignof(x.xfloat)); | |
| Float32 = newBasicType("float32", Float32Kind, unsafe.Sizeof(x.xfloat32), unsafe.Alignof(x.xfloat32)); | |
| Float64 = newBasicType("float64", Float64Kind, unsafe.Sizeof(x.xfloat64), unsafe.Alignof(x.xfloat64)); | |
| String = newBasicType("string", StringKind, unsafe.Sizeof(x.xstring), unsafe.Alignof(x.xstring)); | |
| ) | |
| // Stub types allow us to defer evaluating type names until needed. | |
| // If the name is empty, the type must be non-nil. | |
| type stubType struct { | |
| name string; | |
| typ Type; | |
| } | |
| func newStubType(name string, typ Type) *stubType { | |
| return &stubType{name, typ} | |
| } | |
| func (t *stubType) Get() Type { | |
| if t.typ == nil { | |
| t.typ = ExpandType(t.name) | |
| } | |
| return t.typ | |
| } | |
| // -- Pointer | |
| // PtrType represents a pointer. | |
| type PtrType interface { | |
| Type; | |
| Sub() Type // The type of the pointed-to item; for "*int", it will be "int". | |
| } | |
| type ptrTypeStruct struct { | |
| commonType; | |
| sub *stubType; | |
| } | |
| func newPtrTypeStruct(name, typestring string, sub *stubType) *ptrTypeStruct { | |
| return &ptrTypeStruct{ commonType{PtrKind, typestring, name, ptrsize}, sub} | |
| } | |
| func (t *ptrTypeStruct) FieldAlign() int { | |
| return unsafe.Alignof(x.xptr); | |
| } | |
| func (t *ptrTypeStruct) Sub() Type { | |
| return t.sub.Get() | |
| } | |
| // -- Array | |
| // ArrayType represents an array or slice type. | |
| type ArrayType interface { | |
| Type; | |
| IsSlice() bool; // True for slices, false for arrays. | |
| Len() int; // 0 for slices, the length for array types. | |
| Elem() Type; // The type of the elements. | |
| } | |
| type arrayTypeStruct struct { | |
| commonType; | |
| elem *stubType; | |
| isslice bool; // otherwise fixed array | |
| len int; | |
| } | |
| func newArrayTypeStruct(name, typestring string, open bool, len int, elem *stubType) *arrayTypeStruct { | |
| return &arrayTypeStruct{ commonType{ArrayKind, typestring, name, 0 }, elem, open, len} | |
| } | |
| func (t *arrayTypeStruct) Size() int { | |
| if t.isslice { | |
| return unsafe.Sizeof(x.xslice); | |
| } | |
| return t.len * t.elem.Get().Size(); | |
| } | |
| func (t *arrayTypeStruct) FieldAlign() int { | |
| if t.isslice { | |
| return unsafe.Alignof(x.xslice); | |
| } | |
| return t.elem.Get().FieldAlign(); | |
| } | |
| func (t *arrayTypeStruct) IsSlice() bool { | |
| return t.isslice | |
| } | |
| func (t *arrayTypeStruct) Len() int { | |
| if t.isslice { | |
| return 0 | |
| } | |
| return t.len | |
| } | |
| func (t *arrayTypeStruct) Elem() Type { | |
| return t.elem.Get() | |
| } | |
| // -- Map | |
| // MapType represents a map type. | |
| type MapType interface { | |
| Type; | |
| Key() Type; // The type of the keys. | |
| Elem() Type; // The type of the elements/values. | |
| } | |
| type mapTypeStruct struct { | |
| commonType; | |
| key *stubType; | |
| elem *stubType; | |
| } | |
| func newMapTypeStruct(name, typestring string, key, elem *stubType) *mapTypeStruct { | |
| return &mapTypeStruct{ commonType{MapKind, typestring, name, ptrsize}, key, elem} | |
| } | |
| func (t *mapTypeStruct) FieldAlign() int { | |
| return unsafe.Alignof(x.xmap); | |
| } | |
| func (t *mapTypeStruct) Key() Type { | |
| return t.key.Get() | |
| } | |
| func (t *mapTypeStruct) Elem() Type { | |
| return t.elem.Get() | |
| } | |
| // -- Chan | |
| // ChanType represents a chan type. | |
| type ChanType interface { | |
| Type; | |
| Dir() int; // The direction of the channel. | |
| Elem() Type; // The type of the elements. | |
| } | |
| // Channel direction. | |
| const ( | |
| SendDir = 1 << iota; | |
| RecvDir; | |
| BothDir = SendDir | RecvDir; | |
| ) | |
| type chanTypeStruct struct { | |
| commonType; | |
| elem *stubType; | |
| dir int; | |
| } | |
| func newChanTypeStruct(name, typestring string, dir int, elem *stubType) *chanTypeStruct { | |
| return &chanTypeStruct{ commonType{ChanKind, typestring, name, ptrsize}, elem, dir} | |
| } | |
| func (t *chanTypeStruct) FieldAlign() int { | |
| return unsafe.Alignof(x.xchan); | |
| } | |
| func (t *chanTypeStruct) Dir() int { | |
| return t.dir | |
| } | |
| func (t *chanTypeStruct) Elem() Type { | |
| return t.elem.Get() | |
| } | |
| // -- Struct | |
| // StructType represents a struct type. | |
| type StructType interface { | |
| Type; | |
| // Field returns, for field i, its name, Type, tag information, and byte offset. | |
| // The indices are in declaration order starting at 0. | |
| Field(i int) (name string, typ Type, tag string, offset int); | |
| // Len is the number of fields. | |
| Len() int; | |
| } | |
| type structField struct { | |
| name string; | |
| typ *stubType; | |
| tag string; | |
| offset int; | |
| } | |
| type structTypeStruct struct { | |
| commonType; | |
| field []structField; | |
| fieldAlign int; | |
| } | |
| func newStructTypeStruct(name, typestring string, field []structField) *structTypeStruct { | |
| return &structTypeStruct{ commonType{StructKind, typestring, name, 0}, field, 0} | |
| } | |
| func (t *structTypeStruct) FieldAlign() int { | |
| t.Size(); // Compute size and alignment. | |
| return t.fieldAlign | |
| } | |
| func (t *structTypeStruct) Size() int { | |
| if t.size > 0 { | |
| return t.size | |
| } | |
| size := 0; | |
| structalign := 0; | |
| for i := 0; i < len(t.field); i++ { | |
| typ := t.field[i].typ.Get(); | |
| elemsize := typ.Size(); | |
| align := typ.FieldAlign() - 1; | |
| if align > structalign { | |
| structalign = align | |
| } | |
| if align > 0 { | |
| size = (size + align) &^ align; | |
| } | |
| t.field[i].offset = size; | |
| size += elemsize; | |
| } | |
| if (structalign > 0) { | |
| // TODO: In the PPC64 ELF ABI, floating point fields | |
| // in a struct are aligned to a 4-byte boundary, but | |
| // if the first field in the struct is a 64-bit float, | |
| // the whole struct is aligned to an 8-byte boundary. | |
| size = (size + structalign) &^ structalign; | |
| t.fieldAlign = structalign + 1; | |
| } | |
| t.size = size; | |
| return size; | |
| } | |
| func (t *structTypeStruct) Field(i int) (name string, typ Type, tag string, offset int) { | |
| if t.field[i].offset == 0 { | |
| t.Size(); // will compute offsets | |
| } | |
| return t.field[i].name, t.field[i].typ.Get(), t.field[i].tag, t.field[i].offset | |
| } | |
| func (t *structTypeStruct) Len() int { | |
| return len(t.field) | |
| } | |
| // -- Interface | |
| // InterfaceType represents an interface type. | |
| // It behaves much like a StructType, treating the methods as fields. | |
| type InterfaceType interface { | |
| Type; | |
| // Field returns, for method i, its name, Type, the empty string, and 0. | |
| // The indices are in declaration order starting at 0. TODO: is this true? | |
| Field(int) (name string, typ Type, tag string, offset int); | |
| Len() int; | |
| } | |
| type interfaceTypeStruct struct { | |
| commonType; | |
| field []structField; | |
| } | |
| func newInterfaceTypeStruct(name, typestring string, field []structField) *interfaceTypeStruct { | |
| return &interfaceTypeStruct{ commonType{InterfaceKind, typestring, name, interfacesize}, field } | |
| } | |
| func (t *interfaceTypeStruct) FieldAlign() int { | |
| return unsafe.Alignof(x.xinterface); | |
| } | |
| func (t *interfaceTypeStruct) Field(i int) (name string, typ Type, tag string, offset int) { | |
| return t.field[i].name, t.field[i].typ.Get(), "", 0 | |
| } | |
| func (t *interfaceTypeStruct) Len() int { | |
| return len(t.field) | |
| } | |
| var nilInterface = newInterfaceTypeStruct("nil", "", make([]structField, 0)); | |
| // -- Func | |
| // FuncType represents a function type. | |
| type FuncType interface { | |
| Type; | |
| In() StructType; // The parameters in the form of a StructType. | |
| Out() StructType; // The results in the form of a StructType. | |
| } | |
| type funcTypeStruct struct { | |
| commonType; | |
| in *structTypeStruct; | |
| out *structTypeStruct; | |
| } | |
| func newFuncTypeStruct(name, typestring string, in, out *structTypeStruct) *funcTypeStruct { | |
| return &funcTypeStruct{ commonType{FuncKind, typestring, name, ptrsize}, in, out } | |
| } | |
| func (t *funcTypeStruct) FieldAlign() int { | |
| return unsafe.Alignof(x.xfunc); | |
| } | |
| func (t *funcTypeStruct) In() StructType { | |
| return t.in | |
| } | |
| func (t *funcTypeStruct) Out() StructType { | |
| if t.out == nil { // nil.(StructType) != nil so make sure caller sees real nil | |
| return nil | |
| } | |
| return t.out | |
| } | |
| // Cache of expanded types keyed by type name. | |
| var types map[string] Type | |
| // List of typename, typestring pairs | |
| var typestring map[string] string | |
| var initialized bool = false | |
| // Map of basic types to prebuilt stubTypes | |
| var basicstub map[string] *stubType | |
| var missingStub *stubType; | |
| var dotDotDotStub *stubType; | |
| // The database stored in the maps is global; use locking to guarantee safety. | |
| var typestringlock sync.Mutex | |
| func lock() { | |
| typestringlock.Lock() | |
| } | |
| func unlock() { | |
| typestringlock.Unlock() | |
| } | |
| func init() { | |
| lock(); // not necessary because of init ordering but be safe. | |
| types = make(map[string] Type); | |
| typestring = make(map[string] string); | |
| basicstub = make(map[string] *stubType); | |
| // Basics go into types table | |
| types[missingString] = Missing; | |
| types[dotDotDotString] = DotDotDot; | |
| types["int"] = Int; | |
| types["int8"] = Int8; | |
| types["int16"] = Int16; | |
| types["int32"] = Int32; | |
| types["int64"] = Int64; | |
| types["uint"] = Uint; | |
| types["uint8"] = Uint8; | |
| types["uint16"] = Uint16; | |
| types["uint32"] = Uint32; | |
| types["uint64"] = Uint64; | |
| types["uintptr"] = Uintptr; | |
| types["float"] = Float; | |
| types["float32"] = Float32; | |
| types["float64"] = Float64; | |
| types["string"] = String; | |
| types["bool"] = Bool; | |
| // Basics get prebuilt stubs | |
| missingStub = newStubType(missingString, Missing); | |
| dotDotDotStub = newStubType(dotDotDotString, DotDotDot); | |
| basicstub[missingString] = missingStub; | |
| basicstub[dotDotDotString] = dotDotDotStub; | |
| basicstub["int"] = newStubType("int", Int); | |
| basicstub["int8"] = newStubType("int8", Int8); | |
| basicstub["int16"] = newStubType("int16", Int16); | |
| basicstub["int32"] = newStubType("int32", Int32); | |
| basicstub["int64"] = newStubType("int64", Int64); | |
| basicstub["uint"] = newStubType("uint", Uint); | |
| basicstub["uint8"] = newStubType("uint8", Uint8); | |
| basicstub["uint16"] = newStubType("uint16", Uint16); | |
| basicstub["uint32"] = newStubType("uint32", Uint32); | |
| basicstub["uint64"] = newStubType("uint64", Uint64); | |
| basicstub["uintptr"] = newStubType("uintptr", Uintptr); | |
| basicstub["float"] = newStubType("float", Float); | |
| basicstub["float32"] = newStubType("float32", Float32); | |
| basicstub["float64"] = newStubType("float64", Float64); | |
| basicstub["string"] = newStubType("string", String); | |
| basicstub["bool"] = newStubType("bool", Bool); | |
| unlock(); | |
| } | |
| /* | |
| Parsing of type strings. These strings are how the run-time recovers type | |
| information dynamically. | |
| Grammar | |
| stubtype = - represent as StubType when possible | |
| type | |
| identifier = | |
| name | |
| '?' | |
| type = | |
| basictypename - int8, string, etc. | |
| typename | |
| arraytype | |
| structtype | |
| interfacetype | |
| chantype | |
| maptype | |
| pointertype | |
| functiontype | |
| typename = | |
| name '.' name | |
| doublequotedstring = | |
| string in " "; escapes are \x00 (NUL) \n \t \" \\ | |
| fieldlist = | |
| [ field { [ ',' | ';' ] field } ] | |
| field = | |
| identifier stubtype [ doublequotedstring ] | |
| arraytype = | |
| '[' [ number ] ']' stubtype | |
| structtype = | |
| 'struct' '{' fieldlist '}' | |
| interfacetype = | |
| 'interface' '{' fieldlist '}' | |
| chantype = | |
| '<-' 'chan' stubtype | |
| 'chan' '<-' stubtype | |
| 'chan' stubtype | |
| maptype = | |
| 'map' '[' stubtype ']' stubtype | |
| pointertype = | |
| '*' stubtype | |
| functiontype = | |
| [ 'func' ] '(' fieldlist ')' [ '(' fieldlist ')' | stubtype ] | |
| In functiontype 'func' is optional because it is omitted in | |
| the reflection string for interface types. | |
| */ | |
| // Helper functions for token scanning | |
| func isdigit(c uint8) bool { | |
| return '0' <= c && c <= '9' | |
| } | |
| func special(c uint8) bool { | |
| s := "*[](){}<;,"; // Note: '.' is not in this list. "P.T" is an identifer, as is "?". | |
| for i := 0; i < len(s); i++ { | |
| if c == s[i] { | |
| return true | |
| } | |
| } | |
| return false; | |
| } | |
| func hex00(s string, i int) bool { | |
| return i + 2 < len(s) && s[i] == '0' && s[i+1] == '0' | |
| } | |
| // Process backslashes. String known to be well-formed. | |
| // Initial double-quote is left in, as an indication this token is a string. | |
| func unescape(s string, backslash bool) string { | |
| if !backslash { | |
| return s | |
| } | |
| out := "\""; | |
| for i := 1; i < len(s); i++ { | |
| c := s[i]; | |
| if c == '\\' { | |
| i++; | |
| c = s[i]; | |
| switch c { | |
| case 'n': | |
| c = '\n'; | |
| case 't': | |
| c = '\t'; | |
| case 'x': | |
| if hex00(s, i+1) { | |
| i += 2; | |
| c = 0; | |
| break; | |
| } | |
| // otherwise just put an 'x'; erroneous but safe. | |
| // default is correct already; \\ is \; \" is " | |
| } | |
| } | |
| out += string(c); | |
| } | |
| return out; | |
| } | |
| // Simple parser for type strings | |
| type typeParser struct { | |
| str string; // string being parsed | |
| token string; // the token being parsed now | |
| tokstart int; // starting position of token | |
| prevend int; // (one after) ending position of previous token | |
| index int; // next character position in str | |
| } | |
| // Return typestring starting at position i. It will finish at the | |
| // end of the previous token (before trailing white space). | |
| func (p *typeParser) TypeString(i int) string { | |
| return p.str[i:p.prevend]; | |
| } | |
| // Load next token into p.token | |
| func (p *typeParser) Next() { | |
| p.prevend = p.index; | |
| token := ""; | |
| for ; p.index < len(p.str) && p.str[p.index] == ' '; p.index++ { | |
| } | |
| p.tokstart = p.index; | |
| if p.index >= len(p.str) { | |
| p.token = ""; | |
| return; | |
| } | |
| start := p.index; | |
| c, w := utf8.DecodeRuneInString(p.str, p.index); | |
| p.index += w; | |
| switch { | |
| case c == '<': | |
| if p.index < len(p.str) && p.str[p.index] == '-' { | |
| p.index++; | |
| p.token = "<-"; | |
| return; | |
| } | |
| fallthrough; // shouldn't happen but let the parser figure it out | |
| case c == '.': | |
| if p.index < len(p.str)+2 && p.str[p.index-1:p.index+2] == dotDotDotString { | |
| p.index += 2; | |
| p.token = dotDotDotString; | |
| return; | |
| } | |
| fallthrough; // shouldn't happen but let the parser figure it out | |
| case special(uint8(c)): | |
| p.token = string(c); | |
| return; | |
| case isdigit(uint8(c)): | |
| for p.index < len(p.str) && isdigit(p.str[p.index]) { | |
| p.index++ | |
| } | |
| p.token = p.str[start : p.index]; | |
| return; | |
| case c == '"': // double-quoted string for struct field annotation | |
| backslash := false; | |
| for p.index < len(p.str) && p.str[p.index] != '"' { | |
| if p.str[p.index] == '\\' { | |
| if p.index+1 == len(p.str) { // bad final backslash | |
| break; | |
| } | |
| p.index++; // skip (and accept) backslash | |
| backslash = true; | |
| } | |
| p.index++ | |
| } | |
| p.token = unescape(p.str[start : p.index], backslash); | |
| if p.index < len(p.str) { // properly terminated string | |
| p.index++; // skip the terminating double-quote | |
| } | |
| return; | |
| } | |
| for p.index < len(p.str) && p.str[p.index] != ' ' && !special(p.str[p.index]) { | |
| p.index++ | |
| } | |
| p.token = p.str[start : p.index]; | |
| } | |
| func (p *typeParser) Type(name string) *stubType | |
| func (p *typeParser) Array(name string, tokstart int) *stubType { | |
| size := 0; | |
| open := true; | |
| if p.token != "]" { | |
| if len(p.token) == 0 || !isdigit(p.token[0]) { | |
| return missingStub | |
| } | |
| // write our own (trivial and simpleminded) atoi to avoid dependency | |
| size = 0; | |
| for i := 0; i < len(p.token); i++ { | |
| size = size * 10 + int(p.token[i]) - '0' | |
| } | |
| p.Next(); | |
| open = false; | |
| } | |
| if p.token != "]" { | |
| return missingStub | |
| } | |
| p.Next(); | |
| elemtype := p.Type(""); | |
| return newStubType(name, newArrayTypeStruct(name, p.TypeString(tokstart), open, size, elemtype)); | |
| } | |
| func (p *typeParser) Map(name string, tokstart int) *stubType { | |
| if p.token != "[" { | |
| return missingStub | |
| } | |
| p.Next(); | |
| keytype := p.Type(""); | |
| if p.token != "]" { | |
| return missingStub | |
| } | |
| p.Next(); | |
| elemtype := p.Type(""); | |
| return newStubType(name, newMapTypeStruct(name, p.TypeString(tokstart), keytype, elemtype)); | |
| } | |
| func (p *typeParser) Chan(name string, tokstart, dir int) *stubType { | |
| if p.token == "<-" { | |
| if dir != BothDir { | |
| return missingStub | |
| } | |
| p.Next(); | |
| dir = SendDir; | |
| } | |
| elemtype := p.Type(""); | |
| return newStubType(name, newChanTypeStruct(name, p.TypeString(tokstart), dir, elemtype)); | |
| } | |
| // Parse array of fields for struct, interface, and func arguments | |
| func (p *typeParser) Fields(sep, term string) []structField { | |
| a := make([]structField, 10); | |
| nf := 0; | |
| for p.token != "" && p.token != term { | |
| if nf == len(a) { | |
| a1 := make([]structField, 2*nf); | |
| for i := 0; i < nf; i++ { | |
| a1[i] = a[i]; | |
| } | |
| a = a1; | |
| } | |
| name := p.token; | |
| if name == "?" { // used to represent a missing name | |
| name = "" | |
| } | |
| a[nf].name = name; | |
| p.Next(); | |
| a[nf].typ = p.Type(""); | |
| if p.token != "" && p.token[0] == '"' { | |
| a[nf].tag = p.token[1:len(p.token)]; | |
| p.Next(); | |
| } | |
| nf++; | |
| if p.token != sep { | |
| break; | |
| } | |
| p.Next(); // skip separator | |
| } | |
| return a[0:nf]; | |
| } | |
| // A single type packaged as a field for a function return | |
| func (p *typeParser) OneField() []structField { | |
| a := make([]structField, 1); | |
| a[0].name = ""; | |
| a[0].typ = p.Type(""); | |
| return a; | |
| } | |
| func (p *typeParser) Struct(name string, tokstart int) *stubType { | |
| f := p.Fields(";", "}"); | |
| if p.token != "}" { | |
| return missingStub; | |
| } | |
| p.Next(); | |
| return newStubType(name, newStructTypeStruct(name, p.TypeString(tokstart), f)); | |
| } | |
| func (p *typeParser) Interface(name string, tokstart int) *stubType { | |
| f := p.Fields(";", "}"); | |
| if p.token != "}" { | |
| return missingStub; | |
| } | |
| p.Next(); | |
| return newStubType(name, newInterfaceTypeStruct(name, p.TypeString(tokstart), f)); | |
| } | |
| func (p *typeParser) Func(name string, tokstart int) *stubType { | |
| // may be 1 or 2 parenthesized lists | |
| f1 := newStructTypeStruct("", "", p.Fields(",", ")")); | |
| if p.token != ")" { | |
| return missingStub; | |
| } | |
| p.Next(); | |
| if p.token != "(" { | |
| // 1 list: the in parameters are a list. Is there a single out parameter? | |
| switch p.token { | |
| case "", "}", ")", ",", ";": | |
| return newStubType(name, newFuncTypeStruct(name, p.TypeString(tokstart), f1, nil)); | |
| } | |
| // A single out parameter. | |
| f2 := newStructTypeStruct("", "", p.OneField()); | |
| return newStubType(name, newFuncTypeStruct(name, p.TypeString(tokstart), f1, f2)); | |
| } else { | |
| p.Next(); | |
| } | |
| f2 := newStructTypeStruct("", "", p.Fields(",", ")")); | |
| if p.token != ")" { | |
| return missingStub; | |
| } | |
| p.Next(); | |
| // 2 lists: the in and out parameters are present | |
| return newStubType(name, newFuncTypeStruct(name, p.TypeString(tokstart), f1, f2)); | |
| } | |
| func (p *typeParser) Type(name string) *stubType { | |
| dir := BothDir; | |
| tokstart := p.tokstart; | |
| switch { | |
| case p.token == "": | |
| return nil; | |
| case p.token == "*": | |
| p.Next(); | |
| sub := p.Type(""); | |
| return newStubType(name, newPtrTypeStruct(name, p.TypeString(tokstart), sub)); | |
| case p.token == "[": | |
| p.Next(); | |
| return p.Array(name, tokstart); | |
| case p.token == "map": | |
| p.Next(); | |
| return p.Map(name, tokstart); | |
| case p.token == "<-": | |
| p.Next(); | |
| dir = RecvDir; | |
| if p.token != "chan" { | |
| return missingStub; | |
| } | |
| fallthrough; | |
| case p.token == "chan": | |
| p.Next(); | |
| return p.Chan(name, tokstart, dir); | |
| case p.token == "struct": | |
| p.Next(); | |
| if p.token != "{" { | |
| return missingStub | |
| } | |
| p.Next(); | |
| return p.Struct(name, tokstart); | |
| case p.token == "interface": | |
| p.Next(); | |
| if p.token != "{" { | |
| return missingStub | |
| } | |
| p.Next(); | |
| return p.Interface(name, tokstart); | |
| case p.token == "func": | |
| p.Next(); | |
| if p.token != "(" { | |
| return missingStub | |
| } | |
| p.Next(); | |
| return p.Func(name, tokstart); | |
| case p.token == "(": | |
| p.Next(); | |
| return p.Func(name, tokstart); | |
| case isdigit(p.token[0]): | |
| p.Next(); | |
| return missingStub; | |
| case special(p.token[0]): | |
| p.Next(); | |
| return missingStub; | |
| } | |
| // must be an identifier. is it basic? if so, we have a stub | |
| if s, ok := basicstub[p.token]; ok { | |
| p.Next(); | |
| if name != "" { | |
| // Need to make a copy because we are renaming a basic type | |
| b := s.Get(); | |
| s = newStubType(name, newBasicType(name, b.Kind(), b.Size(), b.FieldAlign())); | |
| } | |
| return s | |
| } | |
| // not a basic - must be of the form "P.T" | |
| ndot := 0; | |
| for i := 0; i < len(p.token); i++ { | |
| if p.token[i] == '.' { | |
| ndot++ | |
| } | |
| } | |
| if ndot != 1 { | |
| p.Next(); | |
| return missingStub; | |
| } | |
| s := newStubType(p.token, nil); | |
| p.Next(); | |
| return s; | |
| } | |
| // ParseTypeString takes a type name and type string (such as "[]int") and | |
| // returns the Type structure representing a type name specifying the corresponding | |
| // type. An empty typestring represents (the type of) a nil interface value. | |
| func ParseTypeString(name, typestring string) Type { | |
| if typestring == "" { | |
| // If the typestring is empty, it represents (the type of) a nil interface value | |
| return nilInterface | |
| } | |
| p := new(typeParser); | |
| p.str = typestring; | |
| p.Next(); | |
| return p.Type(name).Get(); | |
| } | |
| // Create typestring map from reflect.typestrings() data. Lock is held. | |
| func initializeTypeStrings() { | |
| if initialized { | |
| return | |
| } | |
| initialized = true; | |
| s := typestrings(); | |
| slen := len(s); | |
| for i := 0; i < slen; { | |
| // "reflect.PtrType interface { Sub () (? reflect.Type) }\n" | |
| // find the identifier | |
| idstart := i; | |
| for ; i < slen && s[i] != ' '; i++ { | |
| } | |
| if i == slen { | |
| print("reflect.InitializeTypeStrings: bad identifier\n"); | |
| return; | |
| } | |
| idend := i; | |
| i++; | |
| // find the end of the line, terminating the type | |
| typestart := i; | |
| for ; i < slen && s[i] != '\n'; i++ { | |
| } | |
| if i == slen { | |
| print("reflect.InitializeTypeStrings: bad type string\n"); | |
| return; | |
| } | |
| typeend := i; | |
| i++; //skip newline | |
| typestring[s[idstart:idend]] = s[typestart:typeend]; | |
| } | |
| } | |
| // Look up type string associated with name. Lock is held. | |
| func typeNameToTypeString(name string) string { | |
| s, ok := typestring[name]; | |
| if !ok { | |
| initializeTypeStrings(); | |
| s, ok = typestring[name]; | |
| if !ok { | |
| s = missingString; | |
| typestring[name] = s; | |
| } | |
| } | |
| return s | |
| } | |
| // ExpandType takes the name of a type and returns its Type structure, | |
| // unpacking the associated type string if necessary. | |
| func ExpandType(name string) Type { | |
| lock(); | |
| t, ok := types[name]; | |
| if ok { | |
| unlock(); | |
| return t | |
| } | |
| types[name] = Missing; // prevent recursion; will overwrite | |
| t1 := ParseTypeString(name, typeNameToTypeString(name)); | |
| types[name] = t1; | |
| unlock(); | |
| return t1; | |
| } |