From 11986304d5786f3ba1a5350056fc51da0f5f580d Mon Sep 17 00:00:00 2001 From: ZZZank <3410764033@qq.com> Date: Fri, 11 Oct 2024 21:28:08 +0800 Subject: [PATCH] initial implementation of function rest parameter support https://github.com/mozilla/rhino/pull/1451 --- .../dev/latvian/mods/rhino/CodeGenerator.java | 1 + .../dev/latvian/mods/rhino/IRFactory.java | 17 +++-- .../mods/rhino/InterpretedFunction.java | 4 +- .../dev/latvian/mods/rhino/Interpreter.java | 4 +- .../latvian/mods/rhino/InterpreterData.java | 1 + .../dev/latvian/mods/rhino/NativeCall.java | 39 +++++++++-- .../java/dev/latvian/mods/rhino/Parser.java | 33 +++++++-- .../dev/latvian/mods/rhino/ScriptRuntime.java | 59 ++++++++++++++-- .../java/dev/latvian/mods/rhino/Token.java | 3 +- .../dev/latvian/mods/rhino/TokenStream.java | 3 + .../latvian/mods/rhino/ast/FunctionNode.java | 10 +++ .../latvian/mods/rhino/ast/ScriptNode.java | 4 ++ .../mods/rhino/optimizer/BodyCodegen.java | 69 ++++++++++++------- .../latvian/mods/rhino/optimizer/Codegen.java | 44 +++++++----- 14 files changed, 228 insertions(+), 63 deletions(-) diff --git a/common/src/main/java/dev/latvian/mods/rhino/CodeGenerator.java b/common/src/main/java/dev/latvian/mods/rhino/CodeGenerator.java index 1fcb7a7e..f3c87b0f 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/CodeGenerator.java +++ b/common/src/main/java/dev/latvian/mods/rhino/CodeGenerator.java @@ -170,6 +170,7 @@ private void generateICodeFromTree(Node tree) { itsData.argNames = scriptOrFn.getParamAndVarNames(); itsData.argIsConst = scriptOrFn.getParamAndVarConst(); itsData.argCount = scriptOrFn.getParamCount(); + itsData.argsHasRest = scriptOrFn.hasRestParameter(); itsData.encodedSourceStart = scriptOrFn.getEncodedSourceStart(); itsData.encodedSourceEnd = scriptOrFn.getEncodedSourceEnd(); diff --git a/common/src/main/java/dev/latvian/mods/rhino/IRFactory.java b/common/src/main/java/dev/latvian/mods/rhino/IRFactory.java index f950fa8a..e319be47 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/IRFactory.java +++ b/common/src/main/java/dev/latvian/mods/rhino/IRFactory.java @@ -61,6 +61,7 @@ import dev.latvian.mods.rhino.ast.WhileLoop; import dev.latvian.mods.rhino.ast.WithStatement; import dev.latvian.mods.rhino.ast.Yield; +import lombok.val; import java.util.ArrayList; import java.util.List; @@ -1933,13 +1934,21 @@ boolean isDestructuring(Node n) { Node decompileFunctionHeader(FunctionNode fn) { Node mexpr = null; if (fn.getFunctionName() != null) { +// decompiler.addName(fn.getName()); } else if (fn.getMemberExprNode() != null) { mexpr = transform(fn.getMemberExprNode()); } - boolean isArrow = fn.getFunctionType() == FunctionNode.ARROW_FUNCTION; - boolean noParen = isArrow && fn.getLp() == -1; - List params = fn.getParams(); - for (AstNode param : params) { + val isArrow = fn.getFunctionType() == FunctionNode.ARROW_FUNCTION; + val noParen = isArrow && fn.getLp() == -1; + val params = fn.getParams(); + for (int i = 0; i < params.size(); i++) { +// if (i > 0) { +// decompiler.addToken(Token.COMMA); +// } +// if (fn.hasRestParameter() && i == last) { +// decompiler.addToken(Token.DOTDOTDOT); +// } + val param = params.get(i); decompile(param); } return mexpr; diff --git a/common/src/main/java/dev/latvian/mods/rhino/InterpretedFunction.java b/common/src/main/java/dev/latvian/mods/rhino/InterpretedFunction.java index b8ce162e..4a842b10 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/InterpretedFunction.java +++ b/common/src/main/java/dev/latvian/mods/rhino/InterpretedFunction.java @@ -104,8 +104,8 @@ public Object resumeGenerator(Context cx, Scriptable scope, int operation, Objec @Override protected int getParamCount() { - return idata.argCount; - } + return idata.argsHasRest ? idata.argCount - 1 : idata.argCount; + } @Override protected int getParamAndVarCount() { diff --git a/common/src/main/java/dev/latvian/mods/rhino/Interpreter.java b/common/src/main/java/dev/latvian/mods/rhino/Interpreter.java index f2b2df2d..c11c114b 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/Interpreter.java +++ b/common/src/main/java/dev/latvian/mods/rhino/Interpreter.java @@ -121,9 +121,9 @@ void initializeArgs(Context cx, Scriptable callerScope, Object[] args, double[] if (useActivation) { if (idata.itsFunctionType == FunctionNode.ARROW_FUNCTION) { - scope = ScriptRuntime.createArrowFunctionActivation(fnOrScript, scope, args, idata.isStrict); + scope = ScriptRuntime.createArrowFunctionActivation(fnOrScript, cx, scope, args, idata.isStrict, idata.argsHasRest); } else { - scope = ScriptRuntime.createFunctionActivation(fnOrScript, scope, args, idata.isStrict); + scope = ScriptRuntime.createFunctionActivation(fnOrScript, cx, scope, args, idata.isStrict, idata.argsHasRest); } } } else { diff --git a/common/src/main/java/dev/latvian/mods/rhino/InterpreterData.java b/common/src/main/java/dev/latvian/mods/rhino/InterpreterData.java index dd421780..e5b8307f 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/InterpreterData.java +++ b/common/src/main/java/dev/latvian/mods/rhino/InterpreterData.java @@ -15,6 +15,7 @@ final class InterpreterData implements Serializable { static final int INITIAL_MAX_ICODE_LENGTH = 1024; static final int INITIAL_STRINGTABLE_SIZE = 64; static final int INITIAL_NUMBERTABLE_SIZE = 64; + boolean argsHasRest; InterpreterData(String sourceFile, String encodedSource, boolean isStrict) { this.itsSourceFile = sourceFile; diff --git a/common/src/main/java/dev/latvian/mods/rhino/NativeCall.java b/common/src/main/java/dev/latvian/mods/rhino/NativeCall.java index 5c1fb887..3b2d1a97 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/NativeCall.java +++ b/common/src/main/java/dev/latvian/mods/rhino/NativeCall.java @@ -6,6 +6,8 @@ package dev.latvian.mods.rhino; +import lombok.val; + /** * This class implements the activation object. *

