adder-script
Version:
Python like language to execute untrusted codes in browsers and Node.js.
153 lines (124 loc) • 4.56 kB
JavaScript
"use strict";
/**
* The Block class represent a chunk of code, made of statements and sub-blocks to execute by order.
* Do not confuse the Block with the Scope class; Scope is the runtime params, vars, registers, which are alive while executing the code,
* while blocks are the structure of a compiled, loaded code, persistent and const between executions.
*
* Author: Ronen Ness.
* Since: 2016
*/
// include jsface for classes
var jsface = require("./../dependencies/jsface"),
Class = jsface.Class,
extend = jsface.extend;
// include errors
var Errors = require("./../errors");
// require the executable class
var Executable = require("./executable");
// Block class
var Block = Class(Executable, {
// Block constructor
// @param context - context of program currently executed.
constructor: function(context)
{
// call base class
Block.$super.call(this, context, null);
// create executable (sub blocks and statements) queue
this._executables = [];
},
// execute the block statements.
execute: function()
{
// get current scope
var scope = this._context.getScope();
// iterate over executables (statements and sub blocks)
var lastExecutable = null;
for (var i = 0; i < this._executables.length; ++i)
{
// if "return" statement was called
if (scope.calledReturn) {
break;
}
// if "continue" or "break" statement was called (both raise the continue flag.
else if (scope.calledContinue) {
break;
}
// get current executable
var curr = this._executables[i];
// do some tests on last executable block / statement
if (lastExecutable)
{
// check if previous executable is a statement that cause a break
if (lastExecutable.isBreakingBlock)
{
return;
}
// check if current executable is a block that we need to skip
if (lastExecutable.skipFollowingBlock && curr.constructor === Block)
{
continue;
}
}
// execute child block / statement
this._context._interpreter.evalStatement(curr);
lastExecutable = curr;
}
},
// add statement to block.
addStatement: function(statement)
{
statement.setParentBlock(this, this._executables.length);
this._executables.push(statement);
this._lastStatement = statement;
},
// add sub block to block.
addBlock: function(block)
{
// get last executable to set its following block
var lastExecutable = this._executables[this._executables.length-1];
if (lastExecutable === undefined)
{
throw new Errors.SyntaxError("Unexpected new block indent!");
}
lastExecutable.setFollowingBlock(block);
// set block parent block and add to executables list
block.setParentBlock(this, this._executables.length);
this._executables.push(block);
},
// return all sub-blocks and statements.
getChildren: function()
{
return this._executables;
},
// return a debug representation of this block
getDebugBlocksView: function(indent) {
// default indent levels
indent = indent || 1;
// get indent spaces prefix
var indentPrefix = "";
for (var i = 0; i < indent; ++i) {indentPrefix += ' ';}
// return string
var ret = "block:" + "\n";
// iterate over executables and print them
for (var i = 0; i < this._executables.length; ++i)
{
// get current executable
var curr = this._executables[i];
// if block
if (curr.type === "block") {
ret += indentPrefix + curr.getDebugBlocksView(indent + 1) + "\n";
}
else {
ret += indentPrefix + curr.getRepr() + "\n";
}
}
// add block closure
ret += indentPrefix + "end_block" + "\n";
// return the result string
return ret;
},
// executable type
type: "block",
});
// export the scope class
module.exports = Block;