compiler: allow conversion to types from external packages

This commit is contained in:
Evgeniy Stratonikov 2021-02-03 16:10:31 +03:00
parent 1f98289f5d
commit e5d8c1c985
3 changed files with 68 additions and 5 deletions

View file

@ -821,13 +821,20 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
} }
f, ok = c.funcs[name] f, ok = c.funcs[name]
// @FIXME this could cause runtime errors. if ok {
f.selector = fun.X.(*ast.Ident) f.selector = fun.X.(*ast.Ident)
if !ok { isBuiltin = isCustomBuiltin(f)
c.prog.Err = fmt.Errorf("could not resolve function %s", fun.Sel.Name) } 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 return nil
} }
isBuiltin = isCustomBuiltin(f)
case *ast.ArrayType: case *ast.ArrayType:
// For now we will assume that there are only byte slice conversions. // For now we will assume that there are only byte slice conversions.
// E.g. []byte("foobar") or []byte(scriptHash). // 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() { func (c *codegen) rangeLoadKey() {
emit.Int(c.prog.BinWriter, 2) emit.Int(c.prog.BinWriter, 2)
emit.Opcodes(c.prog.BinWriter, emit.Opcodes(c.prog.BinWriter,

View file

@ -87,6 +87,37 @@ func TestTypeConversion(t *testing.T) {
eval(t, src, big.NewInt(42)) 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) { func TestTypeConversionString(t *testing.T) {
src := `package foo src := `package foo
type mystr string type mystr string

7
pkg/compiler/testdata/types/types.go vendored Normal file
View file

@ -0,0 +1,7 @@
package types
// Buffer represents Buffer VM type.
type Buffer []byte
// ByteString represents ByteString VM type.
type ByteString string