blob: f0585f069dbfdba3997d9b99e5bae2edeb993089 [file] [log] [blame]
// 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.
package reflect
import (
"utf8";
"sync";
"unsafe";
)
type Type interface
func ExpandType(name string) Type
func typestrings() string // implemented in C; declared here
const (
MissingKind = iota;
ArrayKind;
BoolKind;
ChanKind;
DotDotDotKind;
FloatKind;
Float32Kind;
Float64Kind;
Float80Kind;
FuncKind;
IntKind;
Int16Kind;
Int32Kind;
Int64Kind;
Int8Kind;
InterfaceKind;
MapKind;
PtrKind;
StringKind;
StructKind;
UintKind;
Uint16Kind;
Uint32Kind;
Uint64Kind;
Uint8Kind;
UintptrKind;
)
var tmp_interface interface{} // used just to compute sizes of these constants
const (
ptrsize = unsafe.Sizeof(&tmp_interface);
interfacesize = unsafe.Sizeof(tmp_interface);
)
var missingString = "$missing$" // syntactic name for undefined type names
var dotDotDotString = "..."
type Type interface {
Kind() int;
Name() string;
String() string;
Size() 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
}
func newBasicType(name string, kind int, size int) Type {
return &basicType( commonType(kind, name, name, size) )
}
// Prebuilt basic types
var (
Missing = newBasicType(missingString, MissingKind, 1);
DotDotDot = newBasicType(dotDotDotString, DotDotDotKind, 16); // TODO(r): size of interface?
Bool = newBasicType("bool", BoolKind, unsafe.Sizeof(true));
Int = newBasicType("int", IntKind, unsafe.Sizeof(int(0)));
Int8 = newBasicType("int8", Int8Kind, 1);
Int16 = newBasicType("int16", Int16Kind, 2);
Int32 = newBasicType("int32", Int32Kind, 4);
Int64 = newBasicType("int64", Int64Kind, 8);
Uint = newBasicType("uint", UintKind, unsafe.Sizeof(uint(0)));
Uint8 = newBasicType("uint8", Uint8Kind, 1);
Uint16 = newBasicType("uint16", Uint16Kind, 2);
Uint32 = newBasicType("uint32", Uint32Kind, 4);
Uint64 = newBasicType("uint64", Uint64Kind, 8);
Uintptr = newBasicType("uintptr", UintptrKind, unsafe.Sizeof(uintptr(0)));
Float = newBasicType("float", FloatKind, unsafe.Sizeof(float(0)));
Float32 = newBasicType("float32", Float32Kind, 4);
Float64 = newBasicType("float64", Float64Kind, 8);
Float80 = newBasicType("float80", Float80Kind, 10); // TODO: strange size?
// TODO(rsc): Sizeof("") should work, doesn't.
String = newBasicType("string", StringKind, unsafe.Sizeof(string(0)));
)
// 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
type PtrType interface {
Type;
Sub() Type
}
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) Sub() Type {
return t.sub.Get()
}
// -- Array
type ArrayType interface {
Type;
IsSlice() bool;
Len() int;
Elem() Type;
}
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 ptrsize*2 // open arrays are 2-word headers
}
return t.len * t.elem.Get().Size();
}
func (t *arrayTypeStruct) IsSlice() bool {
return t.isslice
}
func (t *arrayTypeStruct) Len() int {
// what about open array? TODO
return t.len
}
func (t *arrayTypeStruct) Elem() Type {
return t.elem.Get()
}
// -- Map
type MapType interface {
Type;
Key() Type;
Elem() Type;
}
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) Key() Type {
return t.key.Get()
}
func (t *mapTypeStruct) Elem() Type {
return t.elem.Get()
}
// -- Chan
type ChanType interface {
Type;
Dir() int;
Elem() Type;
}
const ( // channel direction
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) Dir() int {
return t.dir
}
func (t *chanTypeStruct) Elem() Type {
return t.elem.Get()
}
// -- Struct
type StructType interface {
Type;
Field(int) (name string, typ Type, tag string, offset int);
Len() int;
}
type structField struct {
name string;
typ *stubType;
tag string;
size int;
offset int;
}
type structTypeStruct struct {
commonType;
field []structField;
}
func newStructTypeStruct(name, typestring string, field []structField) *structTypeStruct {
return &structTypeStruct( commonType(StructKind, typestring, name, 0), field)
}
// TODO: not portable; depends on 6g
func (t *structTypeStruct) Size() int {
if t.size > 0 {
return t.size
}
size := 0;
structalignmask := 7; // BUG: we know structs are 8-aligned
for i := 0; i < len(t.field); i++ {
elemsize := t.field[i].typ.Get().Size();
// pad until at (elemsize mod 8) boundary
align := elemsize - 1;
if align > structalignmask {
align = structalignmask
}
if align > 0 {
size = (size + align) & ^align;
}
t.field[i].offset = size;
size += elemsize;
}
size = (size + structalignmask) & ^(structalignmask);
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
type InterfaceType interface {
Type;
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) 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
type FuncType interface {
Type;
In() StructType;
Out() StructType;
}
type funcTypeStruct struct {
commonType;
in *structTypeStruct;
out *structTypeStruct;
}
func newFuncTypeStruct(name, typestring string, in, out *structTypeStruct) *funcTypeStruct {
return &funcTypeStruct( commonType(FuncKind, typestring, name, 0), in, out )
}
func (t *funcTypeStruct) Size() int {
panic("reflect.type: func.Size(): cannot happen");
return 0
}
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["float80"] = Float80;
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["float80"] = newStubType("float80", Float80);
basicstub["string"] = newStubType("string", String);
basicstub["bool"] = newStubType("bool", Bool);
unlock();
}
/*
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 =
'(' fieldlist ')'
*/
// 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?
if p.token == "" || p.token == "}" || p.token == "," || p.token == ";" {
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 == "(":
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()));
}
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;
}
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
}
// Type is known by name. Find (and create if necessary) its real type.
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;
}