compiler: initialize struct fields explicitly
By default VM initializes every field of a new struct to a Boolean. If field contains another struct, we need to initialize it to default value explicitly. This commit sets default values for uninitialized field.
This commit is contained in:
parent
051e3608ff
commit
5b0e73ddf0
3 changed files with 35 additions and 37 deletions
|
@ -2,9 +2,7 @@ package compiler
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/types"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
|
@ -23,33 +21,6 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
// typeAndValueForField returns a zero initialized typeAndValue for the given type.Var.
|
||||
func typeAndValueForField(fld *types.Var) (types.TypeAndValue, error) {
|
||||
switch t := fld.Type().(type) {
|
||||
case *types.Basic:
|
||||
switch t.Kind() {
|
||||
case types.Int:
|
||||
return types.TypeAndValue{
|
||||
Type: t,
|
||||
Value: constant.MakeInt64(0),
|
||||
}, nil
|
||||
case types.String:
|
||||
return types.TypeAndValue{
|
||||
Type: t,
|
||||
Value: constant.MakeString(""),
|
||||
}, nil
|
||||
case types.Bool, types.UntypedBool:
|
||||
return types.TypeAndValue{
|
||||
Type: t,
|
||||
Value: constant.MakeBool(false),
|
||||
}, nil
|
||||
default:
|
||||
return types.TypeAndValue{}, fmt.Errorf("could not initialize struct field %s to zero, type: %s", fld.Name(), t)
|
||||
}
|
||||
}
|
||||
return types.TypeAndValue{}, nil
|
||||
}
|
||||
|
||||
// newGlobal creates new global variable.
|
||||
func (c *codegen) newGlobal(name string) {
|
||||
c.globals[name] = len(c.globals)
|
||||
|
|
|
@ -239,8 +239,15 @@ func (c *codegen) emitDefault(t types.Type) {
|
|||
emit.Opcode(c.prog.BinWriter, opcode.NEWBUFFER)
|
||||
}
|
||||
case *types.Struct:
|
||||
emit.Int(c.prog.BinWriter, int64(t.NumFields()))
|
||||
num := t.NumFields()
|
||||
emit.Int(c.prog.BinWriter, int64(num))
|
||||
emit.Opcode(c.prog.BinWriter, opcode.NEWSTRUCT)
|
||||
for i := 0; i < num; i++ {
|
||||
emit.Opcode(c.prog.BinWriter, opcode.DUP)
|
||||
emit.Int(c.prog.BinWriter, int64(i))
|
||||
c.emitDefault(t.Field(i).Type())
|
||||
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
|
||||
}
|
||||
default:
|
||||
emit.Opcode(c.prog.BinWriter, opcode.PUSHNULL)
|
||||
}
|
||||
|
@ -1211,15 +1218,9 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
|
|||
continue
|
||||
}
|
||||
|
||||
typeAndVal, err := typeAndValueForField(sField)
|
||||
if err != nil {
|
||||
c.prog.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
emit.Opcode(c.prog.BinWriter, opcode.DUP)
|
||||
emit.Int(c.prog.BinWriter, int64(i))
|
||||
c.emitLoadConst(typeAndVal)
|
||||
c.emitDefault(sField.Type())
|
||||
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -368,6 +368,32 @@ var structTestCases = []testCase{
|
|||
}`,
|
||||
big.NewInt(11),
|
||||
},
|
||||
{
|
||||
"complex struct default value",
|
||||
`package foo
|
||||
type S1 struct { x S2 }
|
||||
type S2 struct { y S3 }
|
||||
type S3 struct { a int }
|
||||
func Main() int {
|
||||
var s1 S1
|
||||
s1.x.y.a = 11
|
||||
return s1.x.y.a
|
||||
}`,
|
||||
big.NewInt(11),
|
||||
},
|
||||
{
|
||||
"nested selectors (complex write)",
|
||||
`package foo
|
||||
type S1 struct { x S2 }
|
||||
type S2 struct { y, z S3 }
|
||||
type S3 struct { a int }
|
||||
func Main() int {
|
||||
var s1 S1
|
||||
s1.x.y.a, s1.x.z.a = 11, 31
|
||||
return s1.x.y.a + s1.x.z.a
|
||||
}`,
|
||||
big.NewInt(42),
|
||||
},
|
||||
}
|
||||
|
||||
func TestStructs(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue