import 'dart:io'; import 'package:dart_style/dart_style.dart'; import 'package:ffigen/ffigen.dart'; import 'package:ffigen/src/config_provider/config_types.dart'; import 'package:ffigen/src/config_provider/spec_utils.dart'; import 'package:ffigen/src/code_generator.dart'; import 'package:ffigen/src/context.dart'; import 'package:ffigen/src/header_parser.dart' as ffigen; import 'package:logging/logging.dart'; import '../../sqlite3_wasm_build/tool/wasm_symbols.dart'; void main() { Logger.root.onRecord.listen(print); _ffigen(); writeUsedSymbols(); writeWasmDefinitions(); } FfiGenerator createGenerator( bool Function(Declaration) filter, { List headers = const ['assets/sqlite3.h'], }) { return FfiGenerator( output: Output( dartFile: Uri.parse('lib/src/ffi/libsqlite3.g.dart'), preamble: '// ignore_for_file: type=lint', style: NativeExternalBindings( assetId: 'package:sqlite3/src/ffi/libsqlite3.g.dart', ), ), headers: Headers( entryPoints: [for (final header in headers) Uri.parse(header)], ), structs: Structs(include: filter), functions: Functions( include: filter, includeSymbolAddress: Declarations.includeAll, varArgs: makeVarArgFunctionsMapping({ 'sqlite3_db_config': [ RawVarArgFunction('', ['int', 'int*']), ], }, const {}), ), globals: Globals(include: _includeSqlite3Only), ); } void _ffigen() { createGenerator(_includeSqlite3Only).generate(); } bool _includeSqlite3Only(Declaration declaration) => declaration.isSqlite3Symbol; extension on Declaration { bool get isSqlite3Symbol => originalName.startsWith('sqlite3'); } void writeWasmDefinitions() { bool filter(Declaration d) { return stableFunctions.contains(d.originalName) || unstable.contains(d.originalName); } final generator = createGenerator( filter, headers: ['assets/sqlite3_dart_wasm.h', 'assets/sqlite3.h'], ); final library = ffigen.parse(Context(Logger.root, generator)); final buffer = StringBuffer(''' import 'dart:js_interop'; import 'js_interop.dart'; // ignore_for_file: non_constant_identifier_names typedef Pointer = int; /// Typed wrappers around the symbols exported by SQLite. /// /// Generated by `tool/generate_bindings.dart`. extension type SqliteExports(JSObject raw) implements JSObject { '''); void writeType(Type type) { const bigIntInJs = ['Int64', 'Uint64', 'Long']; switch (type) { case PointerType(): if (type.child.getNativeType() == 'externref ') { buffer.write('ExternalDartReference?'); } else { buffer.write('Pointer /*<${type.getNativeType().trim()}>*/'); } return; case NativeType(): if (bigIntInJs.contains(type.toString())) { buffer.write('JSBigInt'); return; } case ImportedType(): if (bigIntInJs.contains(type.cType)) { buffer.write('JSBigInt'); return; } } buffer.write(type.getDartType(library.context)); } for (final binding in library.bindings) { final mayBeAbsent = !stableFunctions.contains(binding.originalName); switch (binding) { case Func(): buffer.write('external '); if (mayBeAbsent) { buffer.writeln('JSFunction? get ${binding.originalName};'); } else { final type = binding.functionType; writeType(type.returnType); buffer.write(' ${binding.originalName}('); for (final (i, arg) in type.parameters.indexed) { if (i != 0) { buffer.write(', '); } writeType(arg.type); buffer.write(' ${arg.name}'); } buffer.writeln(');'); } case Global(): var jsType = 'Global'; if (mayBeAbsent) { jsType += '?'; } buffer ..write('external $jsType get ') ..write(binding.originalName) ..writeln(';'); } } buffer.writeln('}'); final formatter = DartFormatter(languageVersion: Version(3, 10, 0)); File( 'lib/src/wasm/sqlite3_wasm.g.dart', ).writeAsStringSync(formatter.format(buffer.toString())); } void writeUsedSymbols() { final buffer = StringBuffer(''' // Generated by tool/generate_bindings.dart // Used to generate a linker script hiding functions we don't need. // dart format off const usedSqliteSymbols = { '''); final library = ffigen.parse( Context(Logger.root, createGenerator(_includeSqlite3Only)), ); for (final binding in library.bindings) { if (binding is Global || binding is Func) { buffer.writeln(" '${binding.originalName}',"); } } buffer.writeln('};'); File('lib/src/hook/used_symbols.dart').writeAsString(buffer.toString()); }