Skip to content

Commit

Permalink
Merge pull request #3 from andreabergia/super-function-args
Browse files Browse the repository at this point in the history
Allow super in default arguments
  • Loading branch information
andreabergia authored Dec 4, 2024
2 parents 5a3d4e2 + b471915 commit 4244ad6
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 133 deletions.
274 changes: 143 additions & 131 deletions rhino/src/main/java/org/mozilla/javascript/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<String, Node> destructuring = null;
Map<String, AstNode> destructuringDefault = null;

Set<String> 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<String, Node> destructuring = null;
Map<String, AstNode> 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<String> 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<String, Node> 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<String, Node> 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;
}
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand All @@ -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()) {
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -4147,7 +4159,7 @@ protected void checkActivationName(String name, int token) {
}

protected void setRequiresActivation() {
if (insideFunction()) {
if (insideFunctionBody()) {
((FunctionNode) currentScriptOrFn).setRequiresActivation();
}
}
Expand All @@ -4160,7 +4172,7 @@ private void checkCallRequiresActivation(AstNode pn) {
}

protected void setIsGenerator() {
if (insideFunction()) {
if (insideFunctionBody()) {
((FunctionNode) currentScriptOrFn).setIsGenerator();
}
}
Expand Down
Loading

0 comments on commit 4244ad6

Please sign in to comment.