Skip to content

Commit 613ed16

Browse files
committed
offthread
Signed-off-by: sagudev <[email protected]>
1 parent d90edd1 commit 613ed16

File tree

6 files changed

+193
-4
lines changed

6 files changed

+193
-4
lines changed

Diff for: mozjs-sys/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "mozjs_sys"
33
description = "System crate for the Mozilla SpiderMonkey JavaScript engine."
44
repository.workspace = true
5-
version = "0.128.0-9"
5+
version = "0.128.0-10"
66
authors = ["Mozilla"]
77
links = "mozjs"
88
build = "build.rs"

Diff for: mozjs-sys/src/jsapi.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "js/Utility.h"
4141
#include "js/Warnings.h"
4242
#include "js/WasmModule.h"
43+
#include "js/experimental/CompileScript.h"
4344
#include "js/experimental/JSStencil.h"
4445
#include "js/experimental/JitInfo.h"
4546
#include "js/experimental/TypedData.h"
@@ -68,6 +69,16 @@ JS::OwningCompileOptions* JS_NewOwningCompileOptions(JSContext* cx) {
6869
return result;
6970
}
7071

72+
JS::OwningCompileOptions* OwningCompileOptions_for_fc(
73+
JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& rhs) {
74+
JS::OwningCompileOptions* oco = new JS::OwningCompileOptions(
75+
JS::OwningCompileOptions::ForFrontendContext());
76+
if (!oco->copy(fc, rhs)) {
77+
return nullptr;
78+
}
79+
return oco;
80+
}
81+
7182
void DeleteOwningCompileOptions(JS::OwningCompileOptions* opts) { delete opts; }
7283

7384
JS::shadow::Zone* JS_AsShadowZone(JS::Zone* zone) {

Diff for: mozjs/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ pub mod error;
4949
pub mod gc;
5050
pub mod panic;
5151
pub mod typedarray;
52+
pub mod offthread;
5253

5354
pub use crate::consts::*;
5455
pub use mozjs_sys::glue;

Diff for: mozjs/src/offthread.rs

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
use crate::jsapi::JS::{
6+
CompileGlobalScriptToStencil2, DestroyFrontendContext, FrontendContext as RawFrontendContext,
7+
NewFrontendContext, ReadOnlyCompileOptions,
8+
};
9+
use crate::rust::{transform_str_to_source_text, OwningCompileOptionsWrapper, Stencil};
10+
use std::ops::Deref;
11+
use std::sync::Arc;
12+
use std::thread::{self, JoinHandle};
13+
14+
pub struct FrontendContext(*mut RawFrontendContext);
15+
16+
unsafe impl Send for FrontendContext {}
17+
18+
impl FrontendContext {
19+
pub fn new() -> Self {
20+
Self(unsafe { NewFrontendContext() })
21+
}
22+
}
23+
24+
impl Deref for FrontendContext {
25+
type Target = *mut RawFrontendContext;
26+
fn deref(&self) -> &Self::Target {
27+
&self.0
28+
}
29+
}
30+
31+
impl Drop for FrontendContext {
32+
fn drop(&mut self) {
33+
unsafe { DestroyFrontendContext(self.0) }
34+
}
35+
}
36+
37+
pub struct OffThreadToken(JoinHandle<Option<Stencil>>);
38+
39+
impl OffThreadToken {
40+
/// Obtains result
41+
///
42+
/// Blocks until completion
43+
pub fn finish(self) -> Option<Stencil> {
44+
self.0.join().ok().flatten()
45+
}
46+
}
47+
48+
/// Creates a new thread and starts compilation there
49+
///
50+
/// Callback receives stencil that can either consumed or returned
51+
pub fn compile_to_stencil_offthread<F>(
52+
options: *const ReadOnlyCompileOptions,
53+
source: Arc<String>,
54+
callback: F,
55+
) -> OffThreadToken
56+
where
57+
F: FnOnce(Stencil) -> Option<Stencil> + Send + 'static,
58+
{
59+
let fc = FrontendContext::new();
60+
let options = OwningCompileOptionsWrapper::new_for_fc(&fc, options);
61+
OffThreadToken(
62+
thread::Builder::new()
63+
.name("OffThread Compile".to_string())
64+
.spawn(move || {
65+
callback(unsafe {
66+
Stencil::from_raw(CompileGlobalScriptToStencil2(
67+
*fc,
68+
options.read_only(),
69+
&mut transform_str_to_source_text(&source) as *mut _,
70+
))
71+
})
72+
})
73+
.unwrap(),
74+
)
75+
}

Diff for: mozjs/src/rust.rs

+35-3
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,13 @@ use crate::jsapi::HandleObjectVector as RawHandleObjectVector;
4141
use crate::jsapi::HandleValue as RawHandleValue;
4242
use crate::jsapi::JS_AddExtraGCRootsTracer;
4343
use crate::jsapi::MutableHandleIdVector as RawMutableHandleIdVector;
44+
use crate::jsapi::OwningCompileOptions_for_fc;
4445
use crate::jsapi::{already_AddRefed, jsid};
4546
use crate::jsapi::{BuildStackString, CaptureCurrentStack, StackFormat};
47+
use crate::jsapi::{
48+
DeleteOwningCompileOptions, OwningCompileOptions, PersistentRootedObjectVector,
49+
ReadOnlyCompileOptions, RootingContext,
50+
};
4651
use crate::jsapi::{Evaluate2, HandleValueArray, StencilRelease};
4752
use crate::jsapi::{InitSelfHostedCode, IsWindowSlow};
4853
use crate::jsapi::{
@@ -56,11 +61,11 @@ use crate::jsapi::{JS_DefineFunctions, JS_DefineProperties, JS_DestroyContext, J
5661
use crate::jsapi::{JS_EnumerateStandardClasses, JS_GetRuntime, JS_GlobalObjectTraceHook};
5762
use crate::jsapi::{JS_MayResolveStandardClass, JS_NewContext, JS_ResolveStandardClass};
5863
use crate::jsapi::{JS_StackCapture_AllFrames, JS_StackCapture_MaxFrames};
59-
use crate::jsapi::{PersistentRootedObjectVector, ReadOnlyCompileOptions, RootingContext};
6064
use crate::jsapi::{SetWarningReporter, SourceText, ToBooleanSlow};
6165
use crate::jsapi::{ToInt32Slow, ToInt64Slow, ToNumberSlow, ToStringSlow, ToUint16Slow};
6266
use crate::jsapi::{ToUint32Slow, ToUint64Slow, ToWindowProxyIfWindowSlow};
6367
use crate::jsval::ObjectValue;
68+
use crate::offthread::FrontendContext;
6469
use crate::panic::maybe_resume_unwind;
6570
use lazy_static::lazy_static;
6671
use log::{debug, warn};
@@ -470,6 +475,30 @@ impl Drop for RootedObjectVectorWrapper {
470475
}
471476
}
472477

478+
pub struct OwningCompileOptionsWrapper {
479+
pub ptr: *mut OwningCompileOptions,
480+
}
481+
482+
impl OwningCompileOptionsWrapper {
483+
pub fn new_for_fc(fc: &FrontendContext, options: *const ReadOnlyCompileOptions) -> Self {
484+
Self {
485+
ptr: unsafe { OwningCompileOptions_for_fc(**fc, options) },
486+
}
487+
}
488+
489+
pub fn read_only(&self) -> &ReadOnlyCompileOptions {
490+
unsafe { &(*self.ptr)._base }
491+
}
492+
}
493+
494+
unsafe impl Send for OwningCompileOptionsWrapper {}
495+
496+
impl Drop for OwningCompileOptionsWrapper {
497+
fn drop(&mut self) {
498+
unsafe { DeleteOwningCompileOptions(self.ptr) }
499+
}
500+
}
501+
473502
pub struct CompileOptionsWrapper {
474503
pub ptr: *mut ReadOnlyCompileOptions,
475504
}
@@ -493,8 +522,7 @@ pub struct Stencil {
493522
inner: already_AddRefed<CompilationStencil>,
494523
}
495524

496-
/*unsafe impl Send for Stencil {}
497-
unsafe impl Sync for Stencil {}*/
525+
unsafe impl Send for Stencil {}
498526

499527
impl Drop for Stencil {
500528
fn drop(&mut self) {
@@ -519,6 +547,10 @@ impl Stencil {
519547
pub fn is_null(&self) -> bool {
520548
self.inner.mRawPtr.is_null()
521549
}
550+
551+
pub unsafe fn from_raw(inner: already_AddRefed<CompilationStencil>) -> Self {
552+
Self { inner }
553+
}
522554
}
523555

524556
// ___________________________________________________________________________

Diff for: mozjs/tests/offthread.rs

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
use std::ptr;
6+
use std::sync::mpsc::channel;
7+
use std::sync::Arc;
8+
9+
use mozjs::jsapi::{
10+
InstantiateGlobalStencil, InstantiateOptions, JSAutoRealm, JS_NewGlobalObject,
11+
OnNewGlobalHookOption,
12+
};
13+
use mozjs::jsval::UndefinedValue;
14+
use mozjs::offthread::compile_to_stencil_offthread;
15+
use mozjs::rooted;
16+
use mozjs::rust::{
17+
wrappers::JS_ExecuteScript, CompileOptionsWrapper, JSEngine, RealmOptions, Runtime,
18+
SIMPLE_GLOBAL_CLASS,
19+
};
20+
21+
#[test]
22+
fn offthread() {
23+
let engine = JSEngine::init().unwrap();
24+
let runtime = Runtime::new(engine.handle());
25+
let context = runtime.cx();
26+
let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;
27+
let c_option = RealmOptions::default();
28+
29+
unsafe {
30+
rooted!(in(context) let global = JS_NewGlobalObject(
31+
context,
32+
&SIMPLE_GLOBAL_CLASS,
33+
ptr::null_mut(),
34+
h_option,
35+
&*c_option,
36+
));
37+
38+
let _ac = JSAutoRealm::new(context, global.get());
39+
40+
let src = Arc::new("1 + 1".to_string());
41+
let options = CompileOptionsWrapper::new(context, "", 1);
42+
let options_ptr = options.ptr as *const _;
43+
let (sender, receiver) = channel();
44+
let offthread_token = compile_to_stencil_offthread(options_ptr, src, move |stencil| {
45+
sender.send(stencil).unwrap();
46+
None
47+
});
48+
49+
let stencil = receiver.recv().unwrap();
50+
51+
assert!(offthread_token.finish().is_none());
52+
53+
let options = InstantiateOptions {
54+
skipFilenameValidation: false,
55+
hideScriptFromDebugger: false,
56+
deferDebugMetadata: false,
57+
};
58+
rooted!(in(context) let script = InstantiateGlobalStencil(
59+
context,
60+
&options,
61+
*stencil,
62+
ptr::null_mut(),
63+
));
64+
65+
rooted!(in(context) let mut rval = UndefinedValue());
66+
let result = JS_ExecuteScript(context, script.handle(), rval.handle_mut());
67+
assert!(result);
68+
assert_eq!(rval.get().to_int32(), 2);
69+
}
70+
}

0 commit comments

Comments
 (0)