blob: af915c8d106cd23fedca20df01185cffbf8e1172 [file] [log] [blame] [edit]
// Copyright (c) 2024, 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 '../ast/_core/interfaces/compound_declaration.dart';
import '../ast/_core/interfaces/declaration.dart';
import '../ast/_core/interfaces/nestable_declaration.dart';
import '../ast/declarations/built_in/built_in_declaration.dart';
import '../ast/declarations/compounds/class_declaration.dart';
import '../ast/declarations/compounds/enum_declaration.dart';
import '../ast/declarations/compounds/struct_declaration.dart';
import '../ast/declarations/globals/globals.dart';
import '../ast/declarations/typealias_declaration.dart';
import '../ast/visitor.dart';
import '../context.dart';
import '../parser/_core/utils.dart';
import '_core/dependencies.dart';
import '_core/unique_namer.dart';
import '_core/utils.dart';
import 'transformers/transform_compound.dart';
import 'transformers/transform_enum.dart';
import 'transformers/transform_globals.dart';
class TransformationState {
// Map from untransformed decleration to its transformed declaration, or null
// if there is generated code for the declaration.
final map = <Declaration, Declaration?>{};
// All the bindings to be generated.
final bindings = <Declaration>{};
// Bindings that will be generated as stubs.
final stubs = <Declaration>{};
late final UniqueNamer globalNamer;
// Map from tuple signature to generated wrapper class
final tupleWrappers = <String, ClassDeclaration>{};
}
/// Transforms the given declarations into the desired ObjC wrapped declarations
List<Declaration> transform(
Context context,
List<Declaration> declarations, {
required bool Function(Declaration) filter,
}) {
final state = TransformationState();
final includes = visit(
context,
FindIncludesVisitation(filter),
declarations,
).includes;
final directTransitives = visit(
context,
FindDirectTransitiveDepsVisitation(includes),
includes,
).directTransitives;
state.bindings.addAll(includes.union(directTransitives));
final listDecls = visit(
context,
ListDeclsVisitation(includes, directTransitives),
state.bindings,
);
final topLevelDecls = listDecls.topLevelDecls.toList();
state.stubs.addAll(listDecls.stubDecls);
state.bindings.addAll(listDecls.stubDecls);
state.globalNamer = UniqueNamer(
state.bindings.map((declaration) => declaration.name),
);
final globals = Globals(
functions: topLevelDecls.removeWhereType<GlobalFunctionDeclaration>(),
variables: topLevelDecls.removeWhereType<GlobalVariableDeclaration>(),
);
final transformedDeclarations = [
...topLevelDecls.map(
(d) => maybeTransformDeclaration(d, state.globalNamer, state),
),
transformGlobals(globals, state.globalNamer, state),
].nonNulls.toList();
return [
...transformedDeclarations,
..._getPrimitiveWrapperClasses(state),
...state.tupleWrappers.values,
].sortedById();
}
Declaration transformDeclaration(
Declaration declaration,
UniqueNamer parentNamer,
TransformationState state, {
bool nested = false,
}) =>
maybeTransformDeclaration(
declaration,
parentNamer,
state,
nested: nested,
) ??
declaration;
Declaration? maybeTransformDeclaration(
Declaration declaration,
UniqueNamer parentNamer,
TransformationState state, {
bool nested = false,
}) {
if (!state.bindings.contains(declaration)) {
return null;
}
if (state.map.containsKey(declaration)) {
return state.map[declaration];
}
if (declaration is InnerNestableDeclaration &&
declaration.nestingParent != null &&
!nested) {
// It's important that nested declarations are only transformed in the
// context of their parent, so that their parentNamer is correct. So find
// the top level declaration this is nested in, and transform that first.
maybeTransformDeclaration(
_topLevelNestingParent(declaration),
state.globalNamer,
state,
);
// Now that the parents are transformed, this declaration should haven been
// transformed, and will be in the cache.
// TODO(https://github.com/dart-lang/native/issues/1358): This is brittle. Switch naming to a transformer.
return state.map[declaration] ??
maybeTransformDeclaration(
declaration,
parentNamer,
state,
nested: true,
);
}
return switch (declaration) {
ClassDeclaration() || StructDeclaration() => transformCompound(
declaration as CompoundDeclaration,
parentNamer,
state,
),
EnumDeclaration() => transformEnum(declaration, parentNamer, state),
TypealiasDeclaration() => null,
_ => throw UnimplementedError(),
};
}
List<Declaration> _getPrimitiveWrapperClasses(TransformationState state) =>
state.map.entries
.where((entry) => entry.key is BuiltInDeclaration)
.map((entry) => entry.value)
.nonNulls
.toList();
Declaration _topLevelNestingParent(Declaration declaration) =>
declaration is InnerNestableDeclaration && declaration.nestingParent != null
? _topLevelNestingParent(declaration.nestingParent!)
: declaration;