Skip to content

Commit

Permalink
Fix bug 737431 - Destructuring assignment doesn't work in expression …
Browse files Browse the repository at this point in the history
…closures parameters.

This creates a body node for expression closures, allowing extra code for destructuring parameters to be handled properly.
  • Loading branch information
hns committed Mar 20, 2012
1 parent 6722b41 commit 4607754
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 46 deletions.
5 changes: 3 additions & 2 deletions src/org/mozilla/javascript/IRFactory.java
Expand Up @@ -962,14 +962,15 @@ private Node transformRegExp(RegExpLiteral node) {
}

private Node transformReturn(ReturnStatement node) {
if (Boolean.TRUE.equals(node.getProp(Node.EXPRESSION_CLOSURE_PROP))) {
boolean expClosure = Boolean.TRUE.equals(node.getProp(Node.EXPRESSION_CLOSURE_PROP));
if (expClosure) {
decompiler.addName(" ");
} else {
decompiler.addToken(Token.RETURN);
}
AstNode rv = node.getReturnValue();
Node value = rv == null ? null : transform(rv);
decompiler.addEOL(Token.SEMI);
if (!expClosure) decompiler.addEOL(Token.SEMI);
return rv == null
? new Node(Token.RETURN, node.getLineno())
: new Node(Token.RETURN, value, node.getLineno());
Expand Down
79 changes: 37 additions & 42 deletions src/org/mozilla/javascript/Parser.java
Expand Up @@ -619,11 +619,13 @@ private AstRoot parse() throws IOException
private AstNode parseFunctionBody()
throws IOException
{
boolean isExpressionClosure = false;
if (!matchToken(Token.LC)) {
if (compilerEnv.getLanguageVersion() < Context.VERSION_1_8) {
reportError("msg.no.brace.body");
} else {
isExpressionClosure = true;
}
return parseFunctionBodyExpr();
}
++nestingOfFunction;
int pos = ts.tokenBeg;
Expand All @@ -635,32 +637,41 @@ private AstNode parseFunctionBody()

pn.setLineno(ts.lineno);
try {
bodyLoop: for (;;) {
AstNode n;
int tt = peekToken();
switch (tt) {
case Token.ERROR:
case Token.EOF:
case Token.RC:
break bodyLoop;

case Token.FUNCTION:
consumeToken();
n = function(FunctionNode.FUNCTION_STATEMENT);
break;
default:
n = statement();
if (inDirectivePrologue) {
String directive = getDirective(n);
if (directive == null) {
inDirectivePrologue = false;
} else if (directive.equals("use strict")) {
inUseStrictDirective = true;
}
if (isExpressionClosure) {
ReturnStatement n = new ReturnStatement(ts.lineno);
n.setReturnValue(assignExpr());
// expression closure flag is required on both nodes
n.putProp(Node.EXPRESSION_CLOSURE_PROP, Boolean.TRUE);
pn.putProp(Node.EXPRESSION_CLOSURE_PROP, Boolean.TRUE);
pn.addStatement(n);
} else {
bodyLoop: for (;;) {
AstNode n;
int tt = peekToken();
switch (tt) {
case Token.ERROR:
case Token.EOF:
case Token.RC:
break bodyLoop;

case Token.FUNCTION:
consumeToken();
n = function(FunctionNode.FUNCTION_STATEMENT);
break;
default:
n = statement();
if (inDirectivePrologue) {
String directive = getDirective(n);
if (directive == null) {
inDirectivePrologue = false;
} else if (directive.equals("use strict")) {
inUseStrictDirective = true;
}
}
break;
}
break;
pn.addStatement(n);
}
pn.addStatement(n);
}
} catch (ParserException e) {
// Ignore it
Expand All @@ -671,7 +682,7 @@ private AstNode parseFunctionBody()

int end = ts.tokenEnd;
getAndResetJsDoc();
if (mustMatchToken(Token.RC, "msg.no.brace.after.body"))
if (!isExpressionClosure && mustMatchToken(Token.RC, "msg.no.brace.after.body"))
end = ts.tokenEnd;
pn.setLength(end - pos);
return pn;
Expand Down Expand Up @@ -751,22 +762,6 @@ private void parseFunctionParams(FunctionNode fnNode)
}
}


private AstNode parseFunctionBodyExpr()
throws IOException
{
++nestingOfFunction;
int lineno = ts.getLineno();
ReturnStatement n = new ReturnStatement(lineno);
n.putProp(Node.EXPRESSION_CLOSURE_PROP, Boolean.TRUE);
try {
n.setReturnValue(assignExpr());
} finally {
--nestingOfFunction;
}
return n;
}

private FunctionNode function(int type)
throws IOException
{
Expand Down
4 changes: 2 additions & 2 deletions src/org/mozilla/javascript/ast/FunctionNode.java
Expand Up @@ -418,9 +418,9 @@ public String toSource(int depth) {
}
if (isExpressionClosure) {
AstNode body = getBody();
if (body instanceof ReturnStatement) {
if (body.getLastChild() instanceof ReturnStatement) {
// omit "return" keyword, just print the expression
body = ((ReturnStatement) body).getReturnValue();
body = ((ReturnStatement) body.getLastChild()).getReturnValue();
sb.append(body.toSource(0));
if (functionType == FUNCTION_STATEMENT) {
sb.append(";");
Expand Down
38 changes: 38 additions & 0 deletions testsrc/doctests/expressionclosure.doctest
@@ -0,0 +1,38 @@
js> version(180)
0
js> x = function(x) x;

function (x) x

js> x.toSource()
(function (x) x)
js> x(123) === 123
true
js> x = function([a, b]) a + b;

function ([a, b]) a + b

js> x([1, 2])
3
js> x.toSource()
(function ([a, b]) a + b)
js> function outer() {
> var k = function(a) a + 1;
> return function(b) k(b) * 2;
> }
js> outer

function outer() {
var k = function (a) a + 1;
return function (b) k(b) * 2;
}

js> outer()

function (b) k(b) * 2

js> outer()(4)
10
js> outer()(5)
12

0 comments on commit 4607754

Please sign in to comment.