Implement Node-API for Hermes (#61)

This is an implementation of Node-API for Hermes.

### Why do we need this change?

Currently Hermes engine does not offer an ABI safe API that can be used on DLL boundary.
It is not a problem if Hermes code is embedded into an application which uses compatible build system. Though, it does not work well in scenarios when a third-party app does not use the same build system or the same programming language such as C#. The issue can be addressed by distributing Hermes as a DLL with an ABI-safe API.

While it is possible to implement Hermes-specific ABI-safe API, this PR follows a different approach: we adopt the Node-API from Node.JS project that could be considered as an industry standard in that area. It should allow writing native code that works with any JS engine that implement the Node-API. Imagine that developers could create a native module in the language of their choice that works in Node.js, Electron, and ReactNative applications. Since Node-API is already implemented for V8 JS engine, the Node-API for Hermes implementation makes us one step closer to such goal.

We already have the JSI implemented on top of the Node-API in ReactNative for Windows project and any existing ReactNative JSI-based code could continue to work without any changes. The Node-API is C-based API and ideally must never be used directly from C++ code that may throw exceptions. C++ developers should either use JSI or the Node-API C++ wrappers from Node.JS project. In future we must be able to add a .Net wrapper for Node-API to enable use of C# with the JS engines.

### Implementation details

The core of the implementation is in the `hermes_napi.cpp` file. It is done in the single file to match the JSI implementation in `hermes.cpp`.

These files are copied from Node.js sources code and represent the core Node-API:
- `js_native_api.h`
- `js_native_api_types.h`

The `js_native_ext_api.h` is copied from the `v8-jsi` project as the extensions to Node-API required to host JS engine and to implement JSI.

The implementation has required changes to Hermes code:
- Added new `FinalizableNativeConstructor` class inherited from `NativeConstructor` to allow defining classes in native code.
- `UTF8.h` and `UTF8.cpp` - added functions to simplify conversion from UTF16 to UTF8 where a target is an arbitrary buffer instead of `std::string`.
- `JSArrayBuffer.h` and `JSArrayBuffer.cpp` - added support for external buffers.
- `StorageProvider.cpp` - fix use of a wrong variable to suppress the unused variable message.
- Fixed TypedArray property implementation for detached buffers.

### Unit tests

The NAPI unit tests were adopted from the [microsoft/v8-jsi](https://github.com/microsoft/v8-jsi) project.
They have some modifications due to some Hermes vs V8 incompatibilities:
- Hermes does not support BigInt
- Hermes does not support sparce Arrays
- Different error messages

Hermes does not support the full ES6 specification yet, and the unit tests add the Babel transforms with the presets targeting Hermes engine for ReactNative.
This commit is contained in:
Vladimir Morozov 2022-02-07 08:59:44 -08:00 коммит произвёл GitHub
Родитель 336d6c7a0a
Коммит ac2b14e2dd
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
177 изменённых файлов: 20880 добавлений и 39 удалений

Просмотреть файл

@ -87,7 +87,7 @@ function Get-VCVarsParam($plat = "x64", $arch = "win32") {
$args_ = "$args_ $SDKVersion"
}
return $args_
return $args_
}
function Get-CMakeConfiguration($config) {
@ -382,6 +382,10 @@ function Copy-Headers($SourcesPath, $WorkSpacePath, $OutputPath, $Platform, $Con
Copy-Item "$SourcesPath\API\hermes\hermes.h" -Destination "$OutputPath\build\native\include\hermes" -force
Copy-Item "$SourcesPath\API\hermes\hermes_dbg.h" -Destination "$OutputPath\build\native\include\hermes" -force
Copy-Item "$SourcesPath\API\hermes\DebuggerAPI.h" -Destination "$OutputPath\build\native\include\hermes" -force
Copy-Item "$SourcesPath\API\napi\hermes_napi.h" -Destination "$OutputPath\build\native\include\napi" -force
Copy-Item "$SourcesPath\API\napi\js_native_api.h" -Destination "$OutputPath\build\native\include\napi" -force
Copy-Item "$SourcesPath\API\napi\js_native_api_types.h" -Destination "$OutputPath\build\native\include\napi" -force
Copy-Item "$SourcesPath\API\napi\js_native_ext_api.h" -Destination "$OutputPath\build\native\include\napi" -force
Copy-Item "$SourcesPath\public\hermes\*" -Destination "$OutputPath\build\native\include\hermes" -force -Recurse
Copy-Item "$RNDIR\ReactCommon\jsinspector\*.h" -Destination "$OutputPath\build\native\include\hermesinspector" -force -Recurse
Copy-Item "$RNDIR\ReactCommon\hermes\**\*.h" -Destination "$OutputPath\build\native\include\hermesinspector" -force -Recurse

4
.gitignore поставляемый
Просмотреть файл

@ -30,3 +30,7 @@ test/ApplePlatformsIntegrationTestApp/ApplePlatformsIntegrationTestApp.xcworkspa
# Intl testing
android/intltest/java/com/facebook/hermes/test/assets/**
# VS Code and Visual Studio
.vs
.vscode

Просмотреть файл

@ -6,6 +6,7 @@
set(HERMES_ENABLE_EH ON)
set(HERMES_ENABLE_RTTI ON)
add_subdirectory(napi)
add_subdirectory(hermes)
if (HERMES_ENABLE_DEBUGGER)

Просмотреть файл

@ -5,6 +5,7 @@
set(api_sources
hermes.cpp
hermes_napi.cpp
DebuggerAPI.cpp
)
@ -13,12 +14,12 @@ file(GLOB api_public_headers ${PROJECT_SOURCE_DIR}/public/hermes/Public/*.h)
add_hermes_library(hermesapi
${api_sources}
LINK_LIBS jsi hermesVMRuntime)
LINK_LIBS jsi hermesNapi hermesVMRuntime)
target_include_directories(hermesapi PUBLIC ..)
add_hermes_library(hermesapiLean
${api_sources}
LINK_LIBS jsi hermesVMRuntimeLean)
LINK_LIBS jsi hermesNapi hermesVMRuntimeLean)
target_include_directories(hermesapiLean PUBLIC ..)
add_hermes_library(synthTrace hermes_tracing.cpp SynthTrace.cpp TracingRuntime.cpp
@ -55,6 +56,7 @@ endif()
target_link_libraries(libhermes
jsi
hermesNapi
${LIBHERMES_VM_DEP}
# Linker flags
${OPTIONAL_GC_SECTIONS}

7681
API/hermes/hermes_napi.cpp Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

13
API/napi/CMakeLists.txt Normal file
Просмотреть файл

@ -0,0 +1,13 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(hermesNapi INTERFACE)
target_include_directories(hermesNapi INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
install(DIRECTORY "${PROJECT_SOURCE_DIR}/API/napi/" DESTINATION include
FILES_MATCHING PATTERN "*.h")

4
API/napi/hermes_napi.h Normal file
Просмотреть файл

@ -0,0 +1,4 @@
#include "js_native_ext_api.h"
// Creates new Hermes napi_env with ref count 1.
NAPI_EXTERN napi_status __cdecl napi_create_hermes_env(napi_env *env);

419
API/napi/js_native_api.h Normal file
Просмотреть файл

@ -0,0 +1,419 @@
#ifndef SRC_JS_NATIVE_API_H_
#define SRC_JS_NATIVE_API_H_
// This file needs to be compatible with C compilers.
#include <stdbool.h> // NOLINT(modernize-deprecated-headers)
#include <stddef.h> // NOLINT(modernize-deprecated-headers)
// Use INT_MAX, this should only be consumed by the pre-processor anyway.
#define NAPI_VERSION_EXPERIMENTAL 2147483647
#ifndef NAPI_VERSION
#ifdef NAPI_EXPERIMENTAL
#define NAPI_VERSION NAPI_VERSION_EXPERIMENTAL
#else
// The baseline version for N-API.
// The NAPI_VERSION controls which version will be used by default when
// compilling a native addon. If the addon developer specifically wants to use
// functions available in a new version of N-API that is not yet ported in all
// LTS versions, they can set NAPI_VERSION knowing that they have specifically
// depended on that version.
#define NAPI_VERSION 8
#endif
#endif
#include "js_native_api_types.h"
// If you need __declspec(dllimport), either include <node_api.h> instead, or
// define NAPI_EXTERN as __declspec(dllimport) on the compiler's command line.
#ifndef NAPI_EXTERN
#ifdef _WIN32
#define NAPI_EXTERN __declspec(dllexport)
#elif defined(__wasm32__)
#define NAPI_EXTERN __attribute__((visibility("default"))) __attribute__((__import_module__("napi")))
#else
#define NAPI_EXTERN __attribute__((visibility("default")))
#endif
#endif
#define NAPI_AUTO_LENGTH SIZE_MAX
#ifdef __cplusplus
#define EXTERN_C_START extern "C" {
#define EXTERN_C_END }
#else
#define EXTERN_C_START
#define EXTERN_C_END
#endif
EXTERN_C_START
NAPI_EXTERN napi_status __cdecl napi_get_last_error_info(napi_env env, const napi_extended_error_info **result);
// Getters for defined singletons
NAPI_EXTERN napi_status __cdecl napi_get_undefined(napi_env env, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_get_null(napi_env env, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_get_global(napi_env env, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_get_boolean(napi_env env, bool value, napi_value *result);
// Methods to create Primitive types/Objects
NAPI_EXTERN napi_status __cdecl napi_create_object(napi_env env, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_create_array(napi_env env, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_create_array_with_length(napi_env env, size_t length, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_create_double(napi_env env, double value, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_create_int32(napi_env env, int32_t value, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_create_uint32(napi_env env, uint32_t value, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_create_int64(napi_env env, int64_t value, napi_value *result);
NAPI_EXTERN
napi_status __cdecl napi_create_string_latin1(napi_env env, const char *str, size_t length, napi_value *result);
NAPI_EXTERN
napi_status __cdecl napi_create_string_utf8(napi_env env, const char *str, size_t length, napi_value *result);
NAPI_EXTERN
napi_status __cdecl napi_create_string_utf16(napi_env env, const char16_t *str, size_t length, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_create_symbol(napi_env env, napi_value description, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_create_function(
napi_env env,
const char *utf8name,
size_t length,
napi_callback cb,
void *data,
napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_create_error(napi_env env, napi_value code, napi_value msg, napi_value *result);
NAPI_EXTERN
napi_status __cdecl napi_create_type_error(napi_env env, napi_value code, napi_value msg, napi_value *result);
NAPI_EXTERN
napi_status __cdecl napi_create_range_error(napi_env env, napi_value code, napi_value msg, napi_value *result);
// Methods to get the native napi_value from Primitive type
NAPI_EXTERN napi_status __cdecl napi_typeof(napi_env env, napi_value value, napi_valuetype *result);
NAPI_EXTERN napi_status __cdecl napi_get_value_double(napi_env env, napi_value value, double *result);
NAPI_EXTERN napi_status __cdecl napi_get_value_int32(napi_env env, napi_value value, int32_t *result);
NAPI_EXTERN napi_status __cdecl napi_get_value_uint32(napi_env env, napi_value value, uint32_t *result);
NAPI_EXTERN napi_status __cdecl napi_get_value_int64(napi_env env, napi_value value, int64_t *result);
NAPI_EXTERN napi_status __cdecl napi_get_value_bool(napi_env env, napi_value value, bool *result);
// Copies LATIN-1 encoded bytes from a string into a buffer.
NAPI_EXTERN napi_status __cdecl napi_get_value_string_latin1(
napi_env env,
napi_value value,
char *buf,
size_t bufsize,
size_t *result);
// Copies UTF-8 encoded bytes from a string into a buffer.
NAPI_EXTERN napi_status __cdecl napi_get_value_string_utf8(
napi_env env,
napi_value value,
char *buf,
size_t bufsize,
size_t *result);
// Copies UTF-16 encoded bytes from a string into a buffer.
NAPI_EXTERN napi_status __cdecl napi_get_value_string_utf16(
napi_env env,
napi_value value,
char16_t *buf,
size_t bufsize,
size_t *result);
// Methods to coerce values
// These APIs may execute user scripts
NAPI_EXTERN napi_status __cdecl napi_coerce_to_bool(napi_env env, napi_value value, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_coerce_to_number(napi_env env, napi_value value, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_coerce_to_object(napi_env env, napi_value value, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_coerce_to_string(napi_env env, napi_value value, napi_value *result);
// Methods to work with Objects
NAPI_EXTERN napi_status __cdecl napi_get_prototype(napi_env env, napi_value object, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_get_property_names(napi_env env, napi_value object, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_set_property(napi_env env, napi_value object, napi_value key, napi_value value);
NAPI_EXTERN napi_status __cdecl napi_has_property(napi_env env, napi_value object, napi_value key, bool *result);
NAPI_EXTERN napi_status __cdecl napi_get_property(napi_env env, napi_value object, napi_value key, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_delete_property(napi_env env, napi_value object, napi_value key, bool *result);
NAPI_EXTERN napi_status __cdecl napi_has_own_property(napi_env env, napi_value object, napi_value key, bool *result);
NAPI_EXTERN napi_status __cdecl napi_set_named_property(
napi_env env,
napi_value object,
const char *utf8name,
napi_value value);
NAPI_EXTERN
napi_status __cdecl napi_has_named_property(napi_env env, napi_value object, const char *utf8name, bool *result);
NAPI_EXTERN napi_status __cdecl napi_get_named_property(
napi_env env,
napi_value object,
const char *utf8name,
napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_set_element(napi_env env, napi_value object, uint32_t index, napi_value value);
NAPI_EXTERN napi_status __cdecl napi_has_element(napi_env env, napi_value object, uint32_t index, bool *result);
NAPI_EXTERN napi_status __cdecl napi_get_element(napi_env env, napi_value object, uint32_t index, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_delete_element(napi_env env, napi_value object, uint32_t index, bool *result);
NAPI_EXTERN napi_status __cdecl napi_define_properties(
napi_env env,
napi_value object,
size_t property_count,
const napi_property_descriptor *properties);
// Methods to work with Arrays
NAPI_EXTERN napi_status __cdecl napi_is_array(napi_env env, napi_value value, bool *result);
NAPI_EXTERN napi_status __cdecl napi_get_array_length(napi_env env, napi_value value, uint32_t *result);
// Methods to compare values
NAPI_EXTERN napi_status __cdecl napi_strict_equals(napi_env env, napi_value lhs, napi_value rhs, bool *result);
// Methods to work with Functions
NAPI_EXTERN napi_status __cdecl napi_call_function(
napi_env env,
napi_value recv,
napi_value func,
size_t argc,
const napi_value *argv,
napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_new_instance(
napi_env env,
napi_value constructor,
size_t argc,
const napi_value *argv,
napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_instanceof(napi_env env, napi_value object, napi_value constructor, bool *result);
// Methods to work with napi_callbacks
// Gets all callback info in a single call. (Ugly, but faster.)
NAPI_EXTERN napi_status __cdecl napi_get_cb_info(
napi_env env, // [in] NAPI environment handle
napi_callback_info cbinfo, // [in] Opaque callback-info handle
size_t *argc, // [in-out] Specifies the size of the provided argv array
// and receives the actual count of args.
napi_value *argv, // [out] Array of values
napi_value *this_arg, // [out] Receives the JS 'this' arg for the call
void **data); // [out] Receives the data pointer for the callback.
NAPI_EXTERN napi_status __cdecl napi_get_new_target(napi_env env, napi_callback_info cbinfo, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_define_class(
napi_env env,
const char *utf8name,
size_t length,
napi_callback constructor,
void *data,
size_t property_count,
const napi_property_descriptor *properties,
napi_value *result);
// Methods to work with external data objects
NAPI_EXTERN napi_status __cdecl napi_wrap(
napi_env env,
napi_value js_object,
void *native_object,
napi_finalize finalize_cb,
void *finalize_hint,
napi_ref *result);
NAPI_EXTERN napi_status __cdecl napi_unwrap(napi_env env, napi_value js_object, void **result);
NAPI_EXTERN napi_status __cdecl napi_remove_wrap(napi_env env, napi_value js_object, void **result);
NAPI_EXTERN napi_status __cdecl napi_create_external(
napi_env env,
void *data,
napi_finalize finalize_cb,
void *finalize_hint,
napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_get_value_external(napi_env env, napi_value value, void **result);
// Methods to control object lifespan
// Set initial_refcount to 0 for a weak reference, >0 for a strong reference.
NAPI_EXTERN napi_status __cdecl napi_create_reference(
napi_env env,
napi_value value,
uint32_t initial_refcount,
napi_ref *result);
// Deletes a reference. The referenced value is released, and may
// be GC'd unless there are other references to it.
NAPI_EXTERN napi_status __cdecl napi_delete_reference(napi_env env, napi_ref ref);
// Increments the reference count, optionally returning the resulting count.
// After this call the reference will be a strong reference because its
// refcount is >0, and the referenced object is effectively "pinned".
// Calling this when the refcount is 0 and the object is unavailable
// results in an error.
NAPI_EXTERN napi_status __cdecl napi_reference_ref(napi_env env, napi_ref ref, uint32_t *result);
// Decrements the reference count, optionally returning the resulting count.
// If the result is 0 the reference is now weak and the object may be GC'd
// at any time if there are no other references. Calling this when the
// refcount is already 0 results in an error.
NAPI_EXTERN napi_status __cdecl napi_reference_unref(napi_env env, napi_ref ref, uint32_t *result);
// Attempts to get a referenced value. If the reference is weak,
// the value might no longer be available, in that case the call
// is still successful but the result is NULL.
NAPI_EXTERN napi_status __cdecl napi_get_reference_value(napi_env env, napi_ref ref, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_open_handle_scope(napi_env env, napi_handle_scope *result);
NAPI_EXTERN napi_status __cdecl napi_close_handle_scope(napi_env env, napi_handle_scope scope);
NAPI_EXTERN napi_status __cdecl napi_open_escapable_handle_scope(napi_env env, napi_escapable_handle_scope *result);
NAPI_EXTERN napi_status __cdecl napi_close_escapable_handle_scope(napi_env env, napi_escapable_handle_scope scope);
NAPI_EXTERN napi_status __cdecl napi_escape_handle(
napi_env env,
napi_escapable_handle_scope scope,
napi_value escapee,
napi_value *result);
// Methods to support error handling
NAPI_EXTERN napi_status __cdecl napi_throw(napi_env env, napi_value error);
NAPI_EXTERN napi_status __cdecl napi_throw_error(napi_env env, const char *code, const char *msg);
NAPI_EXTERN napi_status __cdecl napi_throw_type_error(napi_env env, const char *code, const char *msg);
NAPI_EXTERN napi_status __cdecl napi_throw_range_error(napi_env env, const char *code, const char *msg);
NAPI_EXTERN napi_status __cdecl napi_is_error(napi_env env, napi_value value, bool *result);
// Methods to support catching exceptions
NAPI_EXTERN napi_status __cdecl napi_is_exception_pending(napi_env env, bool *result);
NAPI_EXTERN napi_status __cdecl napi_get_and_clear_last_exception(napi_env env, napi_value *result);
// Methods to work with array buffers and typed arrays
NAPI_EXTERN napi_status __cdecl napi_is_arraybuffer(napi_env env, napi_value value, bool *result);
NAPI_EXTERN
napi_status __cdecl napi_create_arraybuffer(napi_env env, size_t byte_length, void **data, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_create_external_arraybuffer(
napi_env env,
void *external_data,
size_t byte_length,
napi_finalize finalize_cb,
void *finalize_hint,
napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_get_arraybuffer_info(
napi_env env,
napi_value arraybuffer,
void **data,
size_t *byte_length);
NAPI_EXTERN napi_status __cdecl napi_is_typedarray(napi_env env, napi_value value, bool *result);
NAPI_EXTERN napi_status __cdecl napi_create_typedarray(
napi_env env,
napi_typedarray_type type,
size_t length,
napi_value arraybuffer,
size_t byte_offset,
napi_value *result);
NAPI_EXTERN napi_status __cdecl 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);
NAPI_EXTERN napi_status __cdecl napi_create_dataview(
napi_env env,
size_t length,
napi_value arraybuffer,
size_t byte_offset,
napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_is_dataview(napi_env env, napi_value value, bool *result);
NAPI_EXTERN napi_status __cdecl napi_get_dataview_info(
napi_env env,
napi_value dataview,
size_t *bytelength,
void **data,
napi_value *arraybuffer,
size_t *byte_offset);
// version management
NAPI_EXTERN napi_status __cdecl napi_get_version(napi_env env, uint32_t *result);
// Promises
NAPI_EXTERN napi_status __cdecl napi_create_promise(napi_env env, napi_deferred *deferred, napi_value *promise);
NAPI_EXTERN napi_status __cdecl napi_resolve_deferred(napi_env env, napi_deferred deferred, napi_value resolution);
NAPI_EXTERN napi_status __cdecl napi_reject_deferred(napi_env env, napi_deferred deferred, napi_value rejection);
NAPI_EXTERN napi_status __cdecl napi_is_promise(napi_env env, napi_value value, bool *is_promise);
// Running a script
NAPI_EXTERN napi_status __cdecl napi_run_script(napi_env env, napi_value script, napi_value *result);
// Memory management
NAPI_EXTERN
napi_status __cdecl napi_adjust_external_memory(napi_env env, int64_t change_in_bytes, int64_t *adjusted_value);
#if NAPI_VERSION >= 5
// Dates
NAPI_EXTERN napi_status __cdecl napi_create_date(napi_env env, double time, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_is_date(napi_env env, napi_value value, bool *is_date);
NAPI_EXTERN napi_status __cdecl napi_get_date_value(napi_env env, napi_value value, double *result);
// Add finalizer for pointer
NAPI_EXTERN napi_status __cdecl napi_add_finalizer(
napi_env env,
napi_value js_object,
void *native_object,
napi_finalize finalize_cb,
void *finalize_hint,
napi_ref *result);
#endif // NAPI_VERSION >= 5
#if NAPI_VERSION >= 6
// BigInt
NAPI_EXTERN napi_status __cdecl napi_create_bigint_int64(napi_env env, int64_t value, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_create_bigint_uint64(napi_env env, uint64_t value, napi_value *result);
NAPI_EXTERN napi_status __cdecl napi_create_bigint_words(
napi_env env,
int sign_bit,
size_t word_count,
const uint64_t *words,
napi_value *result);
NAPI_EXTERN
napi_status __cdecl napi_get_value_bigint_int64(napi_env env, napi_value value, int64_t *result, bool *lossless);
NAPI_EXTERN
napi_status __cdecl napi_get_value_bigint_uint64(napi_env env, napi_value value, uint64_t *result, bool *lossless);
NAPI_EXTERN napi_status __cdecl napi_get_value_bigint_words(
napi_env env,
napi_value value,
int *sign_bit,
size_t *word_count,
uint64_t *words);
// Object
NAPI_EXTERN napi_status __cdecl napi_get_all_property_names(
napi_env env,
napi_value object,
napi_key_collection_mode key_mode,
napi_key_filter key_filter,
napi_key_conversion key_conversion,
napi_value *result);
// Instance data
NAPI_EXTERN napi_status __cdecl napi_set_instance_data(
napi_env env,
void *data,
napi_finalize finalize_cb,
void *finalize_hint);
NAPI_EXTERN napi_status __cdecl napi_get_instance_data(napi_env env, void **data);
#endif // NAPI_VERSION >= 6
#if NAPI_VERSION >= 7
// ArrayBuffer detaching
NAPI_EXTERN napi_status __cdecl napi_detach_arraybuffer(napi_env env, napi_value arraybuffer);
NAPI_EXTERN napi_status __cdecl napi_is_detached_arraybuffer(napi_env env, napi_value value, bool *result);
#endif // NAPI_VERSION >= 7
#if NAPI_VERSION >= 8
// Type tagging
NAPI_EXTERN napi_status __cdecl napi_type_tag_object(napi_env env, napi_value value, const napi_type_tag *type_tag);
NAPI_EXTERN napi_status __cdecl napi_check_object_type_tag(
napi_env env,
napi_value value,
const napi_type_tag *type_tag,
bool *result);
NAPI_EXTERN napi_status __cdecl napi_object_freeze(napi_env env, napi_value object);
NAPI_EXTERN napi_status __cdecl napi_object_seal(napi_env env, napi_value object);
#endif // NAPI_VERSION >= 8
EXTERN_C_END
#endif // SRC_JS_NATIVE_API_H_

Просмотреть файл

@ -0,0 +1,149 @@
#ifndef SRC_JS_NATIVE_API_TYPES_H_
#define SRC_JS_NATIVE_API_TYPES_H_
// This file needs to be compatible with C compilers.
// This is a public include file, and these includes have essentially
// became part of it's API.
#include <stddef.h> // NOLINT(modernize-deprecated-headers)
#include <stdint.h> // NOLINT(modernize-deprecated-headers)
#if !defined __cplusplus || (defined(_MSC_VER) && _MSC_VER < 1900)
typedef uint16_t char16_t;
#endif
// 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;
typedef struct napi_value__ *napi_value;
typedef struct napi_ref__ *napi_ref;
typedef struct napi_handle_scope__ *napi_handle_scope;
typedef struct napi_escapable_handle_scope__ *napi_escapable_handle_scope;
typedef struct napi_callback_info__ *napi_callback_info;
typedef struct napi_deferred__ *napi_deferred;
typedef enum {
napi_default = 0,
napi_writable = 1 << 0,
napi_enumerable = 1 << 1,
napi_configurable = 1 << 2,
// Used with napi_define_class to distinguish static properties
// from instance properties. Ignored by napi_define_properties.
napi_static = 1 << 10,
#if NAPI_VERSION >= 8
// Default for class methods.
napi_default_method = napi_writable | napi_configurable,
// Default for object properties, like in JS obj[prop].
napi_default_jsproperty = napi_writable | napi_enumerable | napi_configurable,
#endif // NAPI_VERSION >= 8
} napi_property_attributes;
typedef enum {
// ES6 types (corresponds to typeof)
napi_undefined,
napi_null,
napi_boolean,
napi_number,
napi_string,
napi_symbol,
napi_object,
napi_function,
napi_external,
napi_bigint,
} napi_valuetype;
typedef enum {
napi_int8_array,
napi_uint8_array,
napi_uint8_clamped_array,
napi_int16_array,
napi_uint16_array,
napi_int32_array,
napi_uint32_array,
napi_float32_array,
napi_float64_array,
napi_bigint64_array,
napi_biguint64_array,
} napi_typedarray_type;
typedef enum {
napi_ok,
napi_invalid_arg,
napi_object_expected,
napi_string_expected,
napi_name_expected,
napi_function_expected,
napi_number_expected,
napi_boolean_expected,
napi_array_expected,
napi_generic_failure,
napi_pending_exception,
napi_cancelled,
napi_escape_called_twice,
napi_handle_scope_mismatch,
napi_callback_scope_mismatch,
napi_queue_full,
napi_closing,
napi_bigint_expected,
napi_date_expected,
napi_arraybuffer_expected,
napi_detachable_arraybuffer_expected,
napi_would_deadlock // unused
} napi_status;
// Note: when adding a new enum value to `napi_status`, please also update
// * `const int last_status` in the definition of `napi_get_last_error_info()'
// in file js_native_api_v8.cc.
// * `const char* error_messages[]` in file js_native_api_v8.cc with a brief
// message explaining the error.
// * the definition of `napi_status` in doc/api/n-api.md to reflect the newly
// added value(s).
typedef napi_value (*napi_callback)(napi_env env, napi_callback_info info);
typedef void (*napi_finalize)(napi_env env, void *finalize_data, void *finalize_hint);
typedef struct {
// One of utf8name or name should be NULL.
const char *utf8name;
napi_value name;
napi_callback method;
napi_callback getter;
napi_callback setter;
napi_value value;
napi_property_attributes attributes;
void *data;
} napi_property_descriptor;
typedef struct {
const char *error_message;
void *engine_reserved;
uint32_t engine_error_code;
napi_status error_code;
} napi_extended_error_info;
#if NAPI_VERSION >= 6
typedef enum { napi_key_include_prototypes, napi_key_own_only } napi_key_collection_mode;
typedef enum {
napi_key_all_properties = 0,
napi_key_writable = 1,
napi_key_enumerable = 1 << 1,
napi_key_configurable = 1 << 2,
napi_key_skip_strings = 1 << 3,
napi_key_skip_symbols = 1 << 4
} napi_key_filter;
typedef enum { napi_key_keep_numbers, napi_key_numbers_to_strings } napi_key_conversion;
#endif // NAPI_VERSION >= 6
#if NAPI_VERSION >= 8
typedef struct {
uint64_t lower;
uint64_t upper;
} napi_type_tag;
#endif // NAPI_VERSION >= 8
#endif // SRC_JS_NATIVE_API_TYPES_H_

Просмотреть файл

@ -0,0 +1,285 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#define NAPI_EXPERIMENTAL
#include "js_native_api.h"
//
// N-API extensions required for JavaScript engine hosting.
//
// It is a very early version of the APIs which we consider to be experimental.
// These APIs are not stable yet and are subject to change while we continue
// their development. After some time we will stabilize the APIs and make them
// "officially stable".
//
EXTERN_C_START
// TODO: Revise relevance. Likely obsoleted by napi_ext_env_settings::flags
typedef enum {
napi_ext_env_attribute_none = 0x00000000,
napi_ext_env_attribute_enable_gc_api = 0x00000001,
napi_ext_env_attribute_ignore_unhandled_promises = 0x00000002,
} napi_ext_env_attributes;
typedef struct napi_ext_env_scope__ *napi_ext_env_scope;
typedef struct napi_ext_ref__ *napi_ext_ref;
// A callback to run task
typedef void (*napi_ext_task_callback)(napi_env env, void *task_data);
// A callback to schedule a task
typedef void (*napi_ext_schedule_task_callback)(
napi_env env,
napi_ext_task_callback task_cb,
void *task_data,
uint32_t delay_in_msec,
napi_finalize finalize_cb,
void *finalize_hint);
typedef struct _napi_ext_env_settings {
// Size of this struct to allow extending it in future.
size_t this_size;
// Custom scheduler of the foreground JavaScript tasks.
napi_ext_schedule_task_callback foreground_scheduler;
// The environment attributes.
napi_ext_env_attributes attributes;
uint16_t inspector_port{9223};
size_t initial_heap_size_in_bytes{0};
size_t maximum_heap_size_in_bytes{0};
// Custom data associated with the environment.
void *data;
// The callback to call to destroy the custom data.
napi_finalize finalize_data_cb;
// Additional data for the finalize callback.
void *finalize_data_hint;
// NOTE: Keep in sync with v8runtime::V8RuntimeArgs::flags
// Padded to allow adding boolean flags without breaking the ABI
union {
struct {
bool track_gc_object_stats : 1;
bool enable_jit_tracing : 1;
bool enable_message_tracing : 1;
bool enable_gc_tracing : 1;
bool enable_inspector : 1;
bool wait_for_debugger : 1;
bool enable_gc_api : 1;
bool ignore_unhandled_promises : 1;
bool enable_system_instrumentation : 1;
// Experimental flags (for memory-constrained optimization testing)
bool sparkplug : 1; // https://v8.dev/blog/sparkplug
bool
predictable : 1; // take a big CPU hit to reduce the number of threads
bool optimize_for_size : 1; // enables optimizations which favor memory
// size over execution speed
bool always_compact : 1; // perform compaction on every full GC
bool jitless : 1; // disable JIT entirely
} flags;
uint32_t _flagspad{0};
};
} napi_ext_env_settings;
// Creates a new napi_env with ref count 1.
NAPI_EXTERN napi_status __cdecl napi_ext_create_env(
napi_ext_env_settings *settings,
napi_env *env);
// Increments the napi_env ref count by 1.
NAPI_EXTERN napi_status __cdecl napi_ext_env_ref(napi_env env);
// Decrements the napi_env ref count by 1. If the ref count becomes 0, then the
// napi_env is deleted.
NAPI_EXTERN napi_status __cdecl napi_ext_env_unref(napi_env env);
// Opens the napi_env in the current thread.
// Calling N-API functions without the opened scope may cause a failure.
// The scope must be closed by the napi_ext_close_env_scope call.
NAPI_EXTERN napi_status __cdecl napi_ext_open_env_scope(
napi_env env,
napi_ext_env_scope *result);
// Closes the napi_env in the current thread. It must match to the
// napi_ext_open_env_scope call.
NAPI_EXTERN napi_status __cdecl napi_ext_close_env_scope(
napi_env env,
napi_ext_env_scope scope);
// Provides a hint to run garbage collection.
// It is typically used for unit tests.
NAPI_EXTERN napi_status __cdecl napi_ext_collect_garbage(napi_env env);
// Checks if the environment has an unhandled promise rejection.
NAPI_EXTERN napi_status __cdecl napi_ext_has_unhandled_promise_rejection(
napi_env env,
bool *result);
// Gets and clears the last unhandled promise rejection.
NAPI_EXTERN
napi_status __cdecl napi_get_and_clear_last_unhandled_promise_rejection(
napi_env env,
napi_value *result);
// Use to enable fast string equality check by comparing napi_refs as addresses.
// The caller is responsible for calling napi_reference_unref on the result
// after the use. The caller must not call the napi_delete_reference.
NAPI_EXTERN napi_status __cdecl napi_ext_get_unique_string_utf8_ref(
napi_env env,
const char *str,
size_t length,
napi_ext_ref *result);
// Gets an unique string reference.
NAPI_EXTERN
napi_status __cdecl napi_ext_get_unique_string_ref(
napi_env env,
napi_value str_value,
napi_ext_ref *result);
// Methods to control object lifespan.
// The NAPI's napi_ref can be used only for objects.
// The napi_ext_ref can be used for any value type.
// Creates new napi_ext_ref with ref counter set to 1.
NAPI_EXTERN napi_status __cdecl napi_ext_create_reference(
napi_env env,
napi_value value,
napi_ext_ref *result);
// Creates new napi_ext_ref and associates native data with the reference.
// The ref counter is set to 1.
NAPI_EXTERN napi_status __cdecl napi_ext_create_reference_with_data(
napi_env env,
napi_value value,
void *native_object,
napi_finalize finalize_cb,
void *finalize_hint,
napi_ext_ref *result);
// Creates new napi_ext_ref with ref counter set to 1.
// The napi_ext_ref wraps up a weak reference to the value.
// Even if the napi_ext_ref ref counter is more than 0, the internal weak
// reference can exptire and the napi_ext_get_reference_value for this
// napi_ext_ref may return nullptr.
NAPI_EXTERN napi_status __cdecl napi_ext_create_weak_reference(
napi_env env,
napi_value value,
napi_ext_ref *result);
// Increments the reference count.
NAPI_EXTERN napi_status __cdecl napi_ext_reference_ref(
napi_env env,
napi_ext_ref ref);
// Decrements the reference count.
// The provided ref must not be used after this call because it could be deleted
// if the internal ref counter became zero.
NAPI_EXTERN napi_status __cdecl napi_ext_reference_unref(
napi_env env,
napi_ext_ref ref);
// Gets the referenced value.
NAPI_EXTERN napi_status __cdecl napi_ext_get_reference_value(
napi_env env,
napi_ext_ref ref,
napi_value *result);
//=============================================================================
// Script running, preparing, and serialization.
//
// Script is usually converted to byte code, or in other words - prepared - for
// execution. The APIs below allow not only running the script, but also control
// its preparation phase where we can explicitly prepare the script for running,
// run the prepared script, and serialize or deserialize the prepared script.
//=============================================================================
typedef struct napi_ext_prepared_script__ *napi_ext_prepared_script;
typedef struct {
void *data;
size_t byte_length;
napi_finalize finalize_cb;
void *finalize_hint;
} napi_ext_buffer;
// A callback to return buffer synchronously.
typedef void (*napi_ext_buffer_callback)(
napi_env env,
const uint8_t *buffer,
size_t buffer_length,
void *buffer_hint);
// [DEPRECATED] - use napi_ext_run_script_with_source_map method.
// Run script with the provided source_url origin.
NAPI_EXTERN napi_status __cdecl napi_ext_run_script(
napi_env env,
napi_value source,
const char *source_url,
napi_value *result);
// [DEPRECATED] - use napi_ext_prepare_script_with_source_map with
// napi_ext_run_prepared_script methods.
// Deserialize prepared script and run it.
NAPI_EXTERN napi_status __cdecl napi_ext_run_serialized_script(
napi_env env,
const uint8_t *buffer,
size_t buffer_length,
napi_value source,
const char *source_url,
napi_value *result);
// [DEPRECATED] - use napi_ext_prepare_script_with_source_map with
// napi_ext_serialize_prepared_script methods.
// Prepare the script and serialize it into a buffer.
NAPI_EXTERN napi_status __cdecl napi_ext_serialize_script(
napi_env env,
napi_value source,
const char *source_url,
napi_ext_buffer_callback buffer_cb,
void *buffer_hint);
// Run the script with the source map that can be used for the script debugging.
NAPI_EXTERN napi_status __cdecl napi_ext_run_script_with_source_map(
napi_env env,
napi_ext_buffer script,
napi_ext_buffer source_map,
const char *source_url,
napi_value *result);
// Prepare the script for running.
NAPI_EXTERN napi_status __cdecl napi_ext_prepare_script_with_source_map(
napi_env env,
napi_ext_buffer script,
napi_ext_buffer source_map,
const char *source_url,
napi_ext_prepared_script *prepared_script);
// Run the prepared script.
NAPI_EXTERN napi_status __cdecl napi_ext_run_prepared_script(
napi_env env,
napi_ext_prepared_script prepared_script,
napi_value *result);
// Delete the prepared script.
NAPI_EXTERN napi_status __cdecl napi_ext_delete_prepared_script(
napi_env env,
napi_ext_prepared_script prepared_script);
// Serialize the prepared script.
NAPI_EXTERN napi_status __cdecl napi_ext_serialize_prepared_script(
napi_env env,
napi_ext_prepared_script prepared_script,
napi_ext_buffer_callback buffer_cb,
void *buffer_hint);
EXTERN_C_END

Просмотреть файл

@ -445,6 +445,14 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4201")
# C4530 C++ exception handler used, but unwind semantics are not enabled
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4530")
# C4251 class X needs to have dll-interface to be used by clients of class Y
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4251")
# C4275: non dll-interface class X used as base for dll-interface class Y
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4275")
# C4646: function declared with 'noreturn' has non-void return type
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4646")
# C4312: 'reinterpret_cast': conversion from 'X' to 'hermes::vm::GCCell *' of greater size
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4312")
# Parallelize build
if (HERMES_MSVC_MP)
add_definitions( /MP )

Просмотреть файл

@ -214,7 +214,7 @@ inline OutIt convertUTF8WithSurrogatesToUTF16(
}
/// Convert a UTF-16 encoded string \p input to UTF-8 stored in \p dest,
/// encoding each surrogate halves individully into UTF-8.
/// encoding each surrogate halves individually into UTF-8.
/// This is the inverse function of convertUTF8WithSurrogatesToUTF16.
/// Note the result is not valid utf-8 if it contains surrogate values.
/// Only use it to get the internal representation of utf-8 strings in hermes
@ -223,6 +223,21 @@ void convertUTF16ToUTF8WithSingleSurrogates(
std::string &dest,
llvh::ArrayRef<char16_t> input);
/// Return length of UTF-8 string after converting a UTF-16 encoded string \p
/// input to UTF-8, replacing unpaired surrogates halves with the Unicode
/// replacement character. The length does not include the terminating '\0'
/// character.
size_t utf8LengthWithReplacements(llvh::ArrayRef<char16_t> input);
/// Convert a UTF-16 encoded string \p input to UTF-8 stored in \p buf,
/// replacing unpaired surrogates halves with the Unicode replacement character.
/// The terminating '\0' is not written.
/// \return number of bytes written to \p buf.
size_t convertUTF16ToUTF8WithReplacements(
llvh::ArrayRef<char16_t> input,
char *buf,
size_t bufSize);
/// Convert a UTF-16 encoded string \p input to UTF-8 stored in \p dest,
/// replacing unpaired surrogates halves with the Unicode replacement character.
/// \param maxCharacters If non-zero, the maximum number of characters to

Просмотреть файл

@ -373,7 +373,7 @@ class BoundFunction final : public Callable {
static CallResult<PseudoHandle<>>
_boundCall(BoundFunction *self, const Inst *ip, Runtime *runtime);
/// Intialize the length and name and property of a lazily created bound
/// Initialize the length and name and property of a lazily created bound
/// function.
static ExecutionStatus initializeLengthAndName(
Handle<Callable> selfHandle,
@ -671,7 +671,7 @@ class NativeFunction : public Callable {
/// A NativeFunction to be used as a constructor for native objects other than
/// Object.
class NativeConstructor final : public NativeFunction {
class NativeConstructor : public NativeFunction {
public:
/// A CreatorFunction is responsible for creating the 'this' object that the
/// constructor function sees.
@ -694,7 +694,10 @@ class NativeConstructor final : public NativeFunction {
static const CallableVTable vt;
static bool classof(const GCCell *cell) {
return cell->getKind() == CellKind::NativeConstructorKind;
return kindInRange(
cell->getKind(),
CellKind::NativeConstructorKind_first,
CellKind::NativeConstructorKind_last);
}
/// Create an instance of NativeConstructor.
@ -763,6 +766,23 @@ class NativeConstructor final : public NativeFunction {
/// Typically passed by invoking NativeConstructor::creatorFunction<T>.
CreatorFunction *const creator_;
protected:
NativeConstructor(
Runtime *runtime,
const VTable *vtp,
Handle<JSObject> parent,
Handle<HiddenClass> clazz,
void *context,
NativeFunctionPtr functionPtr,
CreatorFunction *creator,
CellKind targetKind)
: NativeFunction(runtime, vtp, parent, clazz, context, functionPtr),
#ifndef NDEBUG
targetKind_(targetKind),
#endif
creator_(creator) {
}
public:
NativeConstructor(
Runtime *runtime,
@ -808,7 +828,7 @@ class NativeConstructor final : public NativeFunction {
creator_(creator) {
}
private:
protected:
/// Create a an instance of an object from \c creator_ to be passed as the
/// 'this' argument when invoking the constructor.
static CallResult<PseudoHandle<JSObject>> _newObjectImpl(

Просмотреть файл

@ -87,6 +87,7 @@ CELL_CLASS(Proxy, "Proxy")
CELL_KIND(BoundFunction)
CELL_KIND(NativeFunction)
CELL_KIND(NativeConstructor)
CELL_KIND(FinalizableNativeConstructor)
CELL_CLASS(CallableProxy, "CallableProxy")
CELL_KIND(FinalizableNativeFunction)
CELL_KIND(GeneratorFunction)
@ -108,6 +109,7 @@ CELL_RANGE(ArrayImpl, Arguments, Array)
CELL_RANGE(Callable, BoundFunction, Function)
CELL_RANGE(CodeBlockFunction, GeneratorFunction, Function)
CELL_RANGE(NativeFunction, NativeFunction, FinalizableNativeFunction)
CELL_RANGE(NativeConstructor, NativeConstructor, FinalizableNativeConstructor)
CELL_RANGE(TypedArrayBase, Int8Array, Float64Array)
CELL_RANGE(WeakMapImplBase, WeakMap, WeakSet)
CELL_RANGE(

Просмотреть файл

@ -35,6 +35,7 @@ HERMES_VM_GCOBJECT(BoundFunction);
HERMES_VM_GCOBJECT(NativeFunction);
HERMES_VM_GCOBJECT(FinalizableNativeFunction);
HERMES_VM_GCOBJECT(NativeConstructor);
HERMES_VM_GCOBJECT(FinalizableNativeConstructor);
HERMES_VM_GCOBJECT(JSFunction);
HERMES_VM_GCOBJECT(JSGeneratorFunction);
HERMES_VM_GCOBJECT(JSAsyncFunction);

Просмотреть файл

@ -75,6 +75,72 @@ class FinalizableNativeFunction final : public NativeFunction {
}
};
/// This class represents a host constructor callable from JavaScript.
/// This is nearly the same as a NativeConstructor, except an additional
/// function pointer can do cleanup when the FinalizableNativeFunction is
/// finalized.
class FinalizableNativeConstructor final : public NativeConstructor {
FinalizeNativeFunctionPtr finalizePtr_;
public:
static const CallableVTable vt;
static bool classof(const GCCell *cell) {
return cell->getKind() == CellKind::FinalizableNativeConstructorKind;
}
/// Create an instance of FinalizableNativeConstructor.
/// \param context the context pointer to be passed to the native function
/// \param functionPtr the host function
/// \param finalizePtr the finalizer function
/// \param prototypeHandle the prototype of constructed object
/// \param name the name of the constructor function
/// \param paramCount number of parameters (excluding `this`)
static CallResult<Handle<FinalizableNativeConstructor>> create(
Runtime *runtime,
void *context,
NativeFunctionPtr functionPtr,
FinalizeNativeFunctionPtr finalizePtr,
Handle<JSObject> prototypeObjectHandle,
SymbolID name,
unsigned paramCount);
void *getContext() {
return context_;
}
FinalizableNativeConstructor(
Runtime *runtime,
Handle<JSObject> parent,
Handle<HiddenClass> clazz,
void *context,
NativeFunctionPtr functionPtr,
FinalizeNativeFunctionPtr finalizePtr,
CreatorFunction *creator,
CellKind targetKind)
: NativeConstructor(
runtime,
&vt.base.base,
parent,
clazz,
context,
functionPtr,
creator,
targetKind),
finalizePtr_(finalizePtr) {}
protected:
~FinalizableNativeConstructor() {
finalizePtr_(context_);
}
static void _finalizeImpl(GCCell *cell, GC *) {
auto *self = vmcast<FinalizableNativeConstructor>(cell);
// Destruct the object.
self->~FinalizableNativeConstructor();
}
};
/// When a HostObject is created, this proxy provides the business
/// logic for the HostObject's implementation.
class HostObjectProxy : public DecoratedObject::Decoration {

Просмотреть файл

@ -59,6 +59,12 @@ class JSArrayBuffer final : public JSObject {
ExecutionStatus
createDataBlock(Runtime *runtime, size_type size, bool zero = true);
/// Sets data block to the external buffer for this JSArrayBuffer to hold.
/// Replaces the currently used data block.
void setExternalBuffer(
Runtime *runtime,
std::unique_ptr<Buffer> externalBuffer);
/// Retrieves a pointer to the held buffer.
/// \return A pointer to the buffer owned by this object. This can be null
/// if the ArrayBuffer is empty.
@ -95,6 +101,7 @@ class JSArrayBuffer final : public JSObject {
private:
uint8_t *data_;
size_type size_;
std::unique_ptr<Buffer> externalBuffer_;
bool attached_;
public:

Просмотреть файл

@ -63,6 +63,80 @@ void encodeUTF8(char *&dst, uint32_t cp) {
dst = d;
}
char32_t readUTF16WithReplacements(
const char16_t *&cur,
const char16_t *end) noexcept {
char16_t c = *cur++;
// ASCII fast-path.
if (LLVM_LIKELY(c <= 0x7F)) {
return c;
}
if (isLowSurrogate(c)) {
// Unpaired low surrogate.
return UNICODE_REPLACEMENT_CHARACTER;
} else if (isHighSurrogate(c)) {
// Leading high surrogate. See if the next character is a low surrogate.
if (cur == end || !isLowSurrogate(*cur)) {
// Trailing or unpaired high surrogate.
return UNICODE_REPLACEMENT_CHARACTER;
} else {
// Decode surrogate pair and increment, because we consumed two chars.
return decodeSurrogatePair(c, *cur++);
}
} else {
// Not a surrogate.
return c;
}
}
size_t utf8LengthWithReplacements(llvh::ArrayRef<char16_t> input) {
size_t length{0};
for (const char16_t *cur = input.begin(), *end = input.end(); cur < end;) {
char32_t c32 = readUTF16WithReplacements(cur, end);
if (LLVM_LIKELY(c32 <= 0x7F)) {
++length;
} else if (c32 <= 0x7FF) {
length += 2;
} else if (c32 <= 0xFFFF) {
length += 3;
} else if (c32 <= 0x1FFFFF) {
length += 4;
} else if (c32 <= 0x3FFFFFF) {
length += 5;
} else {
length += 6;
}
}
return length;
}
size_t convertUTF16ToUTF8WithReplacements(
llvh::ArrayRef<char16_t> input,
char *buf,
size_t bufSize) {
char *curBuf = buf;
char *endBuf = buf + bufSize;
for (const char16_t *cur = input.begin(), *end = input.end();
cur < end && curBuf < endBuf;) {
char32_t c32 = readUTF16WithReplacements(cur, end);
char buff[UTF8CodepointMaxBytes];
char *ptr = buff;
encodeUTF8(ptr, c32);
size_t u8Length = static_cast<size_t>(ptr - buff);
if (curBuf + u8Length <= endBuf) {
std::char_traits<char>::copy(curBuf, buff, u8Length);
curBuf += u8Length;
} else {
break;
}
}
return static_cast<size_t>(curBuf - buf);
}
bool convertUTF16ToUTF8WithReplacements(
std::string &out,
llvh::ArrayRef<char16_t> input,
@ -77,37 +151,21 @@ bool convertUTF16ToUTF8WithReplacements(
}
for (auto cur = input.begin(), end = input.end();
cur < end && currNumCharacters < maxCharacters;
++cur, ++currNumCharacters) {
++currNumCharacters) {
char16_t c = cur[0];
// ASCII fast-path.
if (LLVM_LIKELY(c <= 0x7F)) {
out.push_back(static_cast<char>(c));
++cur;
continue;
}
char32_t c32;
if (isLowSurrogate(cur[0])) {
// Unpaired low surrogate.
c32 = UNICODE_REPLACEMENT_CHARACTER;
} else if (isHighSurrogate(cur[0])) {
// Leading high surrogate. See if the next character is a low surrogate.
if (cur + 1 == end || !isLowSurrogate(cur[1])) {
// Trailing or unpaired high surrogate.
c32 = UNICODE_REPLACEMENT_CHARACTER;
} else {
// Decode surrogate pair and increment, because we consumed two chars.
c32 = decodeSurrogatePair(cur[0], cur[1]);
++cur;
}
} else {
// Not a surrogate.
c32 = c;
}
char32_t c32 = readUTF16WithReplacements(cur, end);
char buff[UTF8CodepointMaxBytes];
char *ptr = buff;
encodeUTF8(ptr, c32);
out.insert(out.end(), buff, ptr);
out.append(buff, ptr);
}
return currNumCharacters < maxCharacters;
}

Просмотреть файл

@ -78,6 +78,80 @@ CallResult<HermesValue> FinalizableNativeFunction::createWithoutPrototype(
return selfHandle.getHermesValue();
}
//===----------------------------------------------------------------------===//
// class FinalizableNativeConstructor
const CallableVTable FinalizableNativeConstructor::vt{
{
VTable(
CellKind::FinalizableNativeConstructorKind,
cellSize<FinalizableNativeConstructor>(),
FinalizableNativeConstructor::_finalizeImpl),
FinalizableNativeConstructor::_getOwnIndexedRangeImpl,
FinalizableNativeConstructor::_haveOwnIndexedImpl,
FinalizableNativeConstructor::_getOwnIndexedPropertyFlagsImpl,
FinalizableNativeConstructor::_getOwnIndexedImpl,
FinalizableNativeConstructor::_setOwnIndexedImpl,
FinalizableNativeConstructor::_deleteOwnIndexedImpl,
FinalizableNativeConstructor::_checkAllOwnIndexedImpl,
},
FinalizableNativeConstructor::_newObjectImpl,
FinalizableNativeConstructor::_callImpl};
void FinalizableNativeConstructorBuildMeta(
const GCCell *cell,
Metadata::Builder &mb) {
mb.addJSObjectOverlapSlots(
JSObject::numOverlapSlots<FinalizableNativeConstructor>());
NativeConstructorBuildMeta(cell, mb);
mb.setVTable(&FinalizableNativeConstructor::vt.base.base);
}
CallResult<Handle<FinalizableNativeConstructor>>
FinalizableNativeConstructor::create(
Runtime *runtime,
void *context,
NativeFunctionPtr functionPtr,
FinalizeNativeFunctionPtr finalizePtr,
Handle<JSObject> prototypeObjectHandle,
SymbolID name,
unsigned paramCount) {
Handle<JSObject> parentHandle =
Handle<JSObject>::vmcast(&runtime->functionPrototype);
FinalizableNativeConstructor *cell =
runtime->makeAFixed<FinalizableNativeConstructor, HasFinalizer::Yes>(
runtime,
parentHandle,
runtime->getHiddenClassForPrototype(
*parentHandle, numOverlapSlots<FinalizableNativeConstructor>()),
context,
functionPtr,
finalizePtr,
creatorFunction<JSObject>,
CellKind::ObjectKind);
Handle<FinalizableNativeConstructor> selfHandle =
JSObjectInit::initToHandle(runtime, cell);
ExecutionStatus st = defineNameLengthAndPrototype(
selfHandle,
runtime,
name,
paramCount,
prototypeObjectHandle,
Callable::WritablePrototype::Yes,
/*strictMode*/ false);
if (st == ExecutionStatus::EXCEPTION) {
assert(false && "defineLengthAndPrototype() failed");
return ExecutionStatus::EXCEPTION;
}
return selfHandle;
}
//===----------------------------------------------------------------------===//
// class HostObject
HostObjectProxy::~HostObjectProxy() {}
const ObjectVTable HostObject::vt{

Просмотреть файл

@ -115,22 +115,29 @@ JSArrayBuffer::JSArrayBuffer(
void JSArrayBuffer::_finalizeImpl(GCCell *cell, GC *gc) {
auto *self = vmcast<JSArrayBuffer>(cell);
// Need to untrack the native memory that may have been tracked by snapshots.
gc->getIDTracker().untrackNative(self->data_);
gc->debitExternalMemory(self, self->size_);
free(self->data_);
if (LLVM_UNLIKELY(self->externalBuffer_)) {
self->externalBuffer_.reset();
self->data_ = nullptr;
self->size_ = 0;
} else {
// Need to untrack the native memory that may have been tracked by
// snapshots.
gc->getIDTracker().untrackNative(self->data_);
gc->debitExternalMemory(self, self->size_);
free(self->data_);
}
self->~JSArrayBuffer();
}
size_t JSArrayBuffer::_mallocSizeImpl(GCCell *cell) {
const auto *buffer = vmcast<JSArrayBuffer>(cell);
return buffer->size_;
return !buffer->externalBuffer_ ? buffer->size_ : 0;
}
gcheapsize_t JSArrayBuffer::_externalMemorySizeImpl(
hermes::vm::GCCell const *cell) {
const auto *buffer = vmcast<JSArrayBuffer>(cell);
return buffer->size_;
return !buffer->externalBuffer_ ? buffer->size_ : 0;
}
void JSArrayBuffer::_snapshotAddEdgesImpl(
@ -138,7 +145,7 @@ void JSArrayBuffer::_snapshotAddEdgesImpl(
GC *gc,
HeapSnapshot &snap) {
auto *const self = vmcast<JSArrayBuffer>(cell);
if (!self->data_) {
if (!self->data_ || self->externalBuffer_) {
return;
}
// While this is an internal edge, it is to a native node which is not
@ -155,7 +162,7 @@ void JSArrayBuffer::_snapshotAddNodesImpl(
GC *gc,
HeapSnapshot &snap) {
auto *const self = vmcast<JSArrayBuffer>(cell);
if (!self->data_) {
if (!self->data_ || self->externalBuffer_) {
return;
}
// Add the native node before the JSArrayBuffer node.
@ -169,7 +176,11 @@ void JSArrayBuffer::_snapshotAddNodesImpl(
}
void JSArrayBuffer::detach(GC *gc) {
if (data_) {
if (LLVM_UNLIKELY(externalBuffer_)) {
externalBuffer_.reset();
data_ = nullptr;
size_ = 0;
} else if (data_) {
gc->debitExternalMemory(this, size_);
free(data_);
data_ = nullptr;
@ -214,5 +225,17 @@ JSArrayBuffer::createDataBlock(Runtime *runtime, size_type size, bool zero) {
}
}
void JSArrayBuffer::setExternalBuffer(
Runtime *runtime,
std::unique_ptr<Buffer> externalBuffer) {
detach(&runtime->getHeap());
externalBuffer_ = std::move(externalBuffer);
attached_ = true;
if (externalBuffer_) {
data_ = const_cast<uint8_t *>(externalBuffer_->data());
size_ = externalBuffer_->size();
}
}
} // namespace vm
} // namespace hermes

Просмотреть файл

@ -716,7 +716,7 @@ typedArrayPrototypeByteLength(void *, Runtime *runtime, NativeArgs args) {
return ExecutionStatus::EXCEPTION;
}
auto self = args.vmcastThis<JSTypedArrayBase>();
return HermesValue::encodeNumberValue(self->getByteLength());
return HermesValue::encodeNumberValue(self->attached(runtime) ? self->getByteLength() : 0);
}
/// ES6 22.2.3.3
@ -1184,7 +1184,7 @@ typedArrayPrototypeLength(void *, Runtime *runtime, NativeArgs args) {
return ExecutionStatus::EXCEPTION;
}
auto self = args.vmcastThis<JSTypedArrayBase>();
return HermesValue::encodeNumberValue(self->getLength());
return HermesValue::encodeNumberValue(self->attached(runtime) ? self->getLength() : 0);
}
CallResult<HermesValue>

Просмотреть файл

@ -35,3 +35,4 @@ add_subdirectory(PlatformUnicode)
add_subdirectory(API)
add_subdirectory(ADT)
add_subdirectory(Optimizer)
add_subdirectory(NAPI)

Просмотреть файл

@ -0,0 +1,68 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(js)
set(NAPITests
napitest_hermes.cpp
napitest.cpp
napitest.h
test_2_function_arguments.cpp
test_3_callbacks.cpp
test_4_object_factory.cpp
test_5_function_factory.cpp
test_6_object_wrap.cpp
test_7_factory_wrap.cpp
test_8_passing_wrapped.cpp
test_array.cpp
test_assert.cpp
test_basics.cpp
test_bigint.cpp
test_constructor.cpp
test_conversions.cpp
test_dataview.cpp
test_date.cpp
test_error.cpp
test_exception.cpp
test_ext.cpp
test_function.cpp
test_general.cpp
test_handle_scope.cpp
test_instance_data.cpp
test_new_target.cpp
test_number.cpp
test_object.cpp
test_promise.cpp
test_properties.cpp
test_reference_double_free.cpp
test_reference.cpp
test_string.cpp
test_symbol.cpp
test_typedarray.cpp
)
# Turn on EH and RTTI for APITests
set(HERMES_ENABLE_EH ON)
set(HERMES_ENABLE_RTTI ON)
# For some reason (bug?) add_unittest() is clearing LLVM_REQUIRES_RTTI, so
# we need to set this one.
set(LLVM_ENABLE_RTTI ON)
add_hermes_unittest(NAPITests ${NAPITests} LINK_LIBS hermesNapi)
target_link_libraries(NAPITests hermesNapi libhermes)
# transform test JS files to be Hermes-compatible
add_dependencies(NAPITests transformJSFiles copyBabelFiles)
# copy hermes.dll next to the NAPITests.exe
add_custom_command(TARGET NAPITests POST_BUILD
DEPENDS libhermes
VERBATIM
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:libhermes>
$<TARGET_FILE_DIR:NAPITests>)

7
unittests/NAPI/js-native-api/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,7 @@
.buildstamp
.docbuildstamp
Makefile
*.Makefile
*.mk
gyp-mac-tool
/*/build

Просмотреть файл

@ -0,0 +1,38 @@
#include <js_native_api.h>
#include "../common.h"
static napi_value Add(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 2, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
napi_valuetype valuetype1;
NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));
NODE_API_ASSERT(env, valuetype0 == napi_number && valuetype1 == napi_number,
"Wrong argument type. Numbers expected.");
double value0;
NODE_API_CALL(env, napi_get_value_double(env, args[0], &value0));
double value1;
NODE_API_CALL(env, napi_get_value_double(env, args[1], &value1));
napi_value sum;
NODE_API_CALL(env, napi_create_double(env, value0 + value1, &sum));
return sum;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc = DECLARE_NODE_API_PROPERTY("add", Add);
NODE_API_CALL(env, napi_define_properties(env, exports, 1, &desc));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "binding",
"sources": [
"../entry_point.c",
"binding.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,6 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/binding`);
assert.strictEqual(addon.add(3, 5), 8);

Просмотреть файл

@ -0,0 +1,57 @@
#include <js_native_api.h>
#include "../common.h"
#include <string.h>
static napi_value RunCallback(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc == 1,
"Wrong number of arguments. Expects a single argument.");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_function,
"Wrong type of arguments. Expects a function as first argument.");
napi_valuetype valuetype1;
NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));
NODE_API_ASSERT(env, valuetype1 == napi_undefined,
"Additional arguments should be undefined.");
napi_value argv[1];
const char* str = "hello world";
size_t str_len = strlen(str);
NODE_API_CALL(env, napi_create_string_utf8(env, str, str_len, argv));
napi_value global;
NODE_API_CALL(env, napi_get_global(env, &global));
napi_value cb = args[0];
NODE_API_CALL(env, napi_call_function(env, global, cb, 1, argv, NULL));
return NULL;
}
static napi_value RunCallbackWithRecv(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value cb = args[0];
napi_value recv = args[1];
NODE_API_CALL(env, napi_call_function(env, recv, cb, 0, NULL, NULL));
return NULL;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[2] = {
DECLARE_NODE_API_PROPERTY("RunCallback", RunCallback),
DECLARE_NODE_API_PROPERTY("RunCallbackWithRecv", RunCallbackWithRecv),
};
NODE_API_CALL(env, napi_define_properties(env, exports, 2, desc));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "binding",
"sources": [
"../entry_point.c",
"binding.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,22 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/binding`);
addon.RunCallback(function(msg) {
assert.strictEqual(msg, 'hello world');
});
function testRecv(desiredRecv) {
addon.RunCallbackWithRecv(function() {
assert.strictEqual(this, desiredRecv);
}, desiredRecv);
}
testRecv(undefined);
testRecv(null);
testRecv(5);
testRecv(true);
testRecv('Hello');
testRecv([]);
testRecv({});

Просмотреть файл

@ -0,0 +1,23 @@
#include <js_native_api.h>
#include "../common.h"
static napi_value CreateObject(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value obj;
NODE_API_CALL(env, napi_create_object(env, &obj));
NODE_API_CALL(env, napi_set_named_property(env, obj, "msg", args[0]));
return obj;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
NODE_API_CALL(env,
napi_create_function(env, "exports", -1, CreateObject, NULL, &exports));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "binding",
"sources": [
"../entry_point.c",
"binding.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,8 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/binding`);
const obj1 = addon('hello');
const obj2 = addon('world');
assert.strictEqual(`${obj1.msg} ${obj2.msg}`, 'hello world');

Просмотреть файл

@ -0,0 +1,23 @@
#include <js_native_api.h>
#include "../common.h"
static napi_value MyFunction(napi_env env, napi_callback_info info) {
napi_value str;
NODE_API_CALL(env, napi_create_string_utf8(env, "hello world", -1, &str));
return str;
}
static napi_value CreateFunction(napi_env env, napi_callback_info info) {
napi_value fn;
NODE_API_CALL(env,
napi_create_function(env, "theFunction", -1, MyFunction, NULL, &fn));
return fn;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
NODE_API_CALL(env,
napi_create_function(env, "exports", -1, CreateFunction, NULL, &exports));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "binding",
"sources": [
"../entry_point.c",
"binding.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,7 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/binding`);
const fn = addon();
assert.strictEqual(fn(), 'hello world'); // 'hello world'

Просмотреть файл

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "binding",
"sources": [
"../entry_point.c",
"myobject.cc"
]
}
]
}

Просмотреть файл

@ -0,0 +1,159 @@
#include "myobject.h"
#include "../common.h"
napi_ref MyObject::constructor;
MyObject::MyObject(double value)
: value_(value), env_(nullptr), wrapper_(nullptr) {}
MyObject::~MyObject() { napi_delete_reference(env_, wrapper_); }
void MyObject::Destructor(
napi_env env, void* nativeObject, void* /*finalize_hint*/) {
MyObject* obj = static_cast<MyObject*>(nativeObject);
delete obj;
}
void MyObject::Init(napi_env env, napi_value exports) {
napi_property_descriptor properties[] = {
{ "value", nullptr, nullptr, GetValue, SetValue, 0, napi_default, 0 },
{ "valueReadonly", nullptr, nullptr, GetValue, nullptr, 0, napi_default,
0 },
DECLARE_NODE_API_PROPERTY("plusOne", PlusOne),
DECLARE_NODE_API_PROPERTY("multiply", Multiply),
};
napi_value cons;
NODE_API_CALL_RETURN_VOID(env, napi_define_class(
env, "MyObject", -1, New, nullptr,
sizeof(properties) / sizeof(napi_property_descriptor),
properties, &cons));
NODE_API_CALL_RETURN_VOID(env,
napi_create_reference(env, cons, 1, &constructor));
NODE_API_CALL_RETURN_VOID(env,
napi_set_named_property(env, exports, "MyObject", cons));
}
napi_value MyObject::New(napi_env env, napi_callback_info info) {
napi_value new_target;
NODE_API_CALL(env, napi_get_new_target(env, info, &new_target));
bool is_constructor = (new_target != nullptr);
size_t argc = 1;
napi_value args[1];
napi_value _this;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));
if (is_constructor) {
// Invoked as constructor: `new MyObject(...)`
double value = 0;
napi_valuetype valuetype;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
if (valuetype != napi_undefined) {
NODE_API_CALL(env, napi_get_value_double(env, args[0], &value));
}
MyObject* obj = new MyObject(value);
obj->env_ = env;
NODE_API_CALL(env,
napi_wrap(env, _this, obj, MyObject::Destructor,
nullptr /* finalize_hint */, &obj->wrapper_));
return _this;
}
// Invoked as plain function `MyObject(...)`, turn into construct call.
argc = 1;
napi_value argv[1] = {args[0]};
napi_value cons;
NODE_API_CALL(env, napi_get_reference_value(env, constructor, &cons));
napi_value instance;
NODE_API_CALL(env, napi_new_instance(env, cons, argc, argv, &instance));
return instance;
}
napi_value MyObject::GetValue(napi_env env, napi_callback_info info) {
napi_value _this;
NODE_API_CALL(env,
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));
MyObject* obj;
NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
napi_value num;
NODE_API_CALL(env, napi_create_double(env, obj->value_, &num));
return num;
}
napi_value MyObject::SetValue(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
napi_value _this;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));
MyObject* obj;
NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
NODE_API_CALL(env, napi_get_value_double(env, args[0], &obj->value_));
return nullptr;
}
napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) {
napi_value _this;
NODE_API_CALL(env,
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));
MyObject* obj;
NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
obj->value_ += 1;
napi_value num;
NODE_API_CALL(env, napi_create_double(env, obj->value_, &num));
return num;
}
napi_value MyObject::Multiply(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
napi_value _this;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));
double multiple = 1;
if (argc >= 1) {
NODE_API_CALL(env, napi_get_value_double(env, args[0], &multiple));
}
MyObject* obj;
NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
napi_value cons;
NODE_API_CALL(env, napi_get_reference_value(env, constructor, &cons));
const int kArgCount = 1;
napi_value argv[kArgCount];
NODE_API_CALL(env, napi_create_double(env, obj->value_ * multiple, argv));
napi_value instance;
NODE_API_CALL(env, napi_new_instance(env, cons, kArgCount, argv, &instance));
return instance;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
MyObject::Init(env, exports);
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,26 @@
#ifndef TEST_JS_NATIVE_API_6_OBJECT_WRAP_MYOBJECT_H_
#define TEST_JS_NATIVE_API_6_OBJECT_WRAP_MYOBJECT_H_
#include <js_native_api.h>
class MyObject {
public:
static void Init(napi_env env, napi_value exports);
static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);
private:
explicit MyObject(double value_ = 0);
~MyObject();
static napi_value New(napi_env env, napi_callback_info info);
static napi_value GetValue(napi_env env, napi_callback_info info);
static napi_value SetValue(napi_env env, napi_callback_info info);
static napi_value PlusOne(napi_env env, napi_callback_info info);
static napi_value Multiply(napi_env env, napi_callback_info info);
static napi_ref constructor;
double value_;
napi_env env_;
napi_ref wrapper_;
};
#endif // TEST_JS_NATIVE_API_6_OBJECT_WRAP_MYOBJECT_H_

