blob: 0fefb1cd340c2ec4f416a4ff6c90cb374de9bd6f [file]
// 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 '../code_generator.dart';
import '../config_provider/config.dart' show Config;
import '../strings.dart' as strings;
import 'ast.dart';
enum _IncludeBehavior {
configOnly,
configOrTransitive,
configAndTransitive,
configOrDirectTransitive,
}
class ListBindingsVisitation extends Visitation {
final Config config;
final Set<Binding> includes;
final Set<Binding> transitives;
final Set<Binding> directTransitives;
final bindings = <Binding>{};
ListBindingsVisitation(
this.config,
this.includes,
Set<Binding> indirectTransitives,
this.directTransitives,
) : transitives = {...indirectTransitives, ...directTransitives};
void _add(Binding node) {
node.visitChildren(visitor);
bindings.add(node);
}
bool _shouldInclude(Binding node, _IncludeBehavior behavior) {
if (node.isObjCImport) return false;
switch (behavior) {
case _IncludeBehavior.configOnly:
return includes.contains(node);
case _IncludeBehavior.configOrTransitive:
return includes.contains(node) || transitives.contains(node);
case _IncludeBehavior.configAndTransitive:
return includes.contains(node) && transitives.contains(node);
case _IncludeBehavior.configOrDirectTransitive:
return includes.contains(node) || directTransitives.contains(node);
}
}
bool _visitImpl(Binding node, _IncludeBehavior behavior) {
if (_shouldInclude(node, behavior)) {
_add(node);
return true;
}
return false;
}
@override
void visitBinding(Binding node) =>
_visitImpl(node, _IncludeBehavior.configOrTransitive);
@override
void visitObjCInterface(ObjCInterface node) {
final omit =
node.unavailable ||
!_visitImpl(
node,
config.objectiveC?.interfaces.includeTransitive ?? false
? _IncludeBehavior.configOrTransitive
: _IncludeBehavior.configOnly,
);
if (omit && directTransitives.contains(node)) {
node.generateAsStub = true;
bindings.add(node);
// Always visit the supertypes and protocols, even if this is a stub.
visitor.visit(node.superType);
visitor.visitAll(node.protocols);
}
if (includes.contains(node)) {
// Always visit the categories of explicitly included interfaces, even if
// they're built-in types: https://github.com/dart-lang/native/issues/1820
visitor.visitAll(node.categories);
}
}
@override
void visitObjCCategory(ObjCCategory node) => _visitImpl(
node,
config.objectiveC?.categories.includeTransitive ?? false
? _IncludeBehavior.configOrDirectTransitive
: _IncludeBehavior.configOnly,
);
@override
void visitObjCProtocol(ObjCProtocol node) {
final omit =
node.unavailable ||
!_visitImpl(
node,
config.objectiveC?.protocols.includeTransitive ?? false
? _IncludeBehavior.configOrTransitive
: _IncludeBehavior.configOnly,
);
if (omit && directTransitives.contains(node)) {
node.generateAsStub = true;
bindings.add(node);
// Always visit the super protocols, even if this is a stub.
visitor.visitAll(node.superProtocols);
}
}
@override
void visitTypealias(Typealias node) {
_visitImpl(
node,
config.typedefs.includeUnused
? _IncludeBehavior.configOnly
: _IncludeBehavior.configAndTransitive,
);
// Objective C has some core typedefs that are important to keep.
if (config.objectiveC != null &&
node.originalName == strings.objcInstanceType) {
_add(node);
}
// Visit typealias children if it's transitively referenced, regardless of
// whether the typealias itself is included by the config.
if (transitives.contains(node)) {
node.visitChildren(visitor);
}
}
}
class MarkBindingsVisitation extends Visitation {
final Set<Binding> bindings;
MarkBindingsVisitation(this.bindings);
@override
void visitBinding(Binding node) {
node.visitChildren(visitor);
node.generateBindings = bindings.contains(node);
}
}