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.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())
}

View file

@ -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 {

View file

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

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
if !moduleCompiler.lastInstructionIs(OpReturnValue) {
if !moduleCompiler.lastInstructionIs(OpReturn) {
moduleCompiler.emit(nil, OpReturn)
}

View file

@ -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),

View file

@ -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) {

View file

@ -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.

View file

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

View file

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

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