Просмотреть файл

@ -0,0 +1,48 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/binding`);
const getterOnlyErrorRE =
/^TypeError: Cannot (set|assign to) property .*( of #<.*>)? which has only a getter$/;
const valueDescriptor = Object.getOwnPropertyDescriptor(
addon.MyObject.prototype, 'value');
const valueReadonlyDescriptor = Object.getOwnPropertyDescriptor(
addon.MyObject.prototype, 'valueReadonly');
const plusOneDescriptor = Object.getOwnPropertyDescriptor(
addon.MyObject.prototype, 'plusOne');
assert.strictEqual(typeof valueDescriptor.get, 'function');
assert.strictEqual(typeof valueDescriptor.set, 'function');
assert.strictEqual(valueDescriptor.value, undefined);
assert.strictEqual(valueDescriptor.enumerable, false);
assert.strictEqual(valueDescriptor.configurable, false);
assert.strictEqual(typeof valueReadonlyDescriptor.get, 'function');
assert.strictEqual(valueReadonlyDescriptor.set, undefined);
assert.strictEqual(valueReadonlyDescriptor.value, undefined);
assert.strictEqual(valueReadonlyDescriptor.enumerable, false);
assert.strictEqual(valueReadonlyDescriptor.configurable, false);
assert.strictEqual(plusOneDescriptor.get, undefined);
assert.strictEqual(plusOneDescriptor.set, undefined);
assert.strictEqual(typeof plusOneDescriptor.value, 'function');
assert.strictEqual(plusOneDescriptor.enumerable, false);
assert.strictEqual(plusOneDescriptor.configurable, false);
const obj = new addon.MyObject(9);
assert.strictEqual(obj.value, 9);
obj.value = 10;
assert.strictEqual(obj.value, 10);
assert.strictEqual(obj.valueReadonly, 10);
assert.throws(() => { obj.valueReadonly = 14; }, getterOnlyErrorRE);
assert.strictEqual(obj.plusOne(), 11);
assert.strictEqual(obj.plusOne(), 12);
assert.strictEqual(obj.plusOne(), 13);
assert.strictEqual(obj.multiply().value, 13);
assert.strictEqual(obj.multiply(10).value, 130);
const newobj = obj.multiply(-1);
assert.strictEqual(newobj.value, -13);
assert.strictEqual(newobj.valueReadonly, -13);
assert.notStrictEqual(obj, newobj);

Просмотреть файл

@ -0,0 +1,31 @@
#include <js_native_api.h>
#include "myobject.h"
#include "../common.h"
napi_value CreateObject(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env,
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));
napi_value instance;
NODE_API_CALL(env, MyObject::NewInstance(env, args[0], &instance));
return instance;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
NODE_API_CALL(env, MyObject::Init(env));
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_GETTER("finalizeCount", MyObject::GetFinalizeCount),
DECLARE_NODE_API_PROPERTY("createObject", CreateObject),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,12 @@
{
"targets": [
{
"target_name": "binding",
"sources": [
"../entry_point.c",
"binding.cc",
"myobject.cc"
]
}
]
}

Просмотреть файл

@ -0,0 +1,101 @@
#include "myobject.h"
#include "../common.h"
static int finalize_count = 0;
MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {}
MyObject::~MyObject() { napi_delete_reference(env_, wrapper_); }
void MyObject::Destructor(napi_env env,
void* nativeObject,
void* /*finalize_hint*/) {
++finalize_count;
MyObject* obj = static_cast<MyObject*>(nativeObject);
delete obj;
}
napi_value MyObject::GetFinalizeCount(napi_env env, napi_callback_info info) {
napi_value result;
NODE_API_CALL(env, napi_create_int32(env, finalize_count, &result));
return result;
}
napi_ref MyObject::constructor;
napi_status MyObject::Init(napi_env env) {
napi_status status;
napi_property_descriptor properties[] = {
DECLARE_NODE_API_PROPERTY("plusOne", PlusOne),
};
napi_value cons;
status = napi_define_class(
env, "MyObject", -1, New, nullptr, 1, properties, &cons);
if (status != napi_ok) return status;
status = napi_create_reference(env, cons, 1, &constructor);
if (status != napi_ok) return status;
return napi_ok;
}
napi_value MyObject::New(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
napi_value _this;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));
napi_valuetype valuetype;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
MyObject* obj = new MyObject();
if (valuetype == napi_undefined) {
obj->counter_ = 0;
} else {
NODE_API_CALL(env, napi_get_value_uint32(env, args[0], &obj->counter_));
}
obj->env_ = env;
NODE_API_CALL(env,
napi_wrap(
env, _this, obj, MyObject::Destructor, nullptr /* finalize_hint */,
&obj->wrapper_));
return _this;
}
napi_status MyObject::NewInstance(napi_env env,
napi_value arg,
napi_value* instance) {
napi_status status;
const int argc = 1;
napi_value argv[argc] = {arg};
napi_value cons;
status = napi_get_reference_value(env, constructor, &cons);
if (status != napi_ok) return status;
status = napi_new_instance(env, cons, argc, argv, instance);
if (status != napi_ok) return status;
return napi_ok;
}
napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) {
napi_value _this;
NODE_API_CALL(env,
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));
MyObject* obj;
NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast<void**>(&obj)));
obj->counter_ += 1;
napi_value num;
NODE_API_CALL(env, napi_create_uint32(env, obj->counter_, &num));
return num;
}

Просмотреть файл

@ -0,0 +1,27 @@
#ifndef TEST_JS_NATIVE_API_7_FACTORY_WRAP_MYOBJECT_H_
#define TEST_JS_NATIVE_API_7_FACTORY_WRAP_MYOBJECT_H_
#include <js_native_api.h>
class MyObject {
public:
static napi_status Init(napi_env env);
static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);
static napi_value GetFinalizeCount(napi_env env, napi_callback_info info);
static napi_status NewInstance(napi_env env,
napi_value arg,
napi_value* instance);
private:
MyObject();
~MyObject();
static napi_ref constructor;
static napi_value New(napi_env env, napi_callback_info info);
static napi_value PlusOne(napi_env env, napi_callback_info info);
uint32_t counter_;
napi_env env_;
napi_ref wrapper_;
};
#endif // TEST_JS_NATIVE_API_7_FACTORY_WRAP_MYOBJECT_H_

Просмотреть файл

@ -0,0 +1,26 @@
'use strict';
// Flags: --expose-gc
const common = require('../../common');
const assert = require('assert');
const test = require(`./build/${common.buildType}/binding`);
assert.strictEqual(test.finalizeCount, 0);
async function runGCTests() {
(() => {
const obj = test.createObject(10);
assert.strictEqual(obj.plusOne(), 11);
assert.strictEqual(obj.plusOne(), 12);
assert.strictEqual(obj.plusOne(), 13);
})();
await common.gcUntil('test 1', () => (test.finalizeCount === 1));
(() => {
const obj2 = test.createObject(20);
assert.strictEqual(obj2.plusOne(), 21);
assert.strictEqual(obj2.plusOne(), 22);
assert.strictEqual(obj2.plusOne(), 23);
})();
await common.gcUntil('test 2', () => (test.finalizeCount === 2));
}
runGCTests();

Просмотреть файл

@ -0,0 +1,60 @@
#include <js_native_api.h>
#include "myobject.h"
#include "../common.h"
extern size_t finalize_count;
static napi_value CreateObject(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env,
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));
napi_value instance;
NODE_API_CALL(env, MyObject::NewInstance(env, args[0], &instance));
return instance;
}
static napi_value Add(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env,
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));
MyObject* obj1;
NODE_API_CALL(env,
napi_unwrap(env, args[0], reinterpret_cast<void**>(&obj1)));
MyObject* obj2;
NODE_API_CALL(env,
napi_unwrap(env, args[1], reinterpret_cast<void**>(&obj2)));
napi_value sum;
NODE_API_CALL(env, napi_create_double(env, obj1->Val() + obj2->Val(), &sum));
return sum;
}
static napi_value FinalizeCount(napi_env env, napi_callback_info info) {
napi_value return_value;
NODE_API_CALL(env, napi_create_uint32(env, finalize_count, &return_value));
return return_value;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
MyObject::Init(env);
napi_property_descriptor desc[] = {
DECLARE_NODE_API_PROPERTY("createObject", CreateObject),
DECLARE_NODE_API_PROPERTY("add", Add),
DECLARE_NODE_API_PROPERTY("finalizeCount", FinalizeCount),
};
NODE_API_CALL(env,
napi_define_properties(env, exports, sizeof(desc) / sizeof(*desc), desc));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,12 @@
{
"targets": [
{
"target_name": "binding",
"sources": [
"../entry_point.c",
"binding.cc",
"myobject.cc"
]
}
]
}

Просмотреть файл

@ -0,0 +1,80 @@
#include "myobject.h"
#include "../common.h"
size_t finalize_count = 0;
MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {}
MyObject::~MyObject() {
finalize_count++;
napi_delete_reference(env_, wrapper_);
}
void MyObject::Destructor(
napi_env env, void* nativeObject, void* /*finalize_hint*/) {
MyObject* obj = static_cast<MyObject*>(nativeObject);
delete obj;
}
napi_ref MyObject::constructor;
napi_status MyObject::Init(napi_env env) {
napi_status status;
napi_value cons;
status = napi_define_class(
env, "MyObject", -1, New, nullptr, 0, nullptr, &cons);
if (status != napi_ok) return status;
status = napi_create_reference(env, cons, 1, &constructor);
if (status != napi_ok) return status;
return napi_ok;
}
napi_value MyObject::New(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
napi_value _this;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));
MyObject* obj = new MyObject();
napi_valuetype valuetype;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
if (valuetype == napi_undefined) {
obj->val_ = 0;
} else {
NODE_API_CALL(env, napi_get_value_double(env, args[0], &obj->val_));
}
obj->env_ = env;
// The below call to napi_wrap() must request a reference to the wrapped
// object via the out-parameter, because this ensures that we test the code
// path that deals with a reference that is destroyed from its own finalizer.
NODE_API_CALL(env,
napi_wrap(env, _this, obj, MyObject::Destructor,
nullptr /* finalize_hint */, &obj->wrapper_));
return _this;
}
napi_status MyObject::NewInstance(napi_env env,
napi_value arg,
napi_value* instance) {
napi_status status;
const int argc = 1;
napi_value argv[argc] = {arg};
napi_value cons;
status = napi_get_reference_value(env, constructor, &cons);
if (status != napi_ok) return status;
status = napi_new_instance(env, cons, argc, argv, instance);
if (status != napi_ok) return status;
return napi_ok;
}

Просмотреть файл

@ -0,0 +1,26 @@
#ifndef TEST_JS_NATIVE_API_8_PASSING_WRAPPED_MYOBJECT_H_
#define TEST_JS_NATIVE_API_8_PASSING_WRAPPED_MYOBJECT_H_
#include <js_native_api.h>
class MyObject {
public:
static napi_status Init(napi_env env);
static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);
static napi_status NewInstance(napi_env env,
napi_value arg,
napi_value* instance);
double Val() const { return val_; }
private:
MyObject();
~MyObject();
static napi_ref constructor;
static napi_value New(napi_env env, napi_callback_info info);
double val_;
napi_env env_;
napi_ref wrapper_;
};
#endif // TEST_JS_NATIVE_API_8_PASSING_WRAPPED_MYOBJECT_H_

Просмотреть файл

@ -0,0 +1,18 @@
'use strict';
// Flags: --expose-gc
const common = require('../../common');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/binding`);
async function runTest() {
(() => {
let obj1 = addon.createObject(10);
let obj2 = addon.createObject(20);
const result = addon.add(obj1, obj2);
assert.strictEqual(result, 30);
})();
await common.gcUntil('8_passing_wrapped',
() => (addon.finalizeCount() === 2));
}
runTest();

