Skip to content

Commit

Permalink
Merge pull request #493 from goto-bus-stop/objects
Browse files Browse the repository at this point in the history
Plain data objects
  • Loading branch information
dherman committed May 14, 2020
2 parents 326c0cb + 9b338f9 commit b2d3514
Show file tree
Hide file tree
Showing 17 changed files with 437 additions and 146 deletions.
15 changes: 14 additions & 1 deletion crates/neon-runtime/src/napi/array.rs
@@ -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);
len
}
10 changes: 8 additions & 2 deletions crates/neon-runtime/src/napi/convert.rs
@@ -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
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;

*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;

*out
}
45 changes: 33 additions & 12 deletions crates/neon-runtime/src/napi/tag.rs
@@ -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!() }

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!() }

0 comments on commit b2d3514

Please sign in to comment.