Skip to content

Commit

Permalink
offthread
Browse files Browse the repository at this point in the history
Signed-off-by: sagudev <[email protected]>
  • Loading branch information
sagudev committed Aug 30, 2024
1 parent d90edd1 commit fe80450
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 4 deletions.
2 changes: 1 addition & 1 deletion mozjs-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "mozjs_sys"
description = "System crate for the Mozilla SpiderMonkey JavaScript engine."
repository.workspace = true
version = "0.128.0-9"
version = "0.128.0-10"
authors = ["Mozilla"]
links = "mozjs"
build = "build.rs"
Expand Down
11 changes: 11 additions & 0 deletions mozjs-sys/src/jsapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "js/Utility.h"
#include "js/Warnings.h"
#include "js/WasmModule.h"
#include "js/experimental/CompileScript.h"
#include "js/experimental/JSStencil.h"
#include "js/experimental/JitInfo.h"
#include "js/experimental/TypedData.h"
Expand Down Expand Up @@ -68,6 +69,16 @@ JS::OwningCompileOptions* JS_NewOwningCompileOptions(JSContext* cx) {
return result;
}

JS::OwningCompileOptions* OwningCompileOptions_for_fc(
JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& rhs) {
JS::OwningCompileOptions* oco = new JS::OwningCompileOptions(
JS::OwningCompileOptions::ForFrontendContext());
if (!oco->copy(fc, rhs)) {
return nullptr;
}
return oco;
}

void DeleteOwningCompileOptions(JS::OwningCompileOptions* opts) { delete opts; }