Просмотреть файл

@ -0,0 +1,48 @@
#include <js_native_api.h>
#include "common.h"
#include <stdio.h>
void add_returned_status(napi_env env,
const char* key,
napi_value object,
char* expected_message,
napi_status expected_status,
napi_status actual_status) {
char napi_message_string[100] = "";
napi_value prop_value;
if (actual_status != expected_status) {
snprintf(napi_message_string, sizeof(napi_message_string),
"Invalid status [%d]", actual_status);
}
NODE_API_CALL_RETURN_VOID(env,
napi_create_string_utf8(
env,
(actual_status == expected_status ?
expected_message :
napi_message_string),
NAPI_AUTO_LENGTH,
&prop_value));
NODE_API_CALL_RETURN_VOID(env,
napi_set_named_property(env, object, key, prop_value));
}
void add_last_status(napi_env env, const char* key, napi_value return_value) {
napi_value prop_value;
const napi_extended_error_info* p_last_error;
NODE_API_CALL_RETURN_VOID(env,
napi_get_last_error_info(env, &p_last_error));
NODE_API_CALL_RETURN_VOID(env,
napi_create_string_utf8(env,
(p_last_error->error_message == NULL ?
"napi_ok" :
p_last_error->error_message),
NAPI_AUTO_LENGTH,
&prop_value));
NODE_API_CALL_RETURN_VOID(env,
napi_set_named_property(env, return_value, key, prop_value));
}

