blob: ecc0a004ee126654b171a1dc0722f0a25997f03b [file] [log] [blame]
// Copyright (c) 2012, 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 "package:expect/expect.dart";
import 'dart:async';
import "package:async_helper/async_helper.dart";
import 'mock_compiler.dart';
import 'package:compiler/compiler.dart';
import 'package:compiler/implementation/dart2jslib.dart' as leg;
import 'package:compiler/implementation/dart_backend/dart_backend.dart';
import 'package:compiler/implementation/elements/elements.dart';
import 'package:compiler/implementation/tree/tree.dart';
const coreLib = r'''
library corelib;
class Object {
Object();
}
class bool {}
class num {}
class int extends num {}
class double extends num {}
abstract class String {}
class Function {}
class List<T> {}
class Map<K,V> {}
class BoundClosure {}
class Closure {}
class Dynamic_ {}
class Null {}
class TypeError {}
class Type {}
class StackTrace {}
class LinkedHashMap {
factory LinkedHashMap._empty() => null;
factory LinkedHashMap._literal(elements) => null;
}
class Math {
static double parseDouble(String s) => 1.0;
}
print(x) {}
identical(a, b) => true;
const proxy = 0;
''';
const corePatch = r'''
import 'dart:_js_helper';
import 'dart:_interceptors';
import 'dart:_isolate_helper';
import 'dart:_foreign_helper';
''';
const ioLib = r'''
library io;
class Platform {
static int operatingSystem;
}
''';
const htmlLib = r'''
library html;
Window __window;
Window get window => __window;
abstract class Window {
Navigator get navigator;
}
abstract class Navigator {
String get userAgent;
}
''';
const helperLib = r'''
library js_helper;
class JSInvocationMirror {}
assertHelper(a) {}
class Closure {}
class BoundClosure {}
const patch = 0;
''';
const foreignLib = r'''
var JS;
''';
const isolateHelperLib = r'''
class _WorkerStub {
}
''';
testDart2Dart(String src, {void continuation(String s), bool minify: false,
bool stripTypes: false}) {
// If continuation is not provided, check that source string remains the same.
if (continuation == null) {
continuation = (s) { Expect.equals(src, s); };
}
testDart2DartWithLibrary(src, '', continuation: continuation, minify: minify,
stripTypes: stripTypes);
}
/**
* Library name is assumed to be 'mylib' in 'mylib.dart' file.
*/
testDart2DartWithLibrary(
String srcMain, String srcLibrary,
{void continuation(String s), bool minify: false,
bool stripTypes: false}) {
fileUri(path) => new Uri(scheme: 'file', path: path);
final scriptUri = fileUri('script.dart');
final libUri = fileUri('mylib.dart');
provider(uri) {
if (uri == scriptUri) return new Future.value(srcMain);
if (uri.toString() == libUri.toString()) {
return new Future.value(srcLibrary);
}
if (uri.path.endsWith('/core.dart')) {
return new Future.value(coreLib);
} else if (uri.path.endsWith('/core_patch.dart')) {
return new Future.value(corePatch);
} else if (uri.path.endsWith('/io.dart')) {
return new Future.value(ioLib);
} else if (uri.path.endsWith('/js_helper.dart')) {
return new Future.value(helperLib);
} else if (uri.path.endsWith('/html_dart2js.dart')) {
// TODO(smok): The file should change to html_dartium at some point.
return new Future.value(htmlLib);
} else if (uri.path.endsWith('/foreign_helper.dart')) {
return new Future.value(foreignLib);
} else if (uri.path.endsWith('/isolate_helper.dart')) {
return new Future.value(isolateHelperLib);
}
return new Future.value('');
}
handler(uri, begin, end, message, kind) {
if (identical(kind, Diagnostic.ERROR) || identical(kind, Diagnostic.CRASH)) {
Expect.fail('$uri: $begin-$end: $message [$kind]');
}
}
final options = <String>['--output-type=dart'];
// Some tests below are using dart:io.
options.add('--categories=Client,Server');
if (minify) options.add('--minify');
if (stripTypes) options.add('--force-strip=types');
asyncTest(() => compile(
scriptUri,
fileUri('libraryRoot/'),
fileUri('packageRoot/'),
provider,
handler,
options).then(continuation));
}
testSimpleFileUnparse() {
final src = '''
should_be_dropped() {
}
should_be_kept() {
}
main() {
should_be_kept();
}
''';
testDart2Dart(src, continuation: (String s) {
print(s);
Expect.equals('''
should_be_kept() {}
main() {
should_be_kept();
}
''', s);
});
}
testTopLevelField() {
testDart2Dart('''
final String x = "asd";
main() {
x;
}
''');
}
testSimpleTopLevelClass() {
testDart2Dart('''
main() {
new A();
}
class A {
A() {}
}
''');
}
testClassWithSynthesizedConstructor() {
testDart2Dart('''
main() {
new A();
}
class A {}
''');
}
testClassWithMethod() {
testDart2Dart(r'''
main() {
var a = new A();
a.foo();
}
class A {
void foo() {}
}
''');
}
testExtendsImplements() {
testDart2Dart('''
main() {
new B<Object>();
}
class A<T> {}
class B<T> extends A<T> {}
''');
}
testVariableDefinitions() {
testDart2Dart('''
main() {
var x, y;
final String s = null;
}
''');
testDart2Dart('''
main() {
final int x = 0, y = 0;
final String s = null;
}
''');
testDart2Dart('''
foo(f, g) {}
main() {
foo(1, 2);
}
''');
testDart2Dart('''
foo(f(arg)) {}
main() {
foo(main);
}
''');
// A couple of static/finals inside a class.
testDart2Dart('''
main() {
A.a;
A.b;
}
class A {
static const String a = "5";
static const String b = "4";
}
''');
// Class member of typedef-ed function type.
// Maybe typedef should be included in the result too, but it
// works fine without it.
testDart2Dart('''
typedef void foofunc(arg);
main() {
new A((arg) {});
}
class A {
A(foofunc this.handler);
final foofunc handler;
}
''');
}
testGetSet() {
// Top-level get/set.
testDart2Dart('''
set foo(arg) {}
get foo {
return 5;
}
main() {
foo;
foo = 5;
}
''');
// Field get/set.
testDart2Dart('''
main() {
var a = new A();
a.foo;
a.foo = 5;
}
class A {
set foo(a) {}
get foo {
return 5;
}
}
''');
// Typed get/set.
testDart2Dart('''
String get foo {
return "a";
}
main() {
foo;
}
''');
}
testAbstractClass() {
testDart2Dart('''
main() {
A.foo;
}
abstract class A {
static final num foo = 0;
}
''');
}
testConflictSendsRename() {
// Various Send-s to current library and external library. Verify that
// everything is renamed correctly in conflicting class names and global
// functions.
var librarySrc = '''
library mylib;
globalfoo() {}
var globalVar;
var globalVarInitialized = 6, globalVarInitialized2 = 7;
class A {
A(){}
A.fromFoo(){}
static staticfoo(){}
foo(){}
static const field = 5;
}
''';
var mainSrc = '''
import 'mylib.dart' as mylib;
globalfoo() {}
var globalVar;
var globalVarInitialized = 6, globalVarInitialized2 = 7;
class A {
A(){}
A.fromFoo(){}
static staticfoo(){}
foo(){}
static const field = 5;
}
main() {
globalVar;
globalVarInitialized;
globalVarInitialized2;
globalfoo();
A.field;
A.staticfoo();
new A();
new A.fromFoo();
new A().foo();
mylib.globalVar;
mylib.globalVarInitialized;
mylib.globalVarInitialized2;
mylib.globalfoo();
mylib.A.field;
mylib.A.staticfoo();
new mylib.A();
new mylib.A.fromFoo();
new mylib.A().foo();
}
''';
var expectedResult = '''
globalfoo() {}
var globalVar;
var globalVarInitialized = 6;
var globalVarInitialized2 = 7;
class A {
A() {}
A.fromFoo() {}
static staticfoo() {}
foo() {}
static const field = 5;
}
A_globalfoo() {}
var A_globalVar;
var A_globalVarInitialized = 6;
var A_globalVarInitialized2 = 7;
class A_A {
A_A() {}
A_A.A_fromFoo() {}
static A_staticfoo() {}
foo() {}
static const A_field = 5;
}
main() {
A_globalVar;
A_globalVarInitialized;
A_globalVarInitialized2;
A_globalfoo();
A_A.A_field;
A_A.A_staticfoo();
new A_A();
new A_A.A_fromFoo();
new A_A().foo();
globalVar;
globalVarInitialized;
globalVarInitialized2;
globalfoo();
A.field;
A.staticfoo();
new A();
new A.fromFoo();
new A().foo();
}
''';
testDart2DartWithLibrary(mainSrc, librarySrc,
continuation: (String result) { Expect.equals(expectedResult, result); });
}
testNoConflictSendsRename() {
// Various Send-s to current library and external library. Nothing should be
// renamed here, only library prefixes must be cut.
var librarySrc = '''
library mylib;
globalfoo() {}
class A {
A(){}
A.fromFoo(){}
static staticfoo(){}
foo(){}
static const field = 5;
}
''';
var mainSrc = '''
import 'mylib.dart' as mylib;
myglobalfoo() {}
class MyA {
MyA(){}
MyA.myfromFoo(){}
static mystaticfoo(){}
myfoo(){}
static const myfield = 5;
}
main() {
myglobalfoo();
MyA.myfield;
MyA.mystaticfoo();
new MyA();
new MyA.myfromFoo();
new MyA().myfoo();
mylib.globalfoo();
mylib.A.field;
mylib.A.staticfoo();
new mylib.A();
new mylib.A.fromFoo();
new mylib.A().foo();
}
''';
var expectedResult = '''
globalfoo() {}
class A {
A() {}
A.fromFoo() {}
static staticfoo() {}
foo() {}
static const field = 5;
}
myglobalfoo() {}
class MyA {
MyA() {}
MyA.myfromFoo() {}
static mystaticfoo() {}
myfoo() {}
static const myfield = 5;
}
main() {
myglobalfoo();
MyA.myfield;
MyA.mystaticfoo();
new MyA();
new MyA.myfromFoo();
new MyA().myfoo();
globalfoo();
A.field;
A.staticfoo();
new A();
new A.fromFoo();
new A().foo();
}
''';
testDart2DartWithLibrary(mainSrc, librarySrc,
continuation: (String result) { Expect.equals(expectedResult, result); });
}
testConflictLibraryClassRename() {
var librarySrc = '''
library mylib;
topfoo() {}
class A {
foo() {}
}
''';
var mainSrc = '''
import 'mylib.dart' as mylib;
topfoo() {
var x = 5;
}
class A {
num foo() {}
A.fromFoo() {}
mylib.A myliba;
List<A> mylist;
}
mylib.A getA() => null;
main() {
var a = new mylib.A();
a.foo();
var b = new A.fromFoo();
b.foo();
var GREATVAR = b.myliba;
b.mylist;
a = getA();
topfoo();
mylib.topfoo();
}
''';
var expectedResult = '''
topfoo() {}
class A {
foo() {}
}
A_topfoo() {
var x = 5;
}
class A_A {
num foo() {}
A_A.fromFoo() {}
A myliba;
List<A_A> mylist;
}
A getA() => null;
main() {
var a = new A();
a.foo();
var b = new A_A.fromFoo();
b.foo();
var GREATVAR = b.myliba;
b.mylist;
a = getA();
A_topfoo();
topfoo();
}
''';
testDart2DartWithLibrary(mainSrc, librarySrc,
continuation: (String result) { Expect.equals(expectedResult, result); });
}
testClassExtendsWithArgs() {
testDart2Dart('''
main() {
new B<Object>();
}
class A<T extends Object> {}
class B<T extends Object> extends A<T> {}
''');
}
testStaticInvocation() {
testDart2Dart('''
main() {
var x = Math.parseDouble("1");
}
''');
}
testLibraryGetSet() {
var librarySrc = '''
library mylib;
get topgetset => 5;
set topgetset(arg) {}
''';
var mainSrc = '''
import 'mylib.dart' as mylib;
get topgetset => 6;
set topgetset(arg) {}
main() {
topgetset;
topgetset = 6;
mylib.topgetset;
mylib.topgetset = 5;
}
''';
var expectedResult = '''
get topgetset => 5;
set topgetset(arg) {}
get A_topgetset => 6;
set A_topgetset(arg) {}
main() {
A_topgetset;
A_topgetset = 6;
topgetset;
topgetset = 5;
}
''';
testDart2DartWithLibrary(mainSrc, librarySrc,
continuation: (String result) { Expect.equals(expectedResult, result); });
}
testFieldTypeOutput() {
testDart2Dart('''
main() {
new A().field;
}
class B {}
class A {
B field;
}
''');
}
class DynoMap implements Map<Element, ElementAst> {
final compiler;
DynoMap(this.compiler);
ElementAst operator[](Element element) => new ElementAst(element);
noSuchMethod(Invocation invocation) => throw 'unimplemented method';
}
PlaceholderCollector collectPlaceholders(compiler, element) =>
new PlaceholderCollector(compiler, new Set<String>(), new DynoMap(compiler))
..collect(element);
testLocalFunctionPlaceholder() {
var src = '''
main() {
function localfoo() {}
localfoo();
}
''';
MockCompiler compiler = new MockCompiler.internal(emitJavaScript: false);
asyncTest(() => compiler.init().then((_) {
assert(compiler.backend is DartBackend);
compiler.parseScript(src);
FunctionElement mainElement = compiler.mainApp.find(leg.Compiler.MAIN);
compiler.processQueue(compiler.enqueuer.resolution, mainElement);
PlaceholderCollector collector = collectPlaceholders(compiler, mainElement);
FunctionExpression mainNode = mainElement.node;
Block body = mainNode.body;
FunctionDeclaration functionDeclaration = body.statements.nodes.head;
FunctionExpression fooNode = functionDeclaration.function;
LocalPlaceholder fooPlaceholder =
collector.functionScopes[mainElement].localPlaceholders.first;
Expect.isTrue(fooPlaceholder.nodes.contains(fooNode.name));
}));
}
testTypeVariablesAreRenamed() {
// Somewhat a hack: we require all the references of the identifier
// to be renamed in the same way for the whole library. Hence
// if we have a class and type variable with the same name, they
// both should be renamed.
var librarySrc = '''
library mylib;
typedef void MyFunction<T extends num>(T arg);
class T {}
class B<T> {}
class A<T> extends B<T> { T f; }
''';
var mainSrc = '''
import 'mylib.dart' as mylib;
typedef void MyFunction<T extends num>(T arg);
class T {}
class B<T> {}
class A<T> extends B<T> { T f; }
main() {
MyFunction myf1;
mylib.MyFunction myf2;
new A<int>().f;
new T();
new mylib.A<int>().f;
new mylib.T();
}
''';
var expectedResult = '''
typedef void MyFunction<T extends num>(T arg);
class T {}
class B<T> {}
class A<T> extends B<T> {
T f;
}
typedef void A_MyFunction<A_T extends num>(A_T arg);
class A_T {}
class A_B<A_T> {}
class A_A<A_T> extends A_B<A_T> {
A_T f;
}
main() {
A_MyFunction myf1;
MyFunction myf2;
new A_A<int>().f;
new A_T();
new A<int>().f;
new T();
}
''';
testDart2DartWithLibrary(mainSrc, librarySrc,
continuation: (String result) { Expect.equals(expectedResult, result); });
}
testClassTypeArgumentBound() {
var librarySrc = '''
library mylib;
class I {}
class A<T extends I> {}
''';
var mainSrc = '''
import 'mylib.dart' as mylib;
class I {}
class A<T extends I> {}
main() {
new A();
new mylib.A();
}
''';
var expectedResult = '''
class I {}
class A<T extends I> {}
class A_I {}
class A_A<A_T extends A_I> {}
main() {
new A_A();
new A();
}
''';
testDart2DartWithLibrary(mainSrc, librarySrc,
continuation: (String result) { Expect.equals(expectedResult, result); });
}
testDoubleMains() {
var librarySrc = '''
library mylib;
main() {}
''';
var mainSrc = '''
import 'mylib.dart' as mylib;
main() {
mylib.main();
}
''';
var expectedResult = '''
A_main() {}
main() {
A_main();
}
''';
testDart2DartWithLibrary(mainSrc, librarySrc,
continuation: (String result) { Expect.equals(expectedResult, result); });
}
testStaticAccessIoLib() {
var src = '''
import 'dart:io';
main() {
Platform.operatingSystem;
}
''';
var expectedResult = '''
import "dart:io" as p;
main() {
p.Platform.operatingSystem;
}
''';
testDart2Dart(src,
continuation: (String result) { Expect.equals(expectedResult, result); });
}
testMinification() {
var src = '''
class ClassWithVeryVeryLongName {}
main() {
new ClassWithVeryVeryLongName();
}
''';
var expectedResult =
'class A{}'
'main(){new A();}';
testDart2Dart(src, continuation:
(String result) { Expect.equals(expectedResult, result); }, minify: true);
}
testClosureLocalsMinified() {
var src = '''
main() {
var a = 7;
void foo1(a,b) {
void foo2(c,d) {
var E = a;
}
foo2(b, a);
}
foo1(a, 8);
}
''';
var expectedResult =
'main(){var A=7; B(A,C){ D(E,F){var G=A;}D(C,A);}B(A,8);}';
testDart2Dart(src, continuation:
(String result) { Expect.equals(expectedResult, result); }, minify: true);
}
testParametersMinified() {
var src = '''
class A {
var a;
static foo(arg1) {
// Should not rename arg1 to a.
arg1 = 5;
}
}
fooglobal(arg,{optionalarg: 7}) {
arg = 6;
}
main() {
new A().a;
A.foo(8);
fooglobal(8);
}
''';
var expectedResult =
'class B{var E;static C(A){A=5;}}D(A,{optionalarg: 7}){A=6;}'
'main(){new B().E;B.C(8);D(8);}';
testDart2Dart(src, continuation:
(String result) { Expect.equals(expectedResult, result); }, minify: true);
}
testDeclarationTypePlaceholders() {
var src = '''
String globalfield;
const String globalconstfield;
void foo(String arg) {}
main() {
String localvar;
foo("5");
}
''';
var expectedResult = '''
foo( arg) {}
main() {
var localvar;
foo("5");
}
''';
testDart2Dart(src,
continuation: (String result) { Expect.equals(expectedResult, result); },
stripTypes: true);
}
testPlatformLibraryMemberNamesAreFixed() {
var src = '''
import 'dart:html';
class A {
static String get userAgent => window.navigator.userAgent;
}
main() {
A.userAgent;
}
''';
var expectedResult = '''
import "dart:html" as p;
class A {
static String get A_userAgent => p.window.navigator.userAgent;
}
main() {
A.A_userAgent;
}
''';
testDart2Dart(src,
continuation: (String result) { Expect.equals(expectedResult, result); });
}
testConflictsWithCoreLib() {
var src = '''
import 'dart:core' as fisk;
print(x) { throw 'fisk'; }
main() {
fisk.print('corelib');
print('local');
}
''';
var expectedResult = """
A_print(x) {
throw 'fisk';
}
main() {
print('corelib');
A_print('local');
}
""";
testDart2Dart(src,
continuation: (String result) { Expect.equals(expectedResult, result); });
}
testUnresolvedNamedConstructor() {
var src = '''
class A {
A() {}
}
main() {
new A();
new A.named();
}
''';
var expectedResult = """
class A {
A() {}
}
main() {
new A();
new A_Unresolved();
}
""";
testDart2Dart(src,
continuation: (String result) { Expect.equals(expectedResult, result); });
}
main() {
testSimpleFileUnparse();
testTopLevelField();
testSimpleTopLevelClass();
testClassWithSynthesizedConstructor();
testClassWithMethod();
testExtendsImplements();
testVariableDefinitions();
testGetSet();
testAbstractClass();
testConflictSendsRename();
testNoConflictSendsRename();
testConflictLibraryClassRename();
testClassExtendsWithArgs();
testStaticInvocation();
testLibraryGetSet();
testFieldTypeOutput();
testTypeVariablesAreRenamed();
testClassTypeArgumentBound();
testDoubleMains();
testStaticAccessIoLib();
testLocalFunctionPlaceholder();
testMinification();
testClosureLocalsMinified();
testParametersMinified();
testDeclarationTypePlaceholders();
testPlatformLibraryMemberNamesAreFixed();
testConflictsWithCoreLib();
testUnresolvedNamedConstructor();
}