From 18b953c7be61ceb184bc19ddfe59cf71c47048d6 Mon Sep 17 00:00:00 2001 From: wlxwlxwlx <49864724+wlxwlxwlx@users.noreply.github.com> Date: Thu, 21 Dec 2023 02:13:23 +0800 Subject: [PATCH] increase size of jump operands (#438) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [OpJumpFalse, OpAndJump, OpOrJump, OpJump], these four instructions have been changed to use 4 bytes to avoid precision loss and panic when the number of instructions exceeds the maximum of 16 bits (65535) * update test cases * update test cases --------- Co-authored-by: 王录祥 --- compiler_test.go | 54 +++++++++++++++++++++++------------------------ instructions.go | 6 ++++++ parser/opcodes.go | 10 +++++---- vm.go | 14 ++++++------ 4 files changed, 46 insertions(+), 38 deletions(-) diff --git a/compiler_test.go b/compiler_test.go index 28cc901b..5caf2b03 100644 --- a/compiler_test.go +++ b/compiler_test.go @@ -204,11 +204,11 @@ func TestCompiler_Compile(t *testing.T) { expectCompile(t, `if true { 10 }; 3333`, bytecode( concatInsts( - tengo.MakeInstruction(parser.OpTrue), // 0000 - tengo.MakeInstruction(parser.OpJumpFalsy, 8), // 0001 - tengo.MakeInstruction(parser.OpConstant, 0), // 0004 - tengo.MakeInstruction(parser.OpPop), // 0007 - tengo.MakeInstruction(parser.OpConstant, 1), // 0008 + tengo.MakeInstruction(parser.OpTrue), // 0000 + tengo.MakeInstruction(parser.OpJumpFalsy, 10), // 0001 + tengo.MakeInstruction(parser.OpConstant, 0), // 0004 + tengo.MakeInstruction(parser.OpPop), // 0007 + tengo.MakeInstruction(parser.OpConstant, 1), // 0008 tengo.MakeInstruction(parser.OpPop), tengo.MakeInstruction(parser.OpSuspend)), // 0011 objectsArray( @@ -219,10 +219,10 @@ func TestCompiler_Compile(t *testing.T) { bytecode( concatInsts( tengo.MakeInstruction(parser.OpTrue), // 0000 - tengo.MakeInstruction(parser.OpJumpFalsy, 11), // 0001 + tengo.MakeInstruction(parser.OpJumpFalsy, 15), // 0001 tengo.MakeInstruction(parser.OpConstant, 0), // 0004 tengo.MakeInstruction(parser.OpPop), // 0007 - tengo.MakeInstruction(parser.OpJump, 15), // 0008 + tengo.MakeInstruction(parser.OpJump, 19), // 0008 tengo.MakeInstruction(parser.OpConstant, 1), // 0011 tengo.MakeInstruction(parser.OpPop), // 0014 tengo.MakeInstruction(parser.OpConstant, 2), // 0015 @@ -577,12 +577,12 @@ func TestCompiler_Compile(t *testing.T) { intObject(1), intObject(2), compiledFunction(0, 0, - tengo.MakeInstruction(parser.OpTrue), // 0000 - tengo.MakeInstruction(parser.OpJumpFalsy, 9), // 0001 - tengo.MakeInstruction(parser.OpConstant, 0), // 0004 - tengo.MakeInstruction(parser.OpReturn, 1), // 0007 - tengo.MakeInstruction(parser.OpConstant, 1), // 0009 - tengo.MakeInstruction(parser.OpReturn, 1))))) // 0012 + tengo.MakeInstruction(parser.OpTrue), // 0000 + tengo.MakeInstruction(parser.OpJumpFalsy, 11), // 0001 + tengo.MakeInstruction(parser.OpConstant, 0), // 0004 + tengo.MakeInstruction(parser.OpReturn, 1), // 0007 + tengo.MakeInstruction(parser.OpConstant, 1), // 0009 + tengo.MakeInstruction(parser.OpReturn, 1))))) // 0012 expectCompile(t, `func() { 1; if(true) { 2 } else { 3 }; 4 }`, bytecode( @@ -599,10 +599,10 @@ func TestCompiler_Compile(t *testing.T) { tengo.MakeInstruction(parser.OpConstant, 0), // 0000 tengo.MakeInstruction(parser.OpPop), // 0003 tengo.MakeInstruction(parser.OpTrue), // 0004 - tengo.MakeInstruction(parser.OpJumpFalsy, 15), // 0005 + tengo.MakeInstruction(parser.OpJumpFalsy, 19), // 0005 tengo.MakeInstruction(parser.OpConstant, 1), // 0008 tengo.MakeInstruction(parser.OpPop), // 0011 - tengo.MakeInstruction(parser.OpJump, 19), // 0012 + tengo.MakeInstruction(parser.OpJump, 23), // 0012 tengo.MakeInstruction(parser.OpConstant, 2), // 0015 tengo.MakeInstruction(parser.OpPop), // 0018 tengo.MakeInstruction(parser.OpConstant, 3), // 0019 @@ -932,7 +932,7 @@ func() { tengo.MakeInstruction(parser.OpGetGlobal, 0), tengo.MakeInstruction(parser.OpConstant, 1), tengo.MakeInstruction(parser.OpBinaryOp, 38), - tengo.MakeInstruction(parser.OpJumpFalsy, 31), + tengo.MakeInstruction(parser.OpJumpFalsy, 35), tengo.MakeInstruction(parser.OpGetGlobal, 0), tengo.MakeInstruction(parser.OpConstant, 2), tengo.MakeInstruction(parser.OpBinaryOp, 11), @@ -954,7 +954,7 @@ func() { tengo.MakeInstruction(parser.OpSetGlobal, 1), tengo.MakeInstruction(parser.OpGetGlobal, 1), tengo.MakeInstruction(parser.OpIteratorNext), - tengo.MakeInstruction(parser.OpJumpFalsy, 37), + tengo.MakeInstruction(parser.OpJumpFalsy, 41), tengo.MakeInstruction(parser.OpGetGlobal, 1), tengo.MakeInstruction(parser.OpIteratorKey), tengo.MakeInstruction(parser.OpSetGlobal, 2), @@ -973,11 +973,11 @@ func() { tengo.MakeInstruction(parser.OpGetGlobal, 0), tengo.MakeInstruction(parser.OpConstant, 0), tengo.MakeInstruction(parser.OpEqual), - tengo.MakeInstruction(parser.OpAndJump, 23), + tengo.MakeInstruction(parser.OpAndJump, 25), tengo.MakeInstruction(parser.OpGetGlobal, 0), tengo.MakeInstruction(parser.OpConstant, 1), tengo.MakeInstruction(parser.OpNotEqual), - tengo.MakeInstruction(parser.OpOrJump, 34), + tengo.MakeInstruction(parser.OpOrJump, 38), tengo.MakeInstruction(parser.OpGetGlobal, 0), tengo.MakeInstruction(parser.OpConstant, 1), tengo.MakeInstruction(parser.OpBinaryOp, 38), @@ -1089,7 +1089,7 @@ func() { intObject(4), compiledFunction(0, 0, tengo.MakeInstruction(parser.OpTrue), - tengo.MakeInstruction(parser.OpJumpFalsy, 9), + tengo.MakeInstruction(parser.OpJumpFalsy, 11), tengo.MakeInstruction(parser.OpConstant, 0), tengo.MakeInstruction(parser.OpReturn, 1), tengo.MakeInstruction(parser.OpConstant, 1), @@ -1123,7 +1123,7 @@ func() { tengo.MakeInstruction(parser.OpGetLocal, 0), tengo.MakeInstruction(parser.OpConstant, 1), tengo.MakeInstruction(parser.OpEqual), - tengo.MakeInstruction(parser.OpJumpFalsy, 19), + tengo.MakeInstruction(parser.OpJumpFalsy, 21), tengo.MakeInstruction(parser.OpConstant, 2), tengo.MakeInstruction(parser.OpReturn, 1), tengo.MakeInstruction(parser.OpConstant, 1), @@ -1156,7 +1156,7 @@ func() { intObject(4), compiledFunction(0, 0, tengo.MakeInstruction(parser.OpTrue), - tengo.MakeInstruction(parser.OpJumpFalsy, 9), + tengo.MakeInstruction(parser.OpJumpFalsy, 11), tengo.MakeInstruction(parser.OpConstant, 0), tengo.MakeInstruction(parser.OpReturn, 1), tengo.MakeInstruction(parser.OpConstant, 1), @@ -1180,7 +1180,7 @@ func() { intObject(123), compiledFunction(0, 0, tengo.MakeInstruction(parser.OpTrue), - tengo.MakeInstruction(parser.OpJumpFalsy, 6), + tengo.MakeInstruction(parser.OpJumpFalsy, 8), tengo.MakeInstruction(parser.OpReturn, 0), tengo.MakeInstruction(parser.OpReturn, 0), tengo.MakeInstruction(parser.OpConstant, 0), @@ -1200,12 +1200,12 @@ if a := 1; a { tengo.MakeInstruction(parser.OpConstant, 0), tengo.MakeInstruction(parser.OpSetGlobal, 0), tengo.MakeInstruction(parser.OpGetGlobal, 0), - tengo.MakeInstruction(parser.OpJumpFalsy, 27), + tengo.MakeInstruction(parser.OpJumpFalsy, 31), tengo.MakeInstruction(parser.OpConstant, 1), tengo.MakeInstruction(parser.OpSetGlobal, 0), tengo.MakeInstruction(parser.OpGetGlobal, 0), tengo.MakeInstruction(parser.OpSetGlobal, 1), - tengo.MakeInstruction(parser.OpJump, 39), + tengo.MakeInstruction(parser.OpJump, 43), tengo.MakeInstruction(parser.OpConstant, 2), tengo.MakeInstruction(parser.OpSetGlobal, 0), tengo.MakeInstruction(parser.OpGetGlobal, 0), @@ -1238,12 +1238,12 @@ func() { tengo.MakeInstruction(parser.OpConstant, 0), tengo.MakeInstruction(parser.OpDefineLocal, 0), tengo.MakeInstruction(parser.OpGetLocal, 0), - tengo.MakeInstruction(parser.OpJumpFalsy, 22), + tengo.MakeInstruction(parser.OpJumpFalsy, 26), tengo.MakeInstruction(parser.OpConstant, 1), tengo.MakeInstruction(parser.OpSetLocal, 0), tengo.MakeInstruction(parser.OpGetLocal, 0), tengo.MakeInstruction(parser.OpDefineLocal, 1), - tengo.MakeInstruction(parser.OpJump, 31), + tengo.MakeInstruction(parser.OpJump, 35), tengo.MakeInstruction(parser.OpConstant, 2), tengo.MakeInstruction(parser.OpSetLocal, 0), tengo.MakeInstruction(parser.OpGetLocal, 0), diff --git a/instructions.go b/instructions.go index eb1fbf27..27f7af39 100644 --- a/instructions.go +++ b/instructions.go @@ -28,6 +28,12 @@ func MakeInstruction(opcode parser.Opcode, operands ...int) []byte { n := uint16(o) instruction[offset] = byte(n >> 8) instruction[offset+1] = byte(n) + case 4: + n := uint32(o) + instruction[offset] = byte(n >> 24) + instruction[offset+1] = byte(n >> 16) + instruction[offset+2] = byte(n >> 8) + instruction[offset+3] = byte(n) } offset += width } diff --git a/parser/opcodes.go b/parser/opcodes.go index d97f4896..c1e94c1f 100644 --- a/parser/opcodes.go +++ b/parser/opcodes.go @@ -106,10 +106,10 @@ var OpcodeOperands = [...][]int{ OpNotEqual: {}, OpMinus: {}, OpLNot: {}, - OpJumpFalsy: {2}, - OpAndJump: {2}, - OpOrJump: {2}, - OpJump: {2}, + OpJumpFalsy: {4}, + OpAndJump: {4}, + OpOrJump: {4}, + OpJump: {4}, OpNull: {}, OpGetGlobal: {2}, OpSetGlobal: {2}, @@ -149,6 +149,8 @@ func ReadOperands(numOperands []int, ins []byte) (operands []int, offset int) { operands = append(operands, int(ins[offset])) case 2: operands = append(operands, int(ins[offset+1])|int(ins[offset])<<8) + case 4: + operands = append(operands, int(ins[offset+3])|int(ins[offset+2])<<8|int(ins[offset+1])<<16|int(ins[offset])<<24) } offset += width } diff --git a/vm.go b/vm.go index 64bd23bc..2f8f6fc5 100644 --- a/vm.go +++ b/vm.go @@ -218,30 +218,30 @@ func (v *VM) run() { return } case parser.OpJumpFalsy: - v.ip += 2 + v.ip += 4 v.sp-- if v.stack[v.sp].IsFalsy() { - pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 + pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 | int(v.curInsts[v.ip-2])<<16 | int(v.curInsts[v.ip-3])<<24 v.ip = pos - 1 } case parser.OpAndJump: - v.ip += 2 + v.ip += 4 if v.stack[v.sp-1].IsFalsy() { - pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 + pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 | int(v.curInsts[v.ip-2])<<16 | int(v.curInsts[v.ip-3])<<24 v.ip = pos - 1 } else { v.sp-- } case parser.OpOrJump: - v.ip += 2 + v.ip += 4 if v.stack[v.sp-1].IsFalsy() { v.sp-- } else { - pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 + pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 | int(v.curInsts[v.ip-2])<<16 | int(v.curInsts[v.ip-3])<<24 v.ip = pos - 1 } case parser.OpJump: - pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 + pos := int(v.curInsts[v.ip+4]) | int(v.curInsts[v.ip+3])<<8 | int(v.curInsts[v.ip+2])<<16 | int(v.curInsts[v.ip+1])<<24 v.ip = pos - 1 case parser.OpSetGlobal: v.ip += 2