Skip to content
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

Support for global template and global object #9

Merged
merged 1 commit into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion core/00_primordials.js
Original file line number Diff line number Diff line change
Expand Up @@ -611,5 +611,6 @@
ObjectFreeze(primordials);

// Provide bootstrap namespace
globalThis.__bootstrap = { primordials };
globalThis.__bootstrap ??= {};
globalThis.__bootstrap.primordials = primordials;
})();
106 changes: 90 additions & 16 deletions core/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::path::PathBuf;
use std::rc::Rc;
use std::task::Context;
use v8::fast_api::FastFunction;
use v8::ExternalReference;

#[derive(Clone, Debug)]
pub enum ExtensionFileSourceCode {
Expand Down Expand Up @@ -63,13 +64,20 @@ impl ExtensionFileSource {
pub type OpFnRef = v8::FunctionCallback;
pub type OpMiddlewareFn = dyn Fn(OpDecl) -> OpDecl;
pub type OpStateFn = dyn FnOnce(&mut OpState);
pub type OpEventLoopFn = dyn Fn(Rc<RefCell<OpState>>, &mut Context) -> bool;

/// Trait implemented by all generated ops.
pub trait Op {
const NAME: &'static str;
const DECL: OpDecl;
}
pub type EventLoopMiddlewareFn =
dyn Fn(Rc<RefCell<OpState>>, &mut Context) -> bool;
pub type GlobalTemplateMiddlewareFn =
dyn for<'s> Fn(
&mut v8::HandleScope<'s, ()>,
v8::Local<'s, v8::ObjectTemplate>,
) -> v8::Local<'s, v8::ObjectTemplate>;
pub type GlobalObjectMiddlewareFn =
dyn for<'s> Fn(&mut v8::HandleScope<'s>, v8::Local<'s, v8::Object>);

pub struct OpDecl {
pub name: &'static str,
Expand Down Expand Up @@ -179,6 +187,8 @@ macro_rules! ops {
/// * middleware: an [`OpDecl`] middleware function with the signature `fn (OpDecl) -> OpDecl`
/// * state: a state initialization function, with the signature `fn (&mut OpState, ...) -> ()`, where `...` are parameters matching the fields of the config struct
/// * event_loop_middleware: an event-loop middleware function (see [`ExtensionBuilder::event_loop_middleware`])
/// * global_template_middleware: a global template middleware function (see [`ExtensionBuilder::global_template_middleware`])
/// * global_object_middleware: a global object middleware function (see [`ExtensionBuilder::global_object_middleware`])
#[macro_export]
macro_rules! extension {
(
Expand All @@ -195,7 +205,10 @@ macro_rules! extension {
$(, options = { $( $options_id:ident : $options_type:ty ),* $(,)? } )?
$(, middleware = $middleware_fn:expr )?
$(, state = $state_fn:expr )?
$(, event_loop_middleware = $event_loop_middleware_fn:ident )?
$(, event_loop_middleware = $event_loop_middleware_fn:expr )?
$(, global_template_middleware = $global_template_middleware_fn:expr )?
$(, global_object_middleware = $global_object_middleware_fn:expr )?
$(, external_references = [ $( $external_reference:expr ),* $(,)? ] )?
$(, customizer = $customizer_fn:expr )?
$(,)?
) => {
Expand Down Expand Up @@ -262,6 +275,18 @@ macro_rules! extension {
ext.event_loop_middleware($event_loop_middleware_fn);
)?

$(
ext.global_template_middleware($global_template_middleware_fn);
)?

$(
ext.global_object_middleware($global_object_middleware_fn);
)?

$(
ext.external_references(vec![ $( $external_reference ),* ]);
)?

$(
ext.middleware($middleware_fn);
)?
Expand Down Expand Up @@ -357,7 +382,10 @@ pub struct Extension {
ops: Option<Vec<OpDecl>>,
opstate_fn: Option<Box<OpStateFn>>,
middleware_fn: Option<Box<OpMiddlewareFn>>,
event_loop_middleware: Option<Box<OpEventLoopFn>>,
event_loop_middleware: Option<Box<EventLoopMiddlewareFn>>,
global_template_middleware: Option<Box<GlobalTemplateMiddlewareFn>>,
global_object_middleware: Option<Box<GlobalObjectMiddlewareFn>>,
external_references: Option<Vec<v8::ExternalReference<'static>>>,
initialized: bool,
enabled: bool,
deps: Option<&'static [&'static str]>,
Expand Down Expand Up @@ -446,20 +474,28 @@ impl Extension {
self.middleware_fn.take()
}

pub fn init_event_loop_middleware(&mut self) -> Option<Box<OpEventLoopFn>> {
pub fn init_event_loop_middleware(
&mut self,
) -> Option<Box<EventLoopMiddlewareFn>> {
self.event_loop_middleware.take()
}

pub fn run_event_loop_middleware(
&self,
op_state_rc: Rc<RefCell<OpState>>,
cx: &mut Context,
) -> bool {
self
.event_loop_middleware
.as_ref()
.map(|f| f(op_state_rc, cx))
.unwrap_or(false)
pub fn init_global_template_middleware(
&mut self,
) -> Option<Box<GlobalTemplateMiddlewareFn>> {
self.global_template_middleware.take()
}

pub fn init_global_object_middleware(
&mut self,
) -> Option<Box<GlobalObjectMiddlewareFn>> {
self.global_object_middleware.take()
}

pub fn init_external_references(
&mut self,
) -> Option<Vec<v8::ExternalReference<'static>>> {
self.external_references.take()
}

pub fn enabled(self, enabled: bool) -> Self {
Expand All @@ -480,7 +516,10 @@ pub struct ExtensionBuilder {
ops: Vec<OpDecl>,
state: Option<Box<OpStateFn>>,
middleware: Option<Box<OpMiddlewareFn>>,
event_loop_middleware: Option<Box<OpEventLoopFn>>,
event_loop_middleware: Option<Box<EventLoopMiddlewareFn>>,
global_template_middleware: Option<Box<GlobalTemplateMiddlewareFn>>,
global_object_middleware: Option<Box<GlobalObjectMiddlewareFn>>,
external_references: Option<Vec<ExternalReference<'static>>>,
name: &'static str,
deps: &'static [&'static str],
}
Expand Down Expand Up @@ -530,6 +569,35 @@ impl ExtensionBuilder {
self
}

pub fn global_template_middleware<F>(&mut self, middleware_fn: F) -> &mut Self
where
F: for<'s> Fn(
&mut v8::HandleScope<'s, ()>,
v8::Local<'s, v8::ObjectTemplate>,
) -> v8::Local<'s, v8::ObjectTemplate>
+ 'static,
{
self.global_template_middleware = Some(Box::new(middleware_fn));
self
}

pub fn global_object_middleware<F>(&mut self, middleware_fn: F) -> &mut Self
where
F:
for<'s> Fn(&mut v8::HandleScope<'s>, v8::Local<'s, v8::Object>) + 'static,
{
self.global_object_middleware = Some(Box::new(middleware_fn));
self
}

pub fn external_references(
&mut self,
external_references: Vec<ExternalReference<'static>>,
) -> &mut Self {
self.external_references = Some(external_references);
self
}

/// Consume the [`ExtensionBuilder`] and return an [`Extension`].
pub fn take(self) -> Extension {
let ops = Some(self.ops);
Expand All @@ -542,6 +610,9 @@ impl ExtensionBuilder {
opstate_fn: self.state,
middleware_fn: self.middleware,
event_loop_middleware: self.event_loop_middleware,
global_template_middleware: self.global_template_middleware,
global_object_middleware: self.global_object_middleware,
external_references: self.external_references,
initialized: false,
enabled: true,
name: self.name,
Expand All @@ -560,6 +631,9 @@ impl ExtensionBuilder {
opstate_fn: self.state.take(),
middleware_fn: self.middleware.take(),
event_loop_middleware: self.event_loop_middleware.take(),
global_template_middleware: self.global_template_middleware.take(),
global_object_middleware: self.global_object_middleware.take(),
external_references: self.external_references.take(),
initialized: false,
enabled: true,
name: self.name,
Expand Down
12 changes: 9 additions & 3 deletions core/runtime/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ use crate::runtime::InitMode;
use crate::JsRealm;
use crate::JsRuntime;

pub(crate) fn external_references(ops: &[OpCtx]) -> v8::ExternalReferences {
pub(crate) fn external_references(
ops: &[OpCtx],
additional_references: &[v8::ExternalReference],
) -> v8::ExternalReferences {
// Overallocate a bit, it's better than having to resize the vector.
let mut references = Vec::with_capacity(4 + ops.len() * 4);
let mut references =
Vec::with_capacity(4 + (ops.len() * 4) + additional_references.len());

references.push(v8::ExternalReference {
function: call_console.map_fn_to(),
Expand Down Expand Up @@ -53,6 +57,8 @@ pub(crate) fn external_references(ops: &[OpCtx]) -> v8::ExternalReferences {
}
}

references.extend_from_slice(additional_references);

let refs = v8::ExternalReferences::new(&references);
// Leak, V8 takes ownership of the references.
std::mem::forget(references);
Expand Down Expand Up @@ -369,7 +375,7 @@ fn empty_fn(
//Do Nothing
}

//It creates a reference to an empty function which can be maintained after the snapshots
//It creates a reference to an empty function which can be mantained after the snapshots
pub fn create_empty_fn<'s>(
scope: &mut v8::HandleScope<'s>,
) -> Option<v8::Local<'s, v8::Function>> {
Expand Down
84 changes: 77 additions & 7 deletions core/runtime/jsruntime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ use crate::error::generic_error;
use crate::error::to_v8_type_error;
use crate::error::GetErrorClassFn;
use crate::error::JsError;
use crate::extensions::EventLoopMiddlewareFn;
use crate::extensions::GlobalObjectMiddlewareFn;
use crate::extensions::GlobalTemplateMiddlewareFn;
use crate::extensions::OpDecl;
use crate::extensions::OpEventLoopFn;
use crate::include_js_files;
use crate::inspector::JsRuntimeInspector;
use crate::module_specifier::ModuleSpecifier;
Expand Down Expand Up @@ -221,9 +223,11 @@ pub struct JsRuntime {
pub(crate) module_map: Rc<RefCell<ModuleMap>>,
pub(crate) allocations: IsolateAllocations,
extensions: Vec<Extension>,
event_loop_middlewares: Vec<Box<OpEventLoopFn>>,
event_loop_middlewares: Vec<Box<EventLoopMiddlewareFn>>,
global_template_middlewares: Vec<Box<GlobalTemplateMiddlewareFn>>,
global_object_middlewares: Vec<Box<GlobalObjectMiddlewareFn>>,
init_mode: InitMode,
// Marks if this is considered the top-level runtime. Used only be inspector.
// Marks if this is considered the top-level runtime. Used only by inspector.
is_main: bool,
}

Expand Down Expand Up @@ -467,6 +471,13 @@ impl JsRuntime {
state
}

/// Returns the `OpState` associated with the passed `Isolate`.
pub fn op_state_from(isolate: &v8::Isolate) -> Rc<RefCell<OpState>> {
let state = Self::state_from(isolate);
let state = state.borrow();
state.op_state.clone()
}

pub(crate) fn module_map_from(
isolate: &v8::Isolate,
) -> Rc<RefCell<ModuleMap>> {
Expand Down Expand Up @@ -520,13 +531,29 @@ impl JsRuntime {
let (op_state, ops) = Self::create_opstate(&mut options);
let op_state = Rc::new(RefCell::new(op_state));

// Collect event-loop middleware
// Collect event-loop middleware, global template middleware, global object
// middleware, and additional ExternalReferences from extensions.
let mut event_loop_middlewares =
Vec::with_capacity(options.extensions.len());
let mut global_template_middlewares =
Vec::with_capacity(options.extensions.len());
let mut global_object_middlewares =
Vec::with_capacity(options.extensions.len());
let mut additional_references =
Vec::with_capacity(options.extensions.len());
for extension in &mut options.extensions {
if let Some(middleware) = extension.init_event_loop_middleware() {
event_loop_middlewares.push(middleware);
}
if let Some(middleware) = extension.init_global_template_middleware() {
global_template_middlewares.push(middleware);
}
if let Some(middleware) = extension.init_global_object_middleware() {
global_object_middlewares.push(middleware);
}
if let Some(ext_refs) = extension.init_external_references() {
additional_references.extend_from_slice(&ext_refs);
}
}

let align = std::mem::align_of::<usize>();
Expand Down Expand Up @@ -576,7 +603,10 @@ impl JsRuntime {
context_state.borrow_mut().op_ctxs = op_ctxs;
context_state.borrow_mut().isolate = Some(isolate_ptr);

let refs = bindings::external_references(&context_state.borrow().op_ctxs);
let refs = bindings::external_references(
&context_state.borrow().op_ctxs,
&additional_references,
);
// V8 takes ownership of external_references.
let refs: &'static v8::ExternalReferences = Box::leak(Box::new(refs));

Expand Down Expand Up @@ -618,7 +648,12 @@ impl JsRuntime {

let (global_context, snapshotted_data) = {
let scope = &mut v8::HandleScope::new(&mut isolate);
let context = v8::Context::new(scope);

let context = create_context(
scope,
&global_template_middlewares,
&global_object_middlewares,
);

// Get module map data from the snapshot
let snapshotted_data = if init_mode == InitMode::FromSnapshot {
Expand Down Expand Up @@ -699,6 +734,8 @@ impl JsRuntime {
init_mode,
allocations: IsolateAllocations::default(),
event_loop_middlewares,
global_template_middlewares,
global_object_middlewares,
extensions: options.extensions,
module_map: module_map_rc,
is_main: options.is_main,
Expand Down Expand Up @@ -798,7 +835,13 @@ impl JsRuntime {
// access to the isolate, and nothing else we're accessing from self does.
let isolate = unsafe { raw_ptr.as_mut() }.unwrap();
let scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(scope);

let context = create_context(
scope,
&self.global_template_middlewares,
&self.global_object_middlewares,
);

let scope = &mut v8::ContextScope::new(scope, context);

let context = bindings::initialize_context(
Expand Down Expand Up @@ -1481,6 +1524,33 @@ impl JsRuntime {
}
}

fn create_context<'a>(
scope: &mut v8::HandleScope<'a, ()>,
global_template_middlewares: &[Box<GlobalTemplateMiddlewareFn>],
global_object_middlewares: &[Box<GlobalObjectMiddlewareFn>],
) -> v8::Local<'a, v8::Context> {
// Set up the global object template and create context from it.
let mut global_object_template = v8::ObjectTemplate::new(scope);
for middleware in global_template_middlewares {
global_object_template = middleware(scope, global_object_template);
}
let context = v8::Context::new_from_template(scope, global_object_template);
let scope = &mut v8::ContextScope::new(scope, context);

// Get the global wrapper object from the context, get the real inner
// global object from it, and and configure it using the middlewares.
let global_wrapper = context.global(scope);
let real_global = global_wrapper
.get_prototype(scope)
.unwrap()
.to_object(scope)
.unwrap();
for middleware in global_object_middlewares {
middleware(scope, real_global);
}
context
}

impl JsRuntimeForSnapshot {
pub fn new(
mut options: RuntimeOptions,
Expand Down