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:
Marc Vertes
2021-01-05 17:28:03 +01:00
committed by GitHub
parent a83f492309
commit 24b5375636
8 changed files with 67 additions and 17 deletions

15
_test/issue-993.go Normal file
View 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
View File

@@ -0,0 +1,15 @@
package main
var a int = 2
func inca() {
a = a + 1
}
func main() {
inca()
println(a)
}
// Output:
// 3

View File

@@ -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}

View File

@@ -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 {

View File

@@ -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{},

View File

@@ -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:

View File

@@ -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 {

View File

@@ -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