blob: 3b9fb60c852591c991d47c3dd22017ee3df6cb35 [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.constructors;
import '../elements/entities.dart' show FieldEntity;
import '../elements/types.dart';
import '../universe/call_structure.dart' show CallStructure;
import '../util/util.dart';
import 'evaluation.dart';
import 'expressions.dart';
enum ConstantConstructorKind {
GENERATIVE,
REDIRECTING_GENERATIVE,
REDIRECTING_FACTORY,
ERRONEOUS,
}
class InstanceData {
final Map<FieldEntity, ConstantExpression> fieldMap;
final List<AssertConstantExpression> assertions;
InstanceData(this.fieldMap, this.assertions);
}
/// Definition of a constant constructor.
abstract class ConstantConstructor {
ConstantConstructorKind get kind;
/// Computes the type of the instance created in a const constructor
/// invocation with type [newType].
InterfaceType computeInstanceType(
EvaluationEnvironment environment, InterfaceType newType);
/// Computes the constant expressions of the fields and initializer assertions
/// of the created instance in a const constructor invocation with
/// [arguments].
InstanceData computeInstanceData(EvaluationEnvironment environment,
List<ConstantExpression> arguments, CallStructure callStructure);
accept(ConstantConstructorVisitor visitor, arg);
}
abstract class ConstantConstructorVisitor<R, A> {
const ConstantConstructorVisitor();
R visit(
covariant ConstantConstructor constantConstructor, covariant A context) {
return constantConstructor.accept(this, context);
}
R visitGenerative(
covariant GenerativeConstantConstructor constructor, covariant A arg);
R visitRedirectingGenerative(
covariant RedirectingGenerativeConstantConstructor constructor,
covariant A arg);
R visitRedirectingFactory(
covariant RedirectingFactoryConstantConstructor constructor,
covariant A arg);
R visitErroneous(
covariant ErroneousConstantConstructor constructor, covariant A arg);
}
/// A generative constant constructor.
class GenerativeConstantConstructor implements ConstantConstructor {
final InterfaceType type;
final Map<dynamic /*int|String*/, ConstantExpression> defaultValues;
final Map<FieldEntity, ConstantExpression> fieldMap;
final List<AssertConstantExpression> assertions;
final ConstructedConstantExpression superConstructorInvocation;
GenerativeConstantConstructor(this.type, this.defaultValues, this.fieldMap,
this.assertions, this.superConstructorInvocation);
@override
ConstantConstructorKind get kind => ConstantConstructorKind.GENERATIVE;
@override
InterfaceType computeInstanceType(
EvaluationEnvironment environment, InterfaceType newType) {
return environment.substByContext(type, newType);
}
@override
InstanceData computeInstanceData(EvaluationEnvironment environment,
List<ConstantExpression> arguments, CallStructure callStructure) {
NormalizedArguments args =
new NormalizedArguments(defaultValues, callStructure, arguments);
InstanceData appliedInstanceData =
applyInstanceData(environment, args, superConstructorInvocation);
fieldMap.forEach((FieldEntity field, ConstantExpression constant) {
appliedInstanceData.fieldMap[field] = constant.apply(args);
});
for (AssertConstantExpression assertion in assertions) {
appliedInstanceData.assertions.add(assertion.apply(args));
}
return appliedInstanceData;
}
@override
accept(ConstantConstructorVisitor visitor, arg) {
return visitor.visitGenerative(this, arg);
}
@override
int get hashCode {
int hash = Hashing.objectHash(type);
hash = Hashing.mapHash(defaultValues, hash);
hash = Hashing.mapHash(fieldMap, hash);
return Hashing.objectHash(superConstructorInvocation, hash);
}
@override
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! GenerativeConstantConstructor) return false;
return type == other.type &&
superConstructorInvocation == other.superConstructorInvocation &&
mapEquals(defaultValues, other.defaultValues) &&
mapEquals(fieldMap, other.fieldMap);
}
@override
String toString() {
StringBuffer sb = new StringBuffer();
sb.write("{'type': $type");
defaultValues.forEach((key, ConstantExpression expression) {
sb.write(",\n 'default:${key}': ${expression.toDartText()}");
});
fieldMap.forEach((FieldEntity field, ConstantExpression expression) {
sb.write(",\n 'field:${field}': ${expression.toDartText()}");
});
if (superConstructorInvocation != null) {
sb.write(",\n 'constructor: ${superConstructorInvocation.toDartText()}");
}
sb.write("}");
return sb.toString();
}
static bool mapEquals(Map map1, Map map2) {
if (map1.length != map1.length) return false;
for (var key in map1.keys) {
if (map1[key] != map2[key]) {
return false;
}
}
return true;
}
/// Creates the field-to-constant map from applying [args] to
/// [constructorInvocation]. If [constructorInvocation] is `null`, an empty
/// map is created. Returns `null` if an erroneous constant constructor was
/// encountered in the super-chain.
static InstanceData applyInstanceData(
EvaluationEnvironment environment,
NormalizedArguments args,
ConstructedConstantExpression constructorInvocation) {
Map<FieldEntity, ConstantExpression> appliedFieldMap =
<FieldEntity, ConstantExpression>{};
List<AssertConstantExpression> appliedAssertions =
<AssertConstantExpression>[];
if (constructorInvocation != null) {
InstanceData instanceData =
constructorInvocation.computeInstanceData(environment);
if (instanceData == null) {
// An erroneous constant constructor was encountered in the super-chain.
return null;
}
instanceData.fieldMap
.forEach((FieldEntity field, ConstantExpression constant) {
appliedFieldMap[field] = constant.apply(args);
});
for (AssertConstantExpression assertion in instanceData.assertions) {
appliedAssertions.add(assertion.apply(args));
}
}
return new InstanceData(appliedFieldMap, appliedAssertions);
}
}
/// A redirecting generative constant constructor.
class RedirectingGenerativeConstantConstructor implements ConstantConstructor {
final Map<dynamic /*int|String*/, ConstantExpression> defaultValues;
final ConstructedConstantExpression thisConstructorInvocation;
RedirectingGenerativeConstantConstructor(
this.defaultValues, this.thisConstructorInvocation);
@override
ConstantConstructorKind get kind {
return ConstantConstructorKind.REDIRECTING_GENERATIVE;
}
@override
InterfaceType computeInstanceType(
EvaluationEnvironment environment, InterfaceType newType) {
return environment.substByContext(
thisConstructorInvocation.computeInstanceType(environment), newType);
}
@override
InstanceData computeInstanceData(EvaluationEnvironment environment,
List<ConstantExpression> arguments, CallStructure callStructure) {
NormalizedArguments args =
new NormalizedArguments(defaultValues, callStructure, arguments);
InstanceData appliedInstanceData =
GenerativeConstantConstructor.applyInstanceData(
environment, args, thisConstructorInvocation);
return appliedInstanceData;
}
@override
accept(ConstantConstructorVisitor visitor, arg) {
return visitor.visitRedirectingGenerative(this, arg);
}
@override
int get hashCode {
int hash = Hashing.objectHash(thisConstructorInvocation);
return Hashing.mapHash(defaultValues, hash);
}
@override
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! RedirectingGenerativeConstantConstructor) return false;
return thisConstructorInvocation == other.thisConstructorInvocation &&
GenerativeConstantConstructor.mapEquals(
defaultValues, other.defaultValues);
}
@override
String toString() {
StringBuffer sb = new StringBuffer();
sb.write("{'type': ${thisConstructorInvocation.type}");
defaultValues.forEach((key, ConstantExpression expression) {
sb.write(",\n 'default:${key}': ${expression.toDartText()}");
});
sb.write(",\n 'constructor': ${thisConstructorInvocation.toDartText()}");
sb.write("}");
return sb.toString();
}
}
/// A redirecting factory constant constructor.
class RedirectingFactoryConstantConstructor implements ConstantConstructor {
final ConstructedConstantExpression targetConstructorInvocation;
RedirectingFactoryConstantConstructor(this.targetConstructorInvocation);
@override
ConstantConstructorKind get kind {
return ConstantConstructorKind.REDIRECTING_FACTORY;
}
@override
InterfaceType computeInstanceType(
EvaluationEnvironment environment, InterfaceType newType) {
return environment.substByContext(
targetConstructorInvocation.computeInstanceType(environment), newType);
}
@override
InstanceData computeInstanceData(EvaluationEnvironment environment,
List<ConstantExpression> arguments, CallStructure callStructure) {
ConstantConstructor constantConstructor =
environment.getConstructorConstant(targetConstructorInvocation.target);
return constantConstructor.computeInstanceData(
environment, arguments, callStructure);
}
@override
accept(ConstantConstructorVisitor visitor, arg) {
return visitor.visitRedirectingFactory(this, arg);
}
@override
int get hashCode {
return Hashing.objectHash(targetConstructorInvocation);
}
@override
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! RedirectingFactoryConstantConstructor) return false;
return targetConstructorInvocation == other.targetConstructorInvocation;
}
@override
String toString() {
StringBuffer sb = new StringBuffer();
sb.write("{");
sb.write("'constructor': ${targetConstructorInvocation.toDartText()}");
sb.write("}");
return sb.toString();
}
}
/// A constant constructor with errors.
class ErroneousConstantConstructor implements ConstantConstructor {
@override
ConstantConstructorKind get kind => ConstantConstructorKind.ERRONEOUS;
@override
accept(ConstantConstructorVisitor visitor, arg) {
return visitor.visitErroneous(this, arg);
}
@override
InstanceData computeInstanceData(EvaluationEnvironment environment,
List<ConstantExpression> arguments, CallStructure callStructure) {
return null;
}
@override
InterfaceType computeInstanceType(
EvaluationEnvironment environment, InterfaceType newType) {
throw new UnsupportedError(
'ErroneousConstantConstructor.computeInstanceType');
}
}