|
@@ -18,17 +18,10 @@ import { StoreValueRef } from './store/value/store_value_ref';
|
|
|
import { ArrayStoreValue } from './store/value/array_store_value';
|
|
|
import { ArrayStoreValueRef } from './store/value/array_store_value_ref';
|
|
|
import { StoreValueAddress } from './store/value/store_value_address';
|
|
|
+import { LocalizedStrings } from '../services/localizedStringsService';
|
|
|
|
|
|
export class IVProgProcessor {
|
|
|
|
|
|
- static get LOOP_TIMEOUT () {
|
|
|
- return Config.loopTimeout;
|
|
|
- }
|
|
|
-
|
|
|
- static set LOOP_TIMEOUT (ms) {
|
|
|
- Config.setConfig({loopTimeout: ms});
|
|
|
- }
|
|
|
-
|
|
|
static get MAIN_INTERNAL_ID () {
|
|
|
return "$main";
|
|
|
}
|
|
@@ -40,12 +33,14 @@ export class IVProgProcessor {
|
|
|
this.context = [Context.BASE];
|
|
|
this.input = null;
|
|
|
this.forceKill = false;
|
|
|
- this.loopTimers = [];
|
|
|
this.output = null;
|
|
|
+ this.mode = Modes.RUN;
|
|
|
/**
|
|
|
* Stores the sourceInfo of every function call, command or expression
|
|
|
*/
|
|
|
this.function_call_stack = [];
|
|
|
+ this.instruction_count = 0;
|
|
|
+ this.function_call_count = 0;
|
|
|
}
|
|
|
|
|
|
registerInput (input) {
|
|
@@ -86,6 +81,8 @@ export class IVProgProcessor {
|
|
|
this.globalStore = new Store("$global");
|
|
|
this.stores = [this.globalStore];
|
|
|
this.context = [Context.BASE];
|
|
|
+ this.instruction_count = 0;
|
|
|
+ this.mode = Modes.RUN;
|
|
|
}
|
|
|
|
|
|
interpretAST () {
|
|
@@ -134,17 +131,24 @@ export class IVProgProcessor {
|
|
|
const funcName = func.isMain ? IVProgProcessor.MAIN_INTERNAL_ID : func.name;
|
|
|
const funcStore = new Store(funcName);
|
|
|
funcStore.extendStore(this.globalStore);
|
|
|
- const newFuncStore$ = this.associateParameters(func.formalParameters, actualParameters, store, funcStore);
|
|
|
- return newFuncStore$.then(sto => {
|
|
|
- this.context.push(Context.FUNCTION);
|
|
|
- this.stores.push(sto);
|
|
|
- return this.executeCommands(sto, func.variablesDeclarations)
|
|
|
- .then(stoWithVars => this.executeCommands(stoWithVars, func.commands)).then(finalSto => {
|
|
|
- this.stores.pop();
|
|
|
- this.context.pop();
|
|
|
- return finalSto;
|
|
|
- });
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const run_lambda = () => {
|
|
|
+ const newFuncStore$ = this.associateParameters(func.formalParameters, actualParameters, store, funcStore);
|
|
|
+ newFuncStore$.then(sto => {
|
|
|
+ this.context.push(Context.FUNCTION);
|
|
|
+ this.stores.push(sto);
|
|
|
+ return this.executeCommands(sto, func.variablesDeclarations)
|
|
|
+ .then(stoWithVars => this.executeCommands(stoWithVars, func.commands)).then(finalSto => {
|
|
|
+ this.stores.pop();
|
|
|
+ this.context.pop();
|
|
|
+ return finalSto;
|
|
|
+ });
|
|
|
+ }).then(resolve)
|
|
|
+ .catch(reject);
|
|
|
+ }
|
|
|
+ run_lambda();
|
|
|
});
|
|
|
+
|
|
|
}
|
|
|
|
|
|
associateParameters (formal_params, effective_params, caller_store, callee_store) {
|
|
@@ -220,42 +224,57 @@ export class IVProgProcessor {
|
|
|
}
|
|
|
|
|
|
executeCommand (store, cmd) {
|
|
|
- if(this.forceKill) {
|
|
|
- return Promise.reject("FORCED_KILL!");
|
|
|
- } else if (store.mode === Modes.PAUSE) {
|
|
|
- return Promise.resolve(this.executeCommand(store, cmd));
|
|
|
- } else if(store.mode === Modes.RETURN) {
|
|
|
- return Promise.resolve(store);
|
|
|
- } else if(this.checkContext(Context.BREAKABLE) && store.mode === Modes.BREAK) {
|
|
|
- return Promise.resolve(store);
|
|
|
- }
|
|
|
- if (cmd instanceof Commands.Declaration) {
|
|
|
- return this.executeDeclaration(store, cmd);
|
|
|
- } else if (cmd instanceof Commands.ArrayIndexAssign) {
|
|
|
- return this.executeArrayIndexAssign(store, cmd);
|
|
|
- } else if (cmd instanceof Commands.Assign) {
|
|
|
- return this.executeAssign(store, cmd);
|
|
|
- } else if (cmd instanceof Commands.Break) {
|
|
|
- return this.executeBreak(store, cmd);
|
|
|
- } else if (cmd instanceof Commands.Return) {
|
|
|
- return this.executeReturn(store, cmd);
|
|
|
- } else if (cmd instanceof Commands.IfThenElse) {
|
|
|
- return this.executeIfThenElse(store, cmd);
|
|
|
- } else if (cmd instanceof Commands.RepeatUntil) {
|
|
|
- return this.executeRepeatUntil(store, cmd);
|
|
|
- } else if (cmd instanceof Commands.While) {
|
|
|
- return this.executeWhile(store, cmd);
|
|
|
- } else if (cmd instanceof Commands.For) {
|
|
|
- return this.executeFor(store, cmd);
|
|
|
- } else if (cmd instanceof Commands.Switch) {
|
|
|
- return this.executeSwitch(store, cmd);
|
|
|
- } else if (cmd instanceof Expressions.FunctionCall) {
|
|
|
- return this.executeFunctionCall(store, cmd);
|
|
|
- } else if (cmd instanceof Commands.SysCall) {
|
|
|
- return this.executeSysCall(store, cmd);
|
|
|
- } else {
|
|
|
- return Promise.reject(ProcessorErrorFactory.unknown_command(cmd.sourceInfo))
|
|
|
- }
|
|
|
+ this.instruction_count += 1;
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const command_lambda = () => {
|
|
|
+ if(this.instruction_count >= Config.max_instruction_count) {
|
|
|
+ return reject(ProcessorErrorFactory.exceed_max_instructions());
|
|
|
+ } else if(this.forceKill) {
|
|
|
+ return reject("FORCED_KILL!");
|
|
|
+ } else if (store.mode === Modes.PAUSE) {
|
|
|
+ return resolve(this.executeCommand(store, cmd));
|
|
|
+ } else if(store.mode === Modes.RETURN) {
|
|
|
+ return resolve(store);
|
|
|
+ } else if(this.checkContext(Context.BREAKABLE) && store.mode === Modes.BREAK) {
|
|
|
+ return resolve(store);
|
|
|
+ } else if (this.mode === Modes.ABORT) {
|
|
|
+ return reject(LocalizedStrings.getMessage('aborted_execution'));
|
|
|
+ }
|
|
|
+ if (cmd instanceof Commands.Declaration) {
|
|
|
+ return resolve(this.executeDeclaration(store, cmd));
|
|
|
+ } else if (cmd instanceof Commands.ArrayIndexAssign) {
|
|
|
+ return resolve(this.executeArrayIndexAssign(store, cmd));
|
|
|
+ } else if (cmd instanceof Commands.Assign) {
|
|
|
+ return resolve(this.executeAssign(store, cmd));
|
|
|
+ } else if (cmd instanceof Commands.Break) {
|
|
|
+ return resolve(this.executeBreak(store, cmd));
|
|
|
+ } else if (cmd instanceof Commands.Return) {
|
|
|
+ return resolve(this.executeReturn(store, cmd));
|
|
|
+ } else if (cmd instanceof Commands.IfThenElse) {
|
|
|
+ return resolve(this.executeIfThenElse(store, cmd));
|
|
|
+ } else if (cmd instanceof Commands.RepeatUntil) {
|
|
|
+ return resolve(this.executeRepeatUntil(store, cmd));
|
|
|
+ } else if (cmd instanceof Commands.While) {
|
|
|
+ return resolve(this.executeWhile(store, cmd));
|
|
|
+ } else if (cmd instanceof Commands.For) {
|
|
|
+ return resolve(this.executeFor(store, cmd));
|
|
|
+ } else if (cmd instanceof Commands.Switch) {
|
|
|
+ return resolve(this.executeSwitch(store, cmd));
|
|
|
+ } else if (cmd instanceof Expressions.FunctionCall) {
|
|
|
+ return resolve(this.executeFunctionCall(store, cmd));
|
|
|
+ } else if (cmd instanceof Commands.SysCall) {
|
|
|
+ return resolve(this.executeSysCall(store, cmd));
|
|
|
+ } else {
|
|
|
+ return reject(ProcessorErrorFactory.unknown_command(cmd.sourceInfo))
|
|
|
+ }
|
|
|
+ };
|
|
|
+ if(this.instruction_count % Config.suspend_threshold == 0) {
|
|
|
+ //every 100th command should briefly delay its execution in order to allow the browser to process other things
|
|
|
+ setTimeout(command_lambda, 5);
|
|
|
+ } else {
|
|
|
+ command_lambda();
|
|
|
+ }
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
executeSysCall (store, cmd) {
|
|
@@ -270,9 +289,9 @@ export class IVProgProcessor {
|
|
|
} else {
|
|
|
func = this.findFunction(cmd.id);
|
|
|
}
|
|
|
- if(this.function_call_stack.length >= Config.max_call_stack) {
|
|
|
- return Promise.reject(ProcessorErrorFactory.exceeded_recursive_calls(cmd.sourceInfo));
|
|
|
- }
|
|
|
+ // if(this.function_call_stack.length >= Config.max_call_stack) {
|
|
|
+ // return Promise.reject(ProcessorErrorFactory.exceeded_recursive_calls(cmd.sourceInfo));
|
|
|
+ // }
|
|
|
this.function_call_stack.push(cmd.sourceInfo);
|
|
|
return this.runFunction(func, cmd.actualParameters, store)
|
|
|
.then(sto => {
|
|
@@ -364,14 +383,12 @@ export class IVProgProcessor {
|
|
|
|
|
|
executeRepeatUntil (store, cmd) {
|
|
|
try {
|
|
|
- this.loopTimers.push(Date.now());
|
|
|
this.context.push(Context.BREAKABLE);
|
|
|
const $newStore = this.executeCommands(store, cmd.commands);
|
|
|
return $newStore.then(sto => {
|
|
|
if(sto.mode === Modes.BREAK) {
|
|
|
this.context.pop();
|
|
|
sto.mode = Modes.RUN;
|
|
|
- this.loopTimers.pop();
|
|
|
return sto;
|
|
|
}
|
|
|
const $value = this.evaluateExpression(sto, cmd.expression);
|
|
@@ -381,17 +398,9 @@ export class IVProgProcessor {
|
|
|
}
|
|
|
if (!vl.get()) {
|
|
|
this.context.pop();
|
|
|
- if(this.loopTimers.length > 0) {
|
|
|
- const time = this.loopTimers[0];
|
|
|
- if(Date.now() - time >= IVProgProcessor.LOOP_TIMEOUT) {
|
|
|
- this.forceKill = true;
|
|
|
- return Promise.reject(ProcessorErrorFactory.endless_loop_full(cmd.sourceInfo));
|
|
|
- }
|
|
|
- }
|
|
|
return this.executeCommand(sto, cmd);
|
|
|
} else {
|
|
|
this.context.pop();
|
|
|
- this.loopTimers.pop();
|
|
|
return sto;
|
|
|
}
|
|
|
})
|
|
@@ -403,7 +412,6 @@ export class IVProgProcessor {
|
|
|
|
|
|
executeWhile (store, cmd) {
|
|
|
try {
|
|
|
- this.loopTimers.push(Date.now());
|
|
|
this.context.push(Context.BREAKABLE);
|
|
|
const $value = this.evaluateExpression(store, cmd.expression);
|
|
|
return $value.then(vl => {
|
|
@@ -413,22 +421,13 @@ export class IVProgProcessor {
|
|
|
return $newStore.then(sto => {
|
|
|
this.context.pop();
|
|
|
if (sto.mode === Modes.BREAK) {
|
|
|
- this.loopTimers.pop();
|
|
|
sto.mode = Modes.RUN;
|
|
|
return sto;
|
|
|
}
|
|
|
- if (this.loopTimers.length > 0) {
|
|
|
- const time = this.loopTimers[0];
|
|
|
- if(Date.now() - time >= IVProgProcessor.LOOP_TIMEOUT) {
|
|
|
- this.forceKill = true;
|
|
|
- return Promise.reject(ProcessorErrorFactory.endless_loop_full(cmd.sourceInfo));
|
|
|
- }
|
|
|
- }
|
|
|
return this.executeCommand(sto, cmd);
|
|
|
});
|
|
|
} else {
|
|
|
this.context.pop();
|
|
|
- this.loopTimers.pop();
|
|
|
return store;
|
|
|
}
|
|
|
} else {
|
|
@@ -470,7 +469,7 @@ export class IVProgProcessor {
|
|
|
try {
|
|
|
const funcName = store.name === IVProgProcessor.MAIN_INTERNAL_ID ?
|
|
|
LanguageDefinedFunction.getMainFunctionName() : store.name;
|
|
|
- console.log(funcName, store.name === IVProgProcessor.MAIN_INTERNAL_ID);
|
|
|
+ // console.log(funcName, store.name === IVProgProcessor.MAIN_INTERNAL_ID);
|
|
|
const func = this.findFunction(store.name);
|
|
|
const funcType = func.returnType;
|
|
|
const $value = this.evaluateExpression(store, cmd.expression);
|
|
@@ -714,28 +713,46 @@ export class IVProgProcessor {
|
|
|
}
|
|
|
|
|
|
evaluateExpression (store, exp) {
|
|
|
- if (exp instanceof Expressions.UnaryApp) {
|
|
|
- return this.evaluateUnaryApp(store, exp);
|
|
|
- } else if (exp instanceof Expressions.InfixApp) {
|
|
|
- return this.evaluateInfixApp(store, exp);
|
|
|
- } else if (exp instanceof Expressions.ArrayAccess) {
|
|
|
- return this.evaluateArrayAccess(store, exp);
|
|
|
- } else if (exp instanceof Expressions.VariableLiteral) {
|
|
|
- return this.evaluateVariableLiteral(store, exp);
|
|
|
- } else if (exp instanceof Expressions.IntLiteral) {
|
|
|
- return this.evaluateLiteral(store, exp);
|
|
|
- } else if (exp instanceof Expressions.RealLiteral) {
|
|
|
- return this.evaluateLiteral(store, exp);
|
|
|
- } else if (exp instanceof Expressions.BoolLiteral) {
|
|
|
- return this.evaluateLiteral(store, exp);
|
|
|
- } else if (exp instanceof Expressions.StringLiteral) {
|
|
|
- return this.evaluateLiteral(store, exp);
|
|
|
- } else if (exp instanceof Expressions.ArrayLiteral) {
|
|
|
- return Promise.reject(new Error("Internal Error: The system should not eval an array literal."))
|
|
|
- } else if (exp instanceof Expressions.FunctionCall) {
|
|
|
- return this.evaluateFunctionCall(store, exp);
|
|
|
- }
|
|
|
- return Promise.resolve(null);
|
|
|
+ this.instruction_count += 1;
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const expression_lambda = () => {
|
|
|
+ if (this.mode === Modes.ABORT) {
|
|
|
+ return reject(LocalizedStrings.getMessage('aborted_execution'));
|
|
|
+ }
|
|
|
+ if(this.instruction_count >= Config.max_instruction_count) {
|
|
|
+ return reject(new Error("Número de instruções excedeu o limite definido. Verifique se seu código não possui laços infinitos ou muitas chamadas de funções recursivas."))
|
|
|
+ }
|
|
|
+ if (exp instanceof Expressions.UnaryApp) {
|
|
|
+ return resolve(this.evaluateUnaryApp(store, exp));
|
|
|
+ } else if (exp instanceof Expressions.InfixApp) {
|
|
|
+ return resolve(this.evaluateInfixApp(store, exp));
|
|
|
+ } else if (exp instanceof Expressions.ArrayAccess) {
|
|
|
+ return resolve(this.evaluateArrayAccess(store, exp));
|
|
|
+ } else if (exp instanceof Expressions.VariableLiteral) {
|
|
|
+ return resolve(this.evaluateVariableLiteral(store, exp));
|
|
|
+ } else if (exp instanceof Expressions.IntLiteral) {
|
|
|
+ return resolve(this.evaluateLiteral(store, exp));
|
|
|
+ } else if (exp instanceof Expressions.RealLiteral) {
|
|
|
+ return resolve(this.evaluateLiteral(store, exp));
|
|
|
+ } else if (exp instanceof Expressions.BoolLiteral) {
|
|
|
+ return resolve(this.evaluateLiteral(store, exp));
|
|
|
+ } else if (exp instanceof Expressions.StringLiteral) {
|
|
|
+ return resolve(this.evaluateLiteral(store, exp));
|
|
|
+ } else if (exp instanceof Expressions.ArrayLiteral) {
|
|
|
+ return reject(new Error("Internal Error: The system should not eval an array literal."))
|
|
|
+ } else if (exp instanceof Expressions.FunctionCall) {
|
|
|
+ return resolve(this.evaluateFunctionCall(store, exp));
|
|
|
+ }
|
|
|
+ return resolve(null);
|
|
|
+ };
|
|
|
+ if(this.instruction_count % Config.suspend_threshold == 0) {
|
|
|
+ //every 100th command should briefly delay its execution in order to allow the browser to process other things
|
|
|
+ setTimeout(expression_lambda, 5);
|
|
|
+ } else {
|
|
|
+ expression_lambda();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
}
|
|
|
|
|
|
evaluateFunctionCall (store, exp) {
|