blob: 0a39f54cd5ffed84b7a4abdc1e43f8ebc8fbf820 [file] [log] [blame]
// Copyright (c) 2025, 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 'package:kernel/ast.dart';
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/target/targets.dart' show TargetFlags;
import 'package:vm/modular/target/vm.dart' show VmTarget;
import 'package:vm/transformations/pragma.dart';
import 'collector.dart';
bool _allowsCall(ParsedEntryPointPragma pragma) =>
pragma.type == PragmaEntryPointType.Default ||
pragma.type == PragmaEntryPointType.CallOnly;
bool _allowsGet(ParsedEntryPointPragma pragma) =>
pragma.type == PragmaEntryPointType.Default ||
pragma.type == PragmaEntryPointType.GetterOnly;
bool _allowsSet(ParsedEntryPointPragma pragma) =>
pragma.type == PragmaEntryPointType.Default ||
pragma.type == PragmaEntryPointType.SetterOnly;
EntryPointShimCollector visitLibrary(
Component component,
Library library, {
CoreTypes? coreTypes,
bool createUninitializedInstanceMethods = false,
bool errorOnUnhandledEntryPoints = false,
}) {
coreTypes ??= CoreTypes(component);
final collector = EntryPointShimCollector(
coreTypes,
errorOnUnhandledEntryPoints,
);
final visitor = EntryPointShimVisitor(coreTypes, library, collector);
component.accept(visitor);
return visitor.collector;
}
class EntryPointShimVisitor extends RecursiveVisitor {
final Library _library;
final EntryPointShimCollector _collector;
final bool _createUninitializedInstances;
final PragmaAnnotationParser _pragmaParser;
EntryPointShimVisitor(
CoreTypes coreTypes,
this._library,
this._collector, {
bool createUninitializedInstanceMethods = false,
}) : _createUninitializedInstances = createUninitializedInstanceMethods,
_pragmaParser = ConstantPragmaAnnotationParser(
coreTypes,
VmTarget(const TargetFlags()),
);
EntryPointShimCollector get collector => _collector;
ParsedEntryPointPragma? _entryPointAnnotation(Annotatable node) {
if ((node as dynamic).enclosingLibrary != _library) {
return null;
}
final pragmas = _pragmaParser
.parsedPragmas<ParsedEntryPointPragma>(node.annotations)
.where(
(p) =>
p.type != PragmaEntryPointType.Extendable &&
p.type != PragmaEntryPointType.CanBeOverridden,
);
if (pragmas.isEmpty) return null;
var pragma = pragmas.first;
if (pragma.type != PragmaEntryPointType.Default) {
for (final p in pragmas.skip(1)) {
if (p.type == PragmaEntryPointType.Default) {
pragma = p;
break;
}
if (p.type != pragma.type) {
throw "Incompatible non-default pragmas: ${pragma.type} and ${p.type}";
}
}
}
return pragma;
}
@override
void visitField(Field field) {
final pragma = _entryPointAnnotation(field);
if (pragma != null) {
assert(pragma.type != PragmaEntryPointType.CallOnly);
assert(!field.isFinal || pragma.type != PragmaEntryPointType.SetterOnly);
if (_allowsGet(pragma)) {
_collector.addAll(field.getterReference, {
EntryPointRole.getter,
// If the field's value has a Function type, then the value can be
// both retrieved and invoked using a single Dart_Invoke call.
if (field.getterType is FunctionType) EntryPointRole.call,
});
}
if (_allowsSet(pragma)) {
_collector.add(field.setterReference!, EntryPointRole.setter);
}
}
super.visitField(field);
}
@override
void visitConstructor(Constructor constructor) {
final pragma = _entryPointAnnotation(constructor);
if (pragma != null) {
assert(_allowsCall(pragma));
_collector.addAll(constructor.reference, {
// Dart_New
EntryPointRole.allocation,
// Dart_InvokeConstructor
if (_createUninitializedInstances) EntryPointRole.initialization,
});
}
super.visitConstructor(constructor);
}
@override
void visitProcedure(Procedure procedure) {
final pragma = _entryPointAnnotation(procedure);
if (pragma != null) {
if (procedure.isGetter) {
assert(_allowsGet(pragma));
_collector.addAll(procedure.reference, {
EntryPointRole.getter,
// If the returned value has a Function type, then the closure value
// can be both retrieved and invoked using a single Dart_Invoke call.
if (procedure.getterType is FunctionType) EntryPointRole.call,
});
} else if (procedure.isSetter) {
assert(_allowsSet(pragma));
// Treat the procedure the same as a field, since both are accessed via
// Dart_SetField.
_collector.add(procedure.reference, EntryPointRole.setter);
} else if (procedure.isFactory) {
assert(_allowsCall(pragma));
_collector.add(procedure.reference, EntryPointRole.call);
} else {
_collector.addAll(procedure.reference, {
if (_allowsCall(pragma)) EntryPointRole.call,
if (_allowsGet(pragma)) EntryPointRole.closure,
});
}
}
super.visitProcedure(procedure);
}
@override
void visitClass(Class cls) {
final pragma = _entryPointAnnotation(cls);
if (pragma != null) {
assert(pragma.type == PragmaEntryPointType.Default);
final canAllocate = !cls.isAbstract && _createUninitializedInstances;
_collector.addAll(cls.reference, {
EntryPointRole.class_,
EntryPointRole.nonNullableType,
EntryPointRole.nullableType,
if (canAllocate) EntryPointRole.allocation,
});
}
super.visitClass(cls);
}
}