From ee07a4a5fe63c917e5f3082d4bab761eed96aa2e Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Tue, 27 Feb 2024 13:25:36 +0100 Subject: [PATCH] support rest paramaters also for activation --- src/org/mozilla/javascript/Interpreter.java | 14 +- src/org/mozilla/javascript/NativeCall.java | 32 ++- src/org/mozilla/javascript/ScriptRuntime.java | 43 +++- .../javascript/optimizer/BodyCodegen.java | 10 +- .../es6/FunctionsRestParametersTest.java | 208 ++++++++++++------ 5 files changed, 232 insertions(+), 75 deletions(-) diff --git a/src/org/mozilla/javascript/Interpreter.java b/src/org/mozilla/javascript/Interpreter.java index 96e75d0fda..314e434b4b 100644 --- a/src/org/mozilla/javascript/Interpreter.java +++ b/src/org/mozilla/javascript/Interpreter.java @@ -137,11 +137,21 @@ void initializeArgs( if (idata.itsFunctionType == FunctionNode.ARROW_FUNCTION) { scope = ScriptRuntime.createArrowFunctionActivation( - fnOrScript, scope, args, idata.isStrict); + fnOrScript, + cx, + scope, + args, + idata.isStrict, + idata.argsHasRest); } else { scope = ScriptRuntime.createFunctionActivation( - fnOrScript, scope, args, idata.isStrict); + fnOrScript, + cx, + scope, + args, + idata.isStrict, + idata.argsHasRest); } } } else { diff --git a/src/org/mozilla/javascript/NativeCall.java b/src/org/mozilla/javascript/NativeCall.java index 33e9a4cc8b..d8474761c3 100644 --- a/src/org/mozilla/javascript/NativeCall.java +++ b/src/org/mozilla/javascript/NativeCall.java @@ -28,10 +28,12 @@ static void init(Scriptable scope, boolean sealed) { NativeCall( NativeFunction function, + Context cx, Scriptable scope, Object[] args, boolean isArrow, - boolean isStrict) { + boolean isStrict, + boolean argsHasRest) { this.function = function; setParentScope(scope); @@ -44,10 +46,30 @@ static void init(Scriptable scope, boolean sealed) { int paramAndVarCount = function.getParamAndVarCount(); int paramCount = function.getParamCount(); if (paramAndVarCount != 0) { - for (int i = 0; i < paramCount; ++i) { - String name = function.getParamOrVarName(i); - Object val = i < args.length ? args[i] : Undefined.instance; - defineProperty(name, val, PERMANENT); + if (argsHasRest) { + Object[] vals; + if (args.length >= paramCount) { + vals = new Object[args.length - paramCount]; + System.arraycopy(args, paramCount, vals, 0, args.length - paramCount); + } else { + vals = ScriptRuntime.emptyArgs; + } + + for (int i = 0; i < paramCount; ++i) { + String name = function.getParamOrVarName(i); + Object val = i < args.length ? args[i] : Undefined.instance; + defineProperty(name, val, PERMANENT); + } + defineProperty( + function.getParamOrVarName(paramCount), + cx.newArray(scope, vals), + PERMANENT); + } else { + for (int i = 0; i < paramCount; ++i) { + String name = function.getParamOrVarName(i); + Object val = i < args.length ? args[i] : Undefined.instance; + defineProperty(name, val, PERMANENT); + } } } diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java index 95da2fbb64..b449d37c82 100644 --- a/src/org/mozilla/javascript/ScriptRuntime.java +++ b/src/org/mozilla/javascript/ScriptRuntime.java @@ -4028,23 +4028,56 @@ public static void initScript( } /** - * @deprecated Use {@link #createFunctionActivation(NativeFunction, Scriptable, Object[], - * boolean)} instead + * @deprecated Use {@link #createFunctionActivation(NativeFunction, Context, Scriptable, + * Object[], boolean, boolean)} instead */ @Deprecated public static Scriptable createFunctionActivation( NativeFunction funObj, Scriptable scope, Object[] args) { - return createFunctionActivation(funObj, scope, args, false); + return createFunctionActivation( + funObj, Context.getCurrentContext(), scope, args, false, false); } + /** + * @deprecated Use {@link #createFunctionActivation(NativeFunction, Context, Scriptable, + * Object[], boolean, boolean)} instead + */ + @Deprecated public static Scriptable createFunctionActivation( NativeFunction funObj, Scriptable scope, Object[] args, boolean isStrict) { - return new NativeCall(funObj, scope, args, false, isStrict); + return new NativeCall( + funObj, Context.getCurrentContext(), scope, args, false, isStrict, false); + } + + public static Scriptable createFunctionActivation( + NativeFunction funObj, + Context cx, + Scriptable scope, + Object[] args, + boolean isStrict, + boolean argsHasRest) { + return new NativeCall(funObj, cx, scope, args, false, isStrict, argsHasRest); } + /** + * @deprecated Use {@link #createArrowFunctionActivation(NativeFunction, Context, Scriptable, + * Object[], boolean, boolean)} instead + */ + @Deprecated public static Scriptable createArrowFunctionActivation( NativeFunction funObj, Scriptable scope, Object[] args, boolean isStrict) { - return new NativeCall(funObj, scope, args, true, isStrict); + return new NativeCall( + funObj, Context.getCurrentContext(), scope, args, true, isStrict, false); + } + + public static Scriptable createArrowFunctionActivation( + NativeFunction funObj, + Context cx, + Scriptable scope, + Object[] args, + boolean isStrict, + boolean argsHasRest) { + return new NativeCall(funObj, cx, scope, args, true, isStrict, argsHasRest); } public static void enterActivationFunction(Context cx, Scriptable scope) { diff --git a/src/org/mozilla/javascript/optimizer/BodyCodegen.java b/src/org/mozilla/javascript/optimizer/BodyCodegen.java index 209fd208ec..c8d048a40b 100644 --- a/src/org/mozilla/javascript/optimizer/BodyCodegen.java +++ b/src/org/mozilla/javascript/optimizer/BodyCodegen.java @@ -115,15 +115,19 @@ private void generateGenerator() { // generators are forced to have an activation record cfw.addALoad(funObjLocal); + cfw.addALoad(contextLocal); cfw.addALoad(variableObjectLocal); cfw.addALoad(argsLocal); cfw.addPush(scriptOrFn.isInStrictMode()); + cfw.addPush(scriptOrFn.hasRestParameter()); addScriptRuntimeInvoke( "createFunctionActivation", "(Lorg/mozilla/javascript/NativeFunction;" + + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "[Ljava/lang/Object;" + "Z" + + "Z" + ")Lorg/mozilla/javascript/Scriptable;"); cfw.addAStore(variableObjectLocal); @@ -415,17 +419,21 @@ private void generatePrologue() { if (fnCurrent != null) { debugVariableName = "activation"; cfw.addALoad(funObjLocal); + cfw.addALoad(contextLocal); cfw.addALoad(variableObjectLocal); cfw.addALoad(argsLocal); + cfw.addPush(scriptOrFn.isInStrictMode()); + cfw.addPush(scriptOrFn.hasRestParameter()); String methodName = isArrow ? "createArrowFunctionActivation" : "createFunctionActivation"; - cfw.addPush(scriptOrFn.isInStrictMode()); addScriptRuntimeInvoke( methodName, "(Lorg/mozilla/javascript/NativeFunction;" + + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "[Ljava/lang/Object;" + "Z" + + "Z" + ")Lorg/mozilla/javascript/Scriptable;"); cfw.addAStore(variableObjectLocal); cfw.addALoad(contextLocal); diff --git a/testsrc/org/mozilla/javascript/tests/es6/FunctionsRestParametersTest.java b/testsrc/org/mozilla/javascript/tests/es6/FunctionsRestParametersTest.java index dfda2a3fc4..84c4755d53 100644 --- a/testsrc/org/mozilla/javascript/tests/es6/FunctionsRestParametersTest.java +++ b/testsrc/org/mozilla/javascript/tests/es6/FunctionsRestParametersTest.java @@ -40,12 +40,23 @@ public void tearDown() { @Test public void oneRestArg() { String code = - "function rest(...theArgs) {\n" - + " return theArgs;\n" + "function rest(...restArgs) {\n" + + " return restArgs;\n" + "}\n" - + "try {\n" - + " rest(1, 'abc', 2, '##').toString();\n" - + "} catch (e) { e.message }"; + + "rest(1, 'abc', 2, '##').toString();\n"; + + test("1,abc,2,##", code); + } + + @Test + public void oneRestArgActivation() { + String code = + "function rest(...restArgs) {\n" + + " try {\n" + + " return restArgs;\n" + + " } catch {}\n" + + "}\n" + + " rest(1, 'abc', 2, '##').toString();\n"; test("1,abc,2,##", code); } @@ -53,13 +64,25 @@ public void oneRestArg() { @Test public void oneRestArgNothingProvided() { String code = - "function rest(...theArgs) {\n" - + " return theArgs;\n" + "function rest(...restArgs) {\n" + + " return restArgs;\n" + + "}\n" + + "var r = rest();\n" + + "'' + Array.isArray(r) + '-' + r.length;\n"; + + test("true-0", code); + } + + @Test + public void oneRestArgNothingProvidedActivation() { + String code = + "function rest(...restArgs) {\n" + + " try {\n" + + " return restArgs;\n" + + " } catch {}\n" + "}\n" - + "try {\n" - + " var r = rest();\n" - + " '' + Array.isArray(r) + '-' + r.length;\n" - + "} catch (e) { e.message }"; + + "var r = rest();\n" + + "'' + Array.isArray(r) + '-' + r.length;\n"; test("true-0", code); } @@ -67,13 +90,25 @@ public void oneRestArgNothingProvided() { @Test public void oneRestArgOneProvided() { String code = - "function rest(...theArgs) {\n" - + " return theArgs;\n" + "function rest(...restArgs) {\n" + + " return restArgs;\n" + "}\n" - + "try {\n" - + " var r = rest('xy');\n" - + " '' + Array.isArray(r) + '-' + r.length;\n" - + "} catch (e) { e.message }"; + + "var r = rest('xy');\n" + + "'' + Array.isArray(r) + '-' + r.length;\n"; + + test("true-1", code); + } + + @Test + public void oneRestArgOneProvidedActivation() { + String code = + "function rest(...restArgs) {\n" + + " try {\n" + + " return restArgs;\n" + + " } catch {}\n" + + "}\n" + + "var r = rest('xy');\n" + + "'' + Array.isArray(r) + '-' + r.length;\n"; test("true-1", code); } @@ -81,12 +116,23 @@ public void oneRestArgOneProvided() { @Test public void twoRestArg() { String code = - "function rest(v, ...theArgs) {\n" - + " return theArgs;\n" + "function rest(v, ...restArgs) {\n" + + " return restArgs;\n" + "}\n" - + "try {\n" - + " rest(1, 'abc', 2, '##').toString();\n" - + "} catch (e) { e.message }"; + + "rest(1, 'abc', 2, '##').toString();\n"; + + test("abc,2,##", code); + } + + @Test + public void twoRestArgActivation() { + String code = + "function rest(v, ...restArgs) {\n" + + " try {\n" + + " return restArgs;\n" + + " } catch {}\n" + + "}\n" + + "rest(1, 'abc', 2, '##').toString();\n"; test("abc,2,##", code); } @@ -94,12 +140,23 @@ public void twoRestArg() { @Test public void twoRestArgNothingProvided() { String code = - "function rest(v, ...theArgs) {\n" - + " return '' + typeof v + ' - ' + Array.isArray(theArgs) + '-' + theArgs.length;\n" + "function rest(v, ...restArgs) {\n" + + " return '' + typeof v + ' - ' + Array.isArray(restArgs) + '-' + restArgs.length;\n" + "}\n" - + "try {\n" - + " rest();\n" - + "} catch (e) { e.message }"; + + "rest();\n"; + + test("undefined - true-0", code); + } + + @Test + public void twoRestArgNothingProvidedActivation() { + String code = + "function rest(v, ...restArgs) {\n" + + " try {\n" + + " return '' + typeof v + ' - ' + Array.isArray(restArgs) + '-' + restArgs.length;\n" + + " } catch {}\n" + + "}\n" + + "rest();\n"; test("undefined - true-0", code); } @@ -107,12 +164,23 @@ public void twoRestArgNothingProvided() { @Test public void twoRestArgOneProvided() { String code = - "function rest(v, ...theArgs) {\n" - + " return v + ' - ' + Array.isArray(theArgs) + '-' + theArgs.length;\n" + "function rest(v, ...restArgs) {\n" + + " return v + ' - ' + Array.isArray(restArgs) + '-' + restArgs.length;\n" + "}\n" - + "try {\n" - + " rest('77');\n" - + "} catch (e) { e.message }"; + + "rest('77');"; + + test("77 - true-0", code); + } + + @Test + public void twoRestArgOneProvidedActivation() { + String code = + "function rest(v, ...restArgs) {\n" + + " try {\n" + + " return v + ' - ' + Array.isArray(restArgs) + '-' + restArgs.length;\n" + + " } catch {}\n" + + "}\n" + + "rest('77');"; test("77 - true-0", code); } @@ -123,81 +191,97 @@ public void arguments() { "function rest(arg, ...restArgs) {\n" + " return arguments.length;\n" + "}\n" - + "try {\n" - + " '' + rest('77') + '-' + rest(1, 2, 3, 4);\n" - + "} catch (e) { e.message }"; + + "'' + rest('77') + '-' + rest(1, 2, 3, 4);\n"; test("1-4", code); } @Test - public void length() { + public void argumentsActivation() { String code = - "function foo1(...theArgs) {}\n" - + "function foo2(arg, ...theArgs) {}\n" - + "try {\n" - + " foo1.length + '-' + foo2.length;\n" - + "} catch (e) { e.message }"; + "function rest(arg, ...restArgs) {\n" + + " try {\n" + + " return arguments.length;\n" + + " } catch {}\n" + + "}\n" + + "'' + rest('77') + '-' + rest(1, 2, 3, 4);\n"; - test("0-1", code); + test("1-4", code); } @Test public void argLength() { String code = - "function rest(...theArgs) {\n" - + " return theArgs.length;\n" + "function rest(...restArgs) {\n" + + " return restArgs.length;\n" + + "}\n" + + " rest(1,2) + '-' + rest(1) + '-' + rest();\n"; + + test("2-1-0", code); + } + + @Test + public void argLengthActivation() { + String code = + "function rest(...restArgs) {\n" + + " try {\n" + + " return restArgs.length;\n" + + " } catch {}\n" + "}\n" - + "try {\n" - + " rest(1,2) + '-' + rest(1) + '-' + rest();\n" - + "} catch (e) { e.message }"; + + " rest(1,2) + '-' + rest(1) + '-' + rest();\n"; test("2-1-0", code); } + @Test + public void length() { + String code = + "function foo1(...restArgs) {}\n" + + "function foo2(arg, ...restArgs) {}\n" + + "foo1.length + '-' + foo2.length;\n"; + + test("0-1", code); + } + @Test public void string1() { String code = - "function rest(...theArgs) {\n" - + " return theArgs.length;\n" + "function rest(...restArgs) {\n" + + " return restArgs.length;\n" + "}\n" - + "try {\n" - + " rest.toString();\n" - + "} catch (e) { e.message }"; + + "rest.toString();\n"; - test("\nfunction rest(...theArgs) {\n return theArgs.length;\n}\n", code); + test("\nfunction rest(...restArgs) {\n return restArgs.length;\n}\n", code); } @Test public void string2() { String code = - "function rest( arg , ...theArgs ) {\n" - + " return theArgs.length;\n" + "function rest( arg , ...restArgs ) {\n" + + " return restArgs.length;\n" + "}\n" - + "try {\n" - + " rest.toString();\n" - + "} catch (e) { e.message }"; + + "rest.toString();\n"; - test("\nfunction rest(arg, ...theArgs) {\n return theArgs.length;\n}\n", code); + test("\nfunction rest(arg, ...restArgs) {\n return restArgs.length;\n}\n", code); } @Test public void trailingComma() { - String code = "function rest(...theArgs,) {\n" + " return theArgs;\n" + "}\n"; + String code = "function rest(...restArgs,) {\n" + " return restArgs;\n" + "}\n"; assertEvaluatorException("parameter after rest parameter (test#1)", code); } @Test public void twoRestParams() { - String code = "function rest(...rest1, ...rest2) {\n" + " return theArgs;\n" + "}\n"; + String code = "function rest(...rest1, ...rest2) {\n" + " return restArgs;\n" + "}\n"; assertEvaluatorException("parameter after rest parameter (test#1)", code); } @Test public void paramAfterRestParam() { - String code = "function rest(...rest1, param) {\n" + " return theArgs;\n" + "}\n"; + String code = "function rest(...rest1, param) {\n" + " return restArgs;\n" + "}\n"; assertEvaluatorException("parameter after rest parameter (test#1)", code); }