Merge pull request #1750 from nspcc-dev/compiler/append
Append bytes bigger than 0x79 properly
This commit is contained in:
commit
f5767ec710
2 changed files with 129 additions and 18 deletions
|
@ -783,7 +783,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
ln := len(n.Elts)
|
ln := len(n.Elts)
|
||||||
// ByteArrays needs a different approach than normal arrays.
|
// ByteArrays needs a different approach than normal arrays.
|
||||||
if isByteSlice(typ) {
|
if isByteSlice(typ) {
|
||||||
c.convertByteArray(n)
|
c.convertByteArray(n.Elts)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for i := ln - 1; i >= 0; i-- {
|
for i := ln - 1; i >= 0; i-- {
|
||||||
|
@ -1621,20 +1621,46 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
||||||
case "append":
|
case "append":
|
||||||
arg := expr.Args[0]
|
arg := expr.Args[0]
|
||||||
typ := c.typeInfo.Types[arg].Type
|
typ := c.typeInfo.Types[arg].Type
|
||||||
c.emitReverse(len(expr.Args))
|
ast.Walk(c, arg)
|
||||||
emit.Opcodes(c.prog.BinWriter, opcode.DUP, opcode.ISNULL)
|
emit.Opcodes(c.prog.BinWriter, opcode.DUP, opcode.ISNULL)
|
||||||
|
if isByteSlice(typ) {
|
||||||
emit.Instruction(c.prog.BinWriter, opcode.JMPIFNOT, []byte{2 + 3})
|
emit.Instruction(c.prog.BinWriter, opcode.JMPIFNOT, []byte{2 + 3})
|
||||||
if isByteSlice(typ) {
|
emit.Opcodes(c.prog.BinWriter, opcode.DROP, opcode.PUSHDATA1, 0)
|
||||||
emit.Opcodes(c.prog.BinWriter, opcode.DROP, opcode.PUSH0, opcode.NEWBUFFER)
|
if expr.Ellipsis.IsValid() {
|
||||||
|
ast.Walk(c, expr.Args[1])
|
||||||
} else {
|
} else {
|
||||||
emit.Opcodes(c.prog.BinWriter, opcode.DROP, opcode.NEWARRAY0, opcode.NOP)
|
c.convertByteArray(expr.Args[1:])
|
||||||
}
|
}
|
||||||
// Jump target.
|
emit.Opcodes(c.prog.BinWriter, opcode.CAT)
|
||||||
for range expr.Args[1:] {
|
|
||||||
if isByteSlice(typ) {
|
|
||||||
emit.Opcodes(c.prog.BinWriter, opcode.SWAP, opcode.CAT)
|
|
||||||
} else {
|
} else {
|
||||||
emit.Opcodes(c.prog.BinWriter, opcode.DUP, opcode.ROT, opcode.APPEND)
|
emit.Instruction(c.prog.BinWriter, opcode.JMPIFNOT, []byte{2 + 2})
|
||||||
|
emit.Opcodes(c.prog.BinWriter, opcode.DROP, opcode.NEWARRAY0)
|
||||||
|
if expr.Ellipsis.IsValid() {
|
||||||
|
ast.Walk(c, expr.Args[1]) // x y
|
||||||
|
emit.Opcodes(c.prog.BinWriter, opcode.PUSH0) // x y cnt=0
|
||||||
|
start := c.newLabel()
|
||||||
|
c.setLabel(start)
|
||||||
|
emit.Opcodes(c.prog.BinWriter, opcode.PUSH2, opcode.PICK) // x y cnt x
|
||||||
|
emit.Opcodes(c.prog.BinWriter, opcode.PUSH2, opcode.PICK) // x y cnt x y
|
||||||
|
emit.Opcodes(c.prog.BinWriter, opcode.DUP, opcode.SIZE) // x y cnt x y len(y)
|
||||||
|
emit.Opcodes(c.prog.BinWriter, opcode.PUSH3, opcode.PICK) // x y cnt x y len(y) cnt
|
||||||
|
after := c.newLabel()
|
||||||
|
emit.Jmp(c.prog.BinWriter, opcode.JMPEQL, after) // x y cnt x y
|
||||||
|
emit.Opcodes(c.prog.BinWriter, opcode.PUSH2, opcode.PICK, // x y cnt x y cnt
|
||||||
|
opcode.PICKITEM, // x y cnt x y[cnt]
|
||||||
|
opcode.APPEND, // x=append(x, y[cnt]) y cnt
|
||||||
|
opcode.INC) // x y cnt+1
|
||||||
|
emit.Jmp(c.prog.BinWriter, opcode.JMPL, start)
|
||||||
|
c.setLabel(after)
|
||||||
|
for i := 0; i < 4; i++ { // leave x on stack
|
||||||
|
emit.Opcodes(c.prog.BinWriter, opcode.DROP)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, e := range expr.Args[1:] {
|
||||||
|
emit.Opcodes(c.prog.BinWriter, opcode.DUP)
|
||||||
|
ast.Walk(c, e)
|
||||||
|
emit.Opcodes(c.prog.BinWriter, opcode.APPEND)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "panic":
|
case "panic":
|
||||||
|
@ -1696,7 +1722,7 @@ func transformArgs(fun ast.Expr, args []ast.Expr) []ast.Expr {
|
||||||
}
|
}
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
switch f.Name {
|
switch f.Name {
|
||||||
case "make", "copy":
|
case "make", "copy", "append":
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1709,11 +1735,11 @@ func (c *codegen) emitConvert(typ stackitem.Type) {
|
||||||
emit.Instruction(c.prog.BinWriter, opcode.CONVERT, []byte{byte(typ)})
|
emit.Instruction(c.prog.BinWriter, opcode.CONVERT, []byte{byte(typ)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) convertByteArray(lit *ast.CompositeLit) {
|
func (c *codegen) convertByteArray(elems []ast.Expr) {
|
||||||
buf := make([]byte, len(lit.Elts))
|
buf := make([]byte, len(elems))
|
||||||
varIndices := []int{}
|
varIndices := []int{}
|
||||||
for i := 0; i < len(lit.Elts); i++ {
|
for i := 0; i < len(elems); i++ {
|
||||||
t := c.typeAndValueOf(lit.Elts[i])
|
t := c.typeAndValueOf(elems[i])
|
||||||
if t.Value != nil {
|
if t.Value != nil {
|
||||||
val, _ := constant.Int64Val(t.Value)
|
val, _ := constant.Int64Val(t.Value)
|
||||||
buf[i] = byte(val)
|
buf[i] = byte(val)
|
||||||
|
@ -1726,7 +1752,7 @@ func (c *codegen) convertByteArray(lit *ast.CompositeLit) {
|
||||||
for _, i := range varIndices {
|
for _, i := range varIndices {
|
||||||
emit.Opcodes(c.prog.BinWriter, opcode.DUP)
|
emit.Opcodes(c.prog.BinWriter, opcode.DUP)
|
||||||
emit.Int(c.prog.BinWriter, int64(i))
|
emit.Int(c.prog.BinWriter, int64(i))
|
||||||
ast.Walk(c, lit.Elts[i])
|
ast.Walk(c, elems[i])
|
||||||
emit.Opcodes(c.prog.BinWriter, opcode.SETITEM)
|
emit.Opcodes(c.prog.BinWriter, opcode.SETITEM)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,6 +181,23 @@ var sliceTestCases = []testCase{
|
||||||
stackitem.NewBigInteger(big.NewInt(5)),
|
stackitem.NewBigInteger(big.NewInt(5)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"int slice, append slice",
|
||||||
|
`package foo
|
||||||
|
func getByte() byte { return 0x80 }
|
||||||
|
func Main() []int {
|
||||||
|
x := []int{1}
|
||||||
|
y := []int{2, 3}
|
||||||
|
x = append(x, y...)
|
||||||
|
x = append(x, y...)
|
||||||
|
return x
|
||||||
|
}`,
|
||||||
|
[]stackitem.Item{
|
||||||
|
stackitem.Make(1),
|
||||||
|
stackitem.Make(2), stackitem.Make(3),
|
||||||
|
stackitem.Make(2), stackitem.Make(3),
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"declare compound slice",
|
"declare compound slice",
|
||||||
`package foo
|
`package foo
|
||||||
|
@ -336,6 +353,74 @@ func TestSliceOperations(t *testing.T) {
|
||||||
runTestCases(t, sliceTestCases)
|
runTestCases(t, sliceTestCases)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestByteSlices(t *testing.T) {
|
||||||
|
testCases := []testCase{
|
||||||
|
{
|
||||||
|
"append bytes bigger than 0x79",
|
||||||
|
`package foo
|
||||||
|
func Main() []byte {
|
||||||
|
var z []byte
|
||||||
|
z = append(z, 0x78, 0x79, 0x80, 0x81, 0xFF)
|
||||||
|
return z
|
||||||
|
}`,
|
||||||
|
[]byte{0x78, 0x79, 0x80, 0x81, 0xFF},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"append bytes bigger than 0x79, not nil",
|
||||||
|
`package foo
|
||||||
|
func Main() []byte {
|
||||||
|
z := []byte{0x78}
|
||||||
|
z = append(z, 0x79, 0x80, 0x81, 0xFF)
|
||||||
|
return z
|
||||||
|
}`,
|
||||||
|
[]byte{0x78, 0x79, 0x80, 0x81, 0xFF},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"append bytes bigger than 0x79, function return",
|
||||||
|
`package foo
|
||||||
|
func getByte() byte { return 0x80 }
|
||||||
|
func Main() []byte {
|
||||||
|
var z []byte
|
||||||
|
z = append(z, 0x78, 0x79, getByte(), 0x81, 0xFF)
|
||||||
|
return z
|
||||||
|
}`,
|
||||||
|
[]byte{0x78, 0x79, 0x80, 0x81, 0xFF},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"append ints bigger than 0x79, function return byte",
|
||||||
|
`package foo
|
||||||
|
func getByte() byte { return 0x80 }
|
||||||
|
func Main() []int {
|
||||||
|
var z []int
|
||||||
|
z = append(z, 0x78, int(getByte()))
|
||||||
|
return z
|
||||||
|
}`,
|
||||||
|
[]stackitem.Item{stackitem.Make(0x78), stackitem.Make(0x80)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slice literal, bytes bigger than 0x79, function return",
|
||||||
|
`package foo
|
||||||
|
func getByte() byte { return 0x80 }
|
||||||
|
func Main() []byte {
|
||||||
|
z := []byte{0x79, getByte(), 0x81}
|
||||||
|
return z
|
||||||
|
}`,
|
||||||
|
[]byte{0x79, 0x80, 0x81},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"compare bytes as integers",
|
||||||
|
`package foo
|
||||||
|
func getByte1() byte { return 0x79 }
|
||||||
|
func getByte2() byte { return 0x80 }
|
||||||
|
func Main() bool {
|
||||||
|
return getByte1() < getByte2()
|
||||||
|
}`,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
runTestCases(t, testCases)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSubsliceCompound(t *testing.T) {
|
func TestSubsliceCompound(t *testing.T) {
|
||||||
src := `package foo
|
src := `package foo
|
||||||
func Main() []int {
|
func Main() []int {
|
||||||
|
|
Loading…
Reference in a new issue