blob: bbe09e23fcc41a4fccc2408ff140bb7fe3052854 [file] [log] [blame]
// 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.
library dart_backend.sexpr_test;
import 'dart:async';
import 'package:async_helper/async_helper.dart';
import 'package:compiler/src/dart2jslib.dart';
import 'package:compiler/src/cps_ir/cps_ir_nodes.dart';
import 'package:compiler/src/cps_ir/cps_ir_nodes_sexpr.dart';
import 'package:expect/expect.dart';
import '../compiler_helper.dart' hide compilerFor;
import 'sexpr_unstringifier.dart';
import 'test_helper.dart';
const String CODE = """
class Foo {
static int x = 1;
foo() => 42;
}
class Bar extends Foo {
int y = 2;
Bar() {
Foo.x = 2;
}
foo() => this.y + super.foo();
}
class FooBar<T> {
bool foobar() => T is int;
}
main() {
Foo foo;
if (foo is Foo) {
print("Surprise");
}
String inner() => "Inner function";
print(inner());
int j = 42;
for (int i = 0; i < 2; i++) {
print(i.toString());
}
print(Foo.x);
String recursive(int h) {
if (h < 1) {
return j.toString();
}
j++;
return "h\$\{recursive(h - 1)\}";
}
print(recursive(5));
Bar bar = new Bar();
print(bar.foo().toString());
bar = foo as Bar;
if (bar == null) {
var list = [1, 2, 3, 4];
var map = { 1: "one"
, 2: "two"
, 3: "three"
};
var double = 3.14;
list.forEach((i) => print(i.toString()));
print("\$list \$map \$double");
}
print(new FooBar<int>().foobar());
}
""";
bool shouldOutput(Element element) {
return (!element.library.isPlatformLibrary &&
!element.isSynthesized &&
element.kind == ElementKind.FUNCTION);
}
/// Compiles the given dart code (which must include a 'main' function) and
/// returns a list of all generated CPS IR definitions.
Future<List<FunctionDefinition>> compile(String code) {
return compilerFor(code).then((Compiler compiler) {
return compiler.enqueuer.resolution.resolvedElements
.where(shouldOutput)
.map(compiler.irBuilder.getIr)
.toList();
});
}
/// Returns an S-expression string for each compiled function.
List<String> stringifyAll(Iterable<FunctionDefinition> functions) {
final stringifier = new SExpressionStringifier();
return functions.map((f) => stringifier.visitFunctionDefinition(f)).toList();
}
Future<List<String>> testStringifier(String code,
Iterable<String> expectedTokens) {
return compile(code)
.then((List<FunctionDefinition> functions) {
List<String> sexprs = stringifyAll(functions);
String combined = sexprs.join();
String withoutNullConstants = combined.replaceAll("Constant null", "");
Expect.isFalse(withoutNullConstants.contains("null"));
for (String token in expectedTokens) {
Expect.isTrue(combined.contains(token),
"'$combined' doesn't contain '$token' in test:\n$code");
}
return sexprs;
});
}
/// Checks if the generated S-expressions can be processed by the unstringifier,
/// returns the resulting definitions.
List<FunctionDefinition> testUnstringifier(List<String> sexprs) {
return sexprs.map((String sexpr) {
try {
final function = new SExpressionUnstringifier().unstringify(sexpr);
Expect.isNotNull(function, "Unstringification failed:\n\n$sexpr");
return function;
} catch (e, s) {
print('$e\n$s');
Expect.fail('Error unstringifying "$sexpr": $e');
}
}).toList();
}
void main() {
final tokens =
[ "FunctionDefinition"
, "IsTrue"
// Expressions
, "Branch"
, "ConcatenateStrings"
, "DeclareFunction"
, "InvokeConstructor"
, "InvokeContinuation"
, "InvokeMethod"
, "InvokeStatic"
, "InvokeMethodDirectly"
, "LetCont"
, "LetPrim"
, "SetMutableVariable"
, "TypeOperator"
// Primitives
, "Constant"
, "CreateFunction"
, "GetMutableVariable"
, "LiteralList"
, "LiteralMap"
// Parameters are encoded by name only and hence are not in this list.
, "ReifyTypeVar"
, "(this)" // 'this' Parameter declarations
, "this" // 'this' Parameter uses
];
asyncTest(() => testStringifier(CODE, tokens).then((List<String> sexprs) {
final functions = testUnstringifier(sexprs);
// Ensure that
// stringified(CODE) == stringified(unstringified(stringified(CODE)))
Expect.listEquals(sexprs, stringifyAll(functions));
}));
}