blob: 03a4b767c27c83b6b04e928808e77a8572a01f46 [file] [log] [blame] [edit]
// 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 'dart:ffi';
import 'package:logging/logging.dart';
import 'code_generator.dart';
import 'code_generator/scope.dart';
import 'config_provider/config.dart';
import 'config_provider/config_types.dart';
import 'config_provider/spec_utils.dart';
import 'header_parser/clang_bindings/clang_bindings.dart' show Clang;
import 'header_parser/utils.dart';
/// Wrapper around various ffigen-wide variables.
class Context {
final Logger logger;
final Config config;
final CursorIndex cursorIndex;
final bindingsIndex = BindingsIndex();
final savedMacros = <String, Macro>{};
final unnamedEnumConstants = <Constant>[];
late final ObjCBuiltInFunctions objCBuiltInFunctions;
bool hasSourceErrors = false;
final reportedCommentRanges = <((String, int), (String, int))>{};
final libs = LibraryImports();
late final compilerOpts = config.compilerOpts ?? defaultCompilerOpts(logger);
final Scope rootScope = Scope.createRoot('root');
final Scope rootObjCScope = Scope.createRoot('objc_root');
late final ExtraSymbols extraSymbols;
Context(this.logger, FfiGenerator generator, {Uri? libclangDylib})
: config = Config(generator),
cursorIndex = CursorIndex(logger) {
objCBuiltInFunctions = ObjCBuiltInFunctions(
this,
config.wrapperName,
// ignore: deprecated_member_use_from_same_package
generator.objectiveC?.generateForPackageObjectiveC ?? false,
);
final libclangDylibPath =
// ignore: deprecated_member_use_from_same_package
generator.libclangDylib?.toFilePath() ??
libclangDylib?.toFilePath() ??
findDylibAtDefaultLocations(logger);
_clang ??= Clang(DynamicLibrary.open(libclangDylibPath));
}
}
/// The clang bindings.
//
// Ideally this would be in the Context, but the plumbing needed would be
// excessive. The point of putting globals in the Context is to allow multiple
// concurrent FfiGen runs without any risk of clobbering global state. The
// clang bindings are loaded from a dylib specified by the config, so it's
// possible that two different versions of clang could be loaded. But since we
// interact with clang through a stable API, there's no real danger of version
// skew. So the safest thing is to simply load clang the first time a Context
// is created, and reuse it for all subsequent runs.
Clang get clang => _clang!;
Clang? _clang;
class LibraryImports {
String get ffiLibraryPrefix => prefix(ffiImport);
String get ffiPkgLibraryPrefix => prefix(ffiPkgImport);
String get objcPkgPrefix => prefix(objcPkgImport);
String get selfImportPrefix => prefix(selfImport);
// Dedupe [lib] by name.
LibraryImport canonicalize(LibraryImport lib) =>
builtInLibraries[lib.name] ?? (_canonicalImports[lib.name] ??= lib);
final _canonicalImports = <String, LibraryImport>{};
// Mark an import as being used so that it can be assigned a prefix.
void markUsed(LibraryImport lib) {
assert(lib == canonicalize(lib));
_used.add(lib);
}
Iterable<LibraryImport> get used => _used;
final _used = <LibraryImport>{};
// Call after all used imports have been marked by [markUsed]. Creates Symbols
// for all the library prefixes used for codegen.
void createSymbols(Scope scope) {
for (final lib in _used) {
scope.add(_prefixes[lib] = Symbol(lib.name));
}
_prefixesFilled = true;
}
bool _prefixesFilled = false;
final _prefixes = <LibraryImport, Symbol>{};
String prefix(LibraryImport lib) {
assert(lib == canonicalize(lib));
if (!_prefixesFilled) {
// Before the prefixes have been filled, return a placeholder suitable for
// debugging or hashing, but intentionally invalid as generated code.
return '<${lib.name}>';
}
// If this null assert fails, it means that a library was used during code
// generation that wasn't visited by MarkImportsVisitation, which is a bug.
return _prefixes[lib]!.name;
}
void forceFillForTesting() {
_used.addAll(builtInLibraries.values);
for (final lib in _used) {
_prefixes[lib] = Symbol(lib.name)..forceFillForTesting();
}
_prefixesFilled = true;
}
}
typedef ExtraSymbols = ({
Symbol? wrapperClassName,
Symbol? lookupFuncName,
// TODO(https://github.com/dart-lang/native/issues/1259): Make this nullable.
Symbol symbolAddressVariableName,
});