-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improving op_crate API and registering ops #9738
Comments
I agree with the above, except for the I do like the idea of an easy plug and play way of adding modules to trait JsRuntimeModule {
/// This function returns JS source code to be loaded into the isolate (either at snapshotting, or at startup).
/// The function should return a vector of a tuple of the file name, and the source code.
fn init_js(&self): -> Result<Vec<(&'static string, &'static string)>, AnyError> {
// default implementation of `init_js` is to load no runtime code
Ok(vec![])
}
/// This function can set up the initial op state of an isolate at startup.
fn init_state(&self, state: &mut OpState) -> Result<(), AnyError> {
// default implementation of `init_js` is to not mutate the state
Ok(())
}
/// This function lets you middleware the op registrations. This function gets called before this module's init_ops.
fn init_op_registrar_middleware(&self, registrar: Box<dyn OpRegistrar>) -> Box<dyn OpRegistrar> {
// default implementation is to not change the registrar
registrar
}
/// This function gets called at startup to initialize the ops in the isolate.
fn init_ops(&self, registrar: &mut Box<dyn OpRegistrar>) -> Result<(), AnyError> {
// default implementation of `init_ops` is to load no runtime code
Ok(())
}
}
trait OpRegistrar {
register_minimal_op_sync(...)
register_minimal_op_async(...)
register_json_op_sync(...)
register_json_op_async(...)
} |
Having a plug-n-play interface has always been the goal - we've just been concerned about making it general enough to handle all of the corner cases (Like the FetchPermissions trait). At this point we have quite a few op crates, and I'm relatively confident that if a system is made that works with our current inventory, that it will be sufficiently general for future needs. Luca's proposal seems ok but the OpRegistrar seems to break abstractions. Ultimately all ops have the same signature - the JSON/Minimal are just wrappers on top of the ops - so a single registration function is sufficient Line 450 in 3ab4886
|
@ry I agree with what you said and was going to make the same remark regarding @lucacasonato I know the v8-bindings is a sensitive topic so I'll drop it here, it can always be revisited later with specific use-cases, but I personally think it's worth considering and in the future it can be streamlined into this It could also make sense to provide a few default implementations of Regarding op-middleware, I think it would be great to have a few solid ideas/use-cases besides
|
Fixed by #9800 |
Sharing some thoughts about improvements to op_crates, some previously shared on Discord.
Goals
op_crates
plug-and-play / self-contained, reduce complexity leaked to consumers.op_crates
can do, e.g: provide their own v8 funcs, external refs, ...Current state
op_crates
have a convenienceinit
function to execute all JS in the runtime, all ops are exported individually asop_*
funcs.So their main "API" by convention today is
::init()
+ N::op_*
Problems
In short:
op_crates
aren't plug-and-play, op-middleware is not reusable and no-access to v8 for optimisations, etc...init
is somewhat of a misnomer since it doesn't init the entire module, it only inits the JS (ops need to be registered separately)runtime/*
), leaking complexity to the consumermetrics_op
to wrap op setupTextEncoder
's encoding indexes off the heap and out of JS code, it currently consumes~500kb
per isolate between the two. If v8 bindings were allowed, it could be done via external arrays, preserving logic in JS and simply moving the arrays to rust instead of the entire implementation.heapStats()
are inherently low-level and need access to the isolate. This requires them to go intocore/
or limits consumers from adding their ownURL
and other core elements (see refactor(op_crates/web): Move URL parsing to Rust #9276 (comment) for perf gap)metrics_op
aren't really reusable by 3rd-parties and adding new op middleware is tediousImportant gotcha: js/ops/v8 elements have somewhat different lifecycles due to snapshots, which is why having only a naive
init_all()
wouldn't workSuggestions
init_js(js_runtime)
: rename the currentinit
toinit_js
init_ops(registrer, crate_specific_args)
: to register all of crate's opsregistrer
could be a Trait impl allowing composable op-middleware to hook-in and wrap ops (or filter them etc...), similar toplugin_api::Interface
metrics_op
reusable and allowing for new middleware like op-tracing, that could easily be enabled/disabledinit_v8(scope, crate_specific_args)
: to register v8 bindingsV8_EXT_REFS
: exposing a list ofv8::ExternalReference
init(args?) -> ModuleInit
: this could allow for a single external API, that would return aModuleInit
struct of optional rust callbacks (of the above functions essentially) & data (v8 ext refs).JsRuntime
to consume "modules" with a very simple API, e.g:JsRuntime
could then handle lifecycles appropriately (when snapshotting, etc...) and transparently for implementors, providing a much greater "plug-and-play" experiencebootstrap_js(js_runtime)
: for situations like core's shared queue, where some code should not be snapshottedNotes
op_crates
(as per chore: split web op crate #9635) could be great for modularity, but could increase fragmentation withoutinit_ops
and other conventions discussed abovecore
already providesinit_v8
andinit_js
conceptually,bindings::initialize_context
andJsRuntime::js_init
respectivelyURL
) should be consideredThe text was updated successfully, but these errors were encountered: