OLD | NEW |
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 /* ***** BEGIN LICENSE BLOCK ***** | 2 /* ***** BEGIN LICENSE BLOCK ***** |
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | 3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
4 * | 4 * |
5 * The contents of this file are subject to the Mozilla Public License Version | 5 * The contents of this file are subject to the Mozilla Public License Version |
6 * 1.1 (the "License"); you may not use this file except in compliance with | 6 * 1.1 (the "License"); you may not use this file except in compliance with |
7 * the License. You may obtain a copy of the License at | 7 * the License. You may obtain a copy of the License at |
8 * http://www.mozilla.org/MPL/ | 8 * http://www.mozilla.org/MPL/ |
9 * | 9 * |
10 * Software distributed under the License is distributed on an "AS IS" basis, | 10 * Software distributed under the License is distributed on an "AS IS" basis, |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
60 // |genexp| must have the exact same whitespace the decompiler uses | 60 // |genexp| must have the exact same whitespace the decompiler uses |
61 genexp = "x * x for (x in [])"; | 61 genexp = "x * x for (x in [])"; |
62 genexpParened = "(" + genexp + ")"; | 62 genexpParened = "(" + genexp + ")"; |
63 genexpParenedTwice = "(" + genexpParened + ")"; | 63 genexpParenedTwice = "(" + genexpParened + ")"; |
64 | 64 |
65 // Warning: be careful not to put [] around stuff, because that would | 65 // Warning: be careful not to put [] around stuff, because that would |
66 // cause it to be treated as an array comprehension instead of a | 66 // cause it to be treated as an array comprehension instead of a |
67 // generator expression! | 67 // generator expression! |
68 | 68 |
69 // Statements | 69 // Statements |
70 doesNotNeedParens("if (xx) { }"); | 70 doesNotNeedParens(1, "if (xx) { }"); |
71 needParens("if (1, xx) { }"); | 71 needParens(2, "if (1, xx) { }"); |
72 needParens("if (xx, 1) { }"); | 72 needParens(3, "if (xx, 1) { }"); |
73 doesNotNeedParens("do { } while (xx);"); | 73 doesNotNeedParens(4, "do { } while (xx);"); |
74 doesNotNeedParens("while (xx) { }"); | 74 doesNotNeedParens(5, "while (xx) { }"); |
75 doesNotNeedParens("switch (xx) { }"); | 75 doesNotNeedParens(6, "switch (xx) { }"); |
76 doesNotNeedParens("with (xx) { }"); | 76 doesNotNeedParens(7, "with (xx) { }"); |
77 needParens("switch (x) { case xx: }"); | 77 needParens(8, "switch (x) { case xx: }"); |
78 needParens("return xx;"); | 78 needParens(9, "return xx;"); |
79 needParens("yield xx;"); | 79 needParens(10, "yield xx;"); |
80 needParens("for (xx;;) { }"); | 80 needParens(11, "for (xx;;) { }"); |
81 needParens("for (;xx;) { }", "function anonymous() {\n for (;;) {\n }\n}")
; | 81 needParens(12, "for (;xx;) { }", "function anonymous() {\n for (;;) {\n }\
n}"); |
82 needParens("for (;;xx) { }"); | 82 needParens(13, "for (;;xx) { }"); |
83 needParens("for (i in xx) { }"); | 83 needParens(14, "for (i in xx) { }"); |
84 needParens("throw xx"); | 84 needParens(15, "throw xx"); |
85 needParens("try { } catch (e if xx) { }"); | 85 needParens(16, "try { } catch (e if xx) { }"); |
86 needParens("let (x=3) xx"); | 86 needParens(17, "let (x=3) xx"); |
87 needParens("let (x=xx) 3"); | 87 needParens(18, "let (x=xx) 3"); |
88 | 88 |
89 // Function calls | 89 // Function calls |
90 doesNotNeedParens("f(xx);"); | 90 doesNotNeedParens(19, "f(xx);"); |
91 needParens("f(xx, 1);"); | 91 needParens(20, "f(xx, 1);"); |
92 needParens("f(1, xx);"); | 92 needParens(21, "f(1, xx);"); |
93 doesNotNeedParens("/x/(xx);"); | 93 doesNotNeedParens(22, "/x/(xx);"); |
94 needParens("/x/(xx, 1);"); | 94 needParens(23, "/x/(xx, 1);"); |
95 needParens("/x/(1, xx);"); | 95 needParens(24, "/x/(1, xx);"); |
96 | 96 |
97 // eval is special and often confuses the decompiler. | 97 // eval is special and often confuses the decompiler. |
98 doesNotNeedParens("eval(xx);"); | 98 doesNotNeedParens(25, "eval(xx);"); |
99 needParens("eval(xx, 1);"); | 99 needParens(26, "eval(xx, 1);"); |
100 needParens("eval(1, xx);"); | 100 needParens(27, "eval(1, xx);"); |
101 | 101 |
102 // Expressions | 102 // Expressions |
103 needParens("xx;"); // ??? | 103 needParens(28, "xx;"); // ??? |
104 needParens("var g = xx;"); // ??? | 104 needParens(29, "var g = xx;"); // ??? |
105 needParens("g += xx;"); | 105 needParens(30, "g += xx;"); |
106 needParens("xx();"); | 106 needParens(31, "xx();"); |
107 needParens("xx() = 3;"); | 107 needParens(32, "xx() = 3;"); |
108 needParens("a ? xx : c"); | 108 needParens(33, "a ? xx : c"); |
109 needParens("xx ? b : c"); | 109 needParens(34, "xx ? b : c"); |
110 needParens("a ? b : xx"); | 110 needParens(35, "a ? b : xx"); |
111 needParens("1 ? xx : c"); | 111 needParens(36, "1 ? xx : c"); |
112 needParens("0 ? b : xx"); | 112 needParens(37, "0 ? b : xx"); |
113 needParens("1 + xx"); | 113 needParens(38, "1 + xx"); |
114 needParens("xx + 1"); | 114 needParens(39, "xx + 1"); |
115 needParens("1, xx"); | 115 needParens(40, "1, xx"); |
116 doesNotNeedParens("+(xx)"); | 116 doesNotNeedParens(41, "+(xx)"); |
117 doesNotNeedParens("!(xx)"); | 117 doesNotNeedParens(42, "!(xx)"); |
118 needParens("xx, 1"); | 118 needParens(43, "xx, 1"); |
119 needParens("[1, xx]"); | 119 needParens(44, "[1, xx]"); |
120 needParens("[xx, 1]"); | 120 needParens(45, "[xx, 1]"); |
121 needParens("[#1=xx,3]"); | 121 needParens(46, "[#1=xx,3]"); |
122 needParens("[#1=xx,#1#]"); | 122 needParens(47, "[#1=xx,#1#]"); |
123 needParens("xx.p"); | 123 needParens(48, "xx.p"); |
124 needParens("xx.@p"); | 124 needParens(49, "xx.@p"); |
125 needParens("typeof xx;"); | 125 needParens(50, "typeof xx;"); |
126 needParens("void xx;"); | 126 needParens(51, "void xx;"); |
127 needParens("({ a: xx })"); | 127 needParens(52, "({ a: xx })"); |
128 needParens("({ a: 1, b: xx })"); | 128 needParens(53, "({ a: 1, b: xx })"); |
129 needParens("({ a: xx, b: 1 })"); | 129 needParens(54, "({ a: xx, b: 1 })"); |
130 needParens("({ a getter: xx })"); | 130 needParens(55, "({ a getter: xx })"); |
131 needParens("<x a={xx}/>"); | 131 needParens(56, "<x a={xx}/>"); |
132 doesNotNeedParens("new (xx);"); | 132 doesNotNeedParens(57, "new (xx);"); |
133 doesNotNeedParens("new a(xx);"); | 133 doesNotNeedParens(58, "new a(xx);"); |
134 | 134 |
135 | 135 |
136 // Generator expressions cannot be used as LHS, even though they're syntactic | 136 // Generator expressions cannot be used as LHS, even though they're syntactic |
137 // sugar for something that looks a lot like an "lvalue return": (f() = 3). | 137 // sugar for something that looks a lot like an "lvalue return": (f() = 3). |
138 | 138 |
139 rejectLHS("++ (xx);"); | 139 rejectLHS(59, "++ (xx);"); |
140 rejectLHS("delete xx;"); | 140 rejectLHS(60, "delete xx;"); |
141 rejectLHS("delete (xx);"); | 141 rejectLHS(61, "delete (xx);"); |
142 rejectLHS("for (xx in []) { }"); | 142 rejectLHS(62, "for (xx in []) { }"); |
143 rejectLHS("for ((xx) in []) { }"); | 143 rejectLHS(63, "for ((xx) in []) { }"); |
144 rejectLHS("try { } catch(xx) { }"); | 144 rejectLHS(64, "try { } catch(xx) { }"); |
145 rejectLHS("try { } catch([(xx)]) { }"); | 145 rejectLHS(65, "try { } catch([(xx)]) { }"); |
146 rejectLHS("xx += 3;"); | 146 rejectLHS(66, "xx += 3;"); |
147 rejectLHS("(xx) += 3;"); | 147 rejectLHS(67, "(xx) += 3;"); |
148 rejectLHS("xx = 3;"); | 148 rejectLHS(68, "xx = 3;"); |
149 | 149 |
150 // Assignment | 150 // Assignment |
151 rejectLHS(" (xx) = 3;"); | 151 rejectLHS(69, " (xx) = 3;"); |
152 rejectLHS("var (xx) = 3;"); | 152 rejectLHS(70, "var (xx) = 3;"); |
153 rejectLHS("const (xx) = 3;"); | 153 rejectLHS(71, "const (xx) = 3;"); |
154 rejectLHS("let (xx) = 3;"); | 154 rejectLHS(72, "let (xx) = 3;"); |
155 | 155 |
156 // Destructuring assignment | 156 // Destructuring assignment |
157 rejectLHS(" [(xx)] = 3;"); | 157 rejectLHS(73, " [(xx)] = 3;"); |
158 rejectLHS("var [(xx)] = 3;"); | 158 rejectLHS(74, "var [(xx)] = 3;"); |
159 rejectLHS("const [(xx)] = 3;"); | 159 rejectLHS(75, "const [(xx)] = 3;"); |
160 rejectLHS("let [(xx)] = 3;"); | 160 rejectLHS(76, "let [(xx)] = 3;"); |
161 | 161 |
162 // Group assignment (Spidermonkey optimization for certain | 162 // Group assignment (Spidermonkey optimization for certain |
163 // destructuring assignments) | 163 // destructuring assignments) |
164 rejectLHS(" [(xx)] = [3];"); | 164 rejectLHS(77, " [(xx)] = [3];"); |
165 rejectLHS("var [(xx)] = [3];"); | 165 rejectLHS(78, "var [(xx)] = [3];"); |
166 rejectLHS("const [(xx)] = [3];"); | 166 rejectLHS(79, "const [(xx)] = [3];"); |
167 rejectLHS("let [(xx)] = [3];"); | 167 rejectLHS(80, "let [(xx)] = [3];"); |
168 | 168 |
169 // Destructuring & group assignment for array comprehensions, just for kicks. | 169 // Destructuring & group assignment for array comprehensions, just for kicks. |
170 rejectLHS(" [xx] = [3];"); | 170 rejectLHS(81, " [xx] = [3];"); |
171 rejectLHS("var [xx] = [3];"); | 171 rejectLHS(82, "var [xx] = [3];"); |
172 rejectLHS("const [xx] = [3];"); | 172 rejectLHS(83, "const [xx] = [3];"); |
173 rejectLHS("let [xx] = 3;"); | 173 rejectLHS(84, "let [xx] = 3;"); |
174 rejectLHS(" [xx] = 3;"); | 174 rejectLHS(85, " [xx] = 3;"); |
175 rejectLHS("var [xx] = 3;"); | 175 rejectLHS(86, "var [xx] = 3;"); |
176 rejectLHS("const [xx] = 3;"); | 176 rejectLHS(87, "const [xx] = 3;"); |
177 rejectLHS("let [xx] = 3;"); | 177 rejectLHS(88, "let [xx] = 3;"); |
178 | 178 |
179 // This is crazy, ambiguous, and/or buggy. | 179 // This is crazy, ambiguous, and/or buggy. |
180 // See https://bugzilla.mozilla.org/show_bug.cgi?id=380237#c23 et seq. | 180 // See https://bugzilla.mozilla.org/show_bug.cgi?id=380237#c23 et seq. |
181 //doesNotNeedParens("(yield xx);"); | 181 //doesNotNeedParens("(yield xx);"); |
182 | 182 |
183 print("Done!"); | 183 print("Done!"); |
184 | 184 |
185 function doesNotNeedParens(pat) | 185 function doesNotNeedParens(section, pat) |
186 { | 186 { |
187 print("Testing " + pat); | 187 print("Testing section " + section + " pattern " + pat); |
188 | 188 |
189 var f, ft; | 189 var f, ft; |
190 sanityCheck(pat); | 190 sanityCheck(section, pat); |
191 | 191 |
192 expect = 'No Error'; | 192 expect = 'No Error'; |
193 actual = ''; | 193 actual = ''; |
194 ft = pat.replace(/xx/, genexp); | 194 ft = pat.replace(/xx/, genexp); |
195 try { | 195 try { |
196 f = new Function(ft); | 196 f = new Function(ft); |
197 actual = 'No Error'; | 197 actual = 'No Error'; |
198 } catch(e) { | 198 } catch(e) { |
199 print("Unparenthesized genexp SHOULD have been accepted here!"); | 199 print("Unparenthesized genexp SHOULD have been accepted here!"); |
200 actual = e + ''; | 200 actual = e + ''; |
201 } | 201 } |
202 reportCompare(expect, actual, summary + ': doesNotNeedParens ' + pat); | 202 reportCompare(expect, actual, summary + ': doesNotNeedParens section ' + secti
on + ' pattern ' + pat); |
203 | 203 |
204 roundTripTest(f); | 204 roundTripTest(section, f); |
205 | 205 |
206 // Make sure the decompilation is not over-parenthesized. | 206 // Make sure the decompilation is not over-parenthesized. |
207 var uf = "" + f; | 207 var uf = "" + f; |
208 if (pat.indexOf("(xx)") != -1) | 208 if (pat.indexOf("(xx)") != -1) |
209 overParenTest(f); | 209 overParenTest(section, f); |
210 // else | 210 // else |
211 // print("Skipping the over-parenthesization test, because I don't know how
to test for over-parenthesization when the pattern doesn't have parens snugly a
round it.") | 211 // print("Skipping the over-parenthesization test, because I don't know how
to test for over-parenthesization when the pattern doesn't have parens snugly a
round it.") |
212 } | 212 } |
213 | 213 |
214 function needParens(pat, exp) | 214 function needParens(section, pat, exp) |
215 { | 215 { |
216 print("Testing " + pat); | 216 print("Testing section " + section + " pattern " + pat); |
217 | 217 |
218 var f, ft; | 218 var f, ft; |
219 sanityCheck(pat); | 219 sanityCheck(section, pat); |
220 | 220 |
221 expect = 'SyntaxError'; | 221 expect = 'SyntaxError'; |
222 actual = ''; | 222 actual = ''; |
223 ft = pat.replace(/xx/, genexp); | 223 ft = pat.replace(/xx/, genexp); |
224 try { | 224 try { |
225 f = new Function(ft); | 225 f = new Function(ft); |
226 print("Unparenthesized genexp should NOT have been accepted here!"); | 226 print("Unparenthesized genexp should NOT have been accepted here!"); |
227 } catch(e) { | 227 } catch(e) { |
228 /* expected to throw */ | 228 /* expected to throw */ |
229 actual = e.name; | 229 actual = e.name; |
230 } | 230 } |
231 reportCompare(expect, actual, summary + ': needParens ' + pat); | 231 reportCompare(expect, actual, summary + ': needParens section ' + section + '
pattern ' + pat); |
232 | 232 |
233 expect = 'No Error'; | 233 expect = 'No Error'; |
234 actual = ''; | 234 actual = ''; |
235 ft = pat.replace(/xx/, genexpParened); | 235 ft = pat.replace(/xx/, genexpParened); |
236 try { | 236 try { |
237 f = new Function(ft); | 237 f = new Function(ft); |
238 actual = 'No Error'; | 238 actual = 'No Error'; |
239 } catch(e) { | 239 } catch(e) { |
240 print("Yikes!"); | 240 print("Yikes!"); |
241 actual = e + ''; | 241 actual = e + ''; |
242 } | 242 } |
243 reportCompare(expect, actual, summary + ': needParens ' + ft); | 243 reportCompare(expect, actual, summary + ': needParens section ' + section + '
ft ' + ft); |
244 | 244 |
245 roundTripTest(f, exp); | 245 roundTripTest(section, f, exp); |
246 overParenTest(f, exp); | 246 overParenTest(section, f, exp); |
247 } | 247 } |
248 | 248 |
249 function rejectLHS(pat) | 249 function rejectLHS(section, pat) |
250 { | 250 { |
251 print("Testing " + pat); | 251 print("Testing section " + section + " pattern " + pat); |
252 | 252 |
253 // sanityCheck(pat); // because 'z' should be accepted as an LHS or binding | 253 // sanityCheck(pat); // because 'z' should be accepted as an LHS or binding |
254 | 254 |
255 var ft; | 255 var ft; |
256 | 256 |
257 expect = 'SyntaxError'; | 257 expect = 'SyntaxError'; |
258 actual = ''; | 258 actual = ''; |
259 ft = pat.replace(/xx/, genexp) | 259 ft = pat.replace(/xx/, genexp) |
260 try { | 260 try { |
261 new Function(ft); | 261 new Function(ft); |
262 print("That should have been a syntax error!"); | 262 print("That should have been a syntax error!"); |
263 actual = 'No Error'; | 263 actual = 'No Error'; |
264 } catch(e) { | 264 } catch(e) { |
265 actual = e.name; | 265 actual = e.name; |
266 } | 266 } |
267 reportCompare(expect, actual, summary + ': rejectLHS'); | 267 reportCompare(expect, actual, summary + ': rejectLHS section ' + section); |
268 } | 268 } |
269 | 269 |
270 | 270 |
271 function overParenTest(f, exp) | 271 function overParenTest(section, f, exp) |
272 { | 272 { |
273 var uf = "" + f; | 273 var uf = "" + f; |
274 if (uf == exp) | 274 if (uf == exp) |
275 return; | 275 return; |
276 | 276 |
277 reportCompare(false, uf.indexOf(genexpParened) == -1, summary + | 277 reportCompare(false, uf.indexOf(genexpParened) == -1, summary + |
278 ': overParenTest genexp snugly in parentheses: ' + uf); | 278 ': overParenTest genexp snugly in parentheses: section ' + secti
on + ' uf ' + uf); |
279 | 279 |
280 if (uf.indexOf(genexpParened) != -1) { | 280 if (uf.indexOf(genexpParened) != -1) { |
281 reportCompare(true, uf.indexOf(genexpParenedTwice) == -1, summary + | 281 reportCompare(true, uf.indexOf(genexpParenedTwice) == -1, summary + |
282 ': overParensTest decompilation should not be over-parenthesized: ' + uf); | 282 ': overParensTest decompilation should not be over-parenthesized: section
' + ' uf ' + uf); |
283 } | 283 } |
284 } | 284 } |
285 | 285 |
286 function sanityCheck(pat) | 286 function sanityCheck(section, pat) |
287 { | 287 { |
288 expect = ''; | 288 expect = ''; |
289 actual = ''; | 289 actual = ''; |
290 | 290 |
291 if (pat.indexOf("xx") == -1) | 291 if (pat.indexOf("xx") == -1) |
292 { | 292 { |
293 actual += "No 'xx' in this pattern? "; | 293 actual += "No 'xx' in this pattern? "; |
294 } | 294 } |
295 | 295 |
296 var f, ft; | 296 var f, ft; |
297 ft = pat.replace(/xx/, "z"); | 297 ft = pat.replace(/xx/, "z"); |
298 try { | 298 try { |
299 f = new Function(ft); | 299 f = new Function(ft); |
300 } catch(e) { | 300 } catch(e) { |
301 actual += "Yowzers! Probably a bogus test!"; | 301 actual += "Yowzers! Probably a bogus test!"; |
302 } | 302 } |
303 reportCompare(expect, actual, summary + ': sanityCheck ' + pat); | 303 reportCompare(expect, actual, summary + ': sanityCheck section ' + section + '
pattern ' + pat); |
304 } | 304 } |
305 | 305 |
306 function roundTripTest(f, exp) | 306 function roundTripTest(section, f, exp) |
307 { | 307 { |
308 // Decompile | 308 // Decompile |
309 var uf = "" + f; | 309 var uf = "" + f; |
310 | 310 |
311 // Recompile | 311 // Recompile |
312 expect = 'No Error'; | 312 expect = 'No Error'; |
313 actual = ''; | 313 actual = ''; |
314 var euf; | 314 var euf; |
315 try { | 315 try { |
316 euf = eval("(" + uf + ")"); | 316 euf = eval("(" + uf + ")"); |
317 actual = 'No Error'; | 317 actual = 'No Error'; |
318 reportCompare(expect, actual, summary + ': roundTripTest: ' + uf); | 318 reportCompare(expect, actual, summary + ': roundTripTest: section ' + sectio
n + ' uf ' + uf); |
319 } catch(e) { | 319 } catch(e) { |
320 actual = e + ''; | 320 actual = e + ''; |
321 reportCompare(expect, actual, summary + ': roundTripTest: ' + uf); | 321 reportCompare(expect, actual, summary + ': roundTripTest: section ' + sectio
n + ' uf ' + uf); |
322 return; | 322 return; |
323 } | 323 } |
324 | 324 |
325 // Decompile again and make sure the decompilations match exactly. | 325 // Decompile again and make sure the decompilations match exactly. |
326 expect = exp || uf; | 326 expect = exp || uf; |
327 actual = "" + euf; | 327 actual = "" + euf; |
328 reportCompare(expect, actual, summary + ': roundTripTest no round-trip change'
); | 328 reportCompare(expect, actual, summary + ': roundTripTest no round-trip change:
section ' + section); |
329 } | 329 } |
OLD | NEW |