package interp
import (
"fmt"
"go/constant"
"path/filepath"
"reflect"
"strconv"
"strings"
"github.com/traefik/yaegi/internal/unsafe2"
)
type tcat uint
const (
nilT tcat = iota
arrayT
binT
binPkgT
boolT
builtinT
chanT
chanSendT
chanRecvT
comparableT
complex64T
complex128T
constraintT
errorT
float32T
float64T
funcT
genericT
interfaceT
intT
int8T
int16T
int32T
int64T
linkedT
mapT
ptrT
sliceT
srcPkgT
stringT
structT
uintT
uint8T
uint16T
uint32T
uint64T
uintptrT
valueT
variadicT
maxT
)
var cats = [...]string {
nilT : "nilT" ,
arrayT : "arrayT" ,
binT : "binT" ,
binPkgT : "binPkgT" ,
boolT : "boolT" ,
builtinT : "builtinT" ,
chanT : "chanT" ,
comparableT : "comparableT" ,
complex64T : "complex64T" ,
complex128T : "complex128T" ,
constraintT : "constraintT" ,
errorT : "errorT" ,
float32T : "float32" ,
float64T : "float64T" ,
funcT : "funcT" ,
genericT : "genericT" ,
interfaceT : "interfaceT" ,
intT : "intT" ,
int8T : "int8T" ,
int16T : "int16T" ,
int32T : "int32T" ,
int64T : "int64T" ,
linkedT : "linkedT" ,
mapT : "mapT" ,
ptrT : "ptrT" ,
sliceT : "sliceT" ,
srcPkgT : "srcPkgT" ,
stringT : "stringT" ,
structT : "structT" ,
uintT : "uintT" ,
uint8T : "uint8T" ,
uint16T : "uint16T" ,
uint32T : "uint32T" ,
uint64T : "uint64T" ,
uintptrT : "uintptrT" ,
valueT : "valueT" ,
variadicT : "variadicT" ,
}
func (c tcat ) String () string {
if c < tcat (len (cats )) {
return cats [c ]
}
return "Cat(" + strconv .Itoa (int (c )) + ")"
}
type structField struct {
name string
tag string
embed bool
typ *itype
}
type itype struct {
cat tcat
field []structField
key *itype
val *itype
recv *itype
arg []*itype
ret []*itype
ptr *itype
method []*node
constraint []*itype
ulconstraint []*itype
instance []*itype
name string
path string
length int
rtype reflect .Type
node *node
scope *scope
str string
incomplete bool
untyped bool
isBinMethod bool
}
type generic struct {}
func untypedBool(n *node ) *itype {
return &itype {cat : boolT , name : "bool" , untyped : true , str : "untyped bool" , node : n }
}
func untypedString(n *node ) *itype {
return &itype {cat : stringT , name : "string" , untyped : true , str : "untyped string" , node : n }
}
func untypedRune(n *node ) *itype {
return &itype {cat : int32T , name : "int32" , untyped : true , str : "untyped rune" , node : n }
}
func untypedInt(n *node ) *itype {
return &itype {cat : intT , name : "int" , untyped : true , str : "untyped int" , node : n }
}
func untypedFloat(n *node ) *itype {
return &itype {cat : float64T , name : "float64" , untyped : true , str : "untyped float" , node : n }
}
func untypedComplex(n *node ) *itype {
return &itype {cat : complex128T , name : "complex128" , untyped : true , str : "untyped complex" , node : n }
}
func errorMethodType(sc *scope ) *itype {
return &itype {cat : funcT , ret : []*itype {sc .getType ("string" )}, str : "func() string" }
}
type itypeOption func (*itype )
func isBinMethod() itypeOption {
return func (t *itype ) {
t .isBinMethod = true
}
}
func withRecv(typ *itype ) itypeOption {
return func (t *itype ) {
t .recv = typ
}
}
func withNode(n *node ) itypeOption {
return func (t *itype ) {
t .node = n
}
}
func withScope(sc *scope ) itypeOption {
return func (t *itype ) {
t .scope = sc
}
}
func withUntyped(b bool ) itypeOption {
return func (t *itype ) {
t .untyped = b
}
}
func valueTOf(rtype reflect .Type , opts ...itypeOption ) *itype {
t := &itype {cat : valueT , rtype : rtype , str : rtype .String ()}
for _ , opt := range opts {
opt (t )
}
if t .untyped {
t .str = "untyped " + t .str
}
return t
}
func wrapperValueTOf(rtype reflect .Type , val *itype , opts ...itypeOption ) *itype {
t := &itype {cat : valueT , rtype : rtype , val : val , str : rtype .String ()}
for _ , opt := range opts {
opt (t )
}
return t
}
func variadicOf(val *itype , opts ...itypeOption ) *itype {
t := &itype {cat : variadicT , val : val , str : "..." + val .str }
for _ , opt := range opts {
opt (t )
}
return t
}
func ptrOf(val *itype , opts ...itypeOption ) *itype {
if val .ptr != nil {
return val .ptr
}
t := &itype {cat : ptrT , val : val , str : "*" + val .str }
for _ , opt := range opts {
opt (t )
}
val .ptr = t
return t
}
func namedOf(val *itype , path , name string , opts ...itypeOption ) *itype {
str := name
if path != "" {
str = path + "." + name
}
t := &itype {cat : linkedT , val : val , path : path , name : name , str : str }
for _ , opt := range opts {
opt (t )
}
return t
}
func funcOf(args []*itype , ret []*itype , opts ...itypeOption ) *itype {
b := []byte {}
b = append (b , "func(" ...)
b = append (b , paramsTypeString (args )...)
b = append (b , ')' )
if len (ret ) != 0 {
b = append (b , ' ' )
if len (ret ) > 1 {
b = append (b , '(' )
}
b = append (b , paramsTypeString (ret )...)
if len (ret ) > 1 {
b = append (b , ')' )
}
}
t := &itype {cat : funcT , arg : args , ret : ret , str : string (b )}
for _ , opt := range opts {
opt (t )
}
return t
}
type chanDir uint8
const (
chanSendRecv chanDir = iota
chanSend
chanRecv
)
func chanOf(val *itype , dir chanDir , opts ...itypeOption ) *itype {
cat := chanT
str := "chan "
switch dir {
case chanSend :
cat = chanSendT
str = "chan<- "
case chanRecv :
cat = chanRecvT
str = "<-chan "
}
t := &itype {cat : cat , val : val , str : str + val .str }
for _ , opt := range opts {
opt (t )
}
return t
}
func arrayOf(val *itype , l int , opts ...itypeOption ) *itype {
lstr := strconv .Itoa (l )
t := &itype {cat : arrayT , val : val , length : l , str : "[" + lstr + "]" + val .str }
for _ , opt := range opts {
opt (t )
}
return t
}
func sliceOf(val *itype , opts ...itypeOption ) *itype {
t := &itype {cat : sliceT , val : val , str : "[]" + val .str }
for _ , opt := range opts {
opt (t )
}
return t
}
func mapOf(key , val *itype , opts ...itypeOption ) *itype {
t := &itype {cat : mapT , key : key , val : val , str : "map[" + key .str + "]" + val .str }
for _ , opt := range opts {
opt (t )
}
return t
}
func interfaceOf(t *itype , fields []structField , constraint , ulconstraint []*itype , opts ...itypeOption ) *itype {
str := "interface{}"
if len (fields ) > 0 {
str = "interface { " + methodsTypeString (fields ) + "}"
}
if t == nil {
t = &itype {}
}
t .cat = interfaceT
t .field = fields
t .constraint = constraint
t .ulconstraint = ulconstraint
t .str = str
for _ , opt := range opts {
opt (t )
}
return t
}
func structOf(t *itype , fields []structField , opts ...itypeOption ) *itype {
str := "struct {}"
if len (fields ) > 0 {
str = "struct { " + fieldsTypeString (fields ) + "}"
}
if t == nil {
t = &itype {}
}
t .cat = structT
t .field = fields
t .str = str
for _ , opt := range opts {
opt (t )
}
return t
}
func genericOf(val *itype , name , path string , opts ...itypeOption ) *itype {
t := &itype {cat : genericT , name : name , path : path , str : name , val : val }
for _ , opt := range opts {
opt (t )
}
return t
}
func seenNode(ns []*node , n *node ) bool {
for _ , nn := range ns {
if nn == n {
return true
}
}
return false
}
func nodeType(interp *Interpreter , sc *scope , n *node ) (*itype , error ) {
return nodeType2 (interp , sc , n , nil )
}
func nodeType2(interp *Interpreter , sc *scope , n *node , seen []*node ) (t *itype , err error ) {
if n .typ != nil && !n .typ .incomplete {
return n .typ , nil
}
if sname := typeName (n ); sname != "" {
sym , _ , found := sc .lookup (sname )
if found && sym .kind == typeSym && sym .typ != nil {
if sym .typ .isComplete () {
return sym .typ , nil
}
if seenNode (seen , n ) {
sym .typ .incomplete = false
return sym .typ , nil
}
}
}
seen = append (seen , n )
defer func () { seen = seen [:len (seen )-1 ] }()
switch n .kind {
case addressExpr , starExpr :
val , err := nodeType2 (interp , sc , n .child [0 ], seen )
if err != nil {
return nil , err
}
t = ptrOf (val , withNode (n ), withScope (sc ))
t .incomplete = val .incomplete
case arrayType :
c0 := n .child [0 ]
if len (n .child ) == 1 {
val , err := nodeType2 (interp , sc , c0 , seen )
if err != nil {
return nil , err
}
t = sliceOf (val , withNode (n ), withScope (sc ))
t .incomplete = val .incomplete
break
}
var (
length int
incomplete bool
)
switch v := c0 .rval ; {
case v .IsValid ():
if isConstantValue (v .Type ()) {
c := v .Interface ().(constant .Value )
length = constToInt (c )
} else {
switch v .Type ().Kind () {
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
length = int (v .Int ())
case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
length = int (v .Uint ())
default :
return nil , c0 .cfgErrorf ("non integer constant %v" , v )
}
}
case c0 .kind == ellipsisExpr :
length , err = arrayTypeLen (n .anc , sc )
if err != nil {
incomplete = true
}
case c0 .kind == identExpr :
sym , _ , ok := sc .lookup (c0 .ident )
if !ok {
incomplete = true
break
}
if sym .kind != constSym {
return nil , c0 .cfgErrorf ("non-constant array bound %q" , c0 .ident )
}
if sym .typ == nil || !isInt (sym .typ .TypeOf ()) || !sym .rval .IsValid () {
incomplete = true
break
}
length = int (vInt (sym .rval ))
default :
var ok bool
if _ , err := interp .cfg (c0 , sc , sc .pkgID , sc .pkgName ); err != nil {
if strings .Contains (err .Error(), " undefined: " ) {
incomplete = true
break
}
return nil , err
}
if !c0 .rval .IsValid () {
return nil , c0 .cfgErrorf ("undefined array size" )
}
if length , ok = c0 .rval .Interface ().(int ); !ok {
v , ok := c0 .rval .Interface ().(constant .Value )
if !ok {
incomplete = true
break
}
length = constToInt (v )
}
}
val , err := nodeType2 (interp , sc , n .child [1 ], seen )
if err != nil {
return nil , err
}
t = arrayOf (val , length , withNode (n ), withScope (sc ))
t .incomplete = incomplete || val .incomplete
case basicLit :
switch v := n .rval .Interface ().(type ) {
case bool :
n .rval = reflect .ValueOf (constant .MakeBool (v ))
t = untypedBool (n )
case rune :
n .rval = reflect .ValueOf (constant .MakeInt64 (int64 (v )))
t = untypedRune (n )
case constant .Value :
switch v .Kind () {
case constant .Bool :
t = untypedBool (n )
case constant .String :
t = untypedString (n )
case constant .Int :
t = untypedInt (n )
case constant .Float :
t = untypedFloat (n )
case constant .Complex :
t = untypedComplex (n )
default :
err = n .cfgErrorf ("missing support for type %v" , n .rval )
}
default :
err = n .cfgErrorf ("missing support for type %T: %v" , v , n .rval )
}
case unaryExpr :
if isInInterfaceType (n ) {
t1 , err := nodeType2 (interp , sc , n .child [0 ], seen )
if err != nil {
return nil , err
}
t = &itype {cat : constraintT , ulconstraint : []*itype {t1 }}
break
}
t , err = nodeType2 (interp , sc , n .child [0 ], seen )
case binaryExpr :
if isInInterfaceType (n ) {
t = &itype {cat : constraintT , constraint : []*itype {}, ulconstraint : []*itype {}}
for _ , c := range n .child {
t1 , err := nodeType2 (interp , sc , c , seen )
if err != nil {
return nil , err
}
switch t1 .cat {
case constraintT :
t .constraint = append (t .constraint , t1 .constraint ...)
t .ulconstraint = append (t .ulconstraint , t1 .ulconstraint ...)
default :
t .constraint = append (t .constraint , t1 )
}
}
break
}
if t , err = nodeType2 (interp , sc , n .child [0 ], seen ); err != nil {
return nil , err
}
if t .untyped && !isShiftNode (n ) {
var t1 *itype
t1 , err = nodeType2 (interp , sc , n .child [1 ], seen )
if !(t1 .untyped && isInt (t1 .TypeOf ()) && isFloat (t .TypeOf ())) {
t = t1
}
}
dt := t
switch a := n .anc ; {
case a .kind == assignStmt && isEmptyInterface (a .child [0 ].typ ):
a .child [0 ].typ = &itype {cat : interfaceT , val : dt , str : "interface{}" }
case a .kind == defineStmt && len (a .child ) > a .nleft +a .nright :
if dt , err = nodeType2 (interp , sc , a .child [a .nleft ], seen ); err != nil {
return nil , err
}
case a .kind == returnStmt :
dt = sc .def .typ .ret [childPos (n )]
}
if isInterfaceSrc (dt ) {
t2 := *dt
t2 .val = t
dt = &t2
}
t = dt
case callExpr :
if isBuiltinCall (n , sc ) {
switch n .child [0 ].ident {
case bltnComplex :
var nt0 , nt1 *itype
if nt0 , err = nodeType2 (interp , sc , n .child [1 ], seen ); err != nil {
return nil , err
}
if nt1 , err = nodeType2 (interp , sc , n .child [2 ], seen ); err != nil {
return nil , err
}
if nt0 .incomplete || nt1 .incomplete {
t .incomplete = true
} else {
switch t0 , t1 := nt0 .TypeOf (), nt1 .TypeOf (); {
case isFloat32 (t0 ) && isFloat32 (t1 ):
t = sc .getType ("complex64" )
case isFloat64 (t0 ) && isFloat64 (t1 ):
t = sc .getType ("complex128" )
case nt0 .untyped && isNumber (t0 ) && nt1 .untyped && isNumber (t1 ):
t = untypedComplex (n )
case nt0 .untyped && isFloat32 (t1 ) || nt1 .untyped && isFloat32 (t0 ):
t = sc .getType ("complex64" )
case nt0 .untyped && isFloat64 (t1 ) || nt1 .untyped && isFloat64 (t0 ):
t = sc .getType ("complex128" )
default :
err = n .cfgErrorf ("invalid types %s and %s" , t0 .Kind (), t1 .Kind ())
}
if nt0 .untyped && nt1 .untyped {
t = untypedComplex (n )
}
}
case bltnReal , bltnImag :
if t , err = nodeType2 (interp , sc , n .child [1 ], seen ); err != nil {
return nil , err
}
if !t .incomplete {
switch k := t .TypeOf ().Kind (); {
case t .untyped && isNumber (t .TypeOf ()):
t = untypedFloat (n )
case k == reflect .Complex64 :
t = sc .getType ("float32" )
case k == reflect .Complex128 :
t = sc .getType ("float64" )
default :
err = n .cfgErrorf ("invalid complex type %s" , k )
}
}
case bltnCap , bltnCopy , bltnLen :
t = sc .getType ("int" )
case bltnAppend , bltnMake :
t , err = nodeType2 (interp , sc , n .child [1 ], seen )
case bltnNew :
t , err = nodeType2 (interp , sc , n .child [1 ], seen )
incomplete := t .incomplete
t = ptrOf (t , withScope (sc ))
t .incomplete = incomplete
case bltnRecover :
t = sc .getType ("interface{}" )
default :
t = &itype {cat : builtinT }
}
if err != nil {
return nil , err
}
} else {
if t , err = nodeType2 (interp , sc , n .child [0 ], seen ); err != nil || t == nil {
return nil , err
}
switch t .cat {
case valueT :
if rt := t .rtype ; rt .Kind () == reflect .Func && rt .NumOut () == 1 {
t = valueTOf (rt .Out (0 ), withScope (sc ))
}
default :
if len (t .ret ) == 1 {
t = t .ret [0 ]
}
}
}
case compositeLitExpr :
t , err = nodeType2 (interp , sc , n .child [0 ], seen )
case chanType , chanTypeRecv , chanTypeSend :
dir := chanSendRecv
switch n .kind {
case chanTypeRecv :
dir = chanRecv
case chanTypeSend :
dir = chanSend
}
val , err := nodeType2 (interp , sc , n .child [0 ], seen )
if err != nil {
return nil , err
}
t = chanOf (val , dir , withNode (n ), withScope (sc ))
t .incomplete = val .incomplete
case ellipsisExpr :
val , err := nodeType2 (interp , sc , n .child [0 ], seen )
if err != nil {
return nil , err
}
t = variadicOf (val , withNode (n ), withScope (sc ))
t .incomplete = t .val .incomplete
case funcLit :
t , err = nodeType2 (interp , sc , n .child [2 ], seen )
case funcType :
var incomplete bool
for _ , arg := range n .child [0 ].child {
cl := len (arg .child ) - 1
typ , err := nodeType2 (interp , sc , arg .child [cl ], seen )
if err != nil {
return nil , err
}
for _ , c := range arg .child [:cl ] {
sc .sym [c .ident ] = &symbol {index : -1 , kind : varTypeSym , typ : typ }
}
incomplete = incomplete || typ .incomplete
}
args := make ([]*itype , 0 , len (n .child [1 ].child ))
for _ , arg := range n .child [1 ].child {
cl := len (arg .child ) - 1
typ , err := nodeType2 (interp , sc , arg .child [cl ], seen )
if err != nil {
return nil , err
}
args = append (args , typ )
for i := 1 ; i < cl ; i ++ {
args = append (args , typ )
}
incomplete = incomplete || typ .incomplete
}
var rets []*itype
if len (n .child ) == 3 {
for _ , ret := range n .child [2 ].child {
cl := len (ret .child ) - 1
typ , err := nodeType2 (interp , sc , ret .child [cl ], seen )
if err != nil {
return nil , err
}
rets = append (rets , typ )
for i := 1 ; i < cl ; i ++ {
rets = append (rets , typ )
}
incomplete = incomplete || typ .incomplete
}
}
t = funcOf (args , rets , withNode (n ), withScope (sc ))
t .incomplete = incomplete
case identExpr :
sym , _ , found := sc .lookup (n .ident )
if !found {
baseName := filepath .Base (interp .fset .Position (n .pos ).Filename )
ident := filepath .Join (n .ident , baseName )
sym , _, found = sc .lookup (ident )
if !found {
t = &itype {name : n .ident , path : sc .pkgName , node : n , incomplete : true , scope : sc }
sc .sym [n .ident ] = &symbol {kind : typeSym , typ : t }
break
}
}
if sym .kind == varTypeSym {
t = genericOf (sym .typ , n .ident , sc .pkgName , withNode (n ), withScope (sc ))
} else {
t = sym .typ
}
if t == nil {
if t , err = nodeType2 (interp , sc , sym .node , seen ); err != nil {
return nil , err
}
}
if t .incomplete && t .cat == linkedT && t .val != nil && t .val .cat != nilT {
t .incomplete = false
}
if t .incomplete && t .node != n {
m := t .method
if t , err = nodeType2 (interp , sc , t .node , seen ); err != nil {
return nil , err
}
t .method = m
sym .typ = t
}
if t .node == nil {
t .node = n
}
case indexExpr :
var lt *itype
if lt , err = nodeType2 (interp , sc , n .child [0 ], seen ); err != nil {
return nil , err
}
if lt .incomplete {
if t == nil {
t = lt
} else {
t .incomplete = true
}
break
}
switch lt .cat {
case arrayT , mapT , sliceT , variadicT :
t = lt .val
case genericT :
t1 , err := nodeType2 (interp , sc , n .child [1 ], seen )
if err != nil {
return nil , err
}
if t1 .cat == genericT || t1 .incomplete {
t = lt
break
}
name := lt .id () + "[" + t1 .id () + "]"
if sym , _ , found := sc .lookup (name ); found {
t = sym .typ
break
}
t , err = genType (interp , sc , name , lt , []*itype {t1 }, seen )
if err != nil {
return nil , err
}
}
case indexListExpr :
var lt *itype
if lt , err = nodeType2 (interp , sc , n .child [0 ], seen ); err != nil {
return nil , err
}
if lt .incomplete {
if t == nil {
t = lt
} else {
t .incomplete = true
}
break
}
if lt .cat != genericT {
err = n .cfgErrorf ("not a generic type: %s" , lt .id ())
return nil , err
}
name := lt .id () + "["
out := false
types := []*itype {}
for _ , c := range n .child [1 :] {
t1 , err := nodeType2 (interp , sc , c , seen )
if err != nil {
return nil , err
}
if t1 .cat == genericT || t1 .incomplete {
t = lt
out = true
break
}
types = append (types , t1 )
name += t1 .id () + ","
}
if out {
break
}
name = strings .TrimSuffix (name , "," ) + "]"
if sym , _ , found := sc .lookup (name ); found {
t = sym .typ
break
}
t , err = genType (interp , sc , name , lt , types , seen )
case interfaceType :
if sname := typeName (n ); sname != "" {
if sym , _ , found := sc .lookup (sname ); found && sym .kind == typeSym {
t = interfaceOf (sym .typ , sym .typ .field , sym .typ .constraint , sym .typ .ulconstraint , withNode (n ), withScope (sc ))
}
}
var incomplete bool
fields := []structField {}
constraint := []*itype {}
ulconstraint := []*itype {}
for _ , c := range n .child [0 ].child {
c0 := c .child [0 ]
if len (c .child ) == 1 {
if c0 .ident == "error" {
typ := errorMethodType (sc )
fields = append (fields , structField {name : "Error" , typ : typ })
continue
}
typ , err := nodeType2 (interp , sc , c0 , seen )
if err != nil {
return nil , err
}
incomplete = incomplete || typ .incomplete
if typ .cat == constraintT {
constraint = append (constraint , typ .constraint ...)
ulconstraint = append (ulconstraint , typ .ulconstraint ...)
continue
}
fields = append (fields , structField {name : fieldName (c0 ), embed : true , typ : typ })
continue
}
typ , err := nodeType2 (interp , sc , c .child [1 ], seen )
if err != nil {
return nil , err
}
fields = append (fields , structField {name : c0 .ident , typ : typ })
incomplete = incomplete || typ .incomplete
}
t = interfaceOf (t , fields , constraint , ulconstraint , withNode (n ), withScope (sc ))
t .incomplete = incomplete
case landExpr , lorExpr :
t = sc .getType ("bool" )
case mapType :
key , err := nodeType2 (interp , sc , n .child [0 ], seen )
if err != nil {
return nil , err
}
val , err := nodeType2 (interp , sc , n .child [1 ], seen )
if err != nil {
return nil , err
}
t = mapOf (key , val , withNode (n ), withScope (sc ))
t .incomplete = key .incomplete || val .incomplete
case parenExpr :
t , err = nodeType2 (interp , sc , n .child [0 ], seen )
case selectorExpr :
var lt *itype
if n .anc .kind == fieldExpr {
lt = findPackageType (interp , sc , n .child [0 ])
}
if lt == nil {
if lt , err = nodeType2 (interp , sc , n .child [0 ], seen ); err != nil {
return nil , err
}
}
if lt .incomplete {
break
}
name := n .child [1 ].ident
switch lt .cat {
case binPkgT :
pkg := interp .binPkg [lt .path ]
if v , ok := pkg [name ]; ok {
rtype := v .Type ()
if isBinType (v ) {
rtype = rtype .Elem ()
}
t = valueTOf (rtype , withNode (n ), withScope (sc ))
break
}
fallthrough
case srcPkgT :
if pkg , ok := interp .srcPkg [lt .path ]; ok {
if s , ok := pkg [name ]; ok {
t = s .typ
break
}
}
err = n .cfgErrorf ("undefined selector %s.%s" , lt .path , name )
default :
if m , _ := lt .lookupMethod (name ); m != nil {
t , err = nodeType2 (interp , sc , m .child [2 ], seen )
} else if bm , _ , _ , ok := lt .lookupBinMethod (name ); ok {
t = valueTOf (bm .Type , isBinMethod (), withRecv (lt ), withScope (sc ))
} else if ti := lt .lookupField (name ); len (ti ) > 0 {
t = lt .fieldSeq (ti )
} else if bs , _ , ok := lt .lookupBinField (name ); ok {
t = valueTOf (bs .Type , withScope (sc ))
} else {
err = lt .node .cfgErrorf ("undefined selector %s" , name )
}
}
case sliceExpr :
t , err = nodeType2 (interp , sc , n .child [0 ], seen )
if err != nil {
return nil , err
}
if t .cat == valueT {
switch t .rtype .Kind () {
case reflect .Array , reflect .Ptr :
t = valueTOf (reflect .SliceOf (t .rtype .Elem ()), withScope (sc ))
}
break
}
if t .cat == ptrT {
t = t .val
}
if t .cat == arrayT {
incomplete := t .incomplete
t = sliceOf (t .val , withNode (n ), withScope (sc ))
t .incomplete = incomplete
}
case structType :
var sym *symbol
var found bool
sname := structName (n )
if sname != "" {
sym , _, found = sc .lookup (sname )
if found && sym .kind == typeSym && sym .typ != nil {
t = structOf (sym .typ , sym .typ .field , withNode (n ), withScope (sc ))
} else {
t = structOf (nil , nil , withNode (n ), withScope (sc ))
sc .sym [sname ] = &symbol {index : -1 , kind : typeSym , typ : t , node : n }
}
}
var incomplete bool
fields := make ([]structField , 0 , len (n .child [0 ].child ))
for _ , c := range n .child [0 ].child {
switch {
case len (c .child ) == 1 :
typ , err := nodeType2 (interp , sc , c .child [0 ], seen )
if err != nil {
return nil , err
}
fields = append (fields , structField {name : fieldName (c .child [0 ]), embed : true , typ : typ })
incomplete = incomplete || typ .incomplete
case len (c .child ) == 2 && c .child [1 ].kind == basicLit :
tag := vString (c .child [1 ].rval )
typ , err := nodeType2 (interp , sc , c .child [0 ], seen )
if err != nil {
return nil , err
}
fields = append (fields , structField {name : fieldName (c .child [0 ]), embed : true , typ : typ , tag : tag })
incomplete = incomplete || typ .incomplete
default :
var tag string
l := len (c .child )
if c .lastChild ().kind == basicLit {
tag = vString (c .lastChild ().rval )
l --
}
typ , err := nodeType2 (interp , sc , c .child [l -1 ], seen )
if err != nil {
return nil , err
}
incomplete = incomplete || typ .incomplete
for _ , d := range c .child [:l -1 ] {
fields = append (fields , structField {name : d .ident , typ : typ , tag : tag })
}
}
}
t = structOf (t , fields , withNode (n ), withScope (sc ))
t .incomplete = incomplete
if sname != "" {
if sc .sym [sname ] == nil {
sc .sym [sname ] = &symbol {index : -1 , kind : typeSym , node : n }
}
sc .sym [sname ].typ = t
}
case typeAssertExpr :
t , err = nodeType2 (interp , sc , n .child [1 ], seen )
default :
err = n .cfgErrorf ("type definition not implemented: %s" , n .kind )
}
if err == nil && t != nil && t .cat == nilT && !t .incomplete {
err = n .cfgErrorf ("use of untyped nil %s" , t .name )
}
if n .anc .kind == typeSpec && n .kind != selectorExpr && n .kind != identExpr {
name := n .anc .child [0 ].ident
if sym := sc .sym [name ]; sym != nil {
t .path = sc .pkgName
t .name = name
}
}
switch {
case t == nil :
case t .name != "" && t .path != "" :
t .str = t .path + "." + t .name
case t .cat == nilT :
t .str = "nil"
}
return t , err
}
func genType(interp *Interpreter , sc *scope , name string , lt *itype , types []*itype , seen []*node ) (t *itype , err error ) {
g , _ , err := genAST (sc , lt .node .anc , types )
if err != nil {
return nil , err
}
t , err = nodeType2 (interp , sc , g .lastChild (), seen )
if err != nil {
return nil , err
}
lt .instance = append (lt .instance , t )
sc .sym [name ] = &symbol {index : -1 , kind : typeSym , typ : t , node : g }
if lt .scope .sym [name ] == nil {
lt .scope .sym [name ] = sc .sym [name ]
}
for _ , nod := range lt .method {
if err := genMethod (interp , sc , t , nod , types ); err != nil {
return nil , err
}
}
return t , err
}
func genMethod(interp *Interpreter , sc *scope , t *itype , nod *node , types []*itype ) error {
gm , _ , err := genAST (sc , nod , types )
if err != nil {
return err
}
if gm .typ , err = nodeType (interp , sc , gm .child [2 ]); err != nil {
return err
}
t .addMethod (gm )
if rtn := gm .child [0 ].child [0 ].lastChild (); rtn != nil && rtn .kind == starExpr {
pt := ptrOf (t , withNode (t .node ), withScope (sc ))
pt .addMethod (gm )
rtn .typ = pt
}
scop := nod .typ .scope
if _, err = interp .cfg (gm , scop , scop .pkgID , scop .pkgName ); err != nil {
return err
}
return genRun (gm )
}
func findPackageType(interp *Interpreter , sc *scope , n *node ) *itype {
for {
if sc .level == 0 {
break
}
sc = sc .anc
}
baseName := filepath .Base (interp .fset .Position (n .pos ).Filename )
sym , _ , found := sc .lookup (filepath .Join (n .ident , baseName ))
if !found || sym .typ == nil && sym .typ .cat != srcPkgT && sym .typ .cat != binPkgT {
return nil
}
return sym .typ
}
func isBuiltinCall(n *node , sc *scope ) bool {
if n .kind != callExpr {
return false
}
s := n .child [0 ].sym
if s == nil {
if sym , _ , found := sc .lookup (n .child [0 ].ident ); found {
s = sym
}
}
return s != nil && s .kind == bltnSym
}
func typeName(n *node ) string {
if n .anc .kind == typeSpec && len (n .anc .child ) == 2 {
return n .anc .child [0 ].ident
}
return ""
}
func structName(n *node ) string {
if n .anc .kind == typeSpec {
return n .anc .child [0 ].ident
}
return ""
}
func fieldName(n *node ) string {
switch n .kind {
case selectorExpr :
return fieldName (n .child [1 ])
case starExpr :
return fieldName (n .child [0 ])
case indexExpr :
return fieldName (n .child [0 ])
case identExpr :
return n .ident
default :
return ""
}
}
var zeroValues [maxT ]reflect .Value
func init() {
zeroValues [boolT ] = reflect .ValueOf (false )
zeroValues [complex64T ] = reflect .ValueOf (complex64 (0 ))
zeroValues [complex128T ] = reflect .ValueOf (complex128 (0 ))
zeroValues [errorT ] = reflect .ValueOf (new (error )).Elem ()
zeroValues [float32T ] = reflect .ValueOf (float32 (0 ))
zeroValues [float64T ] = reflect .ValueOf (float64 (0 ))
zeroValues [intT ] = reflect .ValueOf (int (0 ))
zeroValues [int8T ] = reflect .ValueOf (int8 (0 ))
zeroValues [int16T ] = reflect .ValueOf (int16 (0 ))
zeroValues [int32T ] = reflect .ValueOf (int32 (0 ))
zeroValues [int64T ] = reflect .ValueOf (int64 (0 ))
zeroValues [stringT ] = reflect .ValueOf ("" )
zeroValues [uintT ] = reflect .ValueOf (uint (0 ))
zeroValues [uint8T ] = reflect .ValueOf (uint8 (0 ))
zeroValues [uint16T ] = reflect .ValueOf (uint16 (0 ))
zeroValues [uint32T ] = reflect .ValueOf (uint32 (0 ))
zeroValues [uint64T ] = reflect .ValueOf (uint64 (0 ))
zeroValues [uintptrT ] = reflect .ValueOf (uintptr (0 ))
}
func (t *itype ) finalize () (*itype , error ) {
var err error
if t .incomplete {
sym , _ , found := t .scope .lookup (t .name )
if found && !sym .typ .incomplete {
sym .typ .method = append (sym .typ .method , t .method ...)
t .method = sym .typ .method
t .incomplete = false
return sym .typ , nil
}
m := t .method
if t , err = nodeType (t .node .interp , t .scope , t .node ); err != nil {
return nil , err
}
if t .incomplete {
return nil , t .node .cfgErrorf ("incomplete type %s" , t .name )
}
t .method = m
t .node .typ = t
if sym != nil {
sym .typ = t
}
}
return t , err
}
func (t *itype ) addMethod (n *node ) {
for _ , m := range t .method {
if m == n {
return
}
}
t .method = append (t .method , n )
}
func (t *itype ) numIn () int {
switch t .cat {
case funcT :
return len (t .arg )
case valueT :
if t .rtype .Kind () != reflect .Func {
return 0
}
in := t .rtype .NumIn ()
if t .recv != nil {
in --
}
return in
}
return 0
}
func (t *itype ) in (i int ) *itype {
switch t .cat {
case funcT :
return t .arg [i ]
case valueT :
if t .rtype .Kind () == reflect .Func {
if t .recv != nil && !isInterface (t .recv ) {
i ++
}
if t .rtype .IsVariadic () && i == t .rtype .NumIn ()-1 {
val := valueTOf (t .rtype .In (i ).Elem ())
return &itype {cat : variadicT , val : val , str : "..." + val .str }
}
return valueTOf (t .rtype .In (i ))
}
}
return nil
}
func (t *itype ) numOut () int {
switch t .cat {
case funcT :
return len (t .ret )
case valueT :
if t .rtype .Kind () == reflect .Func {
return t .rtype .NumOut ()
}
case builtinT :
switch t .name {
case "append" , "cap" , "complex" , "copy" , "imag" , "len" , "make" , "new" , "real" , "recover" , "unsafe.Alignof" , "unsafe.Offsetof" , "unsafe.Sizeof" :
return 1
}
}
return 0
}
func (t *itype ) out (i int ) *itype {
switch t .cat {
case funcT :
return t .ret [i ]
case valueT :
if t .rtype .Kind () == reflect .Func {
return valueTOf (t .rtype .Out (i ))
}
}
return nil
}
func (t *itype ) concrete () *itype {
if isInterface (t ) && t .val != nil {
return t .val .concrete ()
}
return t
}
func (t *itype ) underlying () *itype {
if t .cat == linkedT {
return t .val .underlying ()
}
return t
}
func typeDefined(t1 , t2 *itype ) bool {
if t1 .cat == linkedT && t1 .val == t2 {
return true
}
if t2 .cat == linkedT && t2 .val == t1 {
return true
}
return false
}
func (t *itype ) isVariadic () bool {
switch t .cat {
case funcT :
return len (t .arg ) > 0 && t .arg [len (t .arg )-1 ].cat == variadicT
case valueT :
if t .rtype .Kind () == reflect .Func {
return t .rtype .IsVariadic ()
}
}
return false
}
func (t *itype ) isComplete () bool { return isComplete (t , map [string ]bool {}) }
func isComplete(t *itype , visited map [string ]bool ) bool {
if t .incomplete {
return false
}
name := t .path + "/" + t .name
if visited [name ] {
return true
}
if t .name != "" {
visited [name ] = true
}
switch t .cat {
case linkedT :
if t .val != nil && t .val .cat != nilT {
return true
}
fallthrough
case arrayT , chanT , chanRecvT , chanSendT , ptrT , sliceT , variadicT :
return isComplete (t .val , visited )
case funcT :
complete := true
for _ , a := range t .arg {
complete = complete && isComplete (a , visited )
}
for _ , a := range t .ret {
complete = complete && isComplete (a , visited )
}
return complete
case interfaceT , structT :
complete := true
for _ , f := range t .field {
visited [f .typ .path +"/" +f .typ .name ] = true
complete = complete && isComplete (f .typ , visited )
}
return complete
case mapT :
return isComplete (t .key , visited ) && isComplete (t .val , visited )
case nilT :
return false
}
return true
}
func (t *itype ) comparable () bool {
typ := t .TypeOf ()
return t .cat == nilT || typ != nil && typ .Comparable ()
}
func (t *itype ) assignableTo (o *itype ) bool {
if t .equals (o ) {
return true
}
if t .cat == linkedT && o .cat == linkedT && (t .underlying ().id () != o .underlying ().id () || !typeDefined (t , o )) {
return false
}
if t .isNil () && o .hasNil () || o .isNil () && t .hasNil () {
return true
}
if t .TypeOf ().AssignableTo (o .TypeOf ()) {
return true
}
if isInterface (o ) && t .implements (o ) {
return true
}
if t .cat == sliceT && o .cat == sliceT {
return t .val .assignableTo (o .val )
}
if t .isBinMethod && isFunc (o ) {
return true
}
if t .untyped && isNumber (t .TypeOf ()) && isNumber (o .TypeOf ()) {
return true
}
n := t .node
if n == nil || !n .rval .IsValid () {
return false
}
con , ok := n .rval .Interface ().(constant .Value )
if !ok {
return false
}
if con == nil || !isConstType (o ) {
return false
}
return representableConst (con , o .TypeOf ())
}
func (t *itype ) convertibleTo (o *itype ) bool {
if t .assignableTo (o ) {
return true
}
tt , ot := t .TypeOf (), o .TypeOf ()
if (tt .Kind () == reflect .Ptr || tt .Kind () == reflect .Uintptr ) && ot .Kind () == reflect .UnsafePointer {
return true
}
if tt .Kind () == reflect .UnsafePointer && (ot .Kind () == reflect .Ptr || ot .Kind () == reflect .Uintptr ) {
return true
}
return t .TypeOf ().ConvertibleTo (o .TypeOf ())
}
func (t *itype ) ordered () bool {
typ := t .TypeOf ()
return isInt (typ ) || isFloat (typ ) || isString (typ )
}
func (t *itype ) equals (o *itype ) bool {
switch ti , oi := isInterface (t ), isInterface (o ); {
case ti && oi :
return t .methods ().equals (o .methods ())
case ti && !oi :
return o .methods ().contains (t .methods ())
case oi && !ti :
return t .methods ().contains (o .methods ())
default :
return t .id () == o .id ()
}
}
func (t *itype ) matchDefault (o *itype ) bool {
return t .untyped && t .id () == "untyped " +o .id ()
}
type methodSet map [string ]string
func (m methodSet ) contains (n methodSet ) bool {
for k := range n {
if _ , ok := m [k ]; !ok {
return false
}
}
return true
}
func (m methodSet ) equals (n methodSet ) bool {
return m .contains (n ) && n .contains (m )
}
func (t *itype ) methods () methodSet {
seen := map [*itype ]bool {}
var getMethods func (typ *itype ) methodSet
getMethods = func (typ *itype ) methodSet {
res := make (methodSet )
if seen [typ ] {
return res
}
seen [typ ] = true
switch typ .cat {
case linkedT :
for k , v := range getMethods (typ .val ) {
res [k ] = v
}
case interfaceT :
for _ , f := range typ .field {
if f .typ .cat == funcT {
res [f .name ] = f .typ .TypeOf ().String ()
} else {
for k , v := range getMethods (f .typ ) {
res [k ] = v
}
}
}
case valueT , errorT :
for i := typ .TypeOf ().NumMethod () - 1 ; i >= 0 ; i -- {
m := typ .rtype .Method (i )
res [m .Name ] = m .Type .String ()
}
case ptrT :
if typ .val .cat == valueT {
typ .TypeOf ()
for i := typ .rtype .NumMethod () - 1 ; i >= 0 ; i -- {
m := typ .rtype .Method (i )
res [m .Name ] = m .Type .String ()
}
}
for k , v := range getMethods (typ .val ) {
res [k ] = v
}
case structT :
for _ , f := range typ .field {
if !f .embed {
continue
}
for k , v := range getMethods (f .typ ) {
res [k ] = v
}
}
}
for _ , m := range typ .method {
res [m .ident ] = m .typ .TypeOf ().String ()
}
return res
}
return getMethods (t )
}
func (t *itype ) id () (res string ) {
if t .cat == valueT && t .val != nil {
return t .val .str
}
return t .str
}
func fixPossibleConstType(t reflect .Type ) (r reflect .Type ) {
cv , ok := reflect .New (t ).Elem ().Interface ().(constant .Value )
if !ok {
return t
}
switch cv .Kind () {
case constant .Bool :
r = reflect .TypeOf (true )
case constant .Int :
r = reflect .TypeOf (0 )
case constant .String :
r = reflect .TypeOf ("" )
case constant .Float :
r = reflect .TypeOf (float64 (0 ))
case constant .Complex :
r = reflect .TypeOf (complex128 (0 ))
}
return r
}
func (t *itype ) zero () (v reflect .Value , err error ) {
if t , err = t .finalize (); err != nil {
return v , err
}
switch t .cat {
case linkedT :
v , err = t .val .zero ()
case arrayT , ptrT , structT , sliceT :
v = reflect .New (t .frameType ()).Elem ()
case valueT :
v = reflect .New (t .rtype ).Elem ()
default :
v = zeroValues [t .cat ]
}
return v , err
}
func (t *itype ) fieldIndex (name string ) int {
switch t .cat {
case linkedT , ptrT :
return t .val .fieldIndex (name )
}
for i , field := range t .field {
if name == field .name {
return i
}
}
return -1
}
func (t *itype ) fieldSeq (seq []int ) *itype {
ft := t
for _ , i := range seq {
if ft .cat == ptrT {
ft = ft .val
}
ft = ft .field [i ].typ
}
return ft
}
func (t *itype ) lookupField (name string ) []int {
seen := map [*itype ]bool {}
var lookup func (*itype ) []int
tias := isStruct (t )
lookup = func (typ *itype ) []int {
if seen [typ ] {
return nil
}
seen [typ ] = true
switch typ .cat {
case linkedT , ptrT :
return lookup (typ .val )
}
if fi := typ .fieldIndex (name ); fi >= 0 {
return []int {fi }
}
for i , f := range typ .field {
switch f .typ .cat {
case ptrT , structT , interfaceT , linkedT :
if tias != isStruct (f .typ ) {
break
}
if index2 := lookup (f .typ ); len (index2 ) > 0 {
return append ([]int {i }, index2 ...)
}
}
}
return nil
}
return lookup (t )
}
func (t *itype ) lookupBinField (name string ) (s reflect .StructField , index []int , ok bool ) {
if t .cat == ptrT {
return t .val .lookupBinField (name )
}
if !isStruct (t ) {
return
}
rt := t .TypeOf ()
for t .cat == valueT && rt .Kind () == reflect .Ptr {
rt = rt .Elem ()
}
if rt .Kind () != reflect .Struct {
return
}
s , ok = rt .FieldByName (name )
if !ok {
for i , f := range t .field {
if f .embed {
if s2 , index2 , ok2 := f .typ .lookupBinField (name ); ok2 {
index = append ([]int {i }, index2 ...)
return s2 , index , ok2
}
}
}
}
return s , index , ok
}
func (t *itype ) methodCallType () reflect .Type {
it := []reflect .Type {}
ni := t .rtype .NumIn ()
for i := 1 ; i < ni ; i ++ {
it = append (it , t .rtype .In (i ))
}
ot := []reflect .Type {}
no := t .rtype .NumOut ()
for i := 0 ; i < no ; i ++ {
ot = append (ot , t .rtype .Out (i ))
}
return reflect .FuncOf (it , ot , t .rtype .IsVariadic ())
}
func (t *itype ) resolveAlias () *itype {
for t .cat == linkedT {
t = t .val
}
return t
}
func (t *itype ) getMethod (name string ) *node {
for _ , m := range t .method {
if name == m .ident {
return m
}
}
return nil
}
func (t *itype ) lookupMethod (name string ) (*node , []int ) {
return t .lookupMethod2 (name , nil )
}
func (t *itype ) lookupMethod2 (name string , seen map [*itype ]bool ) (*node , []int ) {
if seen == nil {
seen = map [*itype ]bool {}
}
if seen [t ] {
return nil , nil
}
seen [t ] = true
if t .cat == ptrT {
return t .val .lookupMethod2 (name , seen )
}
var index []int
m := t .getMethod (name )
if m == nil {
for i , f := range t .field {
if f .embed {
if n , index2 := f .typ .lookupMethod2 (name , seen ); n != nil {
index = append ([]int {i }, index2 ...)
return n , index
}
}
}
if t .cat == linkedT || isInterfaceSrc (t ) && t .val != nil {
return t .val .lookupMethod2 (name , seen )
}
}
return m , index
}
func (t *itype ) interfaceMethod (name string ) *itype {
return t .interfaceMethod2 (name , nil )
}
func (t *itype ) interfaceMethod2 (name string , seen map [*itype ]bool ) *itype {
if seen == nil {
seen = map [*itype ]bool {}
}
if seen [t ] {
return nil
}
seen [t ] = true
if t .cat == ptrT {
return t .val .interfaceMethod2 (name , seen )
}
for _ , f := range t .field {
if f .name == name && isInterface (t ) {
return f .typ
}
if !f .embed {
continue
}
if typ := f .typ .interfaceMethod2 (name , seen ); typ != nil {
return typ
}
}
if t .cat == linkedT || isInterfaceSrc (t ) && t .val != nil {
return t .val .interfaceMethod2 (name , seen )
}
return nil
}
func (t *itype ) methodDepth (name string ) int {
if m , lint := t .lookupMethod (name ); m != nil {
return len (lint )
}
if _ , lint , _ , ok := t .lookupBinMethod (name ); ok {
return len (lint )
}
return -1
}
func (t *itype ) lookupBinMethod (name string ) (m reflect .Method , index []int , isPtr , ok bool ) {
return t .lookupBinMethod2 (name , nil )
}
func (t *itype ) lookupBinMethod2 (name string , seen map [*itype ]bool ) (m reflect .Method , index []int , isPtr , ok bool ) {
if seen == nil {
seen = map [*itype ]bool {}
}
if seen [t ] {
return
}
seen [t ] = true
if t .cat == ptrT {
return t .val .lookupBinMethod2 (name , seen )
}
for i , f := range t .field {
if f .embed {
if m2 , index2 , isPtr2 , ok2 := f .typ .lookupBinMethod2 (name , seen ); ok2 {
index = append ([]int {i }, index2 ...)
return m2 , index , isPtr2 , ok2
}
}
}
m , ok = t .TypeOf ().MethodByName (name )
if !ok {
m , ok = reflect .PtrTo (t .TypeOf ()).MethodByName (name )
isPtr = ok
}
return m , index , isPtr , ok
}
func lookupFieldOrMethod(t *itype , name string ) *itype {
switch {
case t .cat == valueT || t .cat == ptrT && t .val .cat == valueT :
m , _ , isPtr , ok := t .lookupBinMethod (name )
if !ok {
return nil
}
var recv *itype
if t .rtype .Kind () != reflect .Interface {
recv = t
if isPtr && t .cat != ptrT && t .rtype .Kind () != reflect .Ptr {
recv = ptrOf (t )
}
}
return valueTOf (m .Type , withRecv (recv ))
case t .cat == interfaceT :
seq := t .lookupField (name )
if seq == nil {
return nil
}
return t .fieldSeq (seq )
default :
n , _ := t .lookupMethod (name )
if n == nil {
return nil
}
return n .typ
}
}
func exportName(s string ) string {
if canExport (s ) {
return s
}
return "X" + s
}
var (
emptyInterfaceType = reflect .TypeOf ((*interface {})(nil )).Elem ()
valueInterfaceType = reflect .TypeOf ((*valueInterface )(nil )).Elem ()
constVal = reflect .TypeOf ((*constant .Value )(nil )).Elem ()
)
type refTypeContext struct {
defined map [string ]*itype
refs map [string ][]*itype
rect *itype
rebuilding bool
slevel int
}
func (c *refTypeContext ) Clone () *refTypeContext {
return &refTypeContext {defined : c .defined , refs : c .refs , rebuilding : c .rebuilding }
}
func (c *refTypeContext ) isComplete () bool {
for _ , t := range c .defined {
if t .rtype == nil {
return false
}
}
return true
}
func (t *itype ) fixDummy (typ reflect .Type ) reflect .Type {
if typ == unsafe2 .DummyType {
return t .rtype
}
switch typ .Kind () {
case reflect .Array :
return reflect .ArrayOf (typ .Len (), t .fixDummy (typ .Elem ()))
case reflect .Chan :
return reflect .ChanOf (typ .ChanDir (), t .fixDummy (typ .Elem ()))
case reflect .Func :
in := make ([]reflect .Type , typ .NumIn ())
for i := range in {
in [i ] = t .fixDummy (typ .In (i ))
}
out := make ([]reflect .Type , typ .NumOut ())
for i := range out {
out [i ] = t .fixDummy (typ .Out (i ))
}
return reflect .FuncOf (in , out , typ .IsVariadic ())
case reflect .Map :
return reflect .MapOf (t .fixDummy (typ .Key ()), t .fixDummy (typ .Elem ()))
case reflect .Ptr :
return reflect .PtrTo (t .fixDummy (typ .Elem ()))
case reflect .Slice :
return reflect .SliceOf (t .fixDummy (typ .Elem ()))
case reflect .Struct :
fields := make ([]reflect .StructField , typ .NumField ())
for i := range fields {
fields [i ] = typ .Field (i )
fields [i ].Type = t .fixDummy (fields [i ].Type )
}
return reflect .StructOf (fields )
}
return typ
}
func (t *itype ) refType (ctx *refTypeContext ) reflect .Type {
if ctx == nil {
ctx = &refTypeContext {
defined : map [string ]*itype {},
refs : map [string ][]*itype {},
}
}
if t .incomplete || t .cat == nilT {
var err error
if t , err = t .finalize (); err != nil {
panic (err )
}
}
name := t .path + "/" + t .name
if t .rtype != nil && !ctx .rebuilding {
return t .rtype
}
if dt := ctx .defined [name ]; dt != nil {
if dt .rtype != nil {
t .rtype = dt .rtype
return dt .rtype
}
flds := ctx .refs [name ]
ctx .rect = dt
ctx .refs [name ] = append (flds , (*itype )(nil ))
return unsafe2 .DummyType
}
if isGeneric (t ) {
return reflect .TypeOf ((*generic )(nil )).Elem ()
}
switch t .cat {
case linkedT :
t .rtype = t .val .refType (ctx )
case arrayT :
t .rtype = reflect .ArrayOf (t .length , t .val .refType (ctx ))
case sliceT , variadicT :
t .rtype = reflect .SliceOf (t .val .refType (ctx ))
case chanT :
t .rtype = reflect .ChanOf (reflect .BothDir , t .val .refType (ctx ))
case chanRecvT :
t .rtype = reflect .ChanOf (reflect .RecvDir , t .val .refType (ctx ))
case chanSendT :
t .rtype = reflect .ChanOf (reflect .SendDir , t .val .refType (ctx ))
case errorT :
t .rtype = reflect .TypeOf (new (error )).Elem ()
case funcT :
variadic := false
in := make ([]reflect .Type , len (t .arg ))
out := make ([]reflect .Type , len (t .ret ))
for i , v := range t .arg {
in [i ] = v .refType (ctx )
variadic = v .cat == variadicT
}
for i , v := range t .ret {
out [i ] = v .refType (ctx )
}
t .rtype = reflect .FuncOf (in , out , variadic )
case interfaceT :
if len (t .field ) == 0 {
t .rtype = emptyInterfaceType
break
}
t .rtype = valueInterfaceType
case mapT :
t .rtype = reflect .MapOf (t .key .refType (ctx ), t .val .refType (ctx ))
case ptrT :
rt := t .val .refType (ctx )
if rt == unsafe2 .DummyType && ctx .slevel > 1 {
return reflect .PtrTo (rt )
}
t .rtype = reflect .PtrTo (rt )
case structT :
if t .name != "" {
ctx .defined [name ] = t
}
ctx .slevel ++
var fields []reflect .StructField
for _ , f := range t .field {
field := reflect .StructField {
Name : exportName (f .name ),
Type : f .typ .refType (ctx ),
Tag : reflect .StructTag (f .tag ),
}
if len (t .field ) == 1 && f .embed {
field .Anonymous = true
}
fields = append (fields , field )
for _ , flds := range ctx .refs {
for j , fld := range flds {
if fld == nil {
flds [j ] = t
}
}
}
}
ctx .slevel --
type fixStructField struct {
name string
index int
}
fieldFix := []fixStructField {}
t .rtype = reflect .StructOf (fields )
if ctx .isComplete () {
for _ , s := range ctx .defined {
for i := 0 ; i < s .rtype .NumField (); i ++ {
f := s .rtype .Field (i )
if strings .HasSuffix (f .Type .String (), "unsafe2.dummy" ) {
unsafe2 .SetFieldType (s .rtype , i , ctx .rect .fixDummy (s .rtype .Field (i ).Type ))
if name == s .path +"/" +s .name {
fieldFix = append (fieldFix , fixStructField {s .name , i })
}
continue
}
if f .Type .Kind () == reflect .Func && strings .Contains (f .Type .String (), "unsafe2.dummy" ) {
fieldFix = append (fieldFix , fixStructField {s .name , i })
}
}
}
}
for _ , f := range ctx .refs [name ] {
for _ , ff := range fieldFix {
if ff .name == f .name {
ftyp := f .field [ff .index ].typ .refType (&refTypeContext {defined : ctx .defined , rebuilding : true })
unsafe2 .SetFieldType (f .rtype , ff .index , ftyp )
}
}
}
default :
if z , _ := t .zero (); z .IsValid () {
t .rtype = z .Type ()
}
}
return t .rtype
}
func (t *itype ) TypeOf () reflect .Type {
return t .refType (nil )
}
func (t *itype ) frameType () (r reflect .Type ) {
var err error
if t , err = t .finalize (); err != nil {
panic (err )
}
switch t .cat {
case linkedT :
r = t .val .frameType ()
case arrayT :
r = reflect .ArrayOf (t .length , t .val .frameType ())
case sliceT , variadicT :
r = reflect .SliceOf (t .val .frameType ())
case interfaceT :
if len (t .field ) == 0 {
r = emptyInterfaceType
break
}
r = valueInterfaceType
case mapT :
r = reflect .MapOf (t .key .frameType (), t .val .frameType ())
case ptrT :
r = reflect .PtrTo (t .val .frameType ())
default :
r = t .TypeOf ()
}
return r
}
func (t *itype ) implements (it *itype ) bool {
if isBin (t ) {
if rt := it .TypeOf (); rt == valueInterfaceType {
return true
}
return t .TypeOf ().Implements (it .TypeOf ())
}
return t .methods ().contains (it .methods ())
}
func (t *itype ) defaultType (v reflect .Value , sc *scope ) *itype {
if !t .untyped {
return t
}
typ := t
if v .IsValid () && v .Type ().Implements (constVal ) {
switch v .Interface ().(constant .Value ).Kind () {
case constant .String :
typ = sc .getType ("string" )
case constant .Bool :
typ = sc .getType ("bool" )
case constant .Int :
switch t .cat {
case int32T :
typ = sc .getType ("int32" )
default :
typ = sc .getType ("int" )
}
case constant .Float :
typ = sc .getType ("float64" )
case constant .Complex :
typ = sc .getType ("complex128" )
}
}
if typ .untyped {
switch t .cat {
case stringT :
typ = sc .getType ("string" )
case boolT :
typ = sc .getType ("bool" )
case intT :
typ = sc .getType ("int" )
case float64T :
typ = sc .getType ("float64" )
case complex128T :
typ = sc .getType ("complex128" )
default :
*typ = *t
typ .untyped = false
}
}
return typ
}
func (t *itype ) isNil () bool { return t .cat == nilT }
func (t *itype ) hasNil () bool {
switch rt := t .TypeOf (); rt .Kind () {
case reflect .UnsafePointer :
return true
case reflect .Slice , reflect .Ptr , reflect .Func , reflect .Interface , reflect .Map , reflect .Chan :
return true
case reflect .Struct :
if rt == valueInterfaceType {
return true
}
}
return false
}
func (t *itype ) elem () *itype {
if t .cat == valueT {
return valueTOf (t .rtype .Elem ())
}
return t .val
}
func hasElem(t reflect .Type ) bool {
switch t .Kind () {
case reflect .Array , reflect .Chan , reflect .Map , reflect .Ptr , reflect .Slice :
return true
}
return false
}
func constToInt(c constant .Value ) int {
if constant .BitLen (c ) > 64 {
panic (fmt .Sprintf ("constant %s overflows int64" , c .ExactString ()))
}
i , _ := constant .Int64Val (c )
return int (i )
}
func constToString(v reflect .Value ) string {
c := v .Interface ().(constant .Value )
return constant .StringVal (c )
}
func wrappedType(n *node ) *itype {
if n .typ .cat != valueT {
return nil
}
return n .typ .val
}
func isShiftNode(n *node ) bool {
switch n .action {
case aShl , aShr , aShlAssign , aShrAssign :
return true
}
return false
}
func chanElement(t *itype ) *itype {
switch t .cat {
case linkedT :
return chanElement (t .val )
case chanT , chanSendT , chanRecvT :
return t .val
case valueT :
return valueTOf (t .rtype .Elem (), withNode (t .node ), withScope (t .scope ))
}
return nil
}
func isBool(t *itype ) bool { return t .TypeOf ().Kind () == reflect .Bool }
func isChan(t *itype ) bool { return t .TypeOf ().Kind () == reflect .Chan }
func isFunc(t *itype ) bool { return t .TypeOf ().Kind () == reflect .Func }
func isMap(t *itype ) bool { return t .TypeOf ().Kind () == reflect .Map }
func isPtr(t *itype ) bool { return t .TypeOf ().Kind () == reflect .Ptr }
func isEmptyInterface(t *itype ) bool {
return t != nil && t .cat == interfaceT && len (t .field ) == 0
}
func isGeneric(t *itype ) bool {
return t .cat == funcT && t .node != nil && len (t .node .child ) > 0 && len (t .node .child [0 ].child ) > 0
}
func isNamedFuncSrc(t *itype ) bool {
return isFuncSrc (t ) && t .node .anc .kind == funcDecl
}
func isFuncSrc(t *itype ) bool {
return t .cat == funcT || (t .cat == linkedT && isFuncSrc (t .val ))
}
func isPtrSrc(t *itype ) bool {
return t .cat == ptrT || (t .cat == linkedT && isPtrSrc (t .val ))
}
func isSendChan(t *itype ) bool {
rt := t .TypeOf ()
return rt .Kind () == reflect .Chan && rt .ChanDir () == reflect .SendDir
}
func isArray(t *itype ) bool {
if t .cat == nilT {
return false
}
k := t .TypeOf ().Kind ()
return k == reflect .Array || k == reflect .Slice
}
func isInterfaceSrc(t *itype ) bool {
return t .cat == interfaceT || (t .cat == linkedT && isInterfaceSrc (t .val ))
}
func isInterfaceBin(t *itype ) bool {
return t .cat == valueT && t .rtype .Kind () == reflect .Interface || t .cat == errorT
}
func isInterface(t *itype ) bool {
return isInterfaceSrc (t ) || t .TypeOf () == valueInterfaceType || t .TypeOf () != nil && t .TypeOf ().Kind () == reflect .Interface
}
func isBin(t *itype ) bool {
switch t .cat {
case valueT :
return true
case linkedT , ptrT :
return isBin (t .val )
default :
return false
}
}
func isStruct(t *itype ) bool {
switch t .cat {
case structT :
return true
case linkedT , ptrT :
return isStruct (t .val )
case valueT :
k := t .rtype .Kind ()
return k == reflect .Struct || (k == reflect .Ptr && t .rtype .Elem ().Kind () == reflect .Struct )
default :
return false
}
}
func isConstType(t *itype ) bool {
rt := t .TypeOf ()
return isBoolean (rt ) || isString (rt ) || isNumber (rt )
}
func isInt(t reflect .Type ) bool {
if t == nil {
return false
}
switch t .Kind () {
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 , reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
return true
}
return false
}
func isUint(t reflect .Type ) bool {
if t == nil {
return false
}
switch t .Kind () {
case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
return true
}
return false
}
func isComplex(t reflect .Type ) bool {
if t == nil {
return false
}
switch t .Kind () {
case reflect .Complex64 , reflect .Complex128 :
return true
}
return false
}
func isFloat(t reflect .Type ) bool {
if t == nil {
return false
}
switch t .Kind () {
case reflect .Float32 , reflect .Float64 :
return true
}
return false
}
func isByteArray(t reflect .Type ) bool {
if t == nil {
return false
}
k := t .Kind ()
return (k == reflect .Array || k == reflect .Slice ) && t .Elem ().Kind () == reflect .Uint8
}
func isFloat32(t reflect .Type ) bool { return t != nil && t .Kind () == reflect .Float32 }
func isFloat64(t reflect .Type ) bool { return t != nil && t .Kind () == reflect .Float64 }
func isNumber(t reflect .Type ) bool {
return isInt (t ) || isFloat (t ) || isComplex (t ) || isConstantValue (t )
}
func isBoolean(t reflect .Type ) bool { return t != nil && t .Kind () == reflect .Bool }
func isString(t reflect .Type ) bool { return t != nil && t .Kind () == reflect .String }
func isConstantValue(t reflect .Type ) bool { return t != nil && t .Implements (constVal ) }
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .