From e5d8c1c985a5b286399314dd2b24470fac893a0d Mon Sep 17 00:00:00 2001 From: Evgeniy Stratonikov Date: Wed, 3 Feb 2021 16:10:31 +0300 Subject: [PATCH] compiler: allow conversion to types from external packages --- pkg/compiler/codegen.go | 35 ++++++++++++++++++++++++---- pkg/compiler/convert_test.go | 31 ++++++++++++++++++++++++ pkg/compiler/testdata/types/types.go | 7 ++++++ 3 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 pkg/compiler/testdata/types/types.go diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index b9204edff..4d9c7dc4d 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -821,13 +821,20 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { } f, ok = c.funcs[name] - // @FIXME this could cause runtime errors. - f.selector = fun.X.(*ast.Ident) - if !ok { - c.prog.Err = fmt.Errorf("could not resolve function %s", fun.Sel.Name) + if ok { + f.selector = fun.X.(*ast.Ident) + isBuiltin = isCustomBuiltin(f) + } else { + typ := c.typeOf(fun) + if _, ok := typ.(*types.Signature); ok { + c.prog.Err = fmt.Errorf("could not resolve function %s", fun.Sel.Name) + return nil + } + + ast.Walk(c, n.Args[0]) + c.emitExplicitConvert(c.typeOf(n.Args[0]), typ) return nil } - isBuiltin = isCustomBuiltin(f) case *ast.ArrayType: // For now we will assume that there are only byte slice conversions. // E.g. []byte("foobar") or []byte(scriptHash). @@ -1242,6 +1249,24 @@ func (c *codegen) processDefers() { } } +// emitExplicitConvert handles `someType(someValue)` conversions between string/[]byte. +// Rules for conversion: +// 1. interop.* types are converted to ByteArray if not already. +// 2. Otherwise convert between ByteArray/Buffer. +// 3. Rules for types which are not string/[]byte should already +// be enforced by go parser. +func (c *codegen) emitExplicitConvert(from, to types.Type) { + if isInteropPath(to.String()) { + if isByteSlice(from) && !isString(from) { + c.emitConvert(stackitem.ByteArrayT) + } + } else if isByteSlice(to) && !isByteSlice(from) { + c.emitConvert(stackitem.BufferT) + } else if isString(to) && !isString(from) { + c.emitConvert(stackitem.ByteArrayT) + } +} + func (c *codegen) rangeLoadKey() { emit.Int(c.prog.BinWriter, 2) emit.Opcodes(c.prog.BinWriter, diff --git a/pkg/compiler/convert_test.go b/pkg/compiler/convert_test.go index f0193acd0..44e4a696b 100644 --- a/pkg/compiler/convert_test.go +++ b/pkg/compiler/convert_test.go @@ -87,6 +87,37 @@ func TestTypeConversion(t *testing.T) { eval(t, src, big.NewInt(42)) } +func TestSelectorTypeConversion(t *testing.T) { + src := `package foo + import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/types" + import "github.com/nspcc-dev/neo-go/pkg/interop/util" + import "github.com/nspcc-dev/neo-go/pkg/interop" + func Main() int { + var a int + if util.Equals(types.Buffer(nil), nil) { + a += 1 + } + + // Buffer != ByteArray + if util.Equals(types.Buffer("\x12"), "\x12") { + a += 10 + } + + tmp := []byte{0x23} + if util.Equals(types.ByteString(tmp), "\x23") { + a += 100 + } + + addr := "aaaaaaaaaaaaaaaaaaaa" + buf := []byte(addr) + if util.Equals(interop.Hash160(addr), interop.Hash160(buf)) { + a += 1000 + } + return a + }` + eval(t, src, big.NewInt(1101)) +} + func TestTypeConversionString(t *testing.T) { src := `package foo type mystr string diff --git a/pkg/compiler/testdata/types/types.go b/pkg/compiler/testdata/types/types.go new file mode 100644 index 000000000..0ab3a1672 --- /dev/null +++ b/pkg/compiler/testdata/types/types.go @@ -0,0 +1,7 @@ +package types + +// Buffer represents Buffer VM type. +type Buffer []byte + +// ByteString represents ByteString VM type. +type ByteString string