Opbinaryop and other minor optimizations (#157)

* OpBinaryOp for performance

* Cleanup

* Remove ip limit check in every iteration of the loop.

* Micro optimizations on frequent ops

* Micro optimizations

* Patches

* Removed redundant binaryop operators from compiler

* Merged OpReturn and OpReturnValue

* Cleanup
This commit is contained in:
earncef 2019-03-22 12:42:34 +01:00 committed by Daniel
parent 7095b5678b
commit 09f3d52265
10 changed files with 367 additions and 574 deletions

View file

@ -102,25 +102,25 @@ func TestBytecode(t *testing.T) {
compiler.MakeInstruction(compiler.OpSetLocal, 0), compiler.MakeInstruction(compiler.OpSetLocal, 0),
compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpGetGlobal, 0),
compiler.MakeInstruction(compiler.OpGetFree, 0), compiler.MakeInstruction(compiler.OpGetFree, 0),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpGetFree, 1), compiler.MakeInstruction(compiler.OpGetFree, 1),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpGetLocal, 0), compiler.MakeInstruction(compiler.OpGetLocal, 0),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpReturnValue)), compiler.MakeInstruction(compiler.OpReturn, 1)),
compiledFunction(1, 0, compiledFunction(1, 0,
compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpConstant, 2),
compiler.MakeInstruction(compiler.OpSetLocal, 0), compiler.MakeInstruction(compiler.OpSetLocal, 0),
compiler.MakeInstruction(compiler.OpGetFree, 0), compiler.MakeInstruction(compiler.OpGetFree, 0),
compiler.MakeInstruction(compiler.OpGetLocal, 0), compiler.MakeInstruction(compiler.OpGetLocal, 0),
compiler.MakeInstruction(compiler.OpClosure, 4, 2), compiler.MakeInstruction(compiler.OpClosure, 4, 2),
compiler.MakeInstruction(compiler.OpReturnValue)), compiler.MakeInstruction(compiler.OpReturn, 1)),
compiledFunction(1, 0, compiledFunction(1, 0,
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpSetLocal, 0), compiler.MakeInstruction(compiler.OpSetLocal, 0),
compiler.MakeInstruction(compiler.OpGetLocal, 0), compiler.MakeInstruction(compiler.OpGetLocal, 0),
compiler.MakeInstruction(compiler.OpClosure, 5, 1), 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}))) 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}, &objects.Int{Value: 88},
compiledFunction(1, 0, compiledFunction(1, 0,
compiler.MakeInstruction(compiler.OpConstant, 3), compiler.MakeInstruction(compiler.OpConstant, 3),
compiler.MakeInstruction(compiler.OpReturnValue)), compiler.MakeInstruction(compiler.OpReturn, 1)),
compiledFunction(1, 0, compiledFunction(1, 0,
compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpConstant, 2),
compiler.MakeInstruction(compiler.OpReturnValue)), compiler.MakeInstruction(compiler.OpReturn, 1)),
compiledFunction(1, 0, compiledFunction(1, 0,
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpReturnValue)))) compiler.MakeInstruction(compiler.OpReturn, 1))))
assert.Equal(t, 7, b.CountObjects()) assert.Equal(t, 7, b.CountObjects())
} }

View file

