blob: 5fa34cc12c5088073032d3de26dc3378cf322798 [file] [log] [blame]
// Copyright (c) 2015, 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 dart2js.constants.expressions.evaluate_test;
import 'dart:async';
import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';
import 'package:compiler/src/constants/evaluation.dart';
import 'package:compiler/src/constants/expressions.dart';
import 'package:compiler/src/constants/values.dart';
import 'package:compiler/src/constant_system_dart.dart';
import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/elements/elements.dart';
import 'memory_compiler.dart';
class TestData {
/// Declarations needed for the [constants].
final String declarations;
/// Tested constants.
final List constants;
const TestData(this.declarations, this.constants);
}
class ConstantData {
/// Source code for the constant expression.
final String code;
/// Map from environment to expected constant value as structured text.
final Map<Map<String, String>, String> expectedValues;
const ConstantData(this.code,
this.expectedValues);
}
class MemoryEnvironment implements Environment {
final Compiler compiler;
final Map<String, String> env;
MemoryEnvironment(this.compiler,
[this.env = const <String, String>{}]);
@override
String readFromEnvironment(String name) => env[name];
}
const List<TestData> DATA = const [
const TestData('', const [
const ConstantData('null', const { const {} : 'NullConstant' }),
const ConstantData('false', const { const {} : 'BoolConstant(false)' }),
const ConstantData('true', const { const {} : 'BoolConstant(true)' }),
const ConstantData('0', const { const {} : 'IntConstant(0)' }),
const ConstantData('0.0', const { const {} : 'DoubleConstant(0.0)' }),
const ConstantData('"foo"', const { const {} : 'StringConstant("foo")' }),
const ConstantData('1 + 2', const { const {} : 'IntConstant(3)' }),
const ConstantData('-(1)', const { const {} : 'IntConstant(-1)' }),
const ConstantData('"foo".length', const { const {} : 'IntConstant(3)' }),
const ConstantData('identical(0, 1)',
const { const {} : 'BoolConstant(false)' }),
const ConstantData('"a" "b"', const { const {} : 'StringConstant("ab")' }),
const ConstantData('identical',
const { const {} : 'FunctionConstant(identical)' }),
const ConstantData('true ? 0 : 1', const { const {} : 'IntConstant(0)' }),
const ConstantData('proxy',
const { const {} : 'ConstructedConstant(_Proxy())' }),
const ConstantData('Object', const { const {} : 'TypeConstant(Object)' }),
const ConstantData('const [0, 1]',
const { const {} : 'ListConstant([IntConstant(0), IntConstant(1)])' }),
const ConstantData('const <int>[0, 1]', const {
const {} : 'ListConstant(<int>[IntConstant(0), IntConstant(1)])' }),
const ConstantData('const {0: 1, 2: 3}',
const { const {} :
'MapConstant({IntConstant(0): IntConstant(1), '
'IntConstant(2): IntConstant(3)})' }),
const ConstantData('const <int, int>{0: 1, 2: 3}',
const { const {} :
'MapConstant(<int, int>{IntConstant(0): IntConstant(1), '
'IntConstant(2): IntConstant(3)})' }),
const ConstantData(
'const bool.fromEnvironment("foo", defaultValue: false)',
const { const {} : 'BoolConstant(false)',
const {'foo': 'true'} : 'BoolConstant(true)'}),
const ConstantData(
'const int.fromEnvironment("foo", defaultValue: 42)',
const { const {} : 'IntConstant(42)',
const {'foo': '87'} : 'IntConstant(87)'}),
const ConstantData(
'const String.fromEnvironment("foo", defaultValue: "bar")',
const { const {} : 'StringConstant("bar")',
const {'foo': 'foo'} : 'StringConstant("foo")'}),
]),
const TestData('''
const a = const bool.fromEnvironment("foo", defaultValue: true);
const b = const int.fromEnvironment("bar", defaultValue: 42);
class A {
const A();
}
class B {
final field1;
const B(this.field1);
}
class C extends B {
final field2;
const C({field1: 42, this.field2: false}) : super(field1);
const C.named([field = false]) : this(field1: field, field2: field);
}
''', const [
const ConstantData('const Object()',
const { const {} : 'ConstructedConstant(Object())' }),
const ConstantData('const A()',
const { const {} : 'ConstructedConstant(A())' }),
const ConstantData('const B(0)',
const { const {} : 'ConstructedConstant(B(field1=IntConstant(0)))' }),
const ConstantData('const B(const A())',
const { const {} :
'ConstructedConstant(B(field1=ConstructedConstant(A())))' }),
const ConstantData('const C()', const { const {} :
'ConstructedConstant(C(field1=IntConstant(42),'
'field2=BoolConstant(false)))' }),
const ConstantData('const C(field1: 87)', const { const {} :
'ConstructedConstant(C(field1=IntConstant(87),'
'field2=BoolConstant(false)))' }),
const ConstantData('const C(field2: true)', const { const {} :
'ConstructedConstant(C(field1=IntConstant(42),'
'field2=BoolConstant(true)))' }),
const ConstantData('const C.named()', const { const {} :
'ConstructedConstant(C(field1=BoolConstant(false),'
'field2=BoolConstant(false)))' }),
const ConstantData('const C.named(87)', const { const {} :
'ConstructedConstant(C(field1=IntConstant(87),'
'field2=IntConstant(87)))' }),
const ConstantData('const C(field1: a, field2: b)', const {
const {} :
'ConstructedConstant(C(field1=BoolConstant(true),'
'field2=IntConstant(42)))',
const {'foo': 'false', 'bar': '87'} :
'ConstructedConstant(C(field1=BoolConstant(false),'
'field2=IntConstant(87)))', }),
]),
const TestData('''
class A<T> implements B {
final field1;
const A({this.field1:42});
}
class B<S> implements C {
const factory B({field1}) = A<B<S>>;
const factory B.named() = A<S>;
}
class C<U> {
const factory C({field1}) = A<B<double>>;
}
''', const [
const ConstantData('const A()',
const { const {} :
'ConstructedConstant(A<dynamic>(field1=IntConstant(42)))' }),
const ConstantData('const A<int>(field1: 87)',
const { const {} :
'ConstructedConstant(A<int>(field1=IntConstant(87)))' }),
const ConstantData('const B()',
const { const {} :
'ConstructedConstant(A<B<dynamic>>(field1=IntConstant(42)))' }),
const ConstantData('const B<int>()',
const { const {} :
'ConstructedConstant(A<B<int>>(field1=IntConstant(42)))' }),
const ConstantData('const B<int>(field1: 87)',
const { const {} :
'ConstructedConstant(A<B<int>>(field1=IntConstant(87)))' }),
const ConstantData('const C<int>(field1: 87)',
const { const {} :
'ConstructedConstant(A<B<double>>(field1=IntConstant(87)))' }),
const ConstantData('const B<int>.named()',
const { const {} :
'ConstructedConstant(A<int>(field1=IntConstant(42)))' }),
]),
const TestData('''
const c = const int.fromEnvironment("foo", defaultValue: 5);
const d = const int.fromEnvironment("bar", defaultValue: 10);
class A {
final field;
const A(a, b) : field = a + b;
}
class B extends A {
const B(a) : super(a, a * 2);
}
''', const [
const ConstantData('const A(c, d)', const {
const {} :
'ConstructedConstant(A(field=IntConstant(15)))',
const {'foo': '7', 'bar': '11'} :
'ConstructedConstant(A(field=IntConstant(18)))', }),
const ConstantData('const B(d)', const {
const {} :
'ConstructedConstant(B(field=IntConstant(30)))',
const {'bar': '42'} :
'ConstructedConstant(B(field=IntConstant(126)))', }),
]),
];
main() {
asyncTest(() => Future.forEach(DATA, testData));
}
Future testData(TestData data) async {
StringBuffer sb = new StringBuffer();
sb.write('${data.declarations}\n');
Map constants = {};
data.constants.forEach((ConstantData constantData) {
String name = 'c${constants.length}';
sb.write('const $name = ${constantData.code};\n');
constants[name] = constantData;
});
sb.write('main() {}\n');
String source = sb.toString();
CompilationResult result = await runCompiler(
memorySourceFiles: {'main.dart': source}, options: ['--analyze-all']);
Compiler compiler = result.compiler;
var library = compiler.mainApp;
constants.forEach((String name, ConstantData data) {
FieldElement field = library.localLookup(name);
ConstantExpression constant = field.constant;
data.expectedValues.forEach(
(Map<String, String> env, String expectedText) {
Environment environment = new MemoryEnvironment(compiler, env);
ConstantValue value =
constant.evaluate(environment, DART_CONSTANT_SYSTEM);
String valueText = value.toStructuredString();
Expect.equals(expectedText, valueText,
"Unexpected value '${valueText}' for contant "
"`${constant.getText()}`, expected '${expectedText}'.");
});
});
}