fix: various small behavioural issues
fix: pesky try-catch logic
This commit is contained in:
parent
0ae24148d8
commit
4f82574b8c
@ -17,6 +17,10 @@ public class ChangeStatement extends Statement {
|
||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, true);
|
||||
if (!pollute) target.add(Instruction.discard().locate(loc()));
|
||||
else if (postfix) {
|
||||
target.add(Instruction.loadValue(addAmount));
|
||||
target.add(Instruction.operation(Operation.SUBTRACT));
|
||||
}
|
||||
}
|
||||
|
||||
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {
|
||||
|
@ -3,9 +3,7 @@ package me.topchetoeu.jscript.engine.frame;
|
||||
import java.util.Stack;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.engine.Context;
|
||||
import me.topchetoeu.jscript.engine.StackData;
|
||||
import me.topchetoeu.jscript.engine.scope.LocalScope;
|
||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||
@ -87,6 +85,7 @@ public class CodeFrame {
|
||||
|
||||
public void addTry(int n, int catchN, int finallyN) {
|
||||
var res = new TryCtx(codePtr + 1, n, catchN, finallyN);
|
||||
if (!tryStack.empty()) res.err = tryStack.peek().err;
|
||||
|
||||
tryStack.add(res);
|
||||
}
|
||||
@ -125,34 +124,31 @@ public class CodeFrame {
|
||||
}
|
||||
|
||||
private void setCause(Context ctx, EngineException err, EngineException cause) {
|
||||
// err.cause = cause;
|
||||
err.setCause(cause);
|
||||
}
|
||||
private Object nextNoTry(Context ctx, Instruction instr) {
|
||||
if (Thread.currentThread().isInterrupted()) throw new InterruptException();
|
||||
if (codePtr < 0 || codePtr >= function.body.length) return null;
|
||||
|
||||
if (instr.location != null) prevLoc = instr.location;
|
||||
|
||||
try {
|
||||
this.jumpFlag = false;
|
||||
return Runners.exec(ctx, instr, this);
|
||||
}
|
||||
catch (EngineException e) {
|
||||
throw e.add(function.name, prevLoc).setCtx(function.environment, ctx.engine);
|
||||
}
|
||||
}
|
||||
|
||||
public Object next(Context ctx, Object value, Object returnValue, EngineException error) {
|
||||
if (value != Runners.NO_RETURN) push(ctx, value);
|
||||
var debugger = StackData.getDebugger(ctx);
|
||||
|
||||
if (returnValue == Runners.NO_RETURN && error == null) {
|
||||
try {
|
||||
var instr = function.body[codePtr];
|
||||
if (Thread.currentThread().isInterrupted()) throw new InterruptException();
|
||||
|
||||
if (debugger != null) debugger.onInstruction(ctx, this, instr, Runners.NO_RETURN, null, false);
|
||||
returnValue = nextNoTry(ctx, instr);
|
||||
var instr = function.body[codePtr];
|
||||
ctx.engine.onInstruction(ctx, this, instr, Runners.NO_RETURN, null, false);
|
||||
|
||||
if (codePtr < 0 || codePtr >= function.body.length) returnValue = null;
|
||||
else {
|
||||
if (instr.location != null) prevLoc = instr.location;
|
||||
|
||||
try {
|
||||
this.jumpFlag = false;
|
||||
returnValue = Runners.exec(ctx, instr, this);
|
||||
}
|
||||
catch (EngineException e) {
|
||||
error = e.add(function.name, prevLoc).setCtx(function.environment, ctx.engine);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (EngineException e) { error = e; }
|
||||
}
|
||||
@ -192,11 +188,11 @@ public class CodeFrame {
|
||||
break;
|
||||
case TryCtx.STATE_CATCH:
|
||||
if (error != null) {
|
||||
setCause(ctx, error, tryCtx.err);
|
||||
if (tryCtx.hasFinally) {
|
||||
tryCtx.err = error;
|
||||
newState = TryCtx.STATE_FINALLY_THREW;
|
||||
}
|
||||
setCause(ctx, error, tryCtx.err);
|
||||
break;
|
||||
}
|
||||
else if (returnValue != Runners.NO_RETURN) {
|
||||
@ -218,27 +214,31 @@ public class CodeFrame {
|
||||
case TryCtx.STATE_FINALLY_THREW:
|
||||
if (error != null) setCause(ctx, error, tryCtx.err);
|
||||
else if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) error = tryCtx.err;
|
||||
else return Runners.NO_RETURN;
|
||||
else if (returnValue == Runners.NO_RETURN) return Runners.NO_RETURN;
|
||||
break;
|
||||
case TryCtx.STATE_FINALLY_RETURNED:
|
||||
if (error != null) setCause(ctx, error, tryCtx.err);
|
||||
if (returnValue == Runners.NO_RETURN) {
|
||||
if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) returnValue = tryCtx.retVal;
|
||||
else return Runners.NO_RETURN;
|
||||
}
|
||||
break;
|
||||
case TryCtx.STATE_FINALLY_JUMPED:
|
||||
if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) {
|
||||
if (error != null) setCause(ctx, error, tryCtx.err);
|
||||
else if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) {
|
||||
if (!jumpFlag) codePtr = tryCtx.jumpPtr;
|
||||
else codePtr = tryCtx.end;
|
||||
}
|
||||
else return Runners.NO_RETURN;
|
||||
else if (returnValue == Runners.NO_RETURN) return Runners.NO_RETURN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (tryCtx.state == TryCtx.STATE_CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
||||
|
||||
if (newState == -1) {
|
||||
var err = tryCtx.err;
|
||||
tryStack.pop();
|
||||
if (!tryStack.isEmpty()) tryStack.peek().err = err;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -247,7 +247,7 @@ public class CodeFrame {
|
||||
case TryCtx.STATE_CATCH:
|
||||
scope.catchVars.add(new ValueVariable(false, tryCtx.err.value));
|
||||
codePtr = tryCtx.catchStart;
|
||||
if (debugger != null) debugger.onInstruction(ctx, this, function.body[codePtr], null, error, true);
|
||||
ctx.engine.onInstruction(ctx, this, function.body[codePtr], null, error, true);
|
||||
break;
|
||||
default:
|
||||
codePtr = tryCtx.finallyStart;
|
||||
@ -255,13 +255,13 @@ public class CodeFrame {
|
||||
|
||||
return Runners.NO_RETURN;
|
||||
}
|
||||
|
||||
|
||||
if (error != null) {
|
||||
if (debugger != null) debugger.onInstruction(ctx, this, function.body[codePtr], null, error, false);
|
||||
ctx.engine.onInstruction(ctx, this, function.body[codePtr], null, error, false);
|
||||
throw error;
|
||||
}
|
||||
if (returnValue != Runners.NO_RETURN) {
|
||||
if (debugger != null) debugger.onInstruction(ctx, this, function.body[codePtr], returnValue, null, false);
|
||||
ctx.engine.onInstruction(ctx, this, function.body[codePtr], returnValue, null, false);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
@ -26,16 +26,12 @@ public class Runners {
|
||||
throw EngineException.ofSyntax((String)instr.get(0));
|
||||
}
|
||||
|
||||
private static Object call(Context ctx, Object func, Object thisArg, Object ...args) {
|
||||
return Values.call(ctx, func, thisArg, args);
|
||||
}
|
||||
|
||||
public static Object execCall(Context ctx, Instruction instr, CodeFrame frame) {
|
||||
var callArgs = frame.take(instr.get(0));
|
||||
var func = frame.pop();
|
||||
var thisArg = frame.pop();
|
||||
|
||||
frame.push(ctx, call(ctx, func, thisArg, callArgs));
|
||||
frame.push(ctx, Values.call(ctx, func, thisArg, callArgs));
|
||||
|
||||
frame.codePtr++;
|
||||
return NO_RETURN;
|
||||
@ -46,17 +42,6 @@ public class Runners {
|
||||
|
||||
frame.push(ctx, Values.callNew(ctx, funcObj, callArgs));
|
||||
|
||||
// if (Values.isFunction(funcObj) && Values.function(funcObj).special) {
|
||||
// frame.push(ctx, call(ctx, funcObj, null, callArgs));
|
||||
// }
|
||||
// else {
|
||||
// var proto = Values.getMember(ctx, funcObj, "prototype");
|
||||
// var obj = new ObjectValue();
|
||||
// obj.setPrototype(ctx, proto);
|
||||
// call(ctx, funcObj, obj, callArgs);
|
||||
// frame.push(ctx, obj);
|
||||
// }
|
||||
|
||||
frame.codePtr++;
|
||||
return NO_RETURN;
|
||||
}
|
||||
|
@ -8,8 +8,7 @@ public class LocalScope {
|
||||
public final ArrayList<ValueVariable> catchVars = new ArrayList<>();
|
||||
|
||||
public ValueVariable get(int i) {
|
||||
if (i >= locals.length)
|
||||
return catchVars.get(i - locals.length);
|
||||
if (i >= locals.length) return catchVars.get(i - locals.length);
|
||||
if (i >= 0) return locals[i];
|
||||
else return captures[~i];
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
|
||||
if (i >= size) size = i + 1;
|
||||
}
|
||||
public boolean has(int i) {
|
||||
return i >= 0 && i < values.length && values[i] != null;
|
||||
return i >= 0 && i < size && values[i] != null;
|
||||
}
|
||||
public void remove(int i) {
|
||||
if (i < 0 || i >= values.length) return;
|
||||
@ -85,8 +85,9 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
|
||||
public void copyTo(Context ctx, ArrayValue arr, int sourceStart, int destStart, int count) {
|
||||
// Iterate in reverse to reallocate at most once
|
||||
for (var i = count - 1; i >= 0; i--) {
|
||||
if (i + sourceStart < 0 || i + sourceStart >= size) arr.set(ctx, i + destStart, null);
|
||||
if (i + sourceStart < 0 || i + sourceStart >= size) arr.remove(i + destStart);
|
||||
if (values[i + sourceStart] == UNDEFINED) arr.set(ctx, i + destStart, null);
|
||||
else if (values[i + sourceStart] == null) arr.remove(i + destStart);
|
||||
else arr.set(ctx, i + destStart, values[i + sourceStart]);
|
||||
}
|
||||
}
|
||||
|
@ -103,8 +103,7 @@ public class ObjectValue {
|
||||
) return true;
|
||||
|
||||
if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false;
|
||||
if (!memberConfigurable(key))
|
||||
return false;
|
||||
if (!memberConfigurable(key)) return false;
|
||||
|
||||
nonWritableSet.remove(key);
|
||||
nonEnumerableSet.remove(key);
|
||||
|
@ -258,8 +258,7 @@ public class Values {
|
||||
|
||||
public static Object getMember(Context ctx, Object obj, Object key) {
|
||||
obj = normalize(ctx, obj); key = normalize(ctx, key);
|
||||
if (obj == null)
|
||||
throw new IllegalArgumentException("Tried to access member of undefined.");
|
||||
if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined.");
|
||||
if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null.");
|
||||
if (isObject(obj)) return object(obj).getMember(ctx, key);
|
||||
|
||||
@ -279,8 +278,7 @@ public class Values {
|
||||
}
|
||||
public static boolean setMember(Context ctx, Object obj, Object key, Object val) {
|
||||
obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val);
|
||||
if (obj == null)
|
||||
throw EngineException.ofType("Tried to access member of undefined.");
|
||||
if (obj == null) throw EngineException.ofType("Tried to access member of undefined.");
|
||||
if (obj == NULL) throw EngineException.ofType("Tried to access member of null.");
|
||||
if (key.equals("__proto__")) return setPrototype(ctx, obj, val);
|
||||
if (isObject(obj)) return object(obj).setMember(ctx, key, val, false);
|
||||
@ -369,8 +367,7 @@ public class Values {
|
||||
}
|
||||
|
||||
public static Object call(Context ctx, Object func, Object thisArg, Object ...args) {
|
||||
if (!isFunction(func))
|
||||
throw EngineException.ofType("Tried to call a non-function value.");
|
||||
if (!isFunction(func)) throw EngineException.ofType("Tried to call a non-function value.");
|
||||
return function(func).call(ctx, thisArg, args);
|
||||
}
|
||||
public static Object callNew(Context ctx, Object func, Object ...args) {
|
||||
@ -524,8 +521,6 @@ public class Values {
|
||||
public static Iterable<Object> toJavaIterable(Context ctx, Object obj) {
|
||||
return () -> {
|
||||
try {
|
||||
var _ctx = ctx;
|
||||
var _obj = obj;
|
||||
var symbol = ctx.environment().symbol("Symbol.iterator");
|
||||
|
||||
var iteratorFunc = getMember(ctx, obj, symbol);
|
||||
@ -587,7 +582,11 @@ public class Values {
|
||||
|
||||
res.defineProperty(ctx, "next", new NativeFunction("", (_ctx, _th, _args) -> {
|
||||
if (!it.hasNext()) return new ObjectValue(ctx, Map.of("done", true));
|
||||
else return new ObjectValue(ctx, Map.of("value", it.next()));
|
||||
else {
|
||||
var obj = new ObjectValue();
|
||||
obj.defineProperty(_ctx, "value", it.next());
|
||||
return obj;
|
||||
}
|
||||
}));
|
||||
|
||||
return res;
|
||||
@ -598,6 +597,11 @@ public class Values {
|
||||
}
|
||||
|
||||
private static void printValue(Context ctx, Object val, HashSet<Object> passed, int tab) {
|
||||
if (tab == 0 && val instanceof String) {
|
||||
System.out.print(val);
|
||||
return;
|
||||
}
|
||||
|
||||
if (passed.contains(val)) {
|
||||
System.out.print("[circular]");
|
||||
return;
|
||||
|
@ -82,8 +82,6 @@ public class Parsing {
|
||||
// Although ES5 allow these, we will comply to ES6 here
|
||||
reserved.add("const");
|
||||
reserved.add("let");
|
||||
reserved.add("async");
|
||||
reserved.add("super");
|
||||
// These are allowed too, however our parser considers them keywords
|
||||
reserved.add("undefined");
|
||||
reserved.add("arguments");
|
||||
@ -1060,7 +1058,6 @@ public class Parsing {
|
||||
|
||||
if (!checkVarName(literal.result)) {
|
||||
if (literal.result.equals("await")) return ParseRes.error(loc, "'await' expressions are not supported.");
|
||||
if (literal.result.equals("async")) return ParseRes.error(loc, "'async' is not supported.");
|
||||
if (literal.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported.");
|
||||
if (literal.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported.");
|
||||
return ParseRes.error(loc, String.format("Unexpected identifier '%s'.", literal.result));
|
||||
@ -1137,7 +1134,9 @@ public class Parsing {
|
||||
var op = opRes.result;
|
||||
if (!op.isAssign()) return ParseRes.failed();
|
||||
|
||||
if (!(prev instanceof AssignableStatement)) return ParseRes.error(loc, "Invalid expression on left hand side of assign operator.");
|
||||
if (!(prev instanceof AssignableStatement)) {
|
||||
return ParseRes.error(loc, "Invalid expression on left hand side of assign operator.");
|
||||
}
|
||||
|
||||
var res = parseValue(filename, tokens, i + n, 2);
|
||||
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected value after assignment operator '%s'.", op.value), res);
|
||||
@ -1411,8 +1410,7 @@ public class Parsing {
|
||||
|
||||
var valRes = parseValue(filename, tokens, i + n, 0);
|
||||
n += valRes.n;
|
||||
if (valRes.isError())
|
||||
return ParseRes.error(loc, "Expected a return value.", valRes);
|
||||
if (valRes.isError()) return ParseRes.error(loc, "Expected a return value.", valRes);
|
||||
|
||||
var res = ParseRes.res(new ReturnStatement(loc, valRes.result), n);
|
||||
|
||||
@ -1534,8 +1532,7 @@ public class Parsing {
|
||||
if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected an if condition.", condRes);
|
||||
n += condRes.n;
|
||||
|
||||
if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE))
|
||||
return ParseRes.error(loc, "Expected a closing paren after if condition.");
|
||||
if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after if condition.");
|
||||
|
||||
var res = parseStatement(filename, tokens, i + n);
|
||||
if (!res.isSuccess()) return ParseRes.error(loc, "Expected an if body.", res);
|
||||
@ -1702,8 +1699,7 @@ public class Parsing {
|
||||
parseVariableDeclare(filename, tokens, i + n),
|
||||
parseValueStatement(filename, tokens, i + n)
|
||||
);
|
||||
if (!declRes.isSuccess())
|
||||
return ParseRes.error(loc, "Expected a declaration or an expression.", declRes);
|
||||
if (!declRes.isSuccess()) return ParseRes.error(loc, "Expected a declaration or an expression.", declRes);
|
||||
n += declRes.n;
|
||||
decl = declRes.result;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user