Просмотреть файл

@ -0,0 +1,71 @@
#include <js_native_api.h>
// Empty value so that macros here are able to return NULL or void
#define NODE_API_RETVAL_NOTHING // Intentionally blank #define
#define GET_AND_THROW_LAST_ERROR(env) \
do { \
const napi_extended_error_info *error_info; \
napi_get_last_error_info((env), &error_info); \
bool is_pending; \
napi_is_exception_pending((env), &is_pending); \
/* If an exception is already pending, don't rethrow it */ \
if (!is_pending) { \
const char* error_message = error_info->error_message != NULL ? \
error_info->error_message : \
"empty error message"; \
napi_throw_error((env), NULL, error_message); \
} \
} while (0)
#define NODE_API_ASSERT_BASE(env, assertion, message, ret_val) \
do { \
if (!(assertion)) { \
napi_throw_error( \
(env), \
NULL, \
"assertion (" #assertion ") failed: " message); \
return ret_val; \
} \
} while (0)
// Returns NULL on failed assertion.
// This is meant to be used inside napi_callback methods.
#define NODE_API_ASSERT(env, assertion, message) \
NODE_API_ASSERT_BASE(env, assertion, message, NULL)
// Returns empty on failed assertion.
// This is meant to be used inside functions with void return type.
#define NODE_API_ASSERT_RETURN_VOID(env, assertion, message) \
NODE_API_ASSERT_BASE(env, assertion, message, NODE_API_RETVAL_NOTHING)
#define NODE_API_CALL_BASE(env, the_call, ret_val) \
do { \
if ((the_call) != napi_ok) { \
GET_AND_THROW_LAST_ERROR((env)); \
return ret_val; \
} \
} while (0)
// Returns NULL if the_call doesn't return napi_ok.
#define NODE_API_CALL(env, the_call) \
NODE_API_CALL_BASE(env, the_call, NULL)
// Returns empty if the_call doesn't return napi_ok.
#define NODE_API_CALL_RETURN_VOID(env, the_call) \
NODE_API_CALL_BASE(env, the_call, NODE_API_RETVAL_NOTHING)
#define DECLARE_NODE_API_PROPERTY(name, func) \
{ (name), NULL, (func), NULL, NULL, NULL, napi_default, NULL }
#define DECLARE_NODE_API_GETTER(name, func) \
{ (name), NULL, NULL, (func), NULL, NULL, napi_default, NULL }
void add_returned_status(napi_env env,
const char* key,
napi_value object,
char* expected_message,
napi_status expected_status,
napi_status actual_status);
void add_last_status(napi_env env, const char* key, napi_value return_value);

Просмотреть файл

@ -0,0 +1,353 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//
// The JavaScript code in this file is adopted from the Node.js project.
// See the src\napi\Readme.md about the Node.js copyright notice.
'use strict';
class AssertionError extends Error {
constructor(options) {
const {
message,
actual,
expected,
method,
errorStack
} = options;
super(String(message));
this.name = 'AssertionError';
this.method = String(method);
this.actual = String(actual);
this.expected = String(expected);
this.errorStack = errorStack || '';
setAssertionSource(this, method);
}
}
function setAssertionSource(error, method) {
let result = { sourceFile: '<Unknown>', sourceLine: 0 };
const stackArray = (error.errorStack || error.stack).split('\n');
const methodNamePattern = `${method} (`;
let methodNameFound = false;
for (const stackFrame of stackArray) {
if (methodNameFound) {
const stackFrameParts = stackFrame.split(':');
if (stackFrameParts.length >= 2) {
let sourceFile = stackFrameParts[0];
if (sourceFile.startsWith(' at ')) {
sourceFile = sourceFile.substr(7);
}
result = { sourceFile, sourceLine: Number(stackFrameParts[1]) };
}
break;
} else {
methodNameFound = stackFrame.indexOf(methodNamePattern) >= 0;
}
}
Object.assign(error, result);
}
const assert = module.exports = ok;
assert.fail = function fail(message) {
message = message || 'Failed';
let errorInfo = message;
if (typeof message !== 'object') {
errorInfo = { message, method: fail.name };
}
throw new AssertionError(errorInfo);
}
function innerOk(fn, argLen, value, message) {
if (!value) {
if (argLen === 0) {
message = 'No value argument passed to `assert.ok()`';
} else if (message == null) {
message = 'The expression evaluated to a falsy value';
}
assert.fail({
message,
actual: formatValue(value),
expected: formatValue(true),
method: fn.name
});
}
}
// Pure assertion tests whether a value is truthy, as determined by !!value.
function ok(...args) {
innerOk(ok, args.length, ...args);
}
assert.ok = ok;
function innerComparison(method, compare, defaultMessage, argLen, actual, expected, message) {
if (!compare(actual, expected)) {
if (argLen < 2) {
message = `'assert.${method.name}' expects two or more arguments.`;
} else if (message == null) {
message = defaultMessage;
}
assert.fail({
message,
actual: formatValue(actual),
expected: formatValue(expected),
method: method.name
});
}
}
assert.strictEqual = function strictEqual(...args) {
innerComparison(strictEqual, Object.is,
'Values are not strict equal', args.length, ...args);
}
assert.notStrictEqual = function notStrictEqual(...args) {
innerComparison(notStrictEqual, negate(Object.is),
'Values must not be strict equal', args.length, ...args);
}
assert.deepStrictEqual = function deepStrictEqual(...args) {
innerComparison(deepStrictEqual, isDeepStrictEqual,
'Values are not deep strict equal', args.length, ...args);
}
assert.notDeepStrictEqual = function notDeepStrictEqual(...args) {
innerComparison(notDeepStrictEqual, negate(isDeepStrictEqual),
'Values must not be deep strict equal', args.length, ...args);
}
function innerThrows(method, argLen, fn, expected, message) {
let actual = 'Did not throw';
function succeeds() {
try {
fn();
return false;
} catch (error) {
if (typeof expected === 'function') {
if (expected.prototype !== undefined && error instanceof expected) {
return true;
} else {
return expected(error);
}
} else if (expected instanceof RegExp) {
actual = `${error.name}: ${error.message}`;
return expected.test(actual);
} else if (expected) {
if (expected.name && expected.name != error.name) {
return false;
} else if (expected.message && expected.message != error.message) {
return false;
} else if (expected.code && expected.code != error.code) {
return false;
}
}
return true;
}
}
if (argLen < 1 || typeof fn !== 'function') {
message = `'assert.${method.name}' expects a function parameter.`;
} else if (message == null) {
if (expected) {
message = `'assert.${method.name}' failed to throw an exception that matches '${expected}'.`;
} else {
message = `'assert.${method.name}' failed to throw an exception.`;
}
}
if (!succeeds()) {
throw new AssertionError({message, actual, expected, method: method.name});
}
}
assert.throws = function throws(...args) {
innerThrows(throws, args.length, ...args);
}
function innerMatch(method, argLen, value, expected, message) {
let succeeds = false;
if (argLen < 1 || typeof value !== 'string') {
message = `'assert.${method.name}' expects a string parameter.`;
} else if (!(expected instanceof RegExp)) {
message = `'assert.${method.name}' expects a RegExp as a second parameter.`;
} else {
succeeds = expected.test(value);
if (!succeeds && message == null) {
message = `'assert.${method.name}' failed to match '${expected}'.`;
}
}
if (!succeeds) {
throw new AssertionError({message, actual: value, expected, method: method.name});
}
}
assert.match = function match(...args) {
innerMatch(match, args.length, ...args);
}
function negate(compare) {
return (...args) => !compare(...args);
}
function isDeepStrictEqual(left, right) {
function check(left, right) {
if (left === right) {
return true;
}
if (typeof left !== typeof right) {
return false;
}
if (Array.isArray(left)) {
return Array.isArray(right) && checkArray(left, right);
}
if (typeof left === 'number') {
return isNaN(left) && isNaN(right);
}
if (typeof left === 'object') {
return (typeof right === 'object') && checkObject(left, right);
}
return false;
}
function checkArray(left, right) {
if (left.length !== right.length) {
return false;
}
for (let i = 0; i < left.length; ++i) {
if (!check(left[i], right[i])) {
return false;
}
}
return true;
}
//TODO: provide detailed error info
function checkObject(left, right) {
const leftNames = Object.getOwnPropertyNames(left);
const rightNames = Object.getOwnPropertyNames(right);
if (leftNames.length !== rightNames.length) {
throw "Different set of properties";
return false;
}
for (let i = 0; i < leftNames.length; ++i) {
if (!check(left[leftNames[i]], right[leftNames[i]])) {
throw leftNames[i] + ": " + left[leftNames[i]] + " vs " + right[leftNames[i]];
return false;
}
}
const leftSymbols = Object.getOwnPropertySymbols(left);
const rightSymbols = Object.getOwnPropertySymbols(right);
if (leftSymbols.length !== rightSymbols.length) {
throw "Different set of symbol names";
return false;
}
for (let i = 0; i < leftSymbols.length; ++i) {
if (!check(left[leftSymbols[i]], right[leftSymbols[i]])) {
throw leftSymbols[i].ToString() + ": different value";
return false;
}
}
return check(Object.getPrototypeOf(left), Object.getPrototypeOf(right));
}
return check(left, right);
}
const mustCallChecks = [];
assert.runCallChecks = function runCallChecks() {
const failed = mustCallChecks.filter(context => {
if ('minimum' in context) {
context.messageSegment = `at least ${context.minimum}`;
return context.actual < context.minimum;
}
context.messageSegment = `exactly ${context.exact}`;
return context.actual !== context.exact;
});
mustCallChecks.length = 0;
failed.forEach(context => {
assert.fail({
message: `Mismatched ${context.name} function calls`,
actual: `${context.actual} calls`,
expected: `${context.messageSegment} calls`,
method: context.method.name,
errorStack: context.stack
});
});
}
function getCallSite() {
try {
throw new Error('');
} catch (err) {
return err.stack;
}
}
assert.mustNotCall = function mustNotCall(msg) {
return function mustNotCall(...args) {
assert.fail({
message: String(msg || 'Function should not have been called'),
actual: args.length > 0 ? `Called with arguments: ${args.map(String).join(', ')}` : 'Called without arguments',
expected: 'Not to be called',
method: mustNotCall.name
});
};
}
assert.mustCall = function mustCall(fn, exact) {
return _mustCallInner(fn, exact, 'exact', mustCall);
}
assert.mustCallAtLeast = function mustCallAtLeast(fn, minimum) {
return _mustCallInner(fn, minimum, 'minimum', mustCallAtLeast);
}
const noop = () => {};
function _mustCallInner(fn, criteria = 1, field, method) {
if (typeof fn === 'number') {
criteria = fn;
fn = noop;
} else if (fn === undefined) {
fn = noop;
}
if (typeof criteria !== 'number') {
throw new TypeError(`Invalid ${field} value: ${criteria}`);
}
const context = {
[field]: criteria,
actual: 0,
stack: getCallSite(),
name: fn.name || '<anonymous>',
method
};
mustCallChecks.push(context);
return function() {
context.actual++;
return fn.apply(this, arguments);
};
}
function formatValue(value) {
let type = typeof value;
if (type === 'object') {
if (Array.isArray(value)) {
return '<array> []';
} else {
return '<object> {}';
}
}
return `<${type}> ${value}`;
}

Просмотреть файл

@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//
// The JavaScript code in this file is adopted from the Node.js project.
// See the src\napi\Readme.md about the Node.js copyright notice.
'use strict';
const { mustCall, mustCallAtLeast, mustNotCall } = require('assert');
const buildType = 'x86';
function gcUntil(name, condition) {
if (typeof name === 'function') {
condition = name;
name = undefined;
}
return new Promise((resolve, reject) => {
let count = 0;
function gcAndCheck() {
setImmediate(() => {
count++;
global.gc();
if (condition()) {
resolve();
} else if (count < 10) {
gcAndCheck();
} else {
reject(name === undefined ? undefined : 'Test ' + name + ' failed');
}
});
}
gcAndCheck();
});
}
Object.assign(module.exports, {
buildType,
gcUntil,
mustCall,
mustCallAtLeast,
mustNotCall,
});

Просмотреть файл

@ -0,0 +1,7 @@
#include <node_api.h>
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports);
EXTERN_C_END
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

