blob: 80799f3666d8076cc124625d415cd6169a1d37ca [file] [log] [blame]
// Copyright (c) 2021, 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.
// @dart=2.12
import 'package:kernel/ast.dart';
import 'package:kernel/clone.dart';
import 'package:kernel/type_algebra.dart';
/// Transforms the libraries in [libraries] by cloning any mixed in methods that
/// use super.
void transformLibraries(List<Library> libraries) {
_CloneMixinMethodsWithSuper.transform(libraries);
}
class _CloneMixinMethodsWithSuper {
/// Transform the given new [libraries]. It is expected that all other
/// libraries have already been transformed.
static void transform(List<Library> libraries) {
// Clone any mixed in methods that uses super.
var processedClasses = Set<Class>();
for (var library in libraries) {
for (var cls in library.classes) {
if (processedClasses.add(cls)) {
transformClass(cls);
}
}
}
}
/// Transforms a given class by cloning any mixed in methods that use super.
static void transformClass(Class cls) {
var mixedInClass = cls.mixedInClass;
if (mixedInClass == null) return;
var mixedInType = cls.mixedInType!;
bool hasProcedureMaps = false;
Map<Name, Procedure> existingNonSetters = {};
Map<Name, Procedure> existingSetters = {};
void ensureExistingProcedureMaps() {
if (hasProcedureMaps) return;
for (Procedure procedure in cls.procedures) {
if (procedure.kind == ProcedureKind.Setter) {
existingSetters[procedure.name] = procedure;
} else {
existingNonSetters[procedure.name] = procedure;
}
}
hasProcedureMaps = true;
}
CloneVisitorWithMembers? cloneVisitor;
for (var field in mixedInClass.mixin.fields) {
if (field.containsSuperCalls) {
cloneVisitor ??= MixinApplicationCloner(cls,
typeSubstitution: getSubstitutionMap(mixedInType));
// TODO(jensj): Provide a "referenceFrom" if we need to support
// the incremental compiler.
ensureExistingProcedureMaps();
Procedure? existingGetter = existingNonSetters[field.name];
Procedure? existingSetter = existingSetters[field.name];
cls.addField(cloneVisitor.cloneField(
field, existingGetter?.reference, existingSetter?.reference));
if (existingGetter != null) {
cls.procedures.remove(existingGetter);
}
if (existingSetter != null) {
cls.procedures.remove(existingSetter);
}
continue;
}
}
for (var procedure in mixedInClass.mixin.procedures) {
if (procedure.containsSuperCalls) {
cloneVisitor ??= MixinApplicationCloner(cls,
typeSubstitution: getSubstitutionMap(mixedInType));
// TODO(jensj): Provide a "referenceFrom" if we need to support
// the incremental compiler.
ensureExistingProcedureMaps();
Procedure? existingProcedure = procedure.kind == ProcedureKind.Setter
? existingSetters[procedure.name]
: existingNonSetters[procedure.name];
if (existingProcedure != null) {
cls.procedures.remove(existingProcedure);
}
cls.addProcedure(cloneVisitor.cloneProcedure(
procedure, existingProcedure?.reference));
continue;
}
}
}
}