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