Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plain data objects #493

Merged
merged 19 commits into from
May 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion crates/neon-runtime/src/napi/array.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
//! Facilities for working with Array `napi_value`s.

use raw::{Local, Env};

use nodejs_sys as napi;

pub unsafe extern "C" fn new(_out: &mut Local, _env: Env, _length: u32) { unimplemented!() }

pub unsafe extern "C" fn len(_array: Local) -> u32 { unimplemented!() }
/// Gets the length of a `napi_value` containing a JavaScript Array.
///
/// # Panics
/// This function panics if `array` is not an Array, or if a previous n-api call caused a pending
/// exception.
pub unsafe extern "C" fn len(env: Env, array: Local) -> u32 {
let mut len = 0;
assert_eq!(napi::napi_get_array_length(env, array, &mut len as *mut _), napi::napi_status::napi_ok);
dherman marked this conversation as resolved.
Show resolved Hide resolved
len
}
10 changes: 8 additions & 2 deletions crates/neon-runtime/src/napi/convert.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
use raw::Local;
use nodejs_sys as napi;

use raw::{Env, Local};

pub unsafe extern "C" fn to_object(_out: &mut Local, _value: &Local) -> bool { unimplemented!() }

pub unsafe extern "C" fn to_string(_out: &mut Local, _value: Local) -> bool { unimplemented!() }
pub unsafe extern "C" fn to_string(out: &mut Local, env: Env, value: Local) -> bool {
let status = napi::napi_coerce_to_string(env, value, out as *mut _);

status == napi::napi_status::napi_ok
}
181 changes: 153 additions & 28 deletions crates/neon-runtime/src/napi/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,180 @@ use std::mem::MaybeUninit;

use nodejs_sys as napi;

use array;
use convert;
use tag;
use raw::{Env, Local};

pub unsafe extern "C" fn new(_out: &mut Local) { unimplemented!() }
/// Mutates the `out` argument to refer to a `napi_value` containing a newly created JavaScript Object.
pub unsafe extern "C" fn new(out: &mut Local, env: Env) {
napi::napi_create_object(env, out as *mut _);
}

/// Mutates the `out` argument to refer to a `napi_value` containing the own property names of the
/// `object` as a JavaScript Array.
pub unsafe extern "C" fn get_own_property_names(out: &mut Local, env: Env, object: Local) -> bool {
// Node.js 13+ have `napi_get_all_property_names`, which does the conversion right and allows
// us to ask for only own properties or prototype properties or anything we like.
// Unfortunately, earlier versions do not support that method, so we have to implement it
// manually.
//
// So we use a temporary array for the raw names:
let mut raw_names = MaybeUninit::uninit();
if napi::napi_get_property_names(env, object, raw_names.as_mut_ptr()) != napi::napi_status::napi_ok {
return false;
}
// And a "fixed" array for the actual return value:
let mut fixed_names = MaybeUninit::uninit();
if napi::napi_create_array(env, fixed_names.as_mut_ptr()) != napi::napi_status::napi_ok {
return false;
}

let raw_names = raw_names.assume_init();
let fixed_names = fixed_names.assume_init();

*out = fixed_names;

let raw_len = array::len(env, raw_names);
let mut fixed_len = 0;
for index in 0..raw_len {
let mut property_name: Local = std::mem::zeroed();

// In general, getters may cause arbitrary JS code to be run, but this is a newly created
// Array from an official internal API so it doesn't do anything strange.
if !get_index(&mut property_name, env, raw_names, index) {
continue;
}

// Before https://github.com/nodejs/node/pull/27524, `napi_get_property_names` would return
// numbers for numeric indices instead of strings.
// Make sure we always return strings.
let property_name = if !tag::is_string(env, property_name) {
let mut stringified: Local = std::mem::zeroed();
// If we can't convert to a string, something went wrong.
if !convert::to_string(&mut stringified, env, property_name) {
return false;
}
stringified
} else {
property_name
};

let mut is_own_property = false;
// May return a non-OK status if `key` is not a string or a Symbol, but here it is always
// a string.
if napi::napi_has_own_property(env, object, property_name, &mut is_own_property as *mut _) != napi::napi_status::napi_ok {
return false;
}

if !is_own_property {
continue;
}

let mut dummy = false;
// If we can't convert assign to this array, something went wrong.
if !set_index(&mut dummy, env, fixed_names, fixed_len, property_name) {
return false;
}
fixed_len += 1;
}

true
}

