feat(napi): set and get instance data

This commit is contained in:
LongYinan 2020-12-17 00:27:33 +08:00
parent c621986ce5
commit c4734e23a1
No known key found for this signature in database
GPG key ID: C3666B7FC82ADAD7
4 changed files with 141 additions and 0 deletions

View file

@ -834,6 +834,68 @@ impl Env {
Ok(unsafe { JsDate::from_raw_unchecked(self.0, js_value) })
}
#[cfg(feature = "napi6")]
#[inline]
/// This API associates data with the currently running Agent. data can later be retrieved using `Env::get_instance_data()`.
/// Any existing data associated with the currently running Agent which was set by means of a previous call to `Env::set_instance_data()` will be overwritten.
/// If a `finalize_cb` was provided by the previous call, it will not be called.
pub fn set_instance_data<T, Hint, F>(&self, native: T, hint: Hint, finalize_cb: F) -> Result<()>
where
T: 'static,
Hint: 'static,
F: FnOnce(FinalizeContext<T, Hint>),
{
check_status!(unsafe {
sys::napi_set_instance_data(
self.0,
Box::leak(Box::new((TaggedObject::new(native), finalize_cb))) as *mut (TaggedObject<T>, F)
as *mut c_void,
Some(
set_instance_finalize_callback::<T, Hint, F>
as unsafe extern "C" fn(
env: sys::napi_env,
finalize_data: *mut c_void,
finalize_hint: *mut c_void,
),
),
Box::leak(Box::new(hint)) as *mut Hint as *mut c_void,
)
})
}
#[cfg(feature = "napi6")]
#[inline]
/// This API retrieves data that was previously associated with the currently running Agent via `Env::set_instance_data()`.
/// If no data is set, the call will succeed and data will be set to NULL.
pub fn get_instance_data<T>(&self) -> Result<Option<&'static mut T>>
where
T: 'static,
{
let mut unknown_tagged_object: *mut c_void = ptr::null_mut();
unsafe {
check_status!(sys::napi_get_instance_data(
self.0,
&mut unknown_tagged_object
))?;
let type_id = unknown_tagged_object as *const TypeId;
if unknown_tagged_object.is_null() {
return Ok(None);
}
if *type_id == TypeId::of::<T>() {
let tagged_object = unknown_tagged_object as *mut TaggedObject<T>;
(*tagged_object).object.as_mut().map(Some).ok_or(Error {
status: Status::InvalidArg,
reason: "Invalid argument, nothing attach to js_object".to_owned(),
})
} else {
Err(Error {
status: Status::InvalidArg,
reason: "Invalid argument, T on unrwap is not the type of wrapped object".to_owned(),
})
}
}
}
/// # Serialize `Rust Struct` into `JavaScript Value`
/// ```
/// #[derive(Serialize, Debug, Deserialize)]
@ -933,6 +995,26 @@ unsafe extern "C" fn raw_finalize<T>(
Box::from_raw(tagged_object);
}
#[cfg(feature = "napi6")]
unsafe extern "C" fn set_instance_finalize_callback<T, Hint, F>(
raw_env: sys::napi_env,
finalize_data: *mut c_void,
finalize_hint: *mut c_void,
) where
T: 'static,
Hint: 'static,
F: FnOnce(FinalizeContext<T, Hint>),
{
let (value, callback) = *Box::from_raw(finalize_data as *mut (TaggedObject<T>, F));
let hint = *Box::from_raw(finalize_hint as *mut Hint);
let env = Env::from_raw(raw_env);
callback(FinalizeContext {
value: value.object.unwrap(),
hint,
env,
});
}
#[cfg(feature = "napi3")]
unsafe extern "C" fn cleanup_env<T: 'static>(hook_data: *mut c_void) {
let cleanup_env_hook = Box::from_raw(hook_data as *mut CleanupEnvHookData<T>);

View file

@ -0,0 +1,24 @@
import test from 'ava'
import { napiVersion } from '../napi-version'
const bindings = require('../../index.node')
test('should set and get instance data', (t) => {
if (napiVersion >= 6) {
t.is(bindings.getInstanceData(), undefined)
bindings.setInstanceData()
t.is(bindings.getInstanceData(), 1024)
} else {
t.is(bindings.getInstanceData, undefined)
t.is(bindings.setInstanceData, undefined)
}
})
test('should throw if get instance data type mismatched', (t) => {
if (napiVersion >= 6) {
t.throws(bindings.getWrongTypeInstanceData)
} else {
t.is(bindings.getWrongTypeInstanceData, undefined)
}
})

View file

@ -0,0 +1,29 @@
use napi::*;
struct NativeObject {
count: i64,
}
#[contextless_function]
pub fn set_instance_data(env: Env) -> ContextlessResult<JsUndefined> {
env.set_instance_data(NativeObject { count: 1024 }, 0, |_ctx| {})?;
env.get_undefined().map(Some)
}
#[contextless_function]
pub fn get_instance_data(env: Env) -> ContextlessResult<JsNumber> {
if let Some(obj) = env.get_instance_data::<NativeObject>()? {
env.create_int64(obj.count).map(Some)
} else {
Ok(None)
}
}
#[contextless_function]
pub fn get_wrong_type_instance_data(env: Env) -> ContextlessResult<JsNumber> {
if let Some(count) = env.get_instance_data::<i32>()? {
env.create_int64(*count as i64).map(Some)
} else {
Ok(None)
}
}

View file

@ -1,8 +1,10 @@
use napi::{JsObject, Result};
mod bigint;
mod instance;
use bigint::*;
use instance::*;
pub fn register_js(exports: &mut JsObject) -> Result<()> {
exports.create_named_method("testCreateBigintFromI64", test_create_bigint_from_i64)?;
@ -13,5 +15,9 @@ pub fn register_js(exports: &mut JsObject) -> Result<()> {
exports.create_named_method("testGetBigintI64", test_get_bigint_i64)?;
exports.create_named_method("testGetBigintU64", test_get_bigint_u64)?;
exports.create_named_method("testGetBigintWords", test_get_bigint_words)?;
exports.create_named_method("setInstanceData", set_instance_data)?;
exports.create_named_method("getInstanceData", get_instance_data)?;
exports.create_named_method("getWrongTypeInstanceData", get_wrong_type_instance_data)?;
Ok(())
}