Skip to content

Commit

Permalink
WIP: Destructuring
Browse files Browse the repository at this point in the history
  • Loading branch information
0xe committed Jun 14, 2024
1 parent 44e544f commit 5ff61be
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2531,6 +2531,10 @@ void decompile(AstNode node) {
case Token.THIS:
decompiler.addToken(node.getType());
break;
case Token.ASSIGN:
decompile(((Assignment)node).getLeft());
decompile(((Assignment)node).getRight());
break;
default:
Kit.codeBug("unexpected token: " + Token.typeToName(node.getType()));
}
Expand Down
139 changes: 130 additions & 9 deletions rhino-runtime/src/main/java/org/mozilla/javascript/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,8 @@ private FunctionNode function(int type, boolean isGenerator) throws IOException
&& (compilerEnv.getLanguageVersion() >= Context.VERSION_ES6)) {
// ES6 generator function
return function(type, true);
} else if (matchToken(Token.ASSIGN, true)) {
AstNode rhs = assignExpr(); // TODO(satish)
} else {
if (compilerEnv.isAllowMemberExprAsFunctionName()) {
// Note that memberExpr can not start with '(' like
Expand Down Expand Up @@ -2404,13 +2406,6 @@ private AstNode assignExpr() throws IOException {
tt = peekToken();
}
if (Token.FIRST_ASSIGN <= tt && tt <= Token.LAST_ASSIGN) {
if (inDestructuringAssignment) {
// default values inside destructuring assignments,
// like 'var [a = 10] = b' or 'var {a: b = 10} = c',
// are not supported
reportError("msg.destruct.default.vals");
}

consumeToken();

// Pull out JSDoc info and reset it before recursing.
Expand Down Expand Up @@ -3144,7 +3139,7 @@ private XmlElemRef xmlElemRef(int atPos, Name namespace, int colonPos) throws IO
private AstNode destructuringPrimaryExpr() throws IOException, ParserException {
try {
inDestructuringAssignment = true;
return primaryExpr();
return assignExpr();
} finally {
inDestructuringAssignment = false;
}
Expand Down Expand Up @@ -3598,7 +3593,41 @@ private ObjectLiteral objectLiteral() throws IOException {
// many tokens.)
int peeked = peekToken();
if (peeked != Token.COMMA && peeked != Token.COLON && peeked != Token.RC) {
if (peeked == Token.LP) {
if (peeked == Token.ASSIGN) {
if (compilerEnv.getLanguageVersion() >= Context.VERSION_ES6) {
FunctionNode fnNode = (FunctionNode) currentScriptOrFn;
Object[] existing = fnNode.getDefaultParams();
Object[] current;
int current_size = 0;
if (existing == null) {
existing = new Object[2];
fnNode.setDefaultParams(existing);
current = existing;
} else {
current = new Object[existing.length + 2];
System.arraycopy(existing, 0, current, 0, existing.length);
current_size = existing.length;
existing = null;
}

if (matchToken(Token.ASSIGN, true)) {
AstNode valueNode = assignExpr();

current[current_size] = propertyName;
current[current.length - 1] = valueNode;
fnNode.setDefaultParams(current);

fnNode.addChildToBack(
new Node(Token.SETNAME, createName(Token.BINDNAME, propertyName, null), valueNode));
// TODO(satish):
// defineSymbol(Token.SETNAME, propertyName, true);
// List<String> destructuringNames =
// (List<String>) fnNode.getProp(Node.DESTRUCTURING_NAMES);
// destructuringNames.add(propertyName);
continue;
}
}
} else if (peeked == Token.LP) {
entryKind = METHOD_ENTRY;
} else if (pname.getType() == Token.NAME) {
if ("get".equals(propertyName)) {
Expand Down Expand Up @@ -4111,6 +4140,40 @@ Node destructuringAssignmentHelper(int variableType, Node left, Node right, Stri
}
comma.addChildToBack(simpleAssignment(left, createName(tempName)));
break;
case Token.ASSIGN:
if (compilerEnv.getLanguageVersion() >= Context.VERSION_ES6) {
FunctionNode fnNode = (FunctionNode) currentScriptOrFn;
Object[] existing = fnNode.getDefaultParams();
Object[] current;
int current_size = 0;
if (existing == null) {
existing = new Object[2];
fnNode.setDefaultParams(existing);
current = existing;
} else {
current = new Object[existing.length + 2];
System.arraycopy(existing, 0, current, 0, existing.length);
current_size = existing.length;
existing = null;
}
AstNode paramNode = ((Assignment) left).getLeft();
AstNode valueNode = ((Assignment) left).getRight();

current[current_size] = tempName;
current[current.length - 1] = valueNode;
fnNode.setDefaultParams(current);

comma.addChildToBack( // TODO(satish): what should be the op?
new Node(Token.SETNAME, createName(Token.BINDNAME, tempName, null), valueNode));
if (variableType != -1) {
defineSymbol(variableType, tempName, true);
destructuringNames.add(tempName);
}
createDestructuringAssignment(variableType, paramNode, createName(currentScriptOrFn.getNextTempName()));
} else { // TODO(satish): appropriate error?
reportError("msg.bad.assign.left");
}
break;
default:
reportError("msg.bad.assign.left");
}
Expand Down Expand Up @@ -4145,6 +4208,35 @@ boolean destructuringArray(
defineSymbol(variableType, name, true);
destructuringNames.add(name);
}
} else if (n.getType() == Token.ASSIGN
&& compilerEnv.getLanguageVersion() >= Context.VERSION_ES6) {
FunctionNode fnNode = (FunctionNode) currentScriptOrFn;
Object[] existing = fnNode.getDefaultParams();
Object[] current;
int current_size = 0;
if (existing == null) {
existing = new Object[2];
fnNode.setDefaultParams(existing);
current = existing;
} else {
current = new Object[existing.length + 2];
System.arraycopy(existing, 0, current, 0, existing.length);
current_size = existing.length;
existing = null;
}
AstNode paramNode = ((Assignment) n).getLeft();
AstNode valueNode = ((Assignment) n).getRight();

current[current_size] = paramNode.getString();
current[current.length - 1] = valueNode;
fnNode.setDefaultParams(current);

parent.addChildToBack(
new Node(setOp, createName(Token.BINDNAME, paramNode.getString(), null), rightElem));
if (variableType != -1) {
defineSymbol(variableType, paramNode.getString(), true);
destructuringNames.add(paramNode.getString());
}
} else {
parent.addChildToBack(
destructuringAssignmentHelper(
Expand Down Expand Up @@ -4197,6 +4289,35 @@ boolean destructuringObject(
defineSymbol(variableType, name, true);
destructuringNames.add(name);
}
} else if (value.getType() == Token.ASSIGN
&& compilerEnv.getLanguageVersion() >= Context.VERSION_ES6) {
FunctionNode fnNode = (FunctionNode) currentScriptOrFn;
Object[] existing = fnNode.getDefaultParams();
Object[] current;
int current_size = 0;
if (existing == null) {
existing = new Object[2];
fnNode.setDefaultParams(existing);
current = existing;
} else {
current = new Object[existing.length + 2];
System.arraycopy(existing, 0, current, 0, existing.length);
current_size = existing.length;
existing = null;
}
AstNode paramNode = ((Assignment) value).getLeft();
AstNode valueNode = ((Assignment) value).getRight();

current[current_size] = paramNode.getString();
current[current.length - 1] = valueNode;
fnNode.setDefaultParams(current);

parent.addChildToBack(
new Node(setOp, createName(Token.BINDNAME, paramNode.getString(), null), rightElem));
if (variableType != -1) {
defineSymbol(variableType, paramNode.getString(), true);
destructuringNames.add(paramNode.getString());
}
} else {
parent.addChildToBack(
destructuringAssignmentHelper(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public void functionDefaultArgsUsage() throws Exception {
}

@Test
@Ignore("temporal-dead-zone")
// @Ignore("temporal-dead-zone")
public void functionDefaultArgsMultiFollowUsage() throws Exception {
final String script =
"function f(a = go()) {\n"
Expand All @@ -66,7 +66,7 @@ public void functionDefaultArgsMultiFollowUsage() throws Exception {
}

@Test
@Ignore("temporal-dead-zone")
// @Ignore("temporal-dead-zone")
public void functionDefaultArgsMultiReferEarlier() throws Exception {
final String script = "var f = function(a = b * 2, b = 3) { return a * b; }\n";
assertThrows("ReferenceError: \"b\" is not defined.", script + "\nf()");
Expand All @@ -81,26 +81,44 @@ public void functionConstructor() throws Exception {
}

@Test
@Ignore("destructuring-not-supported")
public void destructuringAssigmentDefaultArray() throws Exception {
final String script = "function f([x = 1, y = 2] = []) {\n" + " return x + y;\n" + "}\n";
final String script = "function f([x = 1, y = 2] = [], [z = 1] = [4]) {\n" +
" return x + y + z;\n" +
"}";

assertIntEvaluates(3, script + "f()");
assertIntEvaluates(3, script + "f([])");
assertIntEvaluates(4, script + "f([2])");
assertIntEvaluates(5, script + "f([2, 3])");
}

@Test
@Ignore("destructuring-not-supported")
public void destructuringAssigmentBasicArray() throws Exception {
final String script = "function f([x = 1] = [2]) {\n" +
" return x;\n" +
"}";

// assertIntEvaluates(1, script + "f([])");
assertIntEvaluates(2, script + "f()");
// assertIntEvaluates(3, script + "f([3])");
}

@Test
public void destructuringAssigmentBasicObject() throws Exception {
final String script = "function f({x = 1} = {x: 2}) {\n" +
" return x;\n" +
"}";

assertIntEvaluates(1, script + "f({})");
assertIntEvaluates(2, script + "f()");
assertIntEvaluates(3, script + "f({x: 3})");
}

@Test
public void destructuringAssigmentDefaultObject() throws Exception {
final String script =
"function f({ z = 3 } = {}) {\n"
+ " return z;\n"
+ "}\n"
+ "\n"
+ "f();\n"
+ "f({});\n"
+ "f({ z: 2 });";
final String script = "function f({ z = 3, x = 2 } = {}) {\n" +
" return z;\n" +
"}\n";
assertIntEvaluates(3, script + "f()");
assertIntEvaluates(3, script + "f({})");
assertIntEvaluates(2, script + "f({z: 2})");
Expand Down Expand Up @@ -195,21 +213,21 @@ private static void assertIntEvaluatesWithLanguageLevel(
final Object expected, final String source, int languageLevel) {
Utils.runWithAllOptimizationLevels(
cx -> {
// if (cx.getOptimizationLevel() == 0) {
int oldVersion = cx.getLanguageVersion();
cx.setLanguageVersion(languageLevel);
try {
final Scriptable scope = cx.initStandardObjects();
final Object rep = cx.evaluateString(scope, source, "test.js", 0, null);
if (rep instanceof Double)
assertEquals((int) expected, ((Double) rep).intValue());
else assertEquals(expected, rep);
return null;
} finally {
cx.setLanguageVersion(oldVersion);
if (cx.getOptimizationLevel() == -1) {
int oldVersion = cx.getLanguageVersion();
cx.setLanguageVersion(languageLevel);
try {
final Scriptable scope = cx.initStandardObjects();
final Object rep = cx.evaluateString(scope, source, "test.js", 0, null);
if (rep instanceof Double)
assertEquals((int) expected, ((Double) rep).intValue());
else assertEquals(expected, rep);
return null;
} finally {
cx.setLanguageVersion(oldVersion);
}
}
// }
// return null;
return null;
});
}
}

0 comments on commit 5ff61be

Please sign in to comment.