diff --git a/README.md b/README.md index d79319b7..0581122d 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ npm test | [napi_create_buffer](https://nodejs.org/api/n-api.html#n_api_napi_create_buffer) | 1 | v8.0.0 | ✅ | | [napi_create_buffer_copy](https://nodejs.org/api/n-api.html#n_api_napi_create_buffer_copy) | 1 | v8.0.0 | ⛔️ | | [napi_create_date](https://nodejs.org/api/n-api.html#n_api_napi_create_date) | 5 | v11.11.0 | ⛔️ | -| [napi_create_external](https://nodejs.org/api/n-api.html#n_api_napi_create_external) | 1 | v8.0.0 | ⛔️ | +| [napi_create_external](https://nodejs.org/api/n-api.html#n_api_napi_create_external) | 1 | v8.0.0 | ✅ | | [napi_create_external_arraybuffer](https://nodejs.org/api/n-api.html#n_api_napi_create_external_arraybuffer) | 1 | v8.0.0 | ✅ | | [napi_create_external_buffer](https://nodejs.org/api/n-api.html#n_api_napi_create_external_buffer) | 1 | v8.0.0 | ✅ | | [napi_create_object](https://nodejs.org/api/n-api.html#n_api_napi_create_object) | 1 | v8.0.0 | ✅ | diff --git a/napi/src/lib.rs b/napi/src/lib.rs index 4498af2a..113a2dfa 100644 --- a/napi/src/lib.rs +++ b/napi/src/lib.rs @@ -530,6 +530,48 @@ impl Env { } } + pub fn create_external(&self, native_object: T) -> Result> { + let mut object_value = ptr::null_mut(); + let status = unsafe { + sys::napi_create_external( + self.0, + Box::into_raw(Box::new(TaggedObject::new(native_object))) as *mut c_void, + Some(raw_finalize::), + ptr::null_mut(), + &mut object_value, + ) + }; + + check_status(status)?; + Ok(Value::from_raw_value(self, object_value, Object)) + } + + pub fn get_value_external(&self, js_object: &Value) -> Result<&mut T> { + unsafe { + let mut unknown_tagged_object = ptr::null_mut(); + let status = + sys::napi_get_value_external(self.0, js_object.raw_value, &mut unknown_tagged_object); + check_status(status)?; + + let type_id: *const TypeId = mem::transmute(unknown_tagged_object); + if *type_id == TypeId::of::() { + let tagged_object: *mut TaggedObject = mem::transmute(unknown_tagged_object); + (*tagged_object).object.as_mut().ok_or(Error { + status: Status::InvalidArg, + reason: Some("Invalid argument, nothing attach to js_object".to_owned()), + }) + } else { + Err(Error { + status: Status::InvalidArg, + reason: Some( + "Invalid argument, T on get_value_external is not the type of wrapped object" + .to_owned(), + ), + }) + } + } + } + pub fn create_error(&self, e: Error) -> Result> { let reason = e.reason.unwrap_or("".to_owned()); let reason_string = self.create_string(reason.as_str())?; diff --git a/test_module/__test__/create-external.spec.js b/test_module/__test__/create-external.spec.js new file mode 100644 index 00000000..8bc60eaa --- /dev/null +++ b/test_module/__test__/create-external.spec.js @@ -0,0 +1,9 @@ +const test = require('ava') + +const bindings = require('../index.node') + +test('should create external object and get it back', (t) => { + const fixture = 42 + const externalObject = bindings.createExternal(42) + t.is(bindings.getExternalCount(externalObject), fixture) +}) diff --git a/test_module/src/lib.rs b/test_module/src/lib.rs index a079f40a..12766e82 100644 --- a/test_module/src/lib.rs +++ b/test_module/src/lib.rs @@ -24,6 +24,16 @@ fn init(env: &Env, exports: &mut Value) -> Result<()> { "testObjectIsDate", env.create_function("testObjectIsDate", test_object_is_date)?, )?; + + exports.set_named_property( + "createExternal", + env.create_function("createExternal", create_external)?, + )?; + + exports.set_named_property( + "getExternalCount", + env.create_function("getExternalCount", get_external_count)?, + )?; Ok(()) } @@ -84,3 +94,21 @@ fn test_object_is_date(ctx: CallContext) -> Result> { let obj: Value = ctx.get::(0)?; Ok(Env::get_boolean(ctx.env, obj.is_date()?)?) } + +struct NativeObject { + count: i32, +} + +#[js_function(1)] +fn create_external(ctx: CallContext) -> Result> { + let count = ctx.get::(0)?.try_into()?; + let native = NativeObject { count }; + ctx.env.create_external(native) +} + +#[js_function(1)] +fn get_external_count(ctx: CallContext) -> Result> { + let attached_obj = ctx.get::(0)?; + let native_object = ctx.env.get_value_external::(&attached_obj)?; + ctx.env.create_int32(native_object.count) +}