michael@0: // |reftest| skip -- obsolete test, need to remove minor failures to reenable. michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: var BUGNUMBER = 380237; michael@0: var summary = 'Generator expressions parenthesization test'; michael@0: var actual = ''; michael@0: var expect = ''; michael@0: michael@0: michael@0: /* michael@0: michael@0: Given that parentheization seems so fragile *and* the rules for where michael@0: genexps are allowed keep changing, I thought it would be good to have michael@0: a way to test that: michael@0: michael@0: 1) unparenthesized genexps are allowed in some places and the michael@0: decompilation is sane and not over-parenthesized michael@0: michael@0: 2) unparenthesized genexps are disallowed in many places and when michael@0: there are parens, the decompilation is sane and not over-parenthesized michael@0: michael@0: */ michael@0: michael@0: // |genexp| must have the exact same whitespace the decompiler uses michael@0: genexp = "x * x for (x in [])"; michael@0: genexpParened = "(" + genexp + ")"; michael@0: genexpParenedTwice = "(" + genexpParened + ")"; michael@0: michael@0: // Warning: be careful not to put [] around stuff, because that would michael@0: // cause it to be treated as an array comprehension instead of a michael@0: // generator expression! michael@0: michael@0: // Statements michael@0: doesNotNeedParens(1, "if (xx) { }"); michael@0: needParens(2, "if (1, xx) { }"); michael@0: needParens(3, "if (xx, 1) { }"); michael@0: doesNotNeedParens(4, "do { } while (xx);"); michael@0: doesNotNeedParens(5, "while (xx) { }"); michael@0: doesNotNeedParens(6, "switch (xx) { }"); michael@0: doesNotNeedParens(7, "with (xx) { }"); michael@0: needParens(8, "switch (x) { case xx: }"); michael@0: needParens(9, "return xx;"); michael@0: needParens(10, "yield xx;"); michael@0: needParens(11, "for (xx;;) { }"); michael@0: needParens(12, "for (;xx;) { }", "function anonymous() {\n for (;;) {\n }\n}"); michael@0: needParens(13, "for (;;xx) { }"); michael@0: needParens(14, "for (i in xx) { }"); michael@0: needParens(15, "throw xx"); michael@0: needParens(16, "try { } catch (e if xx) { }"); michael@0: needParens(17, "let (x=3) xx"); michael@0: needParens(18, "let (x=xx) 3"); michael@0: michael@0: // Function calls michael@0: doesNotNeedParens(19, "f(xx);"); michael@0: needParens(20, "f(xx, 1);"); michael@0: needParens(21, "f(1, xx);"); michael@0: doesNotNeedParens(22, "/x/(xx);"); michael@0: needParens(23, "/x/(xx, 1);"); michael@0: needParens(24, "/x/(1, xx);"); michael@0: michael@0: // eval is special and often confuses the decompiler. michael@0: doesNotNeedParens(25, "eval(xx);"); michael@0: needParens(26, "eval(xx, 1);"); michael@0: needParens(27, "eval(1, xx);"); michael@0: michael@0: // Expressions michael@0: needParens(28, "xx;"); // ??? michael@0: needParens(29, "var g = xx;"); // ??? michael@0: needParens(30, "g += xx;"); michael@0: needParens(31, "xx();"); michael@0: needParens(32, "xx() = 3;"); michael@0: needParens(33, "a ? xx : c"); michael@0: needParens(34, "xx ? b : c"); michael@0: needParens(35, "a ? b : xx"); michael@0: needParens(36, "1 ? xx : c"); michael@0: needParens(37, "0 ? b : xx"); michael@0: needParens(38, "1 + xx"); michael@0: needParens(39, "xx + 1"); michael@0: needParens(40, "1, xx"); michael@0: doesNotNeedParens(41, "+(xx)"); michael@0: doesNotNeedParens(42, "!(xx)"); michael@0: needParens(43, "xx, 1"); michael@0: needParens(44, "[1, xx]"); michael@0: needParens(45, "[xx, 1]"); michael@0: needParens(46, "[xx,3]"); michael@0: needParens(47, "[xx,null]"); michael@0: needParens(48, "xx.p"); michael@0: needParens(49, "xx.@p"); michael@0: needParens(50, "typeof xx;"); michael@0: needParens(51, "void xx;"); michael@0: needParens(52, "({ a: xx })"); michael@0: needParens(53, "({ a: 1, b: xx })"); michael@0: needParens(54, "({ a: xx, b: 1 })"); michael@0: needParens(55, "({ a getter: xx })"); michael@0: needParens(56, ""); michael@0: doesNotNeedParens(57, "new (xx);"); michael@0: doesNotNeedParens(58, "new a(xx);"); michael@0: michael@0: michael@0: // Generator expressions cannot be used as LHS, even though they're syntactic michael@0: // sugar for something that looks a lot like an "lvalue return": (f() = 3). michael@0: michael@0: rejectLHS(59, "++ (xx);"); michael@0: rejectLHS(60, "delete xx;"); michael@0: rejectLHS(61, "delete (xx);"); michael@0: rejectLHS(62, "for (xx in []) { }"); michael@0: rejectLHS(63, "for ((xx) in []) { }"); michael@0: rejectLHS(64, "try { } catch(xx) { }"); michael@0: rejectLHS(65, "try { } catch([(xx)]) { }"); michael@0: rejectLHS(66, "xx += 3;"); michael@0: rejectLHS(67, "(xx) += 3;"); michael@0: rejectLHS(68, "xx = 3;"); michael@0: michael@0: // Assignment michael@0: rejectLHS(69, " (xx) = 3;"); michael@0: rejectLHS(70, "var (xx) = 3;"); michael@0: rejectLHS(71, "const (xx) = 3;"); michael@0: rejectLHS(72, "let (xx) = 3;"); michael@0: michael@0: // Destructuring assignment michael@0: rejectLHS(73, " [(xx)] = 3;"); michael@0: rejectLHS(74, "var [(xx)] = 3;"); michael@0: rejectLHS(75, "const [(xx)] = 3;"); michael@0: rejectLHS(76, "let [(xx)] = 3;"); michael@0: michael@0: // Group assignment (Spidermonkey optimization for certain michael@0: // destructuring assignments) michael@0: rejectLHS(77, " [(xx)] = [3];"); michael@0: rejectLHS(78, "var [(xx)] = [3];"); michael@0: rejectLHS(79, "const [(xx)] = [3];"); michael@0: rejectLHS(80, "let [(xx)] = [3];"); michael@0: michael@0: // Destructuring & group assignment for array comprehensions, just for kicks. michael@0: rejectLHS(81, " [xx] = [3];"); michael@0: rejectLHS(82, "var [xx] = [3];"); michael@0: rejectLHS(83, "const [xx] = [3];"); michael@0: rejectLHS(84, "let [xx] = 3;"); michael@0: rejectLHS(85, " [xx] = 3;"); michael@0: rejectLHS(86, "var [xx] = 3;"); michael@0: rejectLHS(87, "const [xx] = 3;"); michael@0: rejectLHS(88, "let [xx] = 3;"); michael@0: michael@0: // This is crazy, ambiguous, and/or buggy. michael@0: // See https://bugzilla.mozilla.org/show_bug.cgi?id=380237#c23 et seq. michael@0: //doesNotNeedParens("(yield xx);"); michael@0: michael@0: print("Done!"); michael@0: michael@0: function doesNotNeedParens(section, pat) michael@0: { michael@0: print("Testing section " + section + " pattern " + pat); michael@0: michael@0: var f, ft; michael@0: sanityCheck(section, pat); michael@0: michael@0: expect = 'No Error'; michael@0: actual = ''; michael@0: ft = pat.replace(/xx/, genexp); michael@0: try { michael@0: f = new Function(ft); michael@0: actual = 'No Error'; michael@0: } catch(e) { michael@0: print("Unparenthesized genexp SHOULD have been accepted here!"); michael@0: actual = e + ''; michael@0: } michael@0: reportCompare(expect, actual, summary + ': doesNotNeedParens section ' + section + ' pattern ' + pat); michael@0: michael@0: roundTripTest(section, f); michael@0: michael@0: // Make sure the decompilation is not over-parenthesized. michael@0: var uf = "" + f; michael@0: if (pat.indexOf("(xx)") != -1) michael@0: overParenTest(section, f); michael@0: // else michael@0: // 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 around it.") michael@0: } michael@0: michael@0: function needParens(section, pat, exp) michael@0: { michael@0: print("Testing section " + section + " pattern " + pat); michael@0: michael@0: var f, ft; michael@0: sanityCheck(section, pat); michael@0: michael@0: expect = 'SyntaxError'; michael@0: actual = ''; michael@0: ft = pat.replace(/xx/, genexp); michael@0: try { michael@0: f = new Function(ft); michael@0: print("Unparenthesized genexp should NOT have been accepted here!"); michael@0: } catch(e) { michael@0: /* expected to throw */ michael@0: actual = e.name; michael@0: } michael@0: reportCompare(expect, actual, summary + ': needParens section ' + section + ' pattern ' + pat); michael@0: michael@0: expect = 'No Error'; michael@0: actual = ''; michael@0: ft = pat.replace(/xx/, genexpParened); michael@0: try { michael@0: f = new Function(ft); michael@0: actual = 'No Error'; michael@0: } catch(e) { michael@0: print("Yikes!"); michael@0: actual = e + ''; michael@0: } michael@0: reportCompare(expect, actual, summary + ': needParens section ' + section + ' ft ' + ft); michael@0: michael@0: roundTripTest(section, f, exp); michael@0: overParenTest(section, f, exp); michael@0: } michael@0: michael@0: function rejectLHS(section, pat) michael@0: { michael@0: print("Testing section " + section + " pattern " + pat); michael@0: michael@0: // sanityCheck(pat); // because 'z' should be accepted as an LHS or binding michael@0: michael@0: var ft; michael@0: michael@0: expect = 'SyntaxError'; michael@0: actual = ''; michael@0: ft = pat.replace(/xx/, genexp) michael@0: try { michael@0: new Function(ft); michael@0: print("That should have been a syntax error!"); michael@0: actual = 'No Error'; michael@0: } catch(e) { michael@0: actual = e.name; michael@0: } michael@0: reportCompare(expect, actual, summary + ': rejectLHS section ' + section); michael@0: } michael@0: michael@0: michael@0: function overParenTest(section, f, exp) michael@0: { michael@0: var uf = "" + f; michael@0: if (uf == exp) michael@0: return; michael@0: michael@0: reportCompare(false, uf.indexOf(genexpParened) == -1, summary + michael@0: ': overParenTest genexp snugly in parentheses: section ' + section + ' uf ' + uf); michael@0: michael@0: if (uf.indexOf(genexpParened) != -1) { michael@0: reportCompare(true, uf.indexOf(genexpParenedTwice) == -1, summary + michael@0: ': overParensTest decompilation should not be over-parenthesized: section ' + ' uf ' + uf); michael@0: } michael@0: } michael@0: michael@0: function sanityCheck(section, pat) michael@0: { michael@0: expect = ''; michael@0: actual = ''; michael@0: michael@0: if (pat.indexOf("xx") == -1) michael@0: { michael@0: actual += "No 'xx' in this pattern? "; michael@0: } michael@0: michael@0: var f, ft; michael@0: ft = pat.replace(/xx/, "z"); michael@0: try { michael@0: f = new Function(ft); michael@0: } catch(e) { michael@0: actual += "Yowzers! Probably a bogus test!"; michael@0: } michael@0: reportCompare(expect, actual, summary + ': sanityCheck section ' + section + ' pattern ' + pat); michael@0: } michael@0: michael@0: function roundTripTest(section, f, exp) michael@0: { michael@0: // Decompile michael@0: var uf = "" + f; michael@0: michael@0: // Recompile michael@0: expect = 'No Error'; michael@0: actual = ''; michael@0: var euf; michael@0: try { michael@0: euf = eval("(" + uf + ")"); michael@0: actual = 'No Error'; michael@0: reportCompare(expect, actual, summary + ': roundTripTest: section ' + section + ' uf ' + uf); michael@0: } catch(e) { michael@0: actual = e + ''; michael@0: reportCompare(expect, actual, summary + ': roundTripTest: section ' + section + ' uf ' + uf); michael@0: return; michael@0: } michael@0: michael@0: // Decompile again and make sure the decompilations match exactly. michael@0: expect = exp || uf; michael@0: actual = "" + euf; michael@0: reportCompare(expect, actual, summary + ': roundTripTest no round-trip change: section ' + section); michael@0: }