Просмотреть файл

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "test_array",
"sources": [
"../entry_point.c",
"test_array.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,61 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
// Testing api calls for arrays
const test_array = require(`./build/${common.buildType}/test_array`);
const array = [
1,
9,
48,
13493,
9459324,
{ name: 'hello' },
[
'world',
'node',
'abi',
],
];
assert.throws(
() => {
test_array.TestGetElement(array, array.length + 1);
},
/^Error: assertion \(\(\(uint32_t\)index < length\)\) failed: Index out of bounds!$/
);
assert.throws(
() => {
test_array.TestGetElement(array, -2);
},
/^Error: assertion \(index >= 0\) failed: Invalid index\. Expects a positive integer\.$/
);
array.forEach(function(element, index) {
assert.strictEqual(test_array.TestGetElement(array, index), element);
});
assert.deepStrictEqual(test_array.New(array), array);
assert(test_array.TestHasElement(array, 0));
assert.strictEqual(test_array.TestHasElement(array, array.length + 1), false);
assert(test_array.NewWithLength(0) instanceof Array);
assert(test_array.NewWithLength(1) instanceof Array);
// Check max allowed length for an array 2^32 -1
// TODO: Hermes does not allow such big arrays
// assert(test_array.NewWithLength(4294967295) instanceof Array);
{
// Verify that array elements can be deleted.
const arr = ['a', 'b', 'c', 'd'];
assert.strictEqual(arr.length, 4);
assert.strictEqual(2 in arr, true);
assert.strictEqual(test_array.TestDeleteElement(arr, 2), true);
assert.strictEqual(arr.length, 4);
assert.strictEqual(2 in arr, false);
}

Просмотреть файл

@ -0,0 +1,187 @@
#include <js_native_api.h>
#include <string.h>
#include "../common.h"
static napi_value TestGetElement(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 2, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_object,
"Wrong type of arguments. Expects an array as first argument.");
napi_valuetype valuetype1;
NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));
NODE_API_ASSERT(env, valuetype1 == napi_number,
"Wrong type of arguments. Expects an integer as second argument.");
napi_value array = args[0];
int32_t index;
NODE_API_CALL(env, napi_get_value_int32(env, args[1], &index));
NODE_API_ASSERT(env, index >= 0, "Invalid index. Expects a positive integer.");
bool isarray;
NODE_API_CALL(env, napi_is_array(env, array, &isarray));
if (!isarray) {
return NULL;
}
uint32_t length;
NODE_API_CALL(env, napi_get_array_length(env, array, &length));
NODE_API_ASSERT(env, ((uint32_t)index < length), "Index out of bounds!");
napi_value ret;
NODE_API_CALL(env, napi_get_element(env, array, index, &ret));
return ret;
}
static napi_value TestHasElement(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 2, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_object,
"Wrong type of arguments. Expects an array as first argument.");
napi_valuetype valuetype1;
NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));
NODE_API_ASSERT(env, valuetype1 == napi_number,
"Wrong type of arguments. Expects an integer as second argument.");
napi_value array = args[0];
int32_t index;
NODE_API_CALL(env, napi_get_value_int32(env, args[1], &index));
bool isarray;
NODE_API_CALL(env, napi_is_array(env, array, &isarray));
if (!isarray) {
return NULL;
}
bool has_element;
NODE_API_CALL(env, napi_has_element(env, array, index, &has_element));
napi_value ret;
NODE_API_CALL(env, napi_get_boolean(env, has_element, &ret));
return ret;
}
static napi_value TestDeleteElement(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc == 2, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_object,
"Wrong type of arguments. Expects an array as first argument.");
napi_valuetype valuetype1;
NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));
NODE_API_ASSERT(env, valuetype1 == napi_number,
"Wrong type of arguments. Expects an integer as second argument.");
napi_value array = args[0];
int32_t index;
bool result;
napi_value ret;
NODE_API_CALL(env, napi_get_value_int32(env, args[1], &index));
NODE_API_CALL(env, napi_is_array(env, array, &result));
if (!result) {
return NULL;
}
NODE_API_CALL(env, napi_delete_element(env, array, index, &result));
NODE_API_CALL(env, napi_get_boolean(env, result, &ret));
return ret;
}
static napi_value New(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_object,
"Wrong type of arguments. Expects an array as first argument.");
napi_value ret;
NODE_API_CALL(env, napi_create_array(env, &ret));
uint32_t i, length;
NODE_API_CALL(env, napi_get_array_length(env, args[0], &length));
for (i = 0; i < length; i++) {
napi_value e;
NODE_API_CALL(env, napi_get_element(env, args[0], i, &e));
NODE_API_CALL(env, napi_set_element(env, ret, i, e));
}
return ret;
}
static napi_value NewWithLength(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_number,
"Wrong type of arguments. Expects an integer the first argument.");
int32_t array_length;
NODE_API_CALL(env, napi_get_value_int32(env, args[0], &array_length));
napi_value ret;
NODE_API_CALL(env, napi_create_array_with_length(env, array_length, &ret));
return ret;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("TestGetElement", TestGetElement),
DECLARE_NODE_API_PROPERTY("TestHasElement", TestHasElement),
DECLARE_NODE_API_PROPERTY("TestDeleteElement", TestDeleteElement),
DECLARE_NODE_API_PROPERTY("New", New),
DECLARE_NODE_API_PROPERTY("NewWithLength", NewWithLength),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "test_bigint",
"sources": [
"../entry_point.c",
"test_bigint.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,52 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const {
IsLossless,
TestInt64,
TestUint64,
TestWords,
CreateTooBigBigInt,
MakeBigIntWordsThrow,
} = require(`./build/${common.buildType}/test_bigint`);
[
0n,
-0n,
1n,
-1n,
100n,
2121n,
-1233n,
986583n,
-976675n,
98765432213456789876546896323445679887645323232436587988766545658n,
-4350987086545760976737453646576078997096876957864353245245769809n,
].forEach((num) => {
if (num > -(2n ** 63n) && num < 2n ** 63n) {
assert.strictEqual(TestInt64(num), num);
assert.strictEqual(IsLossless(num, true), true);
} else {
assert.strictEqual(IsLossless(num, true), false);
}
if (num >= 0 && num < 2n ** 64n) {
assert.strictEqual(TestUint64(num), num);
assert.strictEqual(IsLossless(num, false), true);
} else {
assert.strictEqual(IsLossless(num, false), false);
}
assert.strictEqual(num, TestWords(num));
});
assert.throws(CreateTooBigBigInt, {
name: 'Error',
message: 'Invalid argument',
});
// Test that we correctly forward exceptions from the engine.
assert.throws(MakeBigIntWordsThrow, {
name: 'RangeError',
message: 'Maximum BigInt size exceeded'
});

Просмотреть файл

@ -0,0 +1,158 @@
#include <limits.h>
#include <inttypes.h>
#include <stdio.h>
#include <js_native_api.h>
#include "../common.h"
static napi_value IsLossless(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
bool is_signed;
NODE_API_CALL(env, napi_get_value_bool(env, args[1], &is_signed));
bool lossless;
if (is_signed) {
int64_t input;
NODE_API_CALL(env, napi_get_value_bigint_int64(env, args[0], &input, &lossless));
} else {
uint64_t input;
NODE_API_CALL(env, napi_get_value_bigint_uint64(env, args[0], &input, &lossless));
}
napi_value output;
NODE_API_CALL(env, napi_get_boolean(env, lossless, &output));
return output;
}
static napi_value TestInt64(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_bigint,
"Wrong type of arguments. Expects a bigint as first argument.");
int64_t input;
bool lossless;
NODE_API_CALL(env, napi_get_value_bigint_int64(env, args[0], &input, &lossless));
napi_value output;
NODE_API_CALL(env, napi_create_bigint_int64(env, input, &output));
return output;
}
static napi_value TestUint64(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_bigint,
"Wrong type of arguments. Expects a bigint as first argument.");
uint64_t input;
bool lossless;
NODE_API_CALL(env, napi_get_value_bigint_uint64(
env, args[0], &input, &lossless));
napi_value output;
NODE_API_CALL(env, napi_create_bigint_uint64(env, input, &output));
return output;
}
static napi_value TestWords(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_bigint,
"Wrong type of arguments. Expects a bigint as first argument.");
size_t expected_word_count;
NODE_API_CALL(env, napi_get_value_bigint_words(
env, args[0], NULL, &expected_word_count, NULL));
int sign_bit;
size_t word_count = 10;
uint64_t words[10];
NODE_API_CALL(env, napi_get_value_bigint_words(
env, args[0], &sign_bit, &word_count, words));
NODE_API_ASSERT(env, word_count == expected_word_count,
"word counts do not match");
napi_value output;
NODE_API_CALL(env, napi_create_bigint_words(
env, sign_bit, word_count, words, &output));
return output;
}
// throws RangeError
static napi_value CreateTooBigBigInt(napi_env env, napi_callback_info info) {
int sign_bit = 0;
size_t word_count = SIZE_MAX;
uint64_t words[10];
napi_value output;
NODE_API_CALL(env, napi_create_bigint_words(
env, sign_bit, word_count, words, &output));
return output;
}
// Test that we correctly forward exceptions from the engine.
static napi_value MakeBigIntWordsThrow(napi_env env, napi_callback_info info) {
uint64_t words[10];
napi_value output;
napi_status status = napi_create_bigint_words(env,
0,
INT_MAX,
words,
&output);
if (status != napi_pending_exception)
napi_throw_error(env, NULL, "Expected status `napi_pending_exception`");
return NULL;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("IsLossless", IsLossless),
DECLARE_NODE_API_PROPERTY("TestInt64", TestInt64),
DECLARE_NODE_API_PROPERTY("TestUint64", TestUint64),
DECLARE_NODE_API_PROPERTY("TestWords", TestWords),
DECLARE_NODE_API_PROPERTY("CreateTooBigBigInt", CreateTooBigBigInt),
DECLARE_NODE_API_PROPERTY("MakeBigIntWordsThrow", MakeBigIntWordsThrow),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,12 @@
{
"targets": [
{
"target_name": "test_constructor",
"sources": [
"../common.c",
"../entry_point.c",
"test_constructor.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,62 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const getterOnlyErrorRE =
/^TypeError: Cannot (set|assign to) property .*( of #<.*>)? which has only a getter$/;
// Testing api calls for a constructor that defines properties
const TestConstructor = require(`./build/${common.buildType}/test_constructor`);
const test_object = new TestConstructor();
assert.strictEqual(test_object.echo('hello'), 'hello');
test_object.readwriteValue = 1;
assert.strictEqual(test_object.readwriteValue, 1);
test_object.readwriteValue = 2;
assert.strictEqual(test_object.readwriteValue, 2);
assert.throws(() => { test_object.readonlyValue = 3; },
/^TypeError: Cannot assign to read(-| )only property 'readonlyValue'.*(of object '#<MyObject>')?/);
assert.ok(test_object.hiddenValue);
// Properties with napi_enumerable attribute should be enumerable.
const propertyNames = [];
for (const name in test_object) {
propertyNames.push(name);
}
assert.ok(propertyNames.includes('echo'));
assert.ok(propertyNames.includes('readwriteValue'));
assert.ok(propertyNames.includes('readonlyValue'));
assert.ok(!propertyNames.includes('hiddenValue'));
assert.ok(!propertyNames.includes('readwriteAccessor1'));
assert.ok(!propertyNames.includes('readwriteAccessor2'));
assert.ok(!propertyNames.includes('readonlyAccessor1'));
assert.ok(!propertyNames.includes('readonlyAccessor2'));
// The napi_writable attribute should be ignored for accessors.
test_object.readwriteAccessor1 = 1;
assert.strictEqual(test_object.readwriteAccessor1, 1);
assert.strictEqual(test_object.readonlyAccessor1, 1);
assert.throws(() => { test_object.readonlyAccessor1 = 3; }, getterOnlyErrorRE);
test_object.readwriteAccessor2 = 2;
assert.strictEqual(test_object.readwriteAccessor2, 2);
assert.strictEqual(test_object.readonlyAccessor2, 2);
assert.throws(() => { test_object.readonlyAccessor2 = 3; }, getterOnlyErrorRE);
// Validate that static properties are on the class as opposed
// to the instance
assert.strictEqual(TestConstructor.staticReadonlyAccessor1, 10);
assert.strictEqual(test_object.staticReadonlyAccessor1, undefined);
// Verify that passing NULL to napi_define_class() results in the correct
// error.
assert.deepStrictEqual(TestConstructor.TestDefineClass(), {
envIsNull: 'Invalid argument',
nameIsNull: 'Invalid argument',
cbIsNull: 'Invalid argument',
cbDataIsNull: 'napi_ok',
propertiesIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument'
});

Просмотреть файл

@ -0,0 +1,8 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
// Testing api calls for a constructor that defines properties
const TestConstructor =
require(`./build/${common.buildType}/test_constructor`).constructorName;
assert.strictEqual(TestConstructor.name, 'MyObject');

Просмотреть файл

@ -0,0 +1,196 @@
#include <js_native_api.h>
#include "../common.h"
static double value_ = 1;
static double static_value_ = 10;
static napi_value TestDefineClass(napi_env env,
napi_callback_info info) {
napi_status status;
napi_value result, return_value;
napi_property_descriptor property_descriptor = {
"TestDefineClass",
NULL,
TestDefineClass,
NULL,
NULL,
NULL,
napi_enumerable | napi_static,
NULL};
NODE_API_CALL(env, napi_create_object(env, &return_value));
status = napi_define_class(NULL,
"TrackedFunction",
NAPI_AUTO_LENGTH,
TestDefineClass,
NULL,
1,
&property_descriptor,
&result);
add_returned_status(env,
"envIsNull",
return_value,
"Invalid argument",
napi_invalid_arg,
status);
napi_define_class(env,
NULL,
NAPI_AUTO_LENGTH,
TestDefineClass,
NULL,
1,
&property_descriptor,
&result);
add_last_status(env, "nameIsNull", return_value);
napi_define_class(env,
"TrackedFunction",
NAPI_AUTO_LENGTH,
NULL,
NULL,
1,
&property_descriptor,
&result);
add_last_status(env, "cbIsNull", return_value);
napi_define_class(env,
"TrackedFunction",
NAPI_AUTO_LENGTH,
TestDefineClass,
NULL,
1,
&property_descriptor,
&result);
add_last_status(env, "cbDataIsNull", return_value);
napi_define_class(env,
"TrackedFunction",
NAPI_AUTO_LENGTH,
TestDefineClass,
NULL,
1,
NULL,
&result);
add_last_status(env, "propertiesIsNull", return_value);
napi_define_class(env,
"TrackedFunction",
NAPI_AUTO_LENGTH,
TestDefineClass,
NULL,
1,
&property_descriptor,
NULL);
add_last_status(env, "resultIsNull", return_value);
return return_value;
}
static napi_value GetValue(napi_env env, napi_callback_info info) {
size_t argc = 0;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, NULL, NULL, NULL));
NODE_API_ASSERT(env, argc == 0, "Wrong number of arguments");
napi_value number;
NODE_API_CALL(env, napi_create_double(env, value_, &number));
return number;
}
static napi_value SetValue(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
NODE_API_CALL(env, napi_get_value_double(env, args[0], &value_));
return NULL;
}
static napi_value Echo(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
return args[0];
}
static napi_value New(napi_env env, napi_callback_info info) {
napi_value _this;
NODE_API_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &_this, NULL));
return _this;
}
static napi_value GetStaticValue(napi_env env, napi_callback_info info) {
size_t argc = 0;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, NULL, NULL, NULL));
NODE_API_ASSERT(env, argc == 0, "Wrong number of arguments");
napi_value number;
NODE_API_CALL(env, napi_create_double(env, static_value_, &number));
return number;
}
static napi_value NewExtra(napi_env env, napi_callback_info info) {
napi_value _this;
NODE_API_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &_this, NULL));
return _this;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_value number, cons;
NODE_API_CALL(env, napi_create_double(env, value_, &number));
NODE_API_CALL(env, napi_define_class(
env, "MyObject_Extra", 8, NewExtra, NULL, 0, NULL, &cons));
napi_property_descriptor properties[] = {
{ "echo", NULL, Echo, NULL, NULL, NULL, napi_enumerable, NULL },
{ "readwriteValue", NULL, NULL, NULL, NULL, number,
napi_enumerable | napi_writable, NULL },
{ "readonlyValue", NULL, NULL, NULL, NULL, number, napi_enumerable,
NULL },
{ "hiddenValue", NULL, NULL, NULL, NULL, number, napi_default, NULL },
{ "readwriteAccessor1", NULL, NULL, GetValue, SetValue, NULL, napi_default,
NULL },
{ "readwriteAccessor2", NULL, NULL, GetValue, SetValue, NULL,
napi_writable, NULL },
{ "readonlyAccessor1", NULL, NULL, GetValue, NULL, NULL, napi_default,
NULL },
{ "readonlyAccessor2", NULL, NULL, GetValue, NULL, NULL, napi_writable,
NULL },
{ "staticReadonlyAccessor1", NULL, NULL, GetStaticValue, NULL, NULL,
napi_default | napi_static, NULL},
{ "constructorName", NULL, NULL, NULL, NULL, cons,
napi_enumerable | napi_static, NULL },
{ "TestDefineClass", NULL, TestDefineClass, NULL, NULL, NULL,
napi_enumerable | napi_static, NULL },
};
NODE_API_CALL(env, napi_define_class(env, "MyObject", NAPI_AUTO_LENGTH, New,
NULL, sizeof(properties)/sizeof(*properties), properties, &cons));
return cons;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,13 @@
{
"targets": [
{
"target_name": "test_conversions",
"sources": [
"../entry_point.c",
"../common.c",
"test_conversions.c",
"test_null.c",
]
}
]
}

Просмотреть файл

@ -0,0 +1,218 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const test = require(`./build/${common.buildType}/test_conversions`);
const boolExpected = /boolean was expected/;
const numberExpected = /number was expected/;
const stringExpected = /string was expected/;
const testSym = Symbol('test');
assert.strictEqual(test.asBool(false), false);
assert.strictEqual(test.asBool(true), true);
assert.throws(() => test.asBool(undefined), boolExpected);
assert.throws(() => test.asBool(null), boolExpected);
assert.throws(() => test.asBool(Number.NaN), boolExpected);
assert.throws(() => test.asBool(0), boolExpected);
assert.throws(() => test.asBool(''), boolExpected);
assert.throws(() => test.asBool('0'), boolExpected);
assert.throws(() => test.asBool(1), boolExpected);
assert.throws(() => test.asBool('1'), boolExpected);
assert.throws(() => test.asBool('true'), boolExpected);
assert.throws(() => test.asBool({}), boolExpected);
assert.throws(() => test.asBool([]), boolExpected);
assert.throws(() => test.asBool(testSym), boolExpected);
[test.asInt32, test.asUInt32, test.asInt64].forEach((asInt) => {
assert.strictEqual(asInt(0), 0);
assert.strictEqual(asInt(1), 1);
assert.strictEqual(asInt(1.0), 1);
assert.strictEqual(asInt(1.1), 1);
assert.strictEqual(asInt(1.9), 1);
assert.strictEqual(asInt(0.9), 0);
assert.strictEqual(asInt(999.9), 999);
assert.strictEqual(asInt(Number.NaN), 0);
assert.throws(() => asInt(undefined), numberExpected);
assert.throws(() => asInt(null), numberExpected);
assert.throws(() => asInt(false), numberExpected);
assert.throws(() => asInt(''), numberExpected);
assert.throws(() => asInt('1'), numberExpected);
assert.throws(() => asInt({}), numberExpected);
assert.throws(() => asInt([]), numberExpected);
assert.throws(() => asInt(testSym), numberExpected);
});
assert.strictEqual(test.asInt32(-1), -1);
assert.strictEqual(test.asInt64(-1), -1);
assert.strictEqual(test.asUInt32(-1), Math.pow(2, 32) - 1);
assert.strictEqual(test.asDouble(0), 0);
assert.strictEqual(test.asDouble(1), 1);
assert.strictEqual(test.asDouble(1.0), 1.0);
assert.strictEqual(test.asDouble(1.1), 1.1);
assert.strictEqual(test.asDouble(1.9), 1.9);
assert.strictEqual(test.asDouble(0.9), 0.9);
assert.strictEqual(test.asDouble(999.9), 999.9);
assert.strictEqual(test.asDouble(-1), -1);
assert.ok(Number.isNaN(test.asDouble(Number.NaN)));
assert.throws(() => test.asDouble(undefined), numberExpected);
assert.throws(() => test.asDouble(null), numberExpected);
assert.throws(() => test.asDouble(false), numberExpected);
assert.throws(() => test.asDouble(''), numberExpected);
assert.throws(() => test.asDouble('1'), numberExpected);
assert.throws(() => test.asDouble({}), numberExpected);
assert.throws(() => test.asDouble([]), numberExpected);
assert.throws(() => test.asDouble(testSym), numberExpected);
assert.strictEqual(test.asString(''), '');
assert.strictEqual(test.asString('test'), 'test');
assert.throws(() => test.asString(undefined), stringExpected);
assert.throws(() => test.asString(null), stringExpected);
assert.throws(() => test.asString(false), stringExpected);
assert.throws(() => test.asString(1), stringExpected);
assert.throws(() => test.asString(1.1), stringExpected);
assert.throws(() => test.asString(Number.NaN), stringExpected);
assert.throws(() => test.asString({}), stringExpected);
assert.throws(() => test.asString([]), stringExpected);
assert.throws(() => test.asString(testSym), stringExpected);
assert.strictEqual(test.toBool(true), true);
assert.strictEqual(test.toBool(1), true);
assert.strictEqual(test.toBool(-1), true);
assert.strictEqual(test.toBool('true'), true);
assert.strictEqual(test.toBool('false'), true);
assert.strictEqual(test.toBool({}), true);
assert.strictEqual(test.toBool([]), true);
assert.strictEqual(test.toBool(testSym), true);
assert.strictEqual(test.toBool(false), false);
assert.strictEqual(test.toBool(undefined), false);
assert.strictEqual(test.toBool(null), false);
assert.strictEqual(test.toBool(0), false);
assert.strictEqual(test.toBool(Number.NaN), false);
assert.strictEqual(test.toBool(''), false);
assert.strictEqual(test.toNumber(0), 0);
assert.strictEqual(test.toNumber(1), 1);
assert.strictEqual(test.toNumber(1.1), 1.1);
assert.strictEqual(test.toNumber(-1), -1);
assert.strictEqual(test.toNumber('0'), 0);
assert.strictEqual(test.toNumber('1'), 1);
assert.strictEqual(test.toNumber('1.1'), 1.1);
assert.strictEqual(test.toNumber([]), 0);
assert.strictEqual(test.toNumber(false), 0);
assert.strictEqual(test.toNumber(null), 0);
assert.strictEqual(test.toNumber(''), 0);
assert.ok(Number.isNaN(test.toNumber(Number.NaN)));
assert.ok(Number.isNaN(test.toNumber({})));
assert.ok(Number.isNaN(test.toNumber(undefined)));
assert.throws(() => test.toNumber(testSym), TypeError);
assert.deepStrictEqual({}, test.toObject({}));
assert.deepStrictEqual({ 'test': 1 }, test.toObject({ 'test': 1 }));
assert.deepStrictEqual([], test.toObject([]));
assert.deepStrictEqual([ 1, 2, 3 ], test.toObject([ 1, 2, 3 ]));
assert.deepStrictEqual(new Boolean(false), test.toObject(false));
assert.deepStrictEqual(new Boolean(true), test.toObject(true));
assert.deepStrictEqual(new String(''), test.toObject(''));
assert.deepStrictEqual(new Number(0), test.toObject(0));
assert.deepStrictEqual(new Number(Number.NaN), test.toObject(Number.NaN));
assert.deepStrictEqual(new Object(testSym), test.toObject(testSym));
assert.notDeepStrictEqual(test.toObject(false), false);
assert.notDeepStrictEqual(test.toObject(true), true);
assert.notDeepStrictEqual(test.toObject(''), '');
assert.notDeepStrictEqual(test.toObject(0), 0);
assert.ok(!Number.isNaN(test.toObject(Number.NaN)));
assert.strictEqual(test.toString(''), '');
assert.strictEqual(test.toString('test'), 'test');
assert.strictEqual(test.toString(undefined), 'undefined');
assert.strictEqual(test.toString(null), 'null');
assert.strictEqual(test.toString(false), 'false');
assert.strictEqual(test.toString(true), 'true');
assert.strictEqual(test.toString(0), '0');
assert.strictEqual(test.toString(1.1), '1.1');
assert.strictEqual(test.toString(Number.NaN), 'NaN');
assert.strictEqual(test.toString({}), '[object Object]');
assert.strictEqual(test.toString({ toString: () => 'test' }), 'test');
assert.strictEqual(test.toString([]), '');
assert.strictEqual(test.toString([ 1, 2, 3 ]), '1,2,3');
assert.throws(() => test.toString(testSym), TypeError);
assert.deepStrictEqual(test.testNull.getValueBool(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'A boolean was expected'
});
assert.deepStrictEqual(test.testNull.getValueInt32(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'A number was expected'
});
assert.deepStrictEqual(test.testNull.getValueUint32(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'A number was expected'
});
assert.deepStrictEqual(test.testNull.getValueInt64(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'A number was expected'
});
assert.deepStrictEqual(test.testNull.getValueDouble(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'A number was expected'
});
assert.deepStrictEqual(test.testNull.coerceToBool(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'napi_ok'
});
assert.deepStrictEqual(test.testNull.coerceToObject(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'napi_ok'
});
assert.deepStrictEqual(test.testNull.coerceToString(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument',
inputTypeCheck: 'napi_ok'
});
assert.deepStrictEqual(test.testNull.getValueStringUtf8(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
wrongTypeIn: 'A string was expected',
bufAndOutLengthIsNull: 'Invalid argument'
});
assert.deepStrictEqual(test.testNull.getValueStringLatin1(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
wrongTypeIn: 'A string was expected',
bufAndOutLengthIsNull: 'Invalid argument'
});
assert.deepStrictEqual(test.testNull.getValueStringUtf16(), {
envIsNull: 'Invalid argument',
valueIsNull: 'Invalid argument',
wrongTypeIn: 'A string was expected',
bufAndOutLengthIsNull: 'Invalid argument'
});

Просмотреть файл

@ -0,0 +1,157 @@
#include <js_native_api.h>
#include "../common.h"
#include "test_null.h"
static napi_value AsBool(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
bool value;
NODE_API_CALL(env, napi_get_value_bool(env, args[0], &value));
napi_value output;
NODE_API_CALL(env, napi_get_boolean(env, value, &output));
return output;
}
static napi_value AsInt32(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
int32_t value;
NODE_API_CALL(env, napi_get_value_int32(env, args[0], &value));
napi_value output;
NODE_API_CALL(env, napi_create_int32(env, value, &output));
return output;
}
static napi_value AsUInt32(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
uint32_t value;
NODE_API_CALL(env, napi_get_value_uint32(env, args[0], &value));
napi_value output;
NODE_API_CALL(env, napi_create_uint32(env, value, &output));
return output;
}
static napi_value AsInt64(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
int64_t value;
NODE_API_CALL(env, napi_get_value_int64(env, args[0], &value));
napi_value output;
NODE_API_CALL(env, napi_create_int64(env, (double)value, &output));
return output;
}
static napi_value AsDouble(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
double value;
NODE_API_CALL(env, napi_get_value_double(env, args[0], &value));
napi_value output;
NODE_API_CALL(env, napi_create_double(env, value, &output));
return output;
}
static napi_value AsString(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
char value[100];
NODE_API_CALL(env,
napi_get_value_string_utf8(env, args[0], value, sizeof(value), NULL));
napi_value output;
NODE_API_CALL(env, napi_create_string_utf8(
env, value, NAPI_AUTO_LENGTH, &output));
return output;
}
static napi_value ToBool(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value output;
NODE_API_CALL(env, napi_coerce_to_bool(env, args[0], &output));
return output;
}
static napi_value ToNumber(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value output;
NODE_API_CALL(env, napi_coerce_to_number(env, args[0], &output));
return output;
}
static napi_value ToObject(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value output;
NODE_API_CALL(env, napi_coerce_to_object(env, args[0], &output));
return output;
}
static napi_value ToString(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value output;
NODE_API_CALL(env, napi_coerce_to_string(env, args[0], &output));
return output;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("asBool", AsBool),
DECLARE_NODE_API_PROPERTY("asInt32", AsInt32),
DECLARE_NODE_API_PROPERTY("asUInt32", AsUInt32),
DECLARE_NODE_API_PROPERTY("asInt64", AsInt64),
DECLARE_NODE_API_PROPERTY("asDouble", AsDouble),
DECLARE_NODE_API_PROPERTY("asString", AsString),
DECLARE_NODE_API_PROPERTY("toBool", ToBool),
DECLARE_NODE_API_PROPERTY("toNumber", ToNumber),
DECLARE_NODE_API_PROPERTY("toObject", ToObject),
DECLARE_NODE_API_PROPERTY("toString", ToString),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
init_test_null(env, exports);
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,102 @@
#include <js_native_api.h>
#include "../common.h"
#include "test_null.h"
#define GEN_NULL_CHECK_BINDING(binding_name, output_type, api) \
static napi_value binding_name(napi_env env, napi_callback_info info) { \
napi_value return_value; \
output_type result; \
NODE_API_CALL(env, napi_create_object(env, &return_value)); \
add_returned_status(env, \
"envIsNull", \
return_value, \
"Invalid argument", \
napi_invalid_arg, \
api(NULL, return_value, &result)); \
api(env, NULL, &result); \
add_last_status(env, "valueIsNull", return_value); \
api(env, return_value, NULL); \
add_last_status(env, "resultIsNull", return_value); \
api(env, return_value, &result); \
add_last_status(env, "inputTypeCheck", return_value); \
return return_value; \
}
GEN_NULL_CHECK_BINDING(GetValueBool, bool, napi_get_value_bool)
GEN_NULL_CHECK_BINDING(GetValueInt32, int32_t, napi_get_value_int32)
GEN_NULL_CHECK_BINDING(GetValueUint32, uint32_t, napi_get_value_uint32)
GEN_NULL_CHECK_BINDING(GetValueInt64, int64_t, napi_get_value_int64)
GEN_NULL_CHECK_BINDING(GetValueDouble, double, napi_get_value_double)
GEN_NULL_CHECK_BINDING(CoerceToBool, napi_value, napi_coerce_to_bool)
GEN_NULL_CHECK_BINDING(CoerceToObject, napi_value, napi_coerce_to_object)
GEN_NULL_CHECK_BINDING(CoerceToString, napi_value, napi_coerce_to_string)
#define GEN_NULL_CHECK_STRING_BINDING(binding_name, arg_type, api) \
static napi_value binding_name(napi_env env, napi_callback_info info) { \
napi_value return_value; \
NODE_API_CALL(env, napi_create_object(env, &return_value)); \
arg_type buf1[4]; \
size_t length1 = 3; \
add_returned_status(env, \
"envIsNull", \
return_value, \
"Invalid argument", \
napi_invalid_arg, \
api(NULL, return_value, buf1, length1, &length1)); \
arg_type buf2[4]; \
size_t length2 = 3; \
api(env, NULL, buf2, length2, &length2); \
add_last_status(env, "valueIsNull", return_value); \
api(env, return_value, NULL, 3, NULL); \
add_last_status(env, "wrongTypeIn", return_value); \
napi_value string; \
NODE_API_CALL(env, \
napi_create_string_utf8(env, \
"Something", \
NAPI_AUTO_LENGTH, \
&string)); \
api(env, string, NULL, 3, NULL); \
add_last_status(env, "bufAndOutLengthIsNull", return_value); \
return return_value; \
}
GEN_NULL_CHECK_STRING_BINDING(GetValueStringUtf8,
char,
napi_get_value_string_utf8)
GEN_NULL_CHECK_STRING_BINDING(GetValueStringLatin1,
char,
napi_get_value_string_latin1)
GEN_NULL_CHECK_STRING_BINDING(GetValueStringUtf16,
char16_t,
napi_get_value_string_utf16)
void init_test_null(napi_env env, napi_value exports) {
napi_value test_null;
const napi_property_descriptor test_null_props[] = {
DECLARE_NODE_API_PROPERTY("getValueBool", GetValueBool),
DECLARE_NODE_API_PROPERTY("getValueInt32", GetValueInt32),
DECLARE_NODE_API_PROPERTY("getValueUint32", GetValueUint32),
DECLARE_NODE_API_PROPERTY("getValueInt64", GetValueInt64),
DECLARE_NODE_API_PROPERTY("getValueDouble", GetValueDouble),
DECLARE_NODE_API_PROPERTY("coerceToBool", CoerceToBool),
DECLARE_NODE_API_PROPERTY("coerceToObject", CoerceToObject),
DECLARE_NODE_API_PROPERTY("coerceToString", CoerceToString),
DECLARE_NODE_API_PROPERTY("getValueStringUtf8", GetValueStringUtf8),
DECLARE_NODE_API_PROPERTY("getValueStringLatin1", GetValueStringLatin1),
DECLARE_NODE_API_PROPERTY("getValueStringUtf16", GetValueStringUtf16),
};
NODE_API_CALL_RETURN_VOID(env, napi_create_object(env, &test_null));
NODE_API_CALL_RETURN_VOID(env, napi_define_properties(
env, test_null, sizeof(test_null_props) / sizeof(*test_null_props),
test_null_props));
const napi_property_descriptor test_null_set = {
"testNull", NULL, NULL, NULL, NULL, test_null, napi_enumerable, NULL
};
NODE_API_CALL_RETURN_VOID(env,
napi_define_properties(env, exports, 1, &test_null_set));
}

Просмотреть файл

@ -0,0 +1,8 @@
#ifndef TEST_JS_NATIVE_API_TEST_CONVERSIONS_TEST_NULL_H_
#define TEST_JS_NATIVE_API_TEST_CONVERSIONS_TEST_NULL_H_
#include <js_native_api.h>
void init_test_null(napi_env env, napi_value exports);
#endif // TEST_JS_NATIVE_API_TEST_CONVERSIONS_TEST_NULL_H_

Просмотреть файл

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "test_dataview",
"sources": [
"../entry_point.c",
"test_dataview.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,24 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
// Testing api calls for arrays
const test_dataview = require(`./build/${common.buildType}/test_dataview`);
// Test for creating dataview
{
const buffer = new ArrayBuffer(128);
const template = Reflect.construct(DataView, [buffer]);
const theDataview = test_dataview.CreateDataViewFromJSDataView(template);
assert.ok(theDataview instanceof DataView,
`Expect ${theDataview} to be a DataView`);
}
// Test for creating dataview with invalid range
{
const buffer = new ArrayBuffer(128);
assert.throws(() => {
test_dataview.CreateDataView(buffer, 10, 200);
}, RangeError);
}

Просмотреть файл

@ -0,0 +1,101 @@
#include <js_native_api.h>
#include <string.h>
#include "../common.h"
static napi_value CreateDataView(napi_env env, napi_callback_info info) {
size_t argc = 3;
napi_value args [3];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc == 3, "Wrong number of arguments");
napi_valuetype valuetype0;
napi_value arraybuffer = args[0];
NODE_API_CALL(env, napi_typeof(env, arraybuffer, &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_object,
"Wrong type of arguments. Expects a ArrayBuffer as the first "
"argument.");
bool is_arraybuffer;
NODE_API_CALL(env, napi_is_arraybuffer(env, arraybuffer, &is_arraybuffer));
NODE_API_ASSERT(env, is_arraybuffer,
"Wrong type of arguments. Expects a ArrayBuffer as the first "
"argument.");
napi_valuetype valuetype1;
NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));
NODE_API_ASSERT(env, valuetype1 == napi_number,
"Wrong type of arguments. Expects a number as second argument.");
size_t byte_offset = 0;
NODE_API_CALL(env, napi_get_value_uint32(env, args[1], (uint32_t*)(&byte_offset)));
napi_valuetype valuetype2;
NODE_API_CALL(env, napi_typeof(env, args[2], &valuetype2));
NODE_API_ASSERT(env, valuetype2 == napi_number,
"Wrong type of arguments. Expects a number as third argument.");
size_t length = 0;
NODE_API_CALL(env, napi_get_value_uint32(env, args[2], (uint32_t*)(&length)));
napi_value output_dataview;
NODE_API_CALL(env,
napi_create_dataview(env, length, arraybuffer,
byte_offset, &output_dataview));
return output_dataview;
}
static napi_value CreateDataViewFromJSDataView(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args [1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
napi_valuetype valuetype;
napi_value input_dataview = args[0];
NODE_API_CALL(env, napi_typeof(env, input_dataview, &valuetype));
NODE_API_ASSERT(env, valuetype == napi_object,
"Wrong type of arguments. Expects a DataView as the first "
"argument.");
bool is_dataview;
NODE_API_CALL(env, napi_is_dataview(env, input_dataview, &is_dataview));
NODE_API_ASSERT(env, is_dataview,
"Wrong type of arguments. Expects a DataView as the first "
"argument.");
size_t byte_offset = 0;
size_t length = 0;
napi_value buffer;
NODE_API_CALL(env,
napi_get_dataview_info(env, input_dataview, &length, NULL,
&buffer, &byte_offset));
napi_value output_dataview;
NODE_API_CALL(env,
napi_create_dataview(env, length, buffer,
byte_offset, &output_dataview));
return output_dataview;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("CreateDataView", CreateDataView),
DECLARE_NODE_API_PROPERTY("CreateDataViewFromJSDataView",
CreateDataViewFromJSDataView)
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "test_date",
"sources": [
"../entry_point.c",
"test_date.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,21 @@
'use strict';
const common = require('../../common');
// This tests the date-related n-api calls
const assert = require('assert');
const test_date = require(`./build/${common.buildType}/test_date`);
const dateTypeTestDate = test_date.createDate(1549183351);
assert.strictEqual(test_date.isDate(dateTypeTestDate), true);
assert.strictEqual(test_date.isDate(new Date(1549183351)), true);
assert.strictEqual(test_date.isDate(2.4), false);
assert.strictEqual(test_date.isDate('not a date'), false);
assert.strictEqual(test_date.isDate(undefined), false);
assert.strictEqual(test_date.isDate(null), false);
assert.strictEqual(test_date.isDate({}), false);
assert.strictEqual(test_date.getDateValue(new Date(1549183351)), 1549183351);

Просмотреть файл

@ -0,0 +1,63 @@
#include <js_native_api.h>
#include "../common.h"
static napi_value createDate(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_number,
"Wrong type of arguments. Expects a number as first argument.");
double time;
NODE_API_CALL(env, napi_get_value_double(env, args[0], &time));
napi_value date;
NODE_API_CALL(env, napi_create_date(env, time, &date));
return date;
}
static napi_value isDate(napi_env env, napi_callback_info info) {
napi_value date, result;
size_t argc = 1;
bool is_date;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &date, NULL, NULL));
NODE_API_CALL(env, napi_is_date(env, date, &is_date));
NODE_API_CALL(env, napi_get_boolean(env, is_date, &result));
return result;
}
static napi_value getDateValue(napi_env env, napi_callback_info info) {
napi_value date, result;
size_t argc = 1;
double value;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &date, NULL, NULL));
NODE_API_CALL(env, napi_get_date_value(env, date, &value));
NODE_API_CALL(env, napi_create_double(env, value, &result));
return result;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("createDate", createDate),
DECLARE_NODE_API_PROPERTY("isDate", isDate),
DECLARE_NODE_API_PROPERTY("getDateValue", getDateValue),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "test_error",
"sources": [
"../entry_point.c",
"test_error.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,125 @@
'use strict';
const common = require('../../common');
const test_error = require(`./build/${common.buildType}/test_error`);
const assert = require('assert');
const theError = new Error('Some error');
const theTypeError = new TypeError('Some type error');
const theSyntaxError = new SyntaxError('Some syntax error');
const theRangeError = new RangeError('Some type error');
const theReferenceError = new ReferenceError('Some reference error');
const theURIError = new URIError('Some URI error');
const theEvalError = new EvalError('Some eval error');
class MyError extends Error { }
const myError = new MyError('Some MyError');
// Test that native error object is correctly classed
assert.strictEqual(test_error.checkError(theError), true);
// Test that native type error object is correctly classed
assert.strictEqual(test_error.checkError(theTypeError), true);
// Test that native syntax error object is correctly classed
assert.strictEqual(test_error.checkError(theSyntaxError), true);
// Test that native range error object is correctly classed
assert.strictEqual(test_error.checkError(theRangeError), true);
// Test that native reference error object is correctly classed
assert.strictEqual(test_error.checkError(theReferenceError), true);
// Test that native URI error object is correctly classed
assert.strictEqual(test_error.checkError(theURIError), true);
// Test that native eval error object is correctly classed
assert.strictEqual(test_error.checkError(theEvalError), true);
// Test that class derived from native error is correctly classed
assert.strictEqual(test_error.checkError(myError), true);
// Test that non-error object is correctly classed
assert.strictEqual(test_error.checkError({}), false);
// Test that non-error primitive is correctly classed
assert.strictEqual(test_error.checkError('non-object'), false);
assert.throws(() => {
test_error.throwExistingError();
}, /^Error: existing error$/);
assert.throws(() => {
test_error.throwError();
}, /^Error: error$/);
assert.throws(() => {
test_error.throwRangeError();
}, /^RangeError: range error$/);
assert.throws(() => {
test_error.throwTypeError();
}, /^TypeError: type error$/);
[42, {}, [], Symbol('xyzzy'), true, 'ball', undefined, null, NaN]
.forEach((value) => assert.throws(
() => test_error.throwArbitrary(value),
(err) => {
assert.strictEqual(err, value);
return true;
}
));
assert.throws(
() => test_error.throwErrorCode(),
{
code: 'ERR_TEST_CODE',
message: 'Error [error]'
});
assert.throws(
() => test_error.throwRangeErrorCode(),
{
code: 'ERR_TEST_CODE',
message: 'RangeError [range error]'
});
assert.throws(
() => test_error.throwTypeErrorCode(),
{
code: 'ERR_TEST_CODE',
message: 'TypeError [type error]'
});
let error = test_error.createError();
assert.ok(error instanceof Error, 'expected error to be an instance of Error');
assert.strictEqual(error.message, 'error');
error = test_error.createRangeError();
assert.ok(error instanceof RangeError,
'expected error to be an instance of RangeError');
assert.strictEqual(error.message, 'range error');
error = test_error.createTypeError();
assert.ok(error instanceof TypeError,
'expected error to be an instance of TypeError');
assert.strictEqual(error.message, 'type error');
error = test_error.createErrorCode();
assert.ok(error instanceof Error, 'expected error to be an instance of Error');
assert.strictEqual(error.code, 'ERR_TEST_CODE');
assert.strictEqual(error.message, 'Error [error]');
assert.strictEqual(error.name, 'Error');
error = test_error.createRangeErrorCode();
assert.ok(error instanceof RangeError,
'expected error to be an instance of RangeError');
assert.strictEqual(error.message, 'RangeError [range error]');
assert.strictEqual(error.code, 'ERR_TEST_CODE');
assert.strictEqual(error.name, 'RangeError');
error = test_error.createTypeErrorCode();
assert.ok(error instanceof TypeError,
'expected error to be an instance of TypeError');
assert.strictEqual(error.message, 'TypeError [type error]');
assert.strictEqual(error.code, 'ERR_TEST_CODE');
assert.strictEqual(error.name, 'TypeError');

Просмотреть файл

@ -0,0 +1,159 @@
#include <js_native_api.h>
#include "../common.h"
static napi_value checkError(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
bool r;
NODE_API_CALL(env, napi_is_error(env, args[0], &r));
napi_value result;
NODE_API_CALL(env, napi_get_boolean(env, r, &result));
return result;
}
static napi_value throwExistingError(napi_env env, napi_callback_info info) {
napi_value message;
napi_value error;
NODE_API_CALL(env, napi_create_string_utf8(
env, "existing error", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_error(env, NULL, message, &error));
NODE_API_CALL(env, napi_throw(env, error));
return NULL;
}
static napi_value throwError(napi_env env, napi_callback_info info) {
NODE_API_CALL(env, napi_throw_error(env, NULL, "error"));
return NULL;
}
static napi_value throwRangeError(napi_env env, napi_callback_info info) {
NODE_API_CALL(env, napi_throw_range_error(env, NULL, "range error"));
return NULL;
}
static napi_value throwTypeError(napi_env env, napi_callback_info info) {
NODE_API_CALL(env, napi_throw_type_error(env, NULL, "type error"));
return NULL;
}
static napi_value throwErrorCode(napi_env env, napi_callback_info info) {
NODE_API_CALL(env, napi_throw_error(env, "ERR_TEST_CODE", "Error [error]"));
return NULL;
}
static napi_value throwRangeErrorCode(napi_env env, napi_callback_info info) {
NODE_API_CALL(env,
napi_throw_range_error(env, "ERR_TEST_CODE", "RangeError [range error]"));
return NULL;
}
static napi_value throwTypeErrorCode(napi_env env, napi_callback_info info) {
NODE_API_CALL(env,
napi_throw_type_error(env, "ERR_TEST_CODE", "TypeError [type error]"));
return NULL;
}
static napi_value createError(napi_env env, napi_callback_info info) {
napi_value result;
napi_value message;
NODE_API_CALL(env, napi_create_string_utf8(
env, "error", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_error(env, NULL, message, &result));
return result;
}
static napi_value createRangeError(napi_env env, napi_callback_info info) {
napi_value result;
napi_value message;
NODE_API_CALL(env, napi_create_string_utf8(
env, "range error", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_range_error(env, NULL, message, &result));
return result;
}
static napi_value createTypeError(napi_env env, napi_callback_info info) {
napi_value result;
napi_value message;
NODE_API_CALL(env, napi_create_string_utf8(
env, "type error", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_type_error(env, NULL, message, &result));
return result;
}
static napi_value createErrorCode(napi_env env, napi_callback_info info) {
napi_value result;
napi_value message;
napi_value code;
NODE_API_CALL(env, napi_create_string_utf8(
env, "Error [error]", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_string_utf8(
env, "ERR_TEST_CODE", NAPI_AUTO_LENGTH, &code));
NODE_API_CALL(env, napi_create_error(env, code, message, &result));
return result;
}
static napi_value createRangeErrorCode(napi_env env, napi_callback_info info) {
napi_value result;
napi_value message;
napi_value code;
NODE_API_CALL(env,
napi_create_string_utf8(
env, "RangeError [range error]", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_string_utf8(
env, "ERR_TEST_CODE", NAPI_AUTO_LENGTH, &code));
NODE_API_CALL(env, napi_create_range_error(env, code, message, &result));
return result;
}
static napi_value createTypeErrorCode(napi_env env, napi_callback_info info) {
napi_value result;
napi_value message;
napi_value code;
NODE_API_CALL(env,
napi_create_string_utf8(
env, "TypeError [type error]", NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_string_utf8(
env, "ERR_TEST_CODE", NAPI_AUTO_LENGTH, &code));
NODE_API_CALL(env, napi_create_type_error(env, code, message, &result));
return result;
}
static napi_value throwArbitrary(napi_env env, napi_callback_info info) {
napi_value arbitrary;
size_t argc = 1;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &arbitrary, NULL, NULL));
NODE_API_CALL(env, napi_throw(env, arbitrary));
return NULL;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("checkError", checkError),
DECLARE_NODE_API_PROPERTY("throwExistingError", throwExistingError),
DECLARE_NODE_API_PROPERTY("throwError", throwError),
DECLARE_NODE_API_PROPERTY("throwRangeError", throwRangeError),
DECLARE_NODE_API_PROPERTY("throwTypeError", throwTypeError),
DECLARE_NODE_API_PROPERTY("throwErrorCode", throwErrorCode),
DECLARE_NODE_API_PROPERTY("throwRangeErrorCode", throwRangeErrorCode),
DECLARE_NODE_API_PROPERTY("throwTypeErrorCode", throwTypeErrorCode),
DECLARE_NODE_API_PROPERTY("throwArbitrary", throwArbitrary),
DECLARE_NODE_API_PROPERTY("createError", createError),
DECLARE_NODE_API_PROPERTY("createRangeError", createRangeError),
DECLARE_NODE_API_PROPERTY("createTypeError", createTypeError),
DECLARE_NODE_API_PROPERTY("createErrorCode", createErrorCode),
DECLARE_NODE_API_PROPERTY("createRangeErrorCode", createRangeErrorCode),
DECLARE_NODE_API_PROPERTY("createTypeErrorCode", createTypeErrorCode),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "test_exception",
"sources": [
"../entry_point.c",
"test_exception.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,68 @@
'use strict';
// Flags: --expose-gc
const common = require('../../common');
const assert = require('assert');
const theError = new Error('Some error');
// The test module throws an error during Init, but in order for its exports to
// not be lost, it attaches them to the error's "bindings" property. This way,
// we can make sure that exceptions thrown during the module initialization
// phase are propagated through require() into JavaScript.
// https://github.com/nodejs/node/issues/19437
const test_exception = (function() {
let resultingException;
try {
require(`./build/${common.buildType}/test_exception`);
} catch (anException) {
resultingException = anException;
}
assert.strictEqual(resultingException.message, 'Error during Init');
return resultingException.binding;
})();
{
const throwTheError = () => { throw theError; };
// Test that the native side successfully captures the exception
let returnedError = test_exception.returnException(throwTheError);
assert.strictEqual(returnedError, theError);
// Test that the native side passes the exception through
assert.throws(
() => { test_exception.allowException(throwTheError); },
(err) => err === theError
);
// Test that the exception thrown above was marked as pending
// before it was handled on the JS side
const exception_pending = test_exception.wasPending();
assert.strictEqual(exception_pending, true,
'Exception not pending as expected,' +
` .wasPending() returned ${exception_pending}`);
// Test that the native side does not capture a non-existing exception
returnedError = test_exception.returnException(common.mustCall());
assert.strictEqual(returnedError, undefined,
'Returned error should be undefined when no exception is' +
` thrown, but ${returnedError} was passed`);
}
{
// Test that no exception appears that was not thrown by us
let caughtError;
try {
test_exception.allowException(common.mustCall());
} catch (anError) {
caughtError = anError;
}
assert.strictEqual(caughtError, undefined,
'No exception originated on the native side, but' +
` ${caughtError} was passed`);
// Test that the exception state remains clear when no exception is thrown
const exception_pending = test_exception.wasPending();
assert.strictEqual(exception_pending, false,
'Exception state did not remain clear as expected,' +
` .wasPending() returned ${exception_pending}`);
}

Просмотреть файл

@ -0,0 +1,31 @@
'use strict';
if (process.argv[2] === 'child') {
const common = require('../../common');
// Trying, catching the exception, and finding the bindings at the `Error`'s
// `binding` property is done intentionally, because we're also testing what
// happens when the add-on entry point throws. See test.js.
try {
require(`./build/${common.buildType}/test_exception`);
} catch (anException) {
anException.binding.createExternal();
}
// Collect garbage 10 times. At least one of those should throw the exception
// and cause the whole process to bail with it, its text printed to stderr and
// asserted by the parent process to match expectations.
let gcCount = 10;
(function gcLoop() {
global.gc();
if (--gcCount > 0) {
setImmediate(() => gcLoop());
}
})();
} else {
const assert = require('assert');
const { spawnSync } = require('child_process');
const child = spawnSync(process.execPath, [
'--expose-gc', __filename, 'child',
]);
assert.strictEqual(child.signal, null);
assert.match(child.stderr.toString(), /Error during Finalize/m);
}

Просмотреть файл

@ -0,0 +1,83 @@
#include <js_native_api.h>
#include "../common.h"
static bool exceptionWasPending = false;
static napi_value returnException(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value global;
NODE_API_CALL(env, napi_get_global(env, &global));
napi_value result;
napi_status status = napi_call_function(env, global, args[0], 0, 0, &result);
if (status == napi_pending_exception) {
napi_value ex;
NODE_API_CALL(env, napi_get_and_clear_last_exception(env, &ex));
return ex;
}
return NULL;
}
static napi_value allowException(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value global;
NODE_API_CALL(env, napi_get_global(env, &global));
napi_value result;
napi_call_function(env, global, args[0], 0, 0, &result);
// Ignore status and check napi_is_exception_pending() instead.
NODE_API_CALL(env, napi_is_exception_pending(env, &exceptionWasPending));
return NULL;
}
static napi_value wasPending(napi_env env, napi_callback_info info) {
napi_value result;
NODE_API_CALL(env, napi_get_boolean(env, exceptionWasPending, &result));
return result;
}
static void finalizer(napi_env env, void *data, void *hint) {
NODE_API_CALL_RETURN_VOID(env,
napi_throw_error(env, NULL, "Error during Finalize"));
}
static napi_value createExternal(napi_env env, napi_callback_info info) {
napi_value external;
NODE_API_CALL(env,
napi_create_external(env, NULL, finalizer, NULL, &external));
return external;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("returnException", returnException),
DECLARE_NODE_API_PROPERTY("allowException", allowException),
DECLARE_NODE_API_PROPERTY("wasPending", wasPending),
DECLARE_NODE_API_PROPERTY("createExternal", createExternal),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
napi_value error, code, message;
NODE_API_CALL(env, napi_create_string_utf8(env, "Error during Init",
NAPI_AUTO_LENGTH, &message));
NODE_API_CALL(env, napi_create_string_utf8(env, "", NAPI_AUTO_LENGTH, &code));
NODE_API_CALL(env, napi_create_error(env, code, message, &error));
NODE_API_CALL(env, napi_set_named_property(env, error, "binding", exports));
NODE_API_CALL(env, napi_throw(env, error));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,12 @@
{
"targets": [
{
"target_name": "test_function",
"sources": [
"../common.c",
"../entry_point.c",
"test_function.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,46 @@
'use strict';
// Flags: --expose-gc
const common = require('../../common');
const assert = require('assert');
// Testing api calls for function
const test_function = require(`./build/${common.buildType}/test_function`);
function func1() {
return 1;
}
assert.strictEqual(test_function.TestCall(func1), 1);
function func2() {
// console.log('hello world!');
return null;
}
assert.strictEqual(test_function.TestCall(func2), null);
function func3(input) {
return input + 1;
}
assert.strictEqual(test_function.TestCall(func3, 1), 2);
function func4(input) {
return func3(input);
}
assert.strictEqual(test_function.TestCall(func4, 1), 2);
assert.strictEqual(test_function.TestName.name, 'Name');
assert.strictEqual(test_function.TestNameShort.name, 'Name_');
(function () {
let tracked_function = test_function.MakeTrackedFunction(common.mustCall());
assert(!!tracked_function);
tracked_function = null;
})();
global.gc();
assert.deepStrictEqual(test_function.TestCreateFunctionParameters(), {
envIsNull: 'Invalid argument',
nameIsNull: 'napi_ok',
cbIsNull: 'Invalid argument',
resultIsNull: 'Invalid argument'
});

Просмотреть файл

@ -0,0 +1,180 @@
#include <js_native_api.h>
#include "../common.h"
static napi_value TestCreateFunctionParameters(napi_env env,
napi_callback_info info) {
napi_status status;
napi_value result, return_value;
NODE_API_CALL(env, napi_create_object(env, &return_value));
status = napi_create_function(NULL,
"TrackedFunction",
NAPI_AUTO_LENGTH,
TestCreateFunctionParameters,
NULL,
&result);
add_returned_status(env,
"envIsNull",
return_value,
"Invalid argument",
napi_invalid_arg,
status);
napi_create_function(env,
NULL,
NAPI_AUTO_LENGTH,
TestCreateFunctionParameters,
NULL,
&result);
add_last_status(env, "nameIsNull", return_value);
napi_create_function(env,
"TrackedFunction",
NAPI_AUTO_LENGTH,
NULL,
NULL,
&result);
add_last_status(env, "cbIsNull", return_value);
napi_create_function(env,
"TrackedFunction",
NAPI_AUTO_LENGTH,
TestCreateFunctionParameters,
NULL,
NULL);
add_last_status(env, "resultIsNull", return_value);
return return_value;
}
static napi_value TestCallFunction(napi_env env, napi_callback_info info) {
size_t argc = 10;
napi_value args[10];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc > 0, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(env, valuetype0 == napi_function,
"Wrong type of arguments. Expects a function as first argument.");
napi_value* argv = args + 1;
argc = argc - 1;
napi_value global;
NODE_API_CALL(env, napi_get_global(env, &global));
napi_value result;
NODE_API_CALL(env, napi_call_function(env, global, args[0], argc, argv, &result));
return result;
}
static napi_value TestFunctionName(napi_env env, napi_callback_info info) {
return NULL;
}
static void finalize_function(napi_env env, void* data, void* hint) {
napi_ref ref = (napi_ref)data;
// Retrieve the JavaScript undefined value.
napi_value undefined;
NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
// Retrieve the JavaScript function we must call.
napi_value js_function;
NODE_API_CALL_RETURN_VOID(env, napi_get_reference_value(env, ref, &js_function));
// Call the JavaScript function to indicate that the generated JavaScript
// function is about to be gc-ed.
NODE_API_CALL_RETURN_VOID(env,
napi_call_function(env, undefined, js_function, 0, NULL, NULL));
// Destroy the persistent reference to the function we just called so as to
// properly clean up.
NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, ref));
}
static napi_value MakeTrackedFunction(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value js_finalize_cb;
napi_valuetype arg_type;
// Retrieve and validate from the arguments the function we will use to
// indicate to JavaScript that the function we are about to create is about to
// be gc-ed.
NODE_API_CALL(env,
napi_get_cb_info(env, info, &argc, &js_finalize_cb, NULL, NULL));
NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
NODE_API_CALL(env, napi_typeof(env, js_finalize_cb, &arg_type));
NODE_API_ASSERT(env, arg_type == napi_function, "Argument must be a function");
// Dynamically create a function.
napi_value result;
NODE_API_CALL(env,
napi_create_function(
env, "TrackedFunction", NAPI_AUTO_LENGTH, TestFunctionName, NULL,
&result));
// Create a strong reference to the function we will call when the tracked
// function is about to be gc-ed.
napi_ref js_finalize_cb_ref;
NODE_API_CALL(env,
napi_create_reference(env, js_finalize_cb, 1, &js_finalize_cb_ref));
// Attach a finalizer to the dynamically created function and pass it the
// strong reference we created in the previous step.
NODE_API_CALL(env,
napi_wrap(
env, result, js_finalize_cb_ref, finalize_function, NULL, NULL));
return result;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_value fn1;
NODE_API_CALL(env, napi_create_function(
env, NULL, NAPI_AUTO_LENGTH, TestCallFunction, NULL, &fn1));
napi_value fn2;
NODE_API_CALL(env, napi_create_function(
env, "Name", NAPI_AUTO_LENGTH, TestFunctionName, NULL, &fn2));
napi_value fn3;
NODE_API_CALL(env, napi_create_function(
env, "Name_extra", 5, TestFunctionName, NULL, &fn3));
napi_value fn4;
NODE_API_CALL(env,
napi_create_function(
env, "MakeTrackedFunction", NAPI_AUTO_LENGTH, MakeTrackedFunction,
NULL, &fn4));
napi_value fn5;
NODE_API_CALL(env,
napi_create_function(
env, "TestCreateFunctionParameters", NAPI_AUTO_LENGTH,
TestCreateFunctionParameters, NULL, &fn5));
NODE_API_CALL(env, napi_set_named_property(env, exports, "TestCall", fn1));
NODE_API_CALL(env, napi_set_named_property(env, exports, "TestName", fn2));
NODE_API_CALL(env,
napi_set_named_property(env, exports, "TestNameShort", fn3));
NODE_API_CALL(env,
napi_set_named_property(env, exports, "MakeTrackedFunction", fn4));
NODE_API_CALL(env,
napi_set_named_property(
env, exports, "TestCreateFunctionParameters", fn5));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "test_general",
"sources": [
"../entry_point.c",
"test_general.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,97 @@
'use strict';
// Flags: --expose-gc
const common = require('../../common');
const test_general = require(`./build/${common.buildType}/test_general`);
const assert = require('assert');
const val1 = '1';
const val2 = 1;
const val3 = 1;
class BaseClass {
}
class ExtendedClass extends BaseClass {
}
const baseObject = new BaseClass();
const extendedObject = new ExtendedClass();
// Test napi_strict_equals
assert.ok(test_general.testStrictEquals(val1, val1));
assert.strictEqual(test_general.testStrictEquals(val1, val2), false);
assert.ok(test_general.testStrictEquals(val2, val3));
// Test napi_get_prototype
assert.strictEqual(test_general.testGetPrototype(baseObject),
Object.getPrototypeOf(baseObject));
assert.strictEqual(test_general.testGetPrototype(extendedObject),
Object.getPrototypeOf(extendedObject));
// Prototypes for base and extended should be different.
assert.notStrictEqual(test_general.testGetPrototype(baseObject),
test_general.testGetPrototype(extendedObject));
// Test version management functions
assert.strictEqual(test_general.testGetVersion(), 8);
[
123,
'test string',
function() {},
new Object(),
true,
undefined,
Symbol(),
].forEach((val) => {
assert.strictEqual(test_general.testNapiTypeof(val), typeof val);
});
// Since typeof in js return object need to validate specific case
// for null
assert.strictEqual(test_general.testNapiTypeof(null), 'null');
// Assert that wrapping twice fails.
const x = {};
test_general.wrap(x);
assert.throws(() => test_general.wrap(x),
{ name: 'Error', message: 'Invalid argument' });
// Clean up here, otherwise derefItemWasCalled() will be polluted.
test_general.removeWrap(x);
// Ensure that wrapping, removing the wrap, and then wrapping again works.
const y = {};
test_general.wrap(y);
test_general.removeWrap(y);
// Wrapping twice succeeds if a remove_wrap() separates the instances
test_general.wrap(y);
// Clean up here, otherwise derefItemWasCalled() will be polluted.
test_general.removeWrap(y);
// Test napi_adjust_external_memory
// TODO: Hermes does not implement that API
// const adjustedValue = test_general.testAdjustExternalMemory();
// assert.strictEqual(typeof adjustedValue, 'number');
// assert(adjustedValue > 0);
async function runGCTests() {
// Ensure that garbage collecting an object with a wrapped native item results
// in the finalize callback being called.
assert.strictEqual(test_general.derefItemWasCalled(), false);
(() => test_general.wrap({}))();
await common.gcUntil('deref_item() was called upon garbage collecting a ' +
'wrapped object.',
() => test_general.derefItemWasCalled());
// Ensure that removing a wrap and garbage collecting does not fire the
// finalize callback.
let z = {};
test_general.testFinalizeWrap(z);
test_general.removeWrap(z);
z = null;
await common.gcUntil(
'finalize callback was not called upon garbage collection.',
() => (!test_general.finalizeWasCalled()));
}
runGCTests();

Просмотреть файл

@ -0,0 +1,57 @@
'use strict';
if (process.argv[2] === 'child') {
const common = require('../../common');
const test_general = require(`./build/${common.buildType}/test_general`);
// The second argument to `envCleanupWrap()` is an index into the global
// static string array named `env_cleanup_finalizer_messages` on the native
// side. A reverse mapping is reproduced here for clarity.
const finalizerMessages = {
'simple wrap': 0,
'wrap, removeWrap': 1,
'first wrap': 2,
'second wrap': 3
};
// We attach the three objects we will test to `module.exports` to ensure they
// will not be garbage-collected before the process exits.
// Make sure the finalizer for a simple wrap will be called at env cleanup.
module.exports['simple wrap'] =
test_general.envCleanupWrap({}, finalizerMessages['simple wrap']);
// Make sure that a removed wrap does not result in a call to its finalizer at
// env cleanup.
module.exports['wrap, removeWrap'] =
test_general.envCleanupWrap({}, finalizerMessages['wrap, removeWrap']);
test_general.removeWrap(module.exports['wrap, removeWrap']);
// Make sure that only the latest attached version of a re-wrapped item's
// finalizer gets called at env cleanup.
module.exports['first wrap'] =
test_general.envCleanupWrap({}, finalizerMessages['first wrap']);
test_general.removeWrap(module.exports['first wrap']);
test_general.envCleanupWrap(module.exports['first wrap'],
finalizerMessages['second wrap']);
} else {
const assert = require('assert');
const { spawnSync } = require('child_process');
const child = spawnSync(process.execPath, [__filename, 'child'], {
stdio: [ process.stdin, 'pipe', process.stderr ]
});
// Grab the child's output and construct an object whose keys are the rows of
// the output and whose values are `true`, so we can compare the output while
// ignoring the order in which the lines of it were produced.
assert.deepStrictEqual(
child.stdout.toString().split(/\r\n|\r|\n/g).reduce((obj, item) =>
Object.assign(obj, item ? { [item]: true } : {}), {}), {
'finalize at env cleanup for simple wrap': true,
'finalize at env cleanup for second wrap': true
});
// Ensure that the child exited successfully.
assert.strictEqual(child.status, 0);
}

Просмотреть файл

@ -0,0 +1,38 @@
'use strict';
// Flags: --expose-gc
const common = require('../../common');
const test_general = require(`./build/${common.buildType}/test_general`);
const assert = require('assert');
(function() {
let finalized = {};
const callback = common.mustCall(2);
// Add two items to be finalized and ensure the callback is called for each.
test_general.addFinalizerOnly(finalized, callback);
test_general.addFinalizerOnly(finalized, callback);
// Ensure attached items cannot be retrieved.
assert.throws(() => test_general.unwrap(finalized),
{ name: 'Error', message: 'Invalid argument' });
// Ensure attached items cannot be removed.
assert.throws(() => test_general.removeWrap(finalized),
{ name: 'Error', message: 'Invalid argument' });
})();
global.gc();
// Add an item to an object that is already wrapped, and ensure that its
// finalizer as well as the wrap finalizer gets called.
async function testFinalizeAndWrap() {
assert.strictEqual(test_general.derefItemWasCalled(), false);
(function() {
let finalizeAndWrap = {};
test_general.wrap(finalizeAndWrap);
test_general.addFinalizerOnly(finalizeAndWrap, common.mustCall());
})();
await common.gcUntil('test finalize and wrap',
() => test_general.derefItemWasCalled());
}
testFinalizeAndWrap();

Просмотреть файл

@ -0,0 +1,8 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const test_globals = require(`./build/${common.buildType}/test_general`);
assert.strictEqual(test_globals.getUndefined(), undefined);
assert.strictEqual(test_globals.getNull(), null);

Просмотреть файл

@ -0,0 +1,95 @@
'use strict';
const common = require('../../common');
const fs = require('fs');
const assert = require('assert');
// Addon is referenced through the eval expression in testFile
const addon = require(`./build/${common.buildType}/test_general`);
const path = require('path');
// This test depends on a number of V8 tests.
const v8TestsDir = path.resolve(__dirname, '..', '..', '..', 'deps', 'v8',
'test', 'mjsunit');
const v8TestsDirExists = fs.existsSync(v8TestsDir);
// The following assert functions are referenced by v8's unit tests
// See for instance deps/v8/test/mjsunit/instanceof.js
// eslint-disable-next-line no-unused-vars
function assertTrue(assertion) {
return assert.strictEqual(assertion, true);
}
// eslint-disable-next-line no-unused-vars
function assertFalse(assertion) {
assert.strictEqual(assertion, false);
}
// eslint-disable-next-line no-unused-vars
function assertEquals(leftHandSide, rightHandSide) {
assert.strictEqual(leftHandSide, rightHandSide);
}
// eslint-disable-next-line no-unused-vars
function assertThrows(statement) {
assert.throws(function() {
eval(statement);
}, Error);
}
function testFile(fileName) {
try {
const contents = fs.readFileSync(fileName, { encoding: 'utf8' });
eval(contents.replace(/[(]([^\s(]+)\s+instanceof\s+([^)]+)[)]/g,
'(addon.doInstanceOf($1, $2))'));
} catch (err) {
// This test depends on V8 test files, which may not exist in downloaded
// archives. Emit a warning if the tests cannot be found instead of failing.
if (err.code === 'ENOENT' && !v8TestsDirExists)
process.emitWarning(`test file ${fileName} does not exist.`);
else
throw err;
}
}
testFile(path.join(v8TestsDir, 'instanceof.js'));
testFile(path.join(v8TestsDir, 'instanceof-2.js'));
// We can only perform this test if we have a working Symbol.hasInstance
if (typeof Symbol !== 'undefined' && 'hasInstance' in Symbol &&
typeof Symbol.hasInstance === 'symbol') {
function compareToNative(theObject, theConstructor) {
assert.strictEqual(
addon.doInstanceOf(theObject, theConstructor),
(theObject instanceof theConstructor)
);
}
function MyClass() {}
Object.defineProperty(MyClass, Symbol.hasInstance, {
value: function(candidate) {
return 'mark' in candidate;
}
});
function MySubClass() {}
MySubClass.prototype = new MyClass();
let x = new MySubClass();
let y = new MySubClass();
x.mark = true;
compareToNative(x, MySubClass);
compareToNative(y, MySubClass);
compareToNative(x, MyClass);
compareToNative(y, MyClass);
x = new MyClass();
y = new MyClass();
x.mark = true;
compareToNative(x, MySubClass);
compareToNative(y, MySubClass);
compareToNative(x, MyClass);
compareToNative(y, MyClass);
}

Просмотреть файл

@ -0,0 +1,14 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
// `addon` is referenced through the eval expression in testFile
const addon = require(`./build/${common.buildType}/test_general`);
const testCase = '(41.92 + 0.08);';
const expected = 42;
const actual = addon.testNapiRun(testCase);
assert.strictEqual(actual, expected);
assert.throws(() => addon.testNapiRun({ abc: 'def' }), /string was expected/);

Просмотреть файл

@ -0,0 +1,8 @@
'use strict';
const common = require('../../common');
const addon = require(`./build/${common.buildType}/test_general`);
const assert = require('assert');
addon.createNapiError();
assert(addon.testNapiErrorCleanup(), 'napi_status cleaned up for second call');

Просмотреть файл

@ -0,0 +1,309 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <js_native_api.h>
#include "../common.h"
static napi_value testStrictEquals(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
bool bool_result;
napi_value result;
NODE_API_CALL(env, napi_strict_equals(env, args[0], args[1], &bool_result));
NODE_API_CALL(env, napi_get_boolean(env, bool_result, &result));
return result;
}
static napi_value testGetPrototype(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_value result;
NODE_API_CALL(env, napi_get_prototype(env, args[0], &result));
return result;
}
static napi_value testGetVersion(napi_env env, napi_callback_info info) {
uint32_t version;
napi_value result;
NODE_API_CALL(env, napi_get_version(env, &version));
NODE_API_CALL(env, napi_create_uint32(env, version, &result));
return result;
}
static napi_value doInstanceOf(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
bool instanceof;
NODE_API_CALL(env, napi_instanceof(env, args[0], args[1], &instanceof));
napi_value result;
NODE_API_CALL(env, napi_get_boolean(env, instanceof, &result));
return result;
}
static napi_value getNull(napi_env env, napi_callback_info info) {
napi_value result;
NODE_API_CALL(env, napi_get_null(env, &result));
return result;
}
static napi_value getUndefined(napi_env env, napi_callback_info info) {
napi_value result;
NODE_API_CALL(env, napi_get_undefined(env, &result));
return result;
}
static napi_value createNapiError(napi_env env, napi_callback_info info) {
napi_value value;
NODE_API_CALL(env, napi_create_string_utf8(env, "xyz", 3, &value));
double double_value;
napi_status status = napi_get_value_double(env, value, &double_value);
NODE_API_ASSERT(env, status != napi_ok, "Failed to produce error condition");
const napi_extended_error_info *error_info = 0;
NODE_API_CALL(env, napi_get_last_error_info(env, &error_info));
NODE_API_ASSERT(env, error_info->error_code == status,
"Last error info code should match last status");
NODE_API_ASSERT(env, error_info->error_message,
"Last error info message should not be null");
return NULL;
}
static napi_value testNapiErrorCleanup(napi_env env, napi_callback_info info) {
const napi_extended_error_info *error_info = 0;
NODE_API_CALL(env, napi_get_last_error_info(env, &error_info));
napi_value result;
bool is_ok = error_info->error_code == napi_ok;
NODE_API_CALL(env, napi_get_boolean(env, is_ok, &result));
return result;
}
static napi_value testNapiTypeof(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
napi_valuetype argument_type;
NODE_API_CALL(env, napi_typeof(env, args[0], &argument_type));
napi_value result = NULL;
if (argument_type == napi_number) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "number", NAPI_AUTO_LENGTH, &result));
} else if (argument_type == napi_string) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "string", NAPI_AUTO_LENGTH, &result));
} else if (argument_type == napi_function) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "function", NAPI_AUTO_LENGTH, &result));
} else if (argument_type == napi_object) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "object", NAPI_AUTO_LENGTH, &result));
} else if (argument_type == napi_boolean) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "boolean", NAPI_AUTO_LENGTH, &result));
} else if (argument_type == napi_undefined) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "undefined", NAPI_AUTO_LENGTH, &result));
} else if (argument_type == napi_symbol) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "symbol", NAPI_AUTO_LENGTH, &result));
} else if (argument_type == napi_null) {
NODE_API_CALL(env, napi_create_string_utf8(
env, "null", NAPI_AUTO_LENGTH, &result));
}
return result;
}
static bool deref_item_called = false;
static void deref_item(napi_env env, void* data, void* hint) {
(void) hint;
NODE_API_ASSERT_RETURN_VOID(env, data == &deref_item_called,
"Finalize callback was called with the correct pointer");
deref_item_called = true;
}
static napi_value deref_item_was_called(napi_env env, napi_callback_info info) {
napi_value it_was_called;
NODE_API_CALL(env, napi_get_boolean(env, deref_item_called, &it_was_called));
return it_was_called;
}
static napi_value wrap_first_arg(napi_env env,
napi_callback_info info,
napi_finalize finalizer,
void* data) {
size_t argc = 1;
napi_value to_wrap;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &to_wrap, NULL, NULL));
NODE_API_CALL(env, napi_wrap(env, to_wrap, data, finalizer, NULL, NULL));
return to_wrap;
}
static napi_value wrap(napi_env env, napi_callback_info info) {
deref_item_called = false;
return wrap_first_arg(env, info, deref_item, &deref_item_called);
}
static napi_value unwrap(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value wrapped;
void* data;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &wrapped, NULL, NULL));
NODE_API_CALL(env, napi_unwrap(env, wrapped, &data));
return NULL;
}
static napi_value remove_wrap(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value wrapped;
void* data;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &wrapped, NULL, NULL));
NODE_API_CALL(env, napi_remove_wrap(env, wrapped, &data));
return NULL;
}
static bool finalize_called = false;
static void test_finalize(napi_env env, void* data, void* hint) {
finalize_called = true;
}
static napi_value test_finalize_wrap(napi_env env, napi_callback_info info) {
return wrap_first_arg(env, info, test_finalize, NULL);
}
static napi_value finalize_was_called(napi_env env, napi_callback_info info) {
napi_value it_was_called;
NODE_API_CALL(env, napi_get_boolean(env, finalize_called, &it_was_called));
return it_was_called;
}
static napi_value testAdjustExternalMemory(napi_env env, napi_callback_info info) {
napi_value result;
int64_t adjustedValue;
NODE_API_CALL(env, napi_adjust_external_memory(env, 1, &adjustedValue));
NODE_API_CALL(env, napi_create_double(env, (double)adjustedValue, &result));
return result;
}
static napi_value testNapiRun(napi_env env, napi_callback_info info) {
napi_value script, result;
size_t argc = 1;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &script, NULL, NULL));
NODE_API_CALL(env, napi_run_script(env, script, &result));
return result;
}
static void finalizer_only_callback(napi_env env, void* data, void* hint) {
napi_ref js_cb_ref = (napi_ref)data;
napi_value js_cb, undefined;
NODE_API_CALL_RETURN_VOID(env, napi_get_reference_value(env, js_cb_ref, &js_cb));
NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
NODE_API_CALL_RETURN_VOID(env,
napi_call_function(env, undefined, js_cb, 0, NULL, NULL));
NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, js_cb_ref));
}
static napi_value add_finalizer_only(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value argv[2];
napi_ref js_cb_ref;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
NODE_API_CALL(env, napi_create_reference(env, argv[1], 1, &js_cb_ref));
NODE_API_CALL(env,
napi_add_finalizer(
env, argv[0], js_cb_ref, finalizer_only_callback, NULL, NULL));
return NULL;
}
static const char* env_cleanup_finalizer_messages[] = {
"simple wrap",
"wrap, removeWrap",
"first wrap",
"second wrap"
};
static void cleanup_env_finalizer(napi_env env, void* data, void* hint) {
(void) env;
(void) hint;
printf("finalize at env cleanup for %s\n",
env_cleanup_finalizer_messages[(uintptr_t)data]);
}
static napi_value env_cleanup_wrap(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value argv[2];
uint32_t value;
uintptr_t ptr_value;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
NODE_API_CALL(env, napi_get_value_uint32(env, argv[1], &value));
ptr_value = value;
return wrap_first_arg(env, info, cleanup_env_finalizer, (void*)ptr_value);
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("testStrictEquals", testStrictEquals),
DECLARE_NODE_API_PROPERTY("testGetPrototype", testGetPrototype),
DECLARE_NODE_API_PROPERTY("testGetVersion", testGetVersion),
DECLARE_NODE_API_PROPERTY("testNapiRun", testNapiRun),
DECLARE_NODE_API_PROPERTY("doInstanceOf", doInstanceOf),
DECLARE_NODE_API_PROPERTY("getUndefined", getUndefined),
DECLARE_NODE_API_PROPERTY("getNull", getNull),
DECLARE_NODE_API_PROPERTY("createNapiError", createNapiError),
DECLARE_NODE_API_PROPERTY("testNapiErrorCleanup", testNapiErrorCleanup),
DECLARE_NODE_API_PROPERTY("testNapiTypeof", testNapiTypeof),
DECLARE_NODE_API_PROPERTY("wrap", wrap),
DECLARE_NODE_API_PROPERTY("envCleanupWrap", env_cleanup_wrap),
DECLARE_NODE_API_PROPERTY("unwrap", unwrap),
DECLARE_NODE_API_PROPERTY("removeWrap", remove_wrap),
DECLARE_NODE_API_PROPERTY("addFinalizerOnly", add_finalizer_only),
DECLARE_NODE_API_PROPERTY("testFinalizeWrap", test_finalize_wrap),
DECLARE_NODE_API_PROPERTY("finalizeWasCalled", finalize_was_called),
DECLARE_NODE_API_PROPERTY("derefItemWasCalled", deref_item_was_called),
DECLARE_NODE_API_PROPERTY("testAdjustExternalMemory", testAdjustExternalMemory)
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "test_handle_scope",
"sources": [
"../entry_point.c",
"test_handle_scope.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,19 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
// Testing handle scope api calls
const testHandleScope =
require(`./build/${common.buildType}/test_handle_scope`);
testHandleScope.NewScope();
assert.ok(testHandleScope.NewScopeEscape() instanceof Object);
testHandleScope.NewScopeEscapeTwice();
assert.throws(
() => {
testHandleScope.NewScopeWithException(() => { throw new RangeError(); });
},
RangeError);

Просмотреть файл

@ -0,0 +1,85 @@
#include <js_native_api.h>
#include "../common.h"
#include <string.h>
// these tests validate the handle scope functions in the normal
// flow. Forcing gc behavior to fully validate they are doing
// the right right thing would be quite hard so we keep it
// simple for now.
static napi_value NewScope(napi_env env, napi_callback_info info) {
napi_handle_scope scope;
napi_value output = NULL;
NODE_API_CALL(env, napi_open_handle_scope(env, &scope));
NODE_API_CALL(env, napi_create_object(env, &output));
NODE_API_CALL(env, napi_close_handle_scope(env, scope));
return NULL;
}
static napi_value NewScopeEscape(napi_env env, napi_callback_info info) {
napi_escapable_handle_scope scope;
napi_value output = NULL;
napi_value escapee = NULL;
NODE_API_CALL(env, napi_open_escapable_handle_scope(env, &scope));
NODE_API_CALL(env, napi_create_object(env, &output));
NODE_API_CALL(env, napi_escape_handle(env, scope, output, &escapee));
NODE_API_CALL(env, napi_close_escapable_handle_scope(env, scope));
return escapee;
}
static napi_value NewScopeEscapeTwice(napi_env env, napi_callback_info info) {
napi_escapable_handle_scope scope;
napi_value output = NULL;
napi_value escapee = NULL;
napi_status status;
NODE_API_CALL(env, napi_open_escapable_handle_scope(env, &scope));
NODE_API_CALL(env, napi_create_object(env, &output));
NODE_API_CALL(env, napi_escape_handle(env, scope, output, &escapee));
status = napi_escape_handle(env, scope, output, &escapee);
NODE_API_ASSERT(env, status == napi_escape_called_twice, "Escaping twice fails");
NODE_API_CALL(env, napi_close_escapable_handle_scope(env, scope));
return NULL;
}
static napi_value NewScopeWithException(napi_env env, napi_callback_info info) {
napi_handle_scope scope;
size_t argc;
napi_value exception_function;
napi_status status;
napi_value output = NULL;
NODE_API_CALL(env, napi_open_handle_scope(env, &scope));
NODE_API_CALL(env, napi_create_object(env, &output));
argc = 1;
NODE_API_CALL(env, napi_get_cb_info(
env, info, &argc, &exception_function, NULL, NULL));
status = napi_call_function(
env, output, exception_function, 0, NULL, NULL);
NODE_API_ASSERT(env, status == napi_pending_exception,
"Function should have thrown.");
// Closing a handle scope should still work while an exception is pending.
NODE_API_CALL(env, napi_close_handle_scope(env, scope));
return NULL;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor properties[] = {
DECLARE_NODE_API_PROPERTY("NewScope", NewScope),
DECLARE_NODE_API_PROPERTY("NewScopeEscape", NewScopeEscape),
DECLARE_NODE_API_PROPERTY("NewScopeEscapeTwice", NewScopeEscapeTwice),
DECLARE_NODE_API_PROPERTY("NewScopeWithException", NewScopeWithException),
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(properties) / sizeof(*properties), properties));
return exports;
}
EXTERN_C_END

Просмотреть файл

@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "test_instance_data",
"sources": [
"../entry_point.c",
"test_instance_data.c"
]
}
]
}

