| // 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); |
| |
| ConstantConstructorKind get kind => ConstantConstructorKind.GENERATIVE; |
| |
| InterfaceType computeInstanceType( |
| EvaluationEnvironment environment, InterfaceType newType) { |
| return environment.substByContext(type, newType); |
| } |
| |
| 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; |
| } |
| |
| accept(ConstantConstructorVisitor visitor, arg) { |
| return visitor.visitGenerative(this, arg); |
| } |
| |
| int get hashCode { |
| int hash = Hashing.objectHash(type); |
| hash = Hashing.mapHash(defaultValues, hash); |
| hash = Hashing.mapHash(fieldMap, hash); |
| return Hashing.objectHash(superConstructorInvocation, hash); |
| } |
| |
| 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); |
| } |
| |
| 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); |
| |
| ConstantConstructorKind get kind { |
| return ConstantConstructorKind.REDIRECTING_GENERATIVE; |
| } |
| |
| InterfaceType computeInstanceType( |
| EvaluationEnvironment environment, InterfaceType newType) { |
| return environment.substByContext( |
| thisConstructorInvocation.computeInstanceType(environment), newType); |
| } |
| |
| 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; |
| } |
| |
| accept(ConstantConstructorVisitor visitor, arg) { |
| return visitor.visitRedirectingGenerative(this, arg); |
| } |
| |
| int get hashCode { |
| int hash = Hashing.objectHash(thisConstructorInvocation); |
| return Hashing.mapHash(defaultValues, hash); |
| } |
| |
| bool operator ==(other) { |
| if (identical(this, other)) return true; |
| if (other is! RedirectingGenerativeConstantConstructor) return false; |
| return thisConstructorInvocation == other.thisConstructorInvocation && |
| GenerativeConstantConstructor.mapEquals( |
| defaultValues, other.defaultValues); |
| } |
| |
| 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); |
| |
| ConstantConstructorKind get kind { |
| return ConstantConstructorKind.REDIRECTING_FACTORY; |
| } |
| |
| InterfaceType computeInstanceType( |
| EvaluationEnvironment environment, InterfaceType newType) { |
| return environment.substByContext( |
| targetConstructorInvocation.computeInstanceType(environment), newType); |
| } |
| |
| InstanceData computeInstanceData(EvaluationEnvironment environment, |
| List<ConstantExpression> arguments, CallStructure callStructure) { |
| ConstantConstructor constantConstructor = |
| environment.getConstructorConstant(targetConstructorInvocation.target); |
| return constantConstructor.computeInstanceData( |
| environment, arguments, callStructure); |
| } |
| |
| accept(ConstantConstructorVisitor visitor, arg) { |
| return visitor.visitRedirectingFactory(this, arg); |
| } |
| |
| int get hashCode { |
| return Hashing.objectHash(targetConstructorInvocation); |
| } |
| |
| bool operator ==(other) { |
| if (identical(this, other)) return true; |
| if (other is! RedirectingFactoryConstantConstructor) return false; |
| return targetConstructorInvocation == other.targetConstructorInvocation; |
| } |
| |
| 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'); |
| } |
| } |