| // 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; |
| } |
| } |
| } |
| } |