compiler: support make()
This commit is contained in:
parent
d8badd9a8d
commit
0f11116040
5 changed files with 70 additions and 3 deletions
|
@ -7,8 +7,8 @@ The neo-go compiler compiles Go programs to bytecode that the NEO virtual machin
|
|||
The compiler is mostly compatible with regular Go language specification, but
|
||||
there are some important deviations that you need to be aware of that make it
|
||||
a dialect of Go rather than a complete port of the language:
|
||||
* `make()` ane `new()` are not supported, most of the time you can substitute
|
||||
them with composite literals
|
||||
* `new()` is not supported, most of the time you can substitute structs with composite literals
|
||||
* `make()` is supported for maps and slices with elements of basic types
|
||||
* pointers are supported only for struct literals, one can't take an address
|
||||
of an arbitrary variable
|
||||
* there is no real distinction between different integer types, all of them
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
|
||||
var (
|
||||
// Go language builtin functions.
|
||||
goBuiltins = []string{"len", "append", "panic"}
|
||||
goBuiltins = []string{"len", "append", "panic", "make"}
|
||||
// Custom builtin utility functions.
|
||||
customBuiltins = []string{
|
||||
"FromAddress", "Equals",
|
||||
|
|
|
@ -1305,6 +1305,24 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
|||
}
|
||||
|
||||
switch name {
|
||||
case "make":
|
||||
typ := c.typeOf(expr.Args[0])
|
||||
switch {
|
||||
case isMap(typ):
|
||||
emit.Opcode(c.prog.BinWriter, opcode.NEWMAP)
|
||||
default:
|
||||
if len(expr.Args) == 3 {
|
||||
c.prog.Err = fmt.Errorf("`make()` with a capacity argument is not supported")
|
||||
return
|
||||
}
|
||||
ast.Walk(c, expr.Args[1])
|
||||
if isByteSlice(typ) {
|
||||
emit.Opcode(c.prog.BinWriter, opcode.NEWBUFFER)
|
||||
} else {
|
||||
neoT := toNeoType(typ.(*types.Slice).Elem())
|
||||
emit.Instruction(c.prog.BinWriter, opcode.NEWARRAYT, []byte{byte(neoT)})
|
||||
}
|
||||
}
|
||||
case "len":
|
||||
emit.Opcode(c.prog.BinWriter, opcode.DUP)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.ISNULL)
|
||||
|
@ -1397,6 +1415,9 @@ func transformArgs(fun ast.Expr, args []ast.Expr) []ast.Expr {
|
|||
if f.Name == "panic" {
|
||||
return args[1:]
|
||||
}
|
||||
if f.Name == "make" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return args
|
||||
|
|
|
@ -2,9 +2,12 @@ package compiler_test
|
|||
|
||||
import (
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var sliceTestCases = []testCase{
|
||||
|
@ -323,3 +326,41 @@ func TestJumps(t *testing.T) {
|
|||
`
|
||||
eval(t, src, []byte{0x62, 0x01, 0x00})
|
||||
}
|
||||
|
||||
func TestMake(t *testing.T) {
|
||||
t.Run("Map", func(t *testing.T) {
|
||||
src := `package foo
|
||||
func Main() int {
|
||||
a := make(map[int]int)
|
||||
a[1] = 10
|
||||
a[2] = 20
|
||||
return a[1]
|
||||
}`
|
||||
eval(t, src, big.NewInt(10))
|
||||
})
|
||||
t.Run("IntSlice", func(t *testing.T) {
|
||||
src := `package foo
|
||||
func Main() int {
|
||||
a := make([]int, 10)
|
||||
return len(a) + a[0]
|
||||
}`
|
||||
eval(t, src, big.NewInt(10))
|
||||
})
|
||||
t.Run("ByteSlice", func(t *testing.T) {
|
||||
src := `package foo
|
||||
func Main() int {
|
||||
a := make([]byte, 10)
|
||||
return len(a) + int(a[0])
|
||||
}`
|
||||
eval(t, src, big.NewInt(10))
|
||||
})
|
||||
t.Run("CapacityError", func(t *testing.T) {
|
||||
src := `package foo
|
||||
func Main() int {
|
||||
a := make([]int, 1, 2)
|
||||
return a[0]
|
||||
}`
|
||||
_, err := compiler.Compile("foo.go", strings.NewReader(src))
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -27,6 +27,11 @@ func isBasicTypeOfKind(typ types.Type, ks ...types.BasicKind) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func isMap(typ types.Type) bool {
|
||||
_, ok := typ.Underlying().(*types.Map)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isByte(typ types.Type) bool {
|
||||
return isBasicTypeOfKind(typ, types.Uint8, types.Int8)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue