20faaa5d94
Change-Id: Ib18f554c1076f27a3f7c0df5a36410da41a1f467 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/148784 Reviewed-by: Paul Berry <paulberry@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
479 lines
14 KiB
Dart
479 lines
14 KiB
Dart
// Copyright (c) 2014, 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;
|
|
|
|
testStatement(String statement, arguments, String expect) {
|
|
jsAst.Node node = js.statement(statement, arguments);
|
|
String jsText = jsAst.prettyPrint(node, allowVariableMinification: false);
|
|
Expect.stringEquals(expect.trim(), jsText.trim());
|
|
}
|
|
|
|
testError(String statement, arguments, [String expect = ""]) {
|
|
bool doCheck(exception) {
|
|
String message = '$exception';
|
|
Expect.isTrue(message.contains(expect), '"$message" contains "$expect"');
|
|
return true;
|
|
}
|
|
|
|
void action() {
|
|
js.statement(statement, arguments);
|
|
}
|
|
|
|
Expect.throws(action, doCheck);
|
|
}
|
|
|
|
// Function declaration and named function.
|
|
const NAMED_FUNCTION_1 = r'''
|
|
function foo/*function declaration*/() {
|
|
return function harry/*named function*/() { return #; }
|
|
}''';
|
|
|
|
const NAMED_FUNCTION_1_NAMED_HOLE = r'''
|
|
function foo/*function declaration*/() {
|
|
return function harry/*named function*/() { return #hole; }
|
|
}''';
|
|
|
|
const NAMED_FUNCTION_1_ONE = r'''
|
|
function foo() {
|
|
return function harry() {
|
|
return 1;
|
|
};
|
|
}''';
|
|
|
|
const MISC_1 = r'''
|
|
function foo() {
|
|
/a/;
|
|
#;
|
|
}''';
|
|
|
|
const MISC_1_NAMED_HOLE = r'''
|
|
function foo() {
|
|
/a/;
|
|
#hole;
|
|
}''';
|
|
|
|
const MISC_1_1 = r'''
|
|
function foo() {
|
|
/a/;
|
|
1;
|
|
2;
|
|
}''';
|
|
|
|
void main() {
|
|
var eOne = js('1');
|
|
var eTwo = js('2');
|
|
var eTrue = js('true');
|
|
var eVar = js('x');
|
|
var block12 = js.statement('{ 1; 2; }');
|
|
var stm = js.statement('foo();');
|
|
var seq1 = js('1, 2, 3');
|
|
|
|
Expect.isTrue(eOne is jsAst.LiteralNumber);
|
|
Expect.isTrue(eTrue is jsAst.LiteralBool);
|
|
Expect.isTrue(block12 is jsAst.Block);
|
|
|
|
// Interpolated Expressions are upgraded to ExpressionStatements.
|
|
testStatement('{ #; #; }', [eOne, eOne], '{\n 1;\n 1;\n}');
|
|
testStatement('{ #a; #b; }', {'a': eOne, 'b': eOne}, '{\n 1;\n 1;\n}');
|
|
|
|
// Interpolated sub-blocks are spliced.
|
|
testStatement(
|
|
'{ #; #; }', [block12, block12], '{\n 1;\n 2;\n 1;\n 2;\n}\n');
|
|
testStatement('{ #a; #b; }', {'a': block12, 'b': block12},
|
|
'{\n 1;\n 2;\n 1;\n 2;\n}\n');
|
|
|
|
// If-condition. Dart booleans are evaluated, JS Expression booleans are
|
|
// substituted.
|
|
testStatement('if (#) #', [eOne, block12], 'if (1) {\n 1;\n 2;\n}');
|
|
testStatement('if (#) #;', [eTrue, block12], 'if (true) {\n 1;\n 2;\n}');
|
|
testStatement('if (#) #;', [eVar, block12], 'if (x) {\n 1;\n 2;\n}');
|
|
testStatement('if (#) #;', ['a', block12], 'if (a) {\n 1;\n 2;\n}');
|
|
testStatement('if (#) #;', [true, block12], '{\n 1;\n 2;\n}');
|
|
testStatement('if (#) #;', [false, block12], ';');
|
|
testStatement('if (#) 3; else #;', [true, block12], '3;');
|
|
testStatement('if (#) 3; else #;', [false, block12], '{\n 1;\n 2;\n}');
|
|
testStatement(
|
|
'if (#a) #b', {'a': eOne, 'b': block12}, 'if (1) {\n 1;\n 2;\n}');
|
|
testStatement(
|
|
'if (#a) #b;', {'a': eTrue, 'b': block12}, 'if (true) {\n 1;\n 2;\n}');
|
|
testStatement(
|
|
'if (#a) #b;', {'a': eVar, 'b': block12}, 'if (x) {\n 1;\n 2;\n}');
|
|
testStatement(
|
|
'if (#a) #b;', {'a': 'a', 'b': block12}, 'if (a) {\n 1;\n 2;\n}');
|
|
testStatement('if (#a) #b;', {'a': true, 'b': block12}, '{\n 1;\n 2;\n}');
|
|
testStatement('if (#a) #b;', {'a': false, 'b': block12}, ';');
|
|
testStatement('if (#a) 3; else #b;', {'a': true, 'b': block12}, '3;');
|
|
testStatement(
|
|
'if (#a) 3; else #b;', {'a': false, 'b': block12}, '{\n 1;\n 2;\n}');
|
|
|
|
testStatement('while (#) #', [eOne, block12], 'while (1) {\n 1;\n 2;\n}');
|
|
testStatement(
|
|
'while (#) #;', [eTrue, block12], 'while (true) {\n 1;\n 2;\n}');
|
|
testStatement('while (#) #;', [eVar, block12], 'while (x) {\n 1;\n 2;\n}');
|
|
testStatement('while (#) #;', ['a', block12], 'while (a) {\n 1;\n 2;\n}');
|
|
testStatement('while (#) #;', ['a', stm], 'while (a)\n foo();');
|
|
|
|
testStatement(
|
|
'do { {print(1);} do while(true); while (false) } while ( true )', [], '''
|
|
do {
|
|
print(1);
|
|
do
|
|
while (true)
|
|
;
|
|
while (false);
|
|
} while (true);
|
|
''');
|
|
testStatement(
|
|
'do #; while ( # )', [block12, eOne], 'do {\n 1;\n 2;\n} while (1); ');
|
|
testStatement('do #; while ( # )', [block12, eTrue],
|
|
'do {\n 1;\n 2;\n} while (true); ');
|
|
testStatement(
|
|
'do #; while ( # );', [block12, eVar], 'do {\n 1;\n 2;\n} while (x);');
|
|
testStatement(
|
|
'do { # } while ( # )', [block12, 'a'], 'do {\n 1;\n 2;\n} while (a);');
|
|
testStatement('do #; while ( # )', [stm, 'a'], 'do\n foo();\nwhile (a);');
|
|
|
|
testStatement('switch (#) {}', [eOne], 'switch (1) {\n}');
|
|
testStatement('''
|
|
switch (#) {
|
|
case #: { # }
|
|
}''', [eTrue, eOne, block12],
|
|
'switch (true) {\n case 1:\n 1;\n 2;\n}');
|
|
testStatement('''
|
|
switch (#) {
|
|
case #: { # }
|
|
break;
|
|
case #: { # }
|
|
default: { # }
|
|
}''', [eTrue, eOne, block12, eTwo, block12, stm], '''
|
|
switch (true) {
|
|
case 1:
|
|
1;
|
|
2;
|
|
break;
|
|
case 2:
|
|
1;
|
|
2;
|
|
default:
|
|
foo();
|
|
}''');
|
|
|
|
testStatement(NAMED_FUNCTION_1, [eOne], NAMED_FUNCTION_1_ONE);
|
|
testStatement(
|
|
NAMED_FUNCTION_1_NAMED_HOLE, {'hole': eOne}, NAMED_FUNCTION_1_ONE);
|
|
|
|
testStatement(MISC_1, [block12], MISC_1_1);
|
|
testStatement(MISC_1_NAMED_HOLE, {'hole': block12}, MISC_1_1);
|
|
|
|
// Argument list splicing.
|
|
testStatement('foo(#)', [[]], 'foo();');
|
|
testStatement(
|
|
'foo(#)',
|
|
[
|
|
[eOne]
|
|
],
|
|
'foo(1);');
|
|
testStatement('foo(#)', [eOne], 'foo(1);');
|
|
testStatement(
|
|
'foo(#)',
|
|
[
|
|
[eTrue, eOne]
|
|
],
|
|
'foo(true, 1);');
|
|
testStatement('foo(#a)', {'a': []}, 'foo();');
|
|
testStatement(
|
|
'foo(#a)',
|
|
{
|
|
'a': [eOne]
|
|
},
|
|
'foo(1);');
|
|
testStatement('foo(#a)', {'a': eOne}, 'foo(1);');
|
|
testStatement(
|
|
'foo(#a)',
|
|
{
|
|
'a': [eTrue, eOne]
|
|
},
|
|
'foo(true, 1);');
|
|
|
|
testStatement('foo(2,#)', [[]], 'foo(2);');
|
|
testStatement(
|
|
'foo(2,#)',
|
|
[
|
|
[eOne]
|
|
],
|
|
'foo(2, 1);');
|
|
testStatement('foo(2,#)', [eOne], 'foo(2, 1);');
|
|
testStatement(
|
|
'foo(2,#)',
|
|
[
|
|
[eTrue, eOne]
|
|
],
|
|
'foo(2, true, 1);');
|
|
testStatement('foo(2,#a)', {'a': []}, 'foo(2);');
|
|
testStatement(
|
|
'foo(2,#a)',
|
|
{
|
|
'a': [eOne]
|
|
},
|
|
'foo(2, 1);');
|
|
testStatement('foo(2,#a)', {'a': eOne}, 'foo(2, 1);');
|
|
testStatement(
|
|
'foo(2,#a)',
|
|
{
|
|
'a': [eTrue, eOne]
|
|
},
|
|
'foo(2, true, 1);');
|
|
|
|
testStatement('foo(#,3)', [[]], 'foo(3);');
|
|
testStatement(
|
|
'foo(#,3)',
|
|
[
|
|
[eOne]
|
|
],
|
|
'foo(1, 3);');
|
|
testStatement('foo(#,3)', [eOne], 'foo(1, 3);');
|
|
testStatement(
|
|
'foo(#,3)',
|
|
[
|
|
[eTrue, eOne]
|
|
],
|
|
'foo(true, 1, 3);');
|
|
testStatement('foo(#a,3)', {'a': []}, 'foo(3);');
|
|
testStatement(
|
|
'foo(#a,3)',
|
|
{
|
|
'a': [eOne]
|
|
},
|
|
'foo(1, 3);');
|
|
testStatement('foo(#a,3)', {'a': eOne}, 'foo(1, 3);');
|
|
testStatement(
|
|
'foo(#a,3)',
|
|
{
|
|
'a': [eTrue, eOne]
|
|
},
|
|
'foo(true, 1, 3);');
|
|
|
|
testStatement('foo(2,#,3)', [[]], 'foo(2, 3);');
|
|
testStatement(
|
|
'foo(2,#,3)',
|
|
[
|
|
[eOne]
|
|
],
|
|
'foo(2, 1, 3);');
|
|
testStatement('foo(2,#,3)', [eOne], 'foo(2, 1, 3);');
|
|
testStatement(
|
|
'foo(2,#,3)',
|
|
[
|
|
[eTrue, eOne]
|
|
],
|
|
'foo(2, true, 1, 3);');
|
|
testStatement('foo(2,#a,3)', {'a': []}, 'foo(2, 3);');
|
|
testStatement(
|
|
'foo(2,#a,3)',
|
|
{
|
|
'a': [eOne]
|
|
},
|
|
'foo(2, 1, 3);');
|
|
testStatement('foo(2,#a,3)', {'a': eOne}, 'foo(2, 1, 3);');
|
|
testStatement(
|
|
'foo(2,#a,3)',
|
|
{
|
|
'a': [eTrue, eOne]
|
|
},
|
|
'foo(2, true, 1, 3);');
|
|
|
|
// Interpolated Literals
|
|
testStatement('a = {#: 1}', [eOne], 'a = {1: 1};');
|
|
testStatement('a = {#a: 1}', {'a': eOne}, 'a = {1: 1};');
|
|
// Maybe we should make this work?
|
|
testError('a = {#: 1}', [1], 'is not a Literal: 1');
|
|
testError('a = {#a: 1}', {'a': 1}, 'is not a Literal: 1');
|
|
|
|
// Interpolated parameter splicing.
|
|
testStatement(
|
|
'function foo(#){}', [new jsAst.Parameter('x')], 'function foo(x) {\n}');
|
|
testStatement('function foo(#){}', ['x'], 'function foo(x) {\n}');
|
|
testStatement('function foo(#){}', [[]], 'function foo() {\n}');
|
|
testStatement(
|
|
'function foo(#){}',
|
|
[
|
|
['x']
|
|
],
|
|
'function foo(x) {\n}');
|
|
testStatement(
|
|
'function foo(#){}',
|
|
[
|
|
['x', 'y']
|
|
],
|
|
'function foo(x, y) {\n}');
|
|
testStatement('function foo(#a){}', {'a': new jsAst.Parameter('x')},
|
|
'function foo(x) {\n}');
|
|
testStatement('function foo(#a){}', {'a': 'x'}, 'function foo(x) {\n}');
|
|
testStatement('function foo(#a){}', {'a': []}, 'function foo() {\n}');
|
|
testStatement(
|
|
'function foo(#a){}',
|
|
{
|
|
'a': ['x']
|
|
},
|
|
'function foo(x) {\n}');
|
|
testStatement(
|
|
'function foo(#a){}',
|
|
{
|
|
'a': ['x', 'y']
|
|
},
|
|
'function foo(x, y) {\n}');
|
|
|
|
testStatement('function foo() async {}', [], 'function foo() async {\n}');
|
|
testStatement('function foo() sync* {}', [], 'function foo() sync* {\n}');
|
|
testStatement('function foo() async* {}', [], 'function foo() async* {\n}');
|
|
|
|
testStatement('a = #.#', [eVar, eOne], 'a = x[1];');
|
|
testStatement('a = #.#', [eVar, 'foo'], 'a = x.foo;');
|
|
testStatement('a = #a.#b', {'a': eVar, 'b': eOne}, 'a = x[1];');
|
|
testStatement('a = #a.#b', {'a': eVar, 'b': 'foo'}, 'a = x.foo;');
|
|
|
|
testStatement('function f(#) { return #.#; }', ['x', eVar, 'foo'],
|
|
'function f(x) {\n return x.foo;\n}');
|
|
testStatement('function f(#a) { return #b.#c; }',
|
|
{'a': 'x', 'b': eVar, 'c': 'foo'}, 'function f(x) {\n return x.foo;\n}');
|
|
|
|
testStatement(
|
|
'#.prototype.# = function(#) { return #.# };',
|
|
[
|
|
'className',
|
|
'getterName',
|
|
['r', 'y'],
|
|
'r',
|
|
'fieldName'
|
|
],
|
|
'className.prototype.getterName = function(r, y) {\n'
|
|
' return r.fieldName;\n'
|
|
'};');
|
|
testStatement(
|
|
'#a.prototype.#b = function(#c) { return #d.#e };',
|
|
{
|
|
'a': 'className',
|
|
'b': 'getterName',
|
|
'c': ['r', 'y'],
|
|
'd': 'r',
|
|
'e': 'fieldName'
|
|
},
|
|
'className.prototype.getterName = function(r, y) {\n'
|
|
' return r.fieldName;\n'
|
|
'};');
|
|
|
|
testStatement(
|
|
'function foo(r, #) { return #[r](#) }',
|
|
[
|
|
['a', 'b'],
|
|
'g',
|
|
['b', 'a']
|
|
],
|
|
'function foo(r, a, b) {\n return g[r](b, a);\n}');
|
|
testStatement(
|
|
'function foo(r, #a) { return #b[r](#c) }',
|
|
{
|
|
'a': ['a', 'b'],
|
|
'b': 'g',
|
|
'c': ['b', 'a']
|
|
},
|
|
'function foo(r, a, b) {\n return g[r](b, a);\n}');
|
|
|
|
// Sequence is printed flattened
|
|
testStatement('x = #', [seq1], 'x = (1, 2, 3);');
|
|
testStatement('x = (#, #)', [seq1, seq1], 'x = (1, 2, 3, 1, 2, 3);');
|
|
testStatement('x = #, #', [seq1, seq1], 'x = (1, 2, 3), 1, 2, 3;');
|
|
testStatement('for (i = 0, j = #, k = 0; ; ++i, ++j, ++k){}', [seq1],
|
|
'for (i = 0, j = (1, 2, 3), k = 0;; ++i, ++j, ++k) {\n}');
|
|
testStatement('x = #a', {'a': seq1}, 'x = (1, 2, 3);');
|
|
testStatement(
|
|
'x = (#a, #b)', {'a': seq1, 'b': seq1}, 'x = (1, 2, 3, 1, 2, 3);');
|
|
testStatement(
|
|
'x = #a, #b', {'a': seq1, 'b': seq1}, 'x = (1, 2, 3), 1, 2, 3;');
|
|
testStatement('for (i = 0, j = #a, k = 0; ; ++i, ++j, ++k){}', {'a': seq1},
|
|
'for (i = 0, j = (1, 2, 3), k = 0;; ++i, ++j, ++k) {\n}');
|
|
|
|
// Use the same name several times.
|
|
testStatement(
|
|
'#a.prototype.#a = function(#b) { return #c.#c };',
|
|
{
|
|
'a': 'name1_2',
|
|
'b': ['r', 'y'],
|
|
'c': 'name4_5'
|
|
},
|
|
'name1_2.prototype.name1_2 = function(r, y) {\n'
|
|
' return name4_5.name4_5;\n'
|
|
'};');
|
|
|
|
testStatement('label: while (a) { label2: break label;}', [],
|
|
'label:\n while (a)\n label2:\n break label;\n ');
|
|
|
|
testStatement('var # = 3', ['x'], 'var x = 3;');
|
|
testStatement(
|
|
'var # = 3', [new jsAst.VariableDeclaration('x')], 'var x = 3;');
|
|
testStatement(
|
|
'var # = 3, # = #', ['x', 'y', js.number(2)], 'var x = 3, y = 2;');
|
|
testStatement('var #a = 3, #b = #c', {"a": 'x', "b": 'y', "c": js.number(2)},
|
|
'var x = 3, y = 2;');
|
|
testStatement('function #() {}', ['x'], 'function x() {\n}');
|
|
testStatement('function #() {}', [new jsAst.VariableDeclaration('x')],
|
|
'function x() {\n}');
|
|
testStatement('try {} catch (#) {}', ['x'], 'try {\n} catch (x) {\n}');
|
|
testStatement('try {} catch (#a) {}', {"a": 'x'}, 'try {\n} catch (x) {\n}');
|
|
testStatement('try {} catch (#a) {}',
|
|
{"a": new jsAst.VariableDeclaration('x')}, 'try {\n} catch (x) {\n}');
|
|
|
|
// Test that braces around a single-statement block are removed by printer.
|
|
testStatement('while (a) {foo()}', [], 'while (a)\n foo();');
|
|
testStatement('if (a) {foo();}', [], 'if (a)\n foo();');
|
|
testStatement('if (a) {foo();} else {foo2();}', [],
|
|
'if (a)\n foo();\nelse\n foo2();');
|
|
testStatement(
|
|
'if (a) foo(); else {foo2();}', [], 'if (a)\n foo();\nelse\n foo2();');
|
|
testStatement('do {foo();} while(a);', [], 'do\n foo();\nwhile (a);');
|
|
testStatement('label: {foo();}', [], 'label:\n foo();');
|
|
testStatement(
|
|
'for (var key in a) {foo();}', [], 'for (var key in a)\n foo();');
|
|
// `label: break label;` gives problems on IE. Test that it is avoided.
|
|
testStatement('label: {break label;}', [], ';');
|
|
// This works on IE:
|
|
testStatement('label: {label2: {break label;}}', [],
|
|
'label:\n label2:\n break label;\n');
|
|
// Test dangling else:
|
|
testStatement('if (a) {if (b) {foo1();}} else {foo2();}', [], """
|
|
if (a) {
|
|
if (b)
|
|
foo1();
|
|
} else
|
|
foo2();""");
|
|
testStatement('if (a) {if (b) {foo1();} else {foo2();}}', [], """
|
|
if (a)
|
|
if (b)
|
|
foo1();
|
|
else
|
|
foo2();
|
|
""");
|
|
testStatement(
|
|
'if (a) {if (b) {foo1();} else {foo2();}} else {foo3();}', [], """
|
|
if (a)
|
|
if (b)
|
|
foo1();
|
|
else
|
|
foo2();
|
|
else
|
|
foo3();""");
|
|
testStatement('if (a) {while (true) if (b) {foo1();}} else {foo2();}', [], """
|
|
if (a) {
|
|
while (true)
|
|
if (b)
|
|
foo1();
|
|
} else
|
|
foo2();""");
|
|
}
|