From b99f43a7a5af866e45f43c0653caf3b6695eaa8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 26 Feb 2025 11:10:48 -0800 Subject: [PATCH 1/3] add Deref instruction, compile unary deref operator --- bbq/compiler/compiler.go | 12 ++++-- bbq/compiler/compiler_test.go | 80 ++++++++++++++++++++++++++++------- bbq/opcode/instructions.go | 22 ++++++++++ bbq/opcode/instructions.yml | 12 ++++++ bbq/opcode/opcode.go | 9 ++-- bbq/vm/test/vm_test.go | 60 ++++++++++++++++++++++++++ bbq/vm/vm.go | 37 ++++++++++++++-- 7 files changed, 207 insertions(+), 25 deletions(-) diff --git a/bbq/compiler/compiler.go b/bbq/compiler/compiler.go index a906a0fa8..35403e1dc 100644 --- a/bbq/compiler/compiler.go +++ b/bbq/compiler/compiler.go @@ -1250,12 +1250,18 @@ func (c *Compiler[_]) VisitConditionalExpression(expression *ast.ConditionalExpr } func (c *Compiler[_]) VisitUnaryExpression(expression *ast.UnaryExpression) (_ struct{}) { + c.compileExpression(expression.Expression) + switch expression.Operation { - case ast.OperationMove: - c.compileExpression(expression.Expression) case ast.OperationNegate: - c.compileExpression(expression.Expression) c.codeGen.Emit(opcode.InstructionNot{}) + + case ast.OperationMul: + c.codeGen.Emit(opcode.InstructionDeref{}) + + case ast.OperationMove: + // TODO: invalidate + default: // TODO panic(errors.NewUnreachableError()) diff --git a/bbq/compiler/compiler_test.go b/bbq/compiler/compiler_test.go index 3d5dabac5..d03c50980 100644 --- a/bbq/compiler/compiler_test.go +++ b/bbq/compiler/compiler_test.go @@ -1899,10 +1899,10 @@ func TestCompileIntegers(t *testing.T) { checker, err := ParseAndCheck(t, fmt.Sprintf(` - fun test() { - let v: %s = 2 - } - `, + fun test() { + let v: %s = 2 + } + `, integerType, ), ) @@ -1975,10 +1975,10 @@ func TestCompileFixedPoint(t *testing.T) { checker, err := ParseAndCheck(t, fmt.Sprintf(` - fun test() { - let v: %s = 2.3 - } - `, + fun test() { + let v: %s = 2.3 + } + `, fixedPointType, ), ) @@ -2047,12 +2047,11 @@ func TestCompileFixedPoint(t *testing.T) { } } -func TestCompileUnary(t *testing.T) { +func TestCompileUnaryNot(t *testing.T) { t.Parallel() checker, err := ParseAndCheck(t, ` - fun test() { let no = !true } @@ -2094,6 +2093,55 @@ func TestCompileUnary(t *testing.T) { ) } +func TestCompileUnaryDeref(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + fun test(ref: &Int) { + let v = *ref + } + `) + 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 + + // refIndex is the index of the parameter `ref`, which is the first parameter + const refIndex = 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 ( + // vIndex is the index of the local variable `v`, which is the first local variable + vIndex = localsOffset + iota + ) + + assert.Equal(t, + []opcode.Instruction{ + // let v = *ref + opcode.InstructionGetLocal{LocalIndex: refIndex}, + opcode.InstructionDeref{}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: vIndex}, + + opcode.InstructionReturn{}, + }, + functions[0].Code, + ) +} + func TestCompileBinary(t *testing.T) { t.Parallel() @@ -2139,7 +2187,7 @@ func TestCompileBinary(t *testing.T) { assert.Equal(t, []opcode.Instruction{ - // let three = 1 + 2 + // let v = 6 ... 3 opcode.InstructionGetConstant{ConstantIndex: 0}, opcode.InstructionGetConstant{ConstantIndex: 1}, instruction, @@ -3519,11 +3567,11 @@ func TestCompileIf(t *testing.T) { checker, err := ParseAndCheck(t, ` fun test(x: Bool): Int { var y = 0 - if x { - y = 1 - } else { - y = 2 - } + if x { + y = 1 + } else { + y = 2 + } return y } `) diff --git a/bbq/opcode/instructions.go b/bbq/opcode/instructions.go index bf3c08f0c..85adf34e7 100644 --- a/bbq/opcode/instructions.go +++ b/bbq/opcode/instructions.go @@ -781,6 +781,26 @@ func DecodeForceCast(ip *uint16, code []byte) (i InstructionForceCast) { return i } +// InstructionDeref +// +// Pops an (optional) reference off the stack, dereferences it, and then pushes the value back on to the stack. +type InstructionDeref struct { +} + +var _ Instruction = InstructionDeref{} + +func (InstructionDeref) Opcode() Opcode { + return Deref +} + +func (i InstructionDeref) String() string { + return i.Opcode().String() +} + +func (i InstructionDeref) Encode(code *[]byte) { + emitOpcode(code, i.Opcode()) +} + // InstructionJump // // Unconditionally jumps to the given instruction. @@ -1429,6 +1449,8 @@ func DecodeInstruction(ip *uint16, code []byte) Instruction { return DecodeFailableCast(ip, code) case ForceCast: return DecodeForceCast(ip, code) + case Deref: + return InstructionDeref{} case Jump: return DecodeJump(ip, code) case JumpIfFalse: diff --git a/bbq/opcode/instructions.yml b/bbq/opcode/instructions.yml index fb3c38089..045eec0e5 100644 --- a/bbq/opcode/instructions.yml +++ b/bbq/opcode/instructions.yml @@ -376,6 +376,18 @@ - name: "value" type: "value" +- name: deref + description: + Pops an (optional) reference off the stack, dereferences it, + and then pushes the value back on to the stack. + valueEffects: + pop: + - name: reference + type: value + push: + - name: value + type: value + # Control flow instructions - name: "jump" diff --git a/bbq/opcode/opcode.go b/bbq/opcode/opcode.go index 0dfb2a39a..979fd9ae8 100644 --- a/bbq/opcode/opcode.go +++ b/bbq/opcode/opcode.go @@ -38,14 +38,14 @@ const ( _ _ - // Int operations + // Number operations Add Subtract Multiply Divide Mod - _ + Negate _ _ _ @@ -83,7 +83,7 @@ const ( SimpleCast FailableCast ForceCast - _ + Deref _ _ _ @@ -168,4 +168,7 @@ const ( // Other EmitEvent + + // NOTE: not an actual opcode, must be last item + OpcodeMax ) diff --git a/bbq/vm/test/vm_test.go b/bbq/vm/test/vm_test.go index 72f92bd43..6bcef1ab5 100644 --- a/bbq/vm/test/vm_test.go +++ b/bbq/vm/test/vm_test.go @@ -4733,6 +4733,66 @@ func TestCompileAnd(t *testing.T) { }) } +func TestCompileUnaryDeref(t *testing.T) { + + t.Parallel() + + actual, err := compileAndInvoke(t, + ` + fun test(): Int { + let x = 42 + let ref: &Int = &x + return *ref + } + `, + "test", + ) + require.NoError(t, err) + + assert.Equal(t, vm.NewIntValue(42), actual) +} + +func TestCompileUnaryDerefSome(t *testing.T) { + + t.Parallel() + + actual, err := compileAndInvoke(t, + ` + fun test(): Int? { + let x = 42 + let ref: &Int = &x + let optRef = ref as? &Int + return *optRef + } + `, + "test", + ) + require.NoError(t, err) + + assert.Equal(t, + vm.NewSomeValueNonCopying(vm.NewIntValue(42)), + actual, + ) +} + +func TestCompileUnaryDerefNil(t *testing.T) { + + t.Parallel() + + actual, err := compileAndInvoke(t, + ` + fun test(): Int? { + let optRef: &Int? = nil + return *optRef + } + `, + "test", + ) + require.NoError(t, err) + + assert.Equal(t, vm.Nil, actual) +} + func TestBinary(t *testing.T) { t.Parallel() diff --git a/bbq/vm/vm.go b/bbq/vm/vm.go index d288017c9..0df89f7ee 100644 --- a/bbq/vm/vm.go +++ b/bbq/vm/vm.go @@ -839,6 +839,38 @@ func opIteratorNext(vm *VM) { vm.push(element) } +func deref(vm *VM, value Value) Value { + if _, ok := value.(NilValue); ok { + return Nil + } + + var isOptional bool + + if someValue, ok := value.(*SomeValue); ok { + isOptional = true + value = someValue.value + } + + referenceValue, ok := value.(ReferenceValue) + if !ok { + panic(errors.NewUnreachableError()) + } + + // TODO: port and use interpreter.DereferenceValue + dereferencedValue := *referenceValue.ReferencedValue(vm.config, true) + if isOptional { + return NewSomeValueNonCopying(dereferencedValue) + } else { + return dereferencedValue + } +} + +func opDeref(vm *VM) { + value := vm.pop() + dereferenced := deref(vm, value) + vm.push(dereferenced) +} + func (vm *VM) run() { for { @@ -962,12 +994,11 @@ func (vm *VM) run() { opIteratorHasNext(vm) case opcode.InstructionIteratorNext: opIteratorNext(vm) + case opcode.InstructionDeref: + opDeref(vm) default: panic(errors.NewUnexpectedError("cannot execute instruction of type %T", ins)) } - - // Faster in Go <1.19: - // vmOps[op](vm) } } From 80f7219ae232d9bc7dd8a834714a8adaae58d42e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 26 Feb 2025 11:11:27 -0800 Subject: [PATCH 2/3] add Negate instruction, compile unary minus operator --- bbq/compiler/compiler.go | 4 ++- bbq/compiler/compiler_test.go | 49 ++++++++++++++++++++++++++++++++ bbq/opcode/instructions.go | 22 +++++++++++++++ bbq/opcode/instructions.yml | 12 ++++++++ bbq/opcode/opcode_string.go | 21 ++++++++------ bbq/vm/test/vm_test.go | 53 +++++++++++++++++++++++++++++++++++ bbq/vm/value_int.go | 4 +++ bbq/vm/value_number.go | 1 + bbq/vm/value_ufix64.go | 5 ++++ bbq/vm/vm.go | 7 +++++ 10 files changed, 168 insertions(+), 10 deletions(-) diff --git a/bbq/compiler/compiler.go b/bbq/compiler/compiler.go index 35403e1dc..2d42a86f4 100644 --- a/bbq/compiler/compiler.go +++ b/bbq/compiler/compiler.go @@ -1256,6 +1256,9 @@ func (c *Compiler[_]) VisitUnaryExpression(expression *ast.UnaryExpression) (_ s case ast.OperationNegate: c.codeGen.Emit(opcode.InstructionNot{}) + case ast.OperationMinus: + c.codeGen.Emit(opcode.InstructionNegate{}) + case ast.OperationMul: c.codeGen.Emit(opcode.InstructionDeref{}) @@ -1263,7 +1266,6 @@ func (c *Compiler[_]) VisitUnaryExpression(expression *ast.UnaryExpression) (_ s // TODO: invalidate default: - // TODO panic(errors.NewUnreachableError()) } diff --git a/bbq/compiler/compiler_test.go b/bbq/compiler/compiler_test.go index d03c50980..d770200e2 100644 --- a/bbq/compiler/compiler_test.go +++ b/bbq/compiler/compiler_test.go @@ -2093,6 +2093,55 @@ func TestCompileUnaryNot(t *testing.T) { ) } +func TestCompileUnaryNegate(t *testing.T) { + + t.Parallel() + + checker, err := ParseAndCheck(t, ` + fun test(x: Int) { + let v = -x + } + `) + 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 + + // xIndex is the index of the parameter `x`, which is the first parameter + const xIndex = 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 ( + // vIndex is the index of the local variable `v`, which is the first local variable + vIndex = localsOffset + iota + ) + + assert.Equal(t, + []opcode.Instruction{ + // let v = -x + opcode.InstructionGetLocal{LocalIndex: xIndex}, + opcode.InstructionNegate{}, + opcode.InstructionTransfer{TypeIndex: 0}, + opcode.InstructionSetLocal{LocalIndex: vIndex}, + + opcode.InstructionReturn{}, + }, + functions[0].Code, + ) +} + func TestCompileUnaryDeref(t *testing.T) { t.Parallel() diff --git a/bbq/opcode/instructions.go b/bbq/opcode/instructions.go index 85adf34e7..bca6badc4 100644 --- a/bbq/opcode/instructions.go +++ b/bbq/opcode/instructions.go @@ -1121,6 +1121,26 @@ func (i InstructionMod) Encode(code *[]byte) { emitOpcode(code, i.Opcode()) } +// InstructionNegate +// +// Pops a number value off the stack, negates it, and then pushes the result back on to the stack. +type InstructionNegate struct { +} + +var _ Instruction = InstructionNegate{} + +func (InstructionNegate) Opcode() Opcode { + return Negate +} + +func (i InstructionNegate) String() string { + return i.Opcode().String() +} + +func (i InstructionNegate) Encode(code *[]byte) { + emitOpcode(code, i.Opcode()) +} + // InstructionLess // // Pops two values off the stack, checks if the first value is less than the second, and then pushes the result back on to the stack. @@ -1479,6 +1499,8 @@ func DecodeInstruction(ip *uint16, code []byte) Instruction { return InstructionDivide{} case Mod: return InstructionMod{} + case Negate: + return InstructionNegate{} case Less: return InstructionLess{} case LessOrEqual: diff --git a/bbq/opcode/instructions.yml b/bbq/opcode/instructions.yml index 045eec0e5..b81d9b5e5 100644 --- a/bbq/opcode/instructions.yml +++ b/bbq/opcode/instructions.yml @@ -569,6 +569,18 @@ - name: "result" type: "number" +- name: negate + description: + Pops a number value off the stack, negates it, + and then pushes the result back on to the stack. + valueEffects: + pop: + - name: value + type: number + push: + - name: result + type: number + # Integer comparison instructions - name: "less" diff --git a/bbq/opcode/opcode_string.go b/bbq/opcode/opcode_string.go index 30b3a0ec8..14760b9f1 100644 --- a/bbq/opcode/opcode_string.go +++ b/bbq/opcode/opcode_string.go @@ -20,6 +20,7 @@ func _() { _ = x[Multiply-13] _ = x[Divide-14] _ = x[Mod-15] + _ = x[Negate-16] _ = x[BitwiseOr-20] _ = x[BitwiseAnd-21] _ = x[BitwiseXor-22] @@ -38,6 +39,7 @@ func _() { _ = x[SimpleCast-39] _ = x[FailableCast-40] _ = x[ForceCast-41] + _ = x[Deref-42] _ = x[True-49] _ = x[False-50] _ = x[New-51] @@ -63,39 +65,40 @@ func _() { _ = x[IteratorHasNext-108] _ = x[IteratorNext-109] _ = x[EmitEvent-110] + _ = x[OpcodeMax-111] } const ( _Opcode_name_0 = "UnknownReturnReturnValueJumpJumpIfFalseJumpIfTrueJumpIfNil" - _Opcode_name_1 = "AddSubtractMultiplyDivideMod" + _Opcode_name_1 = "AddSubtractMultiplyDivideModNegate" _Opcode_name_2 = "BitwiseOrBitwiseAndBitwiseXorBitwiseLeftShiftBitwiseRightShift" _Opcode_name_3 = "LessGreaterLessOrEqualGreaterOrEqualEqualNotEqualNot" - _Opcode_name_4 = "UnwrapDestroyTransferSimpleCastFailableCastForceCast" + _Opcode_name_4 = "UnwrapDestroyTransferSimpleCastFailableCastForceCastDeref" _Opcode_name_5 = "TrueFalseNewPathNilNewArrayNewDictionaryNewRef" _Opcode_name_6 = "GetConstantGetLocalSetLocalGetGlobalSetGlobalGetFieldSetFieldSetIndexGetIndex" _Opcode_name_7 = "InvokeInvokeDynamic" _Opcode_name_8 = "DropDup" - _Opcode_name_9 = "IteratorIteratorHasNextIteratorNextEmitEvent" + _Opcode_name_9 = "IteratorIteratorHasNextIteratorNextEmitEventOpcodeMax" ) var ( _Opcode_index_0 = [...]uint8{0, 7, 13, 24, 28, 39, 49, 58} - _Opcode_index_1 = [...]uint8{0, 3, 11, 19, 25, 28} + _Opcode_index_1 = [...]uint8{0, 3, 11, 19, 25, 28, 34} _Opcode_index_2 = [...]uint8{0, 9, 19, 29, 45, 62} _Opcode_index_3 = [...]uint8{0, 4, 11, 22, 36, 41, 49, 52} - _Opcode_index_4 = [...]uint8{0, 6, 13, 21, 31, 43, 52} + _Opcode_index_4 = [...]uint8{0, 6, 13, 21, 31, 43, 52, 57} _Opcode_index_5 = [...]uint8{0, 4, 9, 12, 16, 19, 27, 40, 46} _Opcode_index_6 = [...]uint8{0, 11, 19, 27, 36, 45, 53, 61, 69, 77} _Opcode_index_7 = [...]uint8{0, 6, 19} _Opcode_index_8 = [...]uint8{0, 4, 7} - _Opcode_index_9 = [...]uint8{0, 8, 23, 35, 44} + _Opcode_index_9 = [...]uint8{0, 8, 23, 35, 44, 53} ) func (i Opcode) String() string { switch { case i <= 6: return _Opcode_name_0[_Opcode_index_0[i]:_Opcode_index_0[i+1]] - case 11 <= i && i <= 15: + case 11 <= i && i <= 16: i -= 11 return _Opcode_name_1[_Opcode_index_1[i]:_Opcode_index_1[i+1]] case 20 <= i && i <= 24: @@ -104,7 +107,7 @@ func (i Opcode) String() string { case 26 <= i && i <= 32: i -= 26 return _Opcode_name_3[_Opcode_index_3[i]:_Opcode_index_3[i+1]] - case 36 <= i && i <= 41: + case 36 <= i && i <= 42: i -= 36 return _Opcode_name_4[_Opcode_index_4[i]:_Opcode_index_4[i+1]] case 49 <= i && i <= 56: @@ -119,7 +122,7 @@ func (i Opcode) String() string { case 99 <= i && i <= 100: i -= 99 return _Opcode_name_8[_Opcode_index_8[i]:_Opcode_index_8[i+1]] - case 107 <= i && i <= 110: + case 107 <= i && i <= 111: i -= 107 return _Opcode_name_9[_Opcode_index_9[i]:_Opcode_index_9[i+1]] default: diff --git a/bbq/vm/test/vm_test.go b/bbq/vm/test/vm_test.go index 6bcef1ab5..3874f1cb5 100644 --- a/bbq/vm/test/vm_test.go +++ b/bbq/vm/test/vm_test.go @@ -4733,6 +4733,59 @@ func TestCompileAnd(t *testing.T) { }) } +func TestCompileUnaryNot(t *testing.T) { + + t.Parallel() + + test := func(t *testing.T, argument vm.Value) vm.Value { + + actual, err := compileAndInvoke(t, + ` + fun test(x: Bool): Bool { + return !x + } + `, + "test", + argument, + ) + require.NoError(t, err) + + return actual + } + + t.Run("true", func(t *testing.T) { + t.Parallel() + + actual := test(t, vm.BoolValue(true)) + require.Equal(t, vm.BoolValue(false), actual) + }) + + t.Run("false", func(t *testing.T) { + t.Parallel() + + actual := test(t, vm.BoolValue(false)) + require.Equal(t, vm.BoolValue(true), actual) + }) +} + +func TestCompileUnaryNegate(t *testing.T) { + + t.Parallel() + + actual, err := compileAndInvoke(t, + ` + fun test(x: Int): Int { + return -x + } + `, + "test", + vm.NewIntValue(42), + ) + require.NoError(t, err) + + assert.Equal(t, vm.NewIntValue(-42), actual) +} + func TestCompileUnaryDeref(t *testing.T) { t.Parallel() diff --git a/bbq/vm/value_int.go b/bbq/vm/value_int.go index 0aa99c593..5e69035fa 100644 --- a/bbq/vm/value_int.go +++ b/bbq/vm/value_int.go @@ -57,6 +57,10 @@ func (v IntValue) Transfer(*Config, atree.Address, bool, atree.Storable) Value { return v } +func (v IntValue) Negate() NumberValue { + return NewIntValue(-v.SmallInt) +} + func (v IntValue) Add(other NumberValue) NumberValue { otherInt, ok := other.(IntValue) if !ok { diff --git a/bbq/vm/value_number.go b/bbq/vm/value_number.go index 4ed6d24fb..0f06f49c5 100644 --- a/bbq/vm/value_number.go +++ b/bbq/vm/value_number.go @@ -20,6 +20,7 @@ package vm type NumberValue interface { ComparableValue + Negate() NumberValue Add(other NumberValue) NumberValue Subtract(other NumberValue) NumberValue Multiply(other NumberValue) NumberValue diff --git a/bbq/vm/value_ufix64.go b/bbq/vm/value_ufix64.go index 4d579eee6..d8e572da5 100644 --- a/bbq/vm/value_ufix64.go +++ b/bbq/vm/value_ufix64.go @@ -23,6 +23,7 @@ import ( "github.com/onflow/atree" + "github.com/onflow/cadence/errors" "github.com/onflow/cadence/format" "github.com/onflow/cadence/interpreter" "github.com/onflow/cadence/sema" @@ -53,6 +54,10 @@ func (v UFix64Value) Transfer(*Config, atree.Address, bool, atree.Storable) Valu return v } +func (UFix64Value) Negate() NumberValue { + panic(errors.NewUnreachableError()) +} + func (v UFix64Value) Add(other NumberValue) NumberValue { o, ok := other.(UFix64Value) if !ok { diff --git a/bbq/vm/vm.go b/bbq/vm/vm.go index 0df89f7ee..655c45ef6 100644 --- a/bbq/vm/vm.go +++ b/bbq/vm/vm.go @@ -397,6 +397,11 @@ func opMod(vm *VM) { vm.replaceTop(leftNumber.Mod(rightNumber)) } +func opNegate(vm *VM) { + value := vm.pop().(NumberValue) + vm.push(value.Negate()) +} + func opBitwiseOr(vm *VM) { left, right := vm.peekPop() leftNumber := left.(IntegerValue) @@ -908,6 +913,8 @@ func (vm *VM) run() { opDivide(vm) case opcode.InstructionMod: opMod(vm) + case opcode.InstructionNegate: + opNegate(vm) case opcode.InstructionBitwiseOr: opBitwiseOr(vm) case opcode.InstructionBitwiseXor: From 8beca10b74c7d4c47cfcc8468a5e5b10c5b60963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 26 Feb 2025 11:11:57 -0800 Subject: [PATCH 3/3] test printing of all instructions, ensure test for all instructions exists --- bbq/opcode/print_test.go | 79 ++++++++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/bbq/opcode/print_test.go b/bbq/opcode/print_test.go index 48ff3bf3a..7f2328757 100644 --- a/bbq/opcode/print_test.go +++ b/bbq/opcode/print_test.go @@ -95,9 +95,13 @@ func TestPrintInstruction(t *testing.T) { "SetLocal localIndex:258": {byte(SetLocal), 1, 2}, "GetGlobal globalIndex:258": {byte(GetGlobal), 1, 2}, "SetGlobal globalIndex:258": {byte(SetGlobal), 1, 2}, - "Jump target:258": {byte(Jump), 1, 2}, - "JumpIfFalse target:258": {byte(JumpIfFalse), 1, 2}, - "Transfer typeIndex:258": {byte(Transfer), 1, 2}, + + "Jump target:258": {byte(Jump), 1, 2}, + "JumpIfFalse target:258": {byte(JumpIfFalse), 1, 2}, + "JumpIfTrue target:258": {byte(JumpIfTrue), 1, 2}, + "JumpIfNil target:258": {byte(JumpIfNil), 1, 2}, + + "Transfer typeIndex:258": {byte(Transfer), 1, 2}, "New kind:CompositeKind(258) typeIndex:772": {byte(New), 1, 2, 3, 4}, @@ -116,23 +120,30 @@ func TestPrintInstruction(t *testing.T) { }, "NewRef typeIndex:258": {byte(NewRef), 1, 2}, + "Deref": {byte(Deref)}, + + "NewArray typeIndex:258 size:772 isResource:true": {byte(NewArray), 1, 2, 3, 4, 1}, + "NewDictionary typeIndex:258 size:772 isResource:true": {byte(NewDictionary), 1, 2, 3, 4, 1}, + + "Unknown": {byte(Unknown)}, + "Return": {byte(Return)}, + "ReturnValue": {byte(ReturnValue)}, + + "Add": {byte(Add)}, + "Subtract": {byte(Subtract)}, + "Multiply": {byte(Multiply)}, + "Divide": {byte(Divide)}, + "Mod": {byte(Mod)}, + "Negate": {byte(Negate)}, + + "Less": {byte(Less)}, + "Greater": {byte(Greater)}, + "LessOrEqual": {byte(LessOrEqual)}, + "GreaterOrEqual": {byte(GreaterOrEqual)}, + + "Equal": {byte(Equal)}, + "NotEqual": {byte(NotEqual)}, - "NewArray typeIndex:258 size:772 isResource:true": {byte(NewArray), 1, 2, 3, 4, 1}, - - "Unknown": {byte(Unknown)}, - "Return": {byte(Return)}, - "ReturnValue": {byte(ReturnValue)}, - "Add": {byte(Add)}, - "Subtract": {byte(Subtract)}, - "Multiply": {byte(Multiply)}, - "Divide": {byte(Divide)}, - "Mod": {byte(Mod)}, - "Less": {byte(Less)}, - "Greater": {byte(Greater)}, - "LessOrEqual": {byte(LessOrEqual)}, - "GreaterOrEqual": {byte(GreaterOrEqual)}, - "Equal": {byte(Equal)}, - "NotEqual": {byte(NotEqual)}, "Unwrap": {byte(Unwrap)}, "Destroy": {byte(Destroy)}, "True": {byte(True)}, @@ -144,8 +155,38 @@ func TestPrintInstruction(t *testing.T) { "GetIndex": {byte(GetIndex)}, "Drop": {byte(Drop)}, "Dup": {byte(Dup)}, + "Not": {byte(Not)}, + + "BitwiseOr": {byte(BitwiseOr)}, + "BitwiseAnd": {byte(BitwiseAnd)}, + "BitwiseXor": {byte(BitwiseXor)}, + "BitwiseLeftShift": {byte(BitwiseLeftShift)}, + "BitwiseRightShift": {byte(BitwiseRightShift)}, + + "Iterator": {byte(Iterator)}, + "IteratorHasNext": {byte(IteratorHasNext)}, + "IteratorNext": {byte(IteratorNext)}, + + "EmitEvent typeIndex:258": {byte(EmitEvent), 1, 2}, + } + + // Check if there is any opcode that is not tested + + tested := map[string]struct{}{} + for expected := range instructions { + name := strings.SplitN(expected, " ", 2)[0] + tested[name] = struct{}{} + } + + for opcode := range OpcodeMax { + name := opcode.String() + if !strings.HasPrefix(name, "Opcode(") { + assert.Contains(t, tested, name, "missing test for opcode %s", name) + } } + // Run tests + for expected, code := range instructions { t.Run(expected, func(t *testing.T) {