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