From 904b2136fc4d2cff264053ecb2d726017051def0 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 24 Jun 2020 17:58:15 +0300 Subject: [PATCH] compiler: emit CONVERT opcode for type assertions Emit CONVERT for converting between different types. NeoVM behavior is different from that of Go (e.g. assertions of `int` and `uint32` are equivalent). --- pkg/compiler/codegen.go | 2 ++ pkg/compiler/convert_test.go | 11 +++++++++++ pkg/compiler/types.go | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index b30513dda..7f565ab3d 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -991,6 +991,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { // not the assertion type. case *ast.TypeAssertExpr: ast.Walk(c, n.X) + typ := toNeoType(c.typeOf(n.Type)) + emit.Instruction(c.prog.BinWriter, opcode.CONVERT, []byte{byte(typ)}) return nil } return c diff --git a/pkg/compiler/convert_test.go b/pkg/compiler/convert_test.go index 379d7ce39..e768d7573 100644 --- a/pkg/compiler/convert_test.go +++ b/pkg/compiler/convert_test.go @@ -61,3 +61,14 @@ func TestConvert(t *testing.T) { }) } } + +func TestTypeAssertion(t *testing.T) { + src := `package foo + func Main() int { + a := []byte{1} + var u interface{} + u = a + return u.(int) + }` + eval(t, src, big.NewInt(1)) +} diff --git a/pkg/compiler/types.go b/pkg/compiler/types.go index 2011220fc..77bbcfc1c 100644 --- a/pkg/compiler/types.go +++ b/pkg/compiler/types.go @@ -3,6 +3,8 @@ package compiler import ( "go/ast" "go/types" + + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) func (c *codegen) typeAndValueOf(e ast.Expr) types.TypeAndValue { @@ -42,3 +44,34 @@ func isByteSlice(typ types.Type) bool { t, ok := typ.Underlying().(*types.Slice) return ok && isByte(t.Elem()) } + +func toNeoType(typ types.Type) stackitem.Type { + if typ == nil { + return stackitem.AnyT + } + switch t := typ.Underlying().(type) { + case *types.Basic: + info := t.Info() + switch { + case info&types.IsInteger != 0: + return stackitem.IntegerT + case info&types.IsBoolean != 0: + return stackitem.BooleanT + case info&types.IsString != 0: + return stackitem.ByteArrayT + default: + return stackitem.AnyT + } + case *types.Map: + return stackitem.MapT + case *types.Struct: + return stackitem.StructT + case *types.Slice: + if isByte(t.Elem()) { + return stackitem.BufferT + } + return stackitem.ArrayT + default: + return stackitem.AnyT + } +}