d8f2b7cc44
TEST=existing Change-Id: I4d0ae16291414e58207b7de0466d989d27997619 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/195074 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Jens Johansen <jensj@google.com>
240 lines
9.2 KiB
Dart
240 lines
9.2 KiB
Dart
// 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;
|
|
}
|
|
}
|