| // 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. |
| |
| import 'dart:async'; |
| import 'package:expect/expect.dart'; |
| import 'package:async_helper/async_helper.dart'; |
| import 'mock_compiler.dart'; |
| import 'package:compiler/implementation/js/js.dart' as jsAst; |
| import 'package:compiler/implementation/js/js.dart' show js; |
| |
| Future testExpression(String expression, [String expect = ""]) { |
| jsAst.Node node = js(expression); |
| return MockCompiler.create((MockCompiler compiler) { |
| String jsText = |
| jsAst.prettyPrint(node, |
| compiler, |
| allowVariableMinification: false).getText(); |
| if (expect == "") { |
| Expect.stringEquals(expression, jsText); |
| } else { |
| Expect.stringEquals(expect, jsText); |
| } |
| }); |
| } |
| |
| Future testError(String expression, [String expect = ""]) { |
| return new Future.sync(() { |
| bool doCheck(exception) { |
| Expect.isTrue(exception.toString().contains(expect)); |
| return true; |
| } |
| Expect.throws(() => js(expression), doCheck); |
| }); |
| } |
| |
| void main() { |
| asyncTest(() => Future.wait([ |
| // 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)'), |
| // Chaned 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 + 0x0123456789abcdefABCDEF'), |
| // 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'), |
| // 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])'), |
| // *We can't parse array literals with holes in them. |
| testError("[1,, 2]"), |
| testError("[1,]"), |
| testError("[,]"), |
| testError("[, 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"), |
| ])); |
| } |