From d7353e19ed5bfe58f9fc08a2b553e7875af1b7a8 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:18:53 +0300 Subject: [PATCH] fix: treat "arguments" as a keyword (as per strict soecifications) --- .../jscript/common/Instruction.java | 4 ++-- .../compilation/FunctionArrowNode.java | 24 +++++++++++-------- .../jscript/compilation/FunctionNode.java | 10 ++++---- .../jscript/compilation/JavaScript.java | 5 ++-- .../compilation/values/ArgumentsNode.java | 17 +++++++++++++ .../values/operations/CallNode.java | 4 ++++ .../me/topchetoeu/jscript/runtime/Frame.java | 3 ++- .../jscript/runtime/InstructionRunner.java | 8 +++++-- .../values/functions/CodeFunction.java | 8 +++++-- 9 files changed, 59 insertions(+), 24 deletions(-) create mode 100644 src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java diff --git a/src/main/java/me/topchetoeu/jscript/common/Instruction.java b/src/main/java/me/topchetoeu/jscript/common/Instruction.java index 1bb48d5..838049c 100644 --- a/src/main/java/me/topchetoeu/jscript/common/Instruction.java +++ b/src/main/java/me/topchetoeu/jscript/common/Instruction.java @@ -352,8 +352,8 @@ public class Instruction { public static Instruction loadThis() { return new Instruction(Type.LOAD_THIS); } - public static Instruction loadArgs() { - return new Instruction(Type.LOAD_ARGS); + public static Instruction loadArgs(boolean real) { + return new Instruction(Type.LOAD_ARGS, real); } public static Instruction loadRestArgs(int offset) { return new Instruction(Type.LOAD_REST_ARGS, offset); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java index 851724b..7aaeb93 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java @@ -54,17 +54,21 @@ public class FunctionArrowNode extends FunctionNode { if (!src.is(i + n, "=>")) return ParseRes.failed(); n += 2; + n += Parsing.skipEmpty(src, i + n); - ParseRes body = ParseRes.first(src, i + n, - (s, j) -> JavaScript.parseExpression(s, j, 2), - CompoundNode::parse - ); - if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected an expression or a compount statement after '=>'"); - n += body.n; + if (src.is(i + n, "{")) { + var body = CompoundNode.parse(src, i + n); + if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compount statement after '=>'"); + n += body.n; - return ParseRes.res(new FunctionArrowNode( - loc, src.loc(i + n - 1), - params, body.result - ), n); + return ParseRes.res(new FunctionArrowNode(loc, src.loc(i + n - 1), params, body.result), n); + } + else { + var body = JavaScript.parseExpression(src, i + n, 2); + if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compount statement after '=>'"); + n += body.n; + + return ParseRes.res(new FunctionArrowNode(loc, src.loc(i + n - 1), params, body.result), n); + } } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java index 6433c32..876f21a 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -31,12 +31,12 @@ public abstract class FunctionNode extends Node { .remove(LabelContext.CONTINUE_CTX); return new CompileResult(env, scope, params.params.size(), target -> { - if (hasArgs || params.params.size() > 0) target.add(Instruction.loadArgs()); + if (hasArgs || params.params.size() > 0) target.add(Instruction.loadArgs(true)); - if (hasArgs) { - var argsVar = scope.defineStrict(new Variable("arguments", true), loc()); - target.add(_i -> Instruction.storeVar(argsVar.index(), params.params.size() > 0)); - } + // if (hasArgs) { + // var argsVar = scope.defineStrict(new Variable("arguments", true), loc()); + // target.add(_i -> Instruction.storeVar(argsVar.index(), params.params.size() > 0)); + // } if (params.params.size() > 0) { if (params.params.size() > 1) target.add(Instruction.dup(params.params.size() - 1)); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java index 676fdb2..c8bf4ef 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -24,6 +24,7 @@ import me.topchetoeu.jscript.compilation.control.ThrowNode; import me.topchetoeu.jscript.compilation.control.TryNode; import me.topchetoeu.jscript.compilation.control.WhileNode; import me.topchetoeu.jscript.compilation.scope.FunctionScope; +import me.topchetoeu.jscript.compilation.values.ArgumentsNode; import me.topchetoeu.jscript.compilation.values.ArrayNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.RegexNode; @@ -60,7 +61,7 @@ public final class JavaScript { "finally", "for", "do", "while", "switch", "case", "default", "new", "function", "var", "return", "throw", "typeof", "delete", "break", "continue", "debugger", "implements", "interface", "package", "private", - "protected", "public", "static" + "protected", "public", "static", "arguments" ); public static ParseRes parseParens(Source src, int i) { @@ -115,7 +116,7 @@ public final class JavaScript { if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n); if (id.result.equals("null")) return ParseRes.res(new NullNode(loc), n); if (id.result.equals("this")) return ParseRes.res(new ThisNode(loc), n); - // if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n); + if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n); return ParseRes.failed(); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java new file mode 100644 index 0000000..38ab529 --- /dev/null +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java @@ -0,0 +1,17 @@ +package me.topchetoeu.jscript.compilation.values; + +import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.Node; + + +public class ArgumentsNode extends Node { + @Override public void compile(CompileResult target, boolean pollute) { + if (pollute) target.add(Instruction.loadArgs(false)); + } + + public ArgumentsNode(Location loc) { + super(loc); + } +} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java index 91dbc78..74f11c0 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java @@ -13,6 +13,7 @@ import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.ArgumentsNode; import me.topchetoeu.jscript.compilation.values.ArrayNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.ThisNode; @@ -55,6 +56,9 @@ public class CallNode extends Node { else if (func instanceof ThisNode) { res = "this"; } + else if (func instanceof ArgumentsNode) { + res = "arguments"; + } else if (func instanceof ArrayNode) { var els = new ArrayList(); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/Frame.java b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java index acfb0af..31f9434 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java @@ -101,8 +101,9 @@ public final class Frame { */ public final Value[][] captures; public final List locals = new ArrayList<>(); - public final Value self; public final Value argsVal; + public Value self; + public Value fakeArgs; public final Value[] args; public final boolean isNew; public final Stack tryStack = new Stack<>(); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index 48fcd01..585a389 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -185,7 +185,10 @@ public class InstructionRunner { var func = new CodeFunction(env, name, frame.function.body.children[id], captures); if (!callable) func.enableCall = false; if (!constructible) func.enableNew = false; - if (captureThis) func.self = frame.self; + if (captureThis) { + func.self = frame.self; + func.argsVal = frame.argsVal; + } frame.push(func); frame.codePtr++; @@ -472,7 +475,8 @@ public class InstructionRunner { } private static Value execLoadArgs(Environment env, Instruction instr, Frame frame) { - frame.push(frame.argsVal); + if ((boolean)instr.get(0) || frame.fakeArgs == null) frame.push(frame.argsVal); + else frame.push(frame.fakeArgs); frame.codePtr++; return null; } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index d26db0d..7d900fa 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -9,6 +9,7 @@ public final class CodeFunction extends FunctionValue { public final FunctionBody body; public final Value[][] captures; public Value self; + public Value argsVal; public Environment env; private Value onCall(Frame frame) { @@ -26,8 +27,11 @@ public final class CodeFunction extends FunctionValue { } @Override public Value onCall(Environment env, boolean isNew, String name, Value thisArg, Value ...args) { - if (self != null) return onCall(new Frame(env, isNew, self, args, this)); - else return onCall(new Frame(env, isNew, thisArg, args, this)); + var frame = new Frame(env, isNew, thisArg, args, this); + if (argsVal != null) frame.fakeArgs = argsVal; + if (self != null) frame.self = self; + + return onCall(frame); } public CodeFunction(Environment env, String name, FunctionBody body, Value[][] captures) {