nitro_generator 0.4.0
nitro_generator: ^0.4.0 copied to clipboard
Code generator for Nitro Modules (Nitrogen). Converts *.native.dart specs to Dart FFI, Kotlin, Swift, and C++ bindings.
0.4.0 #
- New: Mixed Apple platform implementation targets — A single module can now use different implementation languages per Apple platform. For example,
macos: NativeImpl.cppwithios: NativeImpl.swiftgenerates a single bridge with#if TARGET_OS_OSX/#elseguards — no manual patching required. Supports all combinations: both Swift, both C++, or mixed. - Fixed: Swift
@nitroNativeAsyncprotocol signature — Methods annotated with@nitroNativeAsyncnow correctly declareasync throwsin the generatedHybridXxxProtocol. - SPM and CocoaPods support — Generated C++ bridges compile correctly under both Swift Package Manager (
.mmforwarder vianitrogen link) and CocoaPods (ios/Classes/andmacos/Classes/forwarders). - Ecosystem sync — Aligned with
nitro,nitro_annotations, andnitrogen_cli0.4.0.
0.3.3 #
- Fixed: JNI crash (ART abort) with nested
@HybridStructfields —GetFieldIDwas called with"Ljava/lang/Object;"for nested struct fields, posting aNoSuchFieldErrorthat ART turned into a fatal runtime abort on the next JNI call. Fixed by generating the correct class descriptor (e.g.Lnitro/nitro_ar_module/Vector3;) in the constructor signature,GetFieldIDcalls,pack_*_from_jni,unpack_*_to_jni, and the release function. Both the already-generated file and the generator itself were fixed to prevent regression. - New:
@HybridStructtypes usable as@HybridRecordlist fields —@HybridRecord() class PackageBoxes { final List<BoundingBox> boxes; }now serializes correctly end-to-end.spec_extractor.dart:_recordFieldKindnow recognises@HybridStruct-annotated types, classifyingList<BoundingBox>aslistRecordObject(notlistPrimitive).struct_generator.dartgenerateKotlin: Every Kotlindata classfor a struct now includescompanion object { decodeFrom(buf) / decode(bytes) },writeFieldsTo(out, buf), andencode(): ByteArrayso structs can be embedded inline in record binary payloads.record_generator.dartgenerateDartExtensions: Auto-generatesRecordExtextensions (withfromNative,fromReader,writeFields,toNative) for every@HybridStructtype referenced in a record field, including transitive closure for nested struct types.
- Fixed: Kotlin record list field wire format — The
writeIndexedListhelper (which discarded list items via{ _ ->}and referenced an undefinedit) has been replaced with a simplewriteInt32(size) + forEach { e -> e.writeFieldsTo(out, buf) }. The corresponding Kotlin read no longer skips a phantom offset table; both sides now use the same count-then-items format as the Dart codec. - Tests: 50 new tests in
struct_in_record_test.dart— Cover: DartRecordExtfor struct list items, Kotlin struct codec methods, Kotlin record using struct codecs, transitive nested-struct closure,recordObject(non-list) struct fields, all primitive field types, wire-format consistency, and negative cases (unreferenced structs produce noRecordExt).
0.3.2 #
- Fixed: Nested struct fields generate typed pointers — Fields whose type is another
@HybridStructnow usePointer<NestedFfi>instead ofPointer<Void>.toDart(),toNative(),freeFields(), and proxy lazy getters all handle nested pointers correctly. - Fixed: Proxy
super()for nested struct fields — Zero-value defaults are now generated recursively (e.g.Vector3(x: 0.0, y: 0.0, z: 0.0)) instead ofnull, which was invalid for non-nullable types. - New: Positional constructor param support —
BridgeFieldgainsisNamedandisRequiredflags. The generator emits positional args before named args intoDart()and proxysuper(), matching the struct's actual constructor signature. The spec extractor reads these flags automatically. - Fixed: TypedData length-field matching is case-sensitive — Only exact lowercase names (
length,size,stride,bytelength,bytelen,len) match. A field namedStride(capital S) now correctly falls back toasTypedList(0). - Tests: 135 new tests across 3 files —
nested_struct_test.dart,struct_constructor_params_test.dart, andstruct_field_types_test.dartcover nested structs, all constructor styles, String/enum/TypedData fields,freeFields()combinations, zeroCopy, nullable stripping, and more.
0.3.1 #
- New: macOS targeting in
BridgeSpec—BridgeSpecnow accepts an optionalmacosImplfield (NativeImpl?) and exposestargetsMacosandtargetsAppleCppgetters.targetsAppleCppis true when eitheriosormacos(or both) useNativeImpl.cpp, enabling a single#ifdef __APPLE__guard in the C++ bridge instead of separate iOS/macOS guards. - New:
INVALID_MACOS_IMPLvalidator error —SpecValidatoremits an error with codeINVALID_MACOS_IMPLand severityerrorwhenmacos: NativeImpl.kotlinis specified, since Kotlin is not a valid native language on macOS. - Improved:
isCppImplgetter — Updated to account formacosImpl; a spec is considered cpp-only when all specified platforms useNativeImpl.cpp. - Improved:
CppBridgeGeneratorplatform guard — The Apple-platform#ifdefblock now uses__APPLE__(covers both iOS and macOS) instead of__APPLE__ && TARGET_OS_IOS, so generated C++ bridges compile correctly in both iOS and macOS targets. - New: Edge-case tests in
spec_validator_expansion_test.dart— 5 new cyclic struct detection edge cases: struct with no fields, struct with only primitive fields, two independent mutual cycles (each reported exactly once), four-struct transitive cycle, and a struct referencing a primitive type (not treated as cycle). - Fixed: stale
DeleteLocalReftest assertions —cpp_bridge_generator_test.dartandedge_cases_test.dartexpected explicitenv->DeleteLocalRef(j_param)calls that are no longer emitted; the generator now wraps every JNI call inPushLocalFrame(16)/PopLocalFrame(nullptr)which frees all local refs automatically. Tests updated to assert thePushLocalFrame/PopLocalFramepattern and guard against regressing to manualDeleteLocalRef. - Fixed: record-return exception-ordering test snippet size — the 400-char substring was too small to contain
GetByteArrayRegionafter the extraPopLocalFrameerror paths were added; extended to 700 chars and added presence guards for both substrings. - New: Zero-copy proxy streaming —
StructGenerator.generateDartProxiesnow emitsfinal class ${Name}Proxy extends ${Name} implements Finalizable. Every getter is@overrideand reads lazily from aPointer<${Name}Ffi>; super fields are zeroed and never read. BecauseProxy <: ValueType,Stream<Proxy>satisfiesStream<Value>via Dart covariant generics — no.map()or API change required. - New: Generated C release symbols —
CppBridgeGeneratoremits avoid ${lib}_release_${Struct}(void* ptr)function for every@HybridStructinsideextern "C"blocks on both the direct-C++ and JNI+Swift paths. - New:
NativeFinalizerwith generated release symbol — Each proxy'sstatic NativeFinalizer? _finalizeris lazily bound todylib.lookup('${lib}_release_${Struct}')via an idempotentstatic void _init(DynamicLibrary dylib). The impl constructor calls${Name}Proxy._init(_dylib)for each struct. - New:
isLeaf: trueon sync primitive bindings — All synchronous FFI bindings with primitive-only return types (including read/write property accessors) are emitted with.asFunction<...>(isLeaf: true), skipping the Dart VM safepoint transition. - New: Indexed
@HybridRecordlist encoding —DartFfiGeneratorencodes list record params withRecordWriter.encodeIndexedListand decodes list record returns withLazyRecordList.decode. Kotlin and Swift encode with awriteIndexedListhelper; the decode path skips the offset table. - New:
_superDefaulthelper inStructGenerator— Returns a safe zero-value Dart literal for each field type so the proxy'ssuper(...)call compiles without touching native data. - Breaking fix: struct stream override type — Generated struct stream overrides now emit
Stream<${ValueType}>(matching the spec) while usingopenStream<${Proxy}>internally. Previously the impl emittedStream<${Proxy}>which was an invalid override. - Fixed: missing struct release symbols on Android — Struct release functions (used by Dart's
NativeFinalizer) were previously incorrectly guarded by platform preprocessor blocks inCppBridgeGenerator, causing them to be missing from Android builds. They are now generated in a commonextern "C"block for all platforms. - Fixed: memory leaks in struct return paths — Implemented deep release of heap-allocated native fields (e.g., native strings
char*allocated viastrdup) in the struct release functions. - Fixed: struct property getter leaks — Updated the Dart FFI generator to correctly convert and deeply release struct properties, matching the safety logic used for method returns.
- Fixed: struct release exports in C++ header —
CppHeaderGeneratornow includesNITRO_EXPORTdeclarations for all struct release functions, ensuring they are correctly exported and visible to the Dart FFI layer. - New:
freeFields()for FFI structs — Generated FFI struct extensions now include afreeFields()method to safely release internal native resources. - Improved: memory safety in
toDart()— Struct conversion now performs an eager copy ofTypedDatafields (usingUint8List.fromList), preventing use-after-free errors when the native buffer is quickly released. - Improved: cleaner bridge code — Struct release functions are now coalesced into a single
extern "C"block in the generated bridge C++ source. - Tests: regression coverage for struct release — Added unit tests to
cpp_header_generator_test.dart,cpp_bridge_generator_test.dart, anddart_ffi_generator_test.dartto verify memory safety and correct symbol generation across all layers.
0.3.0 #
-
New: Direct C++ Implementation support — Generator produces
*.native.g.h,*.mock.g.h, and*.test.g.cppwhen@NitroModule(ios: NativeImpl.cpp, android: NativeImpl.cpp)is specified. -
New: Thread-safe bridge —
CppBridgeGeneratorusesstd::atomicforg_impland stream port storage;CppBridgeGeneratornow uses direct virtual dispatch with no platform#ifdefblocks. -
New:
std::optional<T>for nullable types —CppInterfaceGeneratoremits nullable Dart types asstd::optional<T>. -
New: Precise
Pointer<T>C++ mapping —Pointer<SomeEnum>→SomeEnum*,Pointer<SomeStruct>→SomeStruct*,Pointer<Void>→void*. -
New:
#ifndefstruct guards —generateCStructswraps each C struct in an include guard to preventtypedef redefinitionerrors in CocoaPods umbrella builds. -
New:
BridgeSpec.isCppImpl— convenience getter for detecting pure-C++ modules. -
Improved: FFI memory safety —
DartFfiGeneratoremitstry { ... } finally { malloc.free(...); }for all record/struct return paths. -
Improved:
checkDisposed()guards — Added to all generated methods includingFast(leaf) functions; annotated@pragma('vm:prefer-inline'). -
Fixed: Builder diagnostics —
builder.printWarningnow shows actual file paths instead of literal placeholders. -
New: Single-platform targeting —
@NitroModulenow accepts optionaliosandandroidparameters. A module can target iOS only, Android only, or both. Generators skip output for untargeted platforms. -
New:
BridgeSpec.targetsIos/targetsAndroid— convenience getters derived from nullableiosImpl/androidImpl. -
New:
NO_TARGET_PLATFORMvalidation error —SpecValidatoremits an error when neitheriosnorandroidis specified. -
Improved:
BridgeSpec.isCppImpl— correctly handles single-platform C++ specs (ios: NativeImpl.cppwithandroidomitted, and vice versa). -
Improved:
SwiftGenerator— returns a placeholder comment when iOS is not targeted instead of generating an empty/broken file. -
Improved:
KotlinGenerator— returns a placeholder comment when Android is not targeted. -
Improved:
CppBridgeGenerator— omits#ifdef __ANDROID__/#elif __APPLE__/#endifplatform guards for single-platform specs; routes to the appropriate single-platform code path. -
Tests: 72 new tests — platform targeting unit tests, single-platform generator output,
isCppImpledge cases, additional validator rules, JNI parameter handling, CMake variable indirection, C++ mock and interface edge cases. -
Tests: 100+ new regression tests — Benchmark spec tests,
Pointer<T>param/return tests, nullable type tests, and stream backpressure isolation tests across all generators.
0.2.3 #
- Fix: Array — Updated the Swift generator to correctly bridge
Uint8Listparameters asDatawhen matching native Swift signatures, ensuring type-safe binary data transfer without manual casts. - Improved: Header Generator — The generated C++ bridge headers now automatically include
nitro.h. - Improved: Dependency Sync: Synchronized the Nitro ecosystem to version 0.2.3.
0.2.2 #
- Fix: stable annotation resolution — updated
SpecExtractorto useTypeChecker.fromRuntimefor all Nitro annotations, ensuring they are correctly identified when re-exported through thenitroruntime package. This resolves "No @NitroModule annotated classes found" and "UNKNOWN_RETURN_TYPE" errors for enums/structs in complex specifications. - Improved: spec-level type registration — ensured that all enums and structs defined in a spec library are correctly added to the valid type set before function, property, and stream validation.
0.2.1 #
- Fix: non-zero-copy TypedData function parameters now produce correct JNI arrays — previously the raw C pointer (
float*,int32_t*, …) was passed directly as the JNI call argument, causing a crash at runtime. The generator now emitsNewFloatArray/NewIntArray/SetFloatArrayRegion/ etc. and passes the properjarrayreference. ADeleteLocalRefis emitted after the call in every return-type path. - Fix: removed redundant
env->ExceptionClear()at JNI call sites —nitro_report_jni_exceptionalready callsExceptionClear()internally; the duplicate call at each call site was a no-op and has been removed. - Fix:
_streamJobsmap now uses a compositePair<String, Long>key — keying only ondartPortmeant two simultaneous subscriptions on different streams could theoretically overwrite each other's coroutine job if they received the same port value. The key is nowPair(streamName, dartPort). - Polish: spec-path attribution in all generated files — every generated file (Dart, Kotlin, Swift, C++, CMake) now includes
// Generated from: <spec>.native.dartat the top, making it easy to trace any generated file back to its source spec when working with multiple modules. - Polish:
checkDisposed()annotated@pragma('vm:prefer-inline')— the single-field_disposedcheck is now inlined by the Dart VM/AOT compiler, eliminating the call overhead on every generated method invocation. - Performance: single-pass AST extraction in
SpecExtractor—_extractRecordTypespreviously calledlibrary.annotatedWithtwice (once to collect names, once to build types); it now collects class elements in one pass and reuses the list._extractPropertiesand_extractStreamspreviously made two separate loops overelement.accessors; they are now merged into a single combined pass. - Decoupled from the
nitroruntime package to resolvepub.devplatform warnings. - Now depends on the pure-Dart
nitro_annotationspackage. - This ensures the generator is recognized as a cross-platform Dart package.
0.2.0 #
- New:
@HybridRecordBinary Bridge Generator — Generated extensions now use a compact binary protocol (uint8_t*/Pointer<Uint8>) instead of UTF-8 JSON strings, significantly reducing serialization overhead.- Breaking: Extension methods renamed to standard codec names:
fromJson→fromNative/fromReader,toJson→writeFields/toNative. - Full support for
@HybridRecordin Kotlin (.bridge.g.kt) via@Keep data classwith companiondecode/encodemethods. Swift support updated fortoNativeandRecordReaderintegrations.
- Breaking: Extension methods renamed to standard codec names:
- New: Comprehensive Collection Bridging — Added binary-first support for:
List<primitive>(int, double, bool, String) viaRecordWriter.encodePrimitiveList.Map<String, T>using the UTF-8 JSON path (dynamic values).- Nested lists and nullable record fields.
- Improved: Swift Stream Stability — Fixed a compiler error in
_register_*_streamby heap-allocating@HybridStructitems before passing them to the C emit callback. - Improved: Code Quality & Lints — Generated code now follows strict Dart linting rules:
- Cleaned up unbraced for-loops and unused local variable declarations.
- Renamed internal variables to follow public naming conventions (e.g.,
_rawResult→rawResult).
- Testing: Added 28+ regression tests for Kotlin record emission and updated 200+ existing tests to match the binary wire format.
0.1.3 #
- Swift generator: fixed
@_cdeclString type crash (EXC_BAD_ACCESS) —Stringparameters now useUnsafePointer<CChar>?(Cconst char*) and return values useUnsafeMutablePointer<CChar>?(malloc'dchar*), withString(cString:)conversion at the boundary andstrdup()for returns so Dart'stoDartStringWithFree()/free()pairs correctly. - Swift generator: async
String-returning methods useDispatchSemaphore+Task.detachedwith astrdup(result)return. - Swift generator:
Stringproperty getters returnstrdup-allocated C strings; setters acceptUnsafePointer<CChar>?and convert withString(cString:).
0.1.2 #
- Swift generator: replaced
@objc public static func _call_*pattern with top-level@_cdecl("_call_*") public funcstubs. Swift structs and Swift-only protocols cannot cross the Objective-C boundary. - Swift generator:
boolreturn type now maps toInt8(matching C'sint8_t) instead ofBool. - Swift generator: struct-returning functions now return
UnsafeMutableRawPointer?(heap-allocated, caller frees) instead ofAny?.
0.1.1 #
- Renamed package from
nitrogentonitro_generatorto avoid a naming conflict onpub.dev.
0.1.0 #
- Initial release of Nitro code generator.
- Generates Dart FFI, Kotlin, Swift, and C++ bindings.
- Support for
HybridObject,HybridStruct, andHybridEnum. - Support for
@nitroAsyncmethods. - Support for
@NitroStreamwith Backpressure strategies.