// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. // @dart = 2.7 import 'package:expect/expect.dart'; import 'package:compiler/src/js/js.dart' as jsAst; import 'package:compiler/src/js/js.dart' show js; testExpression(String expression, [String expect = ""]) { jsAst.Node node = js(expression); String jsText = jsAst.prettyPrint(node, allowVariableMinification: false); if (expect == "") { Expect.stringEquals(expression, jsText); } else { Expect.stringEquals(expect, jsText); } } testError(String expression, [String expect = ""]) { bool doCheck(exception) { Expect.isTrue(exception.toString().contains(expect)); return true; } Expect.throws(() => js(expression), doCheck); } void main() { // Asterisk indicates deviations from real JS. // Simple var test. testExpression('var a = ""'); // Parse and print will normalize whitespace. testExpression(' var a = "" ', 'var a = ""'); // Operator precedence. testExpression('x = a + b * c'); testExpression('x = a * b + c'); testExpression('x = a + b * c + d'); testExpression('x = a * b + c * d'); testExpression( 'remaining = (remaining / 88) | 0', 'remaining = remaining / 88 | 0'); // Binary operators have left associativity. testExpression('x = a + b + c'); // We can cope with relational operators and non-relational. testExpression('a + b == c + d'); // The prettyprinter will insert braces where needed. testExpression('a + (b == c) + d'); // We can handle () for calls. testExpression('foo(bar)'); testExpression('foo(bar, baz)'); // Chained calls without parentheses. testExpression('foo(bar)(baz)'); // Chained calls with and without new. testExpression('new foo(bar)(baz)'); testExpression('new foo.bar(bar)(baz)'); testExpression('foo.bar(bar)(baz)'); testExpression('constructor = new Function(str)()'); // The prettyprinter understands chained calls without extra parentheses. testExpression('(foo(bar))(baz)', 'foo(bar)(baz)'); // Chains of dotting and calls. testExpression('foo.bar(baz)'); // String literal. testExpression('var x = "fisk"'); // String literal with \n. testExpression(r'var x = "\n"'); // String literal with escaped quote. testExpression(r'var x = "\""'); // *No clever escapes. testError(r'var x = "\x42"', 'escapes are not allowed in literals'); // Operator new. testExpression('new Foo()'); // New with dotted access. testExpression('new Frobinator.frobinate()'); testExpression('new Frobinator().frobinate()'); // The prettyprinter strips some superfluous parentheses. testExpression( '(new Frobinator()).frobinate()', 'new Frobinator().frobinate()'); // *We want a bracket on 'new'. testError('new Foo', 'Parentheses are required'); testError('(new Foo)', 'Parentheses are required'); // Bogus operators. testError('a +++ b', 'Unknown operator'); // This isn't perl. There are rules. testError('a <=> b', 'Unknown operator'); // Typeof. testExpression('typeof foo == "number"'); // Strange relation. testExpression('a < b < c'); // Chained var. testExpression('var x = 0, y = 1.2, z = 42'); // Empty object literal. testExpression('foo({}, {})'); // *Can't handle non-empty object literals testExpression('foo({meaning: 42})'); // Literals. testExpression('x(false, true, null)'); // *We should really throw here. testExpression('var false = 42'); testExpression('var new = 42'); // Bad keyword. testError('var typeof = 42', "Expected ALPHA"); // Malformed decimal/hex. testError('var x = 1.1.1', "Unparseable number"); testError('var x = 0xabcdefga', "Unparseable number"); testError('var x = 0xabcdef\$a', "Unparseable number"); testError('var x = 0x ', "Unparseable number"); // Good hex constants. testExpression('var x = 0xff'); testExpression('var x = 0xff + 0xff'); testExpression('var x = 0xaF + 0x0123456789abcdefA'); // All sorts of keywords are allowed as property names in ES5. testExpression('x.new = 0'); testExpression('x.delete = 0'); testExpression('x.for = 0'); testExpression('x.instanceof = 0'); testExpression('x.in = 0'); testExpression('x.void = 0'); testExpression('x.continue = 0'); // More unary. testExpression('x = !x'); testExpression('!x == false'); testExpression('var foo = void 0'); testExpression('delete foo.bar'); testExpression('delete foo'); testExpression('x in y'); testExpression('x instanceof y'); testExpression('a * b in c * d'); testExpression('a * b instanceof c * d'); testError('x typeof y', 'Unparsed junk'); testExpression('x &= ~mask'); // Await is parsed as an unary prefix operator. testExpression('var foo = await 0'); testExpression('await x++'); testExpression('void (await (x++))', 'void await x++'); testExpression('void (await x)++'); testExpression('++(await x)++'); // Adjacent tokens. testExpression('foo[x[bar]]'); testExpression('foo[[bar]]'); // Prefix ++ etc. testExpression("++x"); testExpression("++foo.bar"); testExpression("+x"); testExpression("+foo.bar"); testExpression("-x"); testExpression("-foo.bar"); testExpression("--x"); testExpression("--foo.bar"); // Postfix ++ etc. testExpression("x++"); testExpression("foo.bar++"); testExpression("x--"); testExpression("foo.bar--"); // Both! testExpression("++x++"); testExpression("++foo.bar++"); testExpression("--x--"); testExpression("--foo.bar--"); // *We can't handle stacked unary operators (apart from !). testError("x++ ++"); testError("++ typeof x"); testExpression(r"var $supportsProtoName = !!{}.__proto__"); // ++ used as a binary operator. testError("x++ ++ 42"); // Shift operators. testExpression("x << 5"); testExpression("x << y + 1"); testExpression("x <<= y + 1"); // Array initializers. testExpression("x = ['foo', 'bar', x[4]]"); testExpression("[]"); testError("[42 42]"); testExpression('beebop([1, 2, 3])'); // Array literals with holes in them. testExpression("[1,, 2]"); testExpression("[1,]", "[1]"); testExpression("[1,,]", "[1,,]"); testExpression("[,]"); testExpression("[,,]"); testExpression("[, 42]"); // Ternary operator. testExpression("x = a ? b : c"); testExpression("y = a == null ? b : a"); testExpression("y = a == null ? b + c : a + c"); testExpression("foo = a ? b : c ? d : e"); testExpression("foo = a ? b ? c : d : e"); testExpression("foo = (a = v) ? b = w : c = x ? d = y : e = z"); testExpression("foo = (a = v) ? b = w ? c = x : d = y : e = z"); // Stacked assignment. testExpression("a = b = c"); testExpression("var a = b = c"); }