diff options
| author | Divy Srivastava <dj.srivastava23@gmail.com> | 2022-10-05 07:06:44 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-10-05 19:36:44 +0530 |
| commit | 0b016a7fb8639ce49603c8c339539174b191a4b1 (patch) | |
| tree | c511060d701db60ede0214b7280e89c5749bbe62 /test_napi | |
| parent | 3a3a8484069c9c6955fcb83ea761f9f74638175a (diff) | |
feat(npm): implement Node API (#13633)
This PR implements the NAPI for loading native modules into Deno.
Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
Co-authored-by: DjDeveloper <43033058+DjDeveloperr@users.noreply.github.com>
Co-authored-by: Ryan Dahl <ry@tinyclouds.org>
Diffstat (limited to 'test_napi')
| -rw-r--r-- | test_napi/.gitignore | 4 | ||||
| -rw-r--r-- | test_napi/Cargo.toml | 20 | ||||
| -rw-r--r-- | test_napi/array_test.js | 19 | ||||
| -rw-r--r-- | test_napi/async_test.js | 16 | ||||
| -rw-r--r-- | test_napi/build.rs | 7 | ||||
| -rw-r--r-- | test_napi/callback_test.js | 38 | ||||
| -rw-r--r-- | test_napi/coerce_test.js | 74 | ||||
| -rw-r--r-- | test_napi/common.js | 20 | ||||
| -rw-r--r-- | test_napi/numbers_test.js | 18 | ||||
| -rw-r--r-- | test_napi/object_wrap_test.js | 17 | ||||
| -rw-r--r-- | test_napi/promise_test.js | 34 | ||||
| -rw-r--r-- | test_napi/properties_test.js | 15 | ||||
| -rw-r--r-- | test_napi/src/array.rs | 73 | ||||
| -rw-r--r-- | test_napi/src/async.rs | 112 | ||||
| -rw-r--r-- | test_napi/src/callback.rs | 113 | ||||
| -rw-r--r-- | test_napi/src/coerce.rs | 71 | ||||
| -rw-r--r-- | test_napi/src/lib.rs | 78 | ||||
| -rw-r--r-- | test_napi/src/numbers.rs | 55 | ||||
| -rw-r--r-- | test_napi/src/object_wrap.rs | 154 | ||||
| -rw-r--r-- | test_napi/src/promise.rs | 76 | ||||
| -rw-r--r-- | test_napi/src/properties.rs | 89 | ||||
| -rw-r--r-- | test_napi/src/strings.rs | 45 | ||||
| -rw-r--r-- | test_napi/src/typedarray.rs | 53 | ||||
| -rw-r--r-- | test_napi/strings_test.js | 15 | ||||
| -rw-r--r-- | test_napi/tests/napi_tests.rs | 45 | ||||
| -rw-r--r-- | test_napi/typedarray_test.js | 12 |
26 files changed, 1273 insertions, 0 deletions
diff --git a/test_napi/.gitignore b/test_napi/.gitignore new file mode 100644 index 000000000..6fdcc4a66 --- /dev/null +++ b/test_napi/.gitignore @@ -0,0 +1,4 @@ +package-lock.json + +# Test generated artifacts +.swc
\ No newline at end of file diff --git a/test_napi/Cargo.toml b/test_napi/Cargo.toml new file mode 100644 index 000000000..09faa4175 --- /dev/null +++ b/test_napi/Cargo.toml @@ -0,0 +1,20 @@ +# Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +[package] +name = "test_napi" +version = "0.1.0" +authors = ["the deno authors"] +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +napi-sys = { version = "2.2.2", default-features = false, features = ["napi4"] } + +[dev-dependencies] +test_util = { path = "../test_util" } + +[build-dependencies] +napi-build = "1" diff --git a/test_napi/array_test.js b/test_napi/array_test.js new file mode 100644 index 000000000..314ea5e50 --- /dev/null +++ b/test_napi/array_test.js @@ -0,0 +1,19 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const array = loadTestLibrary(); + +Deno.test("napi array new", function () { + const e = [0, "Hello", {}]; + const r = array.test_array_new(e); + assertEquals(typeof r, "object"); + assertEquals(r.length, 3); + assertEquals(e, r); +}); + +Deno.test("napi array new with length", function () { + const r = array.test_array_new_with_length(100); + assertEquals(typeof r, "object"); + assertEquals(r.length, 100); +}); diff --git a/test_napi/async_test.js b/test_napi/async_test.js new file mode 100644 index 000000000..ea1b714bd --- /dev/null +++ b/test_napi/async_test.js @@ -0,0 +1,16 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const asyncTask = loadTestLibrary(); + +Deno.test("napi async task schedule", async () => { + let called = false; + await new Promise((resolve) => { + asyncTask.test_async_work(() => { + called = true; + resolve(); + }); + }); + assertEquals(called, true); +}); diff --git a/test_napi/build.rs b/test_napi/build.rs new file mode 100644 index 000000000..9d3021a0e --- /dev/null +++ b/test_napi/build.rs @@ -0,0 +1,7 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +extern crate napi_build; + +fn main() { + napi_build::setup(); +} diff --git a/test_napi/callback_test.js b/test_napi/callback_test.js new file mode 100644 index 000000000..debb1d7ba --- /dev/null +++ b/test_napi/callback_test.js @@ -0,0 +1,38 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const callback = loadTestLibrary(); + +Deno.test("napi callback run with args", function () { + const result = callback.test_callback_run((a, b) => a + b, [1, 2]); + assertEquals(result, 3); +}); + +Deno.test("napi callback run with args (no return)", function () { + const result = callback.test_callback_run(() => {}, []); + assertEquals(result, undefined); +}); + +Deno.test("napi callback run with args (extra arguments)", function () { + const result = callback.test_callback_run((a, b) => a + b, [ + "Hello,", + " Deno!", + 1, + 2, + 3, + ]); + assertEquals(result, "Hello, Deno!"); +}); + +Deno.test("napi callback run with args & recv", function () { + const result = callback.test_callback_run_with_recv( + function () { + assertEquals(this, 69); + return this; + }, + [], + 69, + ); + assertEquals(result, 69); +}); diff --git a/test_napi/coerce_test.js b/test_napi/coerce_test.js new file mode 100644 index 000000000..be0ee03e7 --- /dev/null +++ b/test_napi/coerce_test.js @@ -0,0 +1,74 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const coerce = loadTestLibrary(); + +Deno.test("napi coerce bool", function () { + assertEquals(coerce.test_coerce_bool(true), true); + assertEquals(coerce.test_coerce_bool(false), false); + assertEquals(coerce.test_coerce_bool(0), false); + assertEquals(coerce.test_coerce_bool(69), true); + assertEquals(coerce.test_coerce_bool(Number.MAX_SAFE_INTEGER), true); + assertEquals(coerce.test_coerce_bool(new Array(10)), true); + assertEquals(coerce.test_coerce_bool("Hello, Deno!"), true); + assertEquals(coerce.test_coerce_bool(Symbol("[[test]]")), true); + assertEquals(coerce.test_coerce_bool({}), true); + assertEquals(coerce.test_coerce_bool(() => false), true); + assertEquals(coerce.test_coerce_bool(undefined), false); + assertEquals(coerce.test_coerce_bool(null), false); +}); + +Deno.test("napi coerce number", function () { + assertEquals(coerce.test_coerce_number(true), 1); + assertEquals(coerce.test_coerce_number(false), 0); + assertEquals(coerce.test_coerce_number(0), 0); + assertEquals(coerce.test_coerce_number(69), 69); + assertEquals(coerce.test_coerce_number(""), 0); + assertEquals( + coerce.test_coerce_number(Number.MAX_SAFE_INTEGER), + Number.MAX_SAFE_INTEGER, + ); + assertEquals(coerce.test_coerce_number(new Array(10)), NaN); + assertEquals(coerce.test_coerce_number("Hello, Deno!"), NaN); + assertEquals(coerce.test_coerce_number({}), NaN); + assertEquals(coerce.test_coerce_number(() => false), NaN); + assertEquals(coerce.test_coerce_number(undefined), NaN); + assertEquals(coerce.test_coerce_number(null), 0); +}); + +Deno.test("napi coerce string", function () { + assertEquals(coerce.test_coerce_string(true), "true"); + assertEquals(coerce.test_coerce_string(false), "false"); + assertEquals(coerce.test_coerce_string(0), "0"); + assertEquals(coerce.test_coerce_string(69), "69"); + assertEquals(coerce.test_coerce_string(""), ""); + assertEquals( + coerce.test_coerce_string(Number.MAX_SAFE_INTEGER), + "9007199254740991", + ); + assertEquals(coerce.test_coerce_string(new Array(10)), ",,,,,,,,,"); + assertEquals(coerce.test_coerce_string("Hello, Deno!"), "Hello, Deno!"); + assertEquals(coerce.test_coerce_string({}), "[object Object]"); + assertEquals(coerce.test_coerce_string(() => false), "() => false"); + assertEquals(coerce.test_coerce_string(undefined), "undefined"); + assertEquals(coerce.test_coerce_string(null), "null"); +}); + +Deno.test("napi coerce object", function () { + assertEquals(coerce.test_coerce_object(true), new Boolean(true)); + assertEquals(coerce.test_coerce_object(false), new Boolean(false)); + assertEquals(coerce.test_coerce_object(0), new Number(0)); + assertEquals(coerce.test_coerce_object(69), new Number(0)); + assertEquals(coerce.test_coerce_object(""), new String("")); + assertEquals( + coerce.test_coerce_object(Number.MAX_SAFE_INTEGER), + new Number(Number.MAX_SAFE_INTEGER), + ); + assertEquals(coerce.test_coerce_object(new Array(10)), new Array(10)); + assertEquals( + coerce.test_coerce_object("Hello, Deno!"), + new String("Hello, Deno!"), + ); + assertEquals(coerce.test_coerce_object({}), {}); +}); diff --git a/test_napi/common.js b/test_napi/common.js new file mode 100644 index 000000000..13dad6cfc --- /dev/null +++ b/test_napi/common.js @@ -0,0 +1,20 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +export { + assert, + assertEquals, + assertRejects, +} from "../test_util/std/testing/asserts.ts"; +export { fromFileUrl } from "../test_util/std/path/mod.ts"; + +const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); +const [libPrefix, libSuffix] = { + darwin: ["lib", "dylib"], + linux: ["lib", "so"], + windows: ["", "dll"], +}[Deno.build.os]; + +export function loadTestLibrary() { + const specifier = `${targetDir}/${libPrefix}test_napi.${libSuffix}`; + return Deno.core.ops.op_napi_open(specifier); // Internal, used in ext/node +} diff --git a/test_napi/numbers_test.js b/test_napi/numbers_test.js new file mode 100644 index 000000000..2f778285a --- /dev/null +++ b/test_napi/numbers_test.js @@ -0,0 +1,18 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const numbers = loadTestLibrary(); + +Deno.test("napi int32", function () { + assertEquals(numbers.test_int32(69), 69); + assertEquals(numbers.test_int32(Number.MAX_SAFE_INTEGER), -1); +}); + +Deno.test("napi int64", function () { + assertEquals(numbers.test_int64(69), 69); + assertEquals( + numbers.test_int64(Number.MAX_SAFE_INTEGER), + Number.MAX_SAFE_INTEGER, + ); +}); diff --git a/test_napi/object_wrap_test.js b/test_napi/object_wrap_test.js new file mode 100644 index 000000000..2263894cd --- /dev/null +++ b/test_napi/object_wrap_test.js @@ -0,0 +1,17 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const objectWrap = loadTestLibrary(); + +Deno.test("napi object wrap new", function () { + const obj = new objectWrap.NapiObject(0); + assertEquals(obj.get_value(), 0); + obj.set_value(10); + assertEquals(obj.get_value(), 10); + obj.increment(); + assertEquals(obj.get_value(), 11); + obj.increment(); + obj.set_value(10); + assertEquals(obj.get_value(), 10); +}); diff --git a/test_napi/promise_test.js b/test_napi/promise_test.js new file mode 100644 index 000000000..86a3f134b --- /dev/null +++ b/test_napi/promise_test.js @@ -0,0 +1,34 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, assertRejects, loadTestLibrary } from "./common.js"; + +const promise = loadTestLibrary(); + +Deno.test("napi new promise and resolve", async () => { + const p = promise.test_promise_new(); + promise.test_promise_resolve(69); + + assertEquals(await p, 69); +}); + +Deno.test("napi new promise and reject", () => { + const p = promise.test_promise_new(); + + assertRejects(async () => { + promise.test_promise_reject(new TypeError("pikaboo")); + await p; + }, TypeError); +}); + +Deno.test("napi new promise and reject", async () => { + const p = promise.test_promise_new(); + const is = promise.test_promise_is(p); + assertEquals(typeof is, "boolean"); + assertEquals(is, true); + + assertEquals(promise.test_promise_is(undefined), false); + assertEquals(promise.test_promise_is({}), false); + promise.test_promise_resolve(69); + + assertEquals(await p, 69); +}); diff --git a/test_napi/properties_test.js b/test_napi/properties_test.js new file mode 100644 index 000000000..b24c5649e --- /dev/null +++ b/test_napi/properties_test.js @@ -0,0 +1,15 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const properties = loadTestLibrary(); + +Deno.test("napi properties", () => { + properties.test_property_rw = 1; + assertEquals(properties.test_property_rw, 1); + properties.test_property_rw = 2; + assertEquals(properties.test_property_rw, 2); + + // assertEquals(properties.test_property_r, 2); + // assertRejects(() => properties.test_property_r = 3); +}); diff --git a/test_napi/src/array.rs b/test_napi/src/array.rs new file mode 100644 index 000000000..767aa08b1 --- /dev/null +++ b/test_napi/src/array.rs @@ -0,0 +1,73 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use napi_sys::Status::napi_ok; +use napi_sys::ValueType::napi_number; +use napi_sys::ValueType::napi_object; +use napi_sys::*; +use std::ptr; + +extern "C" fn test_array_new( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok); + assert_eq!(ty, napi_object); + + let mut value: napi_value = ptr::null_mut(); + assert!(unsafe { napi_create_array(env, &mut value) } == napi_ok); + + let mut length: u32 = 0; + assert!( + unsafe { napi_get_array_length(env, args[0], &mut length) } == napi_ok + ); + + for i in 0..length { + let mut e: napi_value = ptr::null_mut(); + assert!(unsafe { napi_get_element(env, args[0], i, &mut e) } == napi_ok); + assert!(unsafe { napi_set_element(env, value, i, e) } == napi_ok); + } + + value +} + +extern "C" fn test_array_new_with_length( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok); + assert_eq!(ty, napi_number); + + let mut len: u32 = 0; + assert!(unsafe { napi_get_value_uint32(env, args[0], &mut len) } == napi_ok); + + let mut value: napi_value = ptr::null_mut(); + assert!( + unsafe { napi_create_array_with_length(env, len as usize, &mut value) } + == napi_ok + ); + + value +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + crate::new_property!(env, "test_array_new\0", test_array_new), + crate::new_property!( + env, + "test_array_new_with_length\0", + test_array_new_with_length + ), + ]; + + unsafe { + napi_define_properties(env, exports, properties.len(), properties.as_ptr()) + }; +} diff --git a/test_napi/src/async.rs b/test_napi/src/async.rs new file mode 100644 index 000000000..d14871a7c --- /dev/null +++ b/test_napi/src/async.rs @@ -0,0 +1,112 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use napi_sys::Status::napi_ok; +use napi_sys::ValueType::napi_function; +use napi_sys::*; +use std::os::raw::c_void; +use std::ptr; + +pub struct Baton { + called: bool, + func: napi_ref, + task: napi_async_work, +} + +unsafe extern "C" fn execute(_env: napi_env, data: *mut c_void) { + let baton: &mut Baton = &mut *(data as *mut Baton); + assert!(!baton.called); + assert!(!baton.func.is_null()); + + baton.called = true; +} + +unsafe extern "C" fn complete( + env: napi_env, + status: napi_status, + data: *mut c_void, +) { + assert!(status == napi_ok); + let baton: Box<Baton> = Box::from_raw(data as *mut Baton); + assert!(baton.called); + assert!(!baton.func.is_null()); + + let mut global: napi_value = ptr::null_mut(); + assert!(napi_get_global(env, &mut global) == napi_ok); + + let mut callback: napi_value = ptr::null_mut(); + assert!(napi_get_reference_value(env, baton.func, &mut callback) == napi_ok); + + let mut _result: napi_value = ptr::null_mut(); + assert!( + napi_call_function(env, global, callback, 0, ptr::null(), &mut _result) + == napi_ok + ); + + assert!(napi_delete_reference(env, baton.func) == napi_ok); + assert!(napi_delete_async_work(env, baton.task) == napi_ok); +} + +extern "C" fn test_async_work( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok); + assert_eq!(ty, napi_function); + + let mut resource_name: napi_value = ptr::null_mut(); + assert!( + unsafe { + napi_create_string_utf8( + env, + "test_async_resource\0".as_ptr() as *const i8, + usize::MAX, + &mut resource_name, + ) + } == napi_ok + ); + + let mut async_work: napi_async_work = ptr::null_mut(); + + let mut func: napi_ref = ptr::null_mut(); + assert!( + unsafe { napi_create_reference(env, args[0], 1, &mut func) } == napi_ok + ); + let baton = Box::new(Baton { + called: false, + func, + task: async_work, + }); + + assert!( + unsafe { + napi_create_async_work( + env, + ptr::null_mut(), + resource_name, + Some(execute), + Some(complete), + Box::into_raw(baton) as *mut c_void, + &mut async_work, + ) + } == napi_ok + ); + assert!(unsafe { napi_queue_async_work(env, async_work) } == napi_ok); + + ptr::null_mut() +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[crate::new_property!( + env, + "test_async_work\0", + test_async_work + )]; + + unsafe { + napi_define_properties(env, exports, properties.len(), properties.as_ptr()) + }; +} diff --git a/test_napi/src/callback.rs b/test_napi/src/callback.rs new file mode 100644 index 000000000..bf6062913 --- /dev/null +++ b/test_napi/src/callback.rs @@ -0,0 +1,113 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use napi_sys::Status::napi_ok; +use napi_sys::ValueType::napi_function; +use napi_sys::ValueType::napi_object; +use napi_sys::*; +use std::ptr; + +/// `test_callback_run((a, b) => a + b, [1, 2])` => 3 +extern "C" fn test_callback_run( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 2); + assert_eq!(argc, 2); + + let mut ty = -1; + assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok); + assert_eq!(ty, napi_function); + + let mut ty = -1; + assert!(unsafe { napi_typeof(env, args[1], &mut ty) } == napi_ok); + assert_eq!(ty, napi_object); + + let mut len = 0; + assert!(unsafe { napi_get_array_length(env, args[1], &mut len) } == napi_ok); + + let mut argv = Vec::with_capacity(len as usize); + for index in 0..len { + let mut value: napi_value = ptr::null_mut(); + assert!( + unsafe { napi_get_element(env, args[1], index, &mut value) } == napi_ok + ); + argv.push(value); + } + let mut global: napi_value = ptr::null_mut(); + assert!(unsafe { napi_get_global(env, &mut global) } == napi_ok); + + let mut result: napi_value = ptr::null_mut(); + assert!( + unsafe { + napi_call_function( + env, + global, + args[0], + argv.len(), + argv.as_mut_ptr(), + &mut result, + ) + } == napi_ok + ); + + result +} + +extern "C" fn test_callback_run_with_recv( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 3); + assert_eq!(argc, 3); + + let mut ty = -1; + assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok); + assert_eq!(ty, napi_function); + + let mut ty = -1; + assert!(unsafe { napi_typeof(env, args[1], &mut ty) } == napi_ok); + assert_eq!(ty, napi_object); + + let mut len = 0; + assert!(unsafe { napi_get_array_length(env, args[1], &mut len) } == napi_ok); + + let mut argv = Vec::with_capacity(len as usize); + for index in 0..len { + let mut value: napi_value = ptr::null_mut(); + assert!( + unsafe { napi_get_element(env, args[1], index, &mut value) } == napi_ok + ); + argv.push(value); + } + + let mut result: napi_value = ptr::null_mut(); + assert!( + unsafe { + napi_call_function( + env, + args[2], // recv + args[0], // cb + argv.len(), + argv.as_mut_ptr(), + &mut result, + ) + } == napi_ok + ); + + result +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + crate::new_property!(env, "test_callback_run\0", test_callback_run), + crate::new_property!( + env, + "test_callback_run_with_recv\0", + test_callback_run_with_recv + ), + ]; + + unsafe { + napi_define_properties(env, exports, properties.len(), properties.as_ptr()) + }; +} diff --git a/test_napi/src/coerce.rs b/test_napi/src/coerce.rs new file mode 100644 index 000000000..611540fae --- /dev/null +++ b/test_napi/src/coerce.rs @@ -0,0 +1,71 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use napi_sys::Status::napi_ok; +use napi_sys::*; +use std::ptr; + +extern "C" fn test_coerce_bool( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut value: napi_value = ptr::null_mut(); + assert!(unsafe { napi_coerce_to_bool(env, args[0], &mut value) } == napi_ok); + value +} + +extern "C" fn test_coerce_number( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut value: napi_value = ptr::null_mut(); + assert!( + unsafe { napi_coerce_to_number(env, args[0], &mut value) } == napi_ok + ); + value +} + +extern "C" fn test_coerce_object( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut value: napi_value = ptr::null_mut(); + assert!( + unsafe { napi_coerce_to_object(env, args[0], &mut value) } == napi_ok + ); + value +} + +extern "C" fn test_coerce_string( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut value: napi_value = ptr::null_mut(); + assert!( + unsafe { napi_coerce_to_string(env, args[0], &mut value) } == napi_ok + ); + value +} +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + crate::new_property!(env, "test_coerce_bool\0", test_coerce_bool), + crate::new_property!(env, "test_coerce_number\0", test_coerce_number), + crate::new_property!(env, "test_coerce_object\0", test_coerce_object), + crate::new_property!(env, "test_coerce_string\0", test_coerce_string), + ]; + + unsafe { + napi_define_properties(env, exports, properties.len(), properties.as_ptr()) + }; +} diff --git a/test_napi/src/lib.rs b/test_napi/src/lib.rs new file mode 100644 index 000000000..e058686c5 --- /dev/null +++ b/test_napi/src/lib.rs @@ -0,0 +1,78 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +#![allow(clippy::all)] +#![allow(clippy::undocumented_unsafe_blocks)] + +use napi_sys::*; + +pub mod array; +pub mod r#async; +pub mod callback; +pub mod coerce; +pub mod numbers; +pub mod object_wrap; +pub mod promise; +pub mod properties; +pub mod strings; +pub mod typedarray; + +#[macro_export] +macro_rules! get_callback_info { + ($env: expr, $callback_info: expr, $size: literal) => {{ + let mut args = [ptr::null_mut(); $size]; + let mut argc = $size; + let mut this = ptr::null_mut(); + unsafe { + assert!( + napi_get_cb_info( + $env, + $callback_info, + &mut argc, + args.as_mut_ptr(), + &mut this, + ptr::null_mut(), + ) == napi_ok, + ) + }; + (args, argc, this) + }}; +} + +#[macro_export] +macro_rules! new_property { + ($env: expr, $name: expr, $value: expr) => { + napi_property_descriptor { + utf8name: $name.as_ptr() as *const i8, + name: ptr::null_mut(), + method: Some($value), + getter: None, + setter: None, + data: ptr::null_mut(), + attributes: 0, + value: ptr::null_mut(), + } + }; +} + +#[no_mangle] +unsafe extern "C" fn napi_register_module_v1( + env: napi_env, + exports: napi_value, +) -> napi_value { + #[cfg(windows)] + { + napi_sys::setup(); + } + + strings::init(env, exports); + numbers::init(env, exports); + typedarray::init(env, exports); + array::init(env, exports); + properties::init(env, exports); + promise::init(env, exports); + coerce::init(env, exports); + object_wrap::init(env, exports); + callback::init(env, exports); + r#async::init(env, exports); + + exports +} diff --git a/test_napi/src/numbers.rs b/test_napi/src/numbers.rs new file mode 100644 index 000000000..a6628af13 --- /dev/null +++ b/test_napi/src/numbers.rs @@ -0,0 +1,55 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use napi_sys::Status::napi_ok; +use napi_sys::ValueType::napi_number; +use napi_sys::*; +use std::ptr; + +extern "C" fn test_int32( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok); + assert_eq!(ty, napi_number); + + let mut int32 = -1; + assert!(unsafe { napi_get_value_int32(env, args[0], &mut int32) } == napi_ok); + + let mut value: napi_value = ptr::null_mut(); + assert!(unsafe { napi_create_int32(env, int32, &mut value) } == napi_ok); + value +} + +extern "C" fn test_int64( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok); + assert_eq!(ty, napi_number); + + let mut int64 = -1; + assert!(unsafe { napi_get_value_int64(env, args[0], &mut int64) } == napi_ok); + + let mut value: napi_value = ptr::null_mut(); + assert!(unsafe { napi_create_int64(env, int64, &mut value) } == napi_ok); + value +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + crate::new_property!(env, "test_int32\0", test_int32), + crate::new_property!(env, "test_int64\0", test_int64), + ]; + + unsafe { + napi_define_properties(env, exports, properties.len(), properties.as_ptr()) + }; +} diff --git a/test_napi/src/object_wrap.rs b/test_napi/src/object_wrap.rs new file mode 100644 index 000000000..3b849b2dc --- /dev/null +++ b/test_napi/src/object_wrap.rs @@ -0,0 +1,154 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use napi_sys::Status::napi_ok; +use napi_sys::ValueType::napi_number; +use napi_sys::*; +use std::os::raw::c_void; +use std::ptr; + +pub struct NapiObject { + counter: i32, + _wrapper: napi_ref, +} + +impl NapiObject { + #[allow(clippy::new_ret_no_self)] + pub extern "C" fn new(env: napi_env, info: napi_callback_info) -> napi_value { + let mut new_target: napi_value = ptr::null_mut(); + assert!( + unsafe { napi_get_new_target(env, info, &mut new_target) } == napi_ok + ); + let is_constructor = !new_target.is_null(); + + let (args, argc, this) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + if is_constructor { + let mut value = 0; + + let mut ty = -1; + assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok); + assert_eq!(ty, napi_number); + + assert!( + unsafe { napi_get_value_int32(env, args[0], &mut value) } == napi_ok + ); + + let mut wrapper: napi_ref = ptr::null_mut(); + let obj = Box::new(Self { + counter: value, + _wrapper: wrapper, + }); + assert!( + unsafe { + napi_wrap( + env, + this, + Box::into_raw(obj) as *mut c_void, + None, + ptr::null_mut(), + &mut wrapper, + ) + } == napi_ok + ); + + return this; + } + + unreachable!(); + } + + pub extern "C" fn set_value( + env: napi_env, + info: napi_callback_info, + ) -> napi_value { + let (args, argc, this) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + let mut obj: *mut Self = ptr::null_mut(); + assert!( + unsafe { napi_unwrap(env, this, &mut obj as *mut _ as *mut *mut c_void) } + == napi_ok + ); + + assert!( + unsafe { napi_get_value_int32(env, args[0], &mut (*obj).counter) } + == napi_ok + ); + + ptr::null_mut() + } + + pub extern "C" fn get_value( + env: napi_env, + info: napi_callback_info, + ) -> napi_value { + let (_args, argc, this) = crate::get_callback_info!(env, info, 0); + assert_eq!(argc, 0); + let mut obj: *mut Self = ptr::null_mut(); + assert!( + unsafe { napi_unwrap(env, this, &mut obj as *mut _ as *mut *mut c_void) } + == napi_ok + ); + + let mut num: napi_value = ptr::null_mut(); + assert!( + unsafe { napi_create_int32(env, (*obj).counter, &mut num) } == napi_ok + ); + + num + } + + pub extern "C" fn increment( + env: napi_env, + info: napi_callback_info, + ) -> napi_value { + let (_args, argc, this) = crate::get_callback_info!(env, info, 0); + assert_eq!(argc, 0); + let mut obj: *mut Self = ptr::null_mut(); + assert!( + unsafe { napi_unwrap(env, this, &mut obj as *mut _ as *mut *mut c_void) } + == napi_ok + ); + + unsafe { + (*obj).counter += 1; + } + + ptr::null_mut() + } +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + crate::new_property!(env, "set_value\0", NapiObject::set_value), + crate::new_property!(env, "get_value\0", NapiObject::get_value), + crate::new_property!(env, "increment\0", NapiObject::increment), + ]; + + let mut cons: napi_value = ptr::null_mut(); + assert!( + unsafe { + napi_define_class( + env, + "NapiObject\0".as_ptr() as *mut i8, + usize::MAX, + Some(NapiObject::new), + ptr::null_mut(), + properties.len(), + properties.as_ptr(), + &mut cons, + ) + } == napi_ok + ); + + assert!( + unsafe { + napi_set_named_property( + env, + exports, + "NapiObject\0".as_ptr() as *const i8, + cons, + ) + } == napi_ok + ); +} diff --git a/test_napi/src/promise.rs b/test_napi/src/promise.rs new file mode 100644 index 000000000..ebb9dedab --- /dev/null +++ b/test_napi/src/promise.rs @@ -0,0 +1,76 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use napi_sys::Status::napi_ok; +use napi_sys::*; +use std::ptr; + +static mut CURRENT_DEFERRED: napi_deferred = ptr::null_mut(); + +extern "C" fn test_promise_new( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut value: napi_value = ptr::null_mut(); + assert!( + unsafe { napi_create_promise(env, &mut CURRENT_DEFERRED, &mut value) } + == napi_ok + ); + value +} + +extern "C" fn test_promise_resolve( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + assert!( + unsafe { napi_resolve_deferred(env, CURRENT_DEFERRED, args[0]) } == napi_ok + ); + unsafe { CURRENT_DEFERRED = ptr::null_mut() }; + ptr::null_mut() +} + +extern "C" fn test_promise_reject( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + assert!( + unsafe { napi_reject_deferred(env, CURRENT_DEFERRED, args[0]) } == napi_ok + ); + unsafe { CURRENT_DEFERRED = ptr::null_mut() }; + ptr::null_mut() +} + +extern "C" fn test_promise_is( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut is_promise: bool = false; + assert!(unsafe { napi_is_promise(env, args[0], &mut is_promise) } == napi_ok); + + let mut result: napi_value = ptr::null_mut(); + assert!(unsafe { napi_get_boolean(env, is_promise, &mut result) } == napi_ok); + + result +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + crate::new_property!(env, "test_promise_new\0", test_promise_new), + crate::new_property!(env, "test_promise_resolve\0", test_promise_resolve), + crate::new_property!(env, "test_promise_reject\0", test_promise_reject), + crate::new_property!(env, "test_promise_is\0", test_promise_is), + ]; + + unsafe { + napi_define_properties(env, exports, properties.len(), properties.as_ptr()) + }; +} diff --git a/test_napi/src/properties.rs b/test_napi/src/properties.rs new file mode 100644 index 000000000..89d95d6c6 --- /dev/null +++ b/test_napi/src/properties.rs @@ -0,0 +1,89 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use napi_sys::PropertyAttributes::*; +use napi_sys::Status::napi_ok; +use napi_sys::*; +use std::ptr; + +pub fn init(env: napi_env, exports: napi_value) { + let mut number: napi_value = ptr::null_mut(); + assert!(unsafe { napi_create_double(env, 1.0, &mut number) } == napi_ok); + + // Key name as napi_value representing `v8::String` + let mut name_value: napi_value = ptr::null_mut(); + assert!( + unsafe { + napi_create_string_utf8( + env, + "key_v8_string".as_ptr() as *const i8, + usize::MAX, + &mut name_value, + ) + } == napi_ok + ); + + // Key symbol + let mut symbol_description: napi_value = ptr::null_mut(); + let mut name_symbol: napi_value = ptr::null_mut(); + assert!( + unsafe { + napi_create_string_utf8( + env, + "key_v8_symbol".as_ptr() as *const i8, + usize::MAX, + &mut symbol_description, + ) + } == napi_ok + ); + assert!( + unsafe { napi_create_symbol(env, symbol_description, &mut name_symbol) } + == napi_ok + ); + + let properties = &[ + napi_property_descriptor { + utf8name: "test_property_rw\0".as_ptr() as *const i8, + name: ptr::null_mut(), + method: None, + getter: None, + setter: None, + data: ptr::null_mut(), + attributes: enumerable | writable, + value: number, + }, + napi_property_descriptor { + utf8name: "test_property_r\0".as_ptr() as *const i8, + name: ptr::null_mut(), + method: None, + getter: None, + setter: None, + data: ptr::null_mut(), + attributes: enumerable, + value: number, + }, + napi_property_descriptor { + utf8name: ptr::null(), + name: name_value, + method: None, + getter: None, + setter: None, + data: ptr::null_mut(), + attributes: enumerable, + value: number, + }, + napi_property_descriptor { + utf8name: ptr::null(), + name: name_symbol, + method: None, + getter: None, + setter: None, + data: ptr::null_mut(), + attributes: enumerable, + value: number, + }, + ]; + + unsafe { + napi_define_properties(env, exports, properties.len(), properties.as_ptr()) + }; +} diff --git a/test_napi/src/strings.rs b/test_napi/src/strings.rs new file mode 100644 index 000000000..f4139c85a --- /dev/null +++ b/test_napi/src/strings.rs @@ -0,0 +1,45 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use napi_sys::Status::napi_ok; +use napi_sys::ValueType::napi_string; +use napi_sys::*; +use std::ptr; + +extern "C" fn test_utf8(env: napi_env, info: napi_callback_info) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok); + assert_eq!(ty, napi_string); + + args[0] +} + +extern "C" fn test_utf16( + env: napi_env, + info: napi_callback_info, +) -> napi_value { + let (args, argc, _) = crate::get_callback_info!(env, info, 1); + assert_eq!(argc, 1); + + let mut ty = -1; + assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok); + assert_eq!(ty, napi_string); + + args[0] +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = &[ + // utf8 + crate::new_property!(env, "test_utf8\0", test_utf8), + // utf16 + crate::new_property!(env, "test_utf16\0", test_utf16), + // latin1 + ]; + + unsafe { + napi_define_properties(env, exports, properties.len(), properties.as_ptr()) + }; +} diff --git a/test_napi/src/typedarray.rs b/test_napi/src/typedarray.rs new file mode 100644 index 000000000..f7d2e2f24 --- /dev/null +++ b/test_napi/src/typedarray.rs @@ -0,0 +1,53 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use core::ffi::c_void; +use napi_sys::Status::napi_ok; +use napi_sys::TypedarrayType::uint8_array; +use napi_sys::*; +use std::ptr; + +extern "C" fn test_external( + env: napi_env, + _info: napi_callback_info, +) -> napi_value { + let mut arraybuffer: napi_value = ptr::null_mut(); + let mut external: Box<[u8; 4]> = Box::new([0, 1, 2, 3]); + assert!( + unsafe { + napi_create_external_arraybuffer( + env, + external.as_mut_ptr() as *mut c_void, + external.len(), + None, + ptr::null_mut(), + &mut arraybuffer, + ) + } == napi_ok + ); + + let mut typedarray: napi_value = ptr::null_mut(); + assert!( + unsafe { + napi_create_typedarray( + env, + uint8_array, + external.len(), + arraybuffer, + 0, + &mut typedarray, + ) + } == napi_ok + ); + + std::mem::forget(external); // Leak into JS land + typedarray +} + +pub fn init(env: napi_env, exports: napi_value) { + let properties = + &[crate::new_property!(env, "test_external\0", test_external)]; + + unsafe { + napi_define_properties(env, exports, properties.len(), properties.as_ptr()) + }; +} diff --git a/test_napi/strings_test.js b/test_napi/strings_test.js new file mode 100644 index 000000000..20e95ba61 --- /dev/null +++ b/test_napi/strings_test.js @@ -0,0 +1,15 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const strings = loadTestLibrary(); + +Deno.test("napi string utf8", function () { + assertEquals(strings.test_utf8(""), ""); + assertEquals(strings.test_utf8("🦕"), "🦕"); +}); + +Deno.test("napi string", function () { + assertEquals(strings.test_utf16(""), ""); + assertEquals(strings.test_utf16("🦕"), "🦕"); +}); diff --git a/test_napi/tests/napi_tests.rs b/test_napi/tests/napi_tests.rs new file mode 100644 index 000000000..41ec5c851 --- /dev/null +++ b/test_napi/tests/napi_tests.rs @@ -0,0 +1,45 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use std::process::Command; +use test_util::deno_cmd; + +#[cfg(debug_assertions)] +const BUILD_VARIANT: &str = "debug"; + +#[cfg(not(debug_assertions))] +const BUILD_VARIANT: &str = "release"; + +fn build() { + let mut build_plugin_base = Command::new("cargo"); + let mut build_plugin = + build_plugin_base.arg("build").arg("-p").arg("test_napi"); + if BUILD_VARIANT == "release" { + build_plugin = build_plugin.arg("--release"); + } + let build_plugin_output = build_plugin.output().unwrap(); + assert!(build_plugin_output.status.success()); +} + +#[test] +fn napi_tests() { + build(); + + let output = deno_cmd() + .current_dir(test_util::napi_tests_path()) + .arg("test") + .arg("--allow-read") + .arg("--allow-env") + .arg("--allow-ffi") + .arg("--unstable") + .spawn() + .unwrap() + .wait_with_output() + .unwrap(); + let stdout = std::str::from_utf8(&output.stdout).unwrap(); + let stderr = std::str::from_utf8(&output.stderr).unwrap(); + if !output.status.success() { + println!("stdout {}", stdout); + println!("stderr {}", stderr); + } + assert!(output.status.success()); +} diff --git a/test_napi/typedarray_test.js b/test_napi/typedarray_test.js new file mode 100644 index 000000000..a3c7322f7 --- /dev/null +++ b/test_napi/typedarray_test.js @@ -0,0 +1,12 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +import { assertEquals, loadTestLibrary } from "./common.js"; + +const typedarray = loadTestLibrary(); + +Deno.test("napi typedarray external", function () { + assertEquals( + new Uint8Array(typedarray.test_external()), + new Uint8Array([0, 1, 2, 3]), + ); +}); |
