blob: b6caca1ffc179aaf8ab33f094323963ad8597aaf [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 "sync"
export type Type interface
export func ExpandType(name string) Type
export func typestrings() string // implemented in C; declared here
export 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;
)
// Int is guaranteed large enough to store a size.
var ptrsize int
var interfacesize int
var MissingString = "$missing$" // syntactic name for undefined type names
var DotDotDotString = "..."
export type Type interface {
Kind() int;
Name() string;
String() string;
Size() int;
}
// Fields and methods common to all types
type Common struct {
kind int;
str string;
name string;
size int;
}
func (c *Common) Kind() int {
return c.kind
}
func (c *Common) Name() string {
return c.name
}
func (c *Common) String() string {
return c.str
}
func (c *Common) Size() int {
return c.size
}
// -- Basic
type BasicType struct {
Common
}
func NewBasicType(name string, kind int, size int) Type {
return &BasicType{ Common{kind, name, name, size} }
}
// Prebuilt basic types
export var (
Missing = NewBasicType(MissingString, MissingKind, 1);
DotDotDot = NewBasicType(DotDotDotString, DotDotDotKind, 16); // TODO(r): size of interface?
Bool = NewBasicType("bool", BoolKind, 1); // TODO: need to know how big a bool is
Int = NewBasicType("int", IntKind, 4); // TODO: need to know how big an int is
Int8 = NewBasicType("int8", Int8Kind, 1);
Int16 = NewBasicType("int16", Int16Kind, 2);
Int32 = NewBasicType("int32", Int32Kind, 4);
Int64 = NewBasicType("int64", Int64Kind, 8);
Uint = NewBasicType("uint", UintKind, 4); // TODO: need to know how big a uint is
Uint8 = NewBasicType("uint8", Uint8Kind, 1);
Uint16 = NewBasicType("uint16", Uint16Kind, 2);
Uint32 = NewBasicType("uint32", Uint32Kind, 4);
Uint64 = NewBasicType("uint64", Uint64Kind, 8);
Uintptr = NewBasicType("uintptr", UintptrKind, 8); // TODO: need to know how big a uintptr is
Float = NewBasicType("float", FloatKind, 4); // TODO: need to know how big a float is
Float32 = NewBasicType("float32", Float32Kind, 4);
Float64 = NewBasicType("float64", Float64Kind, 8);
Float80 = NewBasicType("float80", Float80Kind, 10); // TODO: strange size?
String = NewBasicType("string", StringKind, 8); // implemented as a pointer
)
// 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
export type PtrType interface {
Sub() Type
}
type PtrTypeStruct struct {
Common;
sub *StubType;
}
func NewPtrTypeStruct(name, typestring string, sub *StubType) *PtrTypeStruct {
return &PtrTypeStruct{ Common{PtrKind, typestring, name, ptrsize}, sub}
}
func (t *PtrTypeStruct) Sub() Type {
return t.sub.Get()
}
// -- Array
export type ArrayType interface {
Open() bool;
Len() int;
Elem() Type;
}
type ArrayTypeStruct struct {
Common;
elem *StubType;
open bool; // otherwise fixed size
len int;
}
func NewArrayTypeStruct(name, typestring string, open bool, len int, elem *StubType) *ArrayTypeStruct {
return &ArrayTypeStruct{ Common{ArrayKind, typestring, name, 0}, elem, open, len}
}
func (t *ArrayTypeStruct) Size() int {
if t.open {
return ptrsize // open arrays are pointers to structures
}
return t.len * t.elem.Get().Size();
}
func (t *ArrayTypeStruct) Open() bool {
return t.open
}
func (t *ArrayTypeStruct) Len() int {
// what about open array? TODO
return t.len
}
func (t *ArrayTypeStruct) Elem() Type {
return t.elem.Get()
}
// -- Map
export type MapType interface {
Key() Type;
Elem() Type;
}
type MapTypeStruct struct {
Common;
key *StubType;
elem *StubType;
}
func NewMapTypeStruct(name, typestring string, key, elem *StubType) *MapTypeStruct {
return &MapTypeStruct{ Common{MapKind, typestring, name, 0}, key, elem}
}
func (t *MapTypeStruct) Size() int {
panic("reflect.type: map.Size(): cannot happen");
return 0
}
func (t *MapTypeStruct) Key() Type {
return t.key.Get()
}
func (t *MapTypeStruct) Elem() Type {
return t.elem.Get()
}
// -- Chan
export type ChanType interface {
Dir() int;
Elem() Type;
}
export const ( // channel direction
SendDir = 1 << iota;
RecvDir;
BothDir = SendDir | RecvDir;
)
type ChanTypeStruct struct {
Common;
elem *StubType;
dir int;
}
func NewChanTypeStruct(name, typestring string, dir int, elem *StubType) *ChanTypeStruct {
return &ChanTypeStruct{ Common{ChanKind, typestring, name, 0}, elem, dir}
}
func (t *ChanTypeStruct) Size() int {
panic("reflect.type: chan.Size(): cannot happen");
return 0
}
func (t *ChanTypeStruct) Dir() int {
return t.dir
}
func (t *ChanTypeStruct) Elem() Type {
return t.elem.Get()
}
// -- Struct
export type StructType interface {
Field(int) (name string, typ Type, tag string, offset int);
Len() int;
}
type Field struct {
name string;
typ *StubType;
tag string;
size int;
offset int;
}
type StructTypeStruct struct {
Common;
field *[]Field;
}
func NewStructTypeStruct(name, typestring string, field *[]Field) *StructTypeStruct {
return &StructTypeStruct{ Common{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
export type InterfaceType interface {
Field(int) (name string, typ Type, tag string, offset int);
Len() int;
}
type InterfaceTypeStruct struct {
Common;
field *[]Field;
}
func NewInterfaceTypeStruct(name, typestring string, field *[]Field) *InterfaceTypeStruct {
return &InterfaceTypeStruct{ Common{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", "", new([]Field, 0));
// -- Func
export type FuncType interface {
In() StructType;
Out() StructType;
}
type FuncTypeStruct struct {
Common;
in *StructTypeStruct;
out *StructTypeStruct;
}
func NewFuncTypeStruct(name, typestring string, in, out *StructTypeStruct) *FuncTypeStruct {
return &FuncTypeStruct{ Common{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() {
ptrsize = 8; // TODO: compute this
interfacesize = 2*ptrsize; // TODO: compute this
Lock(); // not necessary because of init ordering but be safe.
types = new(map[string] Type);
typestring = new(map[string] string);
basicstub = new(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 Parser 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 *Parser) TypeString(i int) string {
return p.str[i:p.prevend];
}
// Load next token into p.token
func (p *Parser) 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 := sys.stringtorune(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 *Parser) Type(name string) *StubType
func (p *Parser) 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 *Parser) 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 *Parser) 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 *Parser) Fields(sep, term string) *[]Field {
a := new([]Field, 10);
nf := 0;
for p.token != "" && p.token != term {
if nf == len(a) {
a1 := new([]Field, 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 *Parser) OneField() *[]Field {
a := new([]Field, 1);
a[0].name = "";
a[0].typ = p.Type("");
return a;
}
func (p *Parser) 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 *Parser) 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 *Parser) 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 *Parser) 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;
}
export 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(Parser);
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;
}