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