fix: various small behavioural issues

fix: pesky try-catch logic
This commit is contained in:
TopchetoEU 2023-11-04 11:40:50 +02:00
parent 0ae24148d8
commit 4f82574b8c
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
8 changed files with 58 additions and 70 deletions

View File

@ -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) {

View File

@ -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;
@ -257,11 +257,11 @@ public class CodeFrame {
}
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;
}

View File

@ -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;
}

View File

@ -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];
}

View File

@ -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]);
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
}