Skip to content
Closed
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
45 changes: 45 additions & 0 deletions src/context/ffi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use super::internal::{ContextInternal, Env, Scope, ScopeMetadata};
use super::Context;
use neon_runtime::raw;

/// Opaque type representing the underlying env of a context
///
/// See `Context::with_raw_env` for details.
#[repr(C)]
pub struct RawEnv {
// Create opaque type as suggested in https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
_data: [u8; 0],
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}

impl RawEnv {
/// Creates a context in the env
pub fn with_context<T, F>(&mut self, f: F) -> T
where
F: for<'b> FnOnce(RawContext<'b>) -> T,
{
let env = unsafe { std::mem::transmute(self) };
RawContext::with(env, f)
}
}

pub struct RawContext<'a> {
scope: Scope<'a, raw::HandleScope>,
}

impl<'a> ContextInternal<'a> for RawContext<'a> {
fn scope_metadata(&self) -> &ScopeMetadata {
&self.scope.metadata
}
}

impl<'a> Context<'a> for RawContext<'a> {}

impl<'a> RawContext<'a> {
fn with<T, F>(env: Env, f: F) -> T
where
F: for<'b> FnOnce(RawContext<'b>) -> T,
{
Scope::with(env, |scope| f(RawContext { scope }))
}
}
26 changes: 26 additions & 0 deletions src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@
//! [iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
//! [question-mark]: https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/the-question-mark-operator-for-easier-error-handling.html

#[cfg(feature = "napi-1")]
pub mod ffi;
pub(crate) mod internal;

use crate::borrow::internal::Ledger;
Expand Down Expand Up @@ -181,6 +183,8 @@ use std::marker::PhantomData;
use std::os::raw::c_void;
use std::panic::UnwindSafe;

#[cfg(feature = "napi-1")]
use self::ffi::RawEnv;
use self::internal::{ContextInternal, Scope, ScopeMetadata};

#[repr(C)]
Expand Down Expand Up @@ -294,6 +298,28 @@ impl<'a> Lock<'a> {
///
/// A context has a lifetime `'a`, which ensures the safety of handles managed by the JS garbage collector. All handles created during the lifetime of a context are kept alive for that duration and cannot outlive the context.
pub trait Context<'a>: ContextInternal<'a> {
/// Gets the underlying env of the context.
///
/// This method is useful for creating contexts across FFI boundaries.
///
/// # Safety
/// `&mut RawEnv` can be converted to `*mut void`, passed across FFI boundaries, and then
/// converted back to `&mut RawEnv`.
/// Mutable aliasing is allowed because `RawEnv` is zero-sized (https://rust-lang.github.io/unsafe-code-guidelines/glossary.html),
/// but callers are still responsible for upholding other borrowing rules.
#[cfg(feature = "napi-1")]
fn with_raw_env<T, F>(&mut self, f: F) -> T
where
F: for<'b> FnOnce(&'b mut RawEnv) -> T,
{
self.check_active();
self.deactivate();
let raw_env = unsafe { &mut *(self.env().to_raw().cast::<RawEnv>()) };
let result = f(raw_env);
self.activate();
result
}

/// Lock the JavaScript engine, returning an RAII guard that keeps the lock active as long as the guard is alive.
///
/// If this is not the currently active context (for example, if it was used to spawn a scoped context with `execute_scoped` or `compute_scoped`), this method will panic.
Expand Down
13 changes: 13 additions & 0 deletions test/napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ use js::objects::*;
use js::strings::*;
use js::threads::*;
use js::types::*;
use neon::context::ffi::RawEnv;
use std::os::raw::c_void;

#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
Expand Down Expand Up @@ -107,6 +109,17 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
.collect::<Result<Vec<_>, _>>()?;
assert_eq!(property_names, &["0", "a", "whatever"]);

let forty_two = cx.with_raw_env(|mut env| {
let env_ptr = env as *mut RawEnv as *mut c_void;
let env = unsafe { &mut *(env_ptr.cast::<RawEnv>()) };
env.with_context(|mut cx| {
let num = cx.number(42);
num.value(&mut cx) as u8
})
});

assert_eq!(forty_two, 42);

cx.export_value("rustCreated", rust_created)?;

fn add1(mut cx: FunctionContext) -> JsResult<JsNumber> {
Expand Down