| Index: pkg/compiler/lib/src/ssa/type_builder.dart
|
| diff --git a/pkg/compiler/lib/src/ssa/type_builder.dart b/pkg/compiler/lib/src/ssa/type_builder.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..605678bf87046c6e5dfb45e6518843d8f4b449ae
|
| --- /dev/null
|
| +++ b/pkg/compiler/lib/src/ssa/type_builder.dart
|
| @@ -0,0 +1,252 @@
|
| +// Copyright (c) 2016, 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.
|
| +
|
| +import 'graph_builder.dart';
|
| +import 'nodes.dart';
|
| +import '../closure.dart';
|
| +import '../common.dart';
|
| +import '../dart_types.dart';
|
| +import '../types/types.dart';
|
| +import '../elements/elements.dart';
|
| +import '../io/source_information.dart';
|
| +import '../universe/selector.dart' show Selector;
|
| +import '../universe/use.dart' show TypeUse;
|
| +
|
| +/// Functions to insert type checking, coercion, and instruction insertion
|
| +/// depending on the environment for dart code.
|
| +class TypeBuilder {
|
| + final GraphBuilder builder;
|
| + TypeBuilder(this.builder);
|
| +
|
| + /// Create an instruction to simply trust the provided type.
|
| + HInstruction _trustType(HInstruction original, DartType type) {
|
| + assert(builder.compiler.options.trustTypeAnnotations);
|
| + assert(type != null);
|
| + type = builder.localsHandler.substInContext(type);
|
| + type = type.unaliased;
|
| + if (type.isDynamic) return original;
|
| + if (!type.isInterfaceType) return original;
|
| + if (type.isObject) return original;
|
| + // The type element is either a class or the void element.
|
| + Element element = type.element;
|
| + TypeMask mask = new TypeMask.subtype(element, builder.compiler.closedWorld);
|
| + return new HTypeKnown.pinned(mask, original);
|
| + }
|
| +
|
| + /// Produces code that checks the runtime type is actually the type specified
|
| + /// by attempting a type conversion.
|
| + HInstruction _checkType(HInstruction original, DartType type, int kind) {
|
| + assert(builder.compiler.options.enableTypeAssertions);
|
| + assert(type != null);
|
| + type = builder.localsHandler.substInContext(type);
|
| + HInstruction other = buildTypeConversion(original, type, kind);
|
| + // TODO(johnniwinther): This operation on `registry` may be inconsistent.
|
| + // If it is needed then it seems likely that similar invocations of
|
| + // `buildTypeConversion` in `SsaBuilder.visitAs` should also be followed by
|
| + // a similar operation on `registry`; otherwise, this one might not be
|
| + // needed.
|
| + builder.registry?.registerTypeUse(new TypeUse.isCheck(type));
|
| + return other;
|
| + }
|
| +
|
| + /// Depending on the context and the mode, wrap the given type in an
|
| + /// instruction that checks the type is what we expect or automatically
|
| + /// trusts the written type.
|
| + HInstruction potentiallyCheckOrTrustType(HInstruction original, DartType type,
|
| + {int kind: HTypeConversion.CHECKED_MODE_CHECK}) {
|
| + if (type == null) return original;
|
| + HInstruction checkedOrTrusted = original;
|
| + if (builder.compiler.options.trustTypeAnnotations) {
|
| + checkedOrTrusted = _trustType(original, type);
|
| + } else if (builder.compiler.options.enableTypeAssertions) {
|
| + checkedOrTrusted = _checkType(original, type, kind);
|
| + }
|
| + if (checkedOrTrusted == original) return original;
|
| + builder.add(checkedOrTrusted);
|
| + return checkedOrTrusted;
|
| + }
|
| +
|
| + /// Helper to create an instruction that gets the value of a type variable.
|
| + HInstruction addTypeVariableReference(TypeVariableType type, Element member,
|
| + {SourceInformation sourceInformation}) {
|
| + assert(assertTypeInContext(type));
|
| + if (type is MethodTypeVariableType) {
|
| + return builder.graph.addConstantNull(builder.compiler);
|
| + }
|
| + bool isClosure = member.enclosingElement.isClosure;
|
| + if (isClosure) {
|
| + ClosureClassElement closureClass = member.enclosingElement;
|
| + member = closureClass.methodElement;
|
| + member = member.outermostEnclosingMemberOrTopLevel;
|
| + }
|
| + bool isInConstructorContext =
|
| + member.isConstructor || member.isGenerativeConstructorBody;
|
| + Local typeVariableLocal =
|
| + builder.localsHandler.getTypeVariableAsLocal(type);
|
| + if (isClosure) {
|
| + if (member.isFactoryConstructor ||
|
| + (isInConstructorContext &&
|
| + builder.hasDirectLocal(typeVariableLocal))) {
|
| + // The type variable is used from a closure in a factory constructor.
|
| + // The value of the type argument is stored as a local on the closure
|
| + // itself.
|
| + return builder.localsHandler
|
| + .readLocal(typeVariableLocal, sourceInformation: sourceInformation);
|
| + } else if (member.isFunction ||
|
| + member.isGetter ||
|
| + member.isSetter ||
|
| + isInConstructorContext) {
|
| + // The type variable is stored on the "enclosing object" and needs to be
|
| + // accessed using the this-reference in the closure.
|
| + return readTypeVariable(type, member,
|
| + sourceInformation: sourceInformation);
|
| + } else {
|
| + assert(member.isField);
|
| + // The type variable is stored in a parameter of the method.
|
| + return builder.localsHandler.readLocal(typeVariableLocal);
|
| + }
|
| + } else if (isInConstructorContext ||
|
| + // When [member] is a field, we can be either
|
| + // generating a checked setter or inlining its
|
| + // initializer in a constructor. An initializer is
|
| + // never built standalone, so in that case [target] is not
|
| + // the [member] itself.
|
| + (member.isField && member != builder.targetElement)) {
|
| + // The type variable is stored in a parameter of the method.
|
| + return builder.localsHandler
|
| + .readLocal(typeVariableLocal, sourceInformation: sourceInformation);
|
| + } else if (member.isInstanceMember) {
|
| + // The type variable is stored on the object.
|
| + return readTypeVariable(type, member,
|
| + sourceInformation: sourceInformation);
|
| + } else {
|
| + builder.compiler.reporter.internalError(
|
| + type.element, 'Unexpected type variable in static context.');
|
| + return null;
|
| + }
|
| + }
|
| +
|
| + /// Generate code to extract the type argument from the object.
|
| + HInstruction readTypeVariable(TypeVariableType variable, Element member,
|
| + {SourceInformation sourceInformation}) {
|
| + assert(member.isInstanceMember);
|
| + assert(variable is! MethodTypeVariableType);
|
| + HInstruction target = builder.localsHandler.readThis();
|
| + builder.push(
|
| + new HTypeInfoReadVariable(variable, target, builder.backend.dynamicType)
|
| + ..sourceInformation = sourceInformation);
|
| + return builder.pop();
|
| + }
|
| +
|
| + HInstruction buildTypeArgumentRepresentations(
|
| + DartType type, Element sourceElement) {
|
| + assert(!type.isTypeVariable);
|
| + // Compute the representation of the type arguments, including access
|
| + // to the runtime type information for type variables as instructions.
|
| + assert(type.element.isClass);
|
| + InterfaceType interface = type;
|
| + List<HInstruction> inputs = <HInstruction>[];
|
| + for (DartType argument in interface.typeArguments) {
|
| + inputs.add(analyzeTypeArgument(argument, sourceElement));
|
| + }
|
| + HInstruction representation = new HTypeInfoExpression(
|
| + TypeInfoExpressionKind.INSTANCE,
|
| + interface.element.thisType,
|
| + inputs,
|
| + builder.backend.dynamicType);
|
| + return representation;
|
| + }
|
| +
|
| + /// Check that [type] is valid in the context of `localsHandler.contextClass`.
|
| + /// This should only be called in assertions.
|
| + bool assertTypeInContext(DartType type, [Spannable spannable]) {
|
| + return invariant(spannable == null ? CURRENT_ELEMENT_SPANNABLE : spannable,
|
| + () {
|
| + ClassElement contextClass = Types.getClassContext(type);
|
| + return contextClass == null ||
|
| + contextClass == builder.localsHandler.contextClass;
|
| + },
|
| + message: "Type '$type' is not valid context of "
|
| + "${builder.localsHandler.contextClass}.");
|
| + }
|
| +
|
| + HInstruction analyzeTypeArgument(DartType argument, Element sourceElement,
|
| + {SourceInformation sourceInformation}) {
|
| + assert(assertTypeInContext(argument));
|
| + argument = argument.unaliased;
|
| + if (argument.treatAsDynamic) {
|
| + // Represent [dynamic] as [null].
|
| + return builder.graph.addConstantNull(builder.compiler);
|
| + }
|
| +
|
| + if (argument.isTypeVariable) {
|
| + return addTypeVariableReference(argument, sourceElement,
|
| + sourceInformation: sourceInformation);
|
| + }
|
| +
|
| + List<HInstruction> inputs = <HInstruction>[];
|
| + argument.forEachTypeVariable((variable) {
|
| + if (variable is! MethodTypeVariableType) {
|
| + inputs.add(analyzeTypeArgument(variable, sourceElement));
|
| + }
|
| + });
|
| + HInstruction result = new HTypeInfoExpression(
|
| + TypeInfoExpressionKind.COMPLETE,
|
| + argument,
|
| + inputs,
|
| + builder.backend.dynamicType)..sourceInformation = sourceInformation;
|
| + builder.add(result);
|
| + return result;
|
| + }
|
| +
|
| + /// In checked mode, generate type tests for the parameters of the inlined
|
| + /// function.
|
| + void potentiallyCheckInlinedParameterTypes(FunctionElement function) {
|
| + if (!checkOrTrustTypes) return;
|
| +
|
| + FunctionSignature signature = function.functionSignature;
|
| + signature.orderedForEachParameter((ParameterElement parameter) {
|
| + HInstruction argument = builder.localsHandler.readLocal(parameter);
|
| + potentiallyCheckOrTrustType(argument, parameter.type);
|
| + });
|
| + }
|
| +
|
| + bool get checkOrTrustTypes =>
|
| + builder.compiler.options.enableTypeAssertions ||
|
| + builder.compiler.options.trustTypeAnnotations;
|
| +
|
| + /// Build a [HTypeConversion] for converting [original] to type [type].
|
| + ///
|
| + /// Invariant: [type] must be valid in the context.
|
| + /// See [LocalsHandler.substInContext].
|
| + HInstruction buildTypeConversion(
|
| + HInstruction original, DartType type, int kind) {
|
| + if (type == null) return original;
|
| + // GENERIC_METHODS: The following statement was added for parsing and
|
| + // ignoring method type variables; must be generalized for full support of
|
| + // generic methods.
|
| + type = type.dynamifyMethodTypeVariableType;
|
| + type = type.unaliased;
|
| + assert(assertTypeInContext(type, original));
|
| + if (type.isInterfaceType && !type.treatAsRaw) {
|
| + TypeMask subtype =
|
| + new TypeMask.subtype(type.element, builder.compiler.closedWorld);
|
| + HInstruction representations =
|
| + buildTypeArgumentRepresentations(type, builder.sourceElement);
|
| + builder.add(representations);
|
| + return new HTypeConversion.withTypeRepresentation(
|
| + type, kind, subtype, original, representations);
|
| + } else if (type.isTypeVariable) {
|
| + TypeMask subtype = original.instructionType;
|
| + HInstruction typeVariable =
|
| + addTypeVariableReference(type, builder.sourceElement);
|
| + return new HTypeConversion.withTypeRepresentation(
|
| + type, kind, subtype, original, typeVariable);
|
| + } else if (type.isFunctionType) {
|
| + return builder.buildFunctionTypeConversion(original, type, kind);
|
| + } else {
|
| + return original.convertType(builder.compiler, type, kind);
|
| + }
|
| + }
|
| +}
|
|
|