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 e704d9a6..b275c22e 100644 Binary files a/examples/napi/__test__/typegen.spec.ts.snap and b/examples/napi/__test__/typegen.spec.ts.snap differ 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 +}