Skip to content

Commit

Permalink
Added symbol
Browse files Browse the repository at this point in the history
  • Loading branch information
alshdavid committed Jun 25, 2024
1 parent 6f3a670 commit ff4180a
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 66 deletions.
12 changes: 6 additions & 6 deletions crates/neon/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,7 @@ use crate::{
scope::{EscapableHandleScope, HandleScope},
},
types::{
boxed::{Finalize, JsBox},
error::JsError,
extract::FromArgs,
private::ValueInternal,
Deferred, JsArray, JsArrayBuffer, JsBoolean, JsBuffer, JsFunction, JsNull, JsNumber,
JsObject, JsPromise, JsString, JsUndefined, JsValue, StringResult, Value,
boxed::{Finalize, JsBox}, error::JsError, extract::FromArgs, private::ValueInternal, Deferred, JsArray, JsArrayBuffer, JsBoolean, JsBuffer, JsFunction, JsNull, JsNumber, JsObject, JsPromise, JsString, JsSymbol, JsUndefined, JsValue, StringResult, Value
},
};

Expand Down Expand Up @@ -359,6 +354,11 @@ pub trait Context<'a>: ContextInternal<'a> {
JsNumber::new(self, x.into())
}

/// Convenience method for creating a `JsSymbol` value.
fn symbol<D: AsRef<str>>(&mut self, d: D) -> Handle<'a, JsSymbol> {
JsSymbol::new(self, d)
}

/// Convenience method for creating a `JsString` value.
///
/// If the string exceeds the limits of the JS engine, this method panics.
Expand Down
6 changes: 5 additions & 1 deletion crates/neon/src/handle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub use self::root_global::*;
use crate::{
context::Context,
handle::internal::{SuperType, TransparentNoCopyWrapper},
result::{JsResult, ResultExt},
result::{JsResult, NeonResult, ResultExt},
sys,
types::Value,
};
Expand Down Expand Up @@ -95,6 +95,10 @@ impl<'a, V: Value + 'a> Handle<'a, V> {
phantom: PhantomData,
}
}

pub fn root_global(self, cx: &mut impl Context<'a>) -> NeonResult<RootGlobal<V>> {
RootGlobal::new(cx, self)
}
}

/// An error representing a failed downcast.
Expand Down
137 changes: 78 additions & 59 deletions crates/neon/src/handle/root_global.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,59 @@
use once_cell::unsync::Lazy;
use once_cell::unsync::OnceCell;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::rc::Rc;

use super::TransparentNoCopyWrapper;
use super::Value;
use crate::context::Context;
use crate::object::Object;
use crate::prelude::Handle;
use crate::result::JsResult;
use crate::result::NeonResult;
use crate::sys::Value__;
use crate::types::private::ValueInternal;
use crate::types::JsError;
use crate::types::JsFunction;
use crate::types::JsNumber;
use crate::types::JsObject;
use crate::types::JsValue;

static KEY_NEON_CACHE: &str = "__neon_cache";
static KEY_INSTANCE_KEY: &str = "__instance_count";

thread_local! {
/// Basic unique key generation
static COUNT: Lazy<RefCell<u32>> = Lazy::new(|| Default::default());
static CACHE_KEY: OnceCell<u32> = OnceCell::default();
// Symbol("__neon_cache")
static CACHE_SYMBOL: OnceCell<*mut Value__> = OnceCell::default();
}

/// Reference counted JavaScript value with a static lifetime for use in async closures
pub struct RootGlobal<T> {
pub(crate) count: Rc<RefCell<u32>>,
pub(crate) inner_key: Rc<u32>,
pub(crate) inner: RefCell<Option<T>>,
pub(crate) inner: Rc<*mut Value__>,
_p: PhantomData<T>,
}

impl<T: Value> RootGlobal<T> {
pub(crate) fn new<'a>(
cx: &mut impl Context<'a>,
value: Handle<'a, T>,
) -> NeonResult<RootGlobal<T>> {
let inner_key = set_ref(cx, value)?;

Ok(Self {
count: Rc::new(RefCell::new(1)),
inner_key: Rc::new(inner_key),
inner: Default::default(),
inner: Rc::new(set_ref(cx, value)?),
_p: Default::default(),
})
}

pub fn clone<'a>(&self, cx: impl Context<'a>) -> Result<RootGlobal<T>, ()> {
todo!();
}

pub fn deref<'a>(&self, cx: impl Context<'a>) -> Result<T, ()> {
todo!();
pub fn deref<'a>(&self, cx: &mut impl Context<'a>) -> JsResult<'a, T> {
// TODO error handling
let env_raw = cx.env();
let hydrated = unsafe { T::from_local(env_raw, *self.inner) };
Ok(Handle::new_internal(hydrated))
}

pub fn drop<'a>(&self, cx: impl Context<'a>) -> Result<(), ()> {
Expand All @@ -57,70 +63,83 @@ impl<T: Value> RootGlobal<T> {

/*
globalThis = {
__napi_cache: {
__instance_count: number,
[key: number]: Record<number, any>
}
[key: Symbol("__neon_cache")]: Set<any>
}
Note: Is there a way to store this privately in the module scope?
*/
fn get_cache<'a>(cx: &mut impl Context<'a>) -> JsResult<'a, JsObject> {
let global_this = cx.global_object();

let neon_cache = {
let neon_cache = global_this.get_opt::<JsObject, _, _>(cx, KEY_NEON_CACHE)?;
if let Some(neon_cache) = neon_cache {
neon_cache
} else {
let neon_cache = cx.empty_object();
let initial_count = cx.number(0);
neon_cache.set(cx, KEY_INSTANCE_KEY, initial_count)?;
global_this.set(cx, KEY_NEON_CACHE, neon_cache)?;
global_this.get::<JsObject, _, _>(cx, KEY_NEON_CACHE)?
let neon_cache_symbol = CACHE_SYMBOL.with({
|raw_value| {
raw_value
.get_or_try_init(|| -> NeonResult<*mut Value__> {
let symbol_ctor = global_this.get::<JsFunction, _, _>(cx, "Symbol")?;
let set_ctor = global_this.get::<JsFunction, _, _>(cx, "Set")?;

let neon_cache = set_ctor.construct(cx, &[])?;

let key = cx.string(KEY_NEON_CACHE);
let symbol: Handle<JsValue> = symbol_ctor.call_with(cx).arg(key).apply(cx)?;
let symbol_raw = symbol.to_local();

global_this.set(cx, symbol, neon_cache)?;

Ok(symbol_raw)
})
.cloned()
}
})?;

let neon_cache_symbol =
Handle::new_internal(unsafe { JsValue::from_local(cx.env(), neon_cache_symbol) });

let Some(neon_cache) = global_this.get_opt::<JsObject, _, _>(cx, neon_cache_symbol)? else {
return Err(cx.throw_error("Unable to find cache")?);
};

let instance_count = CACHE_KEY.with(|key| {
key.get_or_try_init(|| -> NeonResult<u32> {
let instance_count = global_this.get::<JsNumber, _, _>(cx, KEY_NEON_CACHE)?;
let instance_count = instance_count.value(cx) as u32;
let instance_count = instance_count + 1;
let instance_count_js = cx.number(instance_count);
global_this.set::<_, _, JsNumber>(cx, KEY_NEON_CACHE, instance_count_js)?;
Ok(instance_count)
})
.cloned()
})?;
console_log(cx, &neon_cache);
console_log(cx, &neon_cache_symbol);

neon_cache.get(cx, instance_count)
Ok(neon_cache)
}

fn set_ref<'a, V: Value>(cx: &mut impl Context<'a>, value: Handle<V>) -> NeonResult<u32> {
fn set_ref<'a, V: Value>(
cx: &mut impl Context<'a>,
value: Handle<'a, V>,
) -> NeonResult<*mut Value__> {
let neon_cache = get_cache(cx)?;
let value_raw = value.to_local();

let key_raw = COUNT.with(|c| {
let mut c = c.borrow_mut();
let current = c.clone();
*c += 1;
current
});
get_cache(cx)?
.get::<JsFunction, _, _>(cx, "add")?
.call_with(cx)
.this(neon_cache)
.arg(value)
.exec(cx)?;

let key = cx.number(key_raw);
neon_cache.set(cx, key, value)?;
Ok(key_raw)
Ok(value_raw)
}

fn get_ref<'a, T: Value>(cx: &mut impl Context<'a>, key: &u32) -> JsResult<'a, T> {
fn delete_ref<'a, V: Value>(cx: &mut impl Context<'a>, value: Handle<'a, V>) -> NeonResult<()> {
let neon_cache = get_cache(cx)?;
let key = cx.number(key.clone());
neon_cache.get(cx, key)
}