@ -119,7 +119,7 @@ func (c *Compiler) Compile(node ast.Node) error {
return err return err
} }
c.emit(node, OpGreaterThan) c.emit(node, OpBinaryOp, int(token.Greater))
return nil return nil
} else if node.Token == token.LessEq { } else if node.Token == token.LessEq {
@ -130,7 +130,7 @@ func (c *Compiler) Compile(node ast.Node) error {
return err return err
} }
c.emit(node, OpGreaterThanEqual) c.emit(node, OpBinaryOp, int(token.GreaterEq))
return nil return nil
} }
@ -144,35 +144,35 @@ func (c *Compiler) Compile(node ast.Node) error {
switch node.Token { switch node.Token {
case token.Add: case token.Add:
c.emit(node, OpAdd) c.emit(node, OpBinaryOp, int(token.Add))
case token.Sub: case token.Sub:
c.emit(node, OpSub) c.emit(node, OpBinaryOp, int(token.Sub))
case token.Mul: case token.Mul:
c.emit(node, OpMul) c.emit(node, OpBinaryOp, int(token.Mul))
case token.Quo: case token.Quo:
c.emit(node, OpDiv) c.emit(node, OpBinaryOp, int(token.Quo))
case token.Rem: case token.Rem:
c.emit(node, OpRem) c.emit(node, OpBinaryOp, int(token.Rem))
case token.Greater: case token.Greater:
c.emit(node, OpGreaterThan) c.emit(node, OpBinaryOp, int(token.Greater))
case token.GreaterEq: case token.GreaterEq:
c.emit(node, OpGreaterThanEqual) c.emit(node, OpBinaryOp, int(token.GreaterEq))
case token.Equal: case token.Equal:
c.emit(node, OpEqual) c.emit(node, OpEqual)
case token.NotEqual: case token.NotEqual:
c.emit(node, OpNotEqual) c.emit(node, OpNotEqual)
case token.And: case token.And:
c.emit(node, OpBAnd) c.emit(node, OpBinaryOp, int(token.And))
case token.Or: case token.Or:
c.emit(node, OpBOr) c.emit(node, OpBinaryOp, int(token.Or))
case token.Xor: case token.Xor:
c.emit(node, OpBXor) c.emit(node, OpBinaryOp, int(token.Xor))
case token.AndNot: case token.AndNot:
c.emit(node, OpBAndNot) c.emit(node, OpBinaryOp, int(token.AndNot))
case token.Shl: case token.Shl:
c.emit(node, OpBShiftLeft) c.emit(node, OpBinaryOp, int(token.Shl))
case token.Shr: case token.Shr:
c.emit(node, OpBShiftRight) c.emit(node, OpBinaryOp, int(token.Shr))
default: default:
return c.errorf(node, "invalid binary operator: %s", node.Token.String()) 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 // add OpReturn if function returns nothing
if !c.lastInstructionIs(OpReturnValue) && !c.lastInstructionIs(OpReturn) { if !c.lastInstructionIs(OpReturn) {
c.emit(node, OpReturn) c.emit(node, OpReturn, 0)
} }
freeSymbols := c.symbolTable.FreeSymbols() freeSymbols := c.symbolTable.FreeSymbols()
@ -486,13 +486,13 @@ func (c *Compiler) Compile(node ast.Node) error {
} }
if node.Result == nil { if node.Result == nil {
c.emit(node, OpReturn) c.emit(node, OpReturn, 0)
} else { } else {
if err := c.Compile(node.Result); err != nil { if err := c.Compile(node.Result); err != nil {
return err return err
} }
c.emit(node, OpReturnValue) c.emit(node, OpReturn, 1)
} }
case *ast.CallExpr: case *ast.CallExpr:
@ -578,7 +578,7 @@ func (c *Compiler) Compile(node ast.Node) error {
} }
c.emit(node, OpImmutable) c.emit(node, OpImmutable)
c.emit(node, OpReturnValue) c.emit(node, OpReturn, 1)
case *ast.ErrorExpr: case *ast.ErrorExpr:
if err := c.Compile(node.Expr); err != nil { if err := c.Compile(node.Expr); err != nil {

View file

@ -51,27 +51,27 @@ func (c *Compiler) compileAssign(node ast.Node, lhs, rhs []ast.Expr, op token.To
switch op { switch op {
case token.AddAssign: case token.AddAssign:
c.emit(node, OpAdd) c.emit(node, OpBinaryOp, int(token.Add))
case token.SubAssign: case token.SubAssign:
c.emit(node, OpSub) c.emit(node, OpBinaryOp, int(token.Sub))
case token.MulAssign: case token.MulAssign:
c.emit(node, OpMul) c.emit(node, OpBinaryOp, int(token.Mul))
case token.QuoAssign: case token.QuoAssign:
c.emit(node, OpDiv) c.emit(node, OpBinaryOp, int(token.Quo))
case token.RemAssign: case token.RemAssign:
c.emit(node, OpRem) c.emit(node, OpBinaryOp, int(token.Rem))
case token.AndAssign: case token.AndAssign:
c.emit(node, OpBAnd) c.emit(node, OpBinaryOp, int(token.And))
case token.OrAssign: case token.OrAssign:
c.emit(node, OpBOr) c.emit(node, OpBinaryOp, int(token.Or))
case token.AndNotAssign: case token.AndNotAssign:
c.emit(node, OpBAndNot) c.emit(node, OpBinaryOp, int(token.AndNot))
case token.XorAssign: case token.XorAssign:
c.emit(node, OpBXor) c.emit(node, OpBinaryOp, int(token.Xor))
case token.ShlAssign: case token.ShlAssign:
c.emit(node, OpBShiftLeft) c.emit(node, OpBinaryOp, int(token.Shl))
case token.ShrAssign: case token.ShrAssign:
c.emit(node, OpBShiftRight) c.emit(node, OpBinaryOp, int(token.Shr))
} }
// compile selector expressions (right to left) // compile selector expressions (right to left)

View file

@ -50,7 +50,7 @@ func (c *Compiler) compileModule(node ast.Node, moduleName, modulePath string, s
} }
// add OpReturn (== export undefined) if export is missing // add OpReturn (== export undefined) if export is missing
if !moduleCompiler.lastInstructionIs(OpReturnValue) { if !moduleCompiler.lastInstructionIs(OpReturn) {
moduleCompiler.emit(nil, OpReturn) moduleCompiler.emit(nil, OpReturn)
} }

View file

@ -18,7 +18,7 @@ func TestCompiler_Compile(t *testing.T) {
concat( concat(
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpPop)), compiler.MakeInstruction(compiler.OpPop)),
objectsArray( objectsArray(
intObject(1), intObject(1),
@ -40,7 +40,7 @@ func TestCompiler_Compile(t *testing.T) {
concat( concat(
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpSub), compiler.MakeInstruction(compiler.OpBinaryOp, 12),
compiler.MakeInstruction(compiler.OpPop)), compiler.MakeInstruction(compiler.OpPop)),
objectsArray( objectsArray(
intObject(1), intObject(1),
@ -51,7 +51,7 @@ func TestCompiler_Compile(t *testing.T) {
concat( concat(
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpMul), compiler.MakeInstruction(compiler.OpBinaryOp, 13),
compiler.MakeInstruction(compiler.OpPop)), compiler.MakeInstruction(compiler.OpPop)),
objectsArray( objectsArray(
intObject(1), intObject(1),
@ -62,7 +62,7 @@ func TestCompiler_Compile(t *testing.T) {
concat( concat(
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpDiv), compiler.MakeInstruction(compiler.OpBinaryOp, 14),
compiler.MakeInstruction(compiler.OpPop)), compiler.MakeInstruction(compiler.OpPop)),
objectsArray( objectsArray(
intObject(2), intObject(2),
@ -87,7 +87,7 @@ func TestCompiler_Compile(t *testing.T) {
concat( concat(
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpGreaterThan), compiler.MakeInstruction(compiler.OpBinaryOp, 39),
compiler.MakeInstruction(compiler.OpPop)), compiler.MakeInstruction(compiler.OpPop)),
objectsArray( objectsArray(
intObject(1), intObject(1),
@ -98,7 +98,29 @@ func TestCompiler_Compile(t *testing.T) {
concat( concat(
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpConstant, 1), 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)), compiler.MakeInstruction(compiler.OpPop)),
objectsArray( objectsArray(
intObject(2), intObject(2),
@ -204,7 +226,7 @@ func TestCompiler_Compile(t *testing.T) {
concat( concat(
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpPop)), compiler.MakeInstruction(compiler.OpPop)),
objectsArray( objectsArray(
stringObject("ka"), stringObject("ka"),
@ -219,7 +241,7 @@ func TestCompiler_Compile(t *testing.T) {
compiler.MakeInstruction(compiler.OpSetGlobal, 1), compiler.MakeInstruction(compiler.OpSetGlobal, 1),
compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpGetGlobal, 0),
compiler.MakeInstruction(compiler.OpGetGlobal, 1), compiler.MakeInstruction(compiler.OpGetGlobal, 1),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpSetGlobal, 0)), compiler.MakeInstruction(compiler.OpSetGlobal, 0)),
objectsArray( objectsArray(
intObject(1), intObject(1),
@ -234,7 +256,7 @@ func TestCompiler_Compile(t *testing.T) {
compiler.MakeInstruction(compiler.OpSetGlobal, 1), compiler.MakeInstruction(compiler.OpSetGlobal, 1),
compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpGetGlobal, 0),
compiler.MakeInstruction(compiler.OpGetGlobal, 1), compiler.MakeInstruction(compiler.OpGetGlobal, 1),
compiler.MakeInstruction(compiler.OpDiv), compiler.MakeInstruction(compiler.OpBinaryOp, 14),
compiler.MakeInstruction(compiler.OpSetGlobal, 0)), compiler.MakeInstruction(compiler.OpSetGlobal, 0)),
objectsArray( objectsArray(
intObject(1), intObject(1),
@ -265,13 +287,13 @@ func TestCompiler_Compile(t *testing.T) {
concat( concat(
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpConstant, 2),
compiler.MakeInstruction(compiler.OpConstant, 3), compiler.MakeInstruction(compiler.OpConstant, 3),
compiler.MakeInstruction(compiler.OpSub), compiler.MakeInstruction(compiler.OpBinaryOp, 12),
compiler.MakeInstruction(compiler.OpConstant, 4), compiler.MakeInstruction(compiler.OpConstant, 4),
compiler.MakeInstruction(compiler.OpConstant, 5), compiler.MakeInstruction(compiler.OpConstant, 5),
compiler.MakeInstruction(compiler.OpMul), compiler.MakeInstruction(compiler.OpBinaryOp, 13),
compiler.MakeInstruction(compiler.OpArray, 3), compiler.MakeInstruction(compiler.OpArray, 3),
compiler.MakeInstruction(compiler.OpPop)), compiler.MakeInstruction(compiler.OpPop)),
objectsArray( objectsArray(
@ -314,11 +336,11 @@ func TestCompiler_Compile(t *testing.T) {
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpConstant, 2),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpConstant, 3), compiler.MakeInstruction(compiler.OpConstant, 3),
compiler.MakeInstruction(compiler.OpConstant, 4), compiler.MakeInstruction(compiler.OpConstant, 4),
compiler.MakeInstruction(compiler.OpConstant, 5), compiler.MakeInstruction(compiler.OpConstant, 5),
compiler.MakeInstruction(compiler.OpMul), compiler.MakeInstruction(compiler.OpBinaryOp, 13),
compiler.MakeInstruction(compiler.OpMap, 4), compiler.MakeInstruction(compiler.OpMap, 4),
compiler.MakeInstruction(compiler.OpPop)), compiler.MakeInstruction(compiler.OpPop)),
objectsArray( objectsArray(
@ -338,7 +360,7 @@ func TestCompiler_Compile(t *testing.T) {
compiler.MakeInstruction(compiler.OpArray, 3), compiler.MakeInstruction(compiler.OpArray, 3),
compiler.MakeInstruction(compiler.OpConstant, 3), compiler.MakeInstruction(compiler.OpConstant, 3),
compiler.MakeInstruction(compiler.OpConstant, 4), compiler.MakeInstruction(compiler.OpConstant, 4),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpIndex), compiler.MakeInstruction(compiler.OpIndex),
compiler.MakeInstruction(compiler.OpPop)), compiler.MakeInstruction(compiler.OpPop)),
objectsArray( objectsArray(
@ -356,7 +378,7 @@ func TestCompiler_Compile(t *testing.T) {
compiler.MakeInstruction(compiler.OpMap, 2), compiler.MakeInstruction(compiler.OpMap, 2),
compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpConstant, 2),
compiler.MakeInstruction(compiler.OpConstant, 3), compiler.MakeInstruction(compiler.OpConstant, 3),
compiler.MakeInstruction(compiler.OpSub), compiler.MakeInstruction(compiler.OpBinaryOp, 12),
compiler.MakeInstruction(compiler.OpIndex), compiler.MakeInstruction(compiler.OpIndex),
compiler.MakeInstruction(compiler.OpPop)), compiler.MakeInstruction(compiler.OpPop)),
objectsArray( objectsArray(
@ -444,8 +466,8 @@ func TestCompiler_Compile(t *testing.T) {
compiledFunction(0, 0, compiledFunction(0, 0,
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpReturnValue))))) compiler.MakeInstruction(compiler.OpReturn, 1)))))
expect(t, `func() { 5 + 10 }`, expect(t, `func() { 5 + 10 }`,
bytecode( bytecode(
@ -458,9 +480,9 @@ func TestCompiler_Compile(t *testing.T) {
compiledFunction(0, 0, compiledFunction(0, 0,
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpPop), compiler.MakeInstruction(compiler.OpPop),
compiler.MakeInstruction(compiler.OpReturn))))) compiler.MakeInstruction(compiler.OpReturn, 0)))))
expect(t, `func() { 1; 2 }`, expect(t, `func() { 1; 2 }`,
bytecode( bytecode(
@ -475,7 +497,7 @@ func TestCompiler_Compile(t *testing.T) {
compiler.MakeInstruction(compiler.OpPop), compiler.MakeInstruction(compiler.OpPop),
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpPop), compiler.MakeInstruction(compiler.OpPop),
compiler.MakeInstruction(compiler.OpReturn))))) compiler.MakeInstruction(compiler.OpReturn, 0)))))
expect(t, `func() { 1; return 2 }`, expect(t, `func() { 1; return 2 }`,
bytecode( bytecode(
@ -489,7 +511,7 @@ func TestCompiler_Compile(t *testing.T) {
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpPop), compiler.MakeInstruction(compiler.OpPop),
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpReturnValue))))) compiler.MakeInstruction(compiler.OpReturn, 1)))))
expect(t, `func() { if(true) { return 1 } else { return 2 } }`, expect(t, `func() { if(true) { return 1 } else { return 2 } }`,
bytecode( bytecode(
@ -500,13 +522,13 @@ func TestCompiler_Compile(t *testing.T) {
intObject(1), intObject(1),
intObject(2), intObject(2),
compiledFunction(0, 0, compiledFunction(0, 0,
compiler.MakeInstruction(compiler.OpTrue), // 0000 compiler.MakeInstruction(compiler.OpTrue), // 0000
compiler.MakeInstruction(compiler.OpJumpFalsy, 11), // 0001 compiler.MakeInstruction(compiler.OpJumpFalsy, 12), // 0001
compiler.MakeInstruction(compiler.OpConstant, 0), // 0004 compiler.MakeInstruction(compiler.OpConstant, 0), // 0004
compiler.MakeInstruction(compiler.OpReturnValue), // 0007 compiler.MakeInstruction(compiler.OpReturn, 1), // 0007
compiler.MakeInstruction(compiler.OpJump, 15), // 0008 compiler.MakeInstruction(compiler.OpJump, 17), // 0008
compiler.MakeInstruction(compiler.OpConstant, 1), // 0011 compiler.MakeInstruction(compiler.OpConstant, 1), // 0011
compiler.MakeInstruction(compiler.OpReturnValue))))) // 0014 compiler.MakeInstruction(compiler.OpReturn, 1))))) // 0014
expect(t, `func() { 1; if(true) { 2 } else { 3 }; 4 }`, expect(t, `func() { 1; if(true) { 2 } else { 3 }; 4 }`,
bytecode( bytecode(
@ -530,7 +552,7 @@ func TestCompiler_Compile(t *testing.T) {
compiler.MakeInstruction(compiler.OpPop), // 0018 compiler.MakeInstruction(compiler.OpPop), // 0018
compiler.MakeInstruction(compiler.OpConstant, 3), // 0019 compiler.MakeInstruction(compiler.OpConstant, 3), // 0019
compiler.MakeInstruction(compiler.OpPop), // 0022 compiler.MakeInstruction(compiler.OpPop), // 0022
compiler.MakeInstruction(compiler.OpReturn))))) // 0023 compiler.MakeInstruction(compiler.OpReturn, 0))))) // 0023
expect(t, `func() { }`, expect(t, `func() { }`,
bytecode( bytecode(
@ -538,7 +560,7 @@ func TestCompiler_Compile(t *testing.T) {
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpPop)), compiler.MakeInstruction(compiler.OpPop)),
objectsArray( objectsArray(
compiledFunction(0, 0, compiler.MakeInstruction(compiler.OpReturn))))) compiledFunction(0, 0, compiler.MakeInstruction(compiler.OpReturn, 0)))))
expect(t, `func() { 24 }()`, expect(t, `func() { 24 }()`,
bytecode( bytecode(
@ -551,7 +573,7 @@ func TestCompiler_Compile(t *testing.T) {
compiledFunction(0, 0, compiledFunction(0, 0,
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpPop), compiler.MakeInstruction(compiler.OpPop),
compiler.MakeInstruction(compiler.OpReturn))))) compiler.MakeInstruction(compiler.OpReturn, 0)))))
expect(t, `func() { return 24 }()`, expect(t, `func() { return 24 }()`,
bytecode( bytecode(
@ -563,7 +585,7 @@ func TestCompiler_Compile(t *testing.T) {
intObject(24), intObject(24),
compiledFunction(0, 0, compiledFunction(0, 0,
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpReturnValue))))) compiler.MakeInstruction(compiler.OpReturn, 1)))))
expect(t, `noArg := func() { 24 }; noArg();`, expect(t, `noArg := func() { 24 }; noArg();`,
bytecode( bytecode(
@ -578,7 +600,7 @@ func TestCompiler_Compile(t *testing.T) {
compiledFunction(0, 0, compiledFunction(0, 0,
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpPop), compiler.MakeInstruction(compiler.OpPop),
compiler.MakeInstruction(compiler.OpReturn))))) compiler.MakeInstruction(compiler.OpReturn, 0)))))
expect(t, `noArg := func() { return 24 }; noArg();`, expect(t, `noArg := func() { return 24 }; noArg();`,
bytecode( bytecode(
@ -592,7 +614,7 @@ func TestCompiler_Compile(t *testing.T) {
intObject(24), intObject(24),
compiledFunction(0, 0, compiledFunction(0, 0,
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpReturnValue))))) compiler.MakeInstruction(compiler.OpReturn, 1)))))
expect(t, `n := 55; func() { n };`, expect(t, `n := 55; func() { n };`,
bytecode( bytecode(
@ -606,7 +628,7 @@ func TestCompiler_Compile(t *testing.T) {
compiledFunction(0, 0, compiledFunction(0, 0,
compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpGetGlobal, 0),
compiler.MakeInstruction(compiler.OpPop), compiler.MakeInstruction(compiler.OpPop),
compiler.MakeInstruction(compiler.OpReturn))))) compiler.MakeInstruction(compiler.OpReturn, 0)))))
expect(t, `func() { n := 55; return n }`, expect(t, `func() { n := 55; return n }`,
bytecode( bytecode(
@ -619,7 +641,7 @@ func TestCompiler_Compile(t *testing.T) {
compiler.MakeInstruction(compiler.OpConstant, 0), compiler.MakeInstruction(compiler.OpConstant, 0),
compiler.MakeInstruction(compiler.OpDefineLocal, 0), compiler.MakeInstruction(compiler.OpDefineLocal, 0),
compiler.MakeInstruction(compiler.OpGetLocal, 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 }`, expect(t, `func() { a := 55; b := 77; return a + b }`,
bytecode( bytecode(
@ -636,8 +658,8 @@ func TestCompiler_Compile(t *testing.T) {
compiler.MakeInstruction(compiler.OpDefineLocal, 1), compiler.MakeInstruction(compiler.OpDefineLocal, 1),
compiler.MakeInstruction(compiler.OpGetLocal, 0), compiler.MakeInstruction(compiler.OpGetLocal, 0),
compiler.MakeInstruction(compiler.OpGetLocal, 1), compiler.MakeInstruction(compiler.OpGetLocal, 1),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpReturnValue))))) compiler.MakeInstruction(compiler.OpReturn, 1)))))
expect(t, `f1 := func(a) { return a }; f1(24);`, expect(t, `f1 := func(a) { return a }; f1(24);`,
bytecode( bytecode(
@ -651,7 +673,7 @@ func TestCompiler_Compile(t *testing.T) {
objectsArray( objectsArray(
compiledFunction(1, 1, compiledFunction(1, 1,
compiler.MakeInstruction(compiler.OpGetLocal, 0), compiler.MakeInstruction(compiler.OpGetLocal, 0),
compiler.MakeInstruction(compiler.OpReturnValue)), compiler.MakeInstruction(compiler.OpReturn, 1)),
intObject(24)))) intObject(24))))
expect(t, `f1 := func(a, b, c) { a; b; return c; }; f1(24, 25, 26);`, 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.OpGetLocal, 1),
compiler.MakeInstruction(compiler.OpPop), compiler.MakeInstruction(compiler.OpPop),
compiler.MakeInstruction(compiler.OpGetLocal, 2), compiler.MakeInstruction(compiler.OpGetLocal, 2),
compiler.MakeInstruction(compiler.OpReturnValue)), compiler.MakeInstruction(compiler.OpReturn, 1)),
intObject(24), intObject(24),
intObject(25), intObject(25),
intObject(26)))) intObject(26))))
@ -691,7 +713,7 @@ func TestCompiler_Compile(t *testing.T) {
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpSetLocal, 0), compiler.MakeInstruction(compiler.OpSetLocal, 0),
compiler.MakeInstruction(compiler.OpGetLocal, 0), compiler.MakeInstruction(compiler.OpGetLocal, 0),
compiler.MakeInstruction(compiler.OpReturnValue))))) compiler.MakeInstruction(compiler.OpReturn, 1)))))
expect(t, `len([]);`, expect(t, `len([]);`,
bytecode( bytecode(
concat( concat(
@ -711,7 +733,7 @@ func TestCompiler_Compile(t *testing.T) {
compiler.MakeInstruction(compiler.OpGetBuiltin, 0), compiler.MakeInstruction(compiler.OpGetBuiltin, 0),
compiler.MakeInstruction(compiler.OpArray, 0), compiler.MakeInstruction(compiler.OpArray, 0),
compiler.MakeInstruction(compiler.OpCall, 1), compiler.MakeInstruction(compiler.OpCall, 1),
compiler.MakeInstruction(compiler.OpReturnValue))))) compiler.MakeInstruction(compiler.OpReturn, 1)))))
expect(t, `func(a) { func(b) { return a + b } }`, expect(t, `func(a) { func(b) { return a + b } }`,
bytecode( bytecode(
@ -722,13 +744,13 @@ func TestCompiler_Compile(t *testing.T) {
compiledFunction(1, 1, compiledFunction(1, 1,
compiler.MakeInstruction(compiler.OpGetFree, 0), compiler.MakeInstruction(compiler.OpGetFree, 0),
compiler.MakeInstruction(compiler.OpGetLocal, 0), compiler.MakeInstruction(compiler.OpGetLocal, 0),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpReturnValue)), compiler.MakeInstruction(compiler.OpReturn, 1)),
compiledFunction(1, 1, compiledFunction(1, 1,
compiler.MakeInstruction(compiler.OpGetLocalPtr, 0), compiler.MakeInstruction(compiler.OpGetLocalPtr, 0),
compiler.MakeInstruction(compiler.OpClosure, 0, 1), compiler.MakeInstruction(compiler.OpClosure, 0, 1),
compiler.MakeInstruction(compiler.OpPop), compiler.MakeInstruction(compiler.OpPop),
compiler.MakeInstruction(compiler.OpReturn))))) compiler.MakeInstruction(compiler.OpReturn, 0)))))
expect(t, ` expect(t, `
func(a) { func(a) {
@ -746,19 +768,19 @@ func(a) {
compiledFunction(1, 1, compiledFunction(1, 1,
compiler.MakeInstruction(compiler.OpGetFree, 0), compiler.MakeInstruction(compiler.OpGetFree, 0),
compiler.MakeInstruction(compiler.OpGetFree, 1), compiler.MakeInstruction(compiler.OpGetFree, 1),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpGetLocal, 0), compiler.MakeInstruction(compiler.OpGetLocal, 0),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpReturnValue)), compiler.MakeInstruction(compiler.OpReturn, 1)),
compiledFunction(1, 1, compiledFunction(1, 1,
compiler.MakeInstruction(compiler.OpGetFreePtr, 0), compiler.MakeInstruction(compiler.OpGetFreePtr, 0),
compiler.MakeInstruction(compiler.OpGetLocalPtr, 0), compiler.MakeInstruction(compiler.OpGetLocalPtr, 0),
compiler.MakeInstruction(compiler.OpClosure, 0, 2), compiler.MakeInstruction(compiler.OpClosure, 0, 2),
compiler.MakeInstruction(compiler.OpReturnValue)), compiler.MakeInstruction(compiler.OpReturn, 1)),
compiledFunction(1, 1, compiledFunction(1, 1,
compiler.MakeInstruction(compiler.OpGetLocalPtr, 0), compiler.MakeInstruction(compiler.OpGetLocalPtr, 0),
compiler.MakeInstruction(compiler.OpClosure, 1, 1), compiler.MakeInstruction(compiler.OpClosure, 1, 1),
compiler.MakeInstruction(compiler.OpReturnValue))))) compiler.MakeInstruction(compiler.OpReturn, 1)))))
expect(t, ` expect(t, `
g := 55; g := 55;
@ -792,25 +814,25 @@ func() {
compiler.MakeInstruction(compiler.OpDefineLocal, 0), compiler.MakeInstruction(compiler.OpDefineLocal, 0),
compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpGetGlobal, 0),
compiler.MakeInstruction(compiler.OpGetFree, 0), compiler.MakeInstruction(compiler.OpGetFree, 0),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpGetFree, 1), compiler.MakeInstruction(compiler.OpGetFree, 1),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpGetLocal, 0), compiler.MakeInstruction(compiler.OpGetLocal, 0),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpReturnValue)), compiler.MakeInstruction(compiler.OpReturn, 1)),
compiledFunction(1, 0, compiledFunction(1, 0,
compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpConstant, 2),
compiler.MakeInstruction(compiler.OpDefineLocal, 0), compiler.MakeInstruction(compiler.OpDefineLocal, 0),
compiler.MakeInstruction(compiler.OpGetFreePtr, 0), compiler.MakeInstruction(compiler.OpGetFreePtr, 0),
compiler.MakeInstruction(compiler.OpGetLocalPtr, 0), compiler.MakeInstruction(compiler.OpGetLocalPtr, 0),
compiler.MakeInstruction(compiler.OpClosure, 4, 2), compiler.MakeInstruction(compiler.OpClosure, 4, 2),
compiler.MakeInstruction(compiler.OpReturnValue)), compiler.MakeInstruction(compiler.OpReturn, 1)),
compiledFunction(1, 0, compiledFunction(1, 0,
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpDefineLocal, 0), compiler.MakeInstruction(compiler.OpDefineLocal, 0),
compiler.MakeInstruction(compiler.OpGetLocalPtr, 0), compiler.MakeInstruction(compiler.OpGetLocalPtr, 0),
compiler.MakeInstruction(compiler.OpClosure, 5, 1), compiler.MakeInstruction(compiler.OpClosure, 5, 1),
compiler.MakeInstruction(compiler.OpReturnValue))))) compiler.MakeInstruction(compiler.OpReturn, 1)))))
expect(t, `for i:=0; i<10; i++ {}`, expect(t, `for i:=0; i<10; i++ {}`,
bytecode( bytecode(
@ -819,11 +841,11 @@ func() {
compiler.MakeInstruction(compiler.OpSetGlobal, 0), compiler.MakeInstruction(compiler.OpSetGlobal, 0),
compiler.MakeInstruction(compiler.OpConstant, 1), compiler.MakeInstruction(compiler.OpConstant, 1),
compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpGetGlobal, 0),
compiler.MakeInstruction(compiler.OpGreaterThan), compiler.MakeInstruction(compiler.OpBinaryOp, 39),
compiler.MakeInstruction(compiler.OpJumpFalsy, 29), compiler.MakeInstruction(compiler.OpJumpFalsy, 31),
compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpGetGlobal, 0),
compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpConstant, 2),
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpSetGlobal, 0), compiler.MakeInstruction(compiler.OpSetGlobal, 0),
compiler.MakeInstruction(compiler.OpJump, 6)), compiler.MakeInstruction(compiler.OpJump, 6)),
objectsArray( objectsArray(
@ -863,10 +885,10 @@ func() {
compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpGetGlobal, 0),
compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpConstant, 2),
compiler.MakeInstruction(compiler.OpNotEqual), compiler.MakeInstruction(compiler.OpNotEqual),
compiler.MakeInstruction(compiler.OpOrJump, 33), compiler.MakeInstruction(compiler.OpOrJump, 34),
compiler.MakeInstruction(compiler.OpConstant, 3), compiler.MakeInstruction(compiler.OpConstant, 3),
compiler.MakeInstruction(compiler.OpGetGlobal, 0), compiler.MakeInstruction(compiler.OpGetGlobal, 0),
compiler.MakeInstruction(compiler.OpGreaterThan), compiler.MakeInstruction(compiler.OpBinaryOp, 39),
compiler.MakeInstruction(compiler.OpPop)), compiler.MakeInstruction(compiler.OpPop)),
objectsArray( objectsArray(
intObject(0), intObject(0),

View file

@ -21,25 +21,25 @@ func TestInstructions_String(t *testing.T) {
assertInstructionString(t, assertInstructionString(t,
[][]byte{ [][]byte{
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpConstant, 2),
compiler.MakeInstruction(compiler.OpConstant, 65535), compiler.MakeInstruction(compiler.OpConstant, 65535),
}, },
`0000 ADD `0000 BINARYOP 11
0001 CONST 2 0002 CONST 2
0004 CONST 65535`) 0005 CONST 65535`)
assertInstructionString(t, assertInstructionString(t,
[][]byte{ [][]byte{
compiler.MakeInstruction(compiler.OpAdd), compiler.MakeInstruction(compiler.OpBinaryOp, 11),
compiler.MakeInstruction(compiler.OpGetLocal, 1), compiler.MakeInstruction(compiler.OpGetLocal, 1),
compiler.MakeInstruction(compiler.OpConstant, 2), compiler.MakeInstruction(compiler.OpConstant, 2),
compiler.MakeInstruction(compiler.OpConstant, 65535), compiler.MakeInstruction(compiler.OpConstant, 65535),
}, },
`0000 ADD `0000 BINARYOP 11
0001 GETL 1 0002 GETL 1
0003 CONST 2 0004 CONST 2
0006 CONST 65535`) 0007 CONST 65535`)
} }
func TestMakeInstruction(t *testing.T) { 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.OpPop)}, compiler.OpPop)
makeInstruction(t, []byte{byte(compiler.OpTrue)}, compiler.OpTrue) makeInstruction(t, []byte{byte(compiler.OpTrue)}, compiler.OpTrue)
makeInstruction(t, []byte{byte(compiler.OpFalse)}, compiler.OpFalse) 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) { func assertInstructionString(t *testing.T, instructions [][]byte, expected string) {

View file

@ -5,176 +5,137 @@ type Opcode = byte
// List of opcodes // List of opcodes
const ( const (
OpConstant Opcode = iota // Load constant OpConstant Opcode = iota // Load constant
OpAdd // Add OpBComplement // bitwise complement
OpSub // Sub OpPop // Pop
OpMul // Multiply OpTrue // Push true
OpDiv // Divide OpFalse // Push false
OpRem // Remainder OpEqual // Equal ==
OpBAnd // bitwise AND OpNotEqual // Not equal !=
OpBOr // bitwise OR OpMinus // Minus -
OpBXor // bitwise XOR OpLNot // Logical not !
OpBShiftLeft // bitwise shift left OpJumpFalsy // Jump if falsy
OpBShiftRight // bitwise shift right OpAndJump // Logical AND jump
OpBAndNot // bitwise AND NOT OpOrJump // Logical OR jump
OpBComplement // bitwise complement OpJump // Jump
OpPop // Pop OpNull // Push null
OpTrue // Push true OpArray // Array object
OpFalse // Push false OpMap // Map object
OpEqual // Equal == OpError // Error object
OpNotEqual // Not equal != OpImmutable // Immutable object
OpGreaterThan // Greater than >= OpIndex // Index operation
OpGreaterThanEqual // Greater than or equal to >= OpSliceIndex // Slice operation
OpMinus // Minus - OpCall // Call function
OpLNot // Logical not ! OpReturn // Return
OpJumpFalsy // Jump if falsy OpGetGlobal // Get global variable
OpAndJump // Logical AND jump OpSetGlobal // Set global variable
OpOrJump // Logical OR jump OpSetSelGlobal // Set global variable using selectors
OpJump // Jump OpGetLocal // Get local variable
OpNull // Push null OpSetLocal // Set local variable
OpArray // Array object OpDefineLocal // Define local variable
OpMap // Map object OpSetSelLocal // Set local variable using selectors
OpError // Error object OpGetFreePtr // Get free variable pointer object
OpImmutable // Immutable object OpGetFree // Get free variables
OpIndex // Index operation OpSetFree // Set free variables
OpSliceIndex // Slice operation OpGetLocalPtr // Get local variable as a pointer
OpCall // Call function OpSetSelFree // Set free variables using selectors
OpReturn // Return OpGetBuiltin // Get builtin function
OpReturnValue // Return value OpClosure // Push closure
OpGetGlobal // Get global variable OpIteratorInit // Iterator init
OpSetGlobal // Set global variable OpIteratorNext // Iterator next
OpSetSelGlobal // Set global variable using selectors OpIteratorKey // Iterator key
OpGetLocal // Get local variable OpIteratorValue // Iterator value
OpSetLocal // Set local variable OpBinaryOp // Binary Operation
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
) )
// OpcodeNames is opcode names. // OpcodeNames is opcode names.
var OpcodeNames = [...]string{ var OpcodeNames = [...]string{
OpConstant: "CONST", OpConstant: "CONST",
OpPop: "POP", OpPop: "POP",
OpTrue: "TRUE", OpTrue: "TRUE",
OpFalse: "FALSE", OpFalse: "FALSE",
OpAdd: "ADD", OpBComplement: "NEG",
OpSub: "SUB", OpEqual: "EQL",
OpMul: "MUL", OpNotEqual: "NEQ",
OpDiv: "DIV", OpMinus: "NEG",
OpRem: "REM", OpLNot: "NOT",
OpBAnd: "AND", OpJumpFalsy: "JMPF",
OpBOr: "OR", OpAndJump: "ANDJMP",
OpBXor: "XOR", OpOrJump: "ORJMP",
OpBAndNot: "ANDN", OpJump: "JMP",
OpBShiftLeft: "SHL", OpNull: "NULL",
OpBShiftRight: "SHR", OpGetGlobal: "GETG",
OpBComplement: "NEG", OpSetGlobal: "SETG",
OpEqual: "EQL", OpSetSelGlobal: "SETSG",
OpNotEqual: "NEQ", OpArray: "ARR",
OpGreaterThan: "GTR", OpMap: "MAP",
OpGreaterThanEqual: "GEQ", OpError: "ERROR",
OpMinus: "NEG", OpImmutable: "IMMUT",
OpLNot: "NOT", OpIndex: "INDEX",
OpJumpFalsy: "JMPF", OpSliceIndex: "SLICE",
OpAndJump: "ANDJMP", OpCall: "CALL",
OpOrJump: "ORJMP", OpReturn: "RET",
OpJump: "JMP", OpGetLocal: "GETL",
OpNull: "NULL", OpSetLocal: "SETL",
OpGetGlobal: "GETG", OpDefineLocal: "DEFL",
OpSetGlobal: "SETG", OpSetSelLocal: "SETSL",
OpSetSelGlobal: "SETSG", OpGetBuiltin: "BUILTIN",
OpArray: "ARR", OpClosure: "CLOSURE",
OpMap: "MAP", OpGetFreePtr: "GETFP",
OpError: "ERROR", OpGetFree: "GETF",
OpImmutable: "IMMUT", OpSetFree: "SETF",
OpIndex: "INDEX", OpGetLocalPtr: "GETLP",
OpSliceIndex: "SLICE", OpSetSelFree: "SETSF",
OpCall: "CALL", OpIteratorInit: "ITER",
OpReturn: "RET", OpIteratorNext: "ITNXT",
OpReturnValue: "RETVAL", OpIteratorKey: "ITKEY",
OpGetLocal: "GETL", OpIteratorValue: "ITVAL",
OpSetLocal: "SETL", OpBinaryOp: "BINARYOP",
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",
} }
// OpcodeOperands is the number of operands. // OpcodeOperands is the number of operands.
var OpcodeOperands = [...][]int{ var OpcodeOperands = [...][]int{
OpConstant: {2}, OpConstant: {2},
OpPop: {}, OpPop: {},
OpTrue: {}, OpTrue: {},
OpFalse: {}, OpFalse: {},
OpAdd: {}, OpBComplement: {},
OpSub: {}, OpEqual: {},
OpMul: {}, OpNotEqual: {},
OpDiv: {}, OpMinus: {},
OpRem: {}, OpLNot: {},
OpBAnd: {}, OpJumpFalsy: {2},
OpBOr: {}, OpAndJump: {2},
OpBXor: {}, OpOrJump: {2},
OpBAndNot: {}, OpJump: {2},
OpBShiftLeft: {}, OpNull: {},
OpBShiftRight: {}, OpGetGlobal: {2},
OpBComplement: {}, OpSetGlobal: {2},
OpEqual: {}, OpSetSelGlobal: {2, 1},
OpNotEqual: {}, OpArray: {2},
OpGreaterThan: {}, OpMap: {2},
OpGreaterThanEqual: {}, OpError: {},
OpMinus: {}, OpImmutable: {},
OpLNot: {}, OpIndex: {},
OpJumpFalsy: {2}, OpSliceIndex: {},
OpAndJump: {2}, OpCall: {1},
OpOrJump: {2}, OpReturn: {1},
OpJump: {2}, OpGetLocal: {1},
OpNull: {}, OpSetLocal: {1},
OpGetGlobal: {2}, OpDefineLocal: {1},
OpSetGlobal: {2}, OpSetSelLocal: {1, 1},
OpSetSelGlobal: {2, 1}, OpGetBuiltin: {1},
OpArray: {2}, OpClosure: {2, 1},
OpMap: {2}, OpGetFreePtr: {1},
OpError: {}, OpGetFree: {1},
OpImmutable: {}, OpSetFree: {1},
OpIndex: {}, OpGetLocalPtr: {1},
OpSliceIndex: {}, OpSetSelFree: {1, 1},
OpCall: {1}, OpIteratorInit: {},
OpReturn: {}, OpIteratorNext: {},
OpReturnValue: {}, OpIteratorKey: {},
OpGetLocal: {1}, OpIteratorValue: {},
OpSetLocal: {1}, OpBinaryOp: {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: {},
} }
// ReadOperands reads operands from the bytecode. // ReadOperands reads operands from the bytecode.

View file

@ -47,3 +47,14 @@ func (o *CompiledFunction) IsFalsy() bool {
func (o *CompiledFunction) Equals(x Object) bool { func (o *CompiledFunction) Equals(x Object) bool {
return false 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
}

View file

@ -24,21 +24,19 @@ const (
// VM is a virtual machine that executes the bytecode compiled by Compiler. // VM is a virtual machine that executes the bytecode compiled by Compiler.
type VM struct { type VM struct {
constants []objects.Object constants []objects.Object
stack []objects.Object stack [StackSize]objects.Object
sp int sp int
globals []objects.Object globals []objects.Object
fileSet *source.FileSet fileSet *source.FileSet
frames []Frame frames [MaxFrames]Frame
framesIndex int framesIndex int
curFrame *Frame curFrame *Frame
curInsts []byte curInsts []byte
curIPLimit int
ip int ip int
aborting int64 aborting int64
maxAllocs int64 maxAllocs int64
allocs int64 allocs int64
err error err error
errOffset int
} }
// NewVM creates a VM. // NewVM creates a VM.
@ -47,24 +45,22 @@ func NewVM(bytecode *compiler.Bytecode, globals []objects.Object, maxAllocs int6
globals = make([]objects.Object, GlobalsSize) globals = make([]objects.Object, GlobalsSize)
} }
frames := make([]Frame, MaxFrames) v := &VM{
frames[0].fn = bytecode.MainFunction
frames[0].ip = -1
return &VM{
constants: bytecode.Constants, constants: bytecode.Constants,
stack: make([]objects.Object, StackSize),
sp: 0, sp: 0,
globals: globals, globals: globals,
fileSet: bytecode.FileSet, fileSet: bytecode.FileSet,
frames: frames,
framesIndex: 1, framesIndex: 1,
curFrame: &(frames[0]),
curInsts: frames[0].fn.Instructions,
curIPLimit: len(frames[0].fn.Instructions) - 1,
ip: -1, ip: -1,
maxAllocs: maxAllocs, 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. // Abort aborts the execution.
@ -78,7 +74,6 @@ func (v *VM) Run() (err error) {
v.sp = 0 v.sp = 0
v.curFrame = &(v.frames[0]) v.curFrame = &(v.frames[0])
v.curInsts = v.curFrame.fn.Instructions v.curInsts = v.curFrame.fn.Instructions
v.curIPLimit = len(v.curInsts) - 1
v.framesIndex = 1 v.framesIndex = 1
v.ip = -1 v.ip = -1
v.allocs = v.maxAllocs + 1 v.allocs = v.maxAllocs + 1
@ -88,13 +83,13 @@ func (v *VM) Run() (err error) {
err = v.err err = v.err
if err != nil { 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) err = fmt.Errorf("Runtime Error: %s\n\tat %s", err.Error(), filePos)
for v.framesIndex > 1 { for v.framesIndex > 1 {
v.framesIndex-- v.framesIndex--
v.curFrame = &v.frames[v.framesIndex-1] 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) err = fmt.Errorf("%s\n\tat %s", err.Error(), filePos)
} }
return err return err
@ -109,80 +104,72 @@ func (v *VM) Run() (err error) {
} }
func (v *VM) run() { 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++ v.ip++
switch v.curInsts[v.ip] { switch v.curInsts[v.ip] {
case compiler.OpConstant: case compiler.OpConstant:
cidx := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2 v.ip += 2
cidx := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = v.constants[cidx] v.stack[v.sp] = v.constants[cidx]
v.sp++ v.sp++
case compiler.OpNull: case compiler.OpNull:
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = objects.UndefinedValue v.stack[v.sp] = objects.UndefinedValue
v.sp++ v.sp++
case compiler.OpAdd: case compiler.OpBinaryOp:
v.binaryOp(token.Add) v.ip++
right := v.stack[v.sp-1]
left := v.stack[v.sp-2]
case compiler.OpSub: tok := token.Token(v.curInsts[v.ip])
v.binaryOp(token.Sub) res, e := left.BinaryOp(tok, right)
if e != nil {
v.sp -= 2
case compiler.OpMul: if e == objects.ErrInvalidOperator {
v.binaryOp(token.Mul) v.err = fmt.Errorf("invalid operation: %s %s %s",
left.TypeName(), tok.String(), right.TypeName())
return
}
case compiler.OpDiv: v.err = e
v.binaryOp(token.Quo) return
}
case compiler.OpRem: v.allocs--
v.binaryOp(token.Rem) if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
case compiler.OpBAnd: v.stack[v.sp-2] = res
v.binaryOp(token.And) v.sp--
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)
case compiler.OpEqual: case compiler.OpEqual:
right := v.stack[v.sp-1] right := v.stack[v.sp-1]
left := v.stack[v.sp-2] left := v.stack[v.sp-2]
v.sp -= 2 v.sp -= 2
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
if left.Equals(right) { if left.Equals(right) {
v.stack[v.sp] = objects.TrueValue v.stack[v.sp] = objects.TrueValue
} else { } else {
@ -195,11 +182,6 @@ func (v *VM) run() {
left := v.stack[v.sp-2] left := v.stack[v.sp-2]
v.sp -= 2 v.sp -= 2
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
if left.Equals(right) { if left.Equals(right) {
v.stack[v.sp] = objects.FalseValue v.stack[v.sp] = objects.FalseValue
} else { } else {
@ -211,20 +193,10 @@ func (v *VM) run() {
v.sp-- v.sp--
case compiler.OpTrue: case compiler.OpTrue:
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = objects.TrueValue v.stack[v.sp] = objects.TrueValue
v.sp++ v.sp++
case compiler.OpFalse: case compiler.OpFalse:
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = objects.FalseValue v.stack[v.sp] = objects.FalseValue
v.sp++ v.sp++
@ -232,11 +204,6 @@ func (v *VM) run() {
operand := v.stack[v.sp-1] operand := v.stack[v.sp-1]
v.sp-- v.sp--
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
if operand.IsFalsy() { if operand.IsFalsy() {
v.stack[v.sp] = objects.TrueValue v.stack[v.sp] = objects.TrueValue
} else { } else {
@ -250,11 +217,6 @@ func (v *VM) run() {
switch x := operand.(type) { switch x := operand.(type) {
case *objects.Int: case *objects.Int:
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
var res objects.Object = &objects.Int{Value: ^x.Value} var res objects.Object = &objects.Int{Value: ^x.Value}
v.allocs-- v.allocs--
@ -276,11 +238,6 @@ func (v *VM) run() {
switch x := operand.(type) { switch x := operand.(type) {
case *objects.Int: case *objects.Int:
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
var res objects.Object = &objects.Int{Value: -x.Value} var res objects.Object = &objects.Int{Value: -x.Value}
v.allocs-- v.allocs--
@ -292,11 +249,6 @@ func (v *VM) run() {
v.stack[v.sp] = res v.stack[v.sp] = res
v.sp++ v.sp++
case *objects.Float: case *objects.Float:
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
var res objects.Object = &objects.Float{Value: -x.Value} var res objects.Object = &objects.Float{Value: -x.Value}
v.allocs-- v.allocs--
@ -313,36 +265,31 @@ func (v *VM) run() {
} }
case compiler.OpJumpFalsy: case compiler.OpJumpFalsy:
pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2 v.ip += 2
condition := v.stack[v.sp-1]
v.sp-- v.sp--
if v.stack[v.sp].IsFalsy() {
if condition.IsFalsy() { pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
v.ip = pos - 1 v.ip = pos - 1
} }
case compiler.OpAndJump: case compiler.OpAndJump:
pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2 v.ip += 2
condition := v.stack[v.sp-1] if v.stack[v.sp-1].IsFalsy() {
if condition.IsFalsy() { pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
v.ip = pos - 1 v.ip = pos - 1
} else { } else {
v.sp-- v.sp--
} }
case compiler.OpOrJump: case compiler.OpOrJump:
pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2 v.ip += 2
condition := v.stack[v.sp-1] if v.stack[v.sp-1].IsFalsy() {
if !condition.IsFalsy() {
v.ip = pos - 1
} else {
v.sp-- v.sp--
} else {
pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
v.ip = pos - 1
} }
case compiler.OpJump: case compiler.OpJump:
@ -350,17 +297,16 @@ func (v *VM) run() {
v.ip = pos - 1 v.ip = pos - 1
case compiler.OpSetGlobal: case compiler.OpSetGlobal:
globalIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2 v.ip += 2
v.sp-- v.sp--
globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
v.globals[globalIndex] = v.stack[v.sp] v.globals[globalIndex] = v.stack[v.sp]
case compiler.OpSetSelGlobal: 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 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 and RHS value
selectors := make([]objects.Object, numSelectors) selectors := make([]objects.Object, numSelectors)
@ -372,28 +318,22 @@ func (v *VM) run() {
v.sp -= numSelectors + 1 v.sp -= numSelectors + 1
if e := indexAssign(v.globals[globalIndex], val, selectors); e != nil { if e := indexAssign(v.globals[globalIndex], val, selectors); e != nil {
v.errOffset = 3
v.err = e v.err = e
return return
} }
case compiler.OpGetGlobal: case compiler.OpGetGlobal:
globalIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2 v.ip += 2
globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
val := v.globals[globalIndex] val := v.globals[globalIndex]
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = val v.stack[v.sp] = val
v.sp++ v.sp++
case compiler.OpArray: case compiler.OpArray:
numElements := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2 v.ip += 2
numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
var elements []objects.Object var elements []objects.Object
for i := v.sp - numElements; i < v.sp; i++ { for i := v.sp - numElements; i < v.sp; i++ {
@ -409,17 +349,12 @@ func (v *VM) run() {
return return
} }
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = arr v.stack[v.sp] = arr
v.sp++ v.sp++
case compiler.OpMap: case compiler.OpMap:
numElements := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2 v.ip += 2
numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
kv := make(map[string]objects.Object) kv := make(map[string]objects.Object)
for i := v.sp - numElements; i < v.sp; i += 2 { for i := v.sp - numElements; i < v.sp; i += 2 {
@ -437,11 +372,6 @@ func (v *VM) run() {
return return
} }
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = m v.stack[v.sp] = m
v.sp++ v.sp++
@ -512,11 +442,6 @@ func (v *VM) run() {
val = objects.UndefinedValue val = objects.UndefinedValue
} }
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = val v.stack[v.sp] = val
v.sp++ v.sp++
@ -527,11 +452,6 @@ func (v *VM) run() {
return return
} }
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = left.Value v.stack[v.sp] = left.Value
v.sp++ v.sp++
@ -586,11 +506,6 @@ func (v *VM) run() {
highIdx = numElements highIdx = numElements
} }
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]} var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]}
v.allocs-- v.allocs--
@ -631,11 +546,6 @@ func (v *VM) run() {
highIdx = numElements highIdx = numElements
} }
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]} var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]}
v.allocs-- v.allocs--
@ -676,11 +586,6 @@ func (v *VM) run() {
highIdx = numElements highIdx = numElements
} }
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
var val objects.Object = &objects.String{Value: left.Value[lowIdx:highIdx]} var val objects.Object = &objects.String{Value: left.Value[lowIdx:highIdx]}
v.allocs-- v.allocs--
@ -721,11 +626,6 @@ func (v *VM) run() {
highIdx = numElements highIdx = numElements
} }
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
var val objects.Object = &objects.Bytes{Value: left.Value[lowIdx:highIdx]} var val objects.Object = &objects.Bytes{Value: left.Value[lowIdx:highIdx]}
v.allocs-- v.allocs--
@ -747,7 +647,6 @@ func (v *VM) run() {
switch callee := value.(type) { switch callee := value.(type) {
case *objects.Closure: case *objects.Closure:
if numArgs != callee.Fn.NumParameters { if numArgs != callee.Fn.NumParameters {
v.errOffset = 1
v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d", v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d",
callee.Fn.NumParameters, numArgs) callee.Fn.NumParameters, numArgs)
return return
@ -756,7 +655,7 @@ func (v *VM) run() {
// test if it's tail-call // test if it's tail-call
if callee.Fn == v.curFrame.fn { // recursion if callee.Fn == v.curFrame.fn { // recursion
nextOp := v.curInsts[v.ip+1] nextOp := v.curInsts[v.ip+1]
if nextOp == compiler.OpReturnValue || if nextOp == compiler.OpReturn ||
(nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) { (nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) {
for p := 0; p < numArgs; p++ { for p := 0; p < numArgs; p++ {
v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-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.curFrame.basePointer = v.sp - numArgs
v.curInsts = callee.Fn.Instructions v.curInsts = callee.Fn.Instructions
v.ip = -1 v.ip = -1
v.curIPLimit = len(v.curInsts) - 1
v.framesIndex++ v.framesIndex++
v.sp = v.sp - numArgs + callee.Fn.NumLocals v.sp = v.sp - numArgs + callee.Fn.NumLocals
case *objects.CompiledFunction: case *objects.CompiledFunction:
if numArgs != callee.NumParameters { if numArgs != callee.NumParameters {
v.errOffset = 1
v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d", v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d",
callee.NumParameters, numArgs) callee.NumParameters, numArgs)
return return
@ -790,7 +687,7 @@ func (v *VM) run() {
// test if it's tail-call // test if it's tail-call
if callee == v.curFrame.fn { // recursion if callee == v.curFrame.fn { // recursion
nextOp := v.curInsts[v.ip+1] nextOp := v.curInsts[v.ip+1]
if nextOp == compiler.OpReturnValue || if nextOp == compiler.OpReturn ||
(nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) { (nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) {
for p := 0; p < numArgs; p++ { for p := 0; p < numArgs; p++ {
v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-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.curFrame.basePointer = v.sp - numArgs
v.curInsts = callee.Instructions v.curInsts = callee.Instructions
v.ip = -1 v.ip = -1
v.curIPLimit = len(v.curInsts) - 1
v.framesIndex++ v.framesIndex++
v.sp = v.sp - numArgs + callee.NumLocals v.sp = v.sp - numArgs + callee.NumLocals
@ -824,8 +720,6 @@ func (v *VM) run() {
// runtime error // runtime error
if e != nil { if e != nil {
v.errOffset = 1
if e == objects.ErrWrongNumArguments { if e == objects.ErrWrongNumArguments {
v.err = fmt.Errorf("wrong number of arguments in call to '%s'", v.err = fmt.Errorf("wrong number of arguments in call to '%s'",
value.TypeName()) value.TypeName())
@ -853,56 +747,39 @@ func (v *VM) run() {
return return
} }
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = ret v.stack[v.sp] = ret
v.sp++ v.sp++
default: default:
v.errOffset = 1
v.err = fmt.Errorf("not callable: %s", callee.TypeName()) v.err = fmt.Errorf("not callable: %s", callee.TypeName())
return return
} }
case compiler.OpReturnValue: case compiler.OpReturn:
retVal := v.stack[v.sp-1] 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.sp--
v.framesIndex-- v.framesIndex--
lastFrame := v.frames[v.framesIndex]
v.curFrame = &v.frames[v.framesIndex-1] v.curFrame = &v.frames[v.framesIndex-1]
v.curInsts = v.curFrame.fn.Instructions v.curInsts = v.curFrame.fn.Instructions
v.curIPLimit = len(v.curInsts) - 1
v.ip = v.curFrame.ip v.ip = v.curFrame.ip
//v.sp = lastFrame.basePointer - 1 //v.sp = lastFrame.basePointer - 1
v.sp = lastFrame.basePointer v.sp = v.frames[v.framesIndex].basePointer
// skip stack overflow check because (newSP) <= (oldSP) // skip stack overflow check because (newSP) <= (oldSP)
v.stack[v.sp-1] = retVal v.stack[v.sp-1] = retVal
//v.sp++ //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: case compiler.OpDefineLocal:
localIndex := int(v.curInsts[v.ip+1])
v.ip++ v.ip++
localIndex := int(v.curInsts[v.ip])
sp := v.curFrame.basePointer + localIndex sp := v.curFrame.basePointer + localIndex
@ -947,14 +824,13 @@ func (v *VM) run() {
sp := v.curFrame.basePointer + localIndex sp := v.curFrame.basePointer + localIndex
if e := indexAssign(v.stack[sp], val, selectors); e != nil { if e := indexAssign(v.stack[sp], val, selectors); e != nil {
v.errOffset = 2
v.err = e v.err = e
return return
} }
case compiler.OpGetLocal: case compiler.OpGetLocal:
localIndex := int(v.curInsts[v.ip+1])
v.ip++ v.ip++
localIndex := int(v.curInsts[v.ip])
val := v.stack[v.curFrame.basePointer+localIndex] val := v.stack[v.curFrame.basePointer+localIndex]
@ -962,34 +838,23 @@ func (v *VM) run() {
val = *obj.Value val = *obj.Value
} }
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = val v.stack[v.sp] = val
v.sp++ v.sp++
case compiler.OpGetBuiltin: case compiler.OpGetBuiltin:
builtinIndex := int(v.curInsts[v.ip+1])
v.ip++ v.ip++
builtinIndex := int(v.curInsts[v.ip])
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = objects.Builtins[builtinIndex] v.stack[v.sp] = objects.Builtins[builtinIndex]
v.sp++ v.sp++
case compiler.OpClosure: 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 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) fn, ok := v.constants[constIndex].(*objects.CompiledFunction)
if !ok { if !ok {
v.errOffset = 3
v.err = fmt.Errorf("not function: %s", fn.TypeName()) v.err = fmt.Errorf("not function: %s", fn.TypeName())
return return
} }
@ -1006,11 +871,6 @@ func (v *VM) run() {
v.sp -= numFree v.sp -= numFree
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
var cl = &objects.Closure{ var cl = &objects.Closure{
Fn: fn, Fn: fn,
Free: free, Free: free,
@ -1026,44 +886,34 @@ func (v *VM) run() {
v.sp++ v.sp++
case compiler.OpGetFreePtr: case compiler.OpGetFreePtr:
freeIndex := int(v.curInsts[v.ip+1])
v.ip++ v.ip++
freeIndex := int(v.curInsts[v.ip])
val := v.curFrame.freeVars[freeIndex] val := v.curFrame.freeVars[freeIndex]
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = val v.stack[v.sp] = val
v.sp++ v.sp++
case compiler.OpGetFree: case compiler.OpGetFree:
freeIndex := int(v.curInsts[v.ip+1])
v.ip++ v.ip++
freeIndex := int(v.curInsts[v.ip])
val := *v.curFrame.freeVars[freeIndex].Value val := *v.curFrame.freeVars[freeIndex].Value
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = val v.stack[v.sp] = val
v.sp++ v.sp++
case compiler.OpSetFree: case compiler.OpSetFree:
freeIndex := int(v.curInsts[v.ip+1])
v.ip++ v.ip++
freeIndex := int(v.curInsts[v.ip])
*v.curFrame.freeVars[freeIndex].Value = v.stack[v.sp-1] *v.curFrame.freeVars[freeIndex].Value = v.stack[v.sp-1]
v.sp-- v.sp--
case compiler.OpGetLocalPtr: case compiler.OpGetLocalPtr:
localIndex := int(v.curInsts[v.ip+1])
v.ip++ v.ip++
localIndex := int(v.curInsts[v.ip])
sp := v.curFrame.basePointer + localIndex sp := v.curFrame.basePointer + localIndex
val := v.stack[sp] val := v.stack[sp]
@ -1076,18 +926,13 @@ func (v *VM) run() {
v.stack[sp] = freeVar v.stack[sp] = freeVar
} }
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = freeVar v.stack[v.sp] = freeVar
v.sp++ v.sp++
case compiler.OpSetSelFree: case compiler.OpSetSelFree:
freeIndex := int(v.curInsts[v.ip+1])
numSelectors := int(v.curInsts[v.ip+2])
v.ip += 2 v.ip += 2
freeIndex := int(v.curInsts[v.ip-1])
numSelectors := int(v.curInsts[v.ip])
// selectors and RHS value // selectors and RHS value
selectors := make([]objects.Object, numSelectors) selectors := make([]objects.Object, numSelectors)
@ -1098,7 +943,6 @@ func (v *VM) run() {
v.sp -= numSelectors + 1 v.sp -= numSelectors + 1
if e := indexAssign(*v.curFrame.freeVars[freeIndex].Value, val, selectors); e != nil { if e := indexAssign(*v.curFrame.freeVars[freeIndex].Value, val, selectors); e != nil {
v.errOffset = 2
v.err = e v.err = e
return return
} }
@ -1123,11 +967,6 @@ func (v *VM) run() {
return return
} }
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = iterator v.stack[v.sp] = iterator
v.sp++ v.sp++
@ -1137,11 +976,6 @@ func (v *VM) run() {
hasMore := iterator.(objects.Iterator).Next() hasMore := iterator.(objects.Iterator).Next()
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
if hasMore { if hasMore {
v.stack[v.sp] = objects.TrueValue v.stack[v.sp] = objects.TrueValue
} else { } else {
@ -1155,11 +989,6 @@ func (v *VM) run() {
val := iterator.(objects.Iterator).Key() val := iterator.(objects.Iterator).Key()
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = val v.stack[v.sp] = val
v.sp++ v.sp++
@ -1169,16 +998,12 @@ func (v *VM) run() {
val := iterator.(objects.Iterator).Value() val := iterator.(objects.Iterator).Value()
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = val v.stack[v.sp] = val
v.sp++ v.sp++
default: 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 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--
}

View file

@ -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")
}