pub unsafe extern "C" fn get_own_property_names(_out: &mut Local, _object: Local) -> bool { unimplemented!() }
// Unused.
pub unsafe extern "C" fn get_isolate(_obj: Local) -> Env {
unimplemented!()
}

/// Mutate the `out` argument to refer to the value at `index` in the given `object`. Returns `false` if the value couldn't be retrieved.
pub unsafe extern "C" fn get_index(out: &mut Local, env: Env, object: Local, index: u32) -> bool {
let status = napi::napi_get_element(env, object, index, out as *mut _);

pub unsafe extern "C" fn get_isolate(_obj: Local) -> Env { unimplemented!() }
status == napi::napi_status::napi_ok
}

pub unsafe extern "C" fn get_index(_out: &mut Local, _object: Local, _index: u32) -> bool { unimplemented!() }
/// Sets the key value of a `napi_value` at the `index` provided. Returns `true` if the set
/// succeeded.
///
/// The `out` parameter and the return value contain the same information for historical reasons,
/// see [discussion].
///
/// [discussion]: https://github.com/neon-bindings/neon/pull/458#discussion_r344827965
pub unsafe extern "C" fn set_index(out: &mut bool, env: Env, object: Local, index: u32, val: Local) -> bool {
let status = napi::napi_set_element(env, object, index, val);
*out = status == napi::napi_status::napi_ok;
goto-bus-stop marked this conversation as resolved.
Show resolved Hide resolved

*out
}

/// Mutate the `out` argument to refer to the value at a named `key` in the given `object`. Returns `false` if the value couldn't be retrieved.
pub unsafe extern "C" fn get_string(env: Env, out: &mut Local, object: Local, key: *const u8, len: i32) -> bool {
let mut key_val = MaybeUninit::uninit();

// Not using `crate::string::new()` because it requires a _reference_ to a Local,
// while we only have uninitialized memory.
if napi::napi_create_string_utf8(env, key as *const i8, len as usize, key_val.as_mut_ptr())
!= napi::napi_status::napi_ok
{
return false;
}

pub unsafe extern "C" fn set_index(_out: &mut bool, _object: Local, _index: u32, _val: Local) -> bool { unimplemented!() }
// Not using napi_get_named_property() because the `key` may not be null terminated.
if napi::napi_get_property(env, object, key_val.assume_init(), out as *mut _)
!= napi::napi_status::napi_ok
{
return false;
}

pub unsafe extern "C" fn get_string(_out: &mut Local, _object: Local, _key: *const u8, _len: i32) -> bool { unimplemented!() }
true
}

