compiler: support copy()
This commit is contained in:
parent
ab4cd8a990
commit
69989e1227
4 changed files with 115 additions and 4 deletions
|
@ -9,6 +9,7 @@ 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:
|
||||
* `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
|
||||
* `copy()` is supported only for byte slices, because of underlying `MEMCPY` opcode
|
||||
* 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", "make"}
|
||||
goBuiltins = []string{"len", "append", "panic", "make", "copy"}
|
||||
// Custom builtin utility functions.
|
||||
customBuiltins = []string{
|
||||
"FromAddress", "Equals",
|
||||
|
|
|
@ -1295,6 +1295,44 @@ func (c *codegen) convertSyscall(expr *ast.CallExpr, api, name string) {
|
|||
emit.Opcode(c.prog.BinWriter, opcode.NOP)
|
||||
}
|
||||
|
||||
// emitSliceHelper emits 3 items on stack: slice, its first index, and its size.
|
||||
func (c *codegen) emitSliceHelper(e ast.Expr) {
|
||||
if !isByteSlice(c.typeOf(e)) {
|
||||
c.prog.Err = fmt.Errorf("copy is supported only for byte-slices")
|
||||
return
|
||||
}
|
||||
var hasLowIndex bool
|
||||
switch src := e.(type) {
|
||||
case *ast.SliceExpr:
|
||||
ast.Walk(c, src.X)
|
||||
if src.High != nil {
|
||||
ast.Walk(c, src.High)
|
||||
} else {
|
||||
emit.Opcode(c.prog.BinWriter, opcode.DUP)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.SIZE)
|
||||
}
|
||||
if src.Low != nil {
|
||||
ast.Walk(c, src.Low)
|
||||
hasLowIndex = true
|
||||
} else {
|
||||
emit.Int(c.prog.BinWriter, 0)
|
||||
}
|
||||
default:
|
||||
ast.Walk(c, src)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.DUP)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.SIZE)
|
||||
emit.Int(c.prog.BinWriter, 0)
|
||||
}
|
||||
if !hasLowIndex {
|
||||
emit.Opcode(c.prog.BinWriter, opcode.SWAP)
|
||||
} else {
|
||||
emit.Opcode(c.prog.BinWriter, opcode.DUP)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.ROT)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.SWAP)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.SUB)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
||||
var name string
|
||||
switch t := expr.Fun.(type) {
|
||||
|
@ -1305,6 +1343,14 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
|||
}
|
||||
|
||||
switch name {
|
||||
case "copy":
|
||||
// stack for MEMCPY is: dst, dst_index, src, src_index, count
|
||||
c.emitSliceHelper(expr.Args[0])
|
||||
c.emitSliceHelper(expr.Args[1])
|
||||
emit.Int(c.prog.BinWriter, 3)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.ROLL)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.MIN)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.MEMCPY)
|
||||
case "make":
|
||||
typ := c.typeOf(expr.Args[0])
|
||||
switch {
|
||||
|
@ -1412,10 +1458,10 @@ func transformArgs(fun ast.Expr, args []ast.Expr) []ast.Expr {
|
|||
return args[1:]
|
||||
}
|
||||
case *ast.Ident:
|
||||
if f.Name == "panic" {
|
||||
switch f.Name {
|
||||
case "panic":
|
||||
return args[1:]
|
||||
}
|
||||
if f.Name == "make" {
|
||||
case "make", "copy":
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -364,3 +364,67 @@ func TestMake(t *testing.T) {
|
|||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCopy(t *testing.T) {
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
src := `package foo
|
||||
func Main() []int {
|
||||
src := []int{3, 2, 1}
|
||||
dst := make([]int, 2)
|
||||
copy(dst, src)
|
||||
return dst
|
||||
}`
|
||||
_, err := compiler.Compile("foo.go", strings.NewReader(src))
|
||||
require.Error(t, err)
|
||||
})
|
||||
t.Run("Simple", func(t *testing.T) {
|
||||
src := `package foo
|
||||
func Main() []byte {
|
||||
src := []byte{3, 2, 1}
|
||||
dst := make([]byte, 2)
|
||||
copy(dst, src)
|
||||
return dst
|
||||
}`
|
||||
eval(t, src, []byte{3, 2})
|
||||
})
|
||||
t.Run("LowSrcIndex", func(t *testing.T) {
|
||||
src := `package foo
|
||||
func Main() []byte {
|
||||
src := []byte{3, 2, 1}
|
||||
dst := make([]byte, 2)
|
||||
copy(dst, src[1:])
|
||||
return dst
|
||||
}`
|
||||
eval(t, src, []byte{2, 1})
|
||||
})
|
||||
t.Run("LowDstIndex", func(t *testing.T) {
|
||||
src := `package foo
|
||||
func Main() []byte {
|
||||
src := []byte{3, 2, 1}
|
||||
dst := make([]byte, 2)
|
||||
copy(dst[1:], src[1:])
|
||||
return dst
|
||||
}`
|
||||
eval(t, src, []byte{0, 2})
|
||||
})
|
||||
t.Run("BothIndices", func(t *testing.T) {
|
||||
src := `package foo
|
||||
func Main() []byte {
|
||||
src := []byte{4, 3, 2, 1}
|
||||
dst := make([]byte, 4)
|
||||
copy(dst[1:], src[1:3])
|
||||
return dst
|
||||
}`
|
||||
eval(t, src, []byte{0, 3, 2, 0})
|
||||
})
|
||||
t.Run("EmptySliceExpr", func(t *testing.T) {
|
||||
src := `package foo
|
||||
func Main() []byte {
|
||||
src := []byte{3, 2, 1}
|
||||
dst := make([]byte, 2)
|
||||
copy(dst[1:], src[:])
|
||||
return dst
|
||||
}`
|
||||
eval(t, src, []byte{0, 3})
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue