diff --git a/src/node_jsvmapi.cc b/src/node_jsvmapi.cc index 33cc98dccc3d74..f5de25d699d8da 100644 --- a/src/node_jsvmapi.cc +++ b/src/node_jsvmapi.cc @@ -1,4 +1,4 @@ -/******************************************************************************* +/******************************************************************************* * Experimental prototype for demonstrating VM agnostic and ABI stable API * for native modules to use instead of using Nan and V8 APIs directly. * @@ -517,7 +517,7 @@ namespace v8impl { napi_ok : napi_set_last_error(napi_pending_exception)) // Static last error returned from napi_get_last_error_info -napi_extended_error_info static_last_error; +napi_extended_error_info static_last_error = {}; // Warning: Keep in-sync with napi_status enum const char* error_messages[] = { @@ -1881,3 +1881,194 @@ napi_status napi_buffer_length(napi_env e, napi_value v, size_t* result) { *result = node::Buffer::Length(v8impl::V8LocalValueFromJsValue(v)); return GET_RETURN_STATUS(); } + +napi_status napi_is_arraybuffer(napi_env e, napi_value value, bool* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); + + v8::Local v8value = v8impl::V8LocalValueFromJsValue(value); + *result = v8value->IsArrayBuffer(); + + return GET_RETURN_STATUS(); +} + +napi_status napi_create_arraybuffer(napi_env e, + size_t byte_length, + void** data, + napi_value* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); + + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local buffer = v8::ArrayBuffer::New(isolate, byte_length); + + // Optionally return a pointer to the buffer's data, to avoid another call to retreive it. + if (data != nullptr) { + *data = buffer->GetContents().Data(); + } + + *result = v8impl::JsValueFromV8LocalValue(buffer); + return GET_RETURN_STATUS(); +} + +napi_status napi_create_external_arraybuffer(napi_env e, + void* external_data, + size_t byte_length, + napi_value* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); + + v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e); + v8::Local buffer = + v8::ArrayBuffer::New(isolate, external_data, byte_length); + + *result = v8impl::JsValueFromV8LocalValue(buffer); + return GET_RETURN_STATUS(); +} + +napi_status napi_get_arraybuffer_info(napi_env e, + napi_value arraybuffer, + void** data, + size_t* byte_length) { + NAPI_PREAMBLE(e); + + v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); + RETURN_STATUS_IF_FALSE(value->IsArrayBuffer(), napi_invalid_arg); + + v8::ArrayBuffer::Contents contents = value.As()->GetContents(); + + if (data != nullptr) { + *data = contents.Data(); + } + + if (byte_length != nullptr) { + *byte_length = contents.ByteLength(); + } + + return GET_RETURN_STATUS(); +} + +napi_status napi_is_typedarray(napi_env e, napi_value value, bool* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); + + v8::Local v8value = v8impl::V8LocalValueFromJsValue(value); + *result = v8value->IsTypedArray(); + + return GET_RETURN_STATUS(); +} + +napi_status napi_create_typedarray(napi_env e, + napi_typedarray_type type, + size_t length, + napi_value arraybuffer, + size_t byte_offset, + napi_value* result) { + NAPI_PREAMBLE(e); + CHECK_ARG(result); + + v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); + RETURN_STATUS_IF_FALSE(value->IsArrayBuffer(), napi_invalid_arg); + + v8::Local buffer = value.As(); + v8::Local typedArray; + + switch (type) { + case napi_int8: + typedArray = v8::Int8Array::New(buffer, byte_offset, length); + break; + case napi_uint8: + typedArray = v8::Uint8Array::New(buffer, byte_offset, length); + break; + case napi_uint8_clamped: + typedArray = v8::Uint8ClampedArray::New(buffer, byte_offset, length); + break; + case napi_int16: + typedArray = v8::Int16Array::New(buffer, byte_offset, length); + break; + case napi_uint16: + typedArray = v8::Uint16Array::New(buffer, byte_offset, length); + break; + case napi_int32: + typedArray = v8::Int32Array::New(buffer, byte_offset, length); + break; + case napi_uint32: + typedArray = v8::Uint32Array::New(buffer, byte_offset, length); + break; + case napi_float32: + typedArray = v8::Float32Array::New(buffer, byte_offset, length); + break; + case napi_float64: + typedArray = v8::Float64Array::New(buffer, byte_offset, length); + break; + default: + return napi_set_last_error(napi_invalid_arg); + } + + *result = v8impl::JsValueFromV8LocalValue(typedArray); + return GET_RETURN_STATUS(); +} + +napi_status napi_get_typedarray_info(napi_env e, + napi_value typedarray, + napi_typedarray_type* type, + size_t* length, + void** data, + napi_value* arraybuffer, + size_t* byte_offset) { + NAPI_PREAMBLE(e); + + v8::Local value = v8impl::V8LocalValueFromJsValue(typedarray); + RETURN_STATUS_IF_FALSE(value->IsTypedArray(), napi_invalid_arg); + + v8::Local array = value.As(); + + if (type != nullptr) { + if (value->IsInt8Array()) { + *type = napi_int8; + } + else if (value->IsUint8Array()) { + *type = napi_uint8; + } + else if (value->IsUint8ClampedArray()) { + *type = napi_uint8_clamped; + } + else if (value->IsInt16Array()) { + *type = napi_int16; + } + else if (value->IsUint16Array()) { + *type = napi_uint16; + } + else if (value->IsInt32Array()) { + *type = napi_int32; + } + else if (value->IsUint32Array()) { + *type = napi_uint32; + } + else if (value->IsFloat32Array()) { + *type = napi_float32; + } + else if (value->IsFloat64Array()) { + *type = napi_float64; + } + } + + if (length != nullptr) { + *length = array->Length(); + } + + v8::Local buffer = array->Buffer(); + if (data != nullptr) { + *data = static_cast(buffer->GetContents().Data()) + array->ByteOffset(); + } + + if (arraybuffer != nullptr) { + *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer); + } + + if (byte_offset != nullptr) { + *byte_offset = array->ByteOffset(); + } + + return GET_RETURN_STATUS(); +} diff --git a/src/node_jsvmapi.h b/src/node_jsvmapi.h index c5443eafd996f5..52073be3902a3a 100644 --- a/src/node_jsvmapi.h +++ b/src/node_jsvmapi.h @@ -19,8 +19,6 @@ #define SRC_NODE_JSVMAPI_H_ #include "node_jsvmapi_types.h" -#include -#include #ifndef NODE_EXTERN # ifdef _WIN32 @@ -51,7 +49,7 @@ struct napi_module_struct { void* nm_context_register_func; const char* nm_modname; void* nm_priv; - struct node_module* nm_link; + void* nm_link; }; NODE_EXTERN void napi_module_register(void* mod); @@ -103,44 +101,6 @@ NODE_EXTERN void napi_module_register(void* mod); // TODO(ianhall): We're using C linkage for the API but we're also using the // bool type in these exports. Is that safe and stable? extern "C" { -enum napi_valuetype { - // ES6 types (corresponds to typeof) - napi_undefined, - napi_null, - napi_boolean, - napi_number, - napi_string, - napi_symbol, - napi_object, - napi_function, -}; - -enum napi_status { - napi_ok, - napi_invalid_arg, - napi_object_expected, - napi_string_expected, - napi_function_expected, - napi_number_expected, - napi_boolean_expected, - napi_generic_failure, - napi_pending_exception, - napi_status_last -}; - -struct napi_extended_error_info { - const char* error_message; - void* engine_reserved; - uint32_t engine_error_code; - napi_status error_code; - - napi_extended_error_info() : - error_message(NULL), - engine_reserved(NULL), - engine_error_code(0), - error_code(napi_ok) - { } -}; NODE_EXTERN const napi_extended_error_info* napi_get_last_error_info(); @@ -354,6 +314,23 @@ NODE_EXTERN napi_status napi_buffer_copy(napi_env e, NODE_EXTERN napi_status napi_buffer_has_instance(napi_env e, napi_value v, bool* result); NODE_EXTERN napi_status napi_buffer_data(napi_env e, napi_value v, char** result); NODE_EXTERN napi_status napi_buffer_length(napi_env e, napi_value v, size_t* result); + +// Methods to work with array buffers and typed arrays +NODE_EXTERN napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result); +NODE_EXTERN napi_status napi_create_arraybuffer(napi_env env, size_t byte_length, void** data, + napi_value* result); +NODE_EXTERN napi_status napi_create_external_arraybuffer(napi_env env, void* external_data, + size_t byte_length, napi_value* result); +NODE_EXTERN napi_status napi_get_arraybuffer_info(napi_env env, napi_value arraybuffer, + void** data, size_t* byte_length); +NODE_EXTERN napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result); +NODE_EXTERN napi_status napi_create_typedarray(napi_env env, napi_typedarray_type type, + size_t length, napi_value arraybuffer, + size_t byte_offset, napi_value* result); +NODE_EXTERN napi_status napi_get_typedarray_info(napi_env env, napi_value typedarray, + napi_typedarray_type* type, size_t* length, + void** data, napi_value* arraybuffer, + size_t* byte_offset); } // extern "C" #endif // SRC_NODE_JSVMAPI_H__ diff --git a/src/node_jsvmapi_types.h b/src/node_jsvmapi_types.h index 533475dc921e1e..5941a29a26aa7d 100644 --- a/src/node_jsvmapi_types.h +++ b/src/node_jsvmapi_types.h @@ -1,6 +1,8 @@ #ifndef SRC_NODE_JSVMAPI_TYPES_H_ #define SRC_NODE_JSVMAPI_TYPES_H_ +#include + // JSVM API types are all opaque pointers for ABI stability // typedef undefined structs instead of void* for compile time type safety typedef struct napi_env__ *napi_env; @@ -34,4 +36,48 @@ struct napi_property_descriptor { void* data; }; +enum napi_valuetype { + // ES6 types (corresponds to typeof) + napi_undefined, + napi_null, + napi_boolean, + napi_number, + napi_string, + napi_symbol, + napi_object, + napi_function, +}; + +enum napi_typedarray_type { + napi_int8, + napi_uint8, + napi_uint8_clamped, + napi_int16, + napi_uint16, + napi_int32, + napi_uint32, + napi_float32, + napi_float64, +}; + +enum napi_status { + napi_ok, + napi_invalid_arg, + napi_object_expected, + napi_string_expected, + napi_function_expected, + napi_number_expected, + napi_boolean_expected, + napi_generic_failure, + napi_pending_exception, + napi_status_last +}; + +struct napi_extended_error_info { + const char* error_message; + void* engine_reserved; + uint32_t engine_error_code; + napi_status error_code; +}; + #endif // SRC_NODE_JSVMAPI_TYPES_H_ diff --git a/test/addons-abi/test_typedarray/binding.gyp b/test/addons-abi/test_typedarray/binding.gyp new file mode 100644 index 00000000000000..e512fbdcdb6f38 --- /dev/null +++ b/test/addons-abi/test_typedarray/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "test_typedarray", + "sources": [ "test_typedarray.cc" ] + } + ] +} diff --git a/test/addons-abi/test_typedarray/test.js b/test/addons-abi/test_typedarray/test.js new file mode 100644 index 00000000000000..e27273321d9426 --- /dev/null +++ b/test/addons-abi/test_typedarray/test.js @@ -0,0 +1,30 @@ +'use strict'; +require('../../common'); +var assert = require('assert'); + +// Testing api calls for arrays +var test_typedarray = require('./build/Release/test_typedarray'); + +var byteArray = new Uint8Array(3); +byteArray[0] = 0; +byteArray[1] = 1; +byteArray[2] = 2; +assert.equal(byteArray.length, 3); + +var doubleArray = new Float64Array(3); +doubleArray[0] = 0.0; +doubleArray[1] = 1.1; +doubleArray[2] = 2.2; +assert.equal(doubleArray.length, 3); + +var byteResult = test_typedarray.Multiply(byteArray, 3); +assert.equal(byteResult.length, 3); +assert.equal(byteResult[0], 0); +assert.equal(byteResult[1], 3); +assert.equal(byteResult[2], 6); + +var doubleResult = test_typedarray.Multiply(doubleArray, -3); +assert.equal(doubleResult.length, 3); +assert.equal(doubleResult[0], 0); +assert.equal(Math.round(10 * doubleResult[1]) / 10, -3.3); +assert.equal(Math.round(10 * doubleResult[2]) / 10, -6.6); diff --git a/test/addons-abi/test_typedarray/test_typedarray.cc b/test/addons-abi/test_typedarray/test_typedarray.cc new file mode 100644 index 00000000000000..d2463af5aa7e00 --- /dev/null +++ b/test/addons-abi/test_typedarray/test_typedarray.cc @@ -0,0 +1,114 @@ +#include +#include + +void Multiply(napi_env env, napi_callback_info info) { + napi_status status; + + int argc; + status = napi_get_cb_args_length(env, info, &argc); + if (status != napi_ok) return; + + if (argc != 2) { + napi_throw_type_error(env, "Wrong number of arguments"); + return; + } + + napi_value args[2]; + status = napi_get_cb_args(env, info, args, 2); + if (status != napi_ok) return; + + napi_valuetype valuetype0; + status = napi_get_type_of_value(env, args[0], &valuetype0); + if (status != napi_ok) return; + + if (valuetype0 != napi_object) { + napi_throw_type_error(env, + "Wrong type of argments. Expects a typed array as first argument."); + return; + } + + napi_value input_array = args[0]; + bool istypedarray; + status = napi_is_typedarray(env, input_array, &istypedarray); + if (status != napi_ok) return; + + if (!istypedarray) { + napi_throw_type_error(env, + "Wrong type of argments. Expects a typed array as first argument."); + return; + } + + napi_valuetype valuetype1; + status = napi_get_type_of_value(env, args[1], &valuetype1); + if (status != napi_ok) return; + + if (valuetype1 != napi_number) { + napi_throw_type_error(env, + "Wrong type of argments. Expects a number as second argument."); + return; + } + + double multiplier; + status = napi_get_value_double(env, args[1], &multiplier); + if (status != napi_ok) return; + + napi_typedarray_type type; + napi_value input_buffer; + size_t byte_offset; + size_t length; + status = napi_get_typedarray_info( + env, input_array, &type, &length, nullptr, &input_buffer, &byte_offset); + if (status != napi_ok) return; + + void* data; + size_t byte_length; + status = napi_get_arraybuffer_info(env, input_buffer, &data, &byte_length); + if (status != napi_ok) return; + + napi_value output_buffer; + void* output_ptr = nullptr; + status = napi_create_arraybuffer(env, byte_length, &output_ptr, &output_buffer); + if (status != napi_ok) return; + + napi_value output_array; + status = napi_create_typedarray(env, type, length, output_buffer, byte_offset, &output_array); + if (status != napi_ok) return; + + if (type == napi_uint8) { + uint8_t* input_bytes = reinterpret_cast(data) + byte_offset; + uint8_t* output_bytes = reinterpret_cast(output_ptr); + for (size_t i = 0; i < length; i++) { + output_bytes[i] = static_cast(input_bytes[i] * multiplier); + } + } + else if (type == napi_float64) { + double* input_doubles = reinterpret_cast( + reinterpret_cast(data) + byte_offset); + double* output_doubles = reinterpret_cast(output_ptr); + for (size_t i = 0; i < length; i++) { + output_doubles[i] = input_doubles[i] * multiplier; + } + } + else { + napi_throw_error(env, "Typed array was of a type not expected by test."); + return; + } + + status = napi_set_return_value(env, info, output_array); + if (status != napi_ok) return; +} + +void Init(napi_env env, napi_value exports, napi_value module) { + napi_status status; + + napi_property_descriptor descriptors[] = { + { "Multiply", Multiply }, + }; + + for (int i = 0; i < sizeof(descriptors) / sizeof(*descriptors); i++) { + status = napi_define_property(env, exports, &descriptors[i]); + if (status != napi_ok) return; + } +} + +NODE_MODULE_ABI(addon, Init)