interp: fix memory handling of global values
In some cases, the global character of a value was lost, leading to undefined behaviour. Now a node level field of -1 means that the value is global, and that it should be accessed from the root data frame. Fixes #993.
This commit is contained in:
15
_test/issue-993.go
Normal file
15
_test/issue-993.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
var m map[string]int64
|
||||
|
||||
func initVar() {
|
||||
m = make(map[string]int64)
|
||||
}
|
||||
|
||||
func main() {
|
||||
initVar()
|
||||
println(len(m))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
15
_test/var15.go
Normal file
15
_test/var15.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
var a int = 2
|
||||
|
||||
func inca() {
|
||||
a = a + 1
|
||||
}
|
||||
|
||||
func main() {
|
||||
inca()
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
@@ -611,7 +611,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
}
|
||||
if n.anc.kind == constDecl {
|
||||
n.gen = nop
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
if sym, _, ok := sc.lookup(dest.ident); ok {
|
||||
sym.kind = constSym
|
||||
}
|
||||
@@ -717,7 +717,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
// This operation involved constants, and the result is already computed
|
||||
// by constOp and available in n.rval. Nothing else to do at execution.
|
||||
n.gen = nop
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
case n.anc.kind == assignStmt && n.anc.action == aAssign:
|
||||
// To avoid a copy in frame, if the result is to be assigned, store it directly
|
||||
// at the frame location of destination.
|
||||
@@ -858,7 +858,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
}
|
||||
switch {
|
||||
case n.typ.cat == builtinT:
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
n.val = nil
|
||||
case n.anc.kind == returnStmt:
|
||||
// Store result directly to frame output location, to avoid a frame copy.
|
||||
@@ -900,7 +900,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
n.rval = c1.rval
|
||||
case c1.rval.IsValid() && isConstType(c0.typ):
|
||||
n.gen = nop
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
n.typ = c0.typ
|
||||
if c, ok := c1.rval.Interface().(constant.Value); ok {
|
||||
i, _ := constant.Int64Val(constant.ToInt(c))
|
||||
@@ -963,7 +963,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1044,7 +1044,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
case fileStmt:
|
||||
wireChild(n, varDecl)
|
||||
sc = sc.pop()
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
|
||||
case forStmt0: // for {}
|
||||
body := n.child[0]
|
||||
@@ -1502,6 +1502,9 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
// Resolve source package symbol
|
||||
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
|
||||
@@ -1516,7 +1519,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
if n.child[0].isType(sc) {
|
||||
// Handle method as a function with receiver in 1st argument
|
||||
n.val = m
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
n.gen = nop
|
||||
n.typ = &itype{}
|
||||
*n.typ = *m.typ
|
||||
@@ -1829,7 +1832,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
switch {
|
||||
case n.rval.IsValid():
|
||||
n.gen = nop
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
case n.anc.kind == assignStmt && n.anc.action == aAssign:
|
||||
dest := n.anc.child[childPos(n)-n.anc.nright]
|
||||
n.typ = dest.typ
|
||||
@@ -1856,6 +1859,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
if sc.global {
|
||||
// Global object allocation is already performed in GTA.
|
||||
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}
|
||||
|
||||
@@ -115,6 +115,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
|
||||
sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ, node: n}
|
||||
continue
|
||||
}
|
||||
c.level = globalFrame
|
||||
|
||||
// redeclaration error
|
||||
if sym.typ.node != nil && sym.typ.node.anc != nil {
|
||||
|
||||
@@ -65,7 +65,8 @@ type frame struct {
|
||||
// Located at start of struct to ensure proper aligment.
|
||||
id uint64
|
||||
|
||||
anc *frame // ancestor frame (global space)
|
||||
root *frame // global space
|
||||
anc *frame // ancestor frame (caller space)
|
||||
data []reflect.Value // values
|
||||
|
||||
mutex sync.RWMutex
|
||||
@@ -80,8 +81,11 @@ func newFrame(anc *frame, len int, id uint64) *frame {
|
||||
data: make([]reflect.Value, len),
|
||||
id: id,
|
||||
}
|
||||
if anc != nil {
|
||||
if anc == nil {
|
||||
f.root = f
|
||||
} else {
|
||||
f.done = anc.done
|
||||
f.root = anc.root
|
||||
}
|
||||
return f
|
||||
}
|
||||
@@ -93,6 +97,7 @@ func (f *frame) clone() *frame {
|
||||
defer f.mutex.RUnlock()
|
||||
return &frame{
|
||||
anc: f.anc,
|
||||
root: f.root,
|
||||
data: f.data,
|
||||
deferred: f.deferred,
|
||||
recovered: f.recovered,
|
||||
@@ -239,7 +244,7 @@ type Options struct {
|
||||
func New(options Options) *Interpreter {
|
||||
i := Interpreter{
|
||||
opt: opt{context: build.Default},
|
||||
frame: &frame{data: []reflect.Value{}},
|
||||
frame: newFrame(nil, 0, 0),
|
||||
fset: token.NewFileSet(),
|
||||
universe: initUniverse(),
|
||||
scopes: map[string]*scope{},
|
||||
|
||||
@@ -1221,6 +1221,8 @@ func call(n *node) {
|
||||
|
||||
func getFrame(f *frame, l int) *frame {
|
||||
switch l {
|
||||
case globalFrame:
|
||||
return f.root
|
||||
case 0:
|
||||
return f
|
||||
case 1:
|
||||
|
||||
@@ -130,6 +130,9 @@ func (s *scope) lookup(ident string) (*symbol, int, bool) {
|
||||
level := s.level
|
||||
for {
|
||||
if sym, ok := s.sym[ident]; ok {
|
||||
if sym.global {
|
||||
return sym, globalFrame, true
|
||||
}
|
||||
return sym, level - s.level, true
|
||||
}
|
||||
if s.anc == nil {
|
||||
|
||||
@@ -5,8 +5,15 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const (
|
||||
notInFrame = -1 // value of node.findex for literal values (not in frame)
|
||||
globalFrame = -1 // value of node.level for global symbols
|
||||
)
|
||||
|
||||
func valueGenerator(n *node, i int) func(*frame) reflect.Value {
|
||||
switch n.level {
|
||||
case globalFrame:
|
||||
return func(f *frame) reflect.Value { return valueOf(f.root.data, i) }
|
||||
case 0:
|
||||
return func(f *frame) reflect.Value { return valueOf(f.data, i) }
|
||||
case 1:
|
||||
@@ -171,18 +178,16 @@ func genValue(n *node) func(*frame) reflect.Value {
|
||||
return func(f *frame) reflect.Value { return v }
|
||||
}
|
||||
if n.sym != nil {
|
||||
if n.sym.index < 0 {
|
||||
i := n.sym.index
|
||||
if i < 0 {
|
||||
return genValue(n.sym.node)
|
||||
}
|
||||
i := n.sym.index
|
||||
if n.sym.global {
|
||||
return func(f *frame) reflect.Value {
|
||||
return n.interp.frame.data[i]
|
||||
}
|
||||
return func(f *frame) reflect.Value { return f.root.data[i] }
|
||||
}
|
||||
return valueGenerator(n, i)
|
||||
}
|
||||
if n.findex < 0 {
|
||||
if n.findex == notInFrame {
|
||||
var v reflect.Value
|
||||
if w, ok := n.val.(reflect.Value); ok {
|
||||
v = w
|
||||
|
||||
Reference in New Issue
Block a user