JS::shadow::Zone* JS_AsShadowZone(JS::Zone* zone) {
Expand Down
1 change: 1 addition & 0 deletions mozjs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ mod consts;
pub mod conversions;
pub mod error;
pub mod gc;
pub mod offthread;
pub mod panic;
pub mod typedarray;

Expand Down
75 changes: 75 additions & 0 deletions mozjs/src/offthread.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use crate::jsapi::JS::{
CompileGlobalScriptToStencil2, DestroyFrontendContext, FrontendContext as RawFrontendContext,
NewFrontendContext, ReadOnlyCompileOptions,
};
use crate::rust::{transform_str_to_source_text, OwningCompileOptionsWrapper, Stencil};
use std::ops::Deref;
use std::sync::Arc;
use std::thread::{self, JoinHandle};

pub struct FrontendContext(*mut RawFrontendContext);

unsafe impl Send for FrontendContext {}

impl FrontendContext {
pub fn new() -> Self {
Self(unsafe { NewFrontendContext() })
}
}

impl Deref for FrontendContext {
type Target = *mut RawFrontendContext;
fn deref(&self) -> &Self::Target {
&self.0
}
}

impl Drop for FrontendContext {
fn drop(&mut self) {
unsafe { DestroyFrontendContext(self.0) }
}
}

pub struct OffThreadToken(JoinHandle<Option<Stencil>>);

impl OffThreadToken {
/// Obtains result
///
/// Blocks until completion
pub fn finish(self) -> Option<Stencil> {
self.0.join().ok().flatten()
}
}

/// Creates a new thread and starts compilation there
///
/// Callback receives stencil that can either consumed or returned
pub fn compile_to_stencil_offthread<F>(
options: *const ReadOnlyCompileOptions,
source: Arc<String>,
callback: F,
) -> OffThreadToken
where
F: FnOnce(Stencil) -> Option<Stencil> + Send + 'static,
{
let fc = FrontendContext::new();
let options = OwningCompileOptionsWrapper::new_for_fc(&fc, options);
OffThreadToken(
thread::Builder::new()
.name("OffThread Compile".to_string())
.spawn(move || {
callback(unsafe {
Stencil::from_raw(CompileGlobalScriptToStencil2(
*fc,
options.read_only(),
&mut transform_str_to_source_text(&source) as *mut _,
))
})
})
.unwrap(),
)
}
38 changes: 35 additions & 3 deletions mozjs/src/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,13 @@ use crate::jsapi::HandleObjectVector as RawHandleObjectVector;
use crate::jsapi::HandleValue as RawHandleValue;
use crate::jsapi::JS_AddExtraGCRootsTracer;
use crate::jsapi::MutableHandleIdVector as RawMutableHandleIdVector;
use crate::jsapi::OwningCompileOptions_for_fc;
use crate::jsapi::{already_AddRefed, jsid};
use crate::jsapi::{BuildStackString, CaptureCurrentStack, StackFormat};
use crate::jsapi::{
DeleteOwningCompileOptions, OwningCompileOptions, PersistentRootedObjectVector,
ReadOnlyCompileOptions, RootingContext,
};
use crate::jsapi::{Evaluate2, HandleValueArray, StencilRelease};
use crate::jsapi::{InitSelfHostedCode, IsWindowSlow};
use crate::jsapi::{
Expand All @@ -56,11 +61,11 @@ use crate::jsapi::{JS_DefineFunctions, JS_DefineProperties, JS_DestroyContext, J
use crate::jsapi::{JS_EnumerateStandardClasses, JS_GetRuntime, JS_GlobalObjectTraceHook};
use crate::jsapi::{JS_MayResolveStandardClass, JS_NewContext, JS_ResolveStandardClass};
use crate::jsapi::{JS_StackCapture_AllFrames, JS_StackCapture_MaxFrames};
use crate::jsapi::{PersistentRootedObjectVector, ReadOnlyCompileOptions, RootingContext};
use crate::jsapi::{SetWarningReporter, SourceText, ToBooleanSlow};
use crate::jsapi::{ToInt32Slow, ToInt64Slow, ToNumberSlow, ToStringSlow, ToUint16Slow};
use crate::jsapi::{ToUint32Slow, ToUint64Slow, ToWindowProxyIfWindowSlow};
use crate::jsval::ObjectValue;
use crate::offthread::FrontendContext;
use crate::panic::maybe_resume_unwind;
use lazy_static::lazy_static;
use log::{debug, warn};
Expand Down Expand Up @@ -470,6 +475,30 @@ impl Drop for RootedObjectVectorWrapper {
}
}

pub struct OwningCompileOptionsWrapper {
pub ptr: *mut OwningCompileOptions,
}

impl OwningCompileOptionsWrapper {
pub fn new_for_fc(fc: &FrontendContext, options: *const ReadOnlyCompileOptions) -> Self {
Self {
ptr: unsafe { OwningCompileOptions_for_fc(**fc, options) },
}
}

pub fn read_only(&self) -> &ReadOnlyCompileOptions {
unsafe { &(*self.ptr)._base }
}
}

unsafe impl Send for OwningCompileOptionsWrapper {}

impl Drop for OwningCompileOptionsWrapper {
fn drop(&mut self) {
unsafe { DeleteOwningCompileOptions(self.ptr) }
}
}

pub struct CompileOptionsWrapper {
pub ptr: *mut ReadOnlyCompileOptions,
}
Expand All @@ -493,8 +522,7 @@ pub struct Stencil {
inner: already_AddRefed<CompilationStencil>,
}

/*unsafe impl Send for Stencil {}
unsafe impl Sync for Stencil {}*/
unsafe impl Send for Stencil {}

impl Drop for Stencil {
fn drop(&mut self) {
Expand All @@ -519,6 +547,10 @@ impl Stencil {
pub fn is_null(&self) -> bool {
self.inner.mRawPtr.is_null()
}

pub unsafe fn from_raw(inner: already_AddRefed<CompilationStencil>) -> Self {
Self { inner }
}
}

// ___________________________________________________________________________
Expand Down
70 changes: 70 additions & 0 deletions mozjs/tests/offthread.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::ptr;
use std::sync::mpsc::channel;
use std::sync::Arc;

use mozjs::jsapi::{
InstantiateGlobalStencil, InstantiateOptions, JSAutoRealm, JS_NewGlobalObject,
OnNewGlobalHookOption,
};
use mozjs::jsval::UndefinedValue;
use mozjs::offthread::compile_to_stencil_offthread;
use mozjs::rooted;
use mozjs::rust::{
wrappers::JS_ExecuteScript, CompileOptionsWrapper, JSEngine, RealmOptions, Runtime,
SIMPLE_GLOBAL_CLASS,
};

#[test]
fn offthread() {
let engine = JSEngine::init().unwrap();
let runtime = Runtime::new(engine.handle());
let context = runtime.cx();
let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;
let c_option = RealmOptions::default();

unsafe {
rooted!(in(context) let global = JS_NewGlobalObject(
context,
&SIMPLE_GLOBAL_CLASS,
ptr::null_mut(),
h_option,
&*c_option,
));

let _ac = JSAutoRealm::new(context, global.get());

let src = Arc::new("1 + 1".to_string());
let options = CompileOptionsWrapper::new(context, "", 1);
let options_ptr = options.ptr as *const _;
let (sender, receiver) = channel();
let offthread_token = compile_to_stencil_offthread(options_ptr, src, move |stencil| {
sender.send(stencil).unwrap();
None
});

let stencil = receiver.recv().unwrap();

assert!(offthread_token.finish().is_none());

let options = InstantiateOptions {
skipFilenameValidation: false,
hideScriptFromDebugger: false,
deferDebugMetadata: false,
};
rooted!(in(context) let script = InstantiateGlobalStencil(
context,
&options,
*stencil,
ptr::null_mut(),
));

rooted!(in(context) let mut rval = UndefinedValue());
let result = JS_ExecuteScript(context, script.handle(), rval.handle_mut());
assert!(result);
assert_eq!(rval.get().to_int32(), 2);
}
}

0 comments on commit fe80450

Please sign in to comment.