Skip to content

Commit 93057c9

Browse files
committed
Handle nested destructuring patterns with defaults
1 parent d90c055 commit 93057c9

File tree

3 files changed

+183
-287
lines changed

3 files changed

+183
-287
lines changed

rhino/src/main/java/org/mozilla/javascript/Parser.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4632,7 +4632,35 @@ private void processDestructuringDefaults(
46324632
destructuringNames.add(name);
46334633
}
46344634
} else {
4635-
// TODO: should handle other nested values on the lhs (ArrayLiteral, ObjectLiteral)
4635+
// Handle nested destructuring patterns with defaults, eg: [[x, y, z] = [4, 5, 6]]
4636+
if (left instanceof ArrayLiteral || left instanceof ObjectLiteral) {
4637+
right = (transformer != null) ? transformer.transform(n.getRight()) : n.getRight();
4638+
4639+
Node cond_default =
4640+
new Node(
4641+
Token.HOOK,
4642+
new Node(
4643+
Token.SHEQ,
4644+
new KeywordLiteral().setType(Token.UNDEFINED),
4645+
rightElem),
4646+
right,
4647+
rightElem);
4648+
4649+
if (transformer == null) {
4650+
currentScriptOrFn.putDestructuringRvalues(cond_default, right);
4651+
}
4652+
4653+
parent.addChildToBack(
4654+
destructuringAssignmentHelper(
4655+
variableType,
4656+
left,
4657+
cond_default,
4658+
currentScriptOrFn.getNextTempName(),
4659+
null,
4660+
transformer));
4661+
} else {
4662+
reportError("msg.bad.assign.left");
4663+
}
46364664
}
46374665
}
46384666

tests/src/test/java/org/mozilla/javascript/tests/es6/ArrayDestructuringTest.java

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,121 @@ public void strangeCase2() {
2626
public void strangeCase3() {
2727
Utils.assertWithAllModes(123, "[0..toString.h] = [123]; Number.prototype.toString.h");
2828
}
29+
30+
/** Test nested array destructuring with default values in arrow function parameters. */
31+
@Test
32+
public void nestedArrayDestructuringWithDefaults() {
33+
Utils.runWithAllModes(
34+
cx -> {
35+
cx.setLanguageVersion(org.mozilla.javascript.Context.VERSION_ES6);
36+
org.mozilla.javascript.ScriptableObject scope = cx.initStandardObjects();
37+
38+
String script =
39+
"assert = { sameValue(a, b, e) { if (a !== b) throw e; } };\n"
40+
+ "var callCount = 0;\n"
41+
+ "var f;\n"
42+
+ "f = ([[x, y, z] = [4, 5, 6]]) => {\n"
43+
+ " assert.sameValue(x, 7);\n"
44+
+ " assert.sameValue(y, 8);\n"
45+
+ " assert.sameValue(z, 9);\n"
46+
+ " callCount = callCount + 1;\n"
47+
+ "};\n"
48+
+ "f([[7, 8, 9]]);\n"
49+
+ "assert.sameValue(callCount, 1, 'arrow function invoked exactly once');";
50+
51+
cx.evaluateString(scope, script, "test", 1, null);
52+
return null;
53+
});
54+
}
55+
56+
/** Test nested array destructuring with default values being used when argument is missing. */
57+
@Test
58+
public void nestedArrayDestructuringWithDefaultsUsed() {
59+
Utils.runWithAllModes(
60+
cx -> {
61+
cx.setLanguageVersion(org.mozilla.javascript.Context.VERSION_ES6);
62+
org.mozilla.javascript.ScriptableObject scope = cx.initStandardObjects();
63+
64+
String script =
65+
"assert = { sameValue(a, b, e) { if (a !== b) throw e; } };\n"
66+
+ "var f = ([[x, y, z] = [4, 5, 6]]) => {\n"
67+
+ " assert.sameValue(x, 4);\n"
68+
+ " assert.sameValue(y, 5);\n"
69+
+ " assert.sameValue(z, 6);\n"
70+
+ "};\n"
71+
+ "f([]);\n";
72+
73+
cx.evaluateString(scope, script, "test", 1, null);
74+
return null;
75+
});
76+
}
77+
78+
/** Test nested object destructuring with default values in arrow function parameters. */
79+
@Test
80+
public void nestedObjectDestructuringWithDefaults() {
81+
Utils.runWithAllModes(
82+
cx -> {
83+
cx.setLanguageVersion(org.mozilla.javascript.Context.VERSION_ES6);
84+
org.mozilla.javascript.ScriptableObject scope = cx.initStandardObjects();
85+
86+
String script =
87+
"assert = { sameValue(a, b, e) { if (a !== b) throw e; } };\n"
88+
+ "var f = ([{x, y} = {x: 4, y: 5}]) => {\n"
89+
+ " assert.sameValue(x, 7);\n"
90+
+ " assert.sameValue(y, 8);\n"
91+
+ "};\n"
92+
+ "f([{x: 7, y: 8}]);\n";
93+
94+
cx.evaluateString(scope, script, "test", 1, null);
95+
return null;
96+
});
97+
}
98+
99+
/** Test multiple nested arrays with defaults. */
100+
@Test
101+
public void multipleNestedArraysWithDefaults() {
102+
Utils.runWithAllModes(
103+
cx -> {
104+
cx.setLanguageVersion(org.mozilla.javascript.Context.VERSION_ES6);
105+
org.mozilla.javascript.ScriptableObject scope = cx.initStandardObjects();
106+
107+
String script =
108+
"assert = { sameValue(a, b, e) { if (a !== b) throw e; } };\n"
109+
+ "var f = ([[a, b] = [1, 2], [c, d] = [3, 4]]) => {\n"
110+
+ " assert.sameValue(a, 10);\n"
111+
+ " assert.sameValue(b, 20);\n"
112+
+ " assert.sameValue(c, 30);\n"
113+
+ " assert.sameValue(d, 40);\n"
114+
+ "};\n"
115+
+ "f([[10, 20], [30, 40]]);\n";
116+
117+
cx.evaluateString(scope, script, "test", 1, null);
118+
return null;
119+
});
120+
}
121+
122+
/**
123+
* Test that default value is NOT used when element exists (even if empty array). Based on
124+
* test262: language/statements/const/dstr/ary-ptrn-elem-ary-elision-iter.js
125+
*/
126+
@Test
127+
public void nestedArrayDefaultNotUsedWhenElementExists() {
128+
Utils.runWithAllModes(
129+
cx -> {
130+
cx.setLanguageVersion(org.mozilla.javascript.Context.VERSION_ES6);
131+
org.mozilla.javascript.ScriptableObject scope = cx.initStandardObjects();
132+
133+
String script =
134+
"var callCount = 0;\n"
135+
+ "function g() {\n"
136+
+ " callCount += 1;\n"
137+
+ " return [];\n"
138+
+ "}\n"
139+
+ "const [[,] = g()] = [[]];\n"
140+
+ "if (callCount !== 0) throw new Error('Expected callCount to be 0, got ' + callCount);";
141+
142+
cx.evaluateString(scope, script, "test", 1, null);
143+
return null;
144+
});
145+
}
29146
}

0 commit comments

Comments
 (0)