@@ -27,7 +29,15 @@ static void init(Scriptable scope, boolean sealed) { NativeCall() { } - NativeCall(NativeFunction function, Scriptable scope, Object[] args, boolean isArrow, boolean isStrict) { + NativeCall( + NativeFunction function, + Context cx, + Scriptable scope, + Object[] args, + boolean isArrow, + boolean isStrict, + boolean argsHasRest + ) { this.function = function; setParentScope(scope); @@ -40,10 +50,27 @@ 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) { + val name = function.getParamOrVarName(i); + val 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) { + val name = function.getParamOrVarName(i); + val val = i < args.length ? args[i] : Undefined.instance; + defineProperty(name, val, PERMANENT); + } } } @@ -56,7 +83,7 @@ static void init(Scriptable scope, boolean sealed) { if (paramAndVarCount != 0) { for (int i = paramCount; i < paramAndVarCount; ++i) { - String name = function.getParamOrVarName(i); + val name = function.getParamOrVarName(i); if (!super.has(name, this)) { if (function.getParamOrVarConst(i)) { defineProperty(name, Undefined.instance, CONST); diff --git a/common/src/main/java/dev/latvian/mods/rhino/Parser.java b/common/src/main/java/dev/latvian/mods/rhino/Parser.java index 02245917..c877c6c1 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/Parser.java +++ b/common/src/main/java/dev/latvian/mods/rhino/Parser.java @@ -8,6 +8,7 @@ import dev.latvian.mods.rhino.ast.*; import dev.latvian.mods.rhino.ast.Symbol; +import lombok.val; import java.io.IOException; import java.util.ArrayList; @@ -637,7 +638,12 @@ private void parseFunctionParams(FunctionNode fnNode) throws IOException { do { int tt = peekToken(); if (tt == Token.LB || tt == Token.LC) { - AstNode expr = destructuringPrimaryExpr(); + if (fnNode.hasRestParameter()) { + // Error: parameter after rest parameter + reportError("msg.parm.after.rest", ts.tokenBeg, ts.tokenEnd - ts.tokenBeg); + } + + val expr = destructuringPrimaryExpr(); markDestructuring(expr); fnNode.addParam(expr); // Destructuring assignment for parameters: add a dummy @@ -646,18 +652,35 @@ private void parseFunctionParams(FunctionNode fnNode) throws IOException { if (destructuring == null) { destructuring = new HashMap<>(); } - String pname = currentScriptOrFn.getNextTempName(); + val pname = currentScriptOrFn.getNextTempName(); defineSymbol(Token.LP, pname, false); destructuring.put(pname, expr); } else { + boolean wasRest = false; + if (tt == Token.DOTDOTDOT) { + if (fnNode.hasRestParameter()) { + // Error: parameter after rest parameter + reportError("msg.parm.after.rest", ts.tokenBeg, ts.tokenEnd - ts.tokenBeg); + } + + fnNode.setHasRestParameter(true); + wasRest = true; + consumeToken(); + } + if (mustMatchToken(Token.NAME, "msg.no.parm", true)) { - Name paramNameNode = createNameNode(); - Comment jsdocNodeForName = getAndResetJsDoc(); + if (!wasRest && fnNode.hasRestParameter()) { + // Error: parameter after rest parameter + reportError("msg.parm.after.rest", ts.tokenBeg, ts.tokenEnd - ts.tokenBeg); + } + + val paramNameNode = createNameNode(); + val jsdocNodeForName = getAndResetJsDoc(); if (jsdocNodeForName != null) { paramNameNode.setJsDocNode(jsdocNodeForName); } fnNode.addParam(paramNameNode); - String paramName = ts.getString(); + val paramName = ts.getString(); defineSymbol(Token.LP, paramName); if (this.inUseStrictDirective) { if ("eval".equals(paramName) || "arguments".equals(paramName)) { diff --git a/common/src/main/java/dev/latvian/mods/rhino/ScriptRuntime.java b/common/src/main/java/dev/latvian/mods/rhino/ScriptRuntime.java index fefeea8b..7931afba 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/ScriptRuntime.java +++ b/common/src/main/java/dev/latvian/mods/rhino/ScriptRuntime.java @@ -635,6 +635,33 @@ public static Object[] padArguments(Object[] args, int count) { return result; } + /** + * Helper function for builtin objects that use the varargs form. ECMA function formal arguments + * are undefined if not supplied; this function pads the argument array out to the expected + * length, if necessary. Also, the rest parameter array construction is done here. + */ + public static Object[] padAndRestArguments( + Context cx, Scriptable scope, Object[] args, int argCount) { + Object[] result = new Object[argCount]; + int paramCount = argCount - 1; + if (args.length < paramCount) { + System.arraycopy(args, 0, result, 0, args.length); + Arrays.fill(result, args.length, paramCount, Undefined.instance); + } else { + System.arraycopy(args, 0, result, 0, paramCount); + } + + Object[] restValues; + if (args.length > paramCount) { + restValues = new Object[args.length - paramCount]; + System.arraycopy(args, paramCount, restValues, 0, restValues.length); + } else { + restValues = ScriptRuntime.emptyArgs; + } + result[paramCount] = cx.newArray(scope, restValues); + return result; + } + public static String escapeString(String s) { return escapeString(s, '"'); } @@ -2915,12 +2942,36 @@ public static void initScript(NativeFunction funObj, Scriptable thisObj, Context } } - public static Scriptable createFunctionActivation(NativeFunction funObj, Scriptable scope, Object[] args, boolean isStrict) { - return new NativeCall(funObj, scope, args, false, isStrict); + 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 + public static Scriptable createFunctionActivation(NativeFunction funObj, Context cx, Scriptable scope, Object[] args, boolean isStrict) { + return createFunctionActivation(funObj, cx, scope, args, 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 Scriptable createArrowFunctionActivation(NativeFunction funObj, Scriptable scope, Object[] args, boolean isStrict) { - return new NativeCall(funObj, scope, args, true, isStrict); + @Deprecated + public static Scriptable createArrowFunctionActivation(NativeFunction funObj, Context cx, Scriptable scope, Object[] args, boolean isStrict) { + return createArrowFunctionActivation(funObj, cx, scope, args, true, isStrict); } public static void enterActivationFunction(Context cx, Scriptable scope) { diff --git a/common/src/main/java/dev/latvian/mods/rhino/Token.java b/common/src/main/java/dev/latvian/mods/rhino/Token.java index 633efbe1..2827f596 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/Token.java +++ b/common/src/main/java/dev/latvian/mods/rhino/Token.java @@ -214,8 +214,9 @@ enum CommentType { int TEMPLATE_CHARS = TEMPLATE_LITERAL + 1; // template literal - literal section int TEMPLATE_LITERAL_SUBST = TEMPLATE_CHARS + 1; // template literal - substitution int TAGGED_TEMPLATE_LITERAL = TEMPLATE_LITERAL_SUBST + 1; // template literal - tagged/handler + int DOTDOTDOT = 174; // spread/rest ... - int LAST_TOKEN = TAGGED_TEMPLATE_LITERAL; + int LAST_TOKEN = DOTDOTDOT; /** diff --git a/common/src/main/java/dev/latvian/mods/rhino/TokenStream.java b/common/src/main/java/dev/latvian/mods/rhino/TokenStream.java index a7b2dad3..e5a5bfaf 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/TokenStream.java +++ b/common/src/main/java/dev/latvian/mods/rhino/TokenStream.java @@ -1178,6 +1178,9 @@ final int getToken() throws IOException { case ':': return Token.COLON; case '.': + if (matchChar('.') && matchChar('.')) { + return Token.DOTDOTDOT; + } return Token.DOT; case '|': diff --git a/common/src/main/java/dev/latvian/mods/rhino/ast/FunctionNode.java b/common/src/main/java/dev/latvian/mods/rhino/ast/FunctionNode.java index 30068c12..65017a19 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/ast/FunctionNode.java +++ b/common/src/main/java/dev/latvian/mods/rhino/ast/FunctionNode.java @@ -65,6 +65,7 @@ public class FunctionNode extends ScriptNode { public static final int FUNCTION_EXPRESSION = 2; public static final int FUNCTION_EXPRESSION_STATEMENT = 3; public static final int ARROW_FUNCTION = 4; + private boolean hasRestParameter; public enum Form { FUNCTION, GETTER, SETTER, METHOD @@ -397,6 +398,15 @@ public AstNode getMemberExprNode() { return memberExprNode; } + @Override + public boolean hasRestParameter() { + return hasRestParameter; + } + + public void setHasRestParameter(boolean hasRestParameter) { + this.hasRestParameter = hasRestParameter; + } + /** * Visits this node, the function name node if supplied, * the parameters, and the body. If there is a member-expr node, diff --git a/common/src/main/java/dev/latvian/mods/rhino/ast/ScriptNode.java b/common/src/main/java/dev/latvian/mods/rhino/ast/ScriptNode.java index 545d3121..6a97ea97 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/ast/ScriptNode.java +++ b/common/src/main/java/dev/latvian/mods/rhino/ast/ScriptNode.java @@ -335,6 +335,10 @@ public boolean isInStrictMode() { return inStrictMode; } + public boolean hasRestParameter() { + return false; + } + @Override public void visit(NodeVisitor v) { if (v.visit(this)) { diff --git a/common/src/main/java/dev/latvian/mods/rhino/optimizer/BodyCodegen.java b/common/src/main/java/dev/latvian/mods/rhino/optimizer/BodyCodegen.java index 891ded88..ec56b281 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/optimizer/BodyCodegen.java +++ b/common/src/main/java/dev/latvian/mods/rhino/optimizer/BodyCodegen.java @@ -113,15 +113,20 @@ 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()); - addScriptRuntimeInvoke("createFunctionActivation", - "(Ldev/latvian/mods/rhino/NativeFunction;" - + "Ldev/latvian/mods/rhino/Scriptable;" + cfw.addPush(scriptOrFn.hasRestParameter()); + addScriptRuntimeInvoke( + "createFunctionActivation", + "(Lorg/mozilla/javascript/NativeFunction;" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + "[Ljava/lang/Object;" + "Z" - + ")Ldev/latvian/mods/rhino/Scriptable;" + + "Z" + + ")Lorg/mozilla/javascript/Scriptable;" ); cfw.addAStore(variableObjectLocal); @@ -333,20 +338,33 @@ private void generatePrologue() { int parmCount = scriptOrFn.getParamCount(); if (parmCount > 0 && !inDirectCallFunction) { // Set up args array - // check length of arguments, pad if need be - cfw.addALoad(argsLocal); - cfw.add(ByteCode.ARRAYLENGTH); - cfw.addPush(parmCount); - int label = cfw.acquireLabel(); - cfw.add(ByteCode.IF_ICMPGE, label); - cfw.addALoad(argsLocal); - cfw.addPush(parmCount); - addScriptRuntimeInvoke("padArguments", - "([Ljava/lang/Object;I" - + ")[Ljava/lang/Object;" - ); - cfw.addAStore(argsLocal); - cfw.markLabel(label); + if (scriptOrFn.hasRestParameter()) { + cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); + cfw.addALoad(argsLocal); + cfw.addPush(parmCount); + addScriptRuntimeInvoke( + "padAndRestArguments", + "(" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + + "[Ljava/lang/Object;" + + "I" + + ")[Ljava/lang/Object;"); + cfw.addAStore(argsLocal); + } else { + // check length of arguments, pad if need be + cfw.addALoad(argsLocal); + cfw.add(ByteCode.ARRAYLENGTH); + cfw.addPush(parmCount); + int label = cfw.acquireLabel(); + cfw.add(ByteCode.IF_ICMPGE, label); + cfw.addALoad(argsLocal); + cfw.addPush(parmCount); + addScriptRuntimeInvoke("padArguments", "([Ljava/lang/Object;I)[Ljava/lang/Object;"); + cfw.addAStore(argsLocal); + cfw.markLabel(label); + } } int paramCount = fnCurrent.fnode.getParamCount(); @@ -399,16 +417,21 @@ private void generatePrologue() { } if (fnCurrent != null) { cfw.addALoad(funObjLocal); + cfw.addALoad(contextLocal); cfw.addALoad(variableObjectLocal); cfw.addALoad(argsLocal); - String methodName = isArrow ? "createArrowFunctionActivation" : "createFunctionActivation"; cfw.addPush(scriptOrFn.isInStrictMode()); - addScriptRuntimeInvoke(methodName, - "(Ldev/latvian/mods/rhino/NativeFunction;" - + "Ldev/latvian/mods/rhino/Scriptable;" + cfw.addPush(scriptOrFn.hasRestParameter()); + String methodName = isArrow ? "createArrowFunctionActivation" : "createFunctionActivation"; + addScriptRuntimeInvoke( + methodName, + "(Lorg/mozilla/javascript/NativeFunction;" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + "[Ljava/lang/Object;" + "Z" - + ")Ldev/latvian/mods/rhino/Scriptable;" + + "Z" + + ")Lorg/mozilla/javascript/Scriptable;" ); cfw.addAStore(variableObjectLocal); cfw.addALoad(contextLocal); diff --git a/common/src/main/java/dev/latvian/mods/rhino/optimizer/Codegen.java b/common/src/main/java/dev/latvian/mods/rhino/optimizer/Codegen.java index 0a8210a5..52143138 100644 --- a/common/src/main/java/dev/latvian/mods/rhino/optimizer/Codegen.java +++ b/common/src/main/java/dev/latvian/mods/rhino/optimizer/Codegen.java @@ -22,6 +22,8 @@ import java.util.List; import java.util.Map; +import static dev.latvian.mods.rhino.classfile.ClassFileWriter.ACC_PUBLIC; + /** * This class generates code for a given IR tree. * @@ -383,7 +385,7 @@ private void generateResumeGenerator(ClassFileWriter cfw) { "Ldev/latvian/mods/rhino/Scriptable;" + "ILjava/lang/Object;" + "Ljava/lang/Object;)Ljava/lang/Object;", - (short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL) + (short) (ACC_PUBLIC | ClassFileWriter.ACC_FINAL) ); // load arguments for dispatch to the corresponding *_gen method @@ -437,7 +439,7 @@ private void generateCallMethod(ClassFileWriter cfw, boolean isStrictMode) { "Ldev/latvian/mods/rhino/Scriptable;" + "Ldev/latvian/mods/rhino/Scriptable;" + "[Ljava/lang/Object;)Ljava/lang/Object;", - (short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL) + (short) (ACC_PUBLIC | ClassFileWriter.ACC_FINAL) ); // Generate code for: @@ -549,7 +551,7 @@ private void generateCallMethod(ClassFileWriter cfw, boolean isStrictMode) { private void generateMain(ClassFileWriter cfw) { cfw.startMethod("main", "([Ljava/lang/String;)V", - (short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_STATIC) + (short) (ACC_PUBLIC | ClassFileWriter.ACC_STATIC) ); // load new ScriptImpl() @@ -576,7 +578,7 @@ private static void generateExecute(ClassFileWriter cfw) { "(Ldev/latvian/mods/rhino/Context;" + "Ldev/latvian/mods/rhino/Scriptable;" + ")Ljava/lang/Object;", - (short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL) + (short) (ACC_PUBLIC | ClassFileWriter.ACC_FINAL) ); final int CONTEXT_ARG = 1; @@ -603,7 +605,7 @@ private static void generateExecute(ClassFileWriter cfw) { } private static void generateScriptCtor(ClassFileWriter cfw) { - cfw.startMethod("", "()V", ClassFileWriter.ACC_PUBLIC); + cfw.startMethod("", "()V", ACC_PUBLIC); cfw.addLoadThis(); cfw.addInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME, @@ -624,7 +626,7 @@ private void generateFunctionConstructor(ClassFileWriter cfw) { final int CONTEXT_ARG = 2; final int ID_ARG = 3; - cfw.startMethod("", FUNCTION_CONSTRUCTOR_SIGNATURE, ClassFileWriter.ACC_PUBLIC); + cfw.startMethod("", FUNCTION_CONSTRUCTOR_SIGNATURE, ACC_PUBLIC); cfw.addALoad(0); cfw.addInvoke(ByteCode.INVOKESPECIAL, SUPER_CLASS_NAME, "", "()V" @@ -723,7 +725,7 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, String encoded // Override NativeFunction.getLanguageVersion() with // public int getLanguageVersion() { return ; } - cfw.startMethod("getLanguageVersion", "()I", ClassFileWriter.ACC_PUBLIC); + cfw.startMethod("getLanguageVersion", "()I", ACC_PUBLIC); cfw.addPush(compilerEnv.getLanguageVersion()); cfw.add(ByteCode.IRETURN); @@ -741,7 +743,8 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, String encoded val Do_getEncodedSource = 4; val Do_getParamOrVarConst = 5; val Do_isGeneratorFunction = 6; - val SWITCH_COUNT = 7; + val Do_hasRestParameter = 7; + val SWITCH_COUNT = Do_hasRestParameter + 1; for (int methodIndex = 0; methodIndex != SWITCH_COUNT; ++methodIndex) { if (methodIndex == Do_getEncodedSource && encodedSource == null) { @@ -758,31 +761,31 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, String encoded case Do_getFunctionName: methodLocals = 1; // Only this cfw.startMethod("getFunctionName", "()Ljava/lang/String;", - ClassFileWriter.ACC_PUBLIC + ACC_PUBLIC ); break; case Do_getParamCount: methodLocals = 1; // Only this cfw.startMethod("getParamCount", "()I", - ClassFileWriter.ACC_PUBLIC + ACC_PUBLIC ); break; case Do_getParamAndVarCount: methodLocals = 1; // Only this cfw.startMethod("getParamAndVarCount", "()I", - ClassFileWriter.ACC_PUBLIC + ACC_PUBLIC ); break; case Do_getParamOrVarName: methodLocals = 1 + 1; // this + paramOrVarIndex cfw.startMethod("getParamOrVarName", "(I)Ljava/lang/String;", - ClassFileWriter.ACC_PUBLIC + ACC_PUBLIC ); break; case Do_getParamOrVarConst: methodLocals = 1 + 1 + 1; // this + paramOrVarName cfw.startMethod("getParamOrVarConst", "(I)Z", - ClassFileWriter.ACC_PUBLIC + ACC_PUBLIC ); break; case Do_isGeneratorFunction: @@ -791,14 +794,17 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, String encoded ClassFileWriter.ACC_PROTECTED ); break; - case Do_getEncodedSource: methodLocals = 1; // Only this cfw.startMethod("getEncodedSource", "()Ljava/lang/String;", - ClassFileWriter.ACC_PUBLIC + ACC_PUBLIC ); cfw.addPush(encodedSource); break; + case Do_hasRestParameter: + methodLocals = 1; // Only this + cfw.startMethod("hasRestParameter", "()Z", ACC_PUBLIC); + break; default: throw Kit.codeBug(); } @@ -847,7 +853,7 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, String encoded case Do_getParamCount: // Push number of defined parameters - cfw.addPush(n.getParamCount()); + cfw.addPush(n.hasRestParameter() ? n.getParamCount() - 1 : n.getParamCount()); cfw.add(ByteCode.IRETURN); break; @@ -947,6 +953,12 @@ private void generateNativeFunctionOverrides(ClassFileWriter cfw, String encoded cfw.add(ByteCode.IRETURN); break; + case Do_hasRestParameter: + // Push boolean of defined hasRestParameter + cfw.addPush(n.hasRestParameter()); + cfw.add(ByteCode.IRETURN); + break; + case Do_getEncodedSource: // Push number encoded source start and end // to prepare for encodedSource.substring(start, end)