Add InstantiationConstantExpression/Value

Change-Id: I2f471560a076e79f944fa70000de4be50385ae18
Reviewed-on: https://dart-review.googlesource.com/52443
Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
Johnni Winther 2018-04-25 09:37:29 +00:00
parent e9ffc02663
commit 5e72411c5c
12 changed files with 452 additions and 146 deletions

View file

@ -42,6 +42,9 @@ abstract class EvaluationEnvironment {
DartType substByContext(
covariant DartType base, covariant InterfaceType target);
/// Returns [type] in the context of the [enclosingConstructedType].
DartType getTypeInContext(DartType type);
void reportWarning(
ConstantExpression expression, MessageKind kind, Map arguments);
@ -127,6 +130,47 @@ abstract class EvaluationEnvironmentBase implements EvaluationEnvironment {
reporter.reportWarningMessage(_spannable, kind, arguments);
}
}
@override
DartType getTypeInContext(DartType type) {
// Running example for comments:
//
// class A<T> {
// final T t;
// const A(dynamic t) : this.t = t; // implicitly `t as A.T`
// }
// class B<S> extends A<S> {
// const B(dynamic s) : super(s);
// }
// main() => const B<num>(0);
//
// We visit `t as A.T` while evaluating `const B<num>(0)`.
// The `as` type is `A.T`.
DartType typeInContext = type;
// The enclosing type is `B<num>`.
DartType enclosingType = enclosingConstructedType;
if (enclosingType != null) {
ClassEntity contextClass;
type.forEachTypeVariable((TypeVariableType type) {
if (type.element.typeDeclaration is ClassEntity) {
// We find `A` from `A.T`. Since we don't have nested classes, class
// based type variables can only come from the same class.
contextClass = type.element.typeDeclaration;
}
});
if (contextClass != null) {
// The enclosing type `B<num>` as an instance of `A` is `A<num>`.
enclosingType = types.asInstanceOf(enclosingType, contextClass);
}
// `A.T` in the context of the enclosing type `A<num>` is `num`.
typeInContext = enclosingType != null
? substByContext(typeInContext, enclosingType)
: typeInContext;
}
return typeInContext;
}
}
/// The normalized arguments passed to a const constructor computed from the

View file

