diff --git a/compiler/bytecode_test.go b/compiler/bytecode_test.go index a8e6d15..ad6ce0b 100644 --- a/compiler/bytecode_test.go +++ b/compiler/bytecode_test.go @@ -102,25 +102,25 @@ func TestBytecode(t *testing.T) { compiler.MakeInstruction(compiler.OpSetLocal, 0), compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpGetFree, 0), - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpGetFree, 1), - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpGetLocal, 0), - compiler.MakeInstruction(compiler.OpAdd), - compiler.MakeInstruction(compiler.OpReturnValue)), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), + compiler.MakeInstruction(compiler.OpReturn, 1)), compiledFunction(1, 0, compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpSetLocal, 0), compiler.MakeInstruction(compiler.OpGetFree, 0), compiler.MakeInstruction(compiler.OpGetLocal, 0), compiler.MakeInstruction(compiler.OpClosure, 4, 2), - compiler.MakeInstruction(compiler.OpReturnValue)), + compiler.MakeInstruction(compiler.OpReturn, 1)), compiledFunction(1, 0, compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpSetLocal, 0), compiler.MakeInstruction(compiler.OpGetLocal, 0), compiler.MakeInstruction(compiler.OpClosure, 5, 1), - compiler.MakeInstruction(compiler.OpReturnValue))), + compiler.MakeInstruction(compiler.OpReturn, 1))), fileSet(srcfile{name: "file1", size: 100}, srcfile{name: "file2", size: 200}))) } @@ -240,13 +240,13 @@ func TestBytecode_CountObjects(t *testing.T) { &objects.Int{Value: 88}, compiledFunction(1, 0, compiler.MakeInstruction(compiler.OpConstant, 3), - compiler.MakeInstruction(compiler.OpReturnValue)), + compiler.MakeInstruction(compiler.OpReturn, 1)), compiledFunction(1, 0, compiler.MakeInstruction(compiler.OpConstant, 2), - compiler.MakeInstruction(compiler.OpReturnValue)), + compiler.MakeInstruction(compiler.OpReturn, 1)), compiledFunction(1, 0, compiler.MakeInstruction(compiler.OpConstant, 1), - compiler.MakeInstruction(compiler.OpReturnValue)))) + compiler.MakeInstruction(compiler.OpReturn, 1)))) assert.Equal(t, 7, b.CountObjects()) } diff --git a/compiler/compiler.go b/compiler/compiler.go index 77aaa54..b00fef6 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -119,7 +119,7 @@ func (c *Compiler) Compile(node ast.Node) error { return err } - c.emit(node, OpGreaterThan) + c.emit(node, OpBinaryOp, int(token.Greater)) return nil } else if node.Token == token.LessEq { @@ -130,7 +130,7 @@ func (c *Compiler) Compile(node ast.Node) error { return err } - c.emit(node, OpGreaterThanEqual) + c.emit(node, OpBinaryOp, int(token.GreaterEq)) return nil } @@ -144,35 +144,35 @@ func (c *Compiler) Compile(node ast.Node) error { switch node.Token { case token.Add: - c.emit(node, OpAdd) + c.emit(node, OpBinaryOp, int(token.Add)) case token.Sub: - c.emit(node, OpSub) + c.emit(node, OpBinaryOp, int(token.Sub)) case token.Mul: - c.emit(node, OpMul) + c.emit(node, OpBinaryOp, int(token.Mul)) case token.Quo: - c.emit(node, OpDiv) + c.emit(node, OpBinaryOp, int(token.Quo)) case token.Rem: - c.emit(node, OpRem) + c.emit(node, OpBinaryOp, int(token.Rem)) case token.Greater: - c.emit(node, OpGreaterThan) + c.emit(node, OpBinaryOp, int(token.Greater)) case token.GreaterEq: - c.emit(node, OpGreaterThanEqual) + c.emit(node, OpBinaryOp, int(token.GreaterEq)) case token.Equal: c.emit(node, OpEqual) case token.NotEqual: c.emit(node, OpNotEqual) case token.And: - c.emit(node, OpBAnd) + c.emit(node, OpBinaryOp, int(token.And)) case token.Or: - c.emit(node, OpBOr) + c.emit(node, OpBinaryOp, int(token.Or)) case token.Xor: - c.emit(node, OpBXor) + c.emit(node, OpBinaryOp, int(token.Xor)) case token.AndNot: - c.emit(node, OpBAndNot) + c.emit(node, OpBinaryOp, int(token.AndNot)) case token.Shl: - c.emit(node, OpBShiftLeft) + c.emit(node, OpBinaryOp, int(token.Shl)) case token.Shr: - c.emit(node, OpBShiftRight) + c.emit(node, OpBinaryOp, int(token.Shr)) default: return c.errorf(node, "invalid binary operator: %s", node.Token.String()) } @@ -405,8 +405,8 @@ func (c *Compiler) Compile(node ast.Node) error { } // add OpReturn if function returns nothing - if !c.lastInstructionIs(OpReturnValue) && !c.lastInstructionIs(OpReturn) { - c.emit(node, OpReturn) + if !c.lastInstructionIs(OpReturn) { + c.emit(node, OpReturn, 0) } freeSymbols := c.symbolTable.FreeSymbols() @@ -486,13 +486,13 @@ func (c *Compiler) Compile(node ast.Node) error { } if node.Result == nil { - c.emit(node, OpReturn) + c.emit(node, OpReturn, 0) } else { if err := c.Compile(node.Result); err != nil { return err } - c.emit(node, OpReturnValue) + c.emit(node, OpReturn, 1) } case *ast.CallExpr: @@ -578,7 +578,7 @@ func (c *Compiler) Compile(node ast.Node) error { } c.emit(node, OpImmutable) - c.emit(node, OpReturnValue) + c.emit(node, OpReturn, 1) case *ast.ErrorExpr: if err := c.Compile(node.Expr); err != nil { diff --git a/compiler/compiler_assign.go b/compiler/compiler_assign.go index 0e086c8..59296f6 100644 --- a/compiler/compiler_assign.go +++ b/compiler/compiler_assign.go @@ -51,27 +51,27 @@ func (c *Compiler) compileAssign(node ast.Node, lhs, rhs []ast.Expr, op token.To switch op { case token.AddAssign: - c.emit(node, OpAdd) + c.emit(node, OpBinaryOp, int(token.Add)) case token.SubAssign: - c.emit(node, OpSub) + c.emit(node, OpBinaryOp, int(token.Sub)) case token.MulAssign: - c.emit(node, OpMul) + c.emit(node, OpBinaryOp, int(token.Mul)) case token.QuoAssign: - c.emit(node, OpDiv) + c.emit(node, OpBinaryOp, int(token.Quo)) case token.RemAssign: - c.emit(node, OpRem) + c.emit(node, OpBinaryOp, int(token.Rem)) case token.AndAssign: - c.emit(node, OpBAnd) + c.emit(node, OpBinaryOp, int(token.And)) case token.OrAssign: - c.emit(node, OpBOr) + c.emit(node, OpBinaryOp, int(token.Or)) case token.AndNotAssign: - c.emit(node, OpBAndNot) + c.emit(node, OpBinaryOp, int(token.AndNot)) case token.XorAssign: - c.emit(node, OpBXor) + c.emit(node, OpBinaryOp, int(token.Xor)) case token.ShlAssign: - c.emit(node, OpBShiftLeft) + c.emit(node, OpBinaryOp, int(token.Shl)) case token.ShrAssign: - c.emit(node, OpBShiftRight) + c.emit(node, OpBinaryOp, int(token.Shr)) } // compile selector expressions (right to left) diff --git a/compiler/compiler_module.go b/compiler/compiler_module.go index 48c2ef2..55590e0 100644 --- a/compiler/compiler_module.go +++ b/compiler/compiler_module.go @@ -50,7 +50,7 @@ func (c *Compiler) compileModule(node ast.Node, moduleName, modulePath string, s } // add OpReturn (== export undefined) if export is missing - if !moduleCompiler.lastInstructionIs(OpReturnValue) { + if !moduleCompiler.lastInstructionIs(OpReturn) { moduleCompiler.emit(nil, OpReturn) } diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index d4125f9..45c6606 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -18,7 +18,7 @@ func TestCompiler_Compile(t *testing.T) { concat( compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 1), - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpPop)), objectsArray( intObject(1), @@ -40,7 +40,7 @@ func TestCompiler_Compile(t *testing.T) { concat( compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 1), - compiler.MakeInstruction(compiler.OpSub), + compiler.MakeInstruction(compiler.OpBinaryOp, 12), compiler.MakeInstruction(compiler.OpPop)), objectsArray( intObject(1), @@ -51,7 +51,7 @@ func TestCompiler_Compile(t *testing.T) { concat( compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 1), - compiler.MakeInstruction(compiler.OpMul), + compiler.MakeInstruction(compiler.OpBinaryOp, 13), compiler.MakeInstruction(compiler.OpPop)), objectsArray( intObject(1), @@ -62,7 +62,7 @@ func TestCompiler_Compile(t *testing.T) { concat( compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 1), - compiler.MakeInstruction(compiler.OpDiv), + compiler.MakeInstruction(compiler.OpBinaryOp, 14), compiler.MakeInstruction(compiler.OpPop)), objectsArray( intObject(2), @@ -87,7 +87,7 @@ func TestCompiler_Compile(t *testing.T) { concat( compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 1), - compiler.MakeInstruction(compiler.OpGreaterThan), + compiler.MakeInstruction(compiler.OpBinaryOp, 39), compiler.MakeInstruction(compiler.OpPop)), objectsArray( intObject(1), @@ -98,7 +98,29 @@ func TestCompiler_Compile(t *testing.T) { concat( compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 1), - compiler.MakeInstruction(compiler.OpGreaterThan), + compiler.MakeInstruction(compiler.OpBinaryOp, 39), + compiler.MakeInstruction(compiler.OpPop)), + objectsArray( + intObject(2), + intObject(1)))) + + expect(t, `1 >= 2`, + bytecode( + concat( + compiler.MakeInstruction(compiler.OpConstant, 0), + compiler.MakeInstruction(compiler.OpConstant, 1), + compiler.MakeInstruction(compiler.OpBinaryOp, 44), + compiler.MakeInstruction(compiler.OpPop)), + objectsArray( + intObject(1), + intObject(2)))) + + expect(t, `1 <= 2`, + bytecode( + concat( + compiler.MakeInstruction(compiler.OpConstant, 0), + compiler.MakeInstruction(compiler.OpConstant, 1), + compiler.MakeInstruction(compiler.OpBinaryOp, 44), compiler.MakeInstruction(compiler.OpPop)), objectsArray( intObject(2), @@ -204,7 +226,7 @@ func TestCompiler_Compile(t *testing.T) { concat( compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 1), - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpPop)), objectsArray( stringObject("ka"), @@ -219,7 +241,7 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpSetGlobal, 1), compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpGetGlobal, 1), - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpSetGlobal, 0)), objectsArray( intObject(1), @@ -234,7 +256,7 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpSetGlobal, 1), compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpGetGlobal, 1), - compiler.MakeInstruction(compiler.OpDiv), + compiler.MakeInstruction(compiler.OpBinaryOp, 14), compiler.MakeInstruction(compiler.OpSetGlobal, 0)), objectsArray( intObject(1), @@ -265,13 +287,13 @@ func TestCompiler_Compile(t *testing.T) { concat( compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 1), - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpConstant, 3), - compiler.MakeInstruction(compiler.OpSub), + compiler.MakeInstruction(compiler.OpBinaryOp, 12), compiler.MakeInstruction(compiler.OpConstant, 4), compiler.MakeInstruction(compiler.OpConstant, 5), - compiler.MakeInstruction(compiler.OpMul), + compiler.MakeInstruction(compiler.OpBinaryOp, 13), compiler.MakeInstruction(compiler.OpArray, 3), compiler.MakeInstruction(compiler.OpPop)), objectsArray( @@ -314,11 +336,11 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 2), - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpConstant, 3), compiler.MakeInstruction(compiler.OpConstant, 4), compiler.MakeInstruction(compiler.OpConstant, 5), - compiler.MakeInstruction(compiler.OpMul), + compiler.MakeInstruction(compiler.OpBinaryOp, 13), compiler.MakeInstruction(compiler.OpMap, 4), compiler.MakeInstruction(compiler.OpPop)), objectsArray( @@ -338,7 +360,7 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpArray, 3), compiler.MakeInstruction(compiler.OpConstant, 3), compiler.MakeInstruction(compiler.OpConstant, 4), - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpIndex), compiler.MakeInstruction(compiler.OpPop)), objectsArray( @@ -356,7 +378,7 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpMap, 2), compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpConstant, 3), - compiler.MakeInstruction(compiler.OpSub), + compiler.MakeInstruction(compiler.OpBinaryOp, 12), compiler.MakeInstruction(compiler.OpIndex), compiler.MakeInstruction(compiler.OpPop)), objectsArray( @@ -444,8 +466,8 @@ func TestCompiler_Compile(t *testing.T) { compiledFunction(0, 0, compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 1), - compiler.MakeInstruction(compiler.OpAdd), - compiler.MakeInstruction(compiler.OpReturnValue))))) + compiler.MakeInstruction(compiler.OpBinaryOp, 11), + compiler.MakeInstruction(compiler.OpReturn, 1))))) expect(t, `func() { 5 + 10 }`, bytecode( @@ -458,9 +480,9 @@ func TestCompiler_Compile(t *testing.T) { compiledFunction(0, 0, compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 1), - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpPop), - compiler.MakeInstruction(compiler.OpReturn))))) + compiler.MakeInstruction(compiler.OpReturn, 0))))) expect(t, `func() { 1; 2 }`, bytecode( @@ -475,7 +497,7 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpPop), compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpPop), - compiler.MakeInstruction(compiler.OpReturn))))) + compiler.MakeInstruction(compiler.OpReturn, 0))))) expect(t, `func() { 1; return 2 }`, bytecode( @@ -489,7 +511,7 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpPop), compiler.MakeInstruction(compiler.OpConstant, 1), - compiler.MakeInstruction(compiler.OpReturnValue))))) + compiler.MakeInstruction(compiler.OpReturn, 1))))) expect(t, `func() { if(true) { return 1 } else { return 2 } }`, bytecode( @@ -500,13 +522,13 @@ func TestCompiler_Compile(t *testing.T) { intObject(1), intObject(2), compiledFunction(0, 0, - compiler.MakeInstruction(compiler.OpTrue), // 0000 - compiler.MakeInstruction(compiler.OpJumpFalsy, 11), // 0001 - compiler.MakeInstruction(compiler.OpConstant, 0), // 0004 - compiler.MakeInstruction(compiler.OpReturnValue), // 0007 - compiler.MakeInstruction(compiler.OpJump, 15), // 0008 - compiler.MakeInstruction(compiler.OpConstant, 1), // 0011 - compiler.MakeInstruction(compiler.OpReturnValue))))) // 0014 + compiler.MakeInstruction(compiler.OpTrue), // 0000 + compiler.MakeInstruction(compiler.OpJumpFalsy, 12), // 0001 + compiler.MakeInstruction(compiler.OpConstant, 0), // 0004 + compiler.MakeInstruction(compiler.OpReturn, 1), // 0007 + compiler.MakeInstruction(compiler.OpJump, 17), // 0008 + compiler.MakeInstruction(compiler.OpConstant, 1), // 0011 + compiler.MakeInstruction(compiler.OpReturn, 1))))) // 0014 expect(t, `func() { 1; if(true) { 2 } else { 3 }; 4 }`, bytecode( @@ -530,7 +552,7 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpPop), // 0018 compiler.MakeInstruction(compiler.OpConstant, 3), // 0019 compiler.MakeInstruction(compiler.OpPop), // 0022 - compiler.MakeInstruction(compiler.OpReturn))))) // 0023 + compiler.MakeInstruction(compiler.OpReturn, 0))))) // 0023 expect(t, `func() { }`, bytecode( @@ -538,7 +560,7 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpPop)), objectsArray( - compiledFunction(0, 0, compiler.MakeInstruction(compiler.OpReturn))))) + compiledFunction(0, 0, compiler.MakeInstruction(compiler.OpReturn, 0))))) expect(t, `func() { 24 }()`, bytecode( @@ -551,7 +573,7 @@ func TestCompiler_Compile(t *testing.T) { compiledFunction(0, 0, compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpPop), - compiler.MakeInstruction(compiler.OpReturn))))) + compiler.MakeInstruction(compiler.OpReturn, 0))))) expect(t, `func() { return 24 }()`, bytecode( @@ -563,7 +585,7 @@ func TestCompiler_Compile(t *testing.T) { intObject(24), compiledFunction(0, 0, compiler.MakeInstruction(compiler.OpConstant, 0), - compiler.MakeInstruction(compiler.OpReturnValue))))) + compiler.MakeInstruction(compiler.OpReturn, 1))))) expect(t, `noArg := func() { 24 }; noArg();`, bytecode( @@ -578,7 +600,7 @@ func TestCompiler_Compile(t *testing.T) { compiledFunction(0, 0, compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpPop), - compiler.MakeInstruction(compiler.OpReturn))))) + compiler.MakeInstruction(compiler.OpReturn, 0))))) expect(t, `noArg := func() { return 24 }; noArg();`, bytecode( @@ -592,7 +614,7 @@ func TestCompiler_Compile(t *testing.T) { intObject(24), compiledFunction(0, 0, compiler.MakeInstruction(compiler.OpConstant, 0), - compiler.MakeInstruction(compiler.OpReturnValue))))) + compiler.MakeInstruction(compiler.OpReturn, 1))))) expect(t, `n := 55; func() { n };`, bytecode( @@ -606,7 +628,7 @@ func TestCompiler_Compile(t *testing.T) { compiledFunction(0, 0, compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpPop), - compiler.MakeInstruction(compiler.OpReturn))))) + compiler.MakeInstruction(compiler.OpReturn, 0))))) expect(t, `func() { n := 55; return n }`, bytecode( @@ -619,7 +641,7 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpDefineLocal, 0), compiler.MakeInstruction(compiler.OpGetLocal, 0), - compiler.MakeInstruction(compiler.OpReturnValue))))) + compiler.MakeInstruction(compiler.OpReturn, 1))))) expect(t, `func() { a := 55; b := 77; return a + b }`, bytecode( @@ -636,8 +658,8 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpDefineLocal, 1), compiler.MakeInstruction(compiler.OpGetLocal, 0), compiler.MakeInstruction(compiler.OpGetLocal, 1), - compiler.MakeInstruction(compiler.OpAdd), - compiler.MakeInstruction(compiler.OpReturnValue))))) + compiler.MakeInstruction(compiler.OpBinaryOp, 11), + compiler.MakeInstruction(compiler.OpReturn, 1))))) expect(t, `f1 := func(a) { return a }; f1(24);`, bytecode( @@ -651,7 +673,7 @@ func TestCompiler_Compile(t *testing.T) { objectsArray( compiledFunction(1, 1, compiler.MakeInstruction(compiler.OpGetLocal, 0), - compiler.MakeInstruction(compiler.OpReturnValue)), + compiler.MakeInstruction(compiler.OpReturn, 1)), intObject(24)))) expect(t, `f1 := func(a, b, c) { a; b; return c; }; f1(24, 25, 26);`, @@ -672,7 +694,7 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpGetLocal, 1), compiler.MakeInstruction(compiler.OpPop), compiler.MakeInstruction(compiler.OpGetLocal, 2), - compiler.MakeInstruction(compiler.OpReturnValue)), + compiler.MakeInstruction(compiler.OpReturn, 1)), intObject(24), intObject(25), intObject(26)))) @@ -691,7 +713,7 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpSetLocal, 0), compiler.MakeInstruction(compiler.OpGetLocal, 0), - compiler.MakeInstruction(compiler.OpReturnValue))))) + compiler.MakeInstruction(compiler.OpReturn, 1))))) expect(t, `len([]);`, bytecode( concat( @@ -711,7 +733,7 @@ func TestCompiler_Compile(t *testing.T) { compiler.MakeInstruction(compiler.OpGetBuiltin, 0), compiler.MakeInstruction(compiler.OpArray, 0), compiler.MakeInstruction(compiler.OpCall, 1), - compiler.MakeInstruction(compiler.OpReturnValue))))) + compiler.MakeInstruction(compiler.OpReturn, 1))))) expect(t, `func(a) { func(b) { return a + b } }`, bytecode( @@ -722,13 +744,13 @@ func TestCompiler_Compile(t *testing.T) { compiledFunction(1, 1, compiler.MakeInstruction(compiler.OpGetFree, 0), compiler.MakeInstruction(compiler.OpGetLocal, 0), - compiler.MakeInstruction(compiler.OpAdd), - compiler.MakeInstruction(compiler.OpReturnValue)), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), + compiler.MakeInstruction(compiler.OpReturn, 1)), compiledFunction(1, 1, compiler.MakeInstruction(compiler.OpGetLocalPtr, 0), compiler.MakeInstruction(compiler.OpClosure, 0, 1), compiler.MakeInstruction(compiler.OpPop), - compiler.MakeInstruction(compiler.OpReturn))))) + compiler.MakeInstruction(compiler.OpReturn, 0))))) expect(t, ` func(a) { @@ -746,19 +768,19 @@ func(a) { compiledFunction(1, 1, compiler.MakeInstruction(compiler.OpGetFree, 0), compiler.MakeInstruction(compiler.OpGetFree, 1), - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpGetLocal, 0), - compiler.MakeInstruction(compiler.OpAdd), - compiler.MakeInstruction(compiler.OpReturnValue)), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), + compiler.MakeInstruction(compiler.OpReturn, 1)), compiledFunction(1, 1, compiler.MakeInstruction(compiler.OpGetFreePtr, 0), compiler.MakeInstruction(compiler.OpGetLocalPtr, 0), compiler.MakeInstruction(compiler.OpClosure, 0, 2), - compiler.MakeInstruction(compiler.OpReturnValue)), + compiler.MakeInstruction(compiler.OpReturn, 1)), compiledFunction(1, 1, compiler.MakeInstruction(compiler.OpGetLocalPtr, 0), compiler.MakeInstruction(compiler.OpClosure, 1, 1), - compiler.MakeInstruction(compiler.OpReturnValue))))) + compiler.MakeInstruction(compiler.OpReturn, 1))))) expect(t, ` g := 55; @@ -792,25 +814,25 @@ func() { compiler.MakeInstruction(compiler.OpDefineLocal, 0), compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpGetFree, 0), - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpGetFree, 1), - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpGetLocal, 0), - compiler.MakeInstruction(compiler.OpAdd), - compiler.MakeInstruction(compiler.OpReturnValue)), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), + compiler.MakeInstruction(compiler.OpReturn, 1)), compiledFunction(1, 0, compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpDefineLocal, 0), compiler.MakeInstruction(compiler.OpGetFreePtr, 0), compiler.MakeInstruction(compiler.OpGetLocalPtr, 0), compiler.MakeInstruction(compiler.OpClosure, 4, 2), - compiler.MakeInstruction(compiler.OpReturnValue)), + compiler.MakeInstruction(compiler.OpReturn, 1)), compiledFunction(1, 0, compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpDefineLocal, 0), compiler.MakeInstruction(compiler.OpGetLocalPtr, 0), compiler.MakeInstruction(compiler.OpClosure, 5, 1), - compiler.MakeInstruction(compiler.OpReturnValue))))) + compiler.MakeInstruction(compiler.OpReturn, 1))))) expect(t, `for i:=0; i<10; i++ {}`, bytecode( @@ -819,11 +841,11 @@ func() { compiler.MakeInstruction(compiler.OpSetGlobal, 0), compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpGetGlobal, 0), - compiler.MakeInstruction(compiler.OpGreaterThan), - compiler.MakeInstruction(compiler.OpJumpFalsy, 29), + compiler.MakeInstruction(compiler.OpBinaryOp, 39), + compiler.MakeInstruction(compiler.OpJumpFalsy, 31), compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpConstant, 2), - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpSetGlobal, 0), compiler.MakeInstruction(compiler.OpJump, 6)), objectsArray( @@ -863,10 +885,10 @@ func() { compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpNotEqual), - compiler.MakeInstruction(compiler.OpOrJump, 33), + compiler.MakeInstruction(compiler.OpOrJump, 34), compiler.MakeInstruction(compiler.OpConstant, 3), compiler.MakeInstruction(compiler.OpGetGlobal, 0), - compiler.MakeInstruction(compiler.OpGreaterThan), + compiler.MakeInstruction(compiler.OpBinaryOp, 39), compiler.MakeInstruction(compiler.OpPop)), objectsArray( intObject(0), diff --git a/compiler/instructions_test.go b/compiler/instructions_test.go index 01d4331..9652c49 100644 --- a/compiler/instructions_test.go +++ b/compiler/instructions_test.go @@ -21,25 +21,25 @@ func TestInstructions_String(t *testing.T) { assertInstructionString(t, [][]byte{ - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpConstant, 65535), }, - `0000 ADD -0001 CONST 2 -0004 CONST 65535`) + `0000 BINARYOP 11 +0002 CONST 2 +0005 CONST 65535`) assertInstructionString(t, [][]byte{ - compiler.MakeInstruction(compiler.OpAdd), + compiler.MakeInstruction(compiler.OpBinaryOp, 11), compiler.MakeInstruction(compiler.OpGetLocal, 1), compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpConstant, 65535), }, - `0000 ADD -0001 GETL 1 -0003 CONST 2 -0006 CONST 65535`) + `0000 BINARYOP 11 +0002 GETL 1 +0004 CONST 2 +0007 CONST 65535`) } func TestMakeInstruction(t *testing.T) { @@ -49,10 +49,6 @@ func TestMakeInstruction(t *testing.T) { makeInstruction(t, []byte{byte(compiler.OpPop)}, compiler.OpPop) makeInstruction(t, []byte{byte(compiler.OpTrue)}, compiler.OpTrue) makeInstruction(t, []byte{byte(compiler.OpFalse)}, compiler.OpFalse) - makeInstruction(t, []byte{byte(compiler.OpAdd)}, compiler.OpAdd) - makeInstruction(t, []byte{byte(compiler.OpSub)}, compiler.OpSub) - makeInstruction(t, []byte{byte(compiler.OpMul)}, compiler.OpMul) - makeInstruction(t, []byte{byte(compiler.OpDiv)}, compiler.OpDiv) } func assertInstructionString(t *testing.T, instructions [][]byte, expected string) { diff --git a/compiler/opcodes.go b/compiler/opcodes.go index aa5fb9d..d832ee1 100644 --- a/compiler/opcodes.go +++ b/compiler/opcodes.go @@ -5,176 +5,137 @@ type Opcode = byte // List of opcodes const ( - OpConstant Opcode = iota // Load constant - OpAdd // Add - OpSub // Sub - OpMul // Multiply - OpDiv // Divide - OpRem // Remainder - OpBAnd // bitwise AND - OpBOr // bitwise OR - OpBXor // bitwise XOR - OpBShiftLeft // bitwise shift left - OpBShiftRight // bitwise shift right - OpBAndNot // bitwise AND NOT - OpBComplement // bitwise complement - OpPop // Pop - OpTrue // Push true - OpFalse // Push false - OpEqual // Equal == - OpNotEqual // Not equal != - OpGreaterThan // Greater than >= - OpGreaterThanEqual // Greater than or equal to >= - OpMinus // Minus - - OpLNot // Logical not ! - OpJumpFalsy // Jump if falsy - OpAndJump // Logical AND jump - OpOrJump // Logical OR jump - OpJump // Jump - OpNull // Push null - OpArray // Array object - OpMap // Map object - OpError // Error object - OpImmutable // Immutable object - OpIndex // Index operation - OpSliceIndex // Slice operation - OpCall // Call function - OpReturn // Return - OpReturnValue // Return value - OpGetGlobal // Get global variable - OpSetGlobal // Set global variable - OpSetSelGlobal // Set global variable using selectors - OpGetLocal // Get local variable - OpSetLocal // Set local variable - OpDefineLocal // Define local variable - OpSetSelLocal // Set local variable using selectors - OpGetFreePtr // Get free variable pointer object - OpGetFree // Get free variables - OpSetFree // Set free variables - OpGetLocalPtr // Get local variable as a pointer - OpSetSelFree // Set free variables using selectors - OpGetBuiltin // Get builtin function - OpClosure // Push closure - OpIteratorInit // Iterator init - OpIteratorNext // Iterator next - OpIteratorKey // Iterator key - OpIteratorValue // Iterator value + OpConstant Opcode = iota // Load constant + OpBComplement // bitwise complement + OpPop // Pop + OpTrue // Push true + OpFalse // Push false + OpEqual // Equal == + OpNotEqual // Not equal != + OpMinus // Minus - + OpLNot // Logical not ! + OpJumpFalsy // Jump if falsy + OpAndJump // Logical AND jump + OpOrJump // Logical OR jump + OpJump // Jump + OpNull // Push null + OpArray // Array object + OpMap // Map object + OpError // Error object + OpImmutable // Immutable object + OpIndex // Index operation + OpSliceIndex // Slice operation + OpCall // Call function + OpReturn // Return + OpGetGlobal // Get global variable + OpSetGlobal // Set global variable + OpSetSelGlobal // Set global variable using selectors + OpGetLocal // Get local variable + OpSetLocal // Set local variable + OpDefineLocal // Define local variable + OpSetSelLocal // Set local variable using selectors + OpGetFreePtr // Get free variable pointer object + OpGetFree // Get free variables + OpSetFree // Set free variables + OpGetLocalPtr // Get local variable as a pointer + OpSetSelFree // Set free variables using selectors + OpGetBuiltin // Get builtin function + OpClosure // Push closure + OpIteratorInit // Iterator init + OpIteratorNext // Iterator next + OpIteratorKey // Iterator key + OpIteratorValue // Iterator value + OpBinaryOp // Binary Operation ) // OpcodeNames is opcode names. var OpcodeNames = [...]string{ - OpConstant: "CONST", - OpPop: "POP", - OpTrue: "TRUE", - OpFalse: "FALSE", - OpAdd: "ADD", - OpSub: "SUB", - OpMul: "MUL", - OpDiv: "DIV", - OpRem: "REM", - OpBAnd: "AND", - OpBOr: "OR", - OpBXor: "XOR", - OpBAndNot: "ANDN", - OpBShiftLeft: "SHL", - OpBShiftRight: "SHR", - OpBComplement: "NEG", - OpEqual: "EQL", - OpNotEqual: "NEQ", - OpGreaterThan: "GTR", - OpGreaterThanEqual: "GEQ", - OpMinus: "NEG", - OpLNot: "NOT", - OpJumpFalsy: "JMPF", - OpAndJump: "ANDJMP", - OpOrJump: "ORJMP", - OpJump: "JMP", - OpNull: "NULL", - OpGetGlobal: "GETG", - OpSetGlobal: "SETG", - OpSetSelGlobal: "SETSG", - OpArray: "ARR", - OpMap: "MAP", - OpError: "ERROR", - OpImmutable: "IMMUT", - OpIndex: "INDEX", - OpSliceIndex: "SLICE", - OpCall: "CALL", - OpReturn: "RET", - OpReturnValue: "RETVAL", - OpGetLocal: "GETL", - OpSetLocal: "SETL", - OpDefineLocal: "DEFL", - OpSetSelLocal: "SETSL", - OpGetBuiltin: "BUILTIN", - OpClosure: "CLOSURE", - OpGetFreePtr: "GETFP", - OpGetFree: "GETF", - OpSetFree: "SETF", - OpGetLocalPtr: "GETLP", - OpSetSelFree: "SETSF", - OpIteratorInit: "ITER", - OpIteratorNext: "ITNXT", - OpIteratorKey: "ITKEY", - OpIteratorValue: "ITVAL", + OpConstant: "CONST", + OpPop: "POP", + OpTrue: "TRUE", + OpFalse: "FALSE", + OpBComplement: "NEG", + OpEqual: "EQL", + OpNotEqual: "NEQ", + OpMinus: "NEG", + OpLNot: "NOT", + OpJumpFalsy: "JMPF", + OpAndJump: "ANDJMP", + OpOrJump: "ORJMP", + OpJump: "JMP", + OpNull: "NULL", + OpGetGlobal: "GETG", + OpSetGlobal: "SETG", + OpSetSelGlobal: "SETSG", + OpArray: "ARR", + OpMap: "MAP", + OpError: "ERROR", + OpImmutable: "IMMUT", + OpIndex: "INDEX", + OpSliceIndex: "SLICE", + OpCall: "CALL", + OpReturn: "RET", + OpGetLocal: "GETL", + OpSetLocal: "SETL", + OpDefineLocal: "DEFL", + OpSetSelLocal: "SETSL", + OpGetBuiltin: "BUILTIN", + OpClosure: "CLOSURE", + OpGetFreePtr: "GETFP", + OpGetFree: "GETF", + OpSetFree: "SETF", + OpGetLocalPtr: "GETLP", + OpSetSelFree: "SETSF", + OpIteratorInit: "ITER", + OpIteratorNext: "ITNXT", + OpIteratorKey: "ITKEY", + OpIteratorValue: "ITVAL", + OpBinaryOp: "BINARYOP", } // OpcodeOperands is the number of operands. var OpcodeOperands = [...][]int{ - OpConstant: {2}, - OpPop: {}, - OpTrue: {}, - OpFalse: {}, - OpAdd: {}, - OpSub: {}, - OpMul: {}, - OpDiv: {}, - OpRem: {}, - OpBAnd: {}, - OpBOr: {}, - OpBXor: {}, - OpBAndNot: {}, - OpBShiftLeft: {}, - OpBShiftRight: {}, - OpBComplement: {}, - OpEqual: {}, - OpNotEqual: {}, - OpGreaterThan: {}, - OpGreaterThanEqual: {}, - OpMinus: {}, - OpLNot: {}, - OpJumpFalsy: {2}, - OpAndJump: {2}, - OpOrJump: {2}, - OpJump: {2}, - OpNull: {}, - OpGetGlobal: {2}, - OpSetGlobal: {2}, - OpSetSelGlobal: {2, 1}, - OpArray: {2}, - OpMap: {2}, - OpError: {}, - OpImmutable: {}, - OpIndex: {}, - OpSliceIndex: {}, - OpCall: {1}, - OpReturn: {}, - OpReturnValue: {}, - OpGetLocal: {1}, - OpSetLocal: {1}, - OpDefineLocal: {1}, - OpSetSelLocal: {1, 1}, - OpGetBuiltin: {1}, - OpClosure: {2, 1}, - OpGetFreePtr: {1}, - OpGetFree: {1}, - OpSetFree: {1}, - OpGetLocalPtr: {1}, - OpSetSelFree: {1, 1}, - OpIteratorInit: {}, - OpIteratorNext: {}, - OpIteratorKey: {}, - OpIteratorValue: {}, + OpConstant: {2}, + OpPop: {}, + OpTrue: {}, + OpFalse: {}, + OpBComplement: {}, + OpEqual: {}, + OpNotEqual: {}, + OpMinus: {}, + OpLNot: {}, + OpJumpFalsy: {2}, + OpAndJump: {2}, + OpOrJump: {2}, + OpJump: {2}, + OpNull: {}, + OpGetGlobal: {2}, + OpSetGlobal: {2}, + OpSetSelGlobal: {2, 1}, + OpArray: {2}, + OpMap: {2}, + OpError: {}, + OpImmutable: {}, + OpIndex: {}, + OpSliceIndex: {}, + OpCall: {1}, + OpReturn: {1}, + OpGetLocal: {1}, + OpSetLocal: {1}, + OpDefineLocal: {1}, + OpSetSelLocal: {1, 1}, + OpGetBuiltin: {1}, + OpClosure: {2, 1}, + OpGetFreePtr: {1}, + OpGetFree: {1}, + OpSetFree: {1}, + OpGetLocalPtr: {1}, + OpSetSelFree: {1, 1}, + OpIteratorInit: {}, + OpIteratorNext: {}, + OpIteratorKey: {}, + OpIteratorValue: {}, + OpBinaryOp: {1}, } // ReadOperands reads operands from the bytecode. diff --git a/objects/compiled_function.go b/objects/compiled_function.go index d20f237..606e3d9 100644 --- a/objects/compiled_function.go +++ b/objects/compiled_function.go @@ -47,3 +47,14 @@ func (o *CompiledFunction) IsFalsy() bool { func (o *CompiledFunction) Equals(x Object) bool { return false } + +// SourcePos returns the source position of the instruction at ip. +func (o *CompiledFunction) SourcePos(ip int) source.Pos { + for ip >= 0 { + if p, ok := o.SourceMap[ip]; ok { + return p + } + ip-- + } + return source.NoPos +} diff --git a/runtime/vm.go b/runtime/vm.go index ac5e1e9..e89188d 100644 --- a/runtime/vm.go +++ b/runtime/vm.go @@ -24,21 +24,19 @@ const ( // VM is a virtual machine that executes the bytecode compiled by Compiler. type VM struct { constants []objects.Object - stack []objects.Object + stack [StackSize]objects.Object sp int globals []objects.Object fileSet *source.FileSet - frames []Frame + frames [MaxFrames]Frame framesIndex int curFrame *Frame curInsts []byte - curIPLimit int ip int aborting int64 maxAllocs int64 allocs int64 err error - errOffset int } // NewVM creates a VM. @@ -47,24 +45,22 @@ func NewVM(bytecode *compiler.Bytecode, globals []objects.Object, maxAllocs int6 globals = make([]objects.Object, GlobalsSize) } - frames := make([]Frame, MaxFrames) - frames[0].fn = bytecode.MainFunction - frames[0].ip = -1 - - return &VM{ + v := &VM{ constants: bytecode.Constants, - stack: make([]objects.Object, StackSize), sp: 0, globals: globals, fileSet: bytecode.FileSet, - frames: frames, framesIndex: 1, - curFrame: &(frames[0]), - curInsts: frames[0].fn.Instructions, - curIPLimit: len(frames[0].fn.Instructions) - 1, ip: -1, maxAllocs: maxAllocs, } + + v.frames[0].fn = bytecode.MainFunction + v.frames[0].ip = -1 + v.curFrame = &v.frames[0] + v.curInsts = v.curFrame.fn.Instructions + + return v } // Abort aborts the execution. @@ -78,7 +74,6 @@ func (v *VM) Run() (err error) { v.sp = 0 v.curFrame = &(v.frames[0]) v.curInsts = v.curFrame.fn.Instructions - v.curIPLimit = len(v.curInsts) - 1 v.framesIndex = 1 v.ip = -1 v.allocs = v.maxAllocs + 1 @@ -88,13 +83,13 @@ func (v *VM) Run() (err error) { err = v.err if err != nil { - filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-v.errOffset]) + filePos := v.fileSet.Position(v.curFrame.fn.SourcePos(v.ip - 1)) err = fmt.Errorf("Runtime Error: %s\n\tat %s", err.Error(), filePos) for v.framesIndex > 1 { v.framesIndex-- v.curFrame = &v.frames[v.framesIndex-1] - filePos = v.fileSet.Position(v.curFrame.fn.SourceMap[v.curFrame.ip-1]) + filePos = v.fileSet.Position(v.curFrame.fn.SourcePos(v.curFrame.ip - 1)) err = fmt.Errorf("%s\n\tat %s", err.Error(), filePos) } return err @@ -109,80 +104,72 @@ func (v *VM) Run() (err error) { } func (v *VM) run() { - for v.ip < v.curIPLimit && (atomic.LoadInt64(&v.aborting) == 0) { + defer func() { + if r := recover(); r != nil { + if v.sp >= StackSize || v.framesIndex >= MaxFrames { + v.err = ErrStackOverflow + return + } + + if v.ip < len(v.curInsts)-1 { + if err, ok := r.(error); ok { + v.err = err + } else { + v.err = fmt.Errorf("panic: %v", r) + } + } + } + }() + + for atomic.LoadInt64(&v.aborting) == 0 { v.ip++ switch v.curInsts[v.ip] { case compiler.OpConstant: - cidx := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 v.ip += 2 - - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } + cidx := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 v.stack[v.sp] = v.constants[cidx] v.sp++ case compiler.OpNull: - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = objects.UndefinedValue v.sp++ - case compiler.OpAdd: - v.binaryOp(token.Add) + case compiler.OpBinaryOp: + v.ip++ + right := v.stack[v.sp-1] + left := v.stack[v.sp-2] - case compiler.OpSub: - v.binaryOp(token.Sub) + tok := token.Token(v.curInsts[v.ip]) + res, e := left.BinaryOp(tok, right) + if e != nil { + v.sp -= 2 - case compiler.OpMul: - v.binaryOp(token.Mul) + if e == objects.ErrInvalidOperator { + v.err = fmt.Errorf("invalid operation: %s %s %s", + left.TypeName(), tok.String(), right.TypeName()) + return + } - case compiler.OpDiv: - v.binaryOp(token.Quo) + v.err = e + return + } - case compiler.OpRem: - v.binaryOp(token.Rem) + v.allocs-- + if v.allocs == 0 { + v.err = ErrObjectAllocLimit + return + } - case compiler.OpBAnd: - v.binaryOp(token.And) - - case compiler.OpBOr: - v.binaryOp(token.Or) - - case compiler.OpBXor: - v.binaryOp(token.Xor) - - case compiler.OpBAndNot: - v.binaryOp(token.AndNot) - - case compiler.OpBShiftLeft: - v.binaryOp(token.Shl) - - case compiler.OpBShiftRight: - v.binaryOp(token.Shr) - - case compiler.OpGreaterThan: - v.binaryOp(token.Greater) - - case compiler.OpGreaterThanEqual: - v.binaryOp(token.GreaterEq) + v.stack[v.sp-2] = res + v.sp-- case compiler.OpEqual: right := v.stack[v.sp-1] left := v.stack[v.sp-2] v.sp -= 2 - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - if left.Equals(right) { v.stack[v.sp] = objects.TrueValue } else { @@ -195,11 +182,6 @@ func (v *VM) run() { left := v.stack[v.sp-2] v.sp -= 2 - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - if left.Equals(right) { v.stack[v.sp] = objects.FalseValue } else { @@ -211,20 +193,10 @@ func (v *VM) run() { v.sp-- case compiler.OpTrue: - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = objects.TrueValue v.sp++ case compiler.OpFalse: - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = objects.FalseValue v.sp++ @@ -232,11 +204,6 @@ func (v *VM) run() { operand := v.stack[v.sp-1] v.sp-- - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - if operand.IsFalsy() { v.stack[v.sp] = objects.TrueValue } else { @@ -250,11 +217,6 @@ func (v *VM) run() { switch x := operand.(type) { case *objects.Int: - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - var res objects.Object = &objects.Int{Value: ^x.Value} v.allocs-- @@ -276,11 +238,6 @@ func (v *VM) run() { switch x := operand.(type) { case *objects.Int: - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - var res objects.Object = &objects.Int{Value: -x.Value} v.allocs-- @@ -292,11 +249,6 @@ func (v *VM) run() { v.stack[v.sp] = res v.sp++ case *objects.Float: - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - var res objects.Object = &objects.Float{Value: -x.Value} v.allocs-- @@ -313,36 +265,31 @@ func (v *VM) run() { } case compiler.OpJumpFalsy: - pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 v.ip += 2 - - condition := v.stack[v.sp-1] v.sp-- - - if condition.IsFalsy() { + if v.stack[v.sp].IsFalsy() { + pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 v.ip = pos - 1 } case compiler.OpAndJump: - pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 v.ip += 2 - condition := v.stack[v.sp-1] - if condition.IsFalsy() { + if v.stack[v.sp-1].IsFalsy() { + pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 v.ip = pos - 1 } else { v.sp-- } case compiler.OpOrJump: - pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 v.ip += 2 - condition := v.stack[v.sp-1] - if !condition.IsFalsy() { - v.ip = pos - 1 - } else { + if v.stack[v.sp-1].IsFalsy() { v.sp-- + } else { + pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 + v.ip = pos - 1 } case compiler.OpJump: @@ -350,17 +297,16 @@ func (v *VM) run() { v.ip = pos - 1 case compiler.OpSetGlobal: - globalIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 v.ip += 2 - v.sp-- + globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 v.globals[globalIndex] = v.stack[v.sp] case compiler.OpSetSelGlobal: - globalIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 - numSelectors := int(v.curInsts[v.ip+3]) v.ip += 3 + globalIndex := int(v.curInsts[v.ip-1]) | int(v.curInsts[v.ip-2])<<8 + numSelectors := int(v.curInsts[v.ip]) // selectors and RHS value selectors := make([]objects.Object, numSelectors) @@ -372,28 +318,22 @@ func (v *VM) run() { v.sp -= numSelectors + 1 if e := indexAssign(v.globals[globalIndex], val, selectors); e != nil { - v.errOffset = 3 v.err = e return } case compiler.OpGetGlobal: - globalIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 v.ip += 2 + globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 val := v.globals[globalIndex] - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = val v.sp++ case compiler.OpArray: - numElements := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 v.ip += 2 + numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 var elements []objects.Object for i := v.sp - numElements; i < v.sp; i++ { @@ -409,17 +349,12 @@ func (v *VM) run() { return } - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = arr v.sp++ case compiler.OpMap: - numElements := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 v.ip += 2 + numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 kv := make(map[string]objects.Object) for i := v.sp - numElements; i < v.sp; i += 2 { @@ -437,11 +372,6 @@ func (v *VM) run() { return } - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = m v.sp++ @@ -512,11 +442,6 @@ func (v *VM) run() { val = objects.UndefinedValue } - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = val v.sp++ @@ -527,11 +452,6 @@ func (v *VM) run() { return } - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = left.Value v.sp++ @@ -586,11 +506,6 @@ func (v *VM) run() { highIdx = numElements } - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]} v.allocs-- @@ -631,11 +546,6 @@ func (v *VM) run() { highIdx = numElements } - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]} v.allocs-- @@ -676,11 +586,6 @@ func (v *VM) run() { highIdx = numElements } - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - var val objects.Object = &objects.String{Value: left.Value[lowIdx:highIdx]} v.allocs-- @@ -721,11 +626,6 @@ func (v *VM) run() { highIdx = numElements } - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - var val objects.Object = &objects.Bytes{Value: left.Value[lowIdx:highIdx]} v.allocs-- @@ -747,7 +647,6 @@ func (v *VM) run() { switch callee := value.(type) { case *objects.Closure: if numArgs != callee.Fn.NumParameters { - v.errOffset = 1 v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d", callee.Fn.NumParameters, numArgs) return @@ -756,7 +655,7 @@ func (v *VM) run() { // test if it's tail-call if callee.Fn == v.curFrame.fn { // recursion nextOp := v.curInsts[v.ip+1] - if nextOp == compiler.OpReturnValue || + if nextOp == compiler.OpReturn || (nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) { for p := 0; p < numArgs; p++ { v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p] @@ -775,13 +674,11 @@ func (v *VM) run() { v.curFrame.basePointer = v.sp - numArgs v.curInsts = callee.Fn.Instructions v.ip = -1 - v.curIPLimit = len(v.curInsts) - 1 v.framesIndex++ v.sp = v.sp - numArgs + callee.Fn.NumLocals case *objects.CompiledFunction: if numArgs != callee.NumParameters { - v.errOffset = 1 v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d", callee.NumParameters, numArgs) return @@ -790,7 +687,7 @@ func (v *VM) run() { // test if it's tail-call if callee == v.curFrame.fn { // recursion nextOp := v.curInsts[v.ip+1] - if nextOp == compiler.OpReturnValue || + if nextOp == compiler.OpReturn || (nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) { for p := 0; p < numArgs; p++ { v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p] @@ -809,7 +706,6 @@ func (v *VM) run() { v.curFrame.basePointer = v.sp - numArgs v.curInsts = callee.Instructions v.ip = -1 - v.curIPLimit = len(v.curInsts) - 1 v.framesIndex++ v.sp = v.sp - numArgs + callee.NumLocals @@ -824,8 +720,6 @@ func (v *VM) run() { // runtime error if e != nil { - v.errOffset = 1 - if e == objects.ErrWrongNumArguments { v.err = fmt.Errorf("wrong number of arguments in call to '%s'", value.TypeName()) @@ -853,56 +747,39 @@ func (v *VM) run() { return } - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = ret v.sp++ default: - v.errOffset = 1 v.err = fmt.Errorf("not callable: %s", callee.TypeName()) return } - case compiler.OpReturnValue: - retVal := v.stack[v.sp-1] + case compiler.OpReturn: + v.ip++ + var retVal objects.Object + if int(v.curInsts[v.ip]) == 1 { + retVal = v.stack[v.sp-1] + } else { + retVal = objects.UndefinedValue + } //v.sp-- v.framesIndex-- - lastFrame := v.frames[v.framesIndex] v.curFrame = &v.frames[v.framesIndex-1] v.curInsts = v.curFrame.fn.Instructions - v.curIPLimit = len(v.curInsts) - 1 v.ip = v.curFrame.ip //v.sp = lastFrame.basePointer - 1 - v.sp = lastFrame.basePointer + v.sp = v.frames[v.framesIndex].basePointer // skip stack overflow check because (newSP) <= (oldSP) v.stack[v.sp-1] = retVal //v.sp++ - case compiler.OpReturn: - v.framesIndex-- - lastFrame := v.frames[v.framesIndex] - v.curFrame = &v.frames[v.framesIndex-1] - v.curInsts = v.curFrame.fn.Instructions - v.curIPLimit = len(v.curInsts) - 1 - v.ip = v.curFrame.ip - - //v.sp = lastFrame.basePointer - 1 - v.sp = lastFrame.basePointer - - // skip stack overflow check because (newSP) <= (oldSP) - v.stack[v.sp-1] = objects.UndefinedValue - //v.sp++ - case compiler.OpDefineLocal: - localIndex := int(v.curInsts[v.ip+1]) v.ip++ + localIndex := int(v.curInsts[v.ip]) sp := v.curFrame.basePointer + localIndex @@ -947,14 +824,13 @@ func (v *VM) run() { sp := v.curFrame.basePointer + localIndex if e := indexAssign(v.stack[sp], val, selectors); e != nil { - v.errOffset = 2 v.err = e return } case compiler.OpGetLocal: - localIndex := int(v.curInsts[v.ip+1]) v.ip++ + localIndex := int(v.curInsts[v.ip]) val := v.stack[v.curFrame.basePointer+localIndex] @@ -962,34 +838,23 @@ func (v *VM) run() { val = *obj.Value } - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = val v.sp++ case compiler.OpGetBuiltin: - builtinIndex := int(v.curInsts[v.ip+1]) v.ip++ - - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } + builtinIndex := int(v.curInsts[v.ip]) v.stack[v.sp] = objects.Builtins[builtinIndex] v.sp++ case compiler.OpClosure: - constIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 - numFree := int(v.curInsts[v.ip+3]) v.ip += 3 + constIndex := int(v.curInsts[v.ip-1]) | int(v.curInsts[v.ip-2])<<8 + numFree := int(v.curInsts[v.ip]) fn, ok := v.constants[constIndex].(*objects.CompiledFunction) if !ok { - v.errOffset = 3 v.err = fmt.Errorf("not function: %s", fn.TypeName()) return } @@ -1006,11 +871,6 @@ func (v *VM) run() { v.sp -= numFree - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - var cl = &objects.Closure{ Fn: fn, Free: free, @@ -1026,44 +886,34 @@ func (v *VM) run() { v.sp++ case compiler.OpGetFreePtr: - freeIndex := int(v.curInsts[v.ip+1]) v.ip++ + freeIndex := int(v.curInsts[v.ip]) val := v.curFrame.freeVars[freeIndex] - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = val v.sp++ case compiler.OpGetFree: - freeIndex := int(v.curInsts[v.ip+1]) v.ip++ + freeIndex := int(v.curInsts[v.ip]) val := *v.curFrame.freeVars[freeIndex].Value - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = val v.sp++ case compiler.OpSetFree: - freeIndex := int(v.curInsts[v.ip+1]) v.ip++ + freeIndex := int(v.curInsts[v.ip]) *v.curFrame.freeVars[freeIndex].Value = v.stack[v.sp-1] v.sp-- case compiler.OpGetLocalPtr: - localIndex := int(v.curInsts[v.ip+1]) v.ip++ + localIndex := int(v.curInsts[v.ip]) sp := v.curFrame.basePointer + localIndex val := v.stack[sp] @@ -1076,18 +926,13 @@ func (v *VM) run() { v.stack[sp] = freeVar } - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = freeVar v.sp++ case compiler.OpSetSelFree: - freeIndex := int(v.curInsts[v.ip+1]) - numSelectors := int(v.curInsts[v.ip+2]) v.ip += 2 + freeIndex := int(v.curInsts[v.ip-1]) + numSelectors := int(v.curInsts[v.ip]) // selectors and RHS value selectors := make([]objects.Object, numSelectors) @@ -1098,7 +943,6 @@ func (v *VM) run() { v.sp -= numSelectors + 1 if e := indexAssign(*v.curFrame.freeVars[freeIndex].Value, val, selectors); e != nil { - v.errOffset = 2 v.err = e return } @@ -1123,11 +967,6 @@ func (v *VM) run() { return } - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = iterator v.sp++ @@ -1137,11 +976,6 @@ func (v *VM) run() { hasMore := iterator.(objects.Iterator).Next() - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - if hasMore { v.stack[v.sp] = objects.TrueValue } else { @@ -1155,11 +989,6 @@ func (v *VM) run() { val := iterator.(objects.Iterator).Key() - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = val v.sp++ @@ -1169,16 +998,12 @@ func (v *VM) run() { val := iterator.(objects.Iterator).Value() - if v.sp >= StackSize { - v.err = ErrStackOverflow - return - } - v.stack[v.sp] = val v.sp++ default: - panic(fmt.Errorf("unknown opcode: %d", v.curInsts[v.ip])) + v.err = fmt.Errorf("unknown opcode: %d", v.curInsts[v.ip]) + return } } } @@ -1224,32 +1049,3 @@ func indexAssign(dst, src objects.Object, selectors []objects.Object) error { return nil } - -func (v *VM) binaryOp(tok token.Token) { - right := v.stack[v.sp-1] - left := v.stack[v.sp-2] - - res, e := left.BinaryOp(tok, right) - if e != nil { - v.sp -= 2 - atomic.StoreInt64(&v.aborting, 1) - - if e == objects.ErrInvalidOperator { - v.err = fmt.Errorf("invalid operation: %s + %s", - left.TypeName(), right.TypeName()) - return - } - - v.err = e - return - } - - v.allocs-- - if v.allocs == 0 { - v.err = ErrObjectAllocLimit - return - } - - v.stack[v.sp-2] = res - v.sp-- -} diff --git a/runtime/vm_stack_overflow_test.go b/runtime/vm_stack_overflow_test.go new file mode 100644 index 0000000..edc92d1 --- /dev/null +++ b/runtime/vm_stack_overflow_test.go @@ -0,0 +1,7 @@ +package runtime_test + +import "testing" + +func TestVMStackOverflow(t *testing.T) { + expectError(t, `f := func() { return f() + 1 }; f()`, nil, "stack overflow") +}