|  | // 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. | 
|  |  | 
|  | library kernel.transformations.mixin_full_resolution; | 
|  |  | 
|  | import '../ast.dart'; | 
|  | import '../class_hierarchy.dart'; | 
|  | import '../clone.dart'; | 
|  | import '../core_types.dart'; | 
|  | import '../reference_from_index.dart'; | 
|  | import '../target/targets.dart' show Target; | 
|  | import '../type_algebra.dart'; | 
|  |  | 
|  | /// Transforms the libraries in [libraries]. | 
|  | /// Note that [referenceFromIndex] can be null, and is generally only needed | 
|  | /// when (ultimately) called from the incremental compiler. | 
|  | void transformLibraries( | 
|  | Target targetInfo, | 
|  | CoreTypes coreTypes, | 
|  | ClassHierarchy hierarchy, | 
|  | List<Library> libraries, | 
|  | ReferenceFromIndex? referenceFromIndex) { | 
|  | new MixinFullResolution(targetInfo, coreTypes, hierarchy) | 
|  | .transform(libraries, referenceFromIndex); | 
|  | } | 
|  |  | 
|  | /// Replaces all mixin applications with regular classes, cloning all fields | 
|  | /// and procedures from the mixed-in class, cloning all constructors from the | 
|  | /// base class. | 
|  | class MixinFullResolution { | 
|  | final Target targetInfo; | 
|  | final CoreTypes coreTypes; | 
|  |  | 
|  | /// The [ClassHierarchy] that should be used after applying this transformer. | 
|  | /// If any class was updated, in general we need to create a new | 
|  | /// [ClassHierarchy] instance, with new dispatch targets; or at least let | 
|  | /// the existing instance know that some of its dispatch tables are not | 
|  | /// valid anymore. | 
|  | ClassHierarchy hierarchy; | 
|  |  | 
|  | MixinFullResolution(this.targetInfo, this.coreTypes, this.hierarchy); | 
|  |  | 
|  | /// Transform the given new [libraries].  It is expected that all other | 
|  | /// libraries have already been transformed. | 
|  | void transform( | 
|  | List<Library> libraries, ReferenceFromIndex? referenceFromIndex) { | 
|  | if (libraries.isEmpty) return; | 
|  |  | 
|  | var transformedClasses = new Set<Class>(); | 
|  |  | 
|  | // Desugar all mixin application classes by copying in fields/methods from | 
|  | // the mixin and constructors from the base class. | 
|  | var processedClasses = new Set<Class>(); | 
|  | for (var library in libraries) { | 
|  | for (var class_ in library.classes) { | 
|  | transformClass(libraries, processedClasses, transformedClasses, class_, | 
|  | referenceFromIndex); | 
|  | } | 
|  | } | 
|  |  | 
|  | // We might need to update the class hierarchy. | 
|  | hierarchy = | 
|  | hierarchy.applyMemberChanges(transformedClasses, findDescendants: true); | 
|  | } | 
|  |  | 
|  | transformClass( | 
|  | List<Library> librariesToBeTransformed, | 
|  | Set<Class> processedClasses, | 
|  | Set<Class> transformedClasses, | 
|  | Class class_, | 
|  | ReferenceFromIndex? referenceFromIndex) { | 
|  | // If this class was already handled then so were all classes up to the | 
|  | // [Object] class. | 
|  | if (!processedClasses.add(class_)) return; | 
|  |  | 
|  | Library enclosingLibrary = class_.enclosingLibrary; | 
|  |  | 
|  | if (!librariesToBeTransformed.contains(enclosingLibrary) && | 
|  | enclosingLibrary.importUri.scheme == "dart") { | 
|  | // If we're not asked to transform the platform libraries then we expect | 
|  | // that they will be already transformed. | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Ensure super classes have been transformed before this class. | 
|  | if (class_.superclass != null) { | 
|  | transformClass(librariesToBeTransformed, processedClasses, | 
|  | transformedClasses, class_.superclass!, referenceFromIndex); | 
|  | } | 
|  |  | 
|  | // If this is not a mixin application we don't need to make forwarding | 
|  | // constructors in this class. | 
|  | if (!class_.isMixinApplication) return; | 
|  | assert(librariesToBeTransformed.contains(enclosingLibrary)); | 
|  |  | 
|  | transformedClasses.add(class_); | 
|  |  | 
|  | // Clone fields and methods from the mixin class. | 
|  | var substitution = getSubstitutionMap(class_.mixedInType!); | 
|  | var cloner = new CloneVisitorWithMembers(typeSubstitution: substitution); | 
|  |  | 
|  | IndexedLibrary? indexedLibrary = | 
|  | referenceFromIndex?.lookupLibrary(enclosingLibrary); | 
|  | IndexedClass? indexedClass = | 
|  | indexedLibrary?.lookupIndexedClass(class_.name); | 
|  |  | 
|  | if (class_.mixin.fields.isNotEmpty) { | 
|  | // When we copy a field from the mixed in class, we remove any | 
|  | // forwarding-stub getters/setters from the superclass, but copy their | 
|  | // covariance-bits onto the new field. | 
|  | var nonSetters = <Name, Procedure>{}; | 
|  | var setters = <Name, Procedure>{}; | 
|  | for (var procedure in class_.procedures) { | 
|  | if (procedure.isSetter) { | 
|  | setters[procedure.name!] = procedure; | 
|  | } else { | 
|  | nonSetters[procedure.name!] = procedure; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (var field in class_.mixin.fields) { | 
|  | Reference? getterReference = | 
|  | indexedClass?.lookupGetterReference(field.name!); | 
|  | Reference? setterReference = | 
|  | indexedClass?.lookupSetterReference(field.name!); | 
|  | if (getterReference == null) { | 
|  | getterReference = nonSetters[field.name]?.reference; | 
|  | getterReference?.canonicalName?.unbind(); | 
|  | } | 
|  | if (setterReference == null) { | 
|  | setterReference = setters[field.name]?.reference; | 
|  | setterReference?.canonicalName?.unbind(); | 
|  | } | 
|  | Field clone = | 
|  | cloner.cloneField(field, getterReference, setterReference); | 
|  | Procedure? setter = setters[field.name!]; | 
|  | if (setter != null) { | 
|  | setters.remove(field.name); | 
|  | VariableDeclaration parameter = | 
|  | setter.function!.positionalParameters.first; | 
|  | clone.isCovariant = parameter.isCovariant; | 
|  | clone.isGenericCovariantImpl = parameter.isGenericCovariantImpl; | 
|  | } | 
|  | nonSetters.remove(field.name); | 
|  | class_.addField(clone); | 
|  | } | 
|  | class_.procedures.clear(); | 
|  | class_.procedures..addAll(nonSetters.values)..addAll(setters.values); | 
|  | } | 
|  |  | 
|  | // Existing procedures in the class should only be forwarding stubs. | 
|  | // Replace them with methods from the mixin class if they have the same | 
|  | // name, but keep their parameter flags. | 
|  | int originalLength = class_.procedures.length; | 
|  | outer: | 
|  | for (var procedure in class_.mixin.procedures) { | 
|  | if (procedure.isSynthetic) continue; | 
|  | // Forwarding stubs in the mixin class are used when calling through the | 
|  | // mixin class's interface, not when calling through the mixin | 
|  | // application.  They should not be copied. | 
|  | if (procedure.isForwardingStub) continue; | 
|  |  | 
|  | // Factory constructors are not cloned. | 
|  | if (procedure.isFactory) continue; | 
|  |  | 
|  | // NoSuchMethod forwarders aren't cloned. | 
|  | if (procedure.isNoSuchMethodForwarder) continue; | 
|  |  | 
|  | Reference? reference; | 
|  | if (procedure.isSetter) { | 
|  | reference = indexedClass?.lookupSetterReference(procedure.name!); | 
|  | } else { | 
|  | reference = indexedClass?.lookupGetterReference(procedure.name!); | 
|  | } | 
|  |  | 
|  | // Linear search for a forwarding stub with the same name. | 
|  | int? originalIndex; | 
|  | for (int i = 0; i < originalLength; ++i) { | 
|  | var originalProcedure = class_.procedures[i]; | 
|  | if (originalProcedure.name == procedure.name && | 
|  | originalProcedure.kind == procedure.kind) { | 
|  | FunctionNode src = originalProcedure.function!; | 
|  | FunctionNode dst = procedure.function!; | 
|  |  | 
|  | if (src.positionalParameters.length != | 
|  | dst.positionalParameters.length || | 
|  | src.namedParameters.length != dst.namedParameters.length) { | 
|  | // A compile time error has already occurred, but don't crash below, | 
|  | // and don't add several procedures with the same name to the class. | 
|  | continue outer; | 
|  | } | 
|  | originalIndex = i; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (originalIndex != null) { | 
|  | reference ??= class_.procedures[originalIndex].reference; | 
|  | } | 
|  | Procedure clone = cloner.cloneProcedure(procedure, reference); | 
|  | if (originalIndex != null) { | 
|  | Procedure originalProcedure = class_.procedures[originalIndex]; | 
|  | FunctionNode src = originalProcedure.function!; | 
|  | FunctionNode dst = clone.function!; | 
|  | assert(src.typeParameters.length == dst.typeParameters.length); | 
|  | for (int j = 0; j < src.typeParameters.length; ++j) { | 
|  | dst.typeParameters[j].flags = src.typeParameters[j].flags; | 
|  | } | 
|  | for (int j = 0; j < src.positionalParameters.length; ++j) { | 
|  | dst.positionalParameters[j].flags = src.positionalParameters[j].flags; | 
|  | } | 
|  | // TODO(kernel team): The named parameters are not sorted, | 
|  | // this might not be correct. | 
|  | for (int j = 0; j < src.namedParameters.length; ++j) { | 
|  | dst.namedParameters[j].isCovariant = | 
|  | src.namedParameters[j].isCovariant; | 
|  | dst.namedParameters[j].isGenericCovariantImpl = | 
|  | src.namedParameters[j].isGenericCovariantImpl; | 
|  | } | 
|  |  | 
|  | class_.procedures[originalIndex] = clone; | 
|  | clone.parent = class_; | 
|  | } else { | 
|  | class_.addProcedure(clone); | 
|  | } | 
|  | } | 
|  | assert(class_.constructors.isNotEmpty); | 
|  |  | 
|  | // This class implements the mixin type. Also, backends rely on the fact | 
|  | // that eliminated mixin is appended into the end of interfaces list. | 
|  | class_.implementedTypes.add(class_.mixedInType!); | 
|  |  | 
|  | // This class is now a normal class. | 
|  | class_.mixedInType = null; | 
|  |  | 
|  | // Leave breadcrumbs for backends (e.g. for dart:mirrors implementation). | 
|  | class_.isEliminatedMixin = true; | 
|  | } | 
|  | } |