@ -11,6 +11,7 @@ import '../elements/entities.dart';
import '../elements/operators.dart';
import '../elements/types.dart';
import '../universe/call_structure.dart' show CallStructure;
import '../util/util.dart';
import 'constructors.dart';
import 'evaluation.dart';
import 'values.dart';
@ -45,6 +46,7 @@ enum ConstantExpressionKind {
POSITIONAL_REFERENCE,
NAMED_REFERENCE,
ASSERT,
INSTANTIATION,
}
/// An expression that is a compile-time constant.
@ -868,30 +870,9 @@ class AsConstantExpression extends ConstantExpression {
DartType expressionType =
expressionValue.getType(environment.commonElements);
// The `as` type is `A.T`.
DartType typeInContext = type;
// The `as` type `A.T` in the context of `B<num>` is `num`.
DartType typeInContext = environment.getTypeInContext(type);
// The enclosing type is `B<num>`.
DartType enclosingType = environment.enclosingConstructedType;
if (enclosingType != null) {
ClassEntity contextClass;
type.forEachTypeVariable((TypeVariableType type) {
if (type.element.typeDeclaration is ClassEntity) {
// We find `A` from `A.T`. Since we don't have nested classes, class
// based type variables can only come from the same class.
contextClass = type.element.typeDeclaration;
}
});
if (contextClass != null) {
// The enclosing type `B<num>` as an instance of `A` is `A<num>`.
enclosingType =
environment.types.asInstanceOf(enclosingType, contextClass);
}
// `A.T` in the context of the enclosing type `A<num>` is `num`.
typeInContext = enclosingType != null
? environment.substByContext(typeInContext, enclosingType)
: typeInContext;
}
// Check that the expression type, `int`, is a subtype of the type in
// context, `num`.
if (!constantSystem.isSubtype(
@ -2154,6 +2135,57 @@ class DeferredConstantExpression extends ConstantExpression {
}
}
class InstantiationConstantExpression extends ConstantExpression {
final List<DartType> typeArguments;
final ConstantExpression expression;
InstantiationConstantExpression(this.typeArguments, this.expression);
ConstantExpressionKind get kind => ConstantExpressionKind.INSTANTIATION;
@override
void _createStructuredText(StringBuffer sb) {
sb.write('Instantiation(typeArguments=$typeArguments,expression=');
expression._createStructuredText(sb);
sb.write(')');
}
@override
ConstantValue evaluate(
EvaluationEnvironment environment, ConstantSystem constantSystem) {
List<DartType> typeArgumentsInContext =
typeArguments.map(environment.getTypeInContext).toList();
return new InstantiationConstantValue(typeArgumentsInContext,
expression.evaluate(environment, constantSystem));
}
@override
int _computeHashCode() {
return Hashing.objectHash(expression, Hashing.listHash(typeArguments));
}
ConstantExpression apply(NormalizedArguments arguments) {
return new InstantiationConstantExpression(
typeArguments, expression.apply(arguments));
}
@override
bool _equals(InstantiationConstantExpression other) {
return equalElements(typeArguments, other.typeArguments) &&
expression == other.expression;
}
@override
accept(ConstantExpressionVisitor visitor, [context]) {
return visitor.visitInstantiation(this, context);
}
@override
bool get isPotential {
return expression.isPotential;
}
}
abstract class ConstantExpressionVisitor<R, A> {
const ConstantExpressionVisitor();
@ -2189,6 +2221,7 @@ abstract class ConstantExpressionVisitor<R, A> {
StringFromEnvironmentConstantExpression exp, A context);
R visitDeferred(DeferredConstantExpression exp, A context);
R visitAssert(AssertConstantExpression exp, A context);
R visitInstantiation(InstantiationConstantExpression exp, A context);
R visitPositional(PositionalArgumentReference exp, A context);
R visitNamed(NamedArgumentReference exp, A context);
@ -2480,5 +2513,14 @@ class ConstExpPrinter extends ConstantExpressionVisitor {
sb.write(')');
}
@override
void visitInstantiation(InstantiationConstantExpression exp, [_]) {
sb.write('<');
sb.write(exp.typeArguments.join(', '));
sb.write('>(');
visit(exp.expression);
sb.write(')');
}
String toString() => sb.toString();
}

View file

@ -9,7 +9,7 @@ import '../common_elements.dart';
import '../elements/entities.dart';
import '../elements/types.dart';
import '../deferred_load.dart' show OutputUnit;
import '../util/util.dart' show Hashing;
import '../util/util.dart';
enum ConstantValueKind {
FUNCTION,
@ -50,6 +50,8 @@ abstract class ConstantValueVisitor<R, A> {
R visitDeferredGlobal(
covariant DeferredGlobalConstantValue constant, covariant A arg);
R visitNonConstant(covariant NonConstantValue constant, covariant A arg);
R visitInstantiation(
covariant InstantiationConstantValue constant, covariant A arg);
}
abstract class ConstantValue {
@ -131,7 +133,7 @@ class FunctionConstantValue extends ConstantValue {
List<ConstantValue> getDependencies() => const <ConstantValue>[];
DartType getType(CommonElements types) => type;
FunctionType getType(CommonElements types) => type;
int get hashCode => (17 * element.hashCode) & 0x7fffffff;
@ -775,7 +777,7 @@ class DeferredConstantValue extends ConstantValue {
import == other.import;
}
get hashCode => (referenced.hashCode * 17 + import.hashCode) & 0x3fffffff;
int get hashCode => (referenced.hashCode * 17 + import.hashCode) & 0x3fffffff;
List<ConstantValue> getDependencies() => <ConstantValue>[referenced];
@ -792,6 +794,45 @@ class DeferredConstantValue extends ConstantValue {
}
}
class InstantiationConstantValue extends ConstantValue {
final List<DartType> typeArguments;
final FunctionConstantValue function;
InstantiationConstantValue(this.typeArguments, this.function);
bool operator ==(other) {
if (identical(this, other)) return true;
return other is InstantiationConstantValue &&
function == other.function &&
equalElements(typeArguments, other.typeArguments);
}
@override
int get hashCode {
return Hashing.objectHash(function, Hashing.listHash(typeArguments));
}
List<ConstantValue> getDependencies() => <ConstantValue>[function];
accept(ConstantValueVisitor visitor, arg) =>
visitor.visitInstantiation(this, arg);
DartType getType(CommonElements types) {
FunctionType type = function.getType(types);
return type.instantiate(typeArguments);
}
ConstantValueKind get kind => ConstantValueKind.DEFERRED;
String toDartText() =>
'<${typeArguments.join(', ')}>(${function.toDartText()})';
String toStructuredText() {
return 'InstantiationConstant($typeArguments,'
'${function.toStructuredText()})';
}
}
/// A reference to a constant in another output unit.
///
/// Used for referring to deferred constants that appear as initializers of

View file

@ -333,6 +333,14 @@ class ConstantEmitter implements ConstantValueVisitor<jsAst.Expression, Null> {
return instantiation;
}
@override
jsAst.Expression visitInstantiation(InstantiationConstantValue constant,
[_]) {
// TODO(sigmund, sra): add a runtime representation for constant
// instantiations. See issue 32774.
return constantReferenceGenerator(constant.function);
}
String stripComments(String rawJavaScript) {
return rawJavaScript.replaceAll(COMMENT_RE, '');
}

View file

@ -1879,6 +1879,11 @@ class ConstantNamingVisitor implements ConstantValueVisitor {
add(constant.element.name);
}
@override
void visitInstantiation(InstantiationConstantValue constant, [_]) {
_visit(constant.function);
}
@override
void visitNull(NullConstantValue constant, [_]) {
add('null');
@ -2080,6 +2085,11 @@ class ConstantCanonicalHasher implements ConstantValueVisitor<int, Null> {
return _hashString(1, constant.element.name);
}
@override
int visitInstantiation(InstantiationConstantValue constant, [_]) {
return _visit(constant.function);
}
@override
int visitInt(IntConstantValue constant, [_]) {
return _hashInt(constant.intValue);

View file

@ -240,6 +240,13 @@ class _ConstantOrdering
if (r != 0) return r;
return a.unit.compareTo(b.unit);
}
int visitInstantiation(
InstantiationConstantValue a, InstantiationConstantValue b) {
int r = compareValues(a.function, b.function);
if (r != 0) return r;
return compareLists(compareDartTypes, a.typeArguments, b.typeArguments);
}
}
class _KindVisitor implements ConstantValueVisitor<int, Null> {
@ -259,7 +266,8 @@ class _KindVisitor implements ConstantValueVisitor<int, Null> {
static const int SYNTHETIC = 12;
static const int DEFERRED = 13;
static const int DEFERRED_GLOBAL = 14;
static const int NONCONSTANT = 13;
static const int NONCONSTANT = 15;
static const int INSTANTIATION = 16;
static int kind(ConstantValue constant) =>
constant.accept(const _KindVisitor(), null);
@ -279,6 +287,7 @@ class _KindVisitor implements ConstantValueVisitor<int, Null> {
int visitSynthetic(SyntheticConstantValue a, _) => SYNTHETIC;
int visitDeferred(DeferredConstantValue a, _) => DEFERRED;
int visitDeferredGlobal(DeferredGlobalConstantValue a, _) => DEFERRED_GLOBAL;
int visitInstantiation(InstantiationConstantValue a, _) => INSTANTIATION;
}
/// Visitor for distinguishing types by kind.

View file

@ -699,6 +699,13 @@ class ConstantConverter implements ConstantValueVisitor<ConstantValue, Null> {
return new DeferredGlobalConstantValue(referenced, constant.unit);
}
ConstantValue visitInstantiation(InstantiationConstantValue constant, _) {
ConstantValue function = constant.function.accept(this, null);
List<DartType> typeArguments =
typeConverter.convertTypes(constant.typeArguments);
return new InstantiationConstantValue(typeArguments, function);
}
List<ConstantValue> _handleValues(List<ConstantValue> values) {
List<ConstantValue> result;
for (int i = 0; i < values.length; i++) {
@ -722,6 +729,8 @@ class TypeConverter extends DartTypeVisitor<DartType, Null> {
DartType convert(DartType type) => type.accept(this, null);
List<DartType> convertTypes(List<DartType> types) => _visitList(types);
DartType visitVoidType(VoidType type, _) => type;
DartType visitDynamicType(DynamicType type, _) => type;

View file

@ -699,9 +699,9 @@ class Constantifier extends ir.ExpressionVisitor<ConstantExpression> {
@override
ConstantExpression visitInstantiation(ir.Instantiation node) {
// TODO(sigmund, sra): add a constant representation for instantiations.
// See issue 32774.
return visit(node.expression);
return new InstantiationConstantExpression(
node.typeArguments.map(elementMap.getDartType).toList(),
visit(node.expression));
}
@override

View file

@ -83,6 +83,12 @@ class ConstantValueTypeMasks
return closedWorld.commonMasks.functionType;
}
@override
TypeMask visitInstantiation(
InstantiationConstantValue constant, ClosedWorld closedWorld) {
return closedWorld.commonMasks.functionType;
}
@override
TypeMask visitInt(IntConstantValue constant, ClosedWorld closedWorld) {
if (constant.isUInt31()) return closedWorld.commonMasks.uint31Type;

View file

@ -951,6 +951,15 @@ class ConstantEquivalence
strategy.testConstants(
exp1, exp2, 'message', exp1.message, exp2.message);
}
@override
bool visitInstantiation(InstantiationConstantExpression exp1,
covariant InstantiationConstantExpression exp2) {
return strategy.testTypeLists(exp1, exp2, 'typeArguments',
exp1.typeArguments, exp2.typeArguments) &&
strategy.testConstants(
exp1, exp2, 'expression', exp1.expression, exp2.expression);
}
}
/// Visitor that checks for structural equivalence of [ConstantValue]s.
@ -1086,6 +1095,15 @@ class ConstantValueEquivalence
covariant InterceptorConstantValue value2) {
return strategy.testElements(value1, value2, 'cls', value1.cls, value2.cls);
}
@override
bool visitInstantiation(InstantiationConstantValue value1,
covariant InstantiationConstantValue value2) {
return strategy.testTypeLists(value1, value2, 'typeArguments',
value1.typeArguments, value2.typeArguments) &&
strategy.testConstantValues(
value1, value2, 'function', value1.function, value2.function);
}
}
/// Tests the equivalence of [impact1] and [impact2] using [strategy].

View file

@ -34,7 +34,10 @@ class TestData {
/// Tested constants.
final List<ConstantData> constants;
const TestData(this.name, this.declarations, this.constants);
final bool strongModeOnly;
const TestData(this.name, this.declarations, this.constants,
{this.strongModeOnly: false});
}
class ConstantData {
@ -46,12 +49,17 @@ class ConstantData {
/// a [ConstantResult].
final expectedResults;
/// Expected results for Dart 2 if different from [expectedResults].
final strongModeResults;
/// A [MessageKind] or a list of [MessageKind]s containing the error messages
/// expected as the result of evaluating the constant under the empty
/// environment.
final expectedErrors;
const ConstantData(this.code, this.expectedResults, [this.expectedErrors]);
const ConstantData(this.code, this.expectedResults,
{this.expectedErrors, strongModeResults})
: this.strongModeResults = strongModeResults ?? expectedResults;
}
class EvaluationError {
@ -76,6 +84,10 @@ class MemoryEnvironment implements EvaluationEnvironment {
return _environment.substByContext(base, target);
}
@override
DartType getTypeInContext(DartType type) =>
_environment.getTypeInContext(type);
@override
ConstantConstructor getConstructorConstant(ConstructorEntity constructor) {
return _environment.getConstructorConstant(constructor);
@ -152,21 +164,25 @@ const List<TestData> DATA = const [
const ConstantData('Object', 'TypeConstant(Object)'),
const ConstantData('null ?? 0', 'IntConstant(0)'),
const ConstantData(
'const [0, 1]', 'ListConstant([IntConstant(0), IntConstant(1)])'),
'const [0, 1]', 'ListConstant([IntConstant(0), IntConstant(1)])',
strongModeResults:
'ListConstant(<int>[IntConstant(0), IntConstant(1)])'),
const ConstantData('const <int>[0, 1]',
'ListConstant(<int>[IntConstant(0), IntConstant(1)])'),
const ConstantData(
'const {0: 1, 2: 3}',
'MapConstant({IntConstant(0): IntConstant(1), '
'IntConstant(2): IntConstant(3)})'),
'IntConstant(2): IntConstant(3)})',
strongModeResults:
'MapConstant(<int, int>{IntConstant(0): IntConstant(1), '
'IntConstant(2): IntConstant(3)})'),
const ConstantData(
'const <int, int>{0: 1, 2: 3}',
'MapConstant(<int, int>{IntConstant(0): IntConstant(1), '
'IntConstant(2): IntConstant(3)})'),
const ConstantData(
'const <int, int>{0: 1, 0: 2}',
const ConstantData('const <int, int>{0: 1, 0: 2}',
'MapConstant(<int, int>{IntConstant(0): IntConstant(2)})',
MessageKind.EQUAL_MAP_ENTRY_KEY),
expectedErrors: MessageKind.EQUAL_MAP_ENTRY_KEY),
const ConstantData(
'const bool.fromEnvironment("foo", defaultValue: false)', const {
const {}: 'BoolConstant(false)',
@ -355,133 +371,137 @@ class B extends A {
const ConstantData(
r'"$integer $string $boolean"', 'StringConstant("5 baz false")'),
const ConstantData('0 ? true : false', 'NonConstant',
MessageKind.INVALID_CONSTANT_CONDITIONAL_TYPE),
expectedErrors: MessageKind.INVALID_CONSTANT_CONDITIONAL_TYPE),
const ConstantData('integer ? true : false', 'NonConstant',
MessageKind.INVALID_CONSTANT_CONDITIONAL_TYPE),
expectedErrors: MessageKind.INVALID_CONSTANT_CONDITIONAL_TYPE),
const ConstantData(r'"${const []}"', 'NonConstant',
MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE),
expectedErrors: MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE),
const ConstantData(r'"${proxy}"', 'NonConstant',
MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE),
const ConstantData(r'"${proxy}${const []}"', 'NonConstant', const [
MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE,
MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE
]),
const ConstantData(r'"${"${proxy}"}${const []}"', 'NonConstant', const [
MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE,
MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE
]),
const ConstantData(
'0 + ""', 'NonConstant', MessageKind.INVALID_CONSTANT_NUM_ADD_TYPE),
const ConstantData(
'0 + string', 'NonConstant', MessageKind.INVALID_CONSTANT_NUM_ADD_TYPE),
const ConstantData(
'"" + 0', 'NonConstant', MessageKind.INVALID_CONSTANT_STRING_ADD_TYPE),
expectedErrors: MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE),
const ConstantData(r'"${proxy}${const []}"', 'NonConstant',
expectedErrors: const [
MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE,
MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE
]),
const ConstantData(r'"${"${proxy}"}${const []}"', 'NonConstant',
expectedErrors: const [
MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE,
MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE
]),
const ConstantData('0 + ""', 'NonConstant',
expectedErrors: MessageKind.INVALID_CONSTANT_NUM_ADD_TYPE),
const ConstantData('0 + string', 'NonConstant',
expectedErrors: MessageKind.INVALID_CONSTANT_NUM_ADD_TYPE),
const ConstantData('"" + 0', 'NonConstant',
expectedErrors: MessageKind.INVALID_CONSTANT_STRING_ADD_TYPE),
const ConstantData('string + 0', 'NonConstant',
MessageKind.INVALID_CONSTANT_STRING_ADD_TYPE),
expectedErrors: MessageKind.INVALID_CONSTANT_STRING_ADD_TYPE),
const ConstantData('true + ""', 'NonConstant',
MessageKind.INVALID_CONSTANT_STRING_ADD_TYPE),
expectedErrors: MessageKind.INVALID_CONSTANT_STRING_ADD_TYPE),
const ConstantData('boolean + string', 'NonConstant',
MessageKind.INVALID_CONSTANT_STRING_ADD_TYPE),
const ConstantData(
'true + false', 'NonConstant', MessageKind.INVALID_CONSTANT_ADD_TYPES),
expectedErrors: MessageKind.INVALID_CONSTANT_STRING_ADD_TYPE),
const ConstantData('true + false', 'NonConstant',
expectedErrors: MessageKind.INVALID_CONSTANT_ADD_TYPES),
const ConstantData('boolean + false', 'NonConstant',
MessageKind.INVALID_CONSTANT_ADD_TYPES),
expectedErrors: MessageKind.INVALID_CONSTANT_ADD_TYPES),
const ConstantData('const [] == null', 'NonConstant',
MessageKind.INVALID_CONSTANT_BINARY_PRIMITIVE_TYPE),
expectedErrors: MessageKind.INVALID_CONSTANT_BINARY_PRIMITIVE_TYPE),
const ConstantData('proxy == null', 'NonConstant',
MessageKind.INVALID_CONSTANT_BINARY_PRIMITIVE_TYPE),
const ConstantData(
'0 * ""', 'NonConstant', MessageKind.INVALID_CONSTANT_BINARY_NUM_TYPE),
expectedErrors: MessageKind.INVALID_CONSTANT_BINARY_PRIMITIVE_TYPE),
const ConstantData('0 * ""', 'NonConstant',
expectedErrors: MessageKind.INVALID_CONSTANT_BINARY_NUM_TYPE),
const ConstantData('0 * string', 'NonConstant',
MessageKind.INVALID_CONSTANT_BINARY_NUM_TYPE),
const ConstantData(
'0 % ""', 'NonConstant', MessageKind.INVALID_CONSTANT_BINARY_NUM_TYPE),
expectedErrors: MessageKind.INVALID_CONSTANT_BINARY_NUM_TYPE),
const ConstantData('0 % ""', 'NonConstant',
expectedErrors: MessageKind.INVALID_CONSTANT_BINARY_NUM_TYPE),
const ConstantData('0 % string', 'NonConstant',
MessageKind.INVALID_CONSTANT_BINARY_NUM_TYPE),
const ConstantData(
'0 << ""', 'NonConstant', MessageKind.INVALID_CONSTANT_BINARY_INT_TYPE),
expectedErrors: MessageKind.INVALID_CONSTANT_BINARY_NUM_TYPE),
const ConstantData('0 << ""', 'NonConstant',
expectedErrors: MessageKind.INVALID_CONSTANT_BINARY_INT_TYPE),
const ConstantData('0 << string', 'NonConstant',
MessageKind.INVALID_CONSTANT_BINARY_INT_TYPE),
const ConstantData(
'null[0]', 'NonConstant', MessageKind.INVALID_CONSTANT_INDEX),
expectedErrors: MessageKind.INVALID_CONSTANT_BINARY_INT_TYPE),
const ConstantData('null[0]', 'NonConstant',
expectedErrors: MessageKind.INVALID_CONSTANT_INDEX),
const ConstantData('const bool.fromEnvironment(0)', 'NonConstant',
MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE),
expectedErrors: MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE),
const ConstantData('const bool.fromEnvironment(integer)', 'NonConstant',
MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE),
expectedErrors: MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE),
const ConstantData(
'const bool.fromEnvironment("baz", defaultValue: 0)',
'NonConstant',
MessageKind.INVALID_BOOL_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE),
'const bool.fromEnvironment("baz", defaultValue: 0)', 'NonConstant',
expectedErrors:
MessageKind.INVALID_BOOL_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE),
const ConstantData(
'const bool.fromEnvironment("baz", defaultValue: integer)',
'NonConstant',
MessageKind.INVALID_BOOL_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE),
expectedErrors:
MessageKind.INVALID_BOOL_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE),
const ConstantData('const int.fromEnvironment(0)', 'NonConstant',
MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE),
expectedErrors: MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE),
const ConstantData('const int.fromEnvironment(integer)', 'NonConstant',
MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE),
expectedErrors: MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE),
const ConstantData(
'const int.fromEnvironment("baz", defaultValue: "")',
'NonConstant',
MessageKind.INVALID_INT_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE),
'const int.fromEnvironment("baz", defaultValue: "")', 'NonConstant',
expectedErrors:
MessageKind.INVALID_INT_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE),
const ConstantData(
'const int.fromEnvironment("baz", defaultValue: string)',
'NonConstant',
MessageKind.INVALID_INT_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE),
'const int.fromEnvironment("baz", defaultValue: string)', 'NonConstant',
expectedErrors:
MessageKind.INVALID_INT_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE),
const ConstantData('const String.fromEnvironment(0)', 'NonConstant',
MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE),
expectedErrors: MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE),
const ConstantData('const String.fromEnvironment(integer)', 'NonConstant',
MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE),
expectedErrors: MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE),
const ConstantData(
'const String.fromEnvironment("baz", defaultValue: 0)',
'NonConstant',
MessageKind.INVALID_STRING_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE),
'const String.fromEnvironment("baz", defaultValue: 0)', 'NonConstant',
expectedErrors:
MessageKind.INVALID_STRING_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE),
const ConstantData(
'const String.fromEnvironment("baz", defaultValue: integer)',
'NonConstant',
MessageKind.INVALID_STRING_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE),
expectedErrors:
MessageKind.INVALID_STRING_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE),
const ConstantData('true || 0', 'NonConstant',
MessageKind.INVALID_LOGICAL_OR_OPERAND_TYPE),
expectedErrors: MessageKind.INVALID_LOGICAL_OR_OPERAND_TYPE),
const ConstantData('0 || true', 'NonConstant',
MessageKind.INVALID_LOGICAL_OR_OPERAND_TYPE),
expectedErrors: MessageKind.INVALID_LOGICAL_OR_OPERAND_TYPE),
const ConstantData('true || integer', 'NonConstant',
MessageKind.INVALID_LOGICAL_OR_OPERAND_TYPE),
expectedErrors: MessageKind.INVALID_LOGICAL_OR_OPERAND_TYPE),
const ConstantData('integer || true', 'NonConstant',
MessageKind.INVALID_LOGICAL_OR_OPERAND_TYPE),
expectedErrors: MessageKind.INVALID_LOGICAL_OR_OPERAND_TYPE),
const ConstantData('true && 0', 'NonConstant',
MessageKind.INVALID_LOGICAL_AND_OPERAND_TYPE),
expectedErrors: MessageKind.INVALID_LOGICAL_AND_OPERAND_TYPE),
const ConstantData('0 && true', 'NonConstant',
MessageKind.INVALID_LOGICAL_AND_OPERAND_TYPE),
expectedErrors: MessageKind.INVALID_LOGICAL_AND_OPERAND_TYPE),
const ConstantData('integer && true', 'NonConstant',
MessageKind.INVALID_LOGICAL_AND_OPERAND_TYPE),
const ConstantData(
'!0', 'NonConstant', MessageKind.INVALID_CONSTANT_NOT_TYPE),
const ConstantData(
'!string', 'NonConstant', MessageKind.INVALID_CONSTANT_NOT_TYPE),
const ConstantData(
'-("")', 'NonConstant', MessageKind.INVALID_CONSTANT_NEGATE_TYPE),
const ConstantData(
'-(string)', 'NonConstant', MessageKind.INVALID_CONSTANT_NEGATE_TYPE),
expectedErrors: MessageKind.INVALID_LOGICAL_AND_OPERAND_TYPE),
const ConstantData('!0', 'NonConstant',
expectedErrors: MessageKind.INVALID_CONSTANT_NOT_TYPE),
const ConstantData('!string', 'NonConstant',
expectedErrors: MessageKind.INVALID_CONSTANT_NOT_TYPE),
const ConstantData('-("")', 'NonConstant',
expectedErrors: MessageKind.INVALID_CONSTANT_NEGATE_TYPE),
const ConstantData('-(string)', 'NonConstant',
expectedErrors: MessageKind.INVALID_CONSTANT_NEGATE_TYPE),
const ConstantData('not_string.length', 'NonConstant',
MessageKind.INVALID_CONSTANT_STRING_LENGTH_TYPE),
expectedErrors: MessageKind.INVALID_CONSTANT_STRING_LENGTH_TYPE),
const ConstantData('const Class1()', 'NonConstant',
MessageKind.INVALID_CONSTANT_STRING_LENGTH_TYPE),
expectedErrors: MessageKind.INVALID_CONSTANT_STRING_LENGTH_TYPE),
const ConstantData('getter', 'NonConstant'),
const ConstantData(
'const Class2()', 'NonConstant', MessageKind.INVALID_ASSERT_VALUE),
const ConstantData('const Class2()', 'NonConstant',
expectedErrors: MessageKind.INVALID_ASSERT_VALUE),
const ConstantData('const Class2.redirect()', 'NonConstant',
MessageKind.INVALID_ASSERT_VALUE),
expectedErrors: MessageKind.INVALID_ASSERT_VALUE),
const ConstantData('const Class3()', 'NonConstant',
MessageKind.INVALID_ASSERT_VALUE_MESSAGE),
const ConstantData(
'const Class3.fact()', 'NonConstant', MessageKind.INVALID_ASSERT_VALUE),
const ConstantData(
'const Class4()', 'NonConstant', MessageKind.INVALID_ASSERT_VALUE),
expectedErrors: MessageKind.INVALID_ASSERT_VALUE_MESSAGE),
const ConstantData('const Class3.fact()', 'NonConstant',
expectedErrors: MessageKind.INVALID_ASSERT_VALUE),
const ConstantData('const Class4()', 'NonConstant',
expectedErrors: MessageKind.INVALID_ASSERT_VALUE),
const ConstantData('const Class5(0)', 'NonConstant',
MessageKind.INVALID_ASSERT_VALUE_MESSAGE),
expectedErrors: MessageKind.INVALID_ASSERT_VALUE_MESSAGE),
const ConstantData('const Class5(1)', 'ConstructedConstant(Class5())'),
const ConstantData('const Class6(1)', 'NonConstant',
MessageKind.INVALID_ASSERT_VALUE_MESSAGE),
expectedErrors: MessageKind.INVALID_ASSERT_VALUE_MESSAGE),
const ConstantData('const Class6(2)', 'ConstructedConstant(Class6())'),
]),
const TestData('assert', '''
@ -505,6 +525,31 @@ class B extends A {
const ConstantData(r'const D(0)',
'ConstructedConstant(D(a=IntConstant(1),b=IntConstant(2)))'),
]),
const TestData(
'instantiations',
'''
T identity<T>(T t) => t;
class C<T> {
final T defaultValue;
final T Function(T t) identityFunction;
const C(this.defaultValue, this.identityFunction);
}
''',
const <ConstantData>[
const ConstantData('identity', 'FunctionConstant(identity)'),
const ConstantData(
'const C<int>(0, identity)',
'ConstructedConstant(C<int>(defaultValue=IntConstant(0),'
'identityFunction=InstantiationConstant([int],'
'FunctionConstant(identity))))'),
const ConstantData(
'const C<double>(0.5, identity)',
'ConstructedConstant(C<double>(defaultValue=DoubleConstant(0.5),'
'identityFunction=InstantiationConstant([double],'
'FunctionConstant(identity))))'),
],
strongModeOnly: true)
];
main(List<String> args) {
@ -518,14 +563,20 @@ main(List<String> args) {
Future testData(TestData data) async {
StringBuffer sb = new StringBuffer();
sb.write('${data.declarations}\n');
sb.writeln('${data.declarations}');
Map<String, ConstantData> constants = {};
List<String> names = <String>[];
data.constants.forEach((ConstantData constantData) {
String name = 'c${constants.length}';
sb.write('const $name = ${constantData.code};\n');
names.add(name);
sb.writeln('const $name = ${constantData.code};');
constants[name] = constantData;
});
sb.write('main() {}\n');
sb.writeln('main() {');
for (String name in names) {
sb.writeln(' print($name);');
}
sb.writeln('}');
String source = sb.toString();
print("--source '${data.name}'---------------------------------------------");
print(source);
@ -533,7 +584,8 @@ Future testData(TestData data) async {
Future runTest(
List<String> options,
EvaluationEnvironment getEnvironment(
Compiler compiler, FieldEntity field)) async {
Compiler compiler, FieldEntity field),
{bool strongMode: false}) async {
CompilationResult result = await runCompiler(
memorySourceFiles: {'main.dart': source}, options: options);
Compiler compiler = result.compiler;
@ -546,7 +598,8 @@ Future testData(TestData data) async {
ConstantExpression constant =
elementEnvironment.getFieldConstant(field);
var expectedResults = data.expectedResults;
var expectedResults =
strongMode ? data.strongModeResults : data.expectedResults;
if (expectedResults is String) {
expectedResults = {const <String, String>{}: expectedResults};
}
@ -606,23 +659,42 @@ Future testData(TestData data) async {
'redirect',
];
if (!skipAstList.contains(data.name)) {
const skipStrongList = const [
// TODO(johnniwinther): Investigate why some types of the constructed
// objects don't match.
'redirect',
// TODO(johnniwinther): Investigate why different errors are reported in
// strong mode.
'errors',
];
if (!skipAstList.contains(data.name) && !data.strongModeOnly) {
print(
'--test ast----------------------------------------------------------');
await runTest(
[Flags.useOldFrontend, Flags.analyzeAll],
[Flags.useOldFrontend],
(Compiler compiler, FieldEntity field) => new AstEvaluationEnvironment(
compiler,
constantRequired: field.isConst));
}
if (!skipKernelList.contains(data.name)) {
if (!skipKernelList.contains(data.name) && !data.strongModeOnly) {
print(
'--test kernel-------------------------------------------------------');
await runTest([Flags.analyzeOnly], (Compiler compiler, FieldEntity field) {
await runTest([], (Compiler compiler, FieldEntity field) {
KernelFrontEndStrategy frontendStrategy = compiler.frontendStrategy;
KernelToElementMap elementMap = frontendStrategy.elementMap;
return new KernelEvaluationEnvironment(elementMap, null, field,
constantRequired: field.isConst);
});
}
if (!skipStrongList.contains(data.name)) {
print(
'--test kernel (strong mode)-----------------------------------------');
await runTest([Flags.strongMode], (Compiler compiler, FieldEntity field) {
KernelFrontEndStrategy frontendStrategy = compiler.frontendStrategy;
KernelToElementMap elementMap = frontendStrategy.elementMap;
return new KernelEvaluationEnvironment(elementMap, null, field,
constantRequired: field.isConst);
}, strongMode: true);
}
}

View file

@ -7,8 +7,9 @@ library constant_expression_test;
import 'dart:async';
import 'package:async_helper/async_helper.dart';
import 'package:expect/expect.dart';
import 'package:compiler/src/constants/expressions.dart';
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/constants/expressions.dart';
import 'package:compiler/src/kernel/element_map_impl.dart';
import 'package:compiler/src/elements/entities.dart';
import '../memory_compiler.dart';
@ -21,7 +22,10 @@ class TestData {
/// Tested constants.
final List<ConstantData> constants;
const TestData(this.declarations, this.constants);
final bool strongModeOnly;
const TestData(this.declarations, this.constants,
{this.strongModeOnly: false});
}
class ConstantData {
@ -34,6 +38,9 @@ class ConstantData {
/// ConstantExpression.getText() result if different from [code].
final String text;
/// ConstantExpression.getText() result if different from [code].
final String strongText;
/// The expected instance type for ConstructedConstantExpression.
final String type;
@ -41,9 +48,10 @@ class ConstantData {
final Map<String, String> fields;
const ConstantData(String code, this.kind,
{String text, this.type, this.fields})
{String text, String strongText, this.type, this.fields})
: this.code = code,
this.text = text != null ? text : code;
this.text = text ?? code,
this.strongText = strongText ?? text ?? code;
}
const List<TestData> DATA = const [
@ -56,7 +64,6 @@ const List<TestData> DATA = const [
const ConstantData('"foo"', ConstantExpressionKind.STRING),
const ConstantData('1 + 2', ConstantExpressionKind.BINARY),
const ConstantData('1 == 2', ConstantExpressionKind.BINARY),
// TODO(sigmund): reenable (Issue 32511)
const ConstantData('1 != 2', ConstantExpressionKind.UNARY,
// a != b is encoded as !(a == b) by CFE.
text: '!(1 == 2)'),
@ -71,9 +78,11 @@ const List<TestData> DATA = const [
const ConstantData('proxy', ConstantExpressionKind.FIELD),
const ConstantData('Object', ConstantExpressionKind.TYPE),
const ConstantData('#name', ConstantExpressionKind.SYMBOL),
const ConstantData('const [0, 1]', ConstantExpressionKind.LIST),
const ConstantData('const [0, 1]', ConstantExpressionKind.LIST,
strongText: 'const <int>[0, 1]'),
const ConstantData('const <int>[0, 1]', ConstantExpressionKind.LIST),
const ConstantData('const {0: 1, 2: 3}', ConstantExpressionKind.MAP),
const ConstantData('const {0: 1, 2: 3}', ConstantExpressionKind.MAP,
strongText: 'const <int, int>{0: 1, 2: 3}'),
const ConstantData(
'const <int, int>{0: 1, 2: 3}', ConstantExpressionKind.MAP),
const ConstantData('const bool.fromEnvironment("foo", defaultValue: false)',
@ -157,7 +166,6 @@ class C<U> {
const ConstantData(
'const A<int>(field1: 87)', ConstantExpressionKind.CONSTRUCTED,
type: 'A<int>', fields: const {'field(A#field1)': '87'}),
// TODO(sigmund): reenable (Issue 32511)
const ConstantData('const B()', ConstantExpressionKind.CONSTRUCTED,
type: 'A<B<dynamic>>',
fields: const {
@ -197,25 +205,63 @@ class C<U> {
// Redirecting factories are replaced by their effective targets by CFE.
text: 'const A<int>()'),
]),
const TestData('''
T identity<T>(T t) => t;
class C<T> {
final T defaultValue;
final T Function(T t) identityFunction;
const C(this.defaultValue, this.identityFunction);
}
''', const <ConstantData>[
const ConstantData('identity', ConstantExpressionKind.FUNCTION),
const ConstantData(
'const C<int>(0, identity)', ConstantExpressionKind.CONSTRUCTED,
type: 'C<int>', strongText: 'const C<int>(0, <int>(identity))'),
const ConstantData(
'const C<double>(0.5, identity)', ConstantExpressionKind.CONSTRUCTED,
type: 'C<double>',
strongText: 'const C<double>(0.5, <double>(identity))'),
], strongModeOnly: true)
];
main() {
asyncTest(() => Future.forEach(DATA, testData));
asyncTest(() async {
print('--test from kernel------------------------------------------------');
await runTest(strongMode: false);
print('--test from kernel (strong)---------------------------------------');
await runTest(strongMode: true);
});
}
Future testData(TestData data) async {
Future runTest({bool strongMode}) async {
for (TestData data in DATA) {
await testData(data, strongMode: strongMode);
}
}
Future testData(TestData data, {bool strongMode}) async {
if (data.strongModeOnly && !strongMode) return;
StringBuffer sb = new StringBuffer();
sb.write('${data.declarations}\n');
sb.writeln('${data.declarations}');
Map<String, ConstantData> constants = {};
List<String> names = <String>[];
data.constants.forEach((ConstantData constantData) {
String name = 'c${constants.length}';
sb.write('const $name = ${constantData.code};\n');
names.add(name);
sb.writeln('const $name = ${constantData.code};');
constants[name] = constantData;
});
sb.write('main() {}\n');
sb.writeln('main() {');
for (String name in names) {
sb.writeln(' print($name);');
}
sb.writeln('}');
String source = sb.toString();
CompilationResult result = await runCompiler(
memorySourceFiles: {'main.dart': source}, options: ['--analyze-all']);
memorySourceFiles: {'main.dart': source},
options: strongMode ? [Flags.strongMode] : []);
Compiler compiler = result.compiler;
var elementEnvironment = compiler.frontendStrategy.elementEnvironment;
@ -233,11 +279,12 @@ Future testData(TestData data) async {
constant.kind,
"Unexpected kind '${constant.kind}' for constant "
"`${constant.toDartText()}`, expected '${data.kind}'.");
String text = strongMode ? data.strongText : data.text;
Expect.equals(
data.text,
text,
constant.toDartText(),
"Unexpected text '${constant.toDartText()}' for constant, "
"expected '${data.text}'.");
"expected '${text}'.");
if (data.type != null) {
String instanceType =
constant.computeInstanceType(environment).toString();