package compiler_test import ( "bytes" "fmt" "math/big" "strings" "testing" "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/stretchr/testify/require" ) func TestUnusedGlobal(t *testing.T) { t.Run("simple unused", func(t *testing.T) { src := `package foo const ( _ int = iota a ) func Main() int { return 1 }` prog := eval(t, src, big.NewInt(1)) require.Equal(t, 2, len(prog)) // PUSH1 + RET }) t.Run("unused with function call inside", func(t *testing.T) { t.Run("specification names count matches values count", func(t *testing.T) { src := `package foo var control int var _ = f() func Main() int { return control } func f() int { control = 1 return 5 }` eval(t, src, big.NewInt(1)) }) t.Run("specification names count differs from values count", func(t *testing.T) { src := `package foo var control int var _, _ = f() func Main() int { return control } func f() (int, int) { control = 1 return 5, 6 }` eval(t, src, big.NewInt(1)) }) t.Run("used", func(t *testing.T) { src := `package foo var _, A = f() func Main() int { return A } func f() (int, int) { return 5, 6 }` eval(t, src, big.NewInt(6)) checkInstrCount(t, src, 1, 1, 0, 0) // sslot for A, single call to f }) }) t.Run("unused without function call", func(t *testing.T) { src := `package foo var _ = 1 var ( _ = 2 + 3 _, _ = 3 + 4, 5 ) func Main() int { return 1 }` prog := eval(t, src, big.NewInt(1)) require.Equal(t, 2, len(prog)) // PUSH1 + RET }) } func TestUnusedOptimizedGlobalVar(t *testing.T) { t.Run("unused, no initialization", func(t *testing.T) { src := `package foo var A int var ( B int C, D, E int ) func Main() int { return 1 }` prog := eval(t, src, big.NewInt(1)) require.Equal(t, 2, len(prog)) // Main }) t.Run("used, no initialization", func(t *testing.T) { src := `package foo var A int func Main() int { return A }` eval(t, src, big.NewInt(0)) checkInstrCount(t, src, 1, 0, 0, 0) // sslot for A }) t.Run("used by unused var, no initialization", func(t *testing.T) { src := `package foo var Unused int var Unused2 = Unused + 1 func Main() int { return 1 }` prog := eval(t, src, big.NewInt(1)) require.Equal(t, 2, len(prog)) // Main }) t.Run("unused, with initialization", func(t *testing.T) { src := `package foo var Unused = 1 func Main() int { return 2 }` prog := eval(t, src, big.NewInt(2)) require.Equal(t, 2, len(prog)) // Main }) t.Run("unused, with initialization by used var", func(t *testing.T) { src := `package foo var ( A = 1 B, Unused, C = f(), A + 2, 3 // the code for Unused initialization won't be emitted as it's a pure expression without function calls Unused2 = 4 ) var Unused3 = 5 func Main() int { return A + C } func f() int { return 4 }` eval(t, src, big.NewInt(4), []interface{}{opcode.INITSSLOT, []byte{2}}, // sslot for A and C opcode.PUSH1, opcode.STSFLD0, // store A []interface{}{opcode.CALL, []byte{10}}, opcode.DROP, // evaluate B and drop opcode.PUSH3, opcode.STSFLD1, opcode.RET, // store C opcode.LDSFLD0, opcode.LDSFLD1, opcode.ADD, opcode.RET, // Main opcode.PUSH4, opcode.RET) // f }) t.Run("used by unused var, with initialization", func(t *testing.T) { src := `package foo var ( Unused1 = 1 Unused2 = Unused1 + 1 ) func Main() int { return 1 }` prog := eval(t, src, big.NewInt(1)) require.Equal(t, 2, len(prog)) // Main }) t.Run("used with combination of nested unused", func(t *testing.T) { src := `package foo var ( A = 1 Unused1 = 2 Unused2 = Unused1 + 1 ) func Main() int { return A }` eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for A opcode.PUSH1, opcode.STSFLD0, opcode.RET, // store A opcode.LDSFLD0, opcode.RET) // Main }) t.Run("single var stmt with both used and unused vars", func(t *testing.T) { src := `package foo var A, Unused1, B, Unused2 = 1, 2, 3, 4 func Main() int { return A + B }` eval(t, src, big.NewInt(4), []interface{}{opcode.INITSSLOT, []byte{2}}, // sslot for A and B opcode.PUSH1, opcode.STSFLD0, // store A opcode.PUSH3, opcode.STSFLD1, opcode.RET, // store B opcode.LDSFLD0, opcode.LDSFLD1, opcode.ADD, opcode.RET) // Main }) t.Run("single var decl token with multiple var specifications", func(t *testing.T) { src := `package foo var ( A, Unused1, B, Unused2 = 1, 2, 3, 4 C, Unused3 int ) func Main() int { return A + B + C }` eval(t, src, big.NewInt(4), []interface{}{opcode.INITSSLOT, []byte{3}}, // sslot for A, B, C opcode.PUSH1, opcode.STSFLD0, // store A opcode.PUSH3, opcode.STSFLD1, // store B opcode.PUSH0, opcode.STSFLD2, opcode.RET, // store C opcode.LDSFLD0, opcode.LDSFLD1, opcode.ADD, opcode.LDSFLD2, opcode.ADD, opcode.RET) // Main }) t.Run("function as unused var value", func(t *testing.T) { src := `package foo var A, Unused1 = 1, f() func Main() int { return A } func f() int { return 2 }` eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for A opcode.PUSH1, opcode.STSFLD0, // store A []interface{}{opcode.CALL, []byte{6}}, opcode.DROP, opcode.RET, // evaluate Unused1 (call to f) and drop its value opcode.LDSFLD0, opcode.RET, // Main opcode.PUSH2, opcode.RET) // f }) t.Run("function as unused struct field", func(t *testing.T) { src := `package foo type Str struct { Int int } var _ = Str{Int: f()} func Main() int { return 1 } func f() int { return 2 }` eval(t, src, big.NewInt(1), []interface{}{opcode.CALL, []byte{8}}, opcode.PUSH1, opcode.PACKSTRUCT, opcode.DROP, opcode.RET, // evaluate struct val opcode.PUSH1, opcode.RET, // Main opcode.PUSH2, opcode.RET) // f }) t.Run("used in unused function", func(t *testing.T) { src := `package foo var Unused1, Unused2, Unused3 = 1, 2, 3 func Main() int { return 1 } func unused1() int { return Unused1 } func unused2() int { return Unused1 + unused1() } func unused3() int { return Unused2 + unused2() }` prog := eval(t, src, big.NewInt(1)) require.Equal(t, 2, len(prog)) // Main }) t.Run("used in used function", func(t *testing.T) { src := `package foo var A = 1 func Main() int { return f() } func f() int { return A }` eval(t, src, big.NewInt(1)) checkInstrCount(t, src, 1, 1, 0, 0) }) t.Run("unused, initialized via init", func(t *testing.T) { src := `package foo var A int func Main() int { return 2 } func init() { A = 1 // Although A is unused from exported functions, it's used from init(), so it should be mark as "used" and stored. }` eval(t, src, big.NewInt(2)) checkInstrCount(t, src, 1, 0, 0, 0) }) t.Run("used, initialized via init", func(t *testing.T) { src := `package foo var A int func Main() int { return A } func init() { A = 1 }` eval(t, src, big.NewInt(1)) checkInstrCount(t, src, 1, 0, 0, 0) }) t.Run("unused, initialized by function call", func(t *testing.T) { t.Run("unnamed", func(t *testing.T) { src := `package foo var _ = f() func Main() int { return 1 } func f() int { return 2 }` eval(t, src, big.NewInt(1)) checkInstrCount(t, src, 0, 1, 0, 0) }) t.Run("named", func(t *testing.T) { src := `package foo var A = f() func Main() int { return 1 } func f() int { return 2 }` eval(t, src, big.NewInt(1)) checkInstrCount(t, src, 0, 1, 0, 0) }) t.Run("named, with dependency on unused var", func(t *testing.T) { src := `package foo var ( A = 1 B = A + 1 // To check nested ident values. C = 3 D = B + f() + C // To check that both idents (before and after the call to f) will be marked as "used". E = C + 1 // Unused, no code expected. ) func Main() int { return 1 } func f() int { return 2 }` eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{3}}, // sslot for A opcode.PUSH1, opcode.STSFLD0, // store A opcode.LDSFLD0, opcode.PUSH1, opcode.ADD, opcode.STSFLD1, // store B opcode.PUSH3, opcode.STSFLD2, // store C opcode.LDSFLD1, []interface{}{opcode.CALL, []byte{9}}, opcode.ADD, opcode.LDSFLD2, opcode.ADD, opcode.DROP, opcode.RET, // evaluate D and drop opcode.PUSH1, opcode.RET, // Main opcode.PUSH2, opcode.RET) // f }) t.Run("named, with dependency on unused var ident inside function call", func(t *testing.T) { src := `package foo var A = 1 var B = f(A) func Main() int { return 1 } func f(a int) int { return a }` eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for A opcode.PUSH1, opcode.STSFLD0, // store A opcode.LDSFLD0, []interface{}{opcode.CALL, []byte{6}}, opcode.DROP, opcode.RET, // evaluate B and drop opcode.PUSH1, opcode.RET, // Main []interface{}{opcode.INITSLOT, []byte{0, 1}}, opcode.LDARG0, opcode.RET) // f }) t.Run("named, inside multi-specs and multi-vals var declaration", func(t *testing.T) { src := `package foo var ( Unused = 1 Unused1, A, Unused2 = 2, 3 + f(), 4 ) func Main() int { return 1 } func f() int { return 5 }` eval(t, src, big.NewInt(1), opcode.PUSH3, []interface{}{opcode.CALL, []byte{7}}, opcode.ADD, opcode.DROP, opcode.RET, // evaluate and drop A opcode.PUSH1, opcode.RET, // Main opcode.PUSH5, opcode.RET) // f }) t.Run("unnamed + unused", func(t *testing.T) { src := `package foo var A = 1 // At least one global variable is used, thus, the whole set of package variables will be walked. var B = 2 var _ = B + 1 // This variable is unnamed and doesn't contain call, thus its children won't be marked as "used". func Main() int { return A }` eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for A opcode.PUSH1, opcode.STSFLD0, opcode.RET, // store A opcode.LDSFLD0, opcode.RET) // Main }) t.Run("mixed value", func(t *testing.T) { src := `package foo var control int // At least one global variable is used, thus the whole set of package variables will be walked. var B = 2 var _ = 1 + f() + B // This variable is unnamed but contains call, thus its children will be marked as "used". func Main() int { return control } func f() int { control = 1 return 3 }` eval(t, src, big.NewInt(1)) checkInstrCount(t, src, 2 /* control + B */, 1, 0, 0) }) t.Run("multiple function return values", func(t *testing.T) { src := `package foo var A, B = f() func Main() int { return A } func f() (int, int) { return 3, 4 }` eval(t, src, big.NewInt(3), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for A []interface{}{opcode.CALL, []byte{7}}, opcode.STSFLD0, opcode.DROP, opcode.RET, // evaluate and store A, drop B opcode.LDSFLD0, opcode.RET, // Main opcode.PUSH4, opcode.PUSH3, opcode.RET) // f }) t.Run("constant in declaration", func(t *testing.T) { src := `package foo const A = 5 var Unused = 1 + A func Main() int { return 1 }` prog := eval(t, src, big.NewInt(1)) require.Equal(t, 2, len(prog)) // Main }) t.Run("mixed expression", func(t *testing.T) { src := `package foo type CustomInt struct { Int int } var A = CustomInt{Int: 2} var B = f(3) + A.f(1) func Main() int { return 1 } func f(a int) int { return a } func (i CustomInt) f(a int) int { // has the same name as f return i.Int + a }` eval(t, src, big.NewInt(1)) checkInstrCount(t, src, 1 /* A */, 2, 2, 0) }) }) t.Run("mixed nested expressions", func(t *testing.T) { src := `package foo type CustomInt struct { Int int} // has the same field name as Int variable, important for test var A = CustomInt{Int: 2} var B = f(A.Int) var Unused = 4 var Int = 5 // unused and MUST NOT be treated as "used" var C = CustomInt{Int: Unused}.Int + f(1) // uses Unused => Unused should be marked as "used" func Main() int { return 1 } func f(a int) int { return a } func (i CustomInt) f(a int) int { // has the same name as f return i.Int + a }` eval(t, src, big.NewInt(1)) }) t.Run("composite literal", func(t *testing.T) { src := `package foo var A = 2 var B = []int{1, A, 3}[1] var C = f(1) + B func Main() int { return 1 } func f(a int) int { return a }` eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{2}}, // sslot for A, B opcode.PUSH2, opcode.STSFLD0, // store A opcode.PUSH3, opcode.LDSFLD0, opcode.PUSH1, opcode.PUSH3, opcode.PACK, opcode.PUSH1, opcode.PICKITEM, opcode.STSFLD1, // evaluate B opcode.PUSH1, []interface{}{opcode.CALL, []byte{8}}, opcode.LDSFLD1, opcode.ADD, opcode.DROP, opcode.RET, // evalute C and drop opcode.PUSH1, opcode.RET, // Main []interface{}{opcode.INITSLOT, []byte{0, 1}}, opcode.LDARG0, opcode.RET) // f }) t.Run("index expression", func(t *testing.T) { src := `package foo var Unused = 2 var A = f(1) + []int{1, 2, 3}[Unused] // index expression func Main() int { return 1 } func f(a int) int { return a }` eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for Unused opcode.PUSH2, opcode.STSFLD0, // store Unused opcode.PUSH1, []interface{}{opcode.CALL, []byte{14}}, // call f(1) opcode.PUSH3, opcode.PUSH2, opcode.PUSH1, opcode.PUSH3, opcode.PACK, opcode.LDSFLD0, opcode.PICKITEM, // eval index expression opcode.ADD, opcode.DROP, opcode.RET, // eval and drop A opcode.PUSH1, opcode.RET, // Main []interface{}{opcode.INITSLOT, []byte{0, 1}}, opcode.LDARG0, opcode.RET) // f(a) }) t.Run("used via nested function calls", func(t *testing.T) { src := `package foo var A = 1 func Main() int { return f() } func f() int { return g() } func g() int { return A }` eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for A opcode.PUSH1, opcode.STSFLD0, opcode.RET, // store A []interface{}{opcode.CALL, []byte{3}}, opcode.RET, // Main []interface{}{opcode.CALL, []byte{3}}, opcode.RET, // f opcode.LDSFLD0, opcode.RET) // g }) t.Run("struct field name matches global var name", func(t *testing.T) { src := `package foo type CustomStr struct { Int int } var str = CustomStr{Int: 2} var Int = 5 // Unused and the code must not be emitted. func Main() int { return str.Int }` eval(t, src, big.NewInt(2), []interface{}{opcode.INITSSLOT, []byte{1}}, // sslot for str opcode.PUSH2, opcode.PUSH1, opcode.PACKSTRUCT, opcode.STSFLD0, opcode.RET, // store str opcode.LDSFLD0, opcode.PUSH0, opcode.PICKITEM, opcode.RET) // Main }) t.Run("var as a struct field initializer", func(t *testing.T) { src := `package foo type CustomStr struct { Int int } var A = 5 var Int = 6 // Unused func Main() int { return CustomStr{Int: A}.Int }` eval(t, src, big.NewInt(5)) }) t.Run("argument of globally called function", func(t *testing.T) { src := `package foo var Unused = 5 var control int var _, A = f(Unused) func Main() int { return control } func f(int) (int, int) { control = 5 return 1, 2 }` eval(t, src, big.NewInt(5)) }) t.Run("argument of locally called function", func(t *testing.T) { src := `package foo var Unused = 5 func Main() int { var _, a = f(Unused) return a } func f(i int) (int, int) { return i, i }` eval(t, src, big.NewInt(5)) }) t.Run("used in globally called defer", func(t *testing.T) { src := `package foo var control1, control2 int var Unused = 5 var _ = f() func Main() int { return control1 + control2 } func f() int { control1 = 1 defer func(){ control2 = Unused }() return 2 }` eval(t, src, big.NewInt(6)) }) t.Run("used in locally called defer", func(t *testing.T) { src := `package foo var control1, control2 int var Unused = 5 func Main() int { _ = f() return control1 + control2 } func f() int { control1 = 1 defer func(){ control2 = Unused }() return 2 }` eval(t, src, big.NewInt(6)) }) t.Run("imported", func(t *testing.T) { t.Run("init by func call", func(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/globalvar" func Main() int { return globalvar.Default }` eval(t, src, big.NewInt(0)) checkInstrCount(t, src, 1 /* Default */, 1 /* f */, 0, 0) }) t.Run("nested var call", func(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/globalvar/nested1" func Main() int { return nested1.C }` eval(t, src, big.NewInt(81)) checkInstrCount(t, src, 6 /* dependant vars of nested1.C */, 3, 1, 1) }) t.Run("nested func call", func(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/globalvar/funccall" func Main() int { return funccall.F() }` eval(t, src, big.NewInt(56)) checkInstrCount(t, src, 2 /* nested2.Argument + nested1.Argument */, -1, -1, -1) }) t.Run("nested method call", func(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/globalvar/funccall" func Main() int { return funccall.GetAge() }` eval(t, src, big.NewInt(24)) checkInstrCount(t, src, 3, /* nested3.Anna + nested2.Argument + nested3.Argument */ 5, /* funccall.GetAge() + Anna.GetAge() + nested1.f + nested1.f + nested2.f */ 2 /* nested1.f + nested2.f */, 0) }) }) } func TestChangeGlobal(t *testing.T) { t.Run("from Main", func(t *testing.T) { src := `package foo var a int func Main() int { setLocal() set42() setLocal() return a } func set42() { a = 42 } func setLocal() { a := 10; _ = a }` eval(t, src, big.NewInt(42)) }) t.Run("from other global", func(t *testing.T) { t.Skip("see https://github.com/nspcc-dev/neo-go/issues/2661") src := `package foo var A = f() var B int func Main() int { return B } func f() int { B = 3 return B }` eval(t, src, big.NewInt(3)) }) } func TestMultiDeclaration(t *testing.T) { src := `package foo var a, b, c int func Main() int { a = 1 b = 2 c = 3 return a + b + c }` eval(t, src, big.NewInt(6)) } func TestCountLocal(t *testing.T) { src := `package foo func Main() int { a, b, c, d := f() return a + b + c + d } func f() (int, int, int, int) { return 1, 2, 3, 4 }` eval(t, src, big.NewInt(10)) } func TestMultiDeclarationLocal(t *testing.T) { src := `package foo func Main() int { var a, b, c int a = 1 b = 2 c = 3 return a + b + c }` eval(t, src, big.NewInt(6)) } func TestMultiDeclarationLocalCompound(t *testing.T) { src := `package foo func Main() int { var a, b, c []int a = append(a, 1) b = append(b, 2) c = append(c, 3) return a[0] + b[0] + c[0] }` eval(t, src, big.NewInt(6)) } func TestShadow(t *testing.T) { srcTmpl := `package foo func Main() int { x := 1 y := 10 %s x += 1 // increase old local x := 30 // introduce new local y += x // make sure is means something } return x+y }` runCase := func(b string) func(t *testing.T) { return func(t *testing.T) { src := fmt.Sprintf(srcTmpl, b) eval(t, src, big.NewInt(42)) } } t.Run("If", runCase("if true {")) t.Run("For", runCase("for i := 0; i < 1; i++ {")) t.Run("Range", runCase("for range []int{1} {")) t.Run("Switch", runCase("switch true {\ncase false: x += 2\ncase true:")) t.Run("Block", runCase("{")) } func TestArgumentLocal(t *testing.T) { srcTmpl := `package foo func some(a int) int { if a > 42 { a := 24 _ = a } return a } func Main() int { return some(%d) }` t.Run("Override", func(t *testing.T) { src := fmt.Sprintf(srcTmpl, 50) eval(t, src, big.NewInt(50)) }) t.Run("NoOverride", func(t *testing.T) { src := fmt.Sprintf(srcTmpl, 40) eval(t, src, big.NewInt(40)) }) } func TestContractWithNoMain(t *testing.T) { src := `package foo var someGlobal int = 1 func Add3(a int) int { someLocal := 2 return someGlobal + someLocal + a }` b, di, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil) require.NoError(t, err) v := vm.New() invokeMethod(t, "Add3", b.Script, v, di) v.Estack().PushVal(39) require.NoError(t, v.Run()) require.Equal(t, 1, v.Estack().Len()) require.Equal(t, big.NewInt(42), v.PopResult()) } func TestMultipleFiles(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi" func Main() int { return multi.Sum() }` eval(t, src, big.NewInt(42)) } func TestExportedVariable(t *testing.T) { t.Run("Use", func(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi" func Main() int { return multi.SomeVar12 }` eval(t, src, big.NewInt(12)) }) t.Run("ChangeAndUse", func(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi" func Main() int { multi.SomeVar12 = 10 return multi.Sum() }` eval(t, src, big.NewInt(40)) }) t.Run("PackageAlias", func(t *testing.T) { src := `package foo import kek "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi" func Main() int { kek.SomeVar12 = 10 return kek.Sum() }` eval(t, src, big.NewInt(40)) }) t.Run("DifferentName", func(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/strange" func Main() int { normal.NormalVar = 42 return normal.NormalVar }` eval(t, src, big.NewInt(42)) }) t.Run("MultipleEqualNames", func(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi" var SomeVar12 = 1 func Main() int { SomeVar30 := 3 sum := SomeVar12 + multi.SomeVar30 sum += SomeVar30 sum += multi.SomeVar12 return sum }` eval(t, src, big.NewInt(46)) }) } func TestExportedConst(t *testing.T) { t.Run("with vars", func(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi" func Main() int { return multi.SomeConst }` eval(t, src, big.NewInt(42)) }) t.Run("const only", func(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/constonly" func Main() int { return constonly.Answer }` eval(t, src, big.NewInt(42)) }) } func TestMultipleFuncSameName(t *testing.T) { t.Run("Simple", func(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi" func Main() int { return multi.Sum() + Sum() } func Sum() int { return 11 }` eval(t, src, big.NewInt(53)) }) t.Run("WithMethod", func(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/foo" type Foo struct{} func (f Foo) Bar() int { return 11 } func Bar() int { return 22 } func Main() int { var a Foo var b foo.Foo return a.Bar() + // 11 foo.Bar() + // 1 b.Bar() + // 8 Bar() // 22 }` eval(t, src, big.NewInt(42)) }) } func TestConstDontUseSlots(t *testing.T) { const count = 256 buf := bytes.NewBufferString("package foo\n") for i := 0; i < count; i++ { buf.WriteString(fmt.Sprintf("const n%d = 1\n", i)) } buf.WriteString("func Main() int { sum := 0\n") for i := 0; i < count; i++ { buf.WriteString(fmt.Sprintf("sum += n%d\n", i)) } buf.WriteString("return sum }") src := buf.String() eval(t, src, big.NewInt(count)) } func TestUnderscoreVarsDontUseSlots(t *testing.T) { const count = 128 buf := bytes.NewBufferString("package foo\n") for i := 0; i < count; i++ { buf.WriteString(fmt.Sprintf("var _, n%d = 1, 1\n", i)) } buf.WriteString("func Main() int { sum := 0\n") for i := 0; i < count; i++ { buf.WriteString(fmt.Sprintf("sum += n%d\n", i)) } buf.WriteString("return sum }") src := buf.String() eval(t, src, big.NewInt(count)) } func TestUnderscoreGlobalVarDontEmitCode(t *testing.T) { src := `package foo var _ int var _ = 1 var ( A = 2 _ = A + 3 _, B, _ = 4, 5, 6 _, C, _ = f(A, B) ) var D = 7 // named unused, after global codegen optimisation no code expected func Main() int { return 1 } func f(a, b int) (int, int, int) { return 8, 9, 10 }` eval(t, src, big.NewInt(1), []interface{}{opcode.INITSSLOT, []byte{2}}, // sslot for A, B opcode.PUSH2, opcode.STSFLD0, // store A opcode.PUSH5, opcode.STSFLD1, // store B opcode.LDSFLD0, opcode.LDSFLD1, opcode.SWAP, []interface{}{opcode.CALL, []byte{8}}, // evaluate f(A,B) opcode.DROP, opcode.DROP, opcode.DROP, opcode.RET, // drop result of f(A,B) opcode.PUSH1, opcode.RET, // Main []interface{}{opcode.INITSLOT, []byte{0, 2}}, opcode.PUSH10, opcode.PUSH9, opcode.PUSH8, opcode.RET) // f }