Просмотреть файл

@ -0,0 +1,40 @@
'use strict';
// Test API calls for instance data.
const common = require('../../common');
const assert = require('assert');
if (module !== require.main) {
// When required as a module, run the tests.
const test_instance_data =
require(`./build/${common.buildType}/test_instance_data`);
// Print to stdout when the environment deletes the instance data. This output
// is checked by the parent process.
test_instance_data.setPrintOnDelete();
// Test that instance data can be accessed from a binding.
assert.strictEqual(test_instance_data.increment(), 42);
// Test that the instance data can be accessed from a finalizer.
test_instance_data.objectWithFinalizer(common.mustCall());
global.gc();
} else {
// When launched as a script, run tests in either a child process or in a
// worker thread.
const requireAs = require('../../common/require-as');
const runOptions = { stdio: ['inherit', 'pipe', 'inherit'] };
function checkOutput(child) {
assert.strictEqual(child.status, 0);
assert.strictEqual(
(child.stdout.toString().split(/\r\n?|\n/) || [])[0],
'deleting addon data');
}
// Run tests in a child process.
checkOutput(requireAs(__filename, ['--expose-gc'], runOptions, 'child'));
// Run tests in a worker thread in a child process.
checkOutput(requireAs(__filename, ['--expose-gc'], runOptions, 'worker'));
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше