892 lines
25 KiB
Go
892 lines
25 KiB
Go
package interp
|
|
|
|
import (
|
|
"log"
|
|
"path"
|
|
"reflect"
|
|
"unicode"
|
|
)
|
|
|
|
// Symbol type defines symbol values entities
|
|
type Symbol struct {
|
|
typ *Type // Type of value
|
|
node *Node // Node value if index is negative
|
|
index int // index of value in frame or -1
|
|
pkgbin *SymMap // Map of package symbols if typ.cat is BinPkgT, or nil
|
|
pkgsrc *NodeMap // Map of package symbols if typ.cat is SrcPkgT, or nil
|
|
bin interface{} // Symbol from imported bin package if typ.cat is BinT, or nil
|
|
}
|
|
|
|
// Scope type stores the list of visible symbols at current scope level
|
|
type Scope struct {
|
|
anc *Scope // Ancestor upper scope
|
|
level int // Frame level: number of frame indirections to access var
|
|
sym map[string]*Symbol // Symbol table indexed by idents
|
|
}
|
|
|
|
// Create a new scope and chain it to the current one
|
|
func (s *Scope) push(indirect int) *Scope {
|
|
return &Scope{anc: s, level: s.level + indirect, sym: map[string]*Symbol{}}
|
|
}
|
|
|
|
// Lookup for a symbol in the current scope, and upper ones if not found
|
|
func (s *Scope) lookup(ident string) (*Symbol, int, bool) {
|
|
level := s.level
|
|
for s != nil {
|
|
if sym, ok := s.sym[ident]; ok {
|
|
return sym, level - s.level, true
|
|
}
|
|
s = s.anc
|
|
}
|
|
return nil, 0, false
|
|
}
|
|
|
|
// FrameIndex type defines metadata for Tracking frame index for variables in function context
|
|
type FrameIndex struct {
|
|
anc *FrameIndex // Ancestor upper frame
|
|
max int // The highest index in frame
|
|
}
|
|
|
|
// Cfg generates a control flow graph (CFG) from AST (wiring successors in AST)
|
|
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
|
|
// variables. A list of nodes of init functions is returned.
|
|
// Following this pass, the CFG is ready to run
|
|
func (interp *Interpreter) Cfg(root *Node, sdef *NodeMap) []*Node {
|
|
scope := &Scope{sym: map[string]*Symbol{}}
|
|
frameIndex := &FrameIndex{}
|
|
var loop, loopRestart *Node
|
|
var funcDef bool // True if a function is defined in the current frame context
|
|
var initNodes []*Node
|
|
var exports *SymMap
|
|
var expval *ValueMap
|
|
var iotaValue int
|
|
|
|
// Fill root scope with initial symbol definitions
|
|
for name, node := range *sdef {
|
|
scope.sym[name] = &Symbol{node: node, index: -1}
|
|
}
|
|
|
|
root.Walk(func(n *Node) bool {
|
|
// Pre-order processing
|
|
switch n.kind {
|
|
case Define, AssignStmt:
|
|
if l := len(n.child); n.anc.kind == ConstDecl && l == 1 {
|
|
// Implicit iota assignment. TODO: replicate previous explicit assignment
|
|
n.child = append(n.child, &Node{anc: n, interp: interp, kind: Ident, ident: "iota"})
|
|
} else if l%2 == 1 {
|
|
// Odd number of children: remove the type node, useless for assign
|
|
i := l / 2
|
|
n.child = append(n.child[:i], n.child[i+1:]...)
|
|
}
|
|
|
|
case BlockStmt:
|
|
scope = scope.push(0)
|
|
|
|
case File:
|
|
pkgName := n.child[0].ident
|
|
if pkg, ok := interp.Exports[pkgName]; ok {
|
|
exports = pkg
|
|
expval = interp.Expval[pkgName]
|
|
} else {
|
|
x := make(SymMap)
|
|
exports = &x
|
|
interp.Exports[pkgName] = exports
|
|
y := make(ValueMap)
|
|
expval = &y
|
|
interp.Expval[pkgName] = expval
|
|
}
|
|
|
|
case For0, ForRangeStmt:
|
|
loop, loopRestart = n, n.child[0]
|
|
scope = scope.push(0)
|
|
|
|
case For1, For2, For3, For3a, For4:
|
|
loop, loopRestart = n, n.child[len(n.child)-1]
|
|
scope = scope.push(0)
|
|
|
|
case FuncDecl, FuncLit:
|
|
// Add a frame indirection level as we enter in a func
|
|
frameIndex = &FrameIndex{anc: frameIndex}
|
|
scope = scope.push(1)
|
|
if n.child[1].ident == "init" {
|
|
initNodes = append(initNodes, n)
|
|
}
|
|
if len(n.child[0].child) > 0 {
|
|
// function is a method, add it to the related type
|
|
var t *Type
|
|
var tname string
|
|
n.ident = n.child[1].ident
|
|
recv := n.child[0].child[0]
|
|
if len(recv.child) < 2 {
|
|
// Receiver var name is skipped in method declaration (fix that in AST ?)
|
|
tname = recv.child[0].ident
|
|
} else {
|
|
tname = recv.child[1].ident
|
|
}
|
|
if tname == "" {
|
|
tname = recv.child[1].child[0].ident
|
|
elemtype := interp.types[tname]
|
|
t = &Type{cat: PtrT, val: elemtype}
|
|
elemtype.method = append(elemtype.method, n)
|
|
} else {
|
|
t = interp.types[tname]
|
|
}
|
|
t.method = append(t.method, n)
|
|
}
|
|
if len(n.child[2].child) == 2 {
|
|
// allocate entries for return values at start of frame
|
|
frameIndex.max += len(n.child[2].child[1].child)
|
|
}
|
|
funcDef = false
|
|
|
|
case If0, If1, If2, If3:
|
|
scope = scope.push(0)
|
|
|
|
case Switch0:
|
|
// Make sure default clause is in last position
|
|
c := n.child[1].child
|
|
if i, l := getDefault(n), len(c)-1; i >= 0 && i != l {
|
|
c[i], c[l] = c[l], c[i]
|
|
}
|
|
scope = scope.push(0)
|
|
|
|
case TypeSpec:
|
|
// Type analysis is performed recursively and no post-order processing
|
|
// needs to be done for types, so do not dive in subtree
|
|
typeName := n.child[0].ident
|
|
if n.child[1].kind == Ident {
|
|
// Create a type alias of an existing one
|
|
interp.types[typeName] = &Type{cat: AliasT, val: nodeType(interp, n.child[1])}
|
|
} else {
|
|
// Define a new type
|
|
interp.types[typeName] = nodeType(interp, n.child[1])
|
|
}
|
|
//if canExport(typeName) {
|
|
// (*exports)[funcName] = reflect.MakeFunc(n.child[2].typ.TypeOf(), n.wrapNode).Interface()
|
|
//}
|
|
return false
|
|
|
|
case ArrayType, BasicLit, ChanType, MapType, StructType:
|
|
n.typ = nodeType(interp, n)
|
|
return false
|
|
}
|
|
return true
|
|
}, func(n *Node) {
|
|
// Post-order processing
|
|
switch n.kind {
|
|
case Address:
|
|
wireChild(n)
|
|
n.typ = &Type{cat: PtrT, val: n.child[0].typ}
|
|
|
|
case ArrayType:
|
|
// TODO: move to pre-processing ? See when handling complex array type def
|
|
n.typ = &Type{cat: ArrayT, val: interp.types[n.child[1].ident]}
|
|
|
|
case Define, AssignStmt:
|
|
wireChild(n)
|
|
if n.kind == Define || n.anc.kind == VarDecl {
|
|
// Force definition of assigned ident in current scope
|
|
frameIndex.max++
|
|
scope.sym[n.child[0].ident] = &Symbol{index: frameIndex.max}
|
|
n.child[0].findex = frameIndex.max
|
|
n.child[0].typ = n.child[1].typ
|
|
}
|
|
n.findex = n.child[0].findex
|
|
// Propagate type
|
|
// TODO: Check that existing destination type matches source type
|
|
//log.Println(n.index, "Assign child1:", n.child[1].index, n.child[1].typ)
|
|
n.typ = n.child[0].typ
|
|
//n.run = setInt // Temporary, debug
|
|
if sym, level, ok := scope.lookup(n.child[0].ident); ok {
|
|
sym.typ = n.typ
|
|
n.level = level
|
|
}
|
|
// If LHS is an indirection, get reference instead of value, to allow setting
|
|
if n.child[0].action == GetIndex {
|
|
if n.child[0].child[0].typ.cat == MapT {
|
|
n.child[0].run = getMap
|
|
n.run = assignMap
|
|
} else if n.child[0].child[0].typ.cat == PtrT {
|
|
// Handle the case where the receiver is a pointer to an object
|
|
n.child[0].run = getPtrIndexAddr
|
|
n.run = assignPtrField
|
|
} else {
|
|
n.child[0].run = getIndexAddr
|
|
n.run = assignField
|
|
}
|
|
} else if n.child[0].action == Star {
|
|
n.findex = n.child[0].child[0].findex
|
|
n.run = indirectAssign
|
|
}
|
|
if n.anc.kind == ConstDecl {
|
|
iotaValue++
|
|
}
|
|
|
|
case IncDecStmt:
|
|
wireChild(n)
|
|
n.findex = n.child[0].findex
|
|
n.child[0].typ = interp.types["int"]
|
|
n.typ = n.child[0].typ
|
|
if sym, level, ok := scope.lookup(n.child[0].ident); ok {
|
|
sym.typ = n.typ
|
|
n.level = level
|
|
}
|
|
if n.child[0].action == Star {
|
|
n.findex = n.child[0].child[0].findex
|
|
n.run = indirectInc
|
|
}
|
|
|
|
case DefineX, AssignXStmt:
|
|
wireChild(n)
|
|
l := len(n.child) - 1
|
|
if n.kind == DefineX {
|
|
// retrieve assigned value types from call signature
|
|
var types []*Type
|
|
switch n.child[l].kind {
|
|
case CallExpr:
|
|
if funtype := n.child[l].child[0].typ; funtype.cat == ValueT {
|
|
// Handle functions imported from runtime
|
|
for i := 0; i < funtype.rtype.NumOut(); i++ {
|
|
types = append(types, &Type{cat: ValueT, rtype: funtype.rtype.Out(i)})
|
|
}
|
|
} else {
|
|
types = funtype.ret
|
|
}
|
|
case TypeAssertExpr, IndexExpr:
|
|
types = append(types, n.child[l].child[1].typ, defaultTypes["error"])
|
|
default:
|
|
log.Fatalln(n.index, "Assign expression unsupported:", n.child[l].kind)
|
|
}
|
|
// Force definition of assigned idents in current scope
|
|
for i, c := range n.child[:l] {
|
|
frameIndex.max++
|
|
scope.sym[c.ident] = &Symbol{index: frameIndex.max, typ: types[i]}
|
|
c.findex = frameIndex.max
|
|
}
|
|
}
|
|
|
|
case BinaryExpr:
|
|
wireChild(n)
|
|
frameIndex.max++
|
|
n.findex = frameIndex.max
|
|
n.typ = n.child[0].typ
|
|
|
|
case IndexExpr:
|
|
wireChild(n)
|
|
frameIndex.max++
|
|
n.findex = frameIndex.max
|
|
n.typ = n.child[0].typ.val
|
|
n.recv = n
|
|
if n.child[0].typ.cat == MapT {
|
|
frameIndex.max++ // Reserve an entry for getIndexMap 2nd return value
|
|
n.run = getIndexMap
|
|
}
|
|
|
|
case BlockStmt:
|
|
wireChild(n)
|
|
n.findex = n.child[len(n.child)-1].findex
|
|
scope = scope.anc
|
|
|
|
case ConstDecl:
|
|
wireChild(n)
|
|
iotaValue = 0
|
|
|
|
case DeclStmt, ExprStmt, VarDecl, ParenExpr, SendStmt:
|
|
wireChild(n)
|
|
n.findex = n.child[len(n.child)-1].findex
|
|
|
|
case Break:
|
|
n.tnext = loop
|
|
|
|
case CallExpr:
|
|
wireChild(n)
|
|
frameIndex.max++
|
|
n.findex = frameIndex.max
|
|
if builtin, ok := goBuiltin[n.child[0].ident]; ok {
|
|
n.run = builtin
|
|
n.child[0].typ = &Type{cat: BuiltinT}
|
|
if n.child[0].ident == "make" {
|
|
if n.typ = interp.types[n.child[1].ident]; n.typ == nil {
|
|
n.typ = nodeType(interp, n.child[1])
|
|
}
|
|
n.child[1].val = n.typ
|
|
n.child[1].kind = BasicLit
|
|
}
|
|
}
|
|
// TODO: Should process according to child type, not kind.
|
|
if n.child[0].kind == SelectorImport {
|
|
n.fsize = n.child[0].fsize
|
|
rtype := n.child[0].val.(reflect.Value).Type()
|
|
if rtype.NumOut() > 0 {
|
|
n.typ = &Type{cat: ValueT, rtype: rtype.Out(0)}
|
|
}
|
|
n.child[0].kind = BasicLit
|
|
for i, c := range n.child[1:] {
|
|
// Wrap function defintion so it can be called from runtime
|
|
if c.kind == FuncLit {
|
|
n.child[1+i].rval = reflect.MakeFunc(rtype.In(i), c.wrapNode)
|
|
n.child[1+i].kind = Rvalue
|
|
} else if c.ident == "nil" {
|
|
n.child[1+i].rval = reflect.New(rtype.In(i)).Elem()
|
|
n.child[1+i].kind = Rvalue
|
|
} else if c.typ != nil && c.typ.cat == FuncT {
|
|
n.child[1+i].rval = reflect.MakeFunc(rtype.In(i), c.wrapNode)
|
|
n.child[1+i].kind = Rvalue
|
|
}
|
|
}
|
|
// TODO: handle multiple return value
|
|
if len(n.child) == 2 && n.child[1].fsize > 1 {
|
|
n.run = callBinX
|
|
} else {
|
|
n.run = callBin
|
|
}
|
|
} else if n.child[0].kind == SelectorExpr {
|
|
if n.child[0].typ.cat == ValueT {
|
|
n.run = callBinMethod
|
|
// TODO: handle multiple return value (_test/time2.go)
|
|
n.child[0].kind = BasicLit // Temporary hack for force value() to return n.val at run
|
|
n.typ = &Type{cat: ValueT, rtype: n.child[0].typ.rtype}
|
|
n.fsize = n.child[0].fsize
|
|
} else if n.child[0].typ.cat == PtrT && n.child[0].typ.val.cat == ValueT {
|
|
n.run = callBinMethod
|
|
n.child[0].kind = BasicLit // Temporary hack for force value() to return n.val at run
|
|
n.fsize = n.child[0].fsize
|
|
// TODO: handle type ?
|
|
} else if n.child[0].typ.cat == SrcPkgT {
|
|
n.val = n.child[0].val
|
|
if def := n.val.(*Node); def != nil {
|
|
// Reserve as many frame entries as nb of ret values for called function
|
|
// node frame index should point to the first entry
|
|
j := len(def.child[2].child) - 1
|
|
l := len(def.child[2].child[j].child) // Number of return values for def
|
|
if l == 1 {
|
|
// If def returns exactly one value, propagate its type in call node.
|
|
// Multiple return values will be handled differently through AssignX.
|
|
n.typ = interp.types[def.child[2].child[j].child[0].child[0].ident]
|
|
}
|
|
n.fsize = l
|
|
}
|
|
} else {
|
|
// Resolve method and receiver path, store them in node static value for run
|
|
if methodDecl := n.child[0].val.(*Node); len(methodDecl.child[2].child) > 1 {
|
|
// Allocate frame for method return values (if any)
|
|
n.fsize = len(methodDecl.child[2].child[1].child)
|
|
} else {
|
|
n.fsize = 0
|
|
}
|
|
n.child[0].findex = -1 // To force reading value from node instead of frame (methods)
|
|
}
|
|
} else if sym, _, _ := scope.lookup(n.child[0].ident); sym != nil {
|
|
if sym.typ != nil && sym.typ.cat == BinT {
|
|
n.run = callBin
|
|
n.typ = &Type{cat: ValueT}
|
|
n.child[0].fsize = reflect.TypeOf(sym.bin).NumOut()
|
|
n.child[0].val = reflect.ValueOf(sym.bin)
|
|
n.child[0].kind = BasicLit
|
|
} else if sym.typ != nil && sym.typ.cat == ValueT {
|
|
n.run = callDirectBin
|
|
n.typ = &Type{cat: ValueT}
|
|
} else {
|
|
n.val = sym.node
|
|
if def := n.val.(*Node); def != nil {
|
|
// Reserve as many frame entries as nb of ret values for called function
|
|
// node frame index should point to the first entry
|
|
j := len(def.child[2].child) - 1
|
|
l := len(def.child[2].child[j].child) // Number of return values for def
|
|
if l == 1 {
|
|
// If def returns exactly one value, propagate its type in call node.
|
|
// Multiple return values will be handled differently through AssignX.
|
|
n.typ = interp.types[def.child[2].child[j].child[0].child[0].ident]
|
|
}
|
|
n.fsize = l
|
|
}
|
|
}
|
|
}
|
|
// Reserve entries in frame to store results of call
|
|
frameIndex.max += n.fsize
|
|
if funcDef {
|
|
// Trigger frame indirection to handle nested functions
|
|
n.action = CallF
|
|
}
|
|
//log.Println(n.index, "callExpr:", n.child[0].ident, "frame index:", n.findex)
|
|
|
|
case CaseClause:
|
|
frameIndex.max++
|
|
n.findex = frameIndex.max
|
|
n.tnext = n.child[len(n.child)-1].start
|
|
|
|
case CompositeLitExpr:
|
|
wireChild(n)
|
|
frameIndex.max++
|
|
n.findex = frameIndex.max
|
|
if n.child[0].typ == nil {
|
|
n.child[0].typ = interp.types[n.child[0].ident]
|
|
}
|
|
// TODO: Check that composite litteral expr matches corresponding type
|
|
n.typ = n.child[0].typ
|
|
switch n.typ.cat {
|
|
case ArrayT:
|
|
n.run = arrayLit
|
|
case MapT:
|
|
n.run = mapLit
|
|
case StructT:
|
|
n.action, n.run = CompositeLit, compositeLit
|
|
// Handle object assign from sparse key / values
|
|
if len(n.child) > 1 && n.child[1].kind == KeyValueExpr {
|
|
n.run = compositeSparse
|
|
n.typ = interp.types[n.child[0].ident]
|
|
for _, c := range n.child[1:] {
|
|
c.findex = n.typ.fieldIndex(c.child[0].ident)
|
|
}
|
|
}
|
|
}
|
|
|
|
case Continue:
|
|
n.tnext = loopRestart
|
|
|
|
case Field:
|
|
// A single child node (no ident, just type) means that the field refers
|
|
// to a return value, and space on frame should be accordingly allocated.
|
|
// Otherwise, just point to corresponding location in frame, resolved in
|
|
// ident child.
|
|
l := len(n.child) - 1
|
|
n.typ = nodeType(interp, n.child[l])
|
|
if l == 0 {
|
|
frameIndex.max++
|
|
n.findex = frameIndex.max
|
|
} else {
|
|
for _, f := range n.child[:l] {
|
|
scope.sym[f.ident].typ = n.typ
|
|
}
|
|
}
|
|
|
|
case File:
|
|
wireChild(n)
|
|
n.fsize = frameIndex.max + 1
|
|
|
|
case For0: // for {}
|
|
body := n.child[0]
|
|
n.start = body.start
|
|
body.tnext = n.start
|
|
loop, loopRestart = nil, nil
|
|
scope = scope.anc
|
|
|
|
case For1: // for cond {}
|
|
cond, body := n.child[0], n.child[1]
|
|
n.start = cond.start
|
|
cond.tnext = body.start
|
|
cond.fnext = n
|
|
body.tnext = cond.start
|
|
loop, loopRestart = nil, nil
|
|
scope = scope.anc
|
|
|
|
case For2: // for init; cond; {}
|
|
init, cond, body := n.child[0], n.child[1], n.child[2]
|
|
n.start = init.start
|
|
init.tnext = cond.start
|
|
cond.tnext = body.start
|
|
cond.fnext = n
|
|
body.tnext = cond.start
|
|
loop, loopRestart = nil, nil
|
|
scope = scope.anc
|
|
|
|
case For3: // for ; cond; post {}
|
|
cond, post, body := n.child[0], n.child[1], n.child[2]
|
|
n.start = cond.start
|
|
cond.tnext = body.start
|
|
cond.fnext = n
|
|
body.tnext = post.start
|
|
post.tnext = cond.start
|
|
loop, loopRestart = nil, nil
|
|
scope = scope.anc
|
|
|
|
case For3a: // for int; ; post {}
|
|
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
|
|
loop, loopRestart = nil, nil
|
|
scope = scope.anc
|
|
|
|
case For4: // for init; cond; post {}
|
|
init, cond, post, body := n.child[0], n.child[1], n.child[2], n.child[3]
|
|
n.start = init.start
|
|
init.tnext = cond.start
|
|
cond.tnext = body.start
|
|
cond.fnext = n
|
|
body.tnext = post.start
|
|
post.tnext = cond.start
|
|
loop, loopRestart = nil, nil
|
|
scope = scope.anc
|
|
|
|
case ForRangeStmt:
|
|
loop, loopRestart = nil, nil
|
|
n.start = n.child[0].start
|
|
n.findex = n.child[0].findex
|
|
n.child[0].fnext = n
|
|
scope = scope.anc
|
|
|
|
case FuncDecl:
|
|
n.findex = frameIndex.max + 1
|
|
if len(n.child[0].child) > 0 {
|
|
// Store receiver frame location (used at run)
|
|
n.child[0].findex = n.child[0].child[0].child[0].findex
|
|
}
|
|
scope = scope.anc
|
|
frameIndex = frameIndex.anc
|
|
funcName := n.child[1].ident
|
|
if canExport(funcName) {
|
|
(*exports)[funcName] = reflect.MakeFunc(n.child[2].typ.TypeOf(), n.wrapNode).Interface()
|
|
(*expval)[funcName] = reflect.MakeFunc(n.child[2].typ.TypeOf(), n.wrapNode)
|
|
}
|
|
n.typ = n.child[2].typ
|
|
n.val = n
|
|
scope.sym[funcName].typ = n.typ
|
|
|
|
case FuncLit:
|
|
n.typ = n.child[2].typ
|
|
n.val = n
|
|
n.findex = -1
|
|
n.findex = frameIndex.max + 1
|
|
scope = scope.anc
|
|
frameIndex = frameIndex.anc
|
|
funcDef = true
|
|
|
|
case FuncType:
|
|
n.typ = nodeType(interp, n)
|
|
// Store list of parameter frame indices in params val
|
|
var list []int
|
|
for _, c := range n.child[0].child {
|
|
for _, f := range c.child[:len(c.child)-1] {
|
|
list = append(list, f.findex)
|
|
}
|
|
}
|
|
n.child[0].val = list
|
|
// TODO: do the same for return values
|
|
|
|
case GoStmt:
|
|
wireChild(n)
|
|
// TODO: should error if call expression refers to a builtin
|
|
n.child[0].run = callGoRoutine
|
|
|
|
case Ident:
|
|
if n.ident == "false" {
|
|
n.val = false
|
|
n.typ = defaultTypes["bool"]
|
|
n.kind = BasicLit
|
|
} else if n.ident == "true" {
|
|
n.val = true
|
|
n.typ = defaultTypes["bool"]
|
|
n.kind = BasicLit
|
|
} else if n.ident == "iota" {
|
|
n.val = iotaValue
|
|
n.typ = defaultTypes["int"]
|
|
n.kind = BasicLit
|
|
} else if sym, level, ok := scope.lookup(n.ident); ok {
|
|
n.typ, n.findex, n.level = sym.typ, sym.index, level
|
|
if n.findex < 0 {
|
|
n.val = sym.node
|
|
} else if n.typ != nil {
|
|
if n.typ.cat == BinPkgT {
|
|
n.val = sym.pkgbin
|
|
} else if n.typ.cat == SrcPkgT {
|
|
log.Println(n.index, "ident SrcPkgT", n.ident)
|
|
n.val = sym.pkgsrc
|
|
}
|
|
}
|
|
n.recv = n
|
|
} else {
|
|
frameIndex.max++
|
|
scope.sym[n.ident] = &Symbol{index: frameIndex.max}
|
|
n.findex = frameIndex.max
|
|
}
|
|
|
|
case If0: // if cond {}
|
|
cond, tbody := n.child[0], n.child[1]
|
|
n.start = cond.start
|
|
cond.tnext = tbody.start
|
|
cond.fnext = n
|
|
tbody.tnext = n
|
|
scope = scope.anc
|
|
|
|
case If1: // if cond {} else {}
|
|
cond, tbody, fbody := n.child[0], n.child[1], n.child[2]
|
|
n.start = cond.start
|
|
cond.tnext = tbody.start
|
|
cond.fnext = fbody.start
|
|
tbody.tnext = n
|
|
fbody.tnext = n
|
|
scope = scope.anc
|
|
|
|
case If2: // if init; cond {}
|
|
init, cond, tbody := n.child[0], n.child[1], n.child[2]
|
|
n.start = init.start
|
|
tbody.tnext = n
|
|
init.tnext = cond.start
|
|
cond.tnext = tbody.start
|
|
cond.fnext = n
|
|
scope = scope.anc
|
|
|
|
case If3: // if init; cond {} else {}
|
|
init, cond, tbody, fbody := n.child[0], n.child[1], n.child[2], n.child[3]
|
|
n.start = init.start
|
|
init.tnext = cond.start
|
|
cond.tnext = tbody.start
|
|
cond.fnext = fbody.start
|
|
tbody.tnext = n
|
|
fbody.tnext = n
|
|
scope = scope.anc
|
|
|
|
case ImportSpec:
|
|
var name, ipath string
|
|
if len(n.child) == 2 {
|
|
ipath = n.child[1].val.(string)
|
|
name = n.child[0].ident
|
|
} else {
|
|
ipath = n.child[0].val.(string)
|
|
name = path.Base(ipath)
|
|
}
|
|
if pkg, ok := interp.binPkg[ipath]; ok {
|
|
if name == "." {
|
|
for n, s := range *pkg {
|
|
scope.sym[n] = &Symbol{typ: &Type{cat: BinT}, bin: s}
|
|
}
|
|
} else {
|
|
scope.sym[name] = &Symbol{typ: &Type{cat: BinPkgT}, pkgbin: pkg}
|
|
}
|
|
} else {
|
|
// TODO: make sure we do not import a src package more than once
|
|
interp.importSrcFile(ipath)
|
|
scope.sym[name] = &Symbol{typ: &Type{cat: SrcPkgT}, pkgsrc: interp.srcPkg[name]}
|
|
}
|
|
|
|
case KeyValueExpr:
|
|
wireChild(n)
|
|
|
|
case LandExpr:
|
|
n.start = n.child[0].start
|
|
n.child[0].tnext = n.child[1].start
|
|
n.child[0].fnext = n
|
|
n.child[1].tnext = n
|
|
frameIndex.max++
|
|
n.findex = frameIndex.max
|
|
n.typ = n.child[0].typ
|
|
|
|
case LorExpr:
|
|
n.start = n.child[0].start
|
|
n.child[0].tnext = n
|
|
n.child[0].fnext = n.child[1].start
|
|
n.child[1].tnext = n
|
|
frameIndex.max++
|
|
n.findex = frameIndex.max
|
|
n.typ = n.child[0].typ
|
|
|
|
case RangeStmt:
|
|
n.start = n
|
|
n.child[3].tnext = n
|
|
n.tnext = n.child[3].start
|
|
frameIndex.max++
|
|
n.findex = frameIndex.max
|
|
|
|
case ReturnStmt:
|
|
wireChild(n)
|
|
n.tnext = nil
|
|
|
|
case SelectorExpr:
|
|
wireChild(n)
|
|
frameIndex.max++
|
|
n.findex = frameIndex.max
|
|
n.typ = n.child[0].typ
|
|
n.recv = n.child[0].recv
|
|
if n.typ == nil {
|
|
log.Fatal("typ should not be nil:", n.index, n.child[0])
|
|
}
|
|
if n.typ.cat == ValueT {
|
|
// Handle object defined in runtime
|
|
if method, ok := n.typ.rtype.MethodByName(n.child[1].ident); ok {
|
|
if method.Func.IsValid() {
|
|
n.rval = method.Func
|
|
n.run = nop
|
|
} else {
|
|
n.val = method.Index
|
|
//n.run = getIndexBinMethod
|
|
n.run = nop
|
|
}
|
|
n.fsize = method.Type.NumOut()
|
|
}
|
|
} else if n.typ.cat == PtrT && n.typ.val.cat == ValueT {
|
|
// Handle pointer on object defined in runtime
|
|
if field, ok := n.typ.val.rtype.FieldByName(n.child[1].ident); ok {
|
|
n.typ = &Type{cat: ValueT, rtype: field.Type}
|
|
n.val = field.Index
|
|
n.run = getPtrIndexBin
|
|
} else if method, ok := reflect.PtrTo(n.typ.val.rtype).MethodByName(n.child[1].ident); ok {
|
|
n.val = method.Func
|
|
n.fsize = method.Type.NumOut()
|
|
n.run = nop
|
|
}
|
|
} else if n.typ.cat == BinPkgT {
|
|
// Resolve binary package symbol
|
|
name := n.child[1].ident
|
|
pkgSym := n.child[0].val.(*SymMap)
|
|
if s, ok := (*pkgSym)[name]; ok {
|
|
n.kind = SelectorImport
|
|
n.val = reflect.ValueOf(s)
|
|
if typ := reflect.TypeOf(s); typ.Kind() == reflect.Func {
|
|
n.typ = &Type{cat: ValueT, rtype: typ}
|
|
n.fsize = typ.NumOut()
|
|
//} else if typ.Kind() == reflect.Ptr {
|
|
// a symbol of kind pointer must be dereferenced to access type
|
|
// n.typ = &Type{cat: ValueT, rtype: typ.Elem(), rzero: n.val.(reflect.Value).Elem()}
|
|
} else {
|
|
n.typ = &Type{cat: ValueT, rtype: typ}
|
|
n.rval = n.val.(reflect.Value)
|
|
n.kind = Rvalue
|
|
}
|
|
n.run = nop
|
|
}
|
|
} else if n.typ.cat == ArrayT {
|
|
n.typ = n.typ.val
|
|
n.run = nop
|
|
} else if n.typ.cat == SrcPkgT {
|
|
// Resolve source package symbol
|
|
pkgSrc := n.child[0].val.(*NodeMap)
|
|
name := n.child[1].ident
|
|
if node, ok := (*pkgSrc)[name]; ok {
|
|
n.val = node
|
|
n.run = nop
|
|
n.kind = SelectorSrc
|
|
}
|
|
} else if fi := n.typ.fieldIndex(n.child[1].ident); fi >= 0 {
|
|
// Resolve struct field index
|
|
if n.typ.cat == PtrT {
|
|
n.run = getPtrIndex
|
|
}
|
|
n.typ = n.typ.fieldType(fi)
|
|
n.child[1].kind = BasicLit
|
|
n.child[1].val = fi
|
|
} else if m, lind := n.typ.lookupMethod(n.child[1].ident); m != nil {
|
|
// Handle method
|
|
n.run = nop
|
|
n.val = m
|
|
n.child[1].val = lind
|
|
n.typ = m.typ
|
|
} else {
|
|
// Handle promoted field in embedded struct
|
|
if ti := n.typ.lookupField(n.child[1].ident); len(ti) > 0 {
|
|
n.child[1].kind = BasicLit
|
|
n.child[1].val = ti
|
|
n.run = getIndexSeq
|
|
} else {
|
|
log.Println(n.index, "Selector not found:", n.child[1].ident)
|
|
n.run = nop
|
|
//panic("Field not found in selector")
|
|
}
|
|
}
|
|
|
|
case Switch0:
|
|
n.start = n.child[1].start
|
|
// Chain case clauses
|
|
clauses := n.child[1].child
|
|
l := len(clauses)
|
|
for i, c := range clauses[:l-1] {
|
|
// chain to next clause
|
|
c.tnext = c.child[1].start
|
|
c.child[1].tnext = n
|
|
c.fnext = clauses[i+1]
|
|
}
|
|
// Handle last clause
|
|
if c := clauses[l-1]; len(c.child) > 1 {
|
|
// No default clause
|
|
c.tnext = c.child[1].start
|
|
c.fnext = n
|
|
c.child[1].tnext = n
|
|
} else {
|
|
// Default
|
|
c.tnext = c.child[0].start
|
|
c.child[0].tnext = n
|
|
}
|
|
scope = scope.anc
|
|
|
|
case TypeAssertExpr:
|
|
if n.child[1].typ == nil {
|
|
n.child[1].typ = interp.types[n.child[1].ident]
|
|
}
|
|
n.typ = n.child[1].typ
|
|
|
|
case ValueSpec:
|
|
l := len(n.child) - 1
|
|
if n.typ = n.child[l].typ; n.typ == nil {
|
|
n.typ = interp.types[n.child[l].ident]
|
|
}
|
|
for _, c := range n.child[:l] {
|
|
c.typ = n.typ
|
|
scope.sym[c.ident].typ = n.typ
|
|
}
|
|
}
|
|
})
|
|
return initNodes
|
|
}
|
|
|
|
// Find default case clause index of a switch statement, if any
|
|
func getDefault(n *Node) int {
|
|
for i, c := range n.child[1].child {
|
|
if len(c.child) == 1 {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// isType returns true if node refers to a type definition, false otherwise
|
|
func (n *Node) isType() bool {
|
|
switch n.kind {
|
|
case ArrayType, ChanType, FuncType, MapType, StructType:
|
|
return true
|
|
case Ident:
|
|
_, ok := n.interp.types[n.ident]
|
|
return ok
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Wire AST nodes for CFG in subtree
|
|
func wireChild(n *Node) {
|
|
// Set start node, in subtree (propagated to ancestors by post-order processing)
|
|
for _, child := range n.child {
|
|
switch child.kind {
|
|
case ArrayType, ChanType, FuncDecl, MapType, BasicLit, FuncLit, Ident:
|
|
continue
|
|
default:
|
|
n.start = child.start
|
|
}
|
|
break
|
|
}
|
|
|
|
// Chain sequential operations inside a block (next is right sibling)
|
|
for i := 1; i < len(n.child); i++ {
|
|
n.child[i-1].tnext = n.child[i].start
|
|
}
|
|
|
|
// Chain subtree next to self
|
|
for i := len(n.child) - 1; i >= 0; i-- {
|
|
switch n.child[i].kind {
|
|
case ArrayType, ChanType, MapType, FuncDecl, FuncLit, BasicLit, Ident:
|
|
continue
|
|
case Break, Continue, ReturnStmt:
|
|
// tnext is already computed, no change
|
|
default:
|
|
n.child[i].tnext = n
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
func canExport(name string) bool {
|
|
if r := []rune(name); len(r) > 0 && unicode.IsUpper(r[0]) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|