pub unsafe extern "C" fn set_string(
env: Env,
out: &mut bool,
object: Local,
key: *const u8,
len: i32,
val: Local,
) -> bool {
/// Sets the key value of a `napi_value` at a named key. Returns `true` if the set succeeded.
///
/// The `out` parameter and the return value contain the same information for historical reasons,
/// see [discussion].
///
/// [discussion]: https://github.com/neon-bindings/neon/pull/458#discussion_r344827965
pub unsafe extern "C" fn set_string(env: Env, out: &mut bool, object: Local, key: *const u8, len: i32, val: Local) -> bool {
let mut key_val = MaybeUninit::uninit();

*out = true;

if napi::napi_create_string_utf8(
env,
key as *const i8,
len as usize,
key_val.as_mut_ptr(),
) != napi::napi_status::napi_ok {
if napi::napi_create_string_utf8(env, key as *const i8, len as usize, key_val.as_mut_ptr())
!= napi::napi_status::napi_ok
{
*out = false;
return false;
}

if napi::napi_set_property(
env,
object,
key_val.assume_init(),
val,
) != napi::napi_status::napi_ok {
if napi::napi_set_property(env, object, key_val.assume_init(), val)
!= napi::napi_status::napi_ok
{
*out = false;
return false;
}

true
}

pub unsafe extern "C" fn get(_out: &mut Local, _object: Local, _key: Local) -> bool { unimplemented!() }
/// Mutates `out` to refer to the value of the property of `object` named by the `key` value.
/// Returns false if the value couldn't be retrieved.
pub unsafe extern "C" fn get(out: &mut Local, env: Env, object: Local, key: Local) -> bool {
let status = napi::napi_get_property(env, object, key, out as *mut _);

pub unsafe extern "C" fn set(_out: &mut bool, _object: Local, _key: Local, _val: Local) -> bool { unimplemented!() }
status == napi::napi_status::napi_ok
}

/// Sets the property value of an `napi_value` object, named by another `napi_value` `key`. Returns `true` if the set succeeded.
///
/// The `out` parameter and the return value contain the same information for historical reasons,
/// see [discussion].
///
/// [discussion]: https://github.com/neon-bindings/neon/pull/458#discussion_r344827965
pub unsafe extern "C" fn set(out: &mut bool, env: Env, object: Local, key: Local, val: Local) -> bool {
let status = napi::napi_set_property(env, object, key, val);
*out = status == napi::napi_status::napi_ok;
goto-bus-stop marked this conversation as resolved.
Show resolved Hide resolved

*out
}
45 changes: 33 additions & 12 deletions crates/neon-runtime/src/napi/tag.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,44 @@
use raw::Local;
use raw::{Env, Local};

pub unsafe extern "C" fn is_undefined(_val: Local) -> bool { unimplemented!() }
use nodejs_sys as napi;

pub unsafe extern "C" fn is_null(_val: Local) -> bool { unimplemented!() }
/// Return true if an `napi_value` `val` has the expected value type.
unsafe fn is_type(env: Env, val: Local, expect: napi::napi_valuetype) -> bool {
let mut actual = napi::napi_valuetype::napi_undefined;
if napi::napi_typeof(env, val, &mut actual as *mut _) == napi::napi_status::napi_ok {
actual == expect
} else {
false
}
}

pub unsafe extern "C" fn is_number(_val: Local) -> bool { unimplemented!() }
pub unsafe extern "C" fn is_undefined(_env: Env, _val: Local) -> bool { unimplemented!() }

pub unsafe extern "C" fn is_boolean(_val: Local) -> bool { unimplemented!() }
pub unsafe extern "C" fn is_null(_env: Env, _val: Local) -> bool { unimplemented!() }

pub unsafe extern "C" fn is_string(_val: Local) -> bool { unimplemented!() }
/// Is `val` a JavaScript number?
pub unsafe extern "C" fn is_number(env: Env, val: Local) -> bool {
is_type(env, val, napi::napi_valuetype::napi_number)
}

pub unsafe extern "C" fn is_object(_val: Local) -> bool { unimplemented!() }
/// Is `val` a JavaScript boolean?
pub unsafe extern "C" fn is_boolean(env: Env, val: Local) -> bool {
is_type(env, val, napi::napi_valuetype::napi_boolean)
}

pub unsafe extern "C" fn is_array(_val: Local) -> bool { unimplemented!() }
/// Is `val` a JavaScript string?
pub unsafe extern "C" fn is_string(env: Env, val: Local) -> bool {
is_type(env, val, napi::napi_valuetype::napi_string)
}

pub unsafe extern "C" fn is_function(_val: Local) -> bool { unimplemented!() }
pub unsafe extern "C" fn is_object(_env: Env, _val: Local) -> bool { unimplemented!() }
goto-bus-stop marked this conversation as resolved.
Show resolved Hide resolved

pub unsafe extern "C" fn is_error(_val: Local) -> bool { unimplemented!() }
pub unsafe extern "C" fn is_array(_env: Env, _val: Local) -> bool { unimplemented!() }

pub unsafe extern "C" fn is_buffer(_obj: Local) -> bool { unimplemented!() }
pub unsafe extern "C" fn is_function(_env: Env, _val: Local) -> bool { unimplemented!() }

pub unsafe extern "C" fn is_arraybuffer(_obj: Local) -> bool { unimplemented!() }
pub unsafe extern "C" fn is_error(_env: Env, _val: Local) -> bool { unimplemented!() }

pub unsafe extern "C" fn is_buffer(_env: Env, _obj: Local) -> bool { unimplemented!() }

pub unsafe extern "C" fn is_arraybuffer(_env: Env, _obj: Local) -> bool { unimplemented!() }
Loading