diff --git a/rhino/src/main/java/org/mozilla/javascript/Parser.java b/rhino/src/main/java/org/mozilla/javascript/Parser.java index b10f529b78..a8835fcc47 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Parser.java +++ b/rhino/src/main/java/org/mozilla/javascript/Parser.java @@ -133,6 +133,7 @@ public class Parser { private Comment currentJsDocComment; protected int nestingOfFunction; + protected int nestingOfFunctionParams; private LabeledStatement currentLabel; private boolean inDestructuringAssignment; protected boolean inUseStrictDirective; @@ -480,10 +481,14 @@ public boolean eof() { return ts.eof(); } - boolean insideFunction() { + boolean insideFunctionBody() { return nestingOfFunction != 0; } + boolean insideFunctionParams() { + return nestingOfFunctionParams != 0; + } + void pushScope(Scope scope) { Scope parent = scope.getParentScope(); // During codegen, parent scope chain may already be initialized, @@ -774,148 +779,155 @@ private static String getDirective(AstNode n) { } private void parseFunctionParams(FunctionNode fnNode) throws IOException { - if (matchToken(Token.RP, true)) { - fnNode.setRp(ts.tokenBeg - fnNode.getPosition()); - return; - } - // Would prefer not to call createDestructuringAssignment until codegen, - // but the symbol definitions have to happen now, before body is parsed. - Map destructuring = null; - Map destructuringDefault = null; - - Set paramNames = new HashSet<>(); - do { - int tt = peekToken(); - if (tt == Token.RP) { - if (fnNode.hasRestParameter()) { - // Error: parameter after rest parameter - reportError("msg.parm.after.rest", ts.tokenBeg, ts.tokenEnd - ts.tokenBeg); - } - - fnNode.putIntProp(Node.TRAILING_COMMA, 1); - break; + ++nestingOfFunctionParams; + try { + if (matchToken(Token.RP, true)) { + fnNode.setRp(ts.tokenBeg - fnNode.getPosition()); + return; } - if (tt == Token.LB || tt == Token.LC) { - if (fnNode.hasRestParameter()) { - // Error: parameter after rest parameter - reportError("msg.parm.after.rest", ts.tokenBeg, ts.tokenEnd - ts.tokenBeg); - } - - AstNode expr = destructuringAssignExpr(); - if (destructuring == null) { - destructuring = new HashMap<>(); - } + // Would prefer not to call createDestructuringAssignment until codegen, + // but the symbol definitions have to happen now, before body is parsed. + Map destructuring = null; + Map destructuringDefault = null; - if (expr instanceof Assignment) { - // We have default arguments inside destructured function parameters - // eg: f([x = 1] = [2]) { ... }, transform this into: - // f(x) { - // if ($1 == undefined) - // var $1 = [2]; - // if (x == undefined) - // if (($1[0]) == undefined) - // var x = 1; - // else - // var x = $1[0]; - // } - // fnNode.addParam(name) - AstNode lhs = ((Assignment) expr).getLeft(); // [x = 1] - AstNode rhs = ((Assignment) expr).getRight(); // [2] - markDestructuring(lhs); - fnNode.addParam(lhs); - String pname = currentScriptOrFn.getNextTempName(); - defineSymbol(Token.LP, pname, false); - if (destructuringDefault == null) { - destructuringDefault = new HashMap<>(); - } - destructuring.put(pname, lhs); - destructuringDefault.put(pname, rhs); - } else { - markDestructuring(expr); - fnNode.addParam(expr); - // Destructuring assignment for parameters: add a dummy - // parameter name, and add a statement to the body to initialize - // variables from the destructuring assignment - String pname = currentScriptOrFn.getNextTempName(); - defineSymbol(Token.LP, pname, false); - destructuring.put(pname, expr); - } - } else { - boolean wasRest = false; - int restStartLineno = -1, restStartColumn = -1; - if (tt == Token.DOTDOTDOT) { + Set paramNames = new HashSet<>(); + do { + int tt = peekToken(); + if (tt == Token.RP) { 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(); - restStartLineno = lineNumber(); - restStartColumn = columnNumber(); + fnNode.putIntProp(Node.TRAILING_COMMA, 1); + break; } - - if (mustMatchToken(Token.NAME, "msg.no.parm", true)) { - if (!wasRest && fnNode.hasRestParameter()) { + if (tt == Token.LB || tt == Token.LC) { + if (fnNode.hasRestParameter()) { // Error: parameter after rest parameter reportError("msg.parm.after.rest", ts.tokenBeg, ts.tokenEnd - ts.tokenBeg); } - Name paramNameNode = createNameNode(); - if (wasRest) { - paramNameNode.setLineColumnNumber(restStartLineno, restStartColumn); + AstNode expr = destructuringAssignExpr(); + if (destructuring == null) { + destructuring = new HashMap<>(); } - Comment jsdocNodeForName = getAndResetJsDoc(); - if (jsdocNodeForName != null) { - paramNameNode.setJsDocNode(jsdocNodeForName); + + if (expr instanceof Assignment) { + // We have default arguments inside destructured function parameters + // eg: f([x = 1] = [2]) { ... }, transform this into: + // f(x) { + // if ($1 == undefined) + // var $1 = [2]; + // if (x == undefined) + // if (($1[0]) == undefined) + // var x = 1; + // else + // var x = $1[0]; + // } + // fnNode.addParam(name) + AstNode lhs = ((Assignment) expr).getLeft(); // [x = 1] + AstNode rhs = ((Assignment) expr).getRight(); // [2] + markDestructuring(lhs); + fnNode.addParam(lhs); + String pname = currentScriptOrFn.getNextTempName(); + defineSymbol(Token.LP, pname, false); + if (destructuringDefault == null) { + destructuringDefault = new HashMap<>(); + } + destructuring.put(pname, lhs); + destructuringDefault.put(pname, rhs); + } else { + markDestructuring(expr); + fnNode.addParam(expr); + // Destructuring assignment for parameters: add a dummy + // parameter name, and add a statement to the body to initialize + // variables from the destructuring assignment + String pname = currentScriptOrFn.getNextTempName(); + defineSymbol(Token.LP, pname, false); + destructuring.put(pname, expr); } - fnNode.addParam(paramNameNode); - String paramName = ts.getString(); - defineSymbol(Token.LP, paramName); - if (this.inUseStrictDirective) { - if ("eval".equals(paramName) || "arguments".equals(paramName)) { - reportError("msg.bad.id.strict", paramName); + } else { + boolean wasRest = false; + int restStartLineno = -1, restStartColumn = -1; + if (tt == Token.DOTDOTDOT) { + if (fnNode.hasRestParameter()) { + // Error: parameter after rest parameter + reportError( + "msg.parm.after.rest", ts.tokenBeg, ts.tokenEnd - ts.tokenBeg); } - if (paramNames.contains(paramName)) - addError("msg.dup.param.strict", paramName); - paramNames.add(paramName); + + fnNode.setHasRestParameter(true); + wasRest = true; + consumeToken(); + restStartLineno = lineNumber(); + restStartColumn = columnNumber(); } - if (matchToken(Token.ASSIGN, true)) { - if (compilerEnv.getLanguageVersion() >= Context.VERSION_ES6) { - fnNode.putDefaultParams(paramName, assignExpr()); - } else { - reportError("msg.default.args"); + if (mustMatchToken(Token.NAME, "msg.no.parm", true)) { + if (!wasRest && fnNode.hasRestParameter()) { + // Error: parameter after rest parameter + reportError( + "msg.parm.after.rest", ts.tokenBeg, ts.tokenEnd - ts.tokenBeg); + } + + Name paramNameNode = createNameNode(); + if (wasRest) { + paramNameNode.setLineColumnNumber(restStartLineno, restStartColumn); + } + Comment jsdocNodeForName = getAndResetJsDoc(); + if (jsdocNodeForName != null) { + paramNameNode.setJsDocNode(jsdocNodeForName); + } + fnNode.addParam(paramNameNode); + String paramName = ts.getString(); + defineSymbol(Token.LP, paramName); + if (this.inUseStrictDirective) { + if ("eval".equals(paramName) || "arguments".equals(paramName)) { + reportError("msg.bad.id.strict", paramName); + } + if (paramNames.contains(paramName)) + addError("msg.dup.param.strict", paramName); + paramNames.add(paramName); + } + + if (matchToken(Token.ASSIGN, true)) { + if (compilerEnv.getLanguageVersion() >= Context.VERSION_ES6) { + fnNode.putDefaultParams(paramName, assignExpr()); + } else { + reportError("msg.default.args"); + } } + } else { + fnNode.addParam(makeErrorNode()); } - } else { - fnNode.addParam(makeErrorNode()); } - } - } while (matchToken(Token.COMMA, true)); - - if (destructuring != null) { - Node destructuringNode = new Node(Token.COMMA); - // Add assignment helper for each destructuring parameter - for (Map.Entry param : destructuring.entrySet()) { - AstNode defaultValue = null; - if (destructuringDefault != null) { - defaultValue = destructuringDefault.get(param.getKey()); + } while (matchToken(Token.COMMA, true)); + + if (destructuring != null) { + Node destructuringNode = new Node(Token.COMMA); + // Add assignment helper for each destructuring parameter + for (Map.Entry param : destructuring.entrySet()) { + AstNode defaultValue = null; + if (destructuringDefault != null) { + defaultValue = destructuringDefault.get(param.getKey()); + } + Node assign = + createDestructuringAssignment( + Token.VAR, + param.getValue(), + createName(param.getKey()), + defaultValue); + destructuringNode.addChildToBack(assign); } - Node assign = - createDestructuringAssignment( - Token.VAR, - param.getValue(), - createName(param.getKey()), - defaultValue); - destructuringNode.addChildToBack(assign); + fnNode.putProp(Node.DESTRUCTURING_PARAMS, destructuringNode); } - fnNode.putProp(Node.DESTRUCTURING_PARAMS, destructuringNode); - } - if (mustMatchToken(Token.RP, "msg.no.paren.after.parms", true)) { - fnNode.setRp(ts.tokenBeg - fnNode.getPosition()); + if (mustMatchToken(Token.RP, "msg.no.paren.after.parms", true)) { + fnNode.setRp(ts.tokenBeg - fnNode.getPosition()); + } + } finally { + --nestingOfFunctionParams; } } @@ -1395,7 +1407,7 @@ private AstNode statementHelper() throws IOException { // We have not consumed any token yet, so the position would be invalid lineno = ts.getLineno(); column = ts.getTokenColumn(); - pn = new ExpressionStatement(expr(false), !insideFunction()); + pn = new ExpressionStatement(expr(false), !insideFunctionBody()); pn.setLineColumnNumber(lineno, column); break; } @@ -2060,7 +2072,7 @@ private static final boolean nowAllSet(int before, int after, int mask) { } private AstNode returnOrYield(int tt, boolean exprContext) throws IOException { - if (!insideFunction()) { + if (!insideFunctionBody()) { reportError(tt == Token.RETURN ? "msg.bad.return" : "msg.bad.yield"); } consumeToken(); @@ -2107,7 +2119,7 @@ private AstNode returnOrYield(int tt, boolean exprContext) throws IOException { if (nowAllSet(before, endFlags, Node.END_RETURNS | Node.END_RETURNS_VALUE)) addStrictWarning("msg.return.inconsistent", "", pos, end - pos); } else { - if (!insideFunction()) reportError("msg.bad.yield"); + if (!insideFunctionBody()) reportError("msg.bad.yield"); endFlags |= Node.END_YIELDS; ret = new Yield(pos, end - pos, e, yieldStar); setRequiresActivation(); @@ -2119,7 +2131,7 @@ private AstNode returnOrYield(int tt, boolean exprContext) throws IOException { } // see if we are mixing yields and value returns. - if (insideFunction() + if (insideFunctionBody() && nowAllSet(before, endFlags, Node.END_YIELDS | Node.END_RETURNS_VALUE)) { FunctionNode fn = (FunctionNode) currentScriptOrFn; if (!fn.isES6Generator()) { @@ -2215,7 +2227,7 @@ private AstNode nameOrLabel() throws IOException { AstNode expr = expr(false); if (expr.getType() != Token.LABEL) { - AstNode n = new ExpressionStatement(expr, !insideFunction()); + AstNode n = new ExpressionStatement(expr, !insideFunctionBody()); n.setLineColumnNumber(expr.getLineno(), expr.getColumn()); return n; } @@ -2229,7 +2241,7 @@ private AstNode nameOrLabel() throws IOException { currentFlaggedToken |= TI_CHECK_LABEL; expr = expr(false); if (expr.getType() != Token.LABEL) { - stmt = new ExpressionStatement(expr, !insideFunction()); + stmt = new ExpressionStatement(expr, !insideFunctionBody()); autoInsertSemicolon(stmt); break; } @@ -2378,7 +2390,7 @@ private AstNode let(boolean isStatement, int pos) throws IOException { pn.setBody(expr); if (isStatement) { // let expression in statement context - ExpressionStatement es = new ExpressionStatement(pn, !insideFunction()); + ExpressionStatement es = new ExpressionStatement(pn, !insideFunctionBody()); es.setLineColumnNumber(pn.getLineno(), pn.getColumn()); return es; } @@ -3390,7 +3402,7 @@ private AstNode primaryExpr() throws IOException { } case Token.SUPER: - if (insideFunction() && insideMethod) { + if ((insideFunctionParams() || insideFunctionBody()) && insideMethod) { consumeToken(); pos = ts.tokenBeg; end = ts.tokenEnd; @@ -4122,7 +4134,7 @@ private AstNode createNumericLiteral(int tt, boolean isProperty) { } protected void checkActivationName(String name, int token) { - if (!insideFunction()) { + if (!insideFunctionBody()) { return; } boolean activation = false; @@ -4147,7 +4159,7 @@ protected void checkActivationName(String name, int token) { } protected void setRequiresActivation() { - if (insideFunction()) { + if (insideFunctionBody()) { ((FunctionNode) currentScriptOrFn).setRequiresActivation(); } } @@ -4160,7 +4172,7 @@ private void checkCallRequiresActivation(AstNode pn) { } protected void setIsGenerator() { - if (insideFunction()) { + if (insideFunctionBody()) { ((FunctionNode) currentScriptOrFn).setIsGenerator(); } } diff --git a/rhino/src/test/java/org/mozilla/javascript/SuperTest.java b/rhino/src/test/java/org/mozilla/javascript/SuperTest.java index 5927d707ea..c2710ad8b2 100644 --- a/rhino/src/test/java/org/mozilla/javascript/SuperTest.java +++ b/rhino/src/test/java/org/mozilla/javascript/SuperTest.java @@ -184,6 +184,17 @@ void getterWithThis() { Utils.assertWithAllOptimizationLevelsES6("bxy", script); } + @Test + void superInDefaultArguments() { + String script = + "" + + "const a = { x: 'a'};\n" + + "const b = { x: 'b', f(p = super.x) { return p; } };\n" + + "Object.setPrototypeOf(b, a);\n" + + "b.f();"; + Utils.assertWithAllOptimizationLevelsES6("a", script); + } + @Test void usesHomeObjectAndIgnoresThis1() { String script = diff --git a/tests/testsrc/test262.properties b/tests/testsrc/test262.properties index 98ce45ff85..81df206773 100644 --- a/tests/testsrc/test262.properties +++ b/tests/testsrc/test262.properties @@ -4953,7 +4953,7 @@ language/expressions/new 41/59 (69.49%) ~language/expressions/new.target -language/expressions/object 785/1169 (67.15%) +language/expressions/object 784/1169 (67.07%) dstr/async-gen-meth-ary-init-iter-close.js {unsupported: [async-iteration, async]} dstr/async-gen-meth-ary-init-iter-get-err.js {unsupported: [async-iteration]} dstr/async-gen-meth-ary-init-iter-get-err-array-prototype.js {unsupported: [async-iteration]} @@ -5640,7 +5640,6 @@ language/expressions/object 785/1169 (67.15%) method-definition/name-prop-name-yield-expr.js non-strict method-definition/name-prop-name-yield-id.js non-strict method-definition/name-prototype-prop.js - method-definition/name-super-prop-param.js method-definition/object-method-returns-promise.js {unsupported: [async-functions]} method-definition/params-dflt-gen-meth-args-unmapped.js method-definition/params-dflt-gen-meth-ref-arguments.js