From 5f3b8c6d518bb3e15e5e1444dfda9842879f0ed3 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 24 Sep 2020 20:20:34 +0300 Subject: [PATCH 1/2] compiler: allow variables in byte-slice literals --- pkg/compiler/codegen.go | 15 +++++++++++++-- pkg/compiler/slice_test.go | 11 +++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 896349972..4c98a30f8 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -1594,13 +1594,24 @@ func (c *codegen) emitConvert(typ stackitem.Type) { func (c *codegen) convertByteArray(lit *ast.CompositeLit) { buf := make([]byte, len(lit.Elts)) + varIndices := []int{} for i := 0; i < len(lit.Elts); i++ { t := c.typeAndValueOf(lit.Elts[i]) - val, _ := constant.Int64Val(t.Value) - buf[i] = byte(val) + if t.Value != nil { + val, _ := constant.Int64Val(t.Value) + buf[i] = byte(val) + } else { + varIndices = append(varIndices, i) + } } emit.Bytes(c.prog.BinWriter, buf) c.emitConvert(stackitem.BufferT) + for _, i := range varIndices { + emit.Opcode(c.prog.BinWriter, opcode.DUP) + emit.Int(c.prog.BinWriter, int64(i)) + ast.Walk(c, lit.Elts[i]) + emit.Opcode(c.prog.BinWriter, opcode.SETITEM) + } } func (c *codegen) convertMap(lit *ast.CompositeLit) { diff --git a/pkg/compiler/slice_test.go b/pkg/compiler/slice_test.go index e4bc8c6e9..858666b70 100644 --- a/pkg/compiler/slice_test.go +++ b/pkg/compiler/slice_test.go @@ -310,6 +310,17 @@ var sliceTestCases = []testCase{ `, big.NewInt(2), }, + { + "literal byte-slice with variable values", + `package foo + const sym1 = 's' + func Main() []byte { + sym2 := byte('t') + sym4 := byte('i') + return []byte{sym1, sym2, 'r', sym4, 'n', 'g'} + }`, + []byte("string"), + }, } func TestSliceOperations(t *testing.T) { From fd52dee79f3937bc63dd864de0f7725d919658b7 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 24 Sep 2020 20:29:52 +0300 Subject: [PATCH 2/2] compiler: process literals in `analyzeVoidCalls` Function call can occur in the slice or map literal and its result surely isn't unused. --- pkg/compiler/func_scope.go | 12 ++++++++++++ pkg/compiler/slice_test.go | 9 +++++++++ pkg/compiler/struct_test.go | 13 +++++++++++++ 3 files changed, 34 insertions(+) diff --git a/pkg/compiler/func_scope.go b/pkg/compiler/func_scope.go index e0df8f6a0..41f3db76c 100644 --- a/pkg/compiler/func_scope.go +++ b/pkg/compiler/func_scope.go @@ -136,6 +136,18 @@ func (c *funcScope) analyzeVoidCalls(node ast.Node) bool { c.voidCalls[n] = true } return false + case *ast.CompositeLit: + for _, e := range n.Elts { + switch val := e.(type) { + case *ast.CallExpr: // slice + c.voidCalls[val] = false + case *ast.KeyValueExpr: // struct and map + ce, ok := val.Value.(*ast.CallExpr) + if ok { + c.voidCalls[ce] = false + } + } + } } return true } diff --git a/pkg/compiler/slice_test.go b/pkg/compiler/slice_test.go index 858666b70..95e667235 100644 --- a/pkg/compiler/slice_test.go +++ b/pkg/compiler/slice_test.go @@ -321,6 +321,15 @@ var sliceTestCases = []testCase{ }`, []byte("string"), }, + { + "literal slice with function call", + `package foo + func fn() byte { return 't' } + func Main() []byte { + return []byte{'s', fn(), 'r'} + }`, + []byte("str"), + }, } func TestSliceOperations(t *testing.T) { diff --git a/pkg/compiler/struct_test.go b/pkg/compiler/struct_test.go index 2048d612c..fe48e9429 100644 --- a/pkg/compiler/struct_test.go +++ b/pkg/compiler/struct_test.go @@ -29,6 +29,19 @@ var structTestCases = []testCase{ `, big.NewInt(2), }, + { + "struct field from func result", + ` + package foo + type S struct { x int } + func fn() int { return 2 } + func Main() int { + t := S{x: fn()} + return t.x + } + `, + big.NewInt(2), + }, { "struct field return", `