From dad941fe48657767413bcfdc846acec2bd2c6aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 19 Feb 2025 15:50:12 -0800 Subject: [PATCH 1/6] fix and test compilation of nested loops --- bbq/compiler/compiler.go | 2 +- bbq/compiler/compiler_test.go | 130 ++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/bbq/compiler/compiler.go b/bbq/compiler/compiler.go index 9fc3f094b..3085e8422 100644 --- a/bbq/compiler/compiler.go +++ b/bbq/compiler/compiler.go @@ -285,7 +285,7 @@ func (c *Compiler[_]) popLoop() { var previousLoop *loop if lastIndex > 0 { - previousLoop = c.loops[lastIndex] + previousLoop = c.loops[lastIndex-1] } c.currentLoop = previousLoop } diff --git a/bbq/compiler/compiler_test.go b/bbq/compiler/compiler_test.go index bcaccd41e..1d6b88d46 100644 --- a/bbq/compiler/compiler_test.go +++ b/bbq/compiler/compiler_test.go @@ -27,6 +27,7 @@ import ( "github.com/onflow/cadence/bbq" "github.com/onflow/cadence/bbq/constantkind" "github.com/onflow/cadence/bbq/opcode" + "github.com/onflow/cadence/common" . "github.com/onflow/cadence/test_utils/sema_utils" ) @@ -935,3 +936,132 @@ func TestCompileFailableCast(t *testing.T) { compiler.ExportFunctions()[0].Code, ) } + +func TestCompileNestedLoop(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + fun test(): Int { + var i = 0 + while i < 10 { + var j = 0 + while j < 10 { + if i == j { + break + } + j = j + 1 + continue + } + i = i + 1 + continue + } + return i + } + `) + require.NoError(t, err) + + compiler := NewInstructionCompiler(checker) + program := compiler.Compile() + + require.Len(t, program.Functions, 1) + + const parameterCount = 0 + + // resultIndex is the index of the $result variable + const resultIndex = parameterCount + + // localsOffset is the offset of the first local variable + const localsOffset = resultIndex + 1 + + const ( + // iIndex is the index of the local variable `i`, which is the first local variable + iIndex = localsOffset + iota + // jIndex is the index of the local variable `j`, which is the second local variable + jIndex + ) + const ( + // zeroIndex is the index of the constant `0`, which is the first constant + zeroIndex = iota + // tenIndex is the index of the constant `10`, which is the second constant + tenIndex + // oneIndex is the index of the constant `1`, which is the third constant + oneIndex + ) + + assert.Equal(t, + []opcode.Instruction{ + // var i = 0 + opcode.InstructionGetConstant{ConstantIndex: zeroIndex}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: iIndex}, + + // i < 10 + opcode.InstructionGetLocal{LocalIndex: iIndex}, + opcode.InstructionGetConstant{ConstantIndex: tenIndex}, + opcode.InstructionLess{}, + + opcode.InstructionJumpIfFalse{Target: 33}, + + // var j = 0 + opcode.InstructionGetConstant{ConstantIndex: zeroIndex}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: jIndex}, + + // j < 10 + opcode.InstructionGetLocal{LocalIndex: jIndex}, + opcode.InstructionGetConstant{ConstantIndex: tenIndex}, + opcode.InstructionLess{}, + + opcode.InstructionJumpIfFalse{Target: 26}, + + // i == j + opcode.InstructionGetLocal{LocalIndex: iIndex}, + opcode.InstructionGetLocal{LocalIndex: jIndex}, + opcode.InstructionEqual{}, + + opcode.InstructionJumpIfFalse{Target: 19}, + + // break + opcode.InstructionJump{Target: 26}, + + // j = j + 1 + opcode.InstructionGetLocal{LocalIndex: jIndex}, + opcode.InstructionGetConstant{ConstantIndex: oneIndex}, + opcode.InstructionAdd{}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: jIndex}, + + // continue + opcode.InstructionJump{Target: 10}, + + // repeat + opcode.InstructionJump{Target: 10}, + + // i = i + 1 + opcode.InstructionGetLocal{LocalIndex: iIndex}, + opcode.InstructionGetConstant{ConstantIndex: oneIndex}, + opcode.InstructionAdd{}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: iIndex}, + + // continue + opcode.InstructionJump{Target: 3}, + + // repeat + opcode.InstructionJump{Target: 3}, + + // return i + opcode.InstructionGetLocal{LocalIndex: iIndex}, + + // assign to temp $result + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: resultIndex}, + + // return $result + opcode.InstructionGetLocal{LocalIndex: resultIndex}, + opcode.InstructionReturnValue{}, + }, + compiler.ExportFunctions()[0].Code, + ) +} From 802387338fe51920b75f237349d68f965f882140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 19 Feb 2025 15:50:24 -0800 Subject: [PATCH 2/6] test compilation of assignments --- bbq/compiler/compiler_test.go | 170 ++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/bbq/compiler/compiler_test.go b/bbq/compiler/compiler_test.go index 1d6b88d46..c6bb29695 100644 --- a/bbq/compiler/compiler_test.go +++ b/bbq/compiler/compiler_test.go @@ -1065,3 +1065,173 @@ func TestCompileNestedLoop(t *testing.T) { compiler.ExportFunctions()[0].Code, ) } + +func TestCompileAssignLocal(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + fun test() { + var x = 0 + x = 1 + } + `) + require.NoError(t, err) + + compiler := NewInstructionCompiler(checker) + program := compiler.Compile() + + require.Len(t, program.Functions, 1) + + const parameterCount = 0 + + // resultIndex is the index of the $result variable + const resultIndex = parameterCount + + // localsOffset is the offset of the first local variable + const localsOffset = resultIndex + 1 + + // xIndex is the index of the local variable `x`, which is the first local variable + const xIndex = localsOffset + + assert.Equal(t, + []opcode.Instruction{ + // var x = 0 + opcode.InstructionGetConstant{ConstantIndex: 0}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: xIndex}, + + // x = 1 + opcode.InstructionGetConstant{ConstantIndex: 1}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: xIndex}, + + opcode.InstructionReturn{}, + }, + compiler.ExportFunctions()[0].Code, + ) +} + +func TestCompileAssignGlobal(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + var x = 0 + + fun test() { + x = 1 + } + `) + require.NoError(t, err) + + compiler := NewInstructionCompiler(checker) + program := compiler.Compile() + + require.Len(t, program.Functions, 1) + + assert.Equal(t, + []opcode.Instruction{ + // x = 1 + opcode.InstructionGetConstant{ConstantIndex: 0}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetGlobal{GlobalIndex: 0}, + + opcode.InstructionReturn{}, + }, + compiler.ExportFunctions()[0].Code, + ) +} + +func TestCompileAssignIndex(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + fun test(array: [Int], index: Int, value: Int) { + array[index] = value + } + `) + require.NoError(t, err) + + compiler := NewInstructionCompiler(checker) + program := compiler.Compile() + + require.Len(t, program.Functions, 1) + + const parameterCount = 3 + + const ( + // arrayIndex is the index of the parameter `array`, which is the first parameter + arrayIndex = iota + // indexIndex is the index of the parameter `index`, which is the second parameter + indexIndex + // valueIndex is the index of the parameter `value`, which is the third parameter + valueIndex + ) + + // resultIndex is the index of the $result variable + const resultIndex = parameterCount + + assert.Equal(t, + []opcode.Instruction{ + opcode.InstructionGetLocal{LocalIndex: arrayIndex}, + opcode.InstructionGetLocal{LocalIndex: indexIndex}, + opcode.InstructionGetLocal{LocalIndex: valueIndex}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetIndex{}, + opcode.InstructionReturn{}, + }, + compiler.ExportFunctions()[0].Code, + ) +} + +func TestCompileAssignMember(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + struct Test { + var x: Int + + init(value: Int) { + self.x = value + } + } + `) + require.NoError(t, err) + + compiler := NewInstructionCompiler(checker) + program := compiler.Compile() + + require.Len(t, program.Functions, 1) + + const ( + // valueIndex is the index of the parameter `value`, which is the first parameter + valueIndex = iota + // selfIndex is the index of the `self` variable + selfIndex + ) + + assert.Equal(t, + []opcode.Instruction{ + // let self = Test() + opcode.InstructionNew{ + Kind: common.CompositeKindStructure, + TypeIndex: 0, + }, + opcode.InstructionSetLocal{LocalIndex: selfIndex}, + + // self.x = value + opcode.InstructionGetLocal{LocalIndex: selfIndex}, + opcode.InstructionGetLocal{LocalIndex: valueIndex}, + opcode.InstructionTransfer{TypeIndex: 1}, + opcode.InstructionSetField{FieldNameIndex: 0}, + + // return $result + opcode.InstructionGetLocal{LocalIndex: selfIndex}, + opcode.InstructionReturnValue{}, + }, + compiler.ExportFunctions()[0].Code, + ) +} From 11866623a7807f1bc14e7d97ba6bb54f4f6b70af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 19 Feb 2025 16:15:21 -0800 Subject: [PATCH 3/6] add more tests for compiling various features --- bbq/compiler/compiler_test.go | 273 ++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) diff --git a/bbq/compiler/compiler_test.go b/bbq/compiler/compiler_test.go index c6bb29695..06c43d7d3 100644 --- a/bbq/compiler/compiler_test.go +++ b/bbq/compiler/compiler_test.go @@ -1064,6 +1064,24 @@ func TestCompileNestedLoop(t *testing.T) { }, compiler.ExportFunctions()[0].Code, ) + + assert.Equal(t, + []*bbq.Constant{ + { + Data: []byte{0x0}, + Kind: constantkind.Int, + }, + { + Data: []byte{0xa}, + Kind: constantkind.Int, + }, + { + Data: []byte{0x1}, + Kind: constantkind.Int, + }, + }, + program.Constants, + ) } func TestCompileAssignLocal(t *testing.T) { @@ -1110,12 +1128,28 @@ func TestCompileAssignLocal(t *testing.T) { }, compiler.ExportFunctions()[0].Code, ) + + assert.Equal(t, + []*bbq.Constant{ + { + Data: []byte{0x0}, + Kind: constantkind.Int, + }, + { + Data: []byte{0x1}, + Kind: constantkind.Int, + }, + }, + program.Constants, + ) } func TestCompileAssignGlobal(t *testing.T) { t.Parallel() + // TODO: compile global variables + checker, err := ParseAndCheck(t, ` var x = 0 @@ -1141,6 +1175,63 @@ func TestCompileAssignGlobal(t *testing.T) { }, compiler.ExportFunctions()[0].Code, ) + + assert.Equal(t, + []*bbq.Constant{ + { + Data: []byte{0x1}, + Kind: constantkind.Int, + }, + }, + program.Constants, + ) +} + +func TestCompileIndex(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + fun test(array: [Int], index: Int): Int { + return array[index] + } + `) + require.NoError(t, err) + + compiler := NewInstructionCompiler(checker) + program := compiler.Compile() + + require.Len(t, program.Functions, 1) + + const parameterCount = 2 + + const ( + // arrayIndex is the index of the parameter `array`, which is the first parameter + arrayIndex = iota + // indexIndex is the index of the parameter `index`, which is the second parameter + indexIndex + ) + + // resultIndex is the index of the $result variable + const resultIndex = parameterCount + + assert.Equal(t, + []opcode.Instruction{ + // array[index] + opcode.InstructionGetLocal{LocalIndex: arrayIndex}, + opcode.InstructionGetLocal{LocalIndex: indexIndex}, + opcode.InstructionGetIndex{}, + + // assign to temp $result + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: resultIndex}, + + // return $result + opcode.InstructionGetLocal{LocalIndex: resultIndex}, + opcode.InstructionReturnValue{}, + }, + compiler.ExportFunctions()[0].Code, + ) } func TestCompileAssignIndex(t *testing.T) { @@ -1235,3 +1326,185 @@ func TestCompileAssignMember(t *testing.T) { compiler.ExportFunctions()[0].Code, ) } + +func TestCompileExpressionStatement(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + fun f() {} + + fun test(){ + f() + } + `) + require.NoError(t, err) + + compiler := NewInstructionCompiler(checker) + program := compiler.Compile() + + require.Len(t, program.Functions, 2) + + assert.Equal(t, + []opcode.Instruction{ + opcode.InstructionReturn{}, + }, + compiler.ExportFunctions()[0].Code, + ) + + assert.Equal(t, + []opcode.Instruction{ + // f() + opcode.InstructionGetGlobal{GlobalIndex: 0}, + opcode.InstructionInvoke{TypeArgs: nil}, + opcode.InstructionDrop{}, + + opcode.InstructionReturn{}, + }, + compiler.ExportFunctions()[1].Code, + ) +} + +func TestCompileBool(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + + fun test() { + let yes = true + let no = false + } + `) + require.NoError(t, err) + + compiler := NewInstructionCompiler(checker) + program := compiler.Compile() + + require.Len(t, program.Functions, 1) + + const parameterCount = 0 + + // resultIndex is the index of the $result variable + const resultIndex = parameterCount + + // localsOffset is the offset of the first local variable + const localsOffset = resultIndex + 1 + + const ( + // yesIndex is the index of the local variable `yes`, which is the first local variable + yesIndex = localsOffset + iota + // noIndex is the index of the local variable `no`, which is the second local variable + noIndex + ) + + assert.Equal(t, + []opcode.Instruction{ + // let yes = true + opcode.InstructionTrue{}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: yesIndex}, + + // let no = false + opcode.InstructionFalse{}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: noIndex}, + + opcode.InstructionReturn{}, + }, + compiler.ExportFunctions()[0].Code, + ) +} + +func TestCompileString(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + + fun test(): String { + return "Hello, world!" + } + `) + require.NoError(t, err) + + compiler := NewInstructionCompiler(checker) + program := compiler.Compile() + + require.Len(t, program.Functions, 1) + + const parameterCount = 0 + + // resultIndex is the index of the $result variable + const resultIndex = parameterCount + + assert.Equal(t, + []opcode.Instruction{ + // return "Hello, world!" + opcode.InstructionGetConstant{ConstantIndex: 0}, + + // assign to temp $result + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: resultIndex}, + + // return $result + opcode.InstructionGetLocal{LocalIndex: resultIndex}, + opcode.InstructionReturnValue{}, + }, + compiler.ExportFunctions()[0].Code, + ) + + assert.Equal(t, + []*bbq.Constant{ + { + Data: []byte("Hello, world!"), + Kind: constantkind.String, + }, + }, + program.Constants, + ) +} + +func TestCompileUnary(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + + fun test() { + let no = !true + } + `) + require.NoError(t, err) + + compiler := NewInstructionCompiler(checker) + program := compiler.Compile() + + require.Len(t, program.Functions, 1) + + const parameterCount = 0 + + // resultIndex is the index of the $result variable + const resultIndex = parameterCount + + // localsOffset is the offset of the first local variable + const localsOffset = resultIndex + 1 + + const ( + // noIndex is the index of the local variable `no`, which is the first local variable + noIndex = localsOffset + iota + ) + + assert.Equal(t, + []opcode.Instruction{ + // let no = !true + opcode.InstructionTrue{}, + opcode.InstructionNot{}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: noIndex}, + + opcode.InstructionReturn{}, + }, + compiler.ExportFunctions()[0].Code, + ) +} From dfb4ff5bcda97248a16dbdbeacf33e850ad4c9fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 20 Feb 2025 13:37:09 -0800 Subject: [PATCH 4/6] move compiler tests to test package, test member access --- bbq/compiler/compiler_test.go | 311 +++++++++++++++++++++++----------- 1 file changed, 212 insertions(+), 99 deletions(-) diff --git a/bbq/compiler/compiler_test.go b/bbq/compiler/compiler_test.go index 06c43d7d3..1474504e8 100644 --- a/bbq/compiler/compiler_test.go +++ b/bbq/compiler/compiler_test.go @@ -16,7 +16,7 @@ * limitations under the License. */ -package compiler +package compiler_test import ( "testing" @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/cadence/bbq" + "github.com/onflow/cadence/bbq/compiler" "github.com/onflow/cadence/bbq/constantkind" "github.com/onflow/cadence/bbq/opcode" "github.com/onflow/cadence/common" @@ -47,10 +48,14 @@ func TestCompileRecursionFib(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + assert.Equal(t, []opcode.Instruction{ // if n < 2 @@ -83,7 +88,7 @@ func TestCompileRecursionFib(t *testing.T) { opcode.InstructionGetLocal{LocalIndex: 0x1}, opcode.InstructionReturnValue{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) assert.Equal(t, @@ -122,8 +127,8 @@ func TestCompileImperativeFib(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() const parameterCount = 1 @@ -148,6 +153,10 @@ func TestCompileImperativeFib(t *testing.T) { ) require.Len(t, program.Functions, 1) + + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + assert.Equal(t, []opcode.Instruction{ // var fib1 = 1 @@ -214,7 +223,7 @@ func TestCompileImperativeFib(t *testing.T) { opcode.InstructionGetLocal{LocalIndex: resultIndex}, opcode.InstructionReturnValue{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) assert.Equal(t, @@ -250,8 +259,8 @@ func TestCompileBreak(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() const parameterCount = 0 @@ -265,6 +274,10 @@ func TestCompileBreak(t *testing.T) { const iIndex = localsOffset require.Len(t, program.Functions, 1) + + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + assert.Equal(t, []opcode.Instruction{ // var i = 0 @@ -306,7 +319,7 @@ func TestCompileBreak(t *testing.T) { opcode.InstructionGetLocal{LocalIndex: resultIndex}, opcode.InstructionReturnValue{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) assert.Equal(t, @@ -347,8 +360,13 @@ func TestCompileContinue(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() + + require.Len(t, program.Functions, 1) + + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) const parameterCount = 0 @@ -361,7 +379,6 @@ func TestCompileContinue(t *testing.T) { // iIndex is the index of the local variable `i`, which is the first local variable const iIndex = localsOffset - require.Len(t, program.Functions, 1) assert.Equal(t, []opcode.Instruction{ // var i = 0 @@ -406,7 +423,7 @@ func TestCompileContinue(t *testing.T) { opcode.InstructionGetLocal{LocalIndex: resultIndex}, opcode.InstructionReturnValue{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) assert.Equal(t, @@ -439,11 +456,14 @@ func TestCompileArray(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + const parameterCount = 0 // resultIndex is the index of the $result variable @@ -473,7 +493,7 @@ func TestCompileArray(t *testing.T) { opcode.InstructionReturn{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) assert.Equal(t, @@ -506,11 +526,14 @@ func TestCompileDictionary(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + const parameterCount = 0 // resultIndex is the index of the $result variable @@ -542,7 +565,7 @@ func TestCompileDictionary(t *testing.T) { opcode.InstructionReturn{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) assert.Equal(t, @@ -593,11 +616,14 @@ func TestCompileIfLet(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + assert.Equal(t, []opcode.Instruction{ // let y = x @@ -625,7 +651,7 @@ func TestCompileIfLet(t *testing.T) { opcode.InstructionReturn{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) assert.Equal(t, @@ -659,11 +685,14 @@ func TestCompileSwitch(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + const parameterCount = 1 // xIndex is the index of the parameter `x`, which is the first parameter @@ -738,7 +767,7 @@ func TestCompileSwitch(t *testing.T) { opcode.InstructionGetLocal{LocalIndex: resultIndex}, opcode.InstructionReturnValue{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) assert.Equal(t, @@ -777,13 +806,16 @@ func TestCompileEmit(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 2) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + var testFunction *bbq.Function[opcode.Instruction] - for _, f := range compiler.ExportFunctions() { + for _, f := range functions { if f.Name == "test" { testFunction = f } @@ -822,11 +854,14 @@ func TestCompileSimpleCast(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + const parameterCount = 1 // xIndex is the index of the parameter `x`, which is the first parameter @@ -849,7 +884,7 @@ func TestCompileSimpleCast(t *testing.T) { opcode.InstructionGetLocal{LocalIndex: resultIndex}, opcode.InstructionReturnValue{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) } @@ -864,11 +899,14 @@ func TestCompileForceCast(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + const parameterCount = 1 // xIndex is the index of the parameter `x`, which is the first parameter @@ -891,7 +929,7 @@ func TestCompileForceCast(t *testing.T) { opcode.InstructionGetLocal{LocalIndex: resultIndex}, opcode.InstructionReturnValue{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) } @@ -906,11 +944,14 @@ func TestCompileFailableCast(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + const parameterCount = 1 // xIndex is the index of the parameter `x`, which is the first parameter @@ -933,7 +974,7 @@ func TestCompileFailableCast(t *testing.T) { opcode.InstructionGetLocal{LocalIndex: resultIndex}, opcode.InstructionReturnValue{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) } @@ -961,11 +1002,14 @@ func TestCompileNestedLoop(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + const parameterCount = 0 // resultIndex is the index of the $result variable @@ -1062,7 +1106,7 @@ func TestCompileNestedLoop(t *testing.T) { opcode.InstructionGetLocal{LocalIndex: resultIndex}, opcode.InstructionReturnValue{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) assert.Equal(t, @@ -1096,11 +1140,14 @@ func TestCompileAssignLocal(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + const parameterCount = 0 // resultIndex is the index of the $result variable @@ -1126,7 +1173,7 @@ func TestCompileAssignLocal(t *testing.T) { opcode.InstructionReturn{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) assert.Equal(t, @@ -1159,11 +1206,14 @@ func TestCompileAssignGlobal(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + assert.Equal(t, []opcode.Instruction{ // x = 1 @@ -1173,7 +1223,7 @@ func TestCompileAssignGlobal(t *testing.T) { opcode.InstructionReturn{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) assert.Equal(t, @@ -1198,11 +1248,14 @@ func TestCompileIndex(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + const parameterCount = 2 const ( @@ -1230,7 +1283,7 @@ func TestCompileIndex(t *testing.T) { opcode.InstructionGetLocal{LocalIndex: resultIndex}, opcode.InstructionReturnValue{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) } @@ -1245,12 +1298,13 @@ func TestCompileAssignIndex(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) - const parameterCount = 3 + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) const ( // arrayIndex is the index of the parameter `array`, which is the first parameter @@ -1261,9 +1315,6 @@ func TestCompileAssignIndex(t *testing.T) { valueIndex ) - // resultIndex is the index of the $result variable - const resultIndex = parameterCount - assert.Equal(t, []opcode.Instruction{ opcode.InstructionGetLocal{LocalIndex: arrayIndex}, @@ -1273,57 +1324,105 @@ func TestCompileAssignIndex(t *testing.T) { opcode.InstructionSetIndex{}, opcode.InstructionReturn{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) } -func TestCompileAssignMember(t *testing.T) { +func TestCompileMember(t *testing.T) { t.Parallel() checker, err := ParseAndCheck(t, ` struct Test { - var x: Int + var foo: Int init(value: Int) { - self.x = value + self.foo = value + } + + fun getValue(): Int { + return self.foo } } `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() - require.Len(t, program.Functions, 1) + require.Len(t, program.Functions, 2) - const ( - // valueIndex is the index of the parameter `value`, which is the first parameter - valueIndex = iota - // selfIndex is the index of the `self` variable - selfIndex - ) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) - assert.Equal(t, - []opcode.Instruction{ - // let self = Test() - opcode.InstructionNew{ - Kind: common.CompositeKindStructure, - TypeIndex: 0, + { + const parameterCount = 1 + + // valueIndex is the index of the parameter `value`, which is the first parameter + const valueIndex = iota + + // localsOffset is the offset of the first local variable. + // Initializers do not have a $result variable + const localsOffset = parameterCount + + const ( + // selfIndex is the index of the local variable `self`, which is the first local variable + selfIndex = localsOffset + iota + ) + + assert.Equal(t, + []opcode.Instruction{ + // let self = Test() + opcode.InstructionNew{ + Kind: common.CompositeKindStructure, + TypeIndex: 0, + }, + opcode.InstructionSetLocal{LocalIndex: selfIndex}, + + // self.x = value + opcode.InstructionGetLocal{LocalIndex: selfIndex}, + opcode.InstructionGetLocal{LocalIndex: valueIndex}, + opcode.InstructionTransfer{TypeIndex: 1}, + opcode.InstructionSetField{FieldNameIndex: 0}, + + // return self + opcode.InstructionGetLocal{LocalIndex: selfIndex}, + opcode.InstructionReturnValue{}, }, - opcode.InstructionSetLocal{LocalIndex: selfIndex}, + functions[0].Code, + ) + } - // self.x = value - opcode.InstructionGetLocal{LocalIndex: selfIndex}, - opcode.InstructionGetLocal{LocalIndex: valueIndex}, - opcode.InstructionTransfer{TypeIndex: 1}, - opcode.InstructionSetField{FieldNameIndex: 0}, + { + const parameterCount = 1 - // return $result - opcode.InstructionGetLocal{LocalIndex: selfIndex}, - opcode.InstructionReturnValue{}, + // nIndex is the index of the parameter `self`, which is the first parameter + const selfIndex = 0 + + // resultIndex is the index of the $result variable + const resultIndex = parameterCount + + assert.Equal(t, + []opcode.Instruction{ + opcode.InstructionGetLocal{LocalIndex: selfIndex}, + opcode.InstructionGetField{FieldNameIndex: 0}, + opcode.InstructionTransfer{TypeIndex: 1}, + opcode.InstructionSetLocal{LocalIndex: resultIndex}, + opcode.InstructionGetLocal{LocalIndex: resultIndex}, + opcode.InstructionReturnValue{}, + }, + functions[1].Code, + ) + } + + assert.Equal(t, + []*bbq.Constant{ + { + Data: []byte("foo"), + Kind: constantkind.String, + }, }, - compiler.ExportFunctions()[0].Code, + program.Constants, ) } @@ -1334,22 +1433,25 @@ func TestCompileExpressionStatement(t *testing.T) { checker, err := ParseAndCheck(t, ` fun f() {} - fun test(){ + fun test() { f() } `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 2) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + assert.Equal(t, []opcode.Instruction{ opcode.InstructionReturn{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) assert.Equal(t, @@ -1361,7 +1463,7 @@ func TestCompileExpressionStatement(t *testing.T) { opcode.InstructionReturn{}, }, - compiler.ExportFunctions()[1].Code, + functions[1].Code, ) } @@ -1378,11 +1480,14 @@ func TestCompileBool(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + const parameterCount = 0 // resultIndex is the index of the $result variable @@ -1412,7 +1517,7 @@ func TestCompileBool(t *testing.T) { opcode.InstructionReturn{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) } @@ -1428,11 +1533,14 @@ func TestCompileString(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + const parameterCount = 0 // resultIndex is the index of the $result variable @@ -1451,7 +1559,7 @@ func TestCompileString(t *testing.T) { opcode.InstructionGetLocal{LocalIndex: resultIndex}, opcode.InstructionReturnValue{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, ) assert.Equal(t, @@ -1477,11 +1585,14 @@ func TestCompileUnary(t *testing.T) { `) require.NoError(t, err) - compiler := NewInstructionCompiler(checker) - program := compiler.Compile() + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() require.Len(t, program.Functions, 1) + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + const parameterCount = 0 // resultIndex is the index of the $result variable @@ -1505,6 +1616,8 @@ func TestCompileUnary(t *testing.T) { opcode.InstructionReturn{}, }, - compiler.ExportFunctions()[0].Code, + functions[0].Code, + ) +} ) } From 2d68fe327dd2d0e95b5ed0fdfd5258ba5bb7ada2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 20 Feb 2025 13:39:17 -0800 Subject: [PATCH 5/6] improve and test compilation of nil-coalescing --- bbq/compiler/compiler.go | 22 ++++++------ bbq/compiler/compiler_test.go | 64 +++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 11 deletions(-) diff --git a/bbq/compiler/compiler.go b/bbq/compiler/compiler.go index 3085e8422..64c584f31 100644 --- a/bbq/compiler/compiler.go +++ b/bbq/compiler/compiler.go @@ -1076,24 +1076,24 @@ func (c *Compiler[_]) VisitBinaryExpression(expression *ast.BinaryExpression) (_ switch expression.Operation { case ast.OperationNilCoalesce: - // create a duplicate to perform the equal check. - // So if the condition succeeds, then the condition's result will be at the top of the stack. + // Duplicate the value for the nil equality check. c.codeGen.Emit(opcode.InstructionDup{}) + elseJump := c.emitUndefinedJumpIfNil() - c.codeGen.Emit(opcode.InstructionNil{}) - c.codeGen.Emit(opcode.InstructionEqual{}) - elseJump := c.emitUndefinedJumpIfFalse() + // Then branch + c.codeGen.Emit(opcode.InstructionUnwrap{}) + thenJump := c.emitUndefinedJump() - // Drop the duplicated condition result. - // It is not needed for the 'then' path. + // Else branch + c.patchJump(elseJump) + // Drop the duplicated condition result, + // as it is not needed for the 'else' path. c.codeGen.Emit(opcode.InstructionDrop{}) - c.compileExpression(expression.Right) - thenJump := c.emitUndefinedJump() - c.patchJump(elseJump) - c.codeGen.Emit(opcode.InstructionUnwrap{}) + // End c.patchJump(thenJump) + default: c.compileExpression(expression.Right) diff --git a/bbq/compiler/compiler_test.go b/bbq/compiler/compiler_test.go index 1474504e8..c482d006c 100644 --- a/bbq/compiler/compiler_test.go +++ b/bbq/compiler/compiler_test.go @@ -1619,5 +1619,69 @@ func TestCompileUnary(t *testing.T) { functions[0].Code, ) } + +func TestCompileNilCoalesce(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + + fun test(_ value: Int?): Int { + return value ?? 0 + } + `) + require.NoError(t, err) + + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() + + require.Len(t, program.Functions, 1) + + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + + const parameterCount = 1 + + // valueIndex is the index of the parameter `value`, which is the first parameter + const valueIndex = 0 + + const resultIndex = parameterCount + + assert.Equal(t, + []opcode.Instruction{ + // value ?? + opcode.InstructionGetLocal{LocalIndex: valueIndex}, + opcode.InstructionDup{}, + opcode.InstructionJumpIfNil{Target: 5}, + + // value + opcode.InstructionUnwrap{}, + opcode.InstructionJump{Target: 7}, + + // 0 + opcode.InstructionDrop{}, + opcode.InstructionGetConstant{ConstantIndex: 0}, + + // assign to temp $result + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: resultIndex}, + + // return $result + opcode.InstructionGetLocal{LocalIndex: resultIndex}, + opcode.InstructionReturnValue{}, + }, + functions[0].Code, + ) + + assert.Equal(t, + []*bbq.Constant{ + { + Data: []byte{0}, + Kind: constantkind.Int, + }, + }, + program.Constants, + ) +} ) } From 62917236dccd69f2f53214af6b4d662c0d11c9a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Thu, 20 Feb 2025 13:39:50 -0800 Subject: [PATCH 6/6] test compilation of binary expressions, method invocation, create/destroy, and paths --- bbq/compiler/compiler_test.go | 280 ++++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) diff --git a/bbq/compiler/compiler_test.go b/bbq/compiler/compiler_test.go index c482d006c..f97b771c5 100644 --- a/bbq/compiler/compiler_test.go +++ b/bbq/compiler/compiler_test.go @@ -1620,6 +1620,68 @@ func TestCompileUnary(t *testing.T) { ) } +func TestCompileBinary(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + + fun test() { + let three = 1 + 2 + } + `) + require.NoError(t, err) + + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() + + require.Len(t, program.Functions, 1) + + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + + const parameterCount = 0 + + // resultIndex is the index of the $result variable + const resultIndex = parameterCount + + // localsOffset is the offset of the first local variable + const localsOffset = resultIndex + 1 + + const ( + // threeIndex is the index of the local variable `three`, which is the first local variable + threeIndex = localsOffset + iota + ) + + assert.Equal(t, + []opcode.Instruction{ + // let three = 1 + 2 + opcode.InstructionGetConstant{ConstantIndex: 0}, + opcode.InstructionGetConstant{ConstantIndex: 1}, + opcode.InstructionAdd{}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: threeIndex}, + + opcode.InstructionReturn{}, + }, + functions[0].Code, + ) + + assert.Equal(t, + []*bbq.Constant{ + { + Data: []byte{0x1}, + Kind: constantkind.Int, + }, + { + Data: []byte{0x2}, + Kind: constantkind.Int, + }, + }, + program.Constants, + ) +} + func TestCompileNilCoalesce(t *testing.T) { t.Parallel() @@ -1683,5 +1745,223 @@ func TestCompileNilCoalesce(t *testing.T) { program.Constants, ) } + +func TestCompileMethodInvocation(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + struct Foo { + fun f(_ x: Bool) {} + } + + fun test() { + let foo = Foo() + foo.f(true) + } + `) + require.NoError(t, err) + + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() + + require.Len(t, program.Functions, 3) + + functions := comp.ExportFunctions() + require.Equal(t, len(program.Functions), len(functions)) + + { + const parameterCount = 0 + + const resultIndex = parameterCount + + const localsOffset = resultIndex + 1 + + const ( + // fooIndex is the index of the local variable `foo`, which is the first local variable + fooIndex = localsOffset + iota + ) + + assert.Equal(t, + []opcode.Instruction{ + // let foo = Foo() + opcode.InstructionGetGlobal{GlobalIndex: 1}, + opcode.InstructionInvoke{TypeArgs: nil}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: fooIndex}, + + // foo.f(true) + opcode.InstructionGetLocal{LocalIndex: fooIndex}, + opcode.InstructionTrue{}, + opcode.InstructionTransfer{TypeIndex: 1}, + opcode.InstructionGetGlobal{GlobalIndex: 2}, + opcode.InstructionInvoke{TypeArgs: nil}, + opcode.InstructionDrop{}, + + opcode.InstructionReturn{}, + }, + functions[0].Code, + ) + } + + { + const parameterCount = 0 + + const resultIndex = parameterCount + + assert.Equal(t, + []opcode.Instruction{ + // Foo() + opcode.InstructionNew{ + Kind: common.CompositeKindStructure, + TypeIndex: 0, + }, + + // assign to temp $result + opcode.InstructionSetLocal{LocalIndex: resultIndex}, + + // return $result + opcode.InstructionGetLocal{LocalIndex: resultIndex}, + opcode.InstructionReturnValue{}, + }, + functions[1].Code, + ) + } + + assert.Equal(t, + []opcode.Instruction{ + opcode.InstructionReturn{}, + }, + functions[2].Code, + ) +} + +func TestCompileResourceCreateAndDestroy(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + resource Foo {} + + fun test() { + let foo <- create Foo() + destroy foo + } + `) + require.NoError(t, err) + + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() + + require.Len(t, program.Functions, 2) + + functions := comp.ExportFunctions() + require.Equal(t, len(functions), len(program.Functions)) + + { + const parameterCount = 0 + + const resultIndex = parameterCount + + const localsOffset = resultIndex + 1 + + const ( + // fooIndex is the index of the local variable `foo`, which is the first local variable + fooIndex = localsOffset + iota + ) + + assert.Equal(t, + []opcode.Instruction{ + // let foo <- create Foo() + opcode.InstructionGetGlobal{GlobalIndex: 1}, + opcode.InstructionInvoke{TypeArgs: nil}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: fooIndex}, + + // destroy foo + opcode.InstructionGetLocal{LocalIndex: fooIndex}, + opcode.InstructionDestroy{}, + + opcode.InstructionReturn{}, + }, + functions[0].Code, + ) + } + + { + const parameterCount = 0 + + const resultIndex = parameterCount + + assert.Equal(t, + []opcode.Instruction{ + // Foo() + opcode.InstructionNew{ + Kind: common.CompositeKindResource, + TypeIndex: 0, + }, + + // assign to temp $result + opcode.InstructionSetLocal{LocalIndex: resultIndex}, + + // return $result + opcode.InstructionGetLocal{LocalIndex: resultIndex}, + opcode.InstructionReturnValue{}, + }, + functions[1].Code, + ) + } +} + +func TestCompilePath(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + fun test(): Path { + return /storage/foo + } + `) + require.NoError(t, err) + + comp := compiler.NewInstructionCompiler(checker) + program := comp.Compile() + + require.Len(t, program.Functions, 1) + + functions := comp.ExportFunctions() + require.Equal(t, len(functions), len(program.Functions)) + + const parameterCount = 0 + + // resultIndex is the index of the $result variable + const resultIndex = parameterCount + + assert.Equal(t, + []opcode.Instruction{ + opcode.InstructionPath{ + Domain: common.PathDomainStorage, + IdentifierIndex: 0, + }, + + // assign to temp $result + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: resultIndex}, + + // return $result + opcode.InstructionGetLocal{LocalIndex: resultIndex}, + opcode.InstructionReturnValue{}, + }, + functions[0].Code, + ) + + assert.Equal(t, + []*bbq.Constant{ + { + Data: []byte("foo"), + Kind: constantkind.String, + }, + }, + program.Constants, ) }