fn remove_ref<'a>(cx: &mut impl Context<'a>, key: u32) -> NeonResult<()> {
let neon_cache = get_cache(cx)?;
let key = cx.number(key);
let value = cx.undefined();
neon_cache.set(cx, key, value)?;
get_cache(cx)?
.get::<JsFunction, _, _>(cx, "delete")?
.call_with(cx)
.this(neon_cache)
.arg(value)
.exec(cx)?;

Ok(())
}

fn console_log<'a, V: Value>(cx: &mut impl Context<'a>, v: &Handle<'a, V>) {
cx.global::<JsObject>("console")
.unwrap()
.get::<JsFunction, _, _>(cx, "log")
.unwrap()
.call_with(cx)
.arg(*v)
.exec(cx)
.unwrap();
}
2 changes: 2 additions & 0 deletions crates/neon/src/sys/bindings/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ mod napi1 {

fn create_range_error(env: Env, code: Value, msg: Value, result: *mut Value) -> Status;

fn create_symbol(env: Env, description: Value, result: *mut Value) -> Status;

fn create_string_utf8(
env: Env,
str: *const c_char,
Expand Down
1 change: 1 addition & 0 deletions crates/neon/src/sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pub(crate) mod raw;
pub(crate) mod reference;
pub(crate) mod scope;
pub(crate) mod string;
pub(crate) mod symbol;
pub(crate) mod tag;
pub(crate) mod typedarray;

Expand Down
12 changes: 12 additions & 0 deletions crates/neon/src/sys/symbol.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use std::{mem::MaybeUninit, ptr};

use super::{
bindings as napi,
raw::{Env, Local},
};

pub unsafe fn new(out: &mut Local, env: Env, description: Local) -> bool {
let status = napi::create_symbol(env, description, out);

status == napi::Status::Ok
}
51 changes: 51 additions & 0 deletions crates/neon/src/types_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,57 @@ impl ValueInternal for JsBoolean {
}
}

/// The type of JavaScript
/// [symbol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol)
/// primitives.
#[derive(Debug)]
#[repr(transparent)]
pub struct JsSymbol(raw::Local);

unsafe impl TransparentNoCopyWrapper for JsSymbol {
type Inner = raw::Local;

fn into_inner(self) -> Self::Inner {
self.0
}
}

impl Value for JsSymbol {}

impl ValueInternal for JsSymbol {
fn name() -> &'static str {
"symbol"
}

fn is_typeof<Other: Value>(env: Env, other: &Other) -> bool {
true
// unsafe { sys::tag::is_string(env.to_raw(), other.to_local()) }
}

fn to_local(&self) -> raw::Local {
self.0
}

unsafe fn from_local(_env: Env, h: raw::Local) -> Self {
JsSymbol(h)
}
}

impl JsSymbol {
pub fn new<'a, C: Context<'a>, S: AsRef<str>>(
cx: &mut C,
description: S,
) -> Handle<'a, JsSymbol> {
let description = JsString::new(cx, description);

unsafe {
let mut local: raw::Local = std::mem::zeroed();
sys::symbol::new(&mut local, cx.env().to_raw(), description.to_local());
Handle::new_internal(JsSymbol(local))
}
}
}

/// The type of JavaScript
/// [string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values)
/// primitives.
Expand Down

0 comments on commit ff4180a

Please sign in to comment.