From dfd213a1ee2d4da821a024bc4504c4af56796ded Mon Sep 17 00:00:00 2001 From: LongYinan Date: Sun, 6 Feb 2022 16:25:32 +0800 Subject: [PATCH] feat(napi): `with_value` method on `Property` --- .../src/bindgen_runtime/module_register.rs | 32 ++++++++++++++++++ crates/napi/src/env.rs | 2 +- crates/napi/src/js_values/object_property.rs | 26 ++++++++++++-- examples/napi/__test__/typegen.spec.ts.md | 2 ++ examples/napi/__test__/typegen.spec.ts.snap | Bin 2558 -> 2593 bytes examples/napi/__test__/values.spec.ts | 7 ++++ examples/napi/index.d.ts | 2 ++ examples/napi/src/lib.rs | 2 ++ examples/napi/src/object.rs | 18 +++++++++- 9 files changed, 86 insertions(+), 5 deletions(-) diff --git a/crates/napi/src/bindgen_runtime/module_register.rs b/crates/napi/src/bindgen_runtime/module_register.rs index 0723a7f9..76e7ca47 100644 --- a/crates/napi/src/bindgen_runtime/module_register.rs +++ b/crates/napi/src/bindgen_runtime/module_register.rs @@ -211,6 +211,38 @@ pub fn get_js_function(raw_fn: ExportRegisterCallback) -> Result { }) } +/// Get `C Callback` from defined Rust `fn` +/// ```rust +/// #[napi] +/// fn some_fn() -> u32 { +/// 1 +/// } +/// +/// #[napi] +/// fn create_obj(env: Env) -> Result { +/// let mut obj = env.create_object()?; +/// obj.define_property(&[Property::new("getter")?.with_getter(get_c_callback(some_fn_js_function)?)])?; +/// Ok(obj) +/// } +/// ``` +/// +/// ```js +/// console.log(createObj().getter) // 1 +/// ``` +/// +pub fn get_c_callback(raw_fn: ExportRegisterCallback) -> Result { + FN_REGISTER_MAP + .borrow_mut() + .get(&raw_fn) + .and_then(|(_env, cb, _name)| *cb) + .ok_or_else(|| { + crate::Error::new( + crate::Status::InvalidArg, + "JavaScript function does not exists".to_owned(), + ) + }) +} + #[no_mangle] unsafe extern "C" fn napi_register_module_v1( env: sys::napi_env, diff --git a/crates/napi/src/env.rs b/crates/napi/src/env.rs index 9093c97d..77f85e92 100644 --- a/crates/napi/src/env.rs +++ b/crates/napi/src/env.rs @@ -31,7 +31,7 @@ use serde::Serialize; #[cfg(all(feature = "tokio_rt", feature = "napi4"))] use std::future::Future; -pub type Callback = extern "C" fn(sys::napi_env, sys::napi_callback_info) -> sys::napi_value; +pub type Callback = unsafe extern "C" fn(sys::napi_env, sys::napi_callback_info) -> sys::napi_value; #[derive(Clone, Copy)] /// `Env` is used to represent a context that the underlying N-API implementation can use to persist VM-specific state. diff --git a/crates/napi/src/js_values/object_property.rs b/crates/napi/src/js_values/object_property.rs index a14d8dfc..9e89d949 100644 --- a/crates/napi/src/js_values/object_property.rs +++ b/crates/napi/src/js_values/object_property.rs @@ -2,18 +2,33 @@ use std::convert::From; use std::ffi::CString; use std::ptr; -use crate::{sys, Callback, Result}; +use crate::{sys, Callback, NapiRaw, Result}; -#[derive(Clone, Default)] +#[derive(Clone)] pub struct Property { pub name: CString, getter: sys::napi_callback, setter: sys::napi_callback, method: sys::napi_callback, attrs: PropertyAttributes, + value: sys::napi_value, pub(crate) is_ctor: bool, } +impl Default for Property { + fn default() -> Self { + Property { + name: Default::default(), + getter: Default::default(), + setter: Default::default(), + method: Default::default(), + attrs: Default::default(), + value: ptr::null_mut(), + is_ctor: Default::default(), + } + } +} + #[repr(i32)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum PropertyAttributes { @@ -75,6 +90,11 @@ impl Property { self } + pub fn with_value(mut self, value: &T) -> Self { + self.value = unsafe { T::raw(value) }; + self + } + pub(crate) fn raw(&self) -> sys::napi_property_descriptor { sys::napi_property_descriptor { utf8name: self.name.as_ptr(), @@ -82,7 +102,7 @@ impl Property { method: self.method, getter: self.getter, setter: self.setter, - value: ptr::null_mut(), + value: self.value, attributes: self.attrs.into(), data: ptr::null_mut(), } diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 20d2e0d7..94ad6647 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -112,6 +112,8 @@ Generated by [AVA](https://avajs.dev). typeOverride: object␊ typeOverrideOptional?: object␊ }␊ + export function createObjWithProperty(): { value: ArrayBuffer, get getter(): number }␊ + export function getterFromObj(): number␊ export function asyncPlus100(p: Promise): Promise␊ /** This is an interface for package.json */␊ export interface PackageJson {␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index e704d9a6ddc9e84b1b87854e4a1c1da2e6616ac8..b275c22ef9faab0b3f2fcd215d692b5bb7431577 100644 GIT binary patch delta 2510 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 diff --git a/examples/napi/__test__/values.spec.ts b/examples/napi/__test__/values.spec.ts index 40d3950a..0af1fbf3 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -81,6 +81,7 @@ import { getStrFromObject, returnJsFunction, testSerdeRoundtrip, + createObjWithProperty, } from '../' test('export const', (t) => { @@ -220,6 +221,12 @@ test('get str from object', (t) => { t.notThrows(() => getStrFromObject()) }) +test('create object from Property', (t) => { + const obj = createObjWithProperty() + t.true(obj.value instanceof ArrayBuffer) + t.is(obj.getter, 42) +}) + test('global', (t) => { t.is(getGlobal(), global) }) diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 60cee36f..dad68889 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -102,6 +102,8 @@ export interface TsTypeChanged { typeOverride: object typeOverrideOptional?: object } +export function createObjWithProperty(): { value: ArrayBuffer, get getter(): number } +export function getterFromObj(): number export function asyncPlus100(p: Promise): Promise /** This is an interface for package.json */ export interface PackageJson { diff --git a/examples/napi/src/lib.rs b/examples/napi/src/lib.rs index 5e0333b2..ce416cf6 100644 --- a/examples/napi/src/lib.rs +++ b/examples/napi/src/lib.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + #[macro_use] extern crate napi_derive; #[macro_use] diff --git a/examples/napi/src/object.rs b/examples/napi/src/object.rs index 9e4720a2..56c81b9e 100644 --- a/examples/napi/src/object.rs +++ b/examples/napi/src/object.rs @@ -1,4 +1,4 @@ -use napi::{bindgen_prelude::*, JsGlobal, JsNull, JsUndefined}; +use napi::{bindgen_prelude::*, JsGlobal, JsNull, JsObject, JsUndefined, Property}; #[napi] fn list_obj_keys(obj: Object) -> Vec { @@ -85,3 +85,19 @@ pub struct TsTypeChanged { #[napi(ts_type = "object")] pub type_override_optional: Option, } + +#[napi(ts_return_type = "{ value: ArrayBuffer, get getter(): number }")] +pub fn create_obj_with_property(env: Env) -> Result { + let mut obj = env.create_object()?; + let arraybuffer = env.create_arraybuffer(10)?.into_raw(); + obj.define_properties(&[ + Property::new("value")?.with_value(&arraybuffer), + Property::new("getter")?.with_getter(get_c_callback(getter_from_obj_js_function)?), + ])?; + Ok(obj) +} + +#[napi] +fn getter_from_obj() -> u32 { + 42 +}