package interp
import (
"fmt"
"go/constant"
"log"
"math"
"path/filepath"
"reflect"
"strings"
"unicode"
)
type cfgError struct {
*node
error
}
func (c *cfgError ) Error () string { return c .error .Error() }
var constOp = map [action ]func (*node ){
aAdd : addConst ,
aSub : subConst ,
aMul : mulConst ,
aQuo : quoConst ,
aRem : remConst ,
aAnd : andConst ,
aOr : orConst ,
aShl : shlConst ,
aShr : shrConst ,
aAndNot : andNotConst ,
aXor : xorConst ,
aNot : notConst ,
aBitNot : bitNotConst ,
aNeg : negConst ,
aPos : posConst ,
}
var constBltn = map [string ]func (*node ){
bltnComplex : complexConst ,
bltnImag : imagConst ,
bltnReal : realConst ,
}
const nilIdent = "nil"
func init() {
constBltn [bltnAlignof ] = alignof
constBltn [bltnOffsetof ] = offsetof
constBltn [bltnSizeof ] = sizeof
}
func (interp *Interpreter ) cfg (root *node , sc *scope , importPath , pkgName string ) ([]*node , error ) {
if sc == nil {
sc = interp .initScopePkg (importPath , pkgName )
}
check := typecheck {scope : sc }
var initNodes []*node
var err error
baseName := filepath .Base (interp .fset .Position (root .pos ).Filename )
root .Walk (func (n *node ) bool {
if err != nil {
return false
}
if n .scope == nil {
n .scope = sc
}
switch n .kind {
case binaryExpr , unaryExpr , parenExpr :
if isBoolAction (n ) {
break
}
switch n .anc .kind {
case assignStmt , defineStmt :
a := n .anc
i := childPos (n ) - a .nright
if i < 0 {
break
}
if len (a .child ) > a .nright +a .nleft {
i --
}
dest := a .child [i ]
if dest .typ == nil {
break
}
if dest .typ .incomplete {
err = n .cfgErrorf ("invalid type declaration" )
return false
}
if !isInterface (dest .typ ) {
n .typ = dest .typ
}
case binaryExpr , unaryExpr , parenExpr :
n .typ = n .anc .typ
}
case defineStmt :
if n .nleft +n .nright == len (n .child ) {
break
}
n .typ , err = nodeType (interp , sc , n .child [n .nleft ])
if err != nil {
break
}
for i := 0 ; i < n .nleft ; i ++ {
n .child [i ].typ = n .typ
}
case blockStmt :
if n .anc != nil && n .anc .kind == rangeStmt {
c := n .anc .child [1 ]
if c != nil && c .typ != nil && isSendChan (c .typ ) {
err = c .cfgErrorf ("invalid operation: range %s receive from send-only channel" , c .ident )
return false
}
if t := sc .rangeChanType (n .anc ); t != nil {
e := n .anc .child [0 ]
index := sc .add (t .val )
sc .sym [e .ident ] = &symbol {index : index , kind : varSym , typ : t .val }
e .typ = t .val
e .findex = index
n .anc .gen = rangeChan
} else {
var ktyp , vtyp *itype
var k , v , o *node
if len (n .anc .child ) == 4 {
k , v , o = n .anc .child [0 ], n .anc .child [1 ], n .anc .child [2 ]
} else {
k , o = n .anc .child [0 ], n .anc .child [1 ]
}
switch o .typ .cat {
case valueT , linkedT :
typ := o .typ .rtype
if o .typ .cat == linkedT {
typ = o .typ .val .TypeOf ()
}
switch typ .Kind () {
case reflect .Map :
n .anc .gen = rangeMap
ityp := valueTOf (reflect .TypeOf ((*reflect .MapIter )(nil )))
sc .add (ityp )
ktyp = valueTOf (typ .Key ())
vtyp = valueTOf (typ .Elem ())
case reflect .String :
sc .add (sc .getType ("int" ))
sc .add (sc .getType ("int" ))
ktyp = sc .getType ("int" )
vtyp = sc .getType ("rune" )
case reflect .Array , reflect .Slice :
sc .add (sc .getType ("int" ))
ktyp = sc .getType ("int" )
vtyp = valueTOf (typ .Elem ())
}
case mapT :
n .anc .gen = rangeMap
ityp := valueTOf (reflect .TypeOf ((*reflect .MapIter )(nil )))
sc .add (ityp )
ktyp = o .typ .key
vtyp = o .typ .val
case ptrT :
ktyp = sc .getType ("int" )
vtyp = o .typ .val
if vtyp .cat == valueT {
vtyp = valueTOf (vtyp .rtype .Elem ())
} else {
vtyp = vtyp .val
}
case stringT :
sc .add (sc .getType ("int" ))
sc .add (sc .getType ("int" ))
ktyp = sc .getType ("int" )
vtyp = sc .getType ("rune" )
case arrayT , sliceT , variadicT :
sc .add (sc .getType ("int" ))
ktyp = sc .getType ("int" )
vtyp = o .typ .val
}
kindex := sc .add (ktyp )
sc .sym [k .ident ] = &symbol {index : kindex , kind : varSym , typ : ktyp }
k .typ = ktyp
k .findex = kindex
if v != nil {
vindex := sc .add (vtyp )
sc .sym [v .ident ] = &symbol {index : vindex , kind : varSym , typ : vtyp }
v .typ = vtyp
v .findex = vindex
}
}
}
n .findex = -1
n .val = nil
sc = sc .pushBloc ()
for _ , c := range n .child {
if c .kind != labeledStmt {
continue
}
label := c .child [0 ].ident
sym := &symbol {kind : labelSym , node : c , index : -1 }
sc .sym [label ] = sym
c .sym = sym
}
if n .anc != nil && n .anc .kind == funcDecl {
for k , v := range sc .anc .sym {
sc .sym [k ] = v
}
}
case breakStmt , continueStmt , gotoStmt :
if len (n .child ) == 0 {
break
}
label := n .child [0 ].ident
if sym , _ , ok := sc .lookup (label ); ok {
if sym .kind != labelSym {
err = n .child [0 ].cfgErrorf ("label %s not defined" , label )
break
}
n .sym = sym
} else {
n .sym = &symbol {kind : labelSym , index : -1 }
sc .sym [label ] = n .sym
}
if n .kind == gotoStmt {
n .sym .from = append (n .sym .from , n )
}
case caseClause :
sc = sc .pushBloc ()
if sn := n .anc .anc ; sn .kind == typeSwitch && sn .child [1 ].action == aAssign {
var typ *itype
if len (n .child ) == 2 {
switch {
case n .child [0 ].ident == nilIdent :
typ = sc .getType ("interface{}" )
case !n .child [0 ].isType (sc ):
err = n .cfgErrorf ("%s is not a type" , n .child [0 ].ident )
default :
typ , err = nodeType (interp , sc , n .child [0 ])
}
} else {
typ = sn .child [1 ].child [1 ].child [0 ].typ
}
if err != nil {
return false
}
nod := n .lastChild ().child [0 ]
index := sc .add (typ )
sc .sym [nod .ident ] = &symbol {index : index , kind : varSym , typ : typ }
nod .findex = index
nod .typ = typ
}
case commClauseDefault :
sc = sc .pushBloc ()
case commClause :
sc = sc .pushBloc ()
if len (n .child ) > 0 && n .child [0 ].action == aAssign {
ch := n .child [0 ].child [1 ].child [0 ]
var typ *itype
if typ , err = nodeType (interp , sc , ch ); err != nil {
return false
}
if !isChan (typ ) {
err = n .cfgErrorf ("invalid operation: receive from non-chan type" )
return false
}
elem := chanElement (typ )
assigned := n .child [0 ].child [0 ]
index := sc .add (elem )
sc .sym [assigned .ident ] = &symbol {index : index , kind : varSym , typ : elem }
assigned .findex = index
assigned .typ = elem
}
case compositeLitExpr :
if len (n .child ) > 0 && n .child [0 ].isType (sc ) {
if n .typ , err = nodeType (interp , sc , n .child [0 ]); err != nil {
return false
}
n .nleft = 1
} else {
if n .anc .kind == keyValueExpr && n == n .anc .child [0 ] {
n .typ = n .anc .typ .key
} else if atyp := n .anc .typ ; atyp != nil {
if atyp .cat == valueT && hasElem (atyp .rtype ) {
n .typ = valueTOf (atyp .rtype .Elem ())
} else {
n .typ = atyp .val
}
}
if n .typ == nil {
if n .child [0 ].kind != indexExpr && n .child [0 ].kind != indexListExpr {
err = n .cfgErrorf ("undefined type" )
return false
}
t0 , err1 := nodeType (interp , sc , n .child [0 ].child [0 ])
if err1 != nil {
return false
}
if t0 .cat != genericT {
err = n .cfgErrorf ("undefined type" )
return false
}
lt := []*itype {}
for _ , n1 := range n .child [0 ].child [1 :] {
t1 , err1 := nodeType (interp , sc , n1 )
if err1 != nil {
return false
}
lt = append (lt , t1 )
}
var g *node
g , _, err = genAST (sc , t0 .node .anc , lt )
if err != nil {
return false
}
n .child [0 ] = g .lastChild ()
n .typ , err = nodeType (interp , sc , n .child [0 ])
if err != nil {
return false
}
for _ , nod := range t0 .method {
gm , _ , err2 := genAST (nod .scope , nod , lt )
if err2 != nil {
err = err2
return false
}
gm .typ , err = nodeType (interp , nod .scope , gm .child [2 ])
if err != nil {
return false
}
if _, err = interp .cfg (gm , sc , sc .pkgID , sc .pkgName ); err != nil {
return false
}
if err = genRun (gm ); err != nil {
return false
}
n .typ .addMethod (gm )
}
n .nleft = 1
}
}
child := n .child
if n .nleft > 0 {
n .child [0 ].typ = n .typ
child = n .child [1 :]
}
for _ , c := range child {
if isBlank (c ) {
err = n .cfgErrorf ("cannot use _ as value" )
return false
}
switch c .kind {
case binaryExpr , unaryExpr , compositeLitExpr :
case keyValueExpr , typeAssertExpr , indexExpr :
c .typ = n .typ
default :
if c .ident == nilIdent {
c .typ = sc .getType (nilIdent )
continue
}
if c .typ , err = nodeType (interp , sc , c ); err != nil {
return false
}
}
}
case forStmt0 , forStmt1 , forStmt2 , forStmt3 , forStmt4 , forStmt5 , forStmt6 , forStmt7 , forRangeStmt :
sc = sc .pushBloc ()
sc .loop , sc .loopRestart = n , n .lastChild ()
case funcLit :
n .typ = nil
if n .typ , err = nodeType (interp , sc , n ); err != nil {
return false
}
n .findex = sc .add (n .typ )
fallthrough
case funcDecl :
if len (n .child ) < 4 {
err = n .cfgErrorf ("missing function body" )
return false
}
n .val = n
if len (n .child [2 ].child [0 ].child ) > 0 {
return false
}
if len (n .child [0 ].child ) > 0 {
recvTypeNode := n .child [0 ].child [0 ].lastChild ()
typ , err := nodeType (interp , sc , recvTypeNode )
if err != nil {
return false
}
if typ .cat == genericT || (typ .val != nil && typ .val .cat == genericT ) {
return false
}
if typ .cat == ptrT {
rc0 := recvTypeNode .child [0 ]
rt0 , err := nodeType (interp , sc , rc0 )
if err != nil {
return false
}
if rc0 .kind == indexExpr && rt0 .cat == structT {
return false
}
}
}
n .child [2 ].typ , err = nodeType (interp , sc , n .child [2 ])
if err != nil {
return false
}
n .typ = n .child [2 ].typ
sc = sc .pushFunc ()
sc .def = n
if len (n .child [2 ].child ) == 3 {
for _ , c := range n .child [2 ].child [2 ].child {
var typ *itype
if typ , err = nodeType (interp , sc , c .lastChild ()); err != nil {
return false
}
if len (c .child ) > 1 {
for _ , cc := range c .child [:len (c .child )-1 ] {
sc .sym [cc .ident ] = &symbol {index : sc .add (typ ), kind : varSym , typ : typ }
}
} else {
sc .add (typ )
}
}
}
if len (n .child [0 ].child ) > 0 {
var typ *itype
fr := n .child [0 ].child [0 ]
recvTypeNode := fr .lastChild ()
if typ , err = nodeType (interp , sc , recvTypeNode ); err != nil {
return false
}
if typ .cat == nilT {
s2 , _ , ok := sc .lookup (typ .id ())
if !ok {
err = n .cfgErrorf ("type not found: %s" , typ .id ())
break
}
typ = s2 .typ
if typ .cat == nilT {
err = n .cfgErrorf ("nil type: %s" , typ .id ())
break
}
}
recvTypeNode .typ = typ
n .child [2 ].typ .recv = typ
n .typ .recv = typ
index := sc .add (typ )
if len (fr .child ) > 1 {
sc .sym [fr .child [0 ].ident ] = &symbol {index : index , kind : varSym , typ : typ }
}
}
for _ , c := range n .child [2 ].child [1 ].child {
var typ *itype
if typ , err = nodeType (interp , sc , c .lastChild ()); err != nil {
return false
}
for _ , cc := range c .child [:len (c .child )-1 ] {
sc .sym [cc .ident ] = &symbol {index : sc .add (typ ), kind : varSym , typ : typ }
}
}
if n .child [1 ].ident == "init" && len (n .child [0 ].child ) == 0 {
initNodes = append (initNodes , n )
}
case ifStmt0 , ifStmt1 , ifStmt2 , ifStmt3 :
sc = sc .pushBloc ()
case switchStmt , switchIfStmt , typeSwitch :
c := n .lastChild ().child
if i , l := getDefault (n ), len (c )-1 ; i >= 0 && i != l {
c [i ], c [l ] = c [l ], c [i ]
}
sc = sc .pushBloc ()
sc .loop = n
case importSpec :
return false
case typeSpec :
if sc .def == nil {
return false
}
typeName := n .child [0 ].ident
var typ *itype
if typ , err = nodeType (interp , sc , n .child [1 ]); err != nil {
return false
}
if typ .incomplete {
if typ , err = typ .finalize (); err != nil {
err = n .cfgErrorf ("invalid type declaration" )
return false
}
}
switch n .child [1 ].kind {
case identExpr , selectorExpr :
n .typ = namedOf (typ , pkgName , typeName )
default :
n .typ = typ
n .typ .name = typeName
}
sc .sym [typeName ] = &symbol {kind : typeSym , typ : n .typ }
return false
case constDecl :
if !sc .global {
for _ , c := range n .child {
if _, err = interp .cfg (c , sc , importPath , pkgName ); err != nil {
err = nil
}
}
}
case arrayType , basicLit , chanType , chanTypeRecv , chanTypeSend , funcType , interfaceType , mapType , structType :
n .typ , err = nodeType (interp , sc , n )
return false
}
return true
}, func (n *node ) {
if err != nil {
return
}
defer func () {
if r := recover (); r != nil {
panic (n .cfgErrorf ("CFG post-order panic: %v" , r ))
}
}()
switch n .kind {
case addressExpr :
if isBlank (n .child [0 ]) {
err = n .cfgErrorf ("cannot use _ as value" )
break
}
wireChild (n )
err = check .addressExpr (n )
if err != nil {
break
}
n .typ = ptrOf (n .child [0 ].typ )
n .findex = sc .add (n .typ )
case assignStmt , defineStmt :
if n .anc .kind == typeSwitch && n .anc .child [1 ] == n {
n .gen = nop
break
}
var atyp *itype
if n .nleft +n .nright < len (n .child ) {
if atyp , err = nodeType (interp , sc , n .child [n .nleft ]); err != nil {
break
}
}
var sbase int
if n .nright > 0 {
sbase = len (n .child ) - n .nright
}
wireChild (n )
for i := 0 ; i < n .nleft ; i ++ {
dest , src := n .child [i ], n .child [sbase +i ]
updateSym := false
var sym *symbol
var level int
if dest .rval .IsValid () && isConstType (dest .typ ) {
err = n .cfgErrorf ("cannot assign to %s (%s constant)" , dest .rval , dest .typ .str )
break
}
if isBlank (src ) {
err = n .cfgErrorf ("cannot use _ as value" )
break
}
if n .kind == defineStmt || (n .kind == assignStmt && dest .ident == "_" ) {
if atyp != nil {
dest .typ = atyp
} else {
if src .typ , err = nodeType (interp , sc , src ); err != nil {
return
}
if src .typ .isBinMethod {
dest .typ = valueTOf (src .typ .methodCallType ())
} else {
dest .typ = sc .fixType (src .typ )
}
}
if dest .typ .incomplete {
return
}
if sc .global {
sym , _, _ = sc .lookup (dest .ident )
}
if sym == nil {
sym = &symbol {index : sc .add (dest .typ ), kind : varSym , typ : dest .typ }
sc .sym [dest .ident ] = sym
}
dest .val = src .val
dest .recv = src .recv
dest .findex = sym .index
updateSym = true
} else {
sym , level , _ = sc .lookup (dest .ident )
}
err = check .assignExpr (n , dest , src )
if err != nil {
break
}
if updateSym {
sym .typ = dest .typ
sym .rval = src .rval
if sym .index >= 0 {
sc .types [sym .index ] = sym .typ .frameType ()
}
}
n .findex = dest .findex
n .level = dest .level
switch {
case n .action != aAssign :
case src .rval .IsValid ():
case isMapEntry (dest ):
dest .gen = nop
case isFuncField (dest ):
case isCall (src ) && !isInterfaceSrc (dest .typ ) && n .kind != defineStmt :
if dest .typ .id () != src .typ .id () {
break
}
n .gen = nop
src .level = level
src .findex = dest .findex
if src .typ .untyped && !dest .typ .untyped {
src .typ = dest .typ
}
case src .action == aRecv :
n .gen = nop
src .findex = dest .findex
dest .typ = src .typ
case src .action == aCompositeLit :
if dest .typ .cat == valueT && dest .typ .rtype .Kind () == reflect .Interface {
break
}
if dest .action == aGetIndex || dest .action == aStar {
break
}
n .gen = nop
src .findex = dest .findex
src .level = level
case len (n .child ) < 4 && n .kind != defineStmt && isArithmeticAction (src ) && !isInterface (dest .typ ):
src .typ = dest .typ
src .findex = dest .findex
src .level = level
n .gen = nop
case src .kind == basicLit :
src .rval = reflect .New (dest .typ .TypeOf ()).Elem ()
case n .nright == 0 :
n .gen = reset
}
n .typ = dest .typ
if sym != nil {
sym .typ = n .typ
sym .recv = src .recv
}
n .level = level
if n .anc .kind == constDecl {
n .gen = nop
n .findex = notInFrame
if sym , _ , ok := sc .lookup (dest .ident ); ok {
sym .kind = constSym
}
if childPos (n ) == len (n .anc .child )-1 {
sc .iota = 0
} else {
sc .iota ++
}
}
}
case incDecStmt :
err = check .unaryExpr (n )
if err != nil {
break
}
wireChild (n )
n .findex = n .child [0 ].findex
n .level = n .child [0 ].level
n .typ = n .child [0 ].typ
if sym , level , ok := sc .lookup (n .child [0 ].ident ); ok {
sym .typ = n .typ
n .level = level
}
case assignXStmt :
wireChild (n )
l := len (n .child ) - 1
switch lc := n .child [l ]; lc .kind {
case callExpr :
if n .child [l -1 ].isType (sc ) {
l --
}
if r := lc .child [0 ].typ .numOut (); r != l {
err = n .cfgErrorf ("assignment mismatch: %d variables but %s returns %d values" , l , lc .child [0 ].name (), r )
}
if isBinCall (lc , sc ) {
n .gen = nop
} else {
n .gen = assignFromCall
}
case indexExpr :
lc .gen = getIndexMap2
n .gen = nop
case typeAssertExpr :
if n .child [0 ].ident == "_" {
lc .gen = typeAssertStatus
} else {
lc .gen = typeAssertLong
}
n .gen = nop
case unaryExpr :
if lc .action == aRecv {
lc .gen = recv2
n .gen = nop
}
}
case defineXStmt :
wireChild (n )
if sc .def == nil {
break
}
err = compDefineX (sc , n )
case binaryExpr :
wireChild (n )
nilSym := interp .universe .sym [nilIdent ]
c0 , c1 := n .child [0 ], n .child [1 ]
err = check .binaryExpr (n )
if err != nil {
break
}
switch n .action {
case aRem :
n .typ = c0 .typ
case aShl , aShr :
if c0 .typ .untyped {
break
}
n .typ = c0 .typ
case aEqual , aNotEqual :
n .typ = sc .getType ("bool" )
if c0 .sym == nilSym || c1 .sym == nilSym {
if n .action == aEqual {
if c1 .sym == nilSym {
n .gen = isNilChild (0 )
} else {
n .gen = isNilChild (1 )
}
} else {
n .gen = isNotNil
}
}
case aGreater , aGreaterEqual , aLower , aLowerEqual :
n .typ = sc .getType ("bool" )
}
if err != nil {
break
}
if n .typ == nil {
if n .typ , err = nodeType (interp , sc , n ); err != nil {
break
}
}
if c0 .rval .IsValid () && c1 .rval .IsValid () && (!isInterface (n .typ )) && constOp [n .action ] != nil {
n .typ .TypeOf ()
constOp [n .action ](n )
}
switch {
case n .rval .IsValid ():
n .gen = nop
n .findex = notInFrame
case n .anc .kind == assignStmt && n .anc .action == aAssign && n .anc .nleft == 1 :
dest := n .anc .child [childPos (n )-n .anc .nright ]
n .typ = dest .typ
n .findex = dest .findex
n .level = dest .level
case n .anc .kind == returnStmt :
n .findex = childPos (n )
default :
n .findex = sc .add (n .typ )
}
case indexExpr :
if isBlank (n .child [0 ]) {
err = n .cfgErrorf ("cannot use _ as value" )
break
}
wireChild (n )
t := n .child [0 ].typ
for t .cat == linkedT {
t = t .val
}
switch t .cat {
case ptrT :
n .typ = t .val
if t .val .cat == valueT {
n .typ = valueTOf (t .val .rtype .Elem ())
} else {
n .typ = t .val .val
}
case stringT :
n .typ = sc .getType ("byte" )
case valueT :
if t .rtype .Kind () == reflect .String {
n .typ = sc .getType ("byte" )
} else {
n .typ = valueTOf (t .rtype .Elem ())
}
case funcT :
c1 := n .child [1 ]
if !c1 .isType (sc ) {
n .typ = t
return
}
g , found , err := genAST (sc , t .node .anc , []*itype {c1 .typ })
if err != nil {
return
}
if !found {
if _, err = interp .cfg (g , t .node .anc .scope , importPath , pkgName ); err != nil {
return
}
if err = genRun (g .child [3 ]); err != nil {
return
}
}
n .anc .child [childPos (n )] = g
n .typ = g .typ
return
case genericT :
name := t .id () + "[" + n .child [1 ].typ .id () + "]"
sym , _ , ok := sc .lookup (name )
if !ok {
err = n .cfgErrorf ("type not found: %s" , name )
return
}
n .gen = nop
n .typ = sym .typ
return
case structT :
name := t .name + "[" + n .child [1 ].ident + "]"
sym , _ , ok := sc .lookup (name )
if ok {
n .typ = sym .typ
n .findex = sc .add (n .typ )
n .gen = nop
return
}
default :
n .typ = t .val
}
n .findex = sc .add (n .typ )
typ := t .TypeOf ()
if typ .Kind () == reflect .Map {
err = check .assignment (n .child [1 ], t .key , "map index" )
n .gen = getIndexMap
break
}
l := -1
switch k := typ .Kind (); k {
case reflect .Array :
l = typ .Len ()
fallthrough
case reflect .Slice , reflect .String :
n .gen = getIndexArray
case reflect .Ptr :
if typ2 := typ .Elem (); typ2 .Kind () == reflect .Array {
l = typ2 .Len ()
n .gen = getIndexArray
} else {
err = n .cfgErrorf ("type %v does not support indexing" , typ )
}
default :
err = n .cfgErrorf ("type is not an array, slice, string or map: %v" , t .id ())
}
err = check .index (n .child [1 ], l )
case blockStmt :
wireChild (n )
if len (n .child ) > 0 {
l := n .lastChild ()
n .findex = l .findex
n .level = l .level
n .val = l .val
n .sym = l .sym
n .typ = l .typ
n .rval = l .rval
}
sc = sc .pop ()
case constDecl :
wireChild (n )
case varDecl :
if n .anc .kind == fileStmt {
break
}
wireChild (n )
case sendStmt :
if !isChan (n .child [0 ].typ ) {
err = n .cfgErrorf ("invalid operation: cannot send to non-channel %s" , n .child [0 ].typ .id ())
break
}
fallthrough
case declStmt , exprStmt :
wireChild (n )
l := n .lastChild ()
n .findex = l .findex
n .level = l .level
n .val = l .val
n .sym = l .sym
n .typ = l .typ
n .rval = l .rval
case breakStmt :
if len (n .child ) == 0 {
n .tnext = sc .loop
break
}
if !n .hasAnc (n .sym .node ) {
err = n .cfgErrorf ("invalid break label %s" , n .child [0 ].ident )
break
}
n .tnext = n .sym .node
case continueStmt :
if len (n .child ) == 0 {
n .tnext = sc .loopRestart
break
}
if !n .hasAnc (n .sym .node ) {
err = n .cfgErrorf ("invalid continue label %s" , n .child [0 ].ident )
break
}
n .tnext = n .sym .node .child [1 ].lastChild ().start
case gotoStmt :
if n .sym .node == nil {
break
}
n .tnext = n .sym .node .start
case labeledStmt :
wireChild (n )
if len (n .child ) > 1 {
n .start = n .child [1 ].start
}
for _ , c := range n .sym .from {
c .tnext = n .start
}
case callExpr :
for _ , c := range n .child {
if isBlank (c ) {
err = n .cfgErrorf ("cannot use _ as value" )
return
}
}
wireChild (n )
switch c0 := n .child [0 ]; {
case c0 .kind == indexListExpr :
fun := c0 .child [0 ].sym .node
lt := []*itype {}
for _ , c := range c0 .child [1 :] {
lt = append (lt , c .typ )
}
g , found , err := genAST (sc , fun , lt )
if err != nil {
return
}
if !found {
_, err = interp .cfg (g , fun .scope , importPath , pkgName )
if err != nil {
return
}
err = genRun (g .child [3 ])
if err != nil {
return
}
}
n .child [0 ] = g
c0 = n .child [0 ]
wireChild (n )
if typ := c0 .typ ; len (typ .ret ) > 0 {
n .typ = typ .ret [0 ]
if n .anc .kind == returnStmt && n .typ .id () == sc .def .typ .ret [0 ].id () {
n .findex = childPos (n )
} else {
n .findex = sc .add (n .typ )
for _ , t := range typ .ret [1 :] {
sc .add (t )
}
}
} else {
n .findex = notInFrame
}
case isBuiltinCall (n , sc ):
bname := c0 .ident
err = check .builtin (bname , n , n .child [1 :], n .action == aCallSlice )
if err != nil {
break
}
n .gen = c0 .sym .builtin
c0 .typ = &itype {cat : builtinT , name : bname }
if n .typ , err = nodeType (interp , sc , n ); err != nil {
return
}
switch {
case n .typ .cat == builtinT :
n .findex = notInFrame
n .val = nil
switch bname {
case "unsafe.alignOf" , "unsafe.Offsetof" , "unsafe.Sizeof" :
n .gen = nop
}
case n .anc .kind == returnStmt :
n .findex = 0
case bname == "cap" && isInConstOrTypeDecl (n ):
t := n .child [1 ].typ .TypeOf ()
for t .Kind () == reflect .Ptr {
t = t .Elem ()
}
switch t .Kind () {
case reflect .Array , reflect .Chan :
capConst (n )
default :
err = n .cfgErrorf ("cap argument is not an array or channel" )
}
n .findex = notInFrame
n .gen = nop
case bname == "len" && isInConstOrTypeDecl (n ):
t := n .child [1 ].typ .TypeOf ()
for t .Kind () == reflect .Ptr {
t = t .Elem ()
}
switch t .Kind () {
case reflect .Array , reflect .Chan , reflect .String :
lenConst (n )
default :
err = n .cfgErrorf ("len argument is not an array, channel or string" )
}
n .findex = notInFrame
n .gen = nop
default :
n .findex = sc .add (n .typ )
}
if op , ok := constBltn [bname ]; ok {
op (n )
}
case c0 .isType (sc ):
c1 := n .child [1 ]
switch len (n .child ) {
case 1 :
err = n .cfgErrorf ("missing argument in conversion to %s" , c0 .typ .id ())
case 2 :
err = check .conversion (c1 , c0 .typ )
default :
err = n .cfgErrorf ("too many arguments in conversion to %s" , c0 .typ .id ())
}
if err != nil {
break
}
n .action = aConvert
switch {
case isInterface (c0 .typ ) && !c1 .isNil ():
if !c1 .typ .implements (c0 .typ ) {
err = n .cfgErrorf ("type %v does not implement interface %v" , c1 .typ .id (), c0 .typ .id ())
}
n .gen = nop
t := *c0 .typ
n .typ = &t
n .typ .val = c1 .typ
n .findex = c1 .findex
n .level = c1 .level
n .val = c1 .val
n .rval = c1 .rval
case c1 .rval .IsValid () && isConstType (c0 .typ ):
n .gen = nop
n .findex = notInFrame
n .typ = c0 .typ
if c , ok := c1 .rval .Interface ().(constant .Value ); ok {
i , _ := constant .Int64Val (constant .ToInt (c ))
n .rval = reflect .ValueOf (i ).Convert (c0 .typ .rtype )
} else {
n .rval = c1 .rval .Convert (c0 .typ .rtype )
}
default :
n .gen = convert
n .typ = c0 .typ
n .findex = sc .add (n .typ )
}
case isBinCall (n , sc ):
err = check .arguments (n , n .child [1 :], c0 , n .action == aCallSlice )
if err != nil {
break
}
n .gen = callBin
typ := c0 .typ .rtype
if typ .NumOut () > 0 {
if funcType := c0 .typ .val ; funcType != nil {
n .typ = funcType .ret [0 ]
n .findex = sc .add (n .typ )
for i := 1 ; i < len (funcType .ret ); i ++ {
sc .add (funcType .ret [i ])
}
} else {
n .typ = valueTOf (typ .Out (0 ))
if n .anc .kind == returnStmt {
n .findex = childPos (n )
} else {
n .findex = sc .add (n .typ )
for i := 1 ; i < typ .NumOut (); i ++ {
sc .add (valueTOf (typ .Out (i )))
}
}
}
}
default :
if isGeneric (c0 .typ ) {
fun := c0 .typ .node .anc
var g *node
var types []*itype
var found bool
if types , err = inferTypesFromCall (sc , fun , n .child [1 :]); err != nil {
break
}
if g , found , err = genAST (sc , fun , types ); err != nil {
break
}
if !found {
if _, err = interp .cfg (g , fun .scope , importPath , pkgName ); err != nil {
break
}
if err = genRun (g .child [3 ]); err != nil {
break
}
}
n .child [0 ] = g
c0 = n .child [0 ]
}
err = check .arguments (n , n .child [1 :], c0 , n .action == aCallSlice )
if err != nil {
break
}
if c0 .action == aGetFunc {
sc .add (c0 .typ )
}
if typ := c0 .typ ; len (typ .ret ) > 0 {
n .typ = typ .ret [0 ]
if n .anc .kind == returnStmt && n .typ .id () == sc .def .typ .ret [0 ].id () {
n .findex = childPos (n )
} else {
n .findex = sc .add (n .typ )
for _ , t := range typ .ret [1 :] {
sc .add (t )
}
}
} else {
n .findex = notInFrame
}
}
case caseBody :
wireChild (n )
switch {
case typeSwichAssign (n ) && len (n .child ) > 1 :
n .start = n .child [1 ].start
case len (n .child ) == 0 :
n .start = n .anc .anc .anc
default :
n .start = n .child [0 ].start
}
case caseClause :
sc = sc .pop ()
case commClauseDefault :
wireChild (n )
sc = sc .pop ()
if len (n .child ) == 0 {
return
}
n .start = n .child [0 ].start
n .lastChild ().tnext = n .anc .anc
case commClause :
wireChild (n )
sc = sc .pop ()
if len (n .child ) == 0 {
return
}
if len (n .child ) > 1 {
n .start = n .child [1 ].start
}
n .lastChild ().tnext = n .anc .anc
case compositeLitExpr :
wireChild (n )
child := n .child
if n .nleft > 0 {
child = child [1 :]
}
switch n .typ .cat {
case arrayT , sliceT :
err = check .arrayLitExpr (child , n .typ )
case mapT :
err = check .mapLitExpr (child , n .typ .key , n .typ .val )
case structT :
err = check .structLitExpr (child , n .typ )
case valueT :
rtype := n .typ .rtype
switch rtype .Kind () {
case reflect .Struct :
err = check .structBinLitExpr (child , rtype )
case reflect .Map :
ktyp := valueTOf (rtype .Key ())
vtyp := valueTOf (rtype .Elem ())
err = check .mapLitExpr (child , ktyp , vtyp )
}
}
if err != nil {
break
}
n .findex = sc .add (n .typ )
n .gen = compositeGenerator (n , n .typ , nil )
case fallthroughtStmt :
if n .anc .kind != caseBody {
err = n .cfgErrorf ("fallthrough statement out of place" )
}
case fileStmt :
wireChild (n , varDecl )
sc = sc .pop ()
n .findex = notInFrame
case forStmt0 :
body := n .child [0 ]
n .start = body .start
body .tnext = n .start
sc = sc .pop ()
case forStmt1 :
init , body := n .child [0 ], n .child [1 ]
n .start = init .start
init .tnext = body .start
body .tnext = n .start
sc = sc .pop ()
case forStmt2 :
cond , body := n .child [0 ], n .child [1 ]
if !isBool (cond .typ ) {
err = cond .cfgErrorf ("non-bool used as for condition" )
}
if cond .rval .IsValid () {
if cond .rval .Bool () {
n .start = body .start
body .tnext = body .start
}
} else {
n .start = cond .start
cond .tnext = body .start
body .tnext = cond .start
}
setFNext (cond , n )
sc = sc .pop ()
case forStmt3 :
init , cond , body := n .child [0 ], n .child [1 ], n .child [2 ]
if !isBool (cond .typ ) {
err = cond .cfgErrorf ("non-bool used as for condition" )
}
n .start = init .start
if cond .rval .IsValid () {
if cond .rval .Bool () {
init .tnext = body .start
body .tnext = body .start
} else {
init .tnext = n
}
} else {
init .tnext = cond .start
body .tnext = cond .start
}
cond .tnext = body .start
setFNext (cond , n )
sc = sc .pop ()
case forStmt4 :
post , body := n .child [0 ], n .child [1 ]
n .start = body .start
post .tnext = body .start
body .tnext = post .start
sc = sc .pop ()
case forStmt5 :
cond , post , body := n .child [0 ], n .child [1 ], n .child [2 ]
if !isBool (cond .typ ) {
err = cond .cfgErrorf ("non-bool used as for condition" )
}
if cond .rval .IsValid () {
if cond .rval .Bool () {
n .start = body .start
post .tnext = body .start
}
} else {
n .start = cond .start
post .tnext = cond .start
}
cond .tnext = body .start
setFNext (cond , n )
body .tnext = post .start
sc = sc .pop ()
case forStmt6 :
init , post , body := n .child [0 ], n .child [1 ], n .child [2 ]
n .start = init .start
init .tnext = body .start
body .tnext = post .start
post .tnext = body .start
sc = sc .pop ()
case forStmt7 :
init , cond , post , body := n .child [0 ], n .child [1 ], n .child [2 ], n .child [3 ]
if !isBool (cond .typ ) {
err = cond .cfgErrorf ("non-bool used as for condition" )
}
n .start = init .start
if cond .rval .IsValid () {
if cond .rval .Bool () {
init .tnext = body .start
post .tnext = body .start
} else {
init .tnext = n
}
} else {
init .tnext = cond .start
post .tnext = cond .start
}
cond .tnext = body .start
setFNext (cond , n )
body .tnext = post .start
sc = sc .pop ()
case forRangeStmt :
n .start = n .child [0 ].start
setFNext (n .child [0 ], n )
sc = sc .pop ()
case funcDecl :
n .start = n .child [3 ].start
n .types , n .scope = sc .types , sc
sc = sc .pop ()
funcName := n .child [1 ].ident
if sym := sc .sym [funcName ]; !isMethod (n ) && sym != nil && !isGeneric (sym .typ ) {
sym .index = -1
sym .typ = n .typ
sym .kind = funcSym
sym .node = n
}
case funcLit :
n .types , n .scope = sc .types , sc
sc = sc .pop ()
err = genRun (n )
case deferStmt , goStmt :
wireChild (n )
case identExpr :
if isKey (n ) || isNewDefine (n , sc ) {
break
}
if n .anc .kind == funcDecl && n .anc .child [1 ] == n {
break
}
sym , level , found := sc .lookup (n .ident )
if !found {
if n .typ != nil {
break
}
sym , level , found = sc .lookup (filepath .Join (n .ident , baseName ))
if !found {
err = n .cfgErrorf ("undefined: %s" , n .ident )
break
}
}
n .sym , n .typ , n .findex , n .level = sym , sym .typ , sym .index , level
if n .findex < 0 {
n .val = sym .node
} else {
switch {
case sym .kind == constSym && sym .rval .IsValid ():
n .rval = sym .rval
n .kind = basicLit
case n .ident == "iota" :
n .rval = reflect .ValueOf (constant .MakeInt64 (int64 (sc .iota )))
n .kind = basicLit
case n .ident == nilIdent :
n .kind = basicLit
case sym .kind == binSym :
n .typ = sym .typ
n .rval = sym .rval
case sym .kind == bltnSym :
if n .anc .kind != callExpr {
err = n .cfgErrorf ("use of builtin %s not in function call" , n .ident )
}
}
}
if n .sym != nil {
n .recv = n .sym .recv
}
case ifStmt0 :
cond , tbody := n .child [0 ], n .child [1 ]
if !isBool (cond .typ ) {
err = cond .cfgErrorf ("non-bool used as if condition" )
}
if cond .rval .IsValid () {
if cond .rval .Bool () {
n .start = tbody .start
}
} else {
n .start = cond .start
cond .tnext = tbody .start
}
setFNext (cond , n )
tbody .tnext = n
sc = sc .pop ()
case ifStmt1 :
cond , tbody , fbody := n .child [0 ], n .child [1 ], n .child [2 ]
if !isBool (cond .typ ) {
err = cond .cfgErrorf ("non-bool used as if condition" )
}
if cond .rval .IsValid () {
if cond .rval .Bool () {
n .start = tbody .start
} else {
n .start = fbody .start
}
} else {
n .start = cond .start
cond .tnext = tbody .start
setFNext (cond , fbody .start )
}
tbody .tnext = n
fbody .tnext = n
sc = sc .pop ()
case ifStmt2 :
init , cond , tbody := n .child [0 ], n .child [1 ], n .child [2 ]
if !isBool (cond .typ ) {
err = cond .cfgErrorf ("non-bool used as if condition" )
}
n .start = init .start
if cond .rval .IsValid () {
if cond .rval .Bool () {
init .tnext = tbody .start
} else {
init .tnext = n
}
} else {
init .tnext = cond .start
cond .tnext = tbody .start
}
tbody .tnext = n
setFNext (cond , n )
sc = sc .pop ()
case ifStmt3 :
init , cond , tbody , fbody := n .child [0 ], n .child [1 ], n .child [2 ], n .child [3 ]
if !isBool (cond .typ ) {
err = cond .cfgErrorf ("non-bool used as if condition" )
}
n .start = init .start
if cond .rval .IsValid () {
if cond .rval .Bool () {
init .tnext = tbody .start
} else {
init .tnext = fbody .start
}
} else {
init .tnext = cond .start
cond .tnext = tbody .start
setFNext (cond , fbody .start )
}
tbody .tnext = n
fbody .tnext = n
sc = sc .pop ()
case keyValueExpr :
if isBlank (n .child [1 ]) {
err = n .cfgErrorf ("cannot use _ as value" )
break
}
wireChild (n )
case landExpr :
if isBlank (n .child [0 ]) || isBlank (n .child [1 ]) {
err = n .cfgErrorf ("cannot use _ as value" )
break
}
n .start = n .child [0 ].start
n .child [0 ].tnext = n .child [1 ].start
setFNext (n .child [0 ], n )
n .child [1 ].tnext = n
n .typ = n .child [0 ].typ
n .findex = sc .add (n .typ )
if n .start .action == aNop {
n .start .gen = branch
}
case lorExpr :
if isBlank (n .child [0 ]) || isBlank (n .child [1 ]) {
err = n .cfgErrorf ("cannot use _ as value" )
break
}
n .start = n .child [0 ].start
n .child [0 ].tnext = n
setFNext (n .child [0 ], n .child [1 ].start )
n .child [1 ].tnext = n
n .typ = n .child [0 ].typ
n .findex = sc .add (n .typ )
if n .start .action == aNop {
n .start .gen = branch
}
case parenExpr :
wireChild (n )
c := n .lastChild ()
n .findex = c .findex
n .level = c .level
n .typ = c .typ
n .rval = c .rval
case rangeStmt :
if sc .rangeChanType (n ) != nil {
n .start = n .child [1 ].start
n .child [1 ].tnext = n
n .tnext = n .child [2 ].start
n .child [2 ].tnext = n
n .child [0 ].gen = empty
} else {
var k , o , body *node
if len (n .child ) == 4 {
k , o , body = n .child [0 ], n .child [2 ], n .child [3 ]
} else {
k , o , body = n .child [0 ], n .child [1 ], n .child [2 ]
}
n .start = o .start
o .tnext = k .start
k .tnext = n
n .tnext = body .start
body .tnext = n
k .gen = empty
}
case returnStmt :
if len (n .child ) > sc .def .typ .numOut () {
err = n .cfgErrorf ("too many arguments to return" )
break
}
for _ , c := range n .child {
if isBlank (c ) {
err = n .cfgErrorf ("cannot use _ as value" )
return
}
}
returnSig := sc .def .child [2 ]
if mustReturnValue (returnSig ) {
nret := len (n .child )
if nret == 1 && isCall (n .child [0 ]) {
nret = n .child [0 ].child [0 ].typ .numOut ()
}
if nret < sc .def .typ .numOut () {
err = n .cfgErrorf ("not enough arguments to return" )
break
}
}
wireChild (n )
n .tnext = nil
n .val = sc .def
for i , c := range n .child {
var typ *itype
typ , err = nodeType (interp , sc .upperLevel (), returnSig .child [2 ].fieldType (i ))
if err != nil {
return
}
c .typ .node = c
if !c .typ .assignableTo (typ ) {
err = c .cfgErrorf ("cannot use %v (type %v) as type %v in return argument" , c .ident , c .typ .cat , typ .cat )
return
}
if c .typ .cat == nilT {
c .rval = reflect .New (typ .TypeOf ()).Elem ()
}
}
case selectorExpr :
wireChild (n )
n .typ = n .child [0 ].typ
n .recv = n .child [0 ].recv
if n .typ == nil {
err = n .cfgErrorf ("undefined type" )
break
}
switch {
case n .typ .cat == binPkgT :
name := n .child [1 ].ident
pkg := n .child [0 ].sym .typ .path
if s , ok := interp .binPkg [pkg ][name ]; ok {
if isBinType (s ) {
n .typ = valueTOf (s .Type ().Elem ())
} else {
n .typ = valueTOf (fixPossibleConstType (s .Type ()), withUntyped (isValueUntyped (s )))
n .rval = s
if pkg == "unsafe" && (name == "AlignOf" || name == "Offsetof" || name == "Sizeof" ) {
n .sym = &symbol {kind : bltnSym , node : n , rval : s }
n .ident = pkg + "." + name
}
}
n .action = aGetSym
n .gen = nop
} else {
err = n .cfgErrorf ("package %s \"%s\" has no symbol %s" , n .child [0 ].ident , pkg , name )
}
case n .typ .cat == srcPkgT :
pkg , name := n .child [0 ].sym .typ .path , n .child [1 ].ident
if sym , ok := interp .srcPkg [pkg ][name ]; ok {
n .findex = sym .index
if sym .global {
n .level = globalFrame
}
n .val = sym .node
n .gen = nop
n .action = aGetSym
n .typ = sym .typ
n .sym = sym
n .recv = sym .recv
n .rval = sym .rval
} else {
err = n .cfgErrorf ("undefined selector: %s.%s" , pkg , name )
}
case isStruct (n .typ ) || isInterfaceSrc (n .typ ):
if ti := n .typ .lookupField (n .child [1 ].ident ); len (ti ) > 0 {
if isStruct (n .typ ) {
d := n .typ .methodDepth (n .child [1 ].ident )
if d >= 0 && d < len (ti ) {
goto tryMethods
}
if d == len (ti ) {
err = n .cfgErrorf ("ambiguous selector: %s" , n .child [1 ].ident )
break
}
}
n .val = ti
switch {
case isInterfaceSrc (n .typ ):
n .typ = n .typ .fieldSeq (ti )
n .gen = getMethodByName
n .action = aMethod
case n .typ .cat == ptrT :
n .typ = n .typ .fieldSeq (ti )
n .gen = getPtrIndexSeq
if n .typ .cat == funcT {
n .typ = wrapperValueTOf (n .typ .TypeOf (), n .typ )
}
default :
n .gen = getIndexSeq
n .typ = n .typ .fieldSeq (ti )
if n .typ .cat == funcT {
n .typ = wrapperValueTOf (n .typ .TypeOf (), n .typ )
}
}
break
}
if s , lind , ok := n .typ .lookupBinField (n .child [1 ].ident ); ok {
n .gen = getIndexSeqField
lind = append (lind , s .Index ...)
if isStruct (n .typ ) {
d := n .typ .methodDepth (n .child [1 ].ident )
if d >= 0 && d < len (lind ) {
goto tryMethods
}
if d == len (lind ) {
err = n .cfgErrorf ("ambiguous selector: %s" , n .child [1 ].ident )
break
}
}
n .val = lind
n .typ = valueTOf (s .Type )
break
}
tryMethods :
fallthrough
default :
err = matchSelectorMethod (sc , n )
}
if err == nil && n .findex != -1 && n .typ .cat != genericT {
n .findex = sc .add (n .typ )
}
case selectStmt :
wireChild (n )
n .child [0 ].gen = _select
var cur *node
for _ , c := range n .child [0 ].child {
if c .kind == commClauseDefault {
continue
}
var an , pn *node
if len (c .child ) > 0 {
switch c0 := c .child [0 ]; {
case c0 .kind == exprStmt && len (c0 .child ) == 1 && c0 .child [0 ].action == aRecv :
an = c0 .child [0 ].child [0 ]
pn = an
case c0 .action == aAssign :
an = c0 .lastChild ().child [0 ]
pn = an
case c0 .kind == sendStmt :
an = c0 .child [0 ]
pn = c0 .child [1 ]
}
}
if an == nil {
continue
}
if cur == nil {
n .start = an .start
} else {
cur .tnext = an .start
}
if pn != nil {
an .tnext = pn
cur = pn
}
}
if cur == nil {
n .start = n .child [0 ]
} else {
cur .tnext = n .child [0 ]
}
case starExpr :
if isBlank (n .child [0 ]) {
err = n .cfgErrorf ("cannot use _ as value" )
break
}
switch {
case n .anc .kind == defineStmt && len (n .anc .child ) == 3 && n .anc .child [1 ] == n :
n .gen = nop
case n .anc .kind == valueSpec && n .anc .lastChild () == n :
n .gen = nop
case n .anc .kind == fieldExpr :
n .gen = nop
case n .child [0 ].isType (sc ):
n .gen = nop
n .typ = ptrOf (n .child [0 ].typ )
default :
wireChild (n )
err = check .starExpr (n .child [0 ])
if err != nil {
break
}
if c0 := n .child [0 ]; c0 .typ .cat == valueT {
n .typ = valueTOf (c0 .typ .rtype .Elem ())
} else {
n .typ = c0 .typ .val
}
n .findex = sc .add (n .typ )
}
case typeSwitch :
usedCase := map [string ]bool {}
for _ , c := range n .lastChild ().child {
for _ , t := range c .child [:len (c .child )-1 ] {
tid := t .typ .id ()
if usedCase [tid ] {
err = c .cfgErrorf ("duplicate case %s in type switch" , t .ident )
return
}
usedCase [tid ] = true
}
}
fallthrough
case switchStmt :
sc = sc .pop ()
sbn := n .lastChild ()
clauses := sbn .child
l := len (clauses )
if l == 0 {
break
}
for i := l - 1 ; i >= 0 ; i -- {
c := clauses [i ]
if len (c .child ) == 0 {
c .tnext = n
} else {
body := c .lastChild ()
c .tnext = body .start
c .child [0 ].tnext = c
c .start = c .child [0 ].start
if i < l -1 && len (body .child ) > 0 && body .lastChild ().kind == fallthroughtStmt {
if n .kind == typeSwitch {
err = body .lastChild ().cfgErrorf ("cannot fallthrough in type switch" )
}
if len (clauses [i +1 ].child ) == 0 {
body .tnext = n
} else {
body .tnext = clauses [i +1 ].lastChild ().start
}
} else {
body .tnext = n
}
}
if i == l -1 {
setFNext (clauses [i ], n )
continue
}
if len (clauses [i +1 ].child ) > 1 {
setFNext (c , clauses [i +1 ].start )
} else {
setFNext (c , clauses [i +1 ])
}
}
sbn .start = clauses [0 ].start
n .start = n .child [0 ].start
if n .kind == typeSwitch {
init := n .child [1 ].lastChild ().child [0 ]
init .tnext = sbn .start
n .child [0 ].tnext = init .start
} else {
n .child [0 ].tnext = sbn .start
}
case switchIfStmt :
sc = sc .pop ()
sbn := n .lastChild ()
clauses := sbn .child
l := len (clauses )
if l == 0 {
break
}
for i := l - 1 ; i >= 0 ; i -- {
c := clauses [i ]
c .gen = nop
if len (c .child ) == 0 {
c .tnext = n
c .fnext = n
} else {
body := c .lastChild ()
if len (c .child ) > 1 {
cond := c .child [0 ]
cond .tnext = body .start
if i == l -1 {
setFNext (cond , n )
} else {
setFNext (cond , clauses [i +1 ].start )
}
c .start = cond .start
} else {
c .start = body .start
}
if i < l -1 && len (body .child ) > 0 && body .lastChild ().kind == fallthroughtStmt {
body .tnext = clauses [i +1 ].lastChild ().start
} else {
body .tnext = n
}
}
}
sbn .start = clauses [0 ].start
n .start = n .child [0 ].start
n .child [0 ].tnext = sbn .start
case typeAssertExpr :
if len (n .child ) == 1 {
n .gen = nop
break
}
wireChild (n )
c0 , c1 := n .child [0 ], n .child [1 ]
if isBlank (c0 ) || isBlank (c1 ) {
err = n .cfgErrorf ("cannot use _ as value" )
break
}
if c1 .typ == nil {
if c1 .typ , err = nodeType (interp , sc , c1 ); err != nil {
return
}
}
err = check .typeAssertionExpr (c0 , c1 .typ )
if err != nil {
break
}
if n .anc .action != aAssignX {
if c0 .typ .cat == valueT && isFunc (c1 .typ ) {
n .typ = valueTOf (c1 .typ .TypeOf ())
} else {
n .typ = c1 .typ
}
n .findex = sc .add (n .typ )
}
case sliceExpr :
wireChild (n )
err = check .sliceExpr (n )
if err != nil {
break
}
if n .typ , err = nodeType (interp , sc , n ); err != nil {
return
}
n .findex = sc .add (n .typ )
case unaryExpr :
wireChild (n )
err = check .unaryExpr (n )
if err != nil {
break
}
n .typ = n .child [0 ].typ
if n .action == aRecv {
if n .typ .cat == valueT {
n .typ = valueTOf (n .typ .rtype .Elem ())
} else {
n .typ = n .typ .val
}
}
if n .typ == nil {
if n .typ , err = nodeType (interp , sc , n ); err != nil {
return
}
}
if n .child [0 ].rval .IsValid () && !isInterface (n .typ ) && constOp [n .action ] != nil {
n .typ .TypeOf ()
constOp [n .action ](n )
}
switch {
case n .rval .IsValid ():
n .gen = nop
n .findex = notInFrame
case n .anc .kind == assignStmt && n .anc .action == aAssign && n .anc .nright == 1 :
dest := n .anc .child [childPos (n )-n .anc .nright ]
n .typ = dest .typ
n .findex = dest .findex
n .level = dest .level
case n .anc .kind == returnStmt :
pos := childPos (n )
n .typ = sc .def .typ .ret [pos ]
n .findex = pos
default :
n .findex = sc .add (n .typ )
}
case valueSpec :
n .gen = reset
l := len (n .child ) - 1
if n .typ = n .child [l ].typ ; n .typ == nil {
if n .typ , err = nodeType (interp , sc , n .child [l ]); err != nil {
return
}
}
for _ , c := range n .child [:l ] {
var index int
if sc .global {
index = sc .sym [c .ident ].index
c .level = globalFrame
} else {
index = sc .add (n .typ )
sc .sym [c .ident ] = &symbol {index : index , kind : varSym , typ : n .typ }
}
c .typ = n .typ
c .findex = index
}
}
})
if sc != interp .universe {
sc .pop ()
}
return initNodes , err
}
func compDefineX(sc *scope , n *node ) error {
l := len (n .child ) - 1
types := []*itype {}
switch src := n .child [l ]; src .kind {
case callExpr :
funtype , err := nodeType (n .interp , sc , src .child [0 ])
if err != nil {
return err
}
for funtype .cat == valueT && funtype .val != nil {
funtype = funtype .val
}
if funtype .cat == valueT {
for i := 0 ; i < funtype .rtype .NumOut (); i ++ {
types = append (types , valueTOf (funtype .rtype .Out (i )))
}
} else {
types = funtype .ret
}
if n .anc .kind == varDecl && n .child [l -1 ].isType (sc ) {
l --
}
if len (types ) != l {
return n .cfgErrorf ("assignment mismatch: %d variables but %s returns %d values" , l , src .child [0 ].name (), len (types ))
}
if isBinCall (src , sc ) {
n .gen = nop
} else {
n .gen = assignFromCall
}
case indexExpr :
types = append (types , src .typ , sc .getType ("bool" ))
n .child [l ].gen = getIndexMap2
n .gen = nop
case typeAssertExpr :
if n .child [0 ].ident == "_" {
n .child [l ].gen = typeAssertStatus
} else {
n .child [l ].gen = typeAssertLong
}
types = append (types , n .child [l ].child [1 ].typ , sc .getType ("bool" ))
n .gen = nop
case unaryExpr :
if n .child [l ].action == aRecv {
types = append (types , src .typ , sc .getType ("bool" ))
n .child [l ].gen = recv2
n .gen = nop
}
default :
return n .cfgErrorf ("unsupported assign expression" )
}
symIsNew := map [string ]bool {}
hasNewSymbol := false
for i := range types {
id := n .child [i ].ident
if id == "_" || id == "" {
continue
}
if _ , found := symIsNew [id ]; found {
return n .cfgErrorf ("%s repeated on left side of :=" , id )
}
if _ , found := sc .sym [id ]; found {
symIsNew [id ] = false
} else {
symIsNew [id ] = true
hasNewSymbol = true
}
}
for i , t := range types {
var index int
id := n .child [i ].ident
sym , level , ok := sc .lookup (id )
canRedeclare := hasNewSymbol && len (symIsNew ) > 1 && !symIsNew [id ] && ok
if canRedeclare && level == n .child [i ].level && sym .kind == varSym && sym .typ .id () == t .id () {
index = sym .index
n .child [i ].redeclared = true
} else {
index = sc .add (t )
sc .sym [id ] = &symbol {index : index , kind : varSym , typ : t }
}
n .child [i ].typ = t
n .child [i ].findex = index
}
return nil
}
func childPos(n *node ) int {
for i , c := range n .anc .child {
if n == c {
return i
}
}
return -1
}
func (n *node ) cfgErrorf (format string , a ...interface {}) *cfgError {
pos := n .interp .fset .Position (n .pos )
posString := n .interp .fset .Position (n .pos ).String ()
if pos .Filename == DefaultSourceName {
posString = strings .TrimPrefix (posString , DefaultSourceName +":" )
}
a = append ([]interface {}{posString }, a ...)
return &cfgError {n , fmt .Errorf ("%s: " +format , a ...)}
}
func genRun(nod *node ) error {
var err error
seen := map [*node ]bool {}
nod .Walk (func (n *node ) bool {
if err != nil || seen [n ] {
return false
}
seen [n ] = true
switch n .kind {
case funcType :
if len (n .anc .child ) == 4 {
setExec (n .anc .child [3 ].start )
}
case constDecl , varDecl :
setExec (n .start )
return false
}
return true
}, nil )
return err
}
func genGlobalVars(roots []*node , sc *scope ) (*node , error ) {
var vars []*node
for _ , n := range roots {
vars = append (vars , getVars (n )...)
}
if len (vars ) == 0 {
return nil , nil
}
varNode , err := genGlobalVarDecl (vars , sc )
if err != nil {
return nil , err
}
setExec (varNode .start )
return varNode , nil
}
func getVars(n *node ) (vars []*node ) {
for _ , child := range n .child {
if child .kind == varDecl {
vars = append (vars , child .child ...)
}
}
return vars
}
func genGlobalVarDecl(nodes []*node , sc *scope ) (*node , error ) {
varNode := &node {kind : varDecl , action : aNop , gen : nop }
deps := map [*node ][]*node {}
for _ , n := range nodes {
deps [n ] = getVarDependencies (n , sc )
}
inited := map [*node ]bool {}
revisit := []*node {}
for {
for _ , n := range nodes {
canInit := true
for _ , d := range deps [n ] {
if !inited [d ] {
canInit = false
}
}
if !canInit {
revisit = append (revisit , n )
continue
}
varNode .child = append (varNode .child , n )
inited [n ] = true
}
if len (revisit ) == 0 || equalNodes (nodes , revisit ) {
break
}
nodes = revisit
revisit = []*node {}
}
if len (revisit ) > 0 {
return nil , revisit [0 ].cfgErrorf ("variable definition loop" )
}
wireChild (varNode )
return varNode , nil
}
func getVarDependencies(nod *node , sc *scope ) (deps []*node ) {
nod .Walk (func (n *node ) bool {
if n .kind != identExpr {
return true
}
if n .anc .kind == selectorExpr && childPos (n ) == 1 {
return false
}
sym , _ , ok := sc .lookup (n .ident )
if !ok {
return false
}
if sym .kind != varSym || !sym .global || sym .node == nod {
return false
}
deps = append (deps , sym .node )
return false
}, nil )
return deps
}
func setFNext(cond , next *node ) {
if cond .action == aNop {
cond .action = aBranch
cond .gen = branch
cond .fnext = next
}
if cond .kind == parenExpr {
setFNext (cond .lastChild (), next )
return
}
cond .fnext = next
}
func getDefault(n *node ) int {
for i , c := range n .lastChild ().child {
switch len (c .child ) {
case 0 :
return i
case 1 :
if c .child [0 ].kind == caseBody {
return i
}
}
}
return -1
}
func isBinType(v reflect .Value ) bool { return v .IsValid () && v .Kind () == reflect .Ptr && v .IsNil () }
func (n *node ) isType (sc *scope ) bool {
switch n .kind {
case arrayType , chanType , chanTypeRecv , chanTypeSend , funcType , interfaceType , mapType , structType :
return true
case parenExpr , starExpr :
if len (n .child ) == 1 {
return n .child [0 ].isType (sc )
}
case selectorExpr :
pkg , name := n .child [0 ].ident , n .child [1 ].ident
baseName := filepath .Base (n .interp .fset .Position (n .pos ).Filename )
suffixedPkg := filepath .Join (pkg , baseName )
sym , _ , ok := sc .lookup (suffixedPkg )
if !ok {
sym , _, ok = sc .lookup (pkg )
if !ok {
return false
}
}
if sym .kind != pkgSym {
return false
}
path := sym .typ .path
if p , ok := n .interp .binPkg [path ]; ok && isBinType (p [name ]) {
return true
}
if p , ok := n .interp .srcPkg [path ]; ok && p [name ] != nil && p [name ].kind == typeSym {
return true
}
case identExpr :
return sc .getType (n .ident ) != nil
case indexExpr :
sym , _ , ok := sc .lookup (n .child [0 ].ident )
return ok && sym .kind == typeSym
}
return false
}
func wireChild(n *node , exclude ...nkind ) {
child := excludeNodeKind (n .child , exclude )
for _ , c := range child {
switch c .kind {
case arrayType , chanType , chanTypeRecv , chanTypeSend , funcDecl , importDecl , mapType , basicLit , identExpr , typeDecl :
continue
default :
n .start = c .start
}
break
}
for i := 1 ; i < len (child ); i ++ {
switch child [i ].kind {
case funcDecl :
child [i -1 ].tnext = child [i ]
default :
switch child [i -1 ].kind {
case breakStmt , continueStmt , gotoStmt , returnStmt :
default :
child [i -1 ].tnext = child [i ].start
}
}
}
for i := len (child ) - 1 ; i >= 0 ; i -- {
switch child [i ].kind {
case arrayType , chanType , chanTypeRecv , chanTypeSend , importDecl , mapType , funcDecl , basicLit , identExpr , typeDecl :
continue
case breakStmt , continueStmt , gotoStmt , returnStmt :
default :
child [i ].tnext = n
}
break
}
}
func excludeNodeKind(child []*node , kinds []nkind ) []*node {
if len (kinds ) == 0 {
return child
}
var res []*node
for _ , c := range child {
exclude := false
for _ , k := range kinds {
if c .kind == k {
exclude = true
}
}
if !exclude {
res = append (res , c )
}
}
return res
}
func (n *node ) name () (s string ) {
switch {
case n .ident != "" :
s = n .ident
case n .action == aGetSym :
s = n .child [0 ].ident + "." + n .child [1 ].ident
}
return s
}
func (n *node ) isNatural () bool {
if isUint (n .typ .TypeOf ()) {
return true
}
if n .rval .IsValid () {
t := n .rval .Type ()
if isUint (t ) {
return true
}
if isInt (t ) && n .rval .Int () >= 0 {
return true
}
if isFloat (t ) {
f := n .rval .Float ()
if f == math .Trunc (f ) && f >= 0 {
n .rval = reflect .ValueOf (uint (f ))
n .typ .rtype = n .rval .Type ()
return true
}
}
if isConstantValue (t ) {
c := n .rval .Interface ().(constant .Value )
switch c .Kind () {
case constant .Int :
i , _ := constant .Int64Val (c )
if i >= 0 {
return true
}
case constant .Float :
f , _ := constant .Float64Val (c )
if f == math .Trunc (f ) {
n .rval = reflect .ValueOf (constant .ToInt (c ))
n .typ .rtype = n .rval .Type ()
return true
}
}
}
}
return false
}
func (n *node ) isNil () bool { return n .kind == basicLit && !n .rval .IsValid () }
func (n *node ) fieldType (m int ) *node {
k := 0
l := len (n .child )
for i := 0 ; i < l ; i ++ {
cl := len (n .child [i ].child )
if cl < 2 {
if k == m {
return n .child [i ].lastChild ()
}
k ++
continue
}
for j := 0 ; j < cl -1 ; j ++ {
if k == m {
return n .child [i ].lastChild ()
}
k ++
}
}
return nil
}
func (n *node ) lastChild () *node { return n .child [len (n .child )-1 ] }
func (n *node ) hasAnc (nod *node ) bool {
for a := n .anc ; a != nil ; a = a .anc {
if a == nod {
return true
}
}
return false
}
func isKey(n *node ) bool {
return n .anc .kind == fileStmt ||
(n .anc .kind == selectorExpr && n .anc .child [0 ] != n ) ||
(n .anc .kind == funcDecl && isMethod (n .anc )) ||
(n .anc .kind == keyValueExpr && isStruct (n .anc .typ ) && n .anc .child [0 ] == n ) ||
(n .anc .kind == fieldExpr && len (n .anc .child ) > 1 && n .anc .child [0 ] == n )
}
func isField(n *node ) bool {
return n .kind == selectorExpr && len (n .child ) > 0 && n .child [0 ].typ != nil && isStruct (n .child [0 ].typ )
}
func isInInterfaceType(n *node ) bool {
anc := n .anc
for anc != nil {
if anc .kind == interfaceType {
return true
}
anc = anc .anc
}
return false
}
func isInConstOrTypeDecl(n *node ) bool {
anc := n .anc
for anc != nil {
switch anc .kind {
case constDecl , typeDecl , arrayType , chanType :
return true
case varDecl , funcDecl :
return false
}
anc = anc .anc
}
return false
}
func isNewDefine(n *node , sc *scope ) bool {
if n .ident == "_" {
return true
}
if (n .anc .kind == defineXStmt || n .anc .kind == defineStmt || n .anc .kind == valueSpec ) && childPos (n ) < n .anc .nleft {
return true
}
if n .anc .kind == rangeStmt {
if n .anc .child [0 ] == n {
return true
}
if sc .rangeChanType (n .anc ) == nil && n .anc .child [1 ] == n && len (n .anc .child ) == 4 {
return true
}
return false
}
return false
}
func isMethod(n *node ) bool {
return len (n .child [0 ].child ) > 0
}
func isFuncField(n *node ) bool {
return isField (n ) && isFunc (n .typ )
}
func isMapEntry(n *node ) bool {
return n .action == aGetIndex && isMap (n .child [0 ].typ )
}
func isCall(n *node ) bool {
return n .action == aCall || n .action == aCallSlice
}
func isBinCall(n *node , sc *scope ) bool {
if !isCall (n ) || len (n .child ) == 0 {
return false
}
c0 := n .child [0 ]
if c0 .typ == nil {
c0 .typ , _ = nodeType (n .interp , sc , c0 )
if c0 .typ == nil {
return false
}
}
return c0 .typ .cat == valueT && c0 .typ .rtype .Kind () == reflect .Func
}
func mustReturnValue(n *node ) bool {
if len (n .child ) < 3 {
return false
}
for _ , f := range n .child [2 ].child {
if len (f .child ) > 1 {
return false
}
}
return true
}
func isRegularCall(n *node ) bool {
return isCall (n ) && n .child [0 ].typ .cat == funcT
}
func variadicPos(n *node ) int {
if len (n .child [0 ].typ .arg ) == 0 {
return -1
}
last := len (n .child [0 ].typ .arg ) - 1
if n .child [0 ].typ .arg [last ].cat == variadicT {
return last
}
return -1
}
func canExport(name string ) bool {
if r := []rune (name ); len (r ) > 0 && unicode .IsUpper (r [0 ]) {
return true
}
return false
}
func getExec(n *node ) bltn {
if n == nil {
return nil
}
if n .exec == nil {
setExec (n )
}
return n .exec
}
func setExec(n *node ) {
if n .exec != nil {
return
}
seen := map [*node ]bool {}
var set func (n *node )
set = func (n *node ) {
if n == nil || n .exec != nil {
return
}
seen [n ] = true
if n .tnext != nil && n .tnext .exec == nil {
if seen [n .tnext ] {
m := n .tnext
n .tnext .exec = func (f *frame ) bltn { return m .exec (f ) }
} else {
set (n .tnext )
}
}
if n .fnext != nil && n .fnext .exec == nil {
if seen [n .fnext ] {
m := n .fnext
n .fnext .exec = func (f *frame ) bltn { return m .exec (f ) }
} else {
set (n .fnext )
}
}
n .gen (n )
}
set (n )
}
func typeSwichAssign(n *node ) bool {
ts := n .anc .anc .anc
return ts .kind == typeSwitch && ts .child [1 ].action == aAssign
}
func compositeGenerator(n *node , typ *itype , rtyp reflect .Type ) (gen bltnGenerator ) {
switch typ .cat {
case linkedT , ptrT :
gen = compositeGenerator (n , typ .val , rtyp )
case arrayT , sliceT :
gen = arrayLit
case mapT :
gen = mapLit
case structT :
switch {
case len (n .child ) == 0 :
gen = compositeLitNotype
case n .lastChild ().kind == keyValueExpr :
if n .nleft == 1 {
gen = compositeLitKeyed
} else {
gen = compositeLitKeyedNotype
}
default :
if n .nleft == 1 {
gen = compositeLit
} else {
gen = compositeLitNotype
}
}
case valueT :
if rtyp == nil {
rtyp = n .typ .TypeOf ()
}
switch k := rtyp .Kind (); k {
case reflect .Struct :
if n .nleft == 1 {
gen = compositeBinStruct
} else {
gen = compositeBinStructNotype
}
case reflect .Map :
gen = compositeBinMap
case reflect .Ptr :
gen = compositeGenerator (n , typ , n .typ .val .rtype )
case reflect .Slice , reflect .Array :
gen = compositeBinSlice
default :
log .Panic (n .cfgErrorf ("compositeGenerator not implemented for type kind: %s" , k ))
}
}
return gen
}
func matchSelectorMethod(sc *scope , n *node ) (err error ) {
name := n .child [1 ].ident
if n .typ .cat == valueT || n .typ .cat == errorT {
switch method , ok := n .typ .rtype .MethodByName (name ); {
case ok :
hasRecvType := n .typ .TypeOf ().Kind () != reflect .Interface
n .val = method .Index
n .gen = getIndexBinMethod
n .action = aGetMethod
n .recv = &receiver {node : n .child [0 ]}
n .typ = valueTOf (method .Type , isBinMethod ())
if hasRecvType {
n .typ .recv = n .typ
}
case n .typ .TypeOf ().Kind () == reflect .Ptr :
if field , ok := n .typ .rtype .Elem ().FieldByName (name ); ok {
n .typ = valueTOf (field .Type )
n .val = field .Index
n .gen = getPtrIndexSeq
break
}
err = n .cfgErrorf ("undefined method: %s" , name )
case n .typ .TypeOf ().Kind () == reflect .Struct :
if field , ok := n .typ .rtype .FieldByName (name ); ok {
n .typ = valueTOf (field .Type )
n .val = field .Index
n .gen = getIndexSeq
break
}
fallthrough
default :
pt := reflect .PtrTo (n .typ .rtype )
if m2 , ok2 := pt .MethodByName (name ); ok2 {
n .val = m2 .Index
n .gen = getIndexBinPtrMethod
n .typ = valueTOf (m2 .Type , isBinMethod (), withRecv (valueTOf (pt )))
n .recv = &receiver {node : n .child [0 ]}
n .action = aGetMethod
break
}
err = n .cfgErrorf ("undefined method: %s" , name )
}
return err
}
if n .typ .cat == ptrT && (n .typ .val .cat == valueT || n .typ .val .cat == errorT ) {
if method , ok := n .typ .val .rtype .MethodByName (name ); ok {
n .val = method .Index
n .typ = valueTOf (method .Type , isBinMethod (), withRecv (n .typ ))
n .recv = &receiver {node : n .child [0 ]}
n .gen = getIndexBinElemMethod
n .action = aGetMethod
} else if method , ok := reflect .PtrTo (n .typ .val .rtype ).MethodByName (name ); ok {
n .val = method .Index
n .gen = getIndexBinMethod
n .typ = valueTOf (method .Type , withRecv (valueTOf (reflect .PtrTo (n .typ .val .rtype ), isBinMethod ())))
n .recv = &receiver {node : n .child [0 ]}
n .action = aGetMethod
} else if field , ok := n .typ .val .rtype .FieldByName (name ); ok {
n .typ = valueTOf (field .Type )
n .val = field .Index
n .gen = getPtrIndexSeq
} else {
err = n .cfgErrorf ("undefined selector: %s" , name )
}
return err
}
if m , lind := n .typ .lookupMethod (name ); m != nil {
n .action = aGetMethod
if n .child [0 ].isType (sc ) {
n .val = m
n .findex = notInFrame
n .gen = nop
n .typ = &itype {}
*n .typ = *m .typ
n .typ .arg = append ([]*itype {n .child [0 ].typ }, m .typ .arg ...)
} else {
n .gen = getMethod
n .val = m
n .typ = m .typ
n .recv = &receiver {node : n .child [0 ], index : lind }
}
return nil
}
if m , lind , isPtr , ok := n .typ .lookupBinMethod (name ); ok {
n .action = aGetMethod
switch {
case isPtr && n .typ .fieldSeq (lind ).cat != ptrT :
n .gen = getIndexSeqPtrMethod
case isInterfaceSrc (n .typ ):
n .gen = getMethodByName
default :
n .gen = getIndexSeqMethod
}
n .recv = &receiver {node : n .child [0 ], index : lind }
n .val = append ([]int {m .Index }, lind ...)
n .typ = valueTOf (m .Type , isBinMethod (), withRecv (n .child [0 ].typ ))
return nil
}
if typ := n .typ .interfaceMethod (name ); typ != nil {
n .typ = typ
n .action = aGetMethod
n .gen = getMethodByName
return nil
}
return n .cfgErrorf ("undefined selector: %s" , name )
}
func arrayTypeLen(n *node , sc *scope ) (int , error ) {
if n .typ != nil && n .typ .cat == arrayT {
return n .typ .length , nil
}
max := -1
for _ , c := range n .child [1 :] {
var r int
if c .kind != keyValueExpr {
r = max + 1
max = r
continue
}
c0 := c .child [0 ]
v := c0 .rval
if v .IsValid () {
r = int (v .Int ())
} else {
if c0 .kind == identExpr {
sym , _ , ok := sc .lookup (c0 .ident )
if !ok {
return 0 , c0 .cfgErrorf ("undefined: %s" , c0 .ident )
}
if sym .kind != constSym {
return 0 , c0 .cfgErrorf ("non-constant array bound %q" , c0 .ident )
}
r = int (vInt (sym .rval ))
} else {
if _ , err := c0 .interp .cfg (c0 , sc , sc .pkgID , sc .pkgName ); err != nil {
return 0 , err
}
cv , ok := c0 .rval .Interface ().(constant .Value )
if !ok {
return 0 , c0 .cfgErrorf ("non-constant expression" )
}
r = constToInt (cv )
}
}
if r > max {
max = r
}
}
return max + 1 , nil
}
func isValueUntyped(v reflect .Value ) bool {
if v .CanSet () {
return false
}
return v .Type ().Implements (constVal )
}
func isArithmeticAction(n *node ) bool {
switch n .action {
case aAdd , aAnd , aAndNot , aBitNot , aMul , aNeg , aOr , aPos , aQuo , aRem , aShl , aShr , aSub , aXor :
return true
}
return false
}
func isBoolAction(n *node ) bool {
switch n .action {
case aEqual , aGreater , aGreaterEqual , aLand , aLor , aLower , aLowerEqual , aNot , aNotEqual :
return true
}
return false
}
func isBlank(n *node ) bool {
if n .kind == parenExpr && len (n .child ) > 0 {
return isBlank (n .child [0 ])
}
return n .ident == "_"
}
func alignof(n *node ) {
n .gen = nop
n .typ = n .scope .getType ("uintptr" )
n .rval = reflect .ValueOf (uintptr (n .child [1 ].typ .TypeOf ().Align ()))
}
func offsetof(n *node ) {
n .gen = nop
n .typ = n .scope .getType ("uintptr" )
c1 := n .child [1 ]
if field , ok := c1 .child [0 ].typ .rtype .FieldByName (c1 .child [1 ].ident ); ok {
n .rval = reflect .ValueOf (field .Offset )
}
}
func sizeof(n *node ) {
n .gen = nop
n .typ = n .scope .getType ("uintptr" )
n .rval = reflect .ValueOf (n .child [1 ].typ .TypeOf ().Size ())
}
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 .