Background
While implementing the Rust-to-Dart logging bridge in #3079, we currently need the Dart side of the bridge to be initialized automatically from RustLib.init().
The current implementation achieves this by adding logging-specific codegen logic:
- The Rust macro expands to functions named
frb_init_logger, frb_logging_max_level, and frb_logging_setup_dart_logging_output.
- Dart codegen scans MIR functions for these exact names.
- If
frb_init_logger exists, codegen injects a FrbDartLogging.init(...) call into generated executeRustInitializers().
This works, but it is too feature-specific. The code generator should not need to know that a particular function name means "initialize logging on the Dart side".
Desired Feature
Add a generic way for Rust-side declarations/macros to request Dart initialization code that is inserted into the generated RustLib.init() flow.
Possible syntax:
#[frb(init_dart_code = r#"
FrbDartLogging.init(
rustLogStream: api.frbInitLogger(maxLevel: api.frbLoggingMaxLevel()),
mapRecord: (record) => FrbLogRecordData(
level: record.level,
message: record.message,
target: record.target,
modulePath: record.modulePath,
file: record.file,
line: record.line,
),
setupDefaultOutput: api.frbLoggingSetupDartLoggingOutput(),
);
"#)]
pub fn frb_init_logger(...) -> ... {
...
}
Name bikeshedding is welcome. Other possible names:
dart_init_code
init_dart_code
dart_initializer
The important part is the capability, not the exact spelling.
Expected Semantics
- The attribute can be attached to a Rust item that is already visible to FRB codegen, probably a function first.
- Codegen records the Dart snippet during parsing.
- Generated
executeRustInitializers() includes these snippets after the existing Rust initializers.
- The snippet is emitted into generated Dart in a context where
api is available, just like the current logging-specific generated code.
- The snippet may reference generated Dart API methods by their Dart names, e.g.
api.frbInitLogger(...).
- Multiple snippets should be supported and emitted deterministically, ideally in source/MIR order.
The generated shape should continue to look roughly like:
@override
Future<void> executeRustInitializers() async {
await api.someRustInitializer();
// generic Dart initializer snippets here
}
Why This Is Needed
The logging bridge is not the only plausible feature that may need this pattern. Any Rust-side macro that expands to FRB APIs and needs a Dart runtime helper could benefit from a generic Dart initializer hook.
Without this feature, each such capability would need codegen to hard-code specific generated function names, which is brittle and does not scale.
Current Code To Replace
The current logging-specific code lives around:
frb_codegen/src/library/codegen/generator/wire/dart/spec_generator/misc/mod.rs
generate_execute_dart_initializers
generate_execute_dart_logging_initializer
DartLoggingInitializerFunctionNames
The goal is to remove or greatly reduce logging-specific detection from this layer.
After this feature exists, the logging macro should declare its Dart initializer through the new generic mechanism instead of relying on codegen scanning for frb_init_logger.
Suggested Implementation Plan
-
Extend #[frb(...)] attribute parsing to accept the new field.
- Relevant parser code is likely under
frb_codegen/src/library/codegen/parser/mir/parser/attribute.rs.
- Existing
dart_code support is a useful reference, but note that current dart_code is class/type extra Dart code, not init-time code.
-
Add a place in MIR/HIR to store Dart initializer snippets.
- It may live on
MirFunc if attached to functions.
- Or it may be collected at pack level if that fits existing architecture better.
-
Update Dart wire codegen.
executeRustInitializers() already has a generated section for Rust initializers.
- Add the collected Dart initializer snippets there.
- Keep ordering deterministic.
-
Migrate the logging bridge to use the new attribute.
- Remove the logging-specific function-name scan from codegen.
- Keep the generated Dart output behavior equivalent.
-
Add tests.
Acceptance Criteria
- A Rust function can be annotated with the new Dart initializer attribute.
- The generated
executeRustInitializers() contains the supplied Dart code.
- The Dart initializer has access to
api.
- Multiple Dart initializer snippets are emitted in deterministic order.
- The current Rust-to-Dart logging bridge no longer depends on codegen scanning for
frb_init_logger.
- Existing
#[frb(init)] Rust initializer behavior remains unchanged.
- Existing
#[frb(dart_code = "...")] behavior remains unchanged.
Tests To Add Or Update
At minimum:
- Attribute parser unit test for the new syntax.
- MIR/parser test showing the initializer snippet is captured.
- Dart wire generator test showing the snippet appears in
executeRustInitializers().
- Regression test for logging initializer generation after migrating logging to the generic hook.
Useful existing tests to inspect:
frb_codegen/src/library/codegen/parser/mir/parser/attribute.rs
frb_codegen/src/library/codegen/generator/wire/dart/spec_generator/misc/mod.rs
Notes
This feature is motivated by the logging bridge, but should be implemented as a general codegen capability. Avoid naming it around logging.
Background
While implementing the Rust-to-Dart logging bridge in #3079, we currently need the Dart side of the bridge to be initialized automatically from
RustLib.init().The current implementation achieves this by adding logging-specific codegen logic:
frb_init_logger,frb_logging_max_level, andfrb_logging_setup_dart_logging_output.frb_init_loggerexists, codegen injects aFrbDartLogging.init(...)call into generatedexecuteRustInitializers().This works, but it is too feature-specific. The code generator should not need to know that a particular function name means "initialize logging on the Dart side".
Desired Feature
Add a generic way for Rust-side declarations/macros to request Dart initialization code that is inserted into the generated
RustLib.init()flow.Possible syntax:
Name bikeshedding is welcome. Other possible names:
dart_init_codeinit_dart_codedart_initializerThe important part is the capability, not the exact spelling.
Expected Semantics
executeRustInitializers()includes these snippets after the existing Rust initializers.apiis available, just like the current logging-specific generated code.api.frbInitLogger(...).The generated shape should continue to look roughly like:
Why This Is Needed
The logging bridge is not the only plausible feature that may need this pattern. Any Rust-side macro that expands to FRB APIs and needs a Dart runtime helper could benefit from a generic Dart initializer hook.
Without this feature, each such capability would need codegen to hard-code specific generated function names, which is brittle and does not scale.
Current Code To Replace
The current logging-specific code lives around:
frb_codegen/src/library/codegen/generator/wire/dart/spec_generator/misc/mod.rsgenerate_execute_dart_initializersgenerate_execute_dart_logging_initializerDartLoggingInitializerFunctionNamesThe goal is to remove or greatly reduce logging-specific detection from this layer.
After this feature exists, the logging macro should declare its Dart initializer through the new generic mechanism instead of relying on codegen scanning for
frb_init_logger.Suggested Implementation Plan
Extend
#[frb(...)]attribute parsing to accept the new field.frb_codegen/src/library/codegen/parser/mir/parser/attribute.rs.dart_codesupport is a useful reference, but note that currentdart_codeis class/type extra Dart code, not init-time code.Add a place in MIR/HIR to store Dart initializer snippets.
MirFuncif attached to functions.Update Dart wire codegen.
executeRustInitializers()already has a generated section for Rust initializers.Migrate the logging bridge to use the new attribute.
Add tests.
Acceptance Criteria
executeRustInitializers()contains the supplied Dart code.api.frb_init_logger.#[frb(init)]Rust initializer behavior remains unchanged.#[frb(dart_code = "...")]behavior remains unchanged.Tests To Add Or Update
At minimum:
executeRustInitializers().Useful existing tests to inspect:
frb_codegen/src/library/codegen/parser/mir/parser/attribute.rsfrb_codegen/src/library/codegen/generator/wire/dart/spec_generator/misc/mod.rsNotes
This feature is motivated by the logging bridge, but should be implemented as a general codegen capability. Avoid naming it around logging.