forked from TrueCloudLab/neoneo-go
compiler: copy structs when passing as arguments
In Go structs must be copied when used as arguments. To do so we must clone struct on VM level. This is done by appending this struct to an intermediate array.
This commit is contained in:
parent
d54c60ded3
commit
f2107bfbc4
2 changed files with 50 additions and 0 deletions
|
@ -793,6 +793,18 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
// Handle the arguments
|
// Handle the arguments
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
ast.Walk(c, arg)
|
ast.Walk(c, arg)
|
||||||
|
typ := c.typeOf(arg)
|
||||||
|
_, ok := typ.Underlying().(*types.Struct)
|
||||||
|
if ok && !isInteropPath(typ.String()) {
|
||||||
|
// To clone struct fields we create a new array and append struct to it.
|
||||||
|
// This way even non-pointer struct fields will be copied.
|
||||||
|
emit.Opcode(c.prog.BinWriter, opcode.NEWARRAY0)
|
||||||
|
emit.Opcode(c.prog.BinWriter, opcode.DUP)
|
||||||
|
emit.Opcode(c.prog.BinWriter, opcode.ROT)
|
||||||
|
emit.Opcode(c.prog.BinWriter, opcode.APPEND)
|
||||||
|
emit.Opcode(c.prog.BinWriter, opcode.PUSH0)
|
||||||
|
emit.Opcode(c.prog.BinWriter, opcode.PICKITEM)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Do not swap for builtin functions.
|
// Do not swap for builtin functions.
|
||||||
if !isBuiltin {
|
if !isBuiltin {
|
||||||
|
|
|
@ -28,3 +28,41 @@ func TestPointerDereference(t *testing.T) {
|
||||||
func setA(s Foo, a int) { s.A = a }`
|
func setA(s Foo, a int) { s.A = a }`
|
||||||
eval(t, src, big.NewInt(4))
|
eval(t, src, big.NewInt(4))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStructArgCopy(t *testing.T) {
|
||||||
|
t.Run("Simple", func(t *testing.T) {
|
||||||
|
src := `package foo
|
||||||
|
type Foo struct { A int }
|
||||||
|
func Main() int {
|
||||||
|
f := Foo{A: 4}
|
||||||
|
setA(f, 3)
|
||||||
|
return f.A
|
||||||
|
}
|
||||||
|
func setA(s Foo, a int) { s.A = a }`
|
||||||
|
eval(t, src, big.NewInt(4))
|
||||||
|
})
|
||||||
|
t.Run("StructField", func(t *testing.T) {
|
||||||
|
src := `package foo
|
||||||
|
type Bar struct { A int }
|
||||||
|
type Foo struct { B Bar }
|
||||||
|
func Main() int {
|
||||||
|
f := Foo{B: Bar{A: 4}}
|
||||||
|
setA(f, 3)
|
||||||
|
return f.B.A
|
||||||
|
}
|
||||||
|
func setA(s Foo, a int) { s.B.A = a }`
|
||||||
|
eval(t, src, big.NewInt(4))
|
||||||
|
})
|
||||||
|
t.Run("StructPointerField", func(t *testing.T) {
|
||||||
|
src := `package foo
|
||||||
|
type Bar struct { A int }
|
||||||
|
type Foo struct { B *Bar }
|
||||||
|
func Main() int {
|
||||||
|
f := Foo{B: &Bar{A: 4}}
|
||||||
|
setA(f, 3)
|
||||||
|
return f.B.A
|
||||||
|
}
|
||||||
|
func setA(s Foo, a int) { s.B.A = a }`
|
||||||
|
eval(t, src, big.NewInt(3))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue