diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8dd350ea6e37a3..dcd06779f80ba5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,7 +27,8 @@ release.
-8.15.1
+8.16.0
+8.15.1
8.15.0
8.14.1
8.14.0
diff --git a/common.gypi b/common.gypi
index fd42f27d351282..b423c9a61a099e 100644
--- a/common.gypi
+++ b/common.gypi
@@ -88,6 +88,19 @@
['OS=="aix"', {
'cflags': [ '-gxcoff' ],
'ldflags': [ '-Wl,-bbigtoc' ],
+ 'conditions': [
+ ['target_arch=="ppc64"', {
+ 'ldflags': [
+ '-Wl,-blibpath:/usr/lib:/lib:'
+ '/opt/freeware/lib/pthread/ppc64'
+ ],
+ }],
+ ['target_arch=="ppc"', {
+ 'ldflags': [
+ '-Wl,-blibpath:/usr/lib:/lib:/opt/freeware/lib/pthread'
+ ],
+ }],
+ ],
}],
['OS == "android"', {
'cflags': [ '-fPIE' ],
@@ -337,11 +350,18 @@
[ 'OS=="aix"', {
'conditions': [
[ 'target_arch=="ppc"', {
- 'ldflags': [ '-Wl,-bmaxdata:0x60000000/dsa' ],
+ 'ldflags': [
+ '-Wl,-bmaxdata:0x60000000/dsa',
+ '-Wl,-blibpath:/usr/lib:/lib:/opt/freeware/lib/pthread',
+ ],
}],
[ 'target_arch=="ppc64"', {
'cflags': [ '-maix64' ],
- 'ldflags': [ '-maix64' ],
+ 'ldflags': [
+ '-maix64',
+ '-Wl,-blibpath:/usr/lib:/lib:'
+ '/opt/freeware/lib/pthread/ppc64',
+ ],
}],
],
'ldflags': [ '-Wl,-bbigtoc' ],
diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h
index 62c81f2ca057ad..db93e49041c665 100644
--- a/deps/v8/include/v8-version.h
+++ b/deps/v8/include/v8-version.h
@@ -11,7 +11,7 @@
#define V8_MAJOR_VERSION 6
#define V8_MINOR_VERSION 2
#define V8_BUILD_NUMBER 414
-#define V8_PATCH_LEVEL 75
+#define V8_PATCH_LEVEL 77
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
diff --git a/deps/v8/src/profiler/cpu-profiler.cc b/deps/v8/src/profiler/cpu-profiler.cc
index 69021cee344eb2..17c3e53d3d0455 100644
--- a/deps/v8/src/profiler/cpu-profiler.cc
+++ b/deps/v8/src/profiler/cpu-profiler.cc
@@ -326,8 +326,11 @@ void CpuProfiler::StartProcessorIfNotStarted() {
// Disable logging when using the new implementation.
saved_is_logging_ = logger->is_logging_;
logger->is_logging_ = false;
+
+ bool codemap_needs_initialization = false;
if (!generator_) {
generator_.reset(new ProfileGenerator(profiles_.get()));
+ codemap_needs_initialization = true;
CreateEntriesForRuntimeCallStats();
}
processor_.reset(new ProfilerEventsProcessor(isolate_, generator_.get(),
@@ -341,12 +344,14 @@ void CpuProfiler::StartProcessorIfNotStarted() {
isolate_->set_is_profiling(true);
// Enumerate stuff we already have in the heap.
DCHECK(isolate_->heap()->HasBeenSetUp());
- if (!FLAG_prof_browser_mode) {
- logger->LogCodeObjects();
+ if (codemap_needs_initialization) {
+ if (!FLAG_prof_browser_mode) {
+ logger->LogCodeObjects();
+ }
+ logger->LogCompiledFunctions();
+ logger->LogAccessorCallbacks();
+ LogBuiltins();
}
- logger->LogCompiledFunctions();
- logger->LogAccessorCallbacks();
- LogBuiltins();
// Enable stack sampling.
processor_->AddCurrentStack(isolate_);
processor_->StartSynchronously();
diff --git a/deps/v8/src/regexp/ppc/regexp-macro-assembler-ppc.cc b/deps/v8/src/regexp/ppc/regexp-macro-assembler-ppc.cc
index a1425b4372ec39..260e98bcc2577f 100644
--- a/deps/v8/src/regexp/ppc/regexp-macro-assembler-ppc.cc
+++ b/deps/v8/src/regexp/ppc/regexp-macro-assembler-ppc.cc
@@ -142,8 +142,13 @@ int RegExpMacroAssemblerPPC::stack_limit_slack() {
void RegExpMacroAssemblerPPC::AdvanceCurrentPosition(int by) {
if (by != 0) {
- __ addi(current_input_offset(), current_input_offset(),
- Operand(by * char_size()));
+ if (is_int16(by * char_size())) {
+ __ addi(current_input_offset(), current_input_offset(),
+ Operand(by * char_size()));
+ } else {
+ __ mov(r0, Operand(by * char_size()));
+ __ add(current_input_offset(), r0, current_input_offset());
+ }
}
}
@@ -1270,7 +1275,12 @@ void RegExpMacroAssemblerPPC::LoadCurrentCharacterUnchecked(int cp_offset,
Register offset = current_input_offset();
if (cp_offset != 0) {
// r25 is not being used to store the capture start index at this point.
- __ addi(r25, current_input_offset(), Operand(cp_offset * char_size()));
+ if (is_int16(cp_offset * char_size())) {
+ __ addi(r25, current_input_offset(), Operand(cp_offset * char_size()));
+ } else {
+ __ mov(r25, Operand(cp_offset * char_size()));
+ __ add(r25, r25, current_input_offset());
+ }
offset = r25;
}
// The lwz, stw, lhz, sth instructions can do unaligned accesses, if the CPU
diff --git a/doc/api/errors.md b/doc/api/errors.md
index 53ef8395ac7065..a9319158387a67 100755
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -1107,6 +1107,31 @@ multiple of the element size.
While calling `napi_create_typedarray()`, `(length * size_of_element) +
byte_offset` was larger than the length of given `buffer`.
+
+### ERR_NAPI_TSFN_CALL_JS
+
+An error occurred while invoking the JavaScript portion of the thread-safe
+function.
+
+
+### ERR_NAPI_TSFN_GET_UNDEFINED
+
+An error occurred while attempting to retrieve the JavaScript `undefined`
+value.
+
+
+### ERR_NAPI_TSFN_START_IDLE_LOOP
+
+On the main thread, values are removed from the queue associated with the
+thread-safe function in an idle loop. This error indicates that an error
+has occurred when attemping to start the loop.
+
+
+### ERR_NAPI_TSFN_STOP_IDLE_LOOP
+
+Once no more items are left in the queue, the idle loop must be suspended. This
+error indicates that the idle loop has failed to stop.
+
### ERR_NO_ICU
@@ -1147,12 +1172,32 @@ A call was made and the UDP subsystem was not running.
### ERR_STDERR_CLOSE
+
+
An attempt was made to close the `process.stderr` stream. By design, Node.js
does not allow `stdout` or `stderr` streams to be closed by user code.
### ERR_STDOUT_CLOSE
+
+
An attempt was made to close the `process.stdout` stream. By design, Node.js
does not allow `stdout` or `stderr` streams to be closed by user code.
diff --git a/doc/api/http.md b/doc/api/http.md
index 0ad9f8a2a45ff8..8af25b4e02efb2 100644
--- a/doc/api/http.md
+++ b/doc/api/http.md
@@ -625,8 +625,9 @@ added: v0.5.9
* `timeout` {number} Milliseconds before a request times out.
* `callback` {Function} Optional function to be called when a timeout occurs. Same as binding to the `timeout` event.
-Once a socket is assigned to this request and is connected
-[`socket.setTimeout()`][] will be called.
+If no socket is assigned to this request then [`socket.setTimeout()`][] will be
+called immediately. Otherwise [`socket.setTimeout()`][] will be called after the
+assigned socket is connected.
Returns `request`.
diff --git a/doc/api/n-api.md b/doc/api/n-api.md
index 1e43e4f224d9f0..cb322345d534f0 100644
--- a/doc/api/n-api.md
+++ b/doc/api/n-api.md
@@ -57,6 +57,8 @@ for the N-API C based functions exported by Node.js. These wrappers are not
part of N-API, nor will they be maintained as part of Node.js. One such
example is: [node-addon-api](/~https://github.com/nodejs/node-addon-api).
+## Usage
+
In order to use the N-API functions, include the file
[node_api.h](/~https://github.com/nodejs/node/blob/master/src/node_api.h)
which is located in the src directory in the node development tree.
@@ -65,6 +67,41 @@ For example:
#include
```
+This will opt into the default `NAPI_VERSION` for the given release of Node.js.
+In order to ensure compatibility with specific versions of N-API, the version
+can be specified explicitly when including the header:
+
+```C
+#define NAPI_VERSION 3
+#include
+```
+
+This restricts the N-API surface to just the functionality that was available in
+the specified (and earlier) versions.
+
+Some of the N-API surface is considered experimental and requires explicit
+opt-in to access those APIs:
+
+```C
+#define NAPI_EXPERIMENTAL
+#include
+```
+
+In this case the entire API surface, including any experimental APIs, will be
+available to the module code.
+
+## N-API Version Matrix
+
+| | 1 | 2 | 3 |
+|:-----:|:-------:|:--------:|:--------:|
+| v4.x | | | |
+| v6.x | | | v6.14.2* |
+| v8.x | v8.0.0* | v8.10.0* | |
+| v9.x | v9.0.0* | v9.3.0* | v9.11.0* |
+| v10.x | | | v10.0.0 |
+
+\* Indicates that the N-API version was released as experimental
+
## Basic N-API Data Types
N-API exposes the following fundamental datatypes as abstractions that are
@@ -90,7 +127,11 @@ typedef enum {
napi_cancelled,
napi_escape_called_twice,
napi_handle_scope_mismatch,
- napi_callback_scope_mismatch
+ napi_callback_scope_mismatch,
+#ifdef NAPI_EXPERIMENTAL
+ napi_queue_full,
+ napi_closing,
+#endif // NAPI_EXPERIMENTAL
} napi_status;
```
If additional information is required upon an API returning a failed status,
@@ -128,6 +169,43 @@ not allowed.
### napi_value
This is an opaque pointer that is used to represent a JavaScript value.
+### napi_threadsafe_function
+
+> Stability: 2 - Stable
+
+This is an opaque pointer that represents a JavaScript function which can be
+called asynchronously from multiple threads via
+`napi_call_threadsafe_function()`.
+
+### napi_threadsafe_function_release_mode
+
+> Stability: 2 - Stable
+
+A value to be given to `napi_release_threadsafe_function()` to indicate whether
+the thread-safe function is to be closed immediately (`napi_tsfn_abort`) or
+merely released (`napi_tsfn_release`) and thus available for subsequent use via
+`napi_acquire_threadsafe_function()` and `napi_call_threadsafe_function()`.
+```C
+typedef enum {
+ napi_tsfn_release,
+ napi_tsfn_abort
+} napi_threadsafe_function_release_mode;
+```
+
+### napi_threadsafe_function_call_mode
+
+> Stability: 2 - Stable
+
+A value to be given to `napi_call_threadsafe_function()` to indicate whether
+the call should block whenever the queue associated with the thread-safe
+function is full.
+```C
+typedef enum {
+ napi_tsfn_nonblocking,
+ napi_tsfn_blocking
+} napi_threadsafe_function_call_mode;
+```
+
### N-API Memory Management types
#### napi_handle_scope
This is an abstraction used to control and modify the lifetime of objects
@@ -205,6 +283,43 @@ typedef void (*napi_async_complete_callback)(napi_env env,
void* data);
```
+#### napi_threadsafe_function_call_js
+
+> Stability: 2 - Stable
+
+Function pointer used with asynchronous thread-safe function calls. The callback
+will be called on the main thread. Its purpose is to use a data item arriving
+via the queue from one of the secondary threads to construct the parameters
+necessary for a call into JavaScript, usually via `napi_call_function`, and then
+make the call into JavaScript.
+
+The data arriving from the secondary thread via the queue is given in the `data`
+parameter and the JavaScript function to call is given in the `js_callback`
+parameter.
+
+N-API sets up the environment prior to calling this callback, so it is
+sufficient to call the JavaScript function via `napi_call_function` rather than
+via `napi_make_callback`.
+
+Callback functions must satisfy the following signature:
+```C
+typedef void (*napi_threadsafe_function_call_js)(napi_env env,
+ napi_value js_callback,
+ void* context,
+ void* data);
+```
+- `[in] env`: The environment to use for API calls, or `NULL` if the thread-safe
+function is being torn down and `data` may need to be freed.
+- `[in] js_callback`: The JavaScript function to call, or `NULL` if the
+thread-safe function is being torn down and `data` may need to be freed.
+- `[in] context`: The optional data with which the thread-safe function was
+created.
+- `[in] data`: Data created by the secondary thread. It is the responsibility of
+the callback to convert this native data to JavaScript values (with N-API
+functions) that can be passed as parameters when `js_callback` is invoked. This
+pointer is managed entirely by the threads and this callback. Thus this callback
+should free the data.
+
## Error Handling
N-API uses both return values and JavaScript exceptions for error handling.
The following sections explain the approach for each case.
@@ -259,6 +374,7 @@ It is intended only for logging purposes.
#### napi_get_last_error_info
```C
napi_status
@@ -358,6 +474,7 @@ TypeError [ERR_ERROR_1]
#### napi_throw
```C
NODE_EXTERN napi_status napi_throw(napi_env env, napi_value error);
@@ -373,6 +490,7 @@ This API throws the JavaScript Error provided.
#### napi_throw_error
```C
NODE_EXTERN napi_status napi_throw_error(napi_env env,
@@ -391,6 +509,7 @@ This API throws a JavaScript Error with the text provided.
#### napi_throw_type_error
```C
NODE_EXTERN napi_status napi_throw_type_error(napi_env env,
@@ -409,6 +528,7 @@ This API throws a JavaScript TypeError with the text provided.
#### napi_throw_range_error
```C
NODE_EXTERN napi_status napi_throw_range_error(napi_env env,
@@ -428,6 +548,7 @@ This API throws a JavaScript RangeError with the text provided.
#### napi_is_error
```C
NODE_EXTERN napi_status napi_is_error(napi_env env,
@@ -447,6 +568,7 @@ This API queries a `napi_value` to check if it represents an error object.
#### napi_create_error
```C
NODE_EXTERN napi_status napi_create_error(napi_env env,
@@ -468,6 +590,7 @@ This API returns a JavaScript Error with the text provided.
#### napi_create_type_error
```C
NODE_EXTERN napi_status napi_create_type_error(napi_env env,
@@ -490,6 +613,7 @@ This API returns a JavaScript TypeError with the text provided.
#### napi_create_range_error
```C
NODE_EXTERN napi_status napi_create_range_error(napi_env env,
@@ -511,6 +635,7 @@ This API returns a JavaScript RangeError with the text provided.
#### napi_get_and_clear_last_exception
```C
napi_status napi_get_and_clear_last_exception(napi_env env,
@@ -529,6 +654,7 @@ This API can be called even if there is a pending JavaScript exception.
#### napi_is_exception_pending
```C
napi_status napi_is_exception_pending(napi_env env, bool* result);
@@ -546,7 +672,9 @@ This API can be called even if there is a pending JavaScript exception.
#### napi_fatal_exception
+
```C
napi_status napi_fatal_exception(napi_env env, napi_value err);
```
@@ -565,6 +693,7 @@ thrown to immediately terminate the process.
#### napi_fatal_error
```C
NAPI_NO_RETURN void napi_fatal_error(const char* location,
@@ -677,6 +806,7 @@ can only be called once.
#### napi_open_handle_scope
```C
NODE_EXTERN napi_status napi_open_handle_scope(napi_env env,
@@ -692,6 +822,7 @@ This API open a new scope.
#### napi_close_handle_scope
```C
NODE_EXTERN napi_status napi_close_handle_scope(napi_env env,
@@ -710,6 +841,7 @@ This API can be called even if there is a pending JavaScript exception.
#### napi_open_escapable_handle_scope
```C
NODE_EXTERN napi_status
@@ -727,6 +859,7 @@ to the outer scope.
#### napi_close_escapable_handle_scope
```C
NODE_EXTERN napi_status
@@ -746,6 +879,7 @@ This API can be called even if there is a pending JavaScript exception.
#### napi_escape_handle
```C
napi_status napi_escape_handle(napi_env env,
@@ -810,6 +944,7 @@ individual count.
#### napi_create_reference
```C
NODE_EXTERN napi_status napi_create_reference(napi_env env,
@@ -832,6 +967,7 @@ to the Object passed in.
#### napi_delete_reference
```C
NODE_EXTERN napi_status napi_delete_reference(napi_env env, napi_ref ref);
@@ -849,6 +985,7 @@ This API can be called even if there is a pending JavaScript exception.
#### napi_reference_ref
```C
NODE_EXTERN napi_status napi_reference_ref(napi_env env,
@@ -867,6 +1004,7 @@ passed in and returns the resulting reference count.
#### napi_reference_unref
```C
NODE_EXTERN napi_status napi_reference_unref(napi_env env,
@@ -885,6 +1023,7 @@ passed in and returns the resulting reference count.
#### napi_get_reference_value
```C
NODE_EXTERN napi_status napi_get_reference_value(napi_env env,
@@ -918,7 +1057,9 @@ should be freed up.
#### napi_add_env_cleanup_hook
+
```C
NODE_EXTERN napi_status napi_add_env_cleanup_hook(napi_env env,
void (*fun)(void* arg),
@@ -943,7 +1084,9 @@ is being torn down anyway.
#### napi_remove_env_cleanup_hook
+
```C
NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(napi_env env,
void (*fun)(void* arg),
@@ -1105,6 +1248,7 @@ of the [ECMAScript Language Specification][].
#### napi_create_array
```C
napi_status napi_create_array(napi_env env, napi_value* result)
@@ -1123,6 +1267,7 @@ ECMAScript Language Specification.
#### napi_create_array_with_length
```C
napi_status napi_create_array_with_length(napi_env env,
@@ -1152,6 +1297,7 @@ ECMAScript Language Specification.
#### napi_create_arraybuffer
```C
napi_status napi_create_arraybuffer(napi_env env,
@@ -1184,6 +1330,7 @@ of the ECMAScript Language Specification.
#### napi_create_buffer
```C
napi_status napi_create_buffer(napi_env env,
@@ -1205,6 +1352,7 @@ fully-supported data structure, in most cases using a TypedArray will suffice.
#### napi_create_buffer_copy
```C
napi_status napi_create_buffer_copy(napi_env env,
@@ -1230,6 +1378,7 @@ structure, in most cases using a TypedArray will suffice.
#### napi_create_external
```C
napi_status napi_create_external(napi_env env,
@@ -1262,6 +1411,7 @@ additional properties. It is considered a distinct value type: calling
#### napi_create_external_arraybuffer
```C
napi_status
@@ -1297,6 +1447,7 @@ of the ECMAScript Language Specification.
#### napi_create_external_buffer
```C
napi_status napi_create_external_buffer(napi_env env,
@@ -1328,6 +1479,7 @@ structure, in most cases using a TypedArray will suffice.
#### napi_create_function
```C
napi_status napi_create_function(napi_env env,
@@ -1361,6 +1513,7 @@ of the ECMAScript Language Specification.
#### napi_create_object
```C
napi_status napi_create_object(napi_env env, napi_value* result)
@@ -1381,6 +1534,7 @@ ECMAScript Language Specification.
#### napi_create_symbol
```C
napi_status napi_create_symbol(napi_env env,
@@ -1404,6 +1558,7 @@ of the ECMAScript Language Specification.
#### napi_create_typedarray
```C
napi_status napi_create_typedarray(napi_env env,
@@ -1440,6 +1595,7 @@ of the ECMAScript Language Specification.
#### napi_create_dataview
```C
@@ -1474,6 +1630,7 @@ JavaScript DataView Objects are described in
#### napi_create_int32
```C
napi_status napi_create_int32(napi_env env, int32_t value, napi_value* result)
@@ -1495,6 +1652,7 @@ of the ECMAScript Language Specification.
#### napi_create_uint32
```C
napi_status napi_create_uint32(napi_env env, uint32_t value, napi_value* result)
@@ -1516,6 +1674,7 @@ of the ECMAScript Language Specification.
#### napi_create_int64
```C
napi_status napi_create_int64(napi_env env, int64_t value, napi_value* result)
@@ -1543,6 +1702,7 @@ outside the range of
#### napi_create_double
```C
napi_status napi_create_double(napi_env env, double value, napi_value* result)
@@ -1564,6 +1724,7 @@ of the ECMAScript Language Specification.
#### napi_create_string_latin1
```C
napi_status napi_create_string_latin1(napi_env env,
@@ -1589,6 +1750,7 @@ of the ECMAScript Language Specification.
#### napi_create_string_utf16
```C
napi_status napi_create_string_utf16(napi_env env,
@@ -1614,6 +1776,7 @@ of the ECMAScript Language Specification.
#### napi_create_string_utf8
```C
napi_status napi_create_string_utf8(napi_env env,
@@ -1640,6 +1803,7 @@ of the ECMAScript Language Specification.
#### napi_get_array_length
```C
napi_status napi_get_array_length(napi_env env,
@@ -1663,6 +1827,7 @@ of the ECMAScript Language Specification.
#### napi_get_arraybuffer_info
```C
napi_status napi_get_arraybuffer_info(napi_env env,
@@ -1691,6 +1856,7 @@ callback as long as there are no calls to other APIs that might trigger a GC.
#### napi_get_buffer_info
```C
napi_status napi_get_buffer_info(napi_env env,
@@ -1715,6 +1881,7 @@ lifetime is not guaranteed if it's managed by the VM.
#### napi_get_prototype
```C
napi_status napi_get_prototype(napi_env env,
@@ -1733,6 +1900,7 @@ Returns `napi_ok` if the API succeeded.
#### napi_get_typedarray_info
```C
napi_status napi_get_typedarray_info(napi_env env,
@@ -1763,6 +1931,7 @@ is managed by the VM
#### napi_get_dataview_info
```C
@@ -1790,6 +1959,7 @@ This API returns various properties of a DataView.
#### napi_get_value_bool
```C
napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result)
@@ -1809,6 +1979,7 @@ Boolean.
#### napi_get_value_double
```C
napi_status napi_get_value_double(napi_env env,
@@ -1830,6 +2001,7 @@ Number.
#### napi_get_value_external
```C
napi_status napi_get_value_external(napi_env env,
@@ -1850,6 +2022,7 @@ This API retrieves the external data pointer that was previously passed to
#### napi_get_value_int32
```C
napi_status napi_get_value_int32(napi_env env,
@@ -1877,6 +2050,7 @@ result to zero.
#### napi_get_value_int64
```C
napi_status napi_get_value_int64(napi_env env,
@@ -1906,6 +2080,7 @@ result to zero.
#### napi_get_value_string_latin1
```C
napi_status napi_get_value_string_latin1(napi_env env,
@@ -1933,6 +2108,7 @@ in.
#### napi_get_value_string_utf8
```C
napi_status napi_get_value_string_utf8(napi_env env,
@@ -1959,6 +2135,7 @@ This API returns the UTF8-encoded string corresponding the value passed in.
#### napi_get_value_string_utf16
```C
napi_status napi_get_value_string_utf16(napi_env env,
@@ -1985,6 +2162,7 @@ This API returns the UTF16-encoded string corresponding the value passed in.
#### napi_get_value_uint32
```C
napi_status napi_get_value_uint32(napi_env env,
@@ -2007,6 +2185,7 @@ This API returns the C primitive equivalent of the given `napi_value` as a
#### napi_get_boolean
```C
napi_status napi_get_boolean(napi_env env, bool value, napi_value* result)
@@ -2025,6 +2204,7 @@ represent the given boolean value
#### napi_get_global
```C
napi_status napi_get_global(napi_env env, napi_value* result)
@@ -2040,6 +2220,7 @@ This API returns the global Object.
#### napi_get_null
```C
napi_status napi_get_null(napi_env env, napi_value* result)
@@ -2055,6 +2236,7 @@ This API returns the null Object.
#### napi_get_undefined
```C
napi_status napi_get_undefined(napi_env env, napi_value* result)
@@ -2083,6 +2265,7 @@ These APIs support doing one of the following:
### napi_coerce_to_bool
```C
napi_status napi_coerce_to_bool(napi_env env,
@@ -2104,6 +2287,7 @@ This API can be re-entrant if getters are defined on the passed-in Object.
### napi_coerce_to_number
```C
napi_status napi_coerce_to_number(napi_env env,
@@ -2125,6 +2309,7 @@ This API can be re-entrant if getters are defined on the passed-in Object.
### napi_coerce_to_object
```C
napi_status napi_coerce_to_object(napi_env env,
@@ -2146,6 +2331,7 @@ This API can be re-entrant if getters are defined on the passed-in Object.
### napi_coerce_to_string
```C
napi_status napi_coerce_to_string(napi_env env,
@@ -2167,6 +2353,7 @@ This API can be re-entrant if getters are defined on the passed-in Object.
### napi_typeof
```C
napi_status napi_typeof(napi_env env, napi_value value, napi_valuetype* result)
@@ -2188,6 +2375,7 @@ If `value` has a type that is invalid, an error is returned.
### napi_instanceof
```C
napi_status napi_instanceof(napi_env env,
@@ -2213,6 +2401,7 @@ of the ECMAScript Language Specification.
### napi_is_array
```C
napi_status napi_is_array(napi_env env, napi_value value, bool* result)
@@ -2231,6 +2420,7 @@ of the ECMAScript Language Specification.
### napi_is_arraybuffer
```C
napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result)
@@ -2247,6 +2437,7 @@ This API checks if the Object passed in is an array buffer.
### napi_is_buffer
```C
napi_status napi_is_buffer(napi_env env, napi_value value, bool* result)
@@ -2264,6 +2455,7 @@ This API checks if the Object passed in is a buffer.
### napi_is_error
```C
napi_status napi_is_error(napi_env env, napi_value value, bool* result)
@@ -2280,6 +2472,7 @@ This API checks if the Object passed in is an Error.
### napi_is_typedarray
```C
napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result)
@@ -2296,6 +2489,7 @@ This API checks if the Object passed in is a typed array.
### napi_is_dataview
```C
@@ -2313,6 +2507,7 @@ This API checks if the Object passed in is a DataView.
### napi_strict_equals
```C
napi_status napi_strict_equals(napi_env env,
@@ -2547,6 +2742,7 @@ this function is invoked.
#### napi_get_property_names
```C
napi_status napi_get_property_names(napi_env env,
@@ -2568,6 +2764,7 @@ This API returns the array of properties for the Object passed in
#### napi_set_property
```C
napi_status napi_set_property(napi_env env,
@@ -2588,6 +2785,7 @@ This API set a property on the Object passed in.
#### napi_get_property
```C
napi_status napi_get_property(napi_env env,
@@ -2609,6 +2807,7 @@ This API gets the requested property from the Object passed in.
#### napi_has_property
```C
napi_status napi_has_property(napi_env env,
@@ -2630,6 +2829,7 @@ This API checks if the Object passed in has the named property.
#### napi_delete_property
```C
napi_status napi_delete_property(napi_env env,
@@ -2652,6 +2852,7 @@ This API attempts to delete the `key` own property from `object`.
#### napi_has_own_property
```C
napi_status napi_has_own_property(napi_env env,
@@ -2675,6 +2876,7 @@ conversion between data types.
#### napi_set_named_property
```C
napi_status napi_set_named_property(napi_env env,
@@ -2696,6 +2898,7 @@ created from the string passed in as `utf8Name`
#### napi_get_named_property
```C
napi_status napi_get_named_property(napi_env env,
@@ -2717,6 +2920,7 @@ created from the string passed in as `utf8Name`
#### napi_has_named_property
```C
napi_status napi_has_named_property(napi_env env,
@@ -2738,6 +2942,7 @@ created from the string passed in as `utf8Name`
#### napi_set_element
```C
napi_status napi_set_element(napi_env env,
@@ -2758,6 +2963,7 @@ This API sets and element on the Object passed in.
#### napi_get_element
```C
napi_status napi_get_element(napi_env env,
@@ -2778,6 +2984,7 @@ This API gets the element at the requested index.
#### napi_has_element
```C
napi_status napi_has_element(napi_env env,
@@ -2799,6 +3006,7 @@ requested index.
#### napi_delete_element
```C
napi_status napi_delete_element(napi_env env,
@@ -2820,6 +3028,7 @@ This API attempts to delete the specified `index` from `object`.
#### napi_define_properties
```C
napi_status napi_define_properties(napi_env env,
@@ -2863,6 +3072,7 @@ function.
### napi_call_function
```C
napi_status napi_call_function(napi_env env,
@@ -2929,6 +3139,7 @@ if (status != napi_ok) return;
### napi_create_function
```C
napi_status napi_create_function(napi_env env,
@@ -2997,6 +3208,7 @@ responsible for creating the `.node` file.
### napi_get_cb_info
```C
napi_status napi_get_cb_info(napi_env env,
@@ -3027,6 +3239,7 @@ call like the arguments and the `this` pointer from a given callback info.
### napi_get_new_target
```C
napi_status napi_get_new_target(napi_env env,
@@ -3046,6 +3259,7 @@ callback is not a constructor call, the result is `NULL`.
### napi_new_instance
```C
napi_status napi_new_instance(napi_env env,
@@ -3141,6 +3355,7 @@ The reference must be freed once it is no longer needed.
### napi_define_class
```C
napi_status napi_define_class(napi_env env,
@@ -3197,6 +3412,7 @@ reference count is kept >= 1.
### napi_wrap
```C
napi_status napi_wrap(napi_env env,
@@ -3256,6 +3472,7 @@ another native instance with the object, use napi_remove_wrap() first.
### napi_unwrap
```C
napi_status napi_unwrap(napi_env env,
@@ -3281,6 +3498,7 @@ then by calling `napi_unwrap()` on the wrapper object.
### napi_remove_wrap
```C
napi_status napi_remove_wrap(napi_env env,
@@ -3351,6 +3569,7 @@ callback invocation, even when it was cancelled.
### napi_create_async_work
```C
napi_status napi_delete_async_work(napi_env env,
@@ -3417,6 +3637,7 @@ This API can be called even if there is a pending JavaScript exception.
### napi_queue_async_work
```C
napi_status napi_queue_async_work(napi_env env,
@@ -3434,6 +3655,7 @@ for execution.
### napi_cancel_async_work
```C
napi_status napi_cancel_async_work(napi_env env,
@@ -3463,6 +3685,7 @@ the runtime.
### napi_async_init
```C
napi_status napi_async_init(napi_env env,
@@ -3484,6 +3707,7 @@ Returns `napi_ok` if the API succeeded.
### napi_async_destroy
```C
napi_status napi_async_destroy(napi_env env,
@@ -3500,6 +3724,7 @@ This API can be called even if there is a pending JavaScript exception.
### napi_make_callback
```C
NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env,
@@ -3571,6 +3797,7 @@ the required scope.
### *napi_close_callback_scope*
```C
NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env,
@@ -3586,6 +3813,7 @@ This API can be called even if there is a pending JavaScript exception.
### napi_get_node_version
```C
@@ -3614,6 +3842,7 @@ The returned buffer is statically allocated and does not need to be freed.
### napi_get_version
```C
napi_status napi_get_version(napi_env env,
@@ -3644,6 +3873,7 @@ support it:
### napi_adjust_external_memory
```C
NAPI_EXTERN napi_status napi_adjust_external_memory(napi_env env,
@@ -3723,6 +3953,7 @@ deferred = NULL;
### napi_create_promise
```C
napi_status napi_create_promise(napi_env env,
@@ -3743,6 +3974,7 @@ This API creates a deferred object and a JavaScript promise.
### napi_resolve_deferred
```C
napi_status napi_resolve_deferred(napi_env env,
@@ -3766,6 +3998,7 @@ The deferred object is freed upon successful completion.
### napi_reject_deferred
```C
napi_status napi_reject_deferred(napi_env env,
@@ -3789,6 +4022,7 @@ The deferred object is freed upon successful completion.
### napi_is_promise
```C
napi_status napi_is_promise(napi_env env,
@@ -3809,6 +4043,7 @@ underlying JavaScript engine.
### napi_run_script
```C
NAPI_EXTERN napi_status napi_run_script(napi_env env,
@@ -3828,6 +4063,7 @@ a specific `napi_env`.
### napi_get_uv_event_loop
```C
NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env,
@@ -3837,6 +4073,296 @@ NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env,
- `[in] env`: The environment that the API is invoked under.
- `[out] loop`: The current libuv loop instance.
+## Asynchronous Thread-safe Function Calls
+
+> Stability: 1 - Experimental
+
+JavaScript functions can normally only be called from a native addon's main
+thread. If an addon creates additional threads, then N-API functions that
+require a `napi_env`, `napi_value`, or `napi_ref` must not be called from those
+threads.
+
+When an addon has additional threads and JavaScript functions need to be invoked
+based on the processing completed by those threads, those threads must
+communicate with the addon's main thread so that the main thread can invoke the
+JavaScript function on their behalf. The thread-safe function APIs provide an
+easy way to do this.
+
+These APIs provide the type `napi_threadsafe_function` as well as APIs to
+create, destroy, and call objects of this type.
+`napi_create_threadsafe_function()` creates a persistent reference to a
+`napi_value` that holds a JavaScript function which can be called from multiple
+threads. The calls happen asynchronously. This means that values with which the
+JavaScript callback is to be called will be placed in a queue, and, for each
+value in the queue, a call will eventually be made to the JavaScript function.
+
+Upon creation of a `napi_threadsafe_function` a `napi_finalize` callback can be
+provided. This callback will be invoked on the main thread when the thread-safe
+function is about to be destroyed. It receives the context and the finalize data
+given during construction, and provides an opportunity for cleaning up after the
+threads e.g. by calling `uv_thread_join()`. **It is important that, aside from
+the main loop thread, there be no threads left using the thread-safe function
+after the finalize callback completes.**
+
+The `context` given during the call to `napi_create_threadsafe_function()` can
+be retrieved from any thread with a call to
+`napi_get_threadsafe_function_context()`.
+
+`napi_call_threadsafe_function()` can then be used for initiating a call into
+JavaScript. `napi_call_threadsafe_function()` accepts a parameter which controls
+whether the API behaves blockingly. If set to `napi_tsfn_nonblocking`, the API
+behaves non-blockingly, returning `napi_queue_full` if the queue was full,
+preventing data from being successfully added to the queue. If set to
+`napi_tsfn_blocking`, the API blocks until space becomes available in the queue.
+`napi_call_threadsafe_function()` never blocks if the thread-safe function was
+created with a maximum queue size of 0.
+
+The actual call into JavaScript is controlled by the callback given via the
+`call_js_cb` parameter. `call_js_cb` is invoked on the main thread once for each
+value that was placed into the queue by a successful call to
+`napi_call_threadsafe_function()`. If such a callback is not given, a default
+callback will be used, and the resulting JavaScript call will have no arguments.
+The `call_js_cb` callback receives the JavaScript function to call as a
+`napi_value` in its parameters, as well as the `void*` context pointer used when
+creating the `napi_threadsafe_function`, and the next data pointer that was
+created by one of the secondary threads. The callback can then use an API such
+as `napi_call_function()` to call into JavaScript.
+
+The callback may also be invoked with `env` and `call_js_cb` both set to `NULL`
+to indicate that calls into JavaScript are no longer possible, while items
+remain in the queue that may need to be freed. This normally occurs when the
+Node.js process exits while there is a thread-safe function still active.
+
+It is not necessary to call into JavaScript via `napi_make_callback()` because
+N-API runs `call_js_cb` in a context appropriate for callbacks.
+
+Threads can be added to and removed from a `napi_threadsafe_function` object
+during its existence. Thus, in addition to specifying an initial number of
+threads upon creation, `napi_acquire_threadsafe_function` can be called to
+indicate that a new thread will start making use of the thread-safe function.
+Similarly, `napi_release_threadsafe_function` can be called to indicate that an
+existing thread will stop making use of the thread-safe function.
+
+`napi_threadsafe_function` objects are destroyed when every thread which uses
+the object has called `napi_release_threadsafe_function()` or has received a
+return status of `napi_closing` in response to a call to
+`napi_call_threadsafe_function`. The queue is emptied before the
+`napi_threadsafe_function` is destroyed. It is important that
+`napi_release_threadsafe_function()` be the last API call made in conjunction
+with a given `napi_threadsafe_function`, because after the call completes, there
+is no guarantee that the `napi_threadsafe_function` is still allocated. For the
+same reason it is also important that no more use be made of a thread-safe
+function after receiving a return value of `napi_closing` in response to a call
+to `napi_call_threadsafe_function`. Data associated with the
+`napi_threadsafe_function` can be freed in its `napi_finalize` callback which
+was passed to `napi_create_threadsafe_function()`.
+
+Once the number of threads making use of a `napi_threadsafe_function` reaches
+zero, no further threads can start making use of it by calling
+`napi_acquire_threadsafe_function()`. In fact, all subsequent API calls
+associated with it, except `napi_release_threadsafe_function()`, will return an
+error value of `napi_closing`.
+
+The thread-safe function can be "aborted" by giving a value of `napi_tsfn_abort`
+to `napi_release_threadsafe_function()`. This will cause all subsequent APIs
+associated with the thread-safe function except
+`napi_release_threadsafe_function()` to return `napi_closing` even before its
+reference count reaches zero. In particular, `napi_call_threadsafe_function()`
+will return `napi_closing`, thus informing the threads that it is no longer
+possible to make asynchronous calls to the thread-safe function. This can be
+used as a criterion for terminating the thread. **Upon receiving a return value
+of `napi_closing` from `napi_call_threadsafe_function()` a thread must make no
+further use of the thread-safe function because it is no longer guaranteed to
+be allocated.**
+
+Similarly to libuv handles, thread-safe functions can be "referenced" and
+"unreferenced". A "referenced" thread-safe function will cause the event loop on
+the thread on which it is created to remain alive until the thread-safe function
+is destroyed. In contrast, an "unreferenced" thread-safe function will not
+prevent the event loop from exiting. The APIs `napi_ref_threadsafe_function` and
+`napi_unref_threadsafe_function` exist for this purpose.
+
+### napi_create_threadsafe_function
+
+> Stability: 2 - Stable
+
+
+```C
+NAPI_EXTERN napi_status
+napi_create_threadsafe_function(napi_env env,
+ napi_value func,
+ napi_value async_resource,
+ napi_value async_resource_name,
+ size_t max_queue_size,
+ size_t initial_thread_count,
+ void* thread_finalize_data,
+ napi_finalize thread_finalize_cb,
+ void* context,
+ napi_threadsafe_function_call_js call_js_cb,
+ napi_threadsafe_function* result);
+```
+
+- `[in] env`: The environment that the API is invoked under.
+- `[in] func`: The JavaScript function to call from another thread.
+- `[in] async_resource`: An optional object associated with the async work that
+will be passed to possible `async_hooks` [`init` hooks][].
+- `[in] async_resource_name`: A javaScript string to provide an identifier for
+the kind of resource that is being provided for diagnostic information exposed
+by the `async_hooks` API.
+- `[in] max_queue_size`: Maximum size of the queue. 0 for no limit.
+- `[in] initial_thread_count`: The initial number of threads, including the main
+thread, which will be making use of this function.
+- `[in] thread_finalize_data`: Optional data to be passed to `thread_finalize_cb`.
+- `[in] thread_finalize_cb`: Optional function to call when the
+`napi_threadsafe_function` is being destroyed.
+- `[in] context`: Optional data to attach to the resulting
+`napi_threadsafe_function`.
+- `[in] call_js_cb`: Optional callback which calls the JavaScript function in
+response to a call on a different thread. This callback will be called on the
+main thread. If not given, the JavaScript function will be called with no
+parameters and with `undefined` as its `this` value.
+- `[out] result`: The asynchronous thread-safe JavaScript function.
+
+### napi_get_threadsafe_function_context
+
+> Stability: 2 - Stable
+
+
+```C
+NAPI_EXTERN napi_status
+napi_get_threadsafe_function_context(napi_threadsafe_function func,
+ void** result);
+```
+
+- `[in] func`: The thread-safe function for which to retrieve the context.
+- `[out] context`: The location where to store the context.
+
+This API may be called from any thread which makes use of `func`.
+
+### napi_call_threadsafe_function
+
+> Stability: 2 - Stable
+
+
+```C
+NAPI_EXTERN napi_status
+napi_call_threadsafe_function(napi_threadsafe_function func,
+ void* data,
+ napi_threadsafe_function_call_mode is_blocking);
+```
+
+- `[in] func`: The asynchronous thread-safe JavaScript function to invoke.
+- `[in] data`: Data to send into JavaScript via the callback `call_js_cb`
+provided during the creation of the thread-safe JavaScript function.
+- `[in] is_blocking`: Flag whose value can be either `napi_tsfn_blocking` to
+indicate that the call should block if the queue is full or
+`napi_tsfn_nonblocking` to indicate that the call should return immediately with
+a status of `napi_queue_full` whenever the queue is full.
+
+This API will return `napi_closing` if `napi_release_threadsafe_function()` was
+called with `abort` set to `napi_tsfn_abort` from any thread. The value is only
+added to the queue if the API returns `napi_ok`.
+
+This API may be called from any thread which makes use of `func`.
+
+### napi_acquire_threadsafe_function
+
+> Stability: 2 - Stable
+
+
+```C
+NAPI_EXTERN napi_status
+napi_acquire_threadsafe_function(napi_threadsafe_function func);
+```
+
+- `[in] func`: The asynchronous thread-safe JavaScript function to start making
+use of.
+
+A thread should call this API before passing `func` to any other thread-safe
+function APIs to indicate that it will be making use of `func`. This prevents
+`func` from being destroyed when all other threads have stopped making use of
+it.
+
+This API may be called from any thread which will start making use of `func`.
+
+### napi_release_threadsafe_function
+
+> Stability: 2 - Stable
+
+
+```C
+NAPI_EXTERN napi_status
+napi_release_threadsafe_function(napi_threadsafe_function func,
+ napi_threadsafe_function_release_mode mode);
+```
+
+- `[in] func`: The asynchronous thread-safe JavaScript function whose reference
+count to decrement.
+- `[in] mode`: Flag whose value can be either `napi_tsfn_release` to indicate
+that the current thread will make no further calls to the thread-safe function,
+or `napi_tsfn_abort` to indicate that in addition to the current thread, no
+other thread should make any further calls to the thread-safe function. If set
+to `napi_tsfn_abort`, further calls to `napi_call_threadsafe_function()` will
+return `napi_closing`, and no further values will be placed in the queue.
+
+A thread should call this API when it stops making use of `func`. Passing `func`
+to any thread-safe APIs after having called this API has undefined results, as
+`func` may have been destroyed.
+
+This API may be called from any thread which will stop making use of `func`.
+
+### napi_ref_threadsafe_function
+
+> Stability: 2 - Stable
+
+
+```C
+NAPI_EXTERN napi_status
+napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func);
+```
+
+- `[in] env`: The environment that the API is invoked under.
+- `[in] func`: The thread-safe function to reference.
+
+This API is used to indicate that the event loop running on the main thread
+should not exit until `func` has been destroyed. Similar to [`uv_ref`][] it is
+also idempotent.
+
+This API may only be called from the main thread.
+
+### napi_unref_threadsafe_function
+
+> Stability: 2 - Stable
+
+
+```C
+NAPI_EXTERN napi_status
+napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func);
+```
+
+- `[in] env`: The environment that the API is invoked under.
+- `[in] func`: The thread-safe function to unreference.
+
+This API is used to indicate that the event loop running on the main thread
+may exit before `func` is destroyed. Similar to [`uv_unref`][] it is also
+idempotent.
+
+This API may only be called from the main thread.
+
[Basic N-API Data Types]: #n_api_basic_n_api_data_types
[Custom Asynchronous Operations]: #n_api_custom_asynchronous_operations
[ECMAScript Language Specification]: https://tc39.github.io/ecma262/
@@ -3899,5 +4425,7 @@ NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env,
[`napi_throw`]: #n_api_napi_throw
[`napi_unwrap`]: #n_api_napi_unwrap
[`napi_wrap`]: #n_api_napi_wrap
+[`uv_ref`]: http://docs.libuv.org/en/v1.x/handle.html#c.uv_ref
+[`uv_unref`]: http://docs.libuv.org/en/v1.x/handle.html#c.uv_unref
[`process.release`]: process.html#process_process_release
[async_hooks `type`]: async_hooks.html#async_hooks_type
diff --git a/doc/api/process.md b/doc/api/process.md
index 8279f596a51fc1..73f6dbda7e332e 100644
--- a/doc/api/process.md
+++ b/doc/api/process.md
@@ -1703,9 +1703,7 @@ important ways:
1. They are used internally by [`console.log()`][] and [`console.error()`][],
respectively.
-2. They cannot be closed ([`end()`][] will throw).
-3. They will never emit the [`'finish'`][] event.
-4. Writes may be synchronous depending on what the stream is connected to
+2. Writes may be synchronous depending on what the stream is connected to
and whether the system is Windows or POSIX:
- Files: *synchronous* on Windows and POSIX
- TTYs (Terminals): *asynchronous* on Windows, *synchronous* on POSIX
@@ -1925,7 +1923,6 @@ cases:
[`'exit'`]: #process_event_exit
-[`'finish'`]: stream.html#stream_event_finish
[`'message'`]: child_process.html#child_process_event_message
[`'rejectionHandled'`]: #process_event_rejectionhandled
[`'uncaughtException'`]: #process_event_uncaughtexception
@@ -1936,7 +1933,6 @@ cases:
[`EventEmitter`]: events.html#events_class_eventemitter
[`console.error()`]: console.html#console_console_error_data_args
[`console.log()`]: console.html#console_console_log_data_args
-[`end()`]: stream.html#stream_writable_end_chunk_encoding_callback
[`net.Server`]: net.html#net_class_net_server
[`net.Socket`]: net.html#net_class_net_socket
[`process.argv`]: #process_process_argv
diff --git a/doc/changelogs/CHANGELOG_V8.md b/doc/changelogs/CHANGELOG_V8.md
index 6c503420e538a3..5aebf0e7c6cdf8 100644
--- a/doc/changelogs/CHANGELOG_V8.md
+++ b/doc/changelogs/CHANGELOG_V8.md
@@ -10,6 +10,7 @@
|
+8.16.0
8.15.1
8.15.0
8.14.1
@@ -62,6 +63,51 @@
[Node.js Long Term Support Plan](/~https://github.com/nodejs/LTS) and
will be supported actively until April 2019 and maintained until December 2019.
+
+## 2019-04-16, Version 8.16.0 'Carbon' (LTS), @MylesBorins
+
+### Notable Changes
+
+* **n-api**:
+ - add API for asynchronous functions (Gabriel Schulhof) [#17887](/~https://github.com/nodejs/node/pull/17887)
+ - mark thread-safe function as stable (Gabriel Schulhof) [#25556](/~https://github.com/nodejs/node/pull/25556)
+
+### Commits
+
+* [[`705935d620`](/~https://github.com/nodejs/node/commit/705935d620)] - **assert**: fix backport regression (Ruben Bridgewater) [#27202](/~https://github.com/nodejs/node/pull/27202)
+* [[`c07ba9681f`](/~https://github.com/nodejs/node/commit/c07ba9681f)] - **build**: skip cctest on Windows shared lib build (Yihong Wang) [#21228](/~https://github.com/nodejs/node/pull/21228)
+* [[`63522886ea`](/~https://github.com/nodejs/node/commit/63522886ea)] - **build**: add loader path to rpath for cctest (Sam Ruby) [#23168](/~https://github.com/nodejs/node/pull/23168)
+* [[`e9369073d9`](/~https://github.com/nodejs/node/commit/e9369073d9)] - **build**: set `-blibpath:` for AIX (Richard Lau) [#25447](/~https://github.com/nodejs/node/pull/25447)
+* [[`97cc0fc51d`](/~https://github.com/nodejs/node/commit/97cc0fc51d)] - **deps**: V8: cherry-pick 3cc6919 (Farazmand) [#25874](/~https://github.com/nodejs/node/pull/25874)
+* [[`a1aff28fba`](/~https://github.com/nodejs/node/commit/a1aff28fba)] - **deps**: cherry-pick 525b396 from V8 upstream (Peter Marshall) [#25041](/~https://github.com/nodejs/node/pull/25041)
+* [[`6b7cccc88a`](/~https://github.com/nodejs/node/commit/6b7cccc88a)] - **doc**: fix optional parameters in n-api.md (Lars-Magnus Skog) [#22998](/~https://github.com/nodejs/node/pull/22998)
+* [[`b17819db3d`](/~https://github.com/nodejs/node/commit/b17819db3d)] - **doc**: update the http.request.setTimeout docs to be accurate (James Bunton) [#25123](/~https://github.com/nodejs/node/pull/25123)
+* [[`ac9b8f7645`](/~https://github.com/nodejs/node/commit/ac9b8f7645)] - **http**: fix error check in `Execute()` (Brian White) [#24738](/~https://github.com/nodejs/node/pull/24738)
+* [[`1d862610f8`](/~https://github.com/nodejs/node/commit/1d862610f8)] - **http**: attach reused parser to correct domain (Julien Gilli) [#25459](/~https://github.com/nodejs/node/pull/25459)
+* [[`d3de1ed653`](/~https://github.com/nodejs/node/commit/d3de1ed653)] - **n-api**: improve performance creating strings (Anthony Tuininga) [#26439](/~https://github.com/nodejs/node/pull/26439)
+* [[`2b2ad96ef2`](/~https://github.com/nodejs/node/commit/2b2ad96ef2)] - **n-api**: finalize during second-pass callback (Gabriel Schulhof) [#25992](/~https://github.com/nodejs/node/pull/25992)
+* [[`d6ffabc37f`](/~https://github.com/nodejs/node/commit/d6ffabc37f)] - **(SEMVER-MINOR)** **n-api**: mark thread-safe function as stable (Gabriel Schulhof) [#25556](/~https://github.com/nodejs/node/pull/25556)
+* [[`44609d1274`](/~https://github.com/nodejs/node/commit/44609d1274)] - **n-api**: restrict exports by version (Kyle Farnung) [#19962](/~https://github.com/nodejs/node/pull/19962)
+* [[`fe4328252a`](/~https://github.com/nodejs/node/commit/fe4328252a)] - **n-api**: add missing handle scopes (Daniel Bevenius) [#24011](/~https://github.com/nodejs/node/pull/24011)
+* [[`902b07959f`](/~https://github.com/nodejs/node/commit/902b07959f)] - **n-api**: clean up thread-safe function (Gabriel Schulhof) [#22259](/~https://github.com/nodejs/node/pull/22259)
+* [[`09b88aabb3`](/~https://github.com/nodejs/node/commit/09b88aabb3)] - **n-api**: remove idle\_running from TsFn (Lars-Magnus Skog) [#22520](/~https://github.com/nodejs/node/pull/22520)
+* [[`367505940a`](/~https://github.com/nodejs/node/commit/367505940a)] - **n-api**: guard against cond null dereference (Gabriel Schulhof) [#21871](/~https://github.com/nodejs/node/pull/21871)
+* [[`c5a11dc58e`](/~https://github.com/nodejs/node/commit/c5a11dc58e)] - **n-api**: fix compiler warning (cjihrig) [#21597](/~https://github.com/nodejs/node/pull/21597)
+* [[`759a0180b5`](/~https://github.com/nodejs/node/commit/759a0180b5)] - **(SEMVER-MINOR)** **n-api**: add API for asynchronous functions (Gabriel Schulhof) [#17887](/~https://github.com/nodejs/node/pull/17887)
+* [[`ea5628e77a`](/~https://github.com/nodejs/node/commit/ea5628e77a)] - **process**: allow reading from stdout/stderr sockets (Anna Henningsen) [#23053](/~https://github.com/nodejs/node/pull/23053)
+* [[`67b6e0d19c`](/~https://github.com/nodejs/node/commit/67b6e0d19c)] - **src**: fix may be uninitialized warning in n-api (Michael Dawson) [#21898](/~https://github.com/nodejs/node/pull/21898)
+* [[`eaf474cc5d`](/~https://github.com/nodejs/node/commit/eaf474cc5d)] - **test**: shared lib build doesn't handle SIGPIPE (Yihong Wang) [#19211](/~https://github.com/nodejs/node/pull/19211)
+* [[`3128cb7da6`](/~https://github.com/nodejs/node/commit/3128cb7da6)] - **test**: avoid running fsync on directory on AIX (John Barboza) [#21298](/~https://github.com/nodejs/node/pull/21298)
+* [[`b4c5435a46`](/~https://github.com/nodejs/node/commit/b4c5435a46)] - **test**: add process.stdin.end() TTY regression test (Matteo Collina) [#23051](/~https://github.com/nodejs/node/pull/23051)
+* [[`c56f3edb10`](/~https://github.com/nodejs/node/commit/c56f3edb10)] - **test**: add stdin writable regression test (Anna Henningsen) [#23053](/~https://github.com/nodejs/node/pull/23053)
+* [[`f6ff8c51bc`](/~https://github.com/nodejs/node/commit/f6ff8c51bc)] - **test**: fix module loading error for AIX 7.1 (Richard Lau) [#25418](/~https://github.com/nodejs/node/pull/25418)
+* [[`d4b6643ac3`](/~https://github.com/nodejs/node/commit/d4b6643ac3)] - **test**: mark test-cli-node-options flaky on arm (Rich Trott) [#25032](/~https://github.com/nodejs/node/pull/25032)
+* [[`60db455961`](/~https://github.com/nodejs/node/commit/60db455961)] - **test**: mark test\_threadsafe\_function/test as flaky (Gireesh Punathil) [#24714](/~https://github.com/nodejs/node/pull/24714)
+* [[`fbafe8d311`](/~https://github.com/nodejs/node/commit/fbafe8d311)] - **test**: fix test-repl-envvars (Anna Henningsen) [#25226](/~https://github.com/nodejs/node/pull/25226)
+* [[`7573b55a15`](/~https://github.com/nodejs/node/commit/7573b55a15)] - **tls**: fix legacy SecurePair clienthello race window (Ben Noordhuis) [#26452](/~https://github.com/nodejs/node/pull/26452)
+* [[`91620b8bd6`](/~https://github.com/nodejs/node/commit/91620b8bd6)] - **tls**: fix legacy SecurePair session resumption (Ben Noordhuis) [#26452](/~https://github.com/nodejs/node/pull/26452)
+* [[`1a9582b7a6`](/~https://github.com/nodejs/node/commit/1a9582b7a6)] - **tools**: allow input for TTY tests (Anna Henningsen) [#23053](/~https://github.com/nodejs/node/pull/23053)
+
## 2019-02-28, Version 8.15.1 'Carbon' (LTS), @rvagg
diff --git a/lib/_http_client.js b/lib/_http_client.js
index 7b5798fe021a87..0dc8da0f4f6c5e 100644
--- a/lib/_http_client.js
+++ b/lib/_http_client.js
@@ -509,12 +509,6 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
var socket = this.socket;
var req = socket._httpMessage;
- // propagate "domain" setting...
- if (req.domain && !res.domain) {
- debug('setting "res.domain"');
- res.domain = req.domain;
- }
-
debug('AGENT incoming response!');
if (req.res) {
@@ -629,6 +623,9 @@ function tickOnSocket(req, socket) {
req.socket = socket;
req.connection = socket;
parser.reinitialize(HTTPParser.RESPONSE, parser[is_reused_symbol]);
+ if (process.domain) {
+ process.domain.add(parser);
+ }
parser.socket = socket;
parser.incoming = null;
parser.outgoing = req;
diff --git a/lib/_tls_legacy.js b/lib/_tls_legacy.js
index fc2fb6db5d12ae..7b9e34bb847c31 100644
--- a/lib/_tls_legacy.js
+++ b/lib/_tls_legacy.js
@@ -659,6 +659,10 @@ function onclienthello(hello) {
if (err) return self.socket.destroy(err);
setImmediate(function() {
+ // SecurePair might have been destroyed in the time window
+ // between callback() and this function.
+ if (!self.ssl) return;
+
self.ssl.loadSession(session);
self.ssl.endParser();
@@ -691,8 +695,9 @@ function onnewsession(key, session) {
return;
once = true;
- if (self.ssl)
- self.ssl.newSessionDone();
+ // Cycle data
+ self.cleartext.read(0);
+ self.encrypted.read(0);
}
}
diff --git a/lib/assert.js b/lib/assert.js
index e29a381acb746d..4cab918e6a74e9 100644
--- a/lib/assert.js
+++ b/lib/assert.js
@@ -720,7 +720,8 @@ function getActual(block) {
async function waitForActual(block) {
if (typeof block !== 'function') {
- throw new errors.ERR_INVALID_ARG_TYPE('block', 'Function', block);
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'block', 'Function',
+ block);
}
// Return a rejected promise if `block` throws synchronously.
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index e878766f680605..c260b547b8a7e8 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -427,8 +427,6 @@ E('ERR_SOCKET_BUFFER_SIZE',
(reason) => `Could not get or set buffer size: ${reason}`);
E('ERR_SOCKET_CANNOT_SEND', 'Unable to send data');
E('ERR_SOCKET_DGRAM_NOT_RUNNING', 'Not running');
-E('ERR_STDERR_CLOSE', 'process.stderr cannot be closed');
-E('ERR_STDOUT_CLOSE', 'process.stdout cannot be closed');
E('ERR_UNKNOWN_BUILTIN_MODULE', (id) => `No such built-in module: ${id}`);
E('ERR_UNKNOWN_FILE_EXTENSION', 'Unknown file extension: %s');
E('ERR_UNKNOWN_MODULE_FORMAT', 'Unknown module format: %s');
diff --git a/lib/internal/process/stdio.js b/lib/internal/process/stdio.js
index 7faef381f895bf..46641b5871646f 100644
--- a/lib/internal/process/stdio.js
+++ b/lib/internal/process/stdio.js
@@ -1,9 +1,11 @@
'use strict';
-const errors = require('internal/errors');
+const errors = require('internal/errors').codes;
exports.setup = setupStdio;
+function dummyDestroy(err, cb) { cb(err); }
+
function setupStdio() {
var stdin;
var stdout;
@@ -13,11 +15,8 @@ function setupStdio() {
if (stdout) return stdout;
stdout = createWritableStdioStream(1);
stdout.destroySoon = stdout.destroy;
- stdout._destroy = function(er, cb) {
- // Avoid errors if we already emitted
- er = er || new errors.Error('ERR_STDOUT_CLOSE');
- cb(er);
- };
+ // Override _destroy so that the fd is never actually closed.
+ stdout._destroy = dummyDestroy;
if (stdout.isTTY) {
process.on('SIGWINCH', () => stdout._refreshSize());
}
@@ -28,11 +27,8 @@ function setupStdio() {
if (stderr) return stderr;
stderr = createWritableStdioStream(2);
stderr.destroySoon = stderr.destroy;
- stderr._destroy = function(er, cb) {
- // Avoid errors if we already emitted
- er = er || new errors.Error('ERR_STDERR_CLOSE');
- cb(er);
- };
+ // Override _destroy so that the fd is never actually closed.
+ stdout._destroy = dummyDestroy;
if (stderr.isTTY) {
process.on('SIGWINCH', () => stderr._refreshSize());
}
diff --git a/node.gyp b/node.gyp
index ed20a490d9ee1d..91a0590993b973 100644
--- a/node.gyp
+++ b/node.gyp
@@ -178,6 +178,9 @@
'sources': [
'src/node_main.cc'
],
+ 'includes': [
+ 'node.gypi'
+ ],
'include_dirs': [
'src',
'deps/v8/include',
@@ -195,9 +198,6 @@
}],
[ 'node_intermediate_lib_type=="static_library" and '
'node_shared=="false"', {
- 'includes': [
- 'node.gypi'
- ],
'xcode_settings': {
'OTHER_LDFLAGS': [
'-Wl,-force_load,<(PRODUCT_DIR)/<(STATIC_LIB_PREFIX)'
@@ -439,22 +439,8 @@
],
}],
],
- 'defines!': [
- 'NODE_PLATFORM="win"',
- ],
- 'defines': [
- 'FD_SETSIZE=1024',
- # we need to use node's preferred "win32" rather than gyp's preferred "win"
- 'NODE_PLATFORM="win32"',
- # Stop from defining macros that conflict with
- # std::min() and std::max(). We don't use (much)
- # but we still inherit it from uv.h.
- 'NOMINMAX',
- '_UNICODE=1',
- ],
'libraries': [ '-lpsapi.lib' ]
}, { # POSIX
- 'defines': [ '__POSIX__' ],
'sources': [ 'src/backtrace_posix.cc' ],
}],
[ 'node_use_etw=="true"', {
@@ -956,6 +942,15 @@
['OS=="solaris"', {
'ldflags': [ '-I<(SHARED_INTERMEDIATE_DIR)' ]
}],
+ [ 'node_shared=="true"', {
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [ '-Wl,-rpath,@loader_path', ],
+ },
+ }],
+ # Skip cctest while building shared lib node for Windows
+ [ 'OS=="win" and node_shared=="true"', {
+ 'type': 'none',
+ }],
],
}
], # end targets
diff --git a/node.gypi b/node.gypi
index 82953ee9ff9af4..3faffdfff3366f 100644
--- a/node.gypi
+++ b/node.gypi
@@ -37,6 +37,24 @@
'NODE_SHARED_MODE',
],
}],
+ [ 'OS=="win"', {
+ 'defines!': [
+ 'NODE_PLATFORM="win"',
+ ],
+ 'defines': [
+ 'FD_SETSIZE=1024',
+ # we need to use node's preferred "win32" rather than gyp's preferred "win"
+ 'NODE_PLATFORM="win32"',
+ # Stop from defining macros that conflict with
+ # std::min() and std::max(). We don't use (much)
+ # but we still inherit it from uv.h.
+ 'NOMINMAX',
+ '_UNICODE=1',
+ ],
+ }, { # POSIX
+ 'defines': [ '__POSIX__' ],
+ }],
+
[ 'node_enable_d8=="true"', {
'dependencies': [ 'deps/v8/src/d8.gyp:d8' ],
}],
diff --git a/src/env.h b/src/env.h
index e378869b4ccdcb..b47d55d87b3e69 100644
--- a/src/env.h
+++ b/src/env.h
@@ -216,7 +216,6 @@ class ModuleWrap;
V(onheaders_string, "onheaders") \
V(onmessage_string, "onmessage") \
V(onnewsession_string, "onnewsession") \
- V(onnewsessiondone_string, "onnewsessiondone") \
V(onocspresponse_string, "onocspresponse") \
V(ongoawaydata_string, "ongoawaydata") \
V(onorigin_string, "onorigin") \
diff --git a/src/node_api.cc b/src/node_api.cc
index 61e9f6b4b00279..4d996f97a58dde 100644
--- a/src/node_api.cc
+++ b/src/node_api.cc
@@ -5,6 +5,7 @@
#include
#include
#include
+#define NAPI_EXPERIMENTAL
#include "node_api.h"
#include "node_internals.h"
#include "env.h"
@@ -447,10 +448,25 @@ class Reference : private Finalizer {
}
private:
+ // The N-API finalizer callback may make calls into the engine. V8's heap is
+ // not in a consistent state during the weak callback, and therefore it does
+ // not support calls back into it. However, it provides a mechanism for adding
+ // a finalizer which may make calls back into the engine by allowing us to
+ // attach such a second-pass finalizer from the first pass finalizer. Thus,
+ // we do that here to ensure that the N-API finalizer callback is free to call
+ // into the engine.
static void FinalizeCallback(const v8::WeakCallbackInfo& data) {
Reference* reference = data.GetParameter();
+
+ // The reference must be reset during the first pass.
reference->_persistent.Reset();
+ data.SetSecondPassCallback(SecondPassCallback);
+ }
+
+ static void SecondPassCallback(const v8::WeakCallbackInfo& data) {
+ Reference* reference = data.GetParameter();
+
// Check before calling the finalize callback, because the callback might
// delete it.
bool delete_self = reference->_delete_self;
@@ -855,6 +871,341 @@ napi_status ConcludeDeferred(napi_env env,
return GET_RETURN_STATUS(env);
}
+class ThreadSafeFunction : public node::AsyncResource {
+ public:
+ ThreadSafeFunction(v8::Local func,
+ v8::Local resource,
+ v8::Local name,
+ size_t thread_count_,
+ void* context_,
+ size_t max_queue_size_,
+ napi_env env_,
+ void* finalize_data_,
+ napi_finalize finalize_cb_,
+ napi_threadsafe_function_call_js call_js_cb_):
+ AsyncResource(env_->isolate,
+ resource,
+ *v8::String::Utf8Value(env_->isolate, name)),
+ thread_count(thread_count_),
+ is_closing(false),
+ context(context_),
+ max_queue_size(max_queue_size_),
+ env(env_),
+ finalize_data(finalize_data_),
+ finalize_cb(finalize_cb_),
+ call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_),
+ handles_closing(false) {
+ ref.Reset(env->isolate, func);
+ node::AddEnvironmentCleanupHook(env->isolate, Cleanup, this);
+ }
+
+ ~ThreadSafeFunction() {
+ node::RemoveEnvironmentCleanupHook(env->isolate, Cleanup, this);
+ if (ref.IsEmpty())
+ return;
+ ref.ClearWeak();
+ ref.Reset();
+ }
+
+ // These methods can be called from any thread.
+
+ napi_status Push(void* data, napi_threadsafe_function_call_mode mode) {
+ node::Mutex::ScopedLock lock(this->mutex);
+
+ while (queue.size() >= max_queue_size &&
+ max_queue_size > 0 &&
+ !is_closing) {
+ if (mode == napi_tsfn_nonblocking) {
+ return napi_queue_full;
+ }
+ cond->Wait(lock);
+ }
+
+ if (is_closing) {
+ if (thread_count == 0) {
+ return napi_invalid_arg;
+ } else {
+ thread_count--;
+ return napi_closing;
+ }
+ } else {
+ if (uv_async_send(&async) != 0) {
+ return napi_generic_failure;
+ }
+ queue.push(data);
+ return napi_ok;
+ }
+ }
+
+ napi_status Acquire() {
+ node::Mutex::ScopedLock lock(this->mutex);
+
+ if (is_closing) {
+ return napi_closing;
+ }
+
+ thread_count++;
+
+ return napi_ok;
+ }
+
+ napi_status Release(napi_threadsafe_function_release_mode mode) {
+ node::Mutex::ScopedLock lock(this->mutex);
+
+ if (thread_count == 0) {
+ return napi_invalid_arg;
+ }
+
+ thread_count--;
+
+ if (thread_count == 0 || mode == napi_tsfn_abort) {
+ if (!is_closing) {
+ is_closing = (mode == napi_tsfn_abort);
+ if (is_closing && max_queue_size > 0) {
+ cond->Signal(lock);
+ }
+ if (uv_async_send(&async) != 0) {
+ return napi_generic_failure;
+ }
+ }
+ }
+
+ return napi_ok;
+ }
+
+ void EmptyQueueAndDelete() {
+ for (; !queue.empty() ; queue.pop()) {
+ call_js_cb(nullptr, nullptr, context, queue.front());
+ }
+ delete this;
+ }
+
+ // These methods must only be called from the loop thread.
+
+ napi_status Init() {
+ ThreadSafeFunction* ts_fn = this;
+
+ if (uv_async_init(env->loop, &async, AsyncCb) == 0) {
+ if (max_queue_size > 0) {
+ cond.reset(new node::ConditionVariable);
+ }
+ if ((max_queue_size == 0 || cond.get() != nullptr) &&
+ uv_idle_init(env->loop, &idle) == 0) {
+ return napi_ok;
+ }
+
+ uv_close(reinterpret_cast(&async),
+ [] (uv_handle_t* handle) -> void {
+ ThreadSafeFunction* ts_fn =
+ node::ContainerOf(&ThreadSafeFunction::async,
+ reinterpret_cast(handle));
+ delete ts_fn;
+ });
+
+ // Prevent the thread-safe function from being deleted here, because
+ // the callback above will delete it.
+ ts_fn = nullptr;
+ }
+
+ delete ts_fn;
+
+ return napi_generic_failure;
+ }
+
+ napi_status Unref() {
+ uv_unref(reinterpret_cast(&async));
+ uv_unref(reinterpret_cast(&idle));
+
+ return napi_ok;
+ }
+
+ napi_status Ref() {
+ uv_ref(reinterpret_cast(&async));
+ uv_ref(reinterpret_cast(&idle));
+
+ return napi_ok;
+ }
+
+ void DispatchOne() {
+ void* data = nullptr;
+ bool popped_value = false;
+ bool idle_stop_failed = false;
+
+ {
+ node::Mutex::ScopedLock lock(this->mutex);
+ if (is_closing) {
+ CloseHandlesAndMaybeDelete();
+ } else {
+ size_t size = queue.size();
+ if (size > 0) {
+ data = queue.front();
+ queue.pop();
+ popped_value = true;
+ if (size == max_queue_size && max_queue_size > 0) {
+ cond->Signal(lock);
+ }
+ size--;
+ }
+
+ if (size == 0) {
+ if (thread_count == 0) {
+ is_closing = true;
+ if (max_queue_size > 0) {
+ cond->Signal(lock);
+ }
+ CloseHandlesAndMaybeDelete();
+ } else {
+ if (uv_idle_stop(&idle) != 0) {
+ idle_stop_failed = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (popped_value || idle_stop_failed) {
+ v8::HandleScope scope(env->isolate);
+ CallbackScope cb_scope(this);
+
+ if (idle_stop_failed) {
+ CHECK(napi_throw_error(env,
+ "ERR_NAPI_TSFN_STOP_IDLE_LOOP",
+ "Failed to stop the idle loop") == napi_ok);
+ } else {
+ v8::Local js_cb =
+ v8::Local::New(env->isolate, ref);
+ call_js_cb(env,
+ v8impl::JsValueFromV8LocalValue(js_cb),
+ context,
+ data);
+ }
+ }
+ }
+
+ node::Environment* NodeEnv() {
+ // For some reason grabbing the Node.js environment requires a handle scope.
+ v8::HandleScope scope(env->isolate);
+ return node::Environment::GetCurrent(env->isolate);
+ }
+
+ void MaybeStartIdle() {
+ if (uv_idle_start(&idle, IdleCb) != 0) {
+ v8::HandleScope scope(env->isolate);
+ CallbackScope cb_scope(this);
+ CHECK(napi_throw_error(env,
+ "ERR_NAPI_TSFN_START_IDLE_LOOP",
+ "Failed to start the idle loop") == napi_ok);
+ }
+ }
+
+ void Finalize() {
+ v8::HandleScope scope(env->isolate);
+ if (finalize_cb) {
+ CallbackScope cb_scope(this);
+ finalize_cb(env, finalize_data, context);
+ }
+ EmptyQueueAndDelete();
+ }
+
+ inline void* Context() {
+ return context;
+ }
+
+ void CloseHandlesAndMaybeDelete(bool set_closing = false) {
+ v8::HandleScope scope(env->isolate);
+ if (set_closing) {
+ node::Mutex::ScopedLock lock(this->mutex);
+ is_closing = true;
+ if (max_queue_size > 0) {
+ cond->Signal(lock);
+ }
+ }
+ if (handles_closing) {
+ return;
+ }
+ handles_closing = true;
+ uv_close(
+ reinterpret_cast(&async),
+ [] (uv_handle_t* handle) -> void {
+ ThreadSafeFunction* ts_fn =
+ node::ContainerOf(&ThreadSafeFunction::async,
+ reinterpret_cast(handle));
+ v8::HandleScope scope(ts_fn->env->isolate);
+ uv_close(
+ reinterpret_cast(&ts_fn->idle),
+ [] (uv_handle_t* handle) -> void {
+ ThreadSafeFunction* ts_fn =
+ node::ContainerOf(&ThreadSafeFunction::idle,
+ reinterpret_cast(handle));
+ ts_fn->Finalize();
+ });
+ });
+ }
+
+ // Default way of calling into JavaScript. Used when ThreadSafeFunction is
+ // constructed without a call_js_cb_.
+ static void CallJs(napi_env env, napi_value cb, void* context, void* data) {
+ if (!(env == nullptr || cb == nullptr)) {
+ napi_value recv;
+ napi_status status;
+
+ status = napi_get_undefined(env, &recv);
+ if (status != napi_ok) {
+ napi_throw_error(env, "ERR_NAPI_TSFN_GET_UNDEFINED",
+ "Failed to retrieve undefined value");
+ return;
+ }
+
+ status = napi_call_function(env, recv, cb, 0, nullptr, nullptr);
+ if (status != napi_ok && status != napi_pending_exception) {
+ napi_throw_error(env, "ERR_NAPI_TSFN_CALL_JS",
+ "Failed to call JS callback");
+ return;
+ }
+ }
+ }
+
+ static void IdleCb(uv_idle_t* idle) {
+ ThreadSafeFunction* ts_fn =
+ node::ContainerOf(&ThreadSafeFunction::idle, idle);
+ ts_fn->DispatchOne();
+ }
+
+ static void AsyncCb(uv_async_t* async) {
+ ThreadSafeFunction* ts_fn =
+ node::ContainerOf(&ThreadSafeFunction::async, async);
+ ts_fn->MaybeStartIdle();
+ }
+
+ static void Cleanup(void* data) {
+ reinterpret_cast(data)
+ ->CloseHandlesAndMaybeDelete(true);
+ }
+
+ private:
+ // These are variables protected by the mutex.
+ node::Mutex mutex;
+ std::unique_ptr cond;
+ std::queue queue;
+ uv_async_t async;
+ uv_idle_t idle;
+ size_t thread_count;
+ bool is_closing;
+
+ // These are variables set once, upon creation, and then never again, which
+ // means we don't need the mutex to read them.
+ void* context;
+ size_t max_queue_size;
+
+ // These are variables accessed only from the loop thread.
+ v8::Persistent ref;
+ napi_env env;
+ void* finalize_data;
+ napi_finalize finalize_cb;
+ napi_threadsafe_function_call_js call_js_cb;
+ bool handles_closing;
+};
+
} // end of namespace v8impl
// Intercepts the Node-V8 module registration callback. Converts parameters
@@ -947,7 +1298,10 @@ const char* error_messages[] = {nullptr,
"The async work item was cancelled",
"napi_escape_handle already called on scope",
"Invalid handle scope usage",
- "Invalid callback scope usage"};
+ "Invalid callback scope usage",
+ "Thread-safe function queue is full",
+ "Thread-safe function handle is closing"
+};
static inline napi_status napi_clear_last_error(napi_env env) {
env->last_error.error_code = napi_ok;
@@ -978,7 +1332,7 @@ napi_status napi_get_last_error_info(napi_env env,
// We don't have a napi_status_last as this would result in an ABI
// change each time a message was added.
static_assert(
- node::arraysize(error_messages) == napi_callback_scope_mismatch + 1,
+ node::arraysize(error_messages) == napi_closing + 1,
"Count of error messages must match count of error values");
CHECK_LE(env->last_error.error_code, napi_callback_scope_mismatch);
@@ -1639,12 +1993,15 @@ napi_status napi_create_string_latin1(napi_env env,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
+ RETURN_STATUS_IF_FALSE(env,
+ (length == NAPI_AUTO_LENGTH) || length <= INT_MAX,
+ napi_invalid_arg);
auto isolate = env->isolate;
auto str_maybe =
v8::String::NewFromOneByte(isolate,
reinterpret_cast(str),
- v8::NewStringType::kInternalized,
+ v8::NewStringType::kNormal,
length);
CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
@@ -1658,11 +2015,18 @@ napi_status napi_create_string_utf8(napi_env env,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
+ RETURN_STATUS_IF_FALSE(env,
+ (length == NAPI_AUTO_LENGTH) || length <= INT_MAX,
+ napi_invalid_arg);
- v8::Local s;
- CHECK_NEW_FROM_UTF8_LEN(env, s, str, length);
-
- *result = v8impl::JsValueFromV8LocalValue(s);
+ auto isolate = env->isolate;
+ auto str_maybe =
+ v8::String::NewFromUtf8(isolate,
+ str,
+ v8::NewStringType::kNormal,
+ static_cast(length));
+ CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
+ *result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
return napi_clear_last_error(env);
}
@@ -1672,12 +2036,15 @@ napi_status napi_create_string_utf16(napi_env env,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
+ RETURN_STATUS_IF_FALSE(env,
+ (length == NAPI_AUTO_LENGTH) || length <= INT_MAX,
+ napi_invalid_arg);
auto isolate = env->isolate;
auto str_maybe =
v8::String::NewFromTwoByte(isolate,
reinterpret_cast(str),
- v8::NewStringType::kInternalized,
+ v8::NewStringType::kNormal,
length);
CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
@@ -3587,3 +3954,107 @@ napi_status napi_run_script(napi_env env,
*result = v8impl::JsValueFromV8LocalValue(script_result.ToLocalChecked());
return GET_RETURN_STATUS(env);
}
+
+napi_status
+napi_create_threadsafe_function(napi_env env,
+ napi_value func,
+ napi_value async_resource,
+ napi_value async_resource_name,
+ size_t max_queue_size,
+ size_t initial_thread_count,
+ void* thread_finalize_data,
+ napi_finalize thread_finalize_cb,
+ void* context,
+ napi_threadsafe_function_call_js call_js_cb,
+ napi_threadsafe_function* result) {
+ CHECK_ENV(env);
+ CHECK_ARG(env, func);
+ CHECK_ARG(env, async_resource_name);
+ RETURN_STATUS_IF_FALSE(env, initial_thread_count > 0, napi_invalid_arg);
+ CHECK_ARG(env, result);
+
+ napi_status status = napi_ok;
+
+ v8::Local v8_func;
+ CHECK_TO_FUNCTION(env, v8_func, func);
+
+ v8::Local v8_context = env->isolate->GetCurrentContext();
+
+ v8::Local v8_resource;
+ if (async_resource == nullptr) {
+ v8_resource = v8::Object::New(env->isolate);
+ } else {
+ CHECK_TO_OBJECT(env, v8_context, v8_resource, async_resource);
+ }
+
+ v8::Local v8_name;
+ CHECK_TO_STRING(env, v8_context, v8_name, async_resource_name);
+
+ v8impl::ThreadSafeFunction* ts_fn =
+ new v8impl::ThreadSafeFunction(v8_func,
+ v8_resource,
+ v8_name,
+ initial_thread_count,
+ context,
+ max_queue_size,
+ env,
+ thread_finalize_data,
+ thread_finalize_cb,
+ call_js_cb);
+
+ if (ts_fn == nullptr) {
+ status = napi_generic_failure;
+ } else {
+ // Init deletes ts_fn upon failure.
+ status = ts_fn->Init();
+ if (status == napi_ok) {
+ *result = reinterpret_cast(ts_fn);
+ }
+ }
+
+ return napi_set_last_error(env, status);
+}
+
+napi_status
+napi_get_threadsafe_function_context(napi_threadsafe_function func,
+ void** result) {
+ CHECK(func != nullptr);
+ CHECK(result != nullptr);
+
+ *result = reinterpret_cast(func)->Context();
+ return napi_ok;
+}
+
+napi_status
+napi_call_threadsafe_function(napi_threadsafe_function func,
+ void* data,
+ napi_threadsafe_function_call_mode is_blocking) {
+ CHECK(func != nullptr);
+ return reinterpret_cast(func)->Push(data,
+ is_blocking);
+}
+
+napi_status
+napi_acquire_threadsafe_function(napi_threadsafe_function func) {
+ CHECK(func != nullptr);
+ return reinterpret_cast(func)->Acquire();
+}
+
+napi_status
+napi_release_threadsafe_function(napi_threadsafe_function func,
+ napi_threadsafe_function_release_mode mode) {
+ CHECK(func != nullptr);
+ return reinterpret_cast(func)->Release(mode);
+}
+
+napi_status
+napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
+ CHECK(func != nullptr);
+ return reinterpret_cast(func)->Unref();
+}
+
+napi_status
+napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
+ CHECK(func != nullptr);
+ return reinterpret_cast(func)->Ref();
+}
diff --git a/src/node_api.h b/src/node_api.h
index 6e6c575b76609a..f683f8d51b7cd8 100644
--- a/src/node_api.h
+++ b/src/node_api.h
@@ -3,6 +3,17 @@
#include
#include
+
+#ifndef NAPI_VERSION
+#ifdef NAPI_EXPERIMENTAL
+// Use INT_MAX, this should only be consumed by the pre-processor anyway.
+#define NAPI_VERSION 2147483647
+#else
+// The baseline version for N-API
+#define NAPI_VERSION 4
+#endif
+#endif
+
#include "node_api_types.h"
struct uv_loop_s; // Forward declaration.
@@ -99,19 +110,10 @@ EXTERN_C_START
NAPI_EXTERN void napi_module_register(napi_module* mod);
-NAPI_EXTERN napi_status napi_add_env_cleanup_hook(napi_env env,
- void (*fun)(void* arg),
- void* arg);
-NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(napi_env env,
- void (*fun)(void* arg),
- void* arg);
-
NAPI_EXTERN napi_status
napi_get_last_error_info(napi_env env,
const napi_extended_error_info** result);
-NAPI_EXTERN napi_status napi_fatal_exception(napi_env env, napi_value err);
-
NAPI_EXTERN NAPI_NO_RETURN void napi_fatal_error(const char* location,
size_t location_len,
const char* message,
@@ -424,14 +426,6 @@ NAPI_EXTERN napi_status napi_escape_handle(napi_env env,
napi_value escapee,
napi_value* result);
-NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env,
- napi_value resource_object,
- napi_async_context context,
- napi_callback_scope* result);
-
-NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env,
- napi_callback_scope scope);
-
// Methods to support error handling
NAPI_EXTERN napi_status napi_throw(napi_env env, napi_value error);
NAPI_EXTERN napi_status napi_throw_error(napi_env env,
@@ -591,10 +585,76 @@ NAPI_EXTERN napi_status napi_run_script(napi_env env,
napi_value script,
napi_value* result);
+#if NAPI_VERSION >= 2
+
// Return the current libuv event loop for a given environment
NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env,
struct uv_loop_s** loop);
+#endif // NAPI_VERSION >= 2
+
+#if NAPI_VERSION >= 3
+
+NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env,
+ napi_value resource_object,
+ napi_async_context context,
+ napi_callback_scope* result);
+
+NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env,
+ napi_callback_scope scope);
+
+NAPI_EXTERN napi_status napi_fatal_exception(napi_env env, napi_value err);
+
+NAPI_EXTERN napi_status napi_add_env_cleanup_hook(napi_env env,
+ void (*fun)(void* arg),
+ void* arg);
+
+NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(napi_env env,
+ void (*fun)(void* arg),
+ void* arg);
+
+#endif // NAPI_VERSION >= 3
+
+#if NAPI_VERSION >= 4
+
+// Calling into JS from other threads
+NAPI_EXTERN napi_status
+napi_create_threadsafe_function(napi_env env,
+ napi_value func,
+ napi_value async_resource,
+ napi_value async_resource_name,
+ size_t max_queue_size,
+ size_t initial_thread_count,
+ void* thread_finalize_data,
+ napi_finalize thread_finalize_cb,
+ void* context,
+ napi_threadsafe_function_call_js call_js_cb,
+ napi_threadsafe_function* result);
+
+NAPI_EXTERN napi_status
+napi_get_threadsafe_function_context(napi_threadsafe_function func,
+ void** result);
+
+NAPI_EXTERN napi_status
+napi_call_threadsafe_function(napi_threadsafe_function func,
+ void* data,
+ napi_threadsafe_function_call_mode is_blocking);
+
+NAPI_EXTERN napi_status
+napi_acquire_threadsafe_function(napi_threadsafe_function func);
+
+NAPI_EXTERN napi_status
+napi_release_threadsafe_function(napi_threadsafe_function func,
+ napi_threadsafe_function_release_mode mode);
+
+NAPI_EXTERN napi_status
+napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func);
+
+NAPI_EXTERN napi_status
+napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func);
+
+#endif // NAPI_VERSION >= 4
+
EXTERN_C_END
#endif // SRC_NODE_API_H_
diff --git a/src/node_api_types.h b/src/node_api_types.h
index 76f38802e83e2e..c9229c3bdd3408 100644
--- a/src/node_api_types.h
+++ b/src/node_api_types.h
@@ -20,6 +20,9 @@ typedef struct napi_callback_info__ *napi_callback_info;
typedef struct napi_async_context__ *napi_async_context;
typedef struct napi_async_work__ *napi_async_work;
typedef struct napi_deferred__ *napi_deferred;
+#if NAPI_VERSION >= 4
+typedef struct napi_threadsafe_function__* napi_threadsafe_function;
+#endif // NAPI_VERSION >= 4
typedef enum {
napi_default = 0,
@@ -72,9 +75,25 @@ typedef enum {
napi_cancelled,
napi_escape_called_twice,
napi_handle_scope_mismatch,
- napi_callback_scope_mismatch
+ napi_callback_scope_mismatch,
+#if NAPI_VERSION >= 4
+ napi_queue_full,
+ napi_closing,
+#endif // NAPI_VERSION >= 4
} napi_status;
+#if NAPI_VERSION >= 4
+typedef enum {
+ napi_tsfn_release,
+ napi_tsfn_abort
+} napi_threadsafe_function_release_mode;
+
+typedef enum {
+ napi_tsfn_nonblocking,
+ napi_tsfn_blocking
+} napi_threadsafe_function_call_mode;
+#endif // NAPI_VERSION >= 4
+
typedef napi_value (*napi_callback)(napi_env env,
napi_callback_info info);
typedef void (*napi_finalize)(napi_env env,
@@ -86,6 +105,13 @@ typedef void (*napi_async_complete_callback)(napi_env env,
napi_status status,
void* data);
+#if NAPI_VERSION >= 4
+typedef void (*napi_threadsafe_function_call_js)(napi_env env,
+ napi_value js_callback,
+ void* context,
+ void* data);
+#endif // NAPI_VERSION >= 4
+
typedef struct {
// One of utf8name or name should be NULL.
const char* utf8name;
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index d0e17717d9c2cb..61fefd78b9d970 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -2965,9 +2965,6 @@ void Connection::SetShutdownFlags() {
void Connection::NewSessionDoneCb() {
- HandleScope scope(env()->isolate());
-
- MakeCallback(env()->onnewsessiondone_string(), 0, nullptr);
}
diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc
index 504130e5d7c3f1..1b03fe1c719cad 100644
--- a/src/node_http_parser.cc
+++ b/src/node_http_parser.cc
@@ -621,7 +621,28 @@ class Parser : public AsyncWrap {
size_t nparsed =
http_parser_execute(&parser_, &settings, data, len);
- Save();
+ enum http_errno err = HTTP_PARSER_ERRNO(&parser_);
+
+ // Finish()
+ if (data == nullptr) {
+ // `http_parser_execute()` returns either `0` or `1` when `len` is 0
+ // (part of the finishing sequence).
+ CHECK_EQ(len, 0);
+ switch (nparsed) {
+ case 0:
+ err = HPE_OK;
+ break;
+ case 1:
+ nparsed = 0;
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ // Regular Execute()
+ } else {
+ Save();
+ }
// Unassign the 'buffer_' variable
current_buffer_.Clear();
@@ -635,7 +656,7 @@ class Parser : public AsyncWrap {
Local nparsed_obj = Integer::New(env()->isolate(), nparsed);
// If there was a parse error in one of the callbacks
// TODO(bnoordhuis) What if there is an error on EOF?
- if (!parser_.upgrade && nparsed != len) {
+ if (!parser_.upgrade && err != HPE_OK) {
enum http_errno err = HTTP_PARSER_ERRNO(&parser_);
Local e = Exception::Error(env()->parse_error_string());
@@ -646,6 +667,11 @@ class Parser : public AsyncWrap {
return scope.Escape(e);
}
+
+ // No return value is needed for `Finish()`
+ if (data == nullptr) {
+ return scope.Escape(Local());
+ }
return scope.Escape(nparsed_obj);
}
diff --git a/src/node_main.cc b/src/node_main.cc
index 7ab612d53e500a..8907c47ae0ea4c 100644
--- a/src/node_main.cc
+++ b/src/node_main.cc
@@ -82,12 +82,30 @@ int wmain(int argc, wchar_t *wargv[]) {
#endif // __LP64__
extern char** environ;
#endif // __linux__
+#if defined(__POSIX__) && defined(NODE_SHARED_MODE)
+#include
+#include
+#endif
namespace node {
extern bool linux_at_secure;
} // namespace node
int main(int argc, char *argv[]) {
+#if defined(__POSIX__) && defined(NODE_SHARED_MODE)
+ // In node::PlatformInit(), we squash all signal handlers for non-shared lib
+ // build. In order to run test cases against shared lib build, we also need
+ // to do the same thing for shared lib build here, but only for SIGPIPE for
+ // now. If node::PlatformInit() is moved to here, then this section could be
+ // removed.
+ {
+ struct sigaction act;
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &act, nullptr);
+ }
+#endif
+
#if defined(__linux__)
char** envp = environ;
while (*envp++ != nullptr) {}
diff --git a/src/node_version.h b/src/node_version.h
index 5442c2ef49bc48..8c57b7de65d6a7 100644
--- a/src/node_version.h
+++ b/src/node_version.h
@@ -23,13 +23,13 @@
#define SRC_NODE_VERSION_H_
#define NODE_MAJOR_VERSION 8
-#define NODE_MINOR_VERSION 15
-#define NODE_PATCH_VERSION 2
+#define NODE_MINOR_VERSION 16
+#define NODE_PATCH_VERSION 0
#define NODE_VERSION_IS_LTS 1
#define NODE_VERSION_LTS_CODENAME "Carbon"
-#define NODE_VERSION_IS_RELEASE 0
+#define NODE_VERSION_IS_RELEASE 1
#ifndef NODE_STRINGIFY
#define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)
@@ -108,6 +108,6 @@
#define NODE_MODULE_VERSION 57
// the NAPI_VERSION provided by this version of the runtime
-#define NAPI_VERSION 3
+#define NAPI_VERSION 4
#endif // SRC_NODE_VERSION_H_
diff --git a/test/addons-napi/addons-napi.status b/test/addons-napi/addons-napi.status
new file mode 100644
index 00000000000000..dffcf3787bfe47
--- /dev/null
+++ b/test/addons-napi/addons-napi.status
@@ -0,0 +1,11 @@
+prefix addons-napi
+
+# To mark a test as flaky, list the test name in the appropriate section
+# below, without ".js", followed by ": PASS,FLAKY". Example:
+# sample-test : PASS,FLAKY
+
+[true] # This section applies to all platforms
+
+[$system==win32]
+# /~https://github.com/nodejs/node/issues/23621
+test_threadsafe_function/test: PASS,FLAKY
diff --git a/test/addons-napi/test_general/test.js b/test/addons-napi/test_general/test.js
index 4faf508d5db145..03c284bae53869 100644
--- a/test/addons-napi/test_general/test.js
+++ b/test/addons-napi/test_general/test.js
@@ -33,8 +33,8 @@ assert.notStrictEqual(test_general.testGetPrototype(baseObject),
test_general.testGetPrototype(extendedObject));
// test version management functions
-// expected version is currently 3
-assert.strictEqual(test_general.testGetVersion(), 3);
+// expected version is currently 4
+assert.strictEqual(test_general.testGetVersion(), 4);
const [ major, minor, patch, release ] = test_general.testGetNodeVersion();
assert.strictEqual(process.version.split('-')[0],
diff --git a/test/addons-napi/test_string/test.js b/test/addons-napi/test_string/test.js
index 5ce3d739c7a941..86fdc1640602bc 100644
--- a/test/addons-napi/test_string/test.js
+++ b/test/addons-napi/test_string/test.js
@@ -73,3 +73,11 @@ assert.strictEqual(test_string.Utf8Length(str6), 14);
assert.throws(() => {
test_string.TestLargeUtf8();
}, /^Error: Invalid argument$/);
+
+assert.throws(() => {
+ test_string.TestLargeLatin1();
+}, /^Error: Invalid argument$/);
+
+assert.throws(() => {
+ test_string.TestLargeUtf16();
+}, /^Error: Invalid argument$/);
diff --git a/test/addons-napi/test_string/test_string.c b/test/addons-napi/test_string/test_string.c
index 99a3f7d3545a20..0f96112970eae1 100644
--- a/test/addons-napi/test_string/test_string.c
+++ b/test/addons-napi/test_string/test_string.c
@@ -215,6 +215,32 @@ napi_value TestLargeUtf8(napi_env env, napi_callback_info info) {
return output;
}
+static napi_value TestLargeLatin1(napi_env env, napi_callback_info info) {
+ napi_value output;
+ if (SIZE_MAX > INT_MAX) {
+ NAPI_CALL(env, napi_create_string_latin1(env, "", ((size_t)INT_MAX) + 1, &output));
+ } else {
+ // just throw the expected error as there is nothing to test
+ // in this case since we can't overflow
+ NAPI_CALL(env, napi_throw_error(env, NULL, "Invalid argument"));
+ }
+
+ return output;
+}
+
+static napi_value TestLargeUtf16(napi_env env, napi_callback_info info) {
+ napi_value output;
+ if (SIZE_MAX > INT_MAX) {
+ NAPI_CALL(env, napi_create_string_utf16(env, "", ((size_t)INT_MAX) + 1, &output));
+ } else {
+ // just throw the expected error as there is nothing to test
+ // in this case since we can't overflow
+ NAPI_CALL(env, napi_throw_error(env, NULL, "Invalid argument"));
+ }
+
+ return output;
+}
+
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor properties[] = {
DECLARE_NAPI_PROPERTY("TestLatin1", TestLatin1),
@@ -226,6 +252,8 @@ napi_value Init(napi_env env, napi_value exports) {
DECLARE_NAPI_PROPERTY("Utf16Length", Utf16Length),
DECLARE_NAPI_PROPERTY("Utf8Length", Utf8Length),
DECLARE_NAPI_PROPERTY("TestLargeUtf8", TestLargeUtf8),
+ DECLARE_NAPI_PROPERTY("TestLargeLatin1", TestLargeLatin1),
+ DECLARE_NAPI_PROPERTY("TestLargeUtf16", TestLargeUtf16),
};
NAPI_CALL(env, napi_define_properties(
diff --git a/test/addons-napi/test_threadsafe_function/binding.c b/test/addons-napi/test_threadsafe_function/binding.c
new file mode 100644
index 00000000000000..d78710cac1317a
--- /dev/null
+++ b/test/addons-napi/test_threadsafe_function/binding.c
@@ -0,0 +1,282 @@
+// For the purpose of this test we use libuv's threading library. When deciding
+// on a threading library for a new project it bears remembering that in the
+// future libuv may introduce API changes which may render it non-ABI-stable,
+// which, in turn, may affect the ABI stability of the project despite its use
+// of N-API.
+#include
+#include
+#include "../common.h"
+
+#define ARRAY_LENGTH 10
+#define MAX_QUEUE_SIZE 2
+
+static uv_thread_t uv_threads[2];
+static napi_threadsafe_function ts_fn;
+
+typedef struct {
+ napi_threadsafe_function_call_mode block_on_full;
+ napi_threadsafe_function_release_mode abort;
+ bool start_secondary;
+ napi_ref js_finalize_cb;
+ uint32_t max_queue_size;
+} ts_fn_hint;
+
+static ts_fn_hint ts_info;
+
+// Thread data to transmit to JS
+static int ints[ARRAY_LENGTH];
+
+static void secondary_thread(void* data) {
+ napi_threadsafe_function ts_fn = data;
+
+ if (napi_release_threadsafe_function(ts_fn, napi_tsfn_release) != napi_ok) {
+ napi_fatal_error("secondary_thread", NAPI_AUTO_LENGTH,
+ "napi_release_threadsafe_function failed", NAPI_AUTO_LENGTH);
+ }
+}
+
+// Source thread producing the data
+static void data_source_thread(void* data) {
+ napi_threadsafe_function ts_fn = data;
+ int index;
+ void* hint;
+ ts_fn_hint *ts_fn_info;
+ napi_status status;
+ bool queue_was_full = false;
+ bool queue_was_closing = false;
+
+ if (napi_get_threadsafe_function_context(ts_fn, &hint) != napi_ok) {
+ napi_fatal_error("data_source_thread", NAPI_AUTO_LENGTH,
+ "napi_get_threadsafe_function_context failed", NAPI_AUTO_LENGTH);
+ }
+
+ ts_fn_info = (ts_fn_hint *)hint;
+
+ if (ts_fn_info != &ts_info) {
+ napi_fatal_error("data_source_thread", NAPI_AUTO_LENGTH,
+ "thread-safe function hint is not as expected", NAPI_AUTO_LENGTH);
+ }
+
+ if (ts_fn_info->start_secondary) {
+ if (napi_acquire_threadsafe_function(ts_fn) != napi_ok) {
+ napi_fatal_error("data_source_thread", NAPI_AUTO_LENGTH,
+ "napi_acquire_threadsafe_function failed", NAPI_AUTO_LENGTH);
+ }
+
+ if (uv_thread_create(&uv_threads[1], secondary_thread, ts_fn) != 0) {
+ napi_fatal_error("data_source_thread", NAPI_AUTO_LENGTH,
+ "failed to start secondary thread", NAPI_AUTO_LENGTH);
+ }
+ }
+
+ for (index = ARRAY_LENGTH - 1; index > -1 && !queue_was_closing; index--) {
+ status = napi_call_threadsafe_function(ts_fn, &ints[index],
+ ts_fn_info->block_on_full);
+ if (ts_fn_info->max_queue_size == 0) {
+ // Let's make this thread really busy for 200 ms to give the main thread a
+ // chance to abort.
+ uint64_t start = uv_hrtime();
+ for (; uv_hrtime() - start < 200000000;);
+ }
+ switch (status) {
+ case napi_queue_full:
+ queue_was_full = true;
+ index++;
+ // fall through
+
+ case napi_ok:
+ continue;
+
+ case napi_closing:
+ queue_was_closing = true;
+ break;
+
+ default:
+ napi_fatal_error("data_source_thread", NAPI_AUTO_LENGTH,
+ "napi_call_threadsafe_function failed", NAPI_AUTO_LENGTH);
+ }
+ }
+
+ // Assert that the enqueuing of a value was refused at least once, if this is
+ // a non-blocking test run.
+ if (!ts_fn_info->block_on_full && !queue_was_full) {
+ napi_fatal_error("data_source_thread", NAPI_AUTO_LENGTH,
+ "queue was never full", NAPI_AUTO_LENGTH);
+ }
+
+ // Assert that the queue was marked as closing at least once, if this is an
+ // aborting test run.
+ if (ts_fn_info->abort == napi_tsfn_abort && !queue_was_closing) {
+ napi_fatal_error("data_source_thread", NAPI_AUTO_LENGTH,
+ "queue was never closing", NAPI_AUTO_LENGTH);
+ }
+
+ if (!queue_was_closing &&
+ napi_release_threadsafe_function(ts_fn, napi_tsfn_release) != napi_ok) {
+ napi_fatal_error("data_source_thread", NAPI_AUTO_LENGTH,
+ "napi_release_threadsafe_function failed", NAPI_AUTO_LENGTH);
+ }
+}
+
+// Getting the data into JS
+static void call_js(napi_env env, napi_value cb, void* hint, void* data) {
+ if (!(env == NULL || cb == NULL)) {
+ napi_value argv, undefined;
+ NAPI_CALL_RETURN_VOID(env, napi_create_int32(env, *(int*)data, &argv));
+ NAPI_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
+ NAPI_CALL_RETURN_VOID(env, napi_call_function(env, undefined, cb, 1, &argv,
+ NULL));
+ }
+}
+
+// Cleanup
+static napi_value StopThread(napi_env env, napi_callback_info info) {
+ size_t argc = 2;
+ napi_value argv[2];
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
+ napi_valuetype value_type;
+ NAPI_CALL(env, napi_typeof(env, argv[0], &value_type));
+ NAPI_ASSERT(env, value_type == napi_function,
+ "StopThread argument is a function");
+ NAPI_ASSERT(env, (ts_fn != NULL), "Existing threadsafe function");
+ NAPI_CALL(env,
+ napi_create_reference(env, argv[0], 1, &(ts_info.js_finalize_cb)));
+ bool abort;
+ NAPI_CALL(env, napi_get_value_bool(env, argv[1], &abort));
+ NAPI_CALL(env,
+ napi_release_threadsafe_function(ts_fn,
+ abort ? napi_tsfn_abort : napi_tsfn_release));
+ ts_fn = NULL;
+ return NULL;
+}
+
+// Join the thread and inform JS that we're done.
+static void join_the_threads(napi_env env, void *data, void *hint) {
+ uv_thread_t *the_threads = data;
+ ts_fn_hint *the_hint = hint;
+ napi_value js_cb, undefined;
+
+ uv_thread_join(&the_threads[0]);
+ if (the_hint->start_secondary) {
+ uv_thread_join(&the_threads[1]);
+ }
+
+ NAPI_CALL_RETURN_VOID(env,
+ napi_get_reference_value(env, the_hint->js_finalize_cb, &js_cb));
+ NAPI_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
+ NAPI_CALL_RETURN_VOID(env,
+ napi_call_function(env, undefined, js_cb, 0, NULL, NULL));
+ NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env,
+ the_hint->js_finalize_cb));
+}
+
+static napi_value StartThreadInternal(napi_env env,
+ napi_callback_info info,
+ napi_threadsafe_function_call_js cb,
+ bool block_on_full) {
+ size_t argc = 4;
+ napi_value argv[4];
+
+ ts_info.block_on_full =
+ (block_on_full ? napi_tsfn_blocking : napi_tsfn_nonblocking);
+
+ NAPI_ASSERT(env, (ts_fn == NULL), "Existing thread-safe function");
+ NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
+ napi_value async_name;
+ NAPI_CALL(env, napi_create_string_utf8(env, "N-API Thread-safe Function Test",
+ NAPI_AUTO_LENGTH, &async_name));
+ NAPI_CALL(env, napi_get_value_uint32(env, argv[3], &ts_info.max_queue_size));
+ NAPI_CALL(env, napi_create_threadsafe_function(env,
+ argv[0],
+ NULL,
+ async_name,
+ ts_info.max_queue_size,
+ 2,
+ uv_threads,
+ join_the_threads,
+ &ts_info,
+ cb,
+ &ts_fn));
+ bool abort;
+ NAPI_CALL(env, napi_get_value_bool(env, argv[1], &abort));
+ ts_info.abort = abort ? napi_tsfn_abort : napi_tsfn_release;
+ NAPI_CALL(env, napi_get_value_bool(env, argv[2], &(ts_info.start_secondary)));
+
+ NAPI_ASSERT(env,
+ (uv_thread_create(&uv_threads[0], data_source_thread, ts_fn) == 0),
+ "Thread creation");
+
+ return NULL;
+}
+
+static napi_value Unref(napi_env env, napi_callback_info info) {
+ NAPI_ASSERT(env, ts_fn != NULL, "No existing thread-safe function");
+ NAPI_CALL(env, napi_unref_threadsafe_function(env, ts_fn));
+ return NULL;
+}
+
+static napi_value Release(napi_env env, napi_callback_info info) {
+ NAPI_ASSERT(env, ts_fn != NULL, "No existing thread-safe function");
+ NAPI_CALL(env, napi_release_threadsafe_function(ts_fn, napi_tsfn_release));
+ return NULL;
+}
+
+// Startup
+static napi_value StartThread(napi_env env, napi_callback_info info) {
+ return StartThreadInternal(env, info, call_js, true);
+}
+
+static napi_value StartThreadNonblocking(napi_env env,
+ napi_callback_info info) {
+ return StartThreadInternal(env, info, call_js, false);
+}
+
+static napi_value StartThreadNoNative(napi_env env, napi_callback_info info) {
+ return StartThreadInternal(env, info, NULL, true);
+}
+
+// Module init
+static napi_value Init(napi_env env, napi_value exports) {
+ size_t index;
+ for (index = 0; index < ARRAY_LENGTH; index++) {
+ ints[index] = index;
+ }
+ napi_value js_array_length, js_max_queue_size;
+ napi_create_uint32(env, ARRAY_LENGTH, &js_array_length);
+ napi_create_uint32(env, MAX_QUEUE_SIZE, &js_max_queue_size);
+
+ napi_property_descriptor properties[] = {
+ {
+ "ARRAY_LENGTH",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ js_array_length,
+ napi_enumerable,
+ NULL
+ },
+ {
+ "MAX_QUEUE_SIZE",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ js_max_queue_size,
+ napi_enumerable,
+ NULL
+ },
+ DECLARE_NAPI_PROPERTY("StartThread", StartThread),
+ DECLARE_NAPI_PROPERTY("StartThreadNoNative", StartThreadNoNative),
+ DECLARE_NAPI_PROPERTY("StartThreadNonblocking", StartThreadNonblocking),
+ DECLARE_NAPI_PROPERTY("StopThread", StopThread),
+ DECLARE_NAPI_PROPERTY("Unref", Unref),
+ DECLARE_NAPI_PROPERTY("Release", Release),
+ };
+
+ NAPI_CALL(env, napi_define_properties(env, exports,
+ sizeof(properties)/sizeof(properties[0]), properties));
+
+ return exports;
+}
+NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
diff --git a/test/addons-napi/test_threadsafe_function/binding.gyp b/test/addons-napi/test_threadsafe_function/binding.gyp
new file mode 100644
index 00000000000000..b60352e05af103
--- /dev/null
+++ b/test/addons-napi/test_threadsafe_function/binding.gyp
@@ -0,0 +1,8 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'binding',
+ 'sources': ['binding.c']
+ }
+ ]
+}
diff --git a/test/addons-napi/test_threadsafe_function/test.js b/test/addons-napi/test_threadsafe_function/test.js
new file mode 100644
index 00000000000000..ac2549a8c08e5c
--- /dev/null
+++ b/test/addons-napi/test_threadsafe_function/test.js
@@ -0,0 +1,208 @@
+'use strict';
+
+const common = require('../../common');
+const assert = require('assert');
+const binding = require(`./build/${common.buildType}/binding`);
+const { fork } = require('child_process');
+const expectedArray = (function(arrayLength) {
+ const result = [];
+ for (let index = 0; index < arrayLength; index++) {
+ result.push(arrayLength - 1 - index);
+ }
+ return result;
+})(binding.ARRAY_LENGTH);
+
+common.crashOnUnhandledRejection();
+
+// Handle the rapid teardown test case as the child process. We unref the
+// thread-safe function after we have received two values. This causes the
+// process to exit and the environment cleanup handler to be invoked.
+if (process.argv[2] === 'child') {
+ let callCount = 0;
+ binding.StartThread((value) => {
+ callCount++;
+ console.log(value);
+ if (callCount === 2) {
+ binding.Unref();
+ }
+ }, false /* abort */, true /* launchSecondary */, +process.argv[3]);
+
+ // Release the thread-safe function from the main thread so that it may be
+ // torn down via the environment cleanup handler.
+ binding.Release();
+ return;
+}
+
+function testWithJSMarshaller({
+ threadStarter,
+ quitAfter,
+ abort,
+ maxQueueSize,
+ launchSecondary }) {
+ return new Promise((resolve) => {
+ const array = [];
+ binding[threadStarter](function testCallback(value) {
+ array.push(value);
+ if (array.length === quitAfter) {
+ setImmediate(() => {
+ binding.StopThread(common.mustCall(() => {
+ resolve(array);
+ }), !!abort);
+ });
+ }
+ }, !!abort, !!launchSecondary, maxQueueSize);
+ if (threadStarter === 'StartThreadNonblocking') {
+ // Let's make this thread really busy for a short while to ensure that
+ // the queue fills and the thread receives a napi_queue_full.
+ const start = Date.now();
+ while (Date.now() - start < 200);
+ }
+ });
+}
+
+function testUnref(queueSize) {
+ return new Promise((resolve, reject) => {
+ let output = '';
+ const child = fork(__filename, ['child', queueSize], {
+ stdio: [process.stdin, 'pipe', process.stderr, 'ipc']
+ });
+ child.on('close', (code) => {
+ if (code === 0) {
+ resolve(output.match(/\S+/g));
+ } else {
+ reject(new Error('Child process died with code ' + code));
+ }
+ });
+ child.stdout.on('data', (data) => (output += data.toString()));
+ })
+ .then((result) => assert.strictEqual(result.indexOf(0), -1));
+}
+
+new Promise(function testWithoutJSMarshaller(resolve) {
+ let callCount = 0;
+ binding.StartThreadNoNative(function testCallback() {
+ callCount++;
+
+ // The default call-into-JS implementation passes no arguments.
+ assert.strictEqual(arguments.length, 0);
+ if (callCount === binding.ARRAY_LENGTH) {
+ setImmediate(() => {
+ binding.StopThread(common.mustCall(() => {
+ resolve();
+ }), false);
+ });
+ }
+ }, false /* abort */, false /* launchSecondary */, binding.MAX_QUEUE_SIZE);
+})
+
+// Start the thread in blocking mode, and assert that all values are passed.
+// Quit after it's done.
+.then(() => testWithJSMarshaller({
+ threadStarter: 'StartThread',
+ maxQueueSize: binding.MAX_QUEUE_SIZE,
+ quitAfter: binding.ARRAY_LENGTH
+}))
+.then((result) => assert.deepStrictEqual(result, expectedArray))
+
+// Start the thread in blocking mode with an infinite queue, and assert that all
+// values are passed. Quit after it's done.
+.then(() => testWithJSMarshaller({
+ threadStarter: 'StartThread',
+ maxQueueSize: 0,
+ quitAfter: binding.ARRAY_LENGTH
+}))
+.then((result) => assert.deepStrictEqual(result, expectedArray))
+
+// Start the thread in non-blocking mode, and assert that all values are passed.
+// Quit after it's done.
+.then(() => testWithJSMarshaller({
+ threadStarter: 'StartThreadNonblocking',
+ maxQueueSize: binding.MAX_QUEUE_SIZE,
+ quitAfter: binding.ARRAY_LENGTH
+}))
+.then((result) => assert.deepStrictEqual(result, expectedArray))
+
+// Start the thread in blocking mode, and assert that all values are passed.
+// Quit early, but let the thread finish.
+.then(() => testWithJSMarshaller({
+ threadStarter: 'StartThread',
+ maxQueueSize: binding.MAX_QUEUE_SIZE,
+ quitAfter: 1
+}))
+.then((result) => assert.deepStrictEqual(result, expectedArray))
+
+// Start the thread in blocking mode with an infinite queue, and assert that all
+// values are passed. Quit early, but let the thread finish.
+.then(() => testWithJSMarshaller({
+ threadStarter: 'StartThread',
+ maxQueueSize: 0,
+ quitAfter: 1
+}))
+.then((result) => assert.deepStrictEqual(result, expectedArray))
+
+// Start the thread in non-blocking mode, and assert that all values are passed.
+// Quit early, but let the thread finish.
+.then(() => testWithJSMarshaller({
+ threadStarter: 'StartThreadNonblocking',
+ maxQueueSize: binding.MAX_QUEUE_SIZE,
+ quitAfter: 1
+}))
+.then((result) => assert.deepStrictEqual(result, expectedArray))
+
+// Start the thread in blocking mode, and assert that all values are passed.
+// Quit early, but let the thread finish. Launch a secondary thread to test the
+// reference counter incrementing functionality.
+.then(() => testWithJSMarshaller({
+ threadStarter: 'StartThread',
+ quitAfter: 1,
+ maxQueueSize: binding.MAX_QUEUE_SIZE,
+ launchSecondary: true
+}))
+.then((result) => assert.deepStrictEqual(result, expectedArray))
+
+// Start the thread in non-blocking mode, and assert that all values are passed.
+// Quit early, but let the thread finish. Launch a secondary thread to test the
+// reference counter incrementing functionality.
+.then(() => testWithJSMarshaller({
+ threadStarter: 'StartThreadNonblocking',
+ quitAfter: 1,
+ maxQueueSize: binding.MAX_QUEUE_SIZE,
+ launchSecondary: true
+}))
+.then((result) => assert.deepStrictEqual(result, expectedArray))
+
+// Start the thread in blocking mode, and assert that it could not finish.
+// Quit early by aborting.
+.then(() => testWithJSMarshaller({
+ threadStarter: 'StartThread',
+ quitAfter: 1,
+ maxQueueSize: binding.MAX_QUEUE_SIZE,
+ abort: true
+}))
+.then((result) => assert.strictEqual(result.indexOf(0), -1))
+
+// Start the thread in blocking mode with an infinite queue, and assert that it
+// could not finish. Quit early by aborting.
+.then(() => testWithJSMarshaller({
+ threadStarter: 'StartThread',
+ quitAfter: 1,
+ maxQueueSize: 0,
+ abort: true
+}))
+.then((result) => assert.strictEqual(result.indexOf(0), -1))
+
+// Start the thread in non-blocking mode, and assert that it could not finish.
+// Quit early and aborting.
+.then(() => testWithJSMarshaller({
+ threadStarter: 'StartThreadNonblocking',
+ quitAfter: 1,
+ maxQueueSize: binding.MAX_QUEUE_SIZE,
+ abort: true
+}))
+.then((result) => assert.strictEqual(result.indexOf(0), -1))
+
+// Start a child process to test rapid teardown
+.then(() => testUnref(binding.MAX_QUEUE_SIZE))
+
+// Start a child process with an infinite queue to test rapid teardown
+.then(() => testUnref(0));
diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status
index 8163bc672f34a5..bfb0f8289c850b 100644
--- a/test/parallel/parallel.status
+++ b/test/parallel/parallel.status
@@ -16,6 +16,8 @@ test-postmortem-metadata: PASS,FLAKY
[$arch==arm || $arch==arm64]
test-npm-install: PASS,FLAKY
+# /~https://github.com/nodejs/node/issues/25028
+test-cli-node-options: PASS,FLAKY
[$system==solaris] # Also applies to SmartOS
diff --git a/test/parallel/test-fs-utimes.js b/test/parallel/test-fs-utimes.js
index 3dc0bb59def6a2..ad83bc93653256 100644
--- a/test/parallel/test-fs-utimes.js
+++ b/test/parallel/test-fs-utimes.js
@@ -35,7 +35,11 @@ function stat_resource(resource) {
if (typeof resource === 'string') {
return fs.statSync(resource);
} else {
+ const stats = fs.fstatSync(resource);
// ensure mtime has been written to disk
+ // except for directories on AIX where it cannot be synced
+ if (common.isAIX && stats.isDirectory())
+ return stats;
fs.fsyncSync(resource);
return fs.fstatSync(resource);
}
diff --git a/test/parallel/test-http-max-header-size.js b/test/parallel/test-http-max-header-size.js
index 07fbe902963525..43dcbec5ab8967 100644
--- a/test/parallel/test-http-max-header-size.js
+++ b/test/parallel/test-http-max-header-size.js
@@ -1,6 +1,6 @@
'use strict';
-require('../common');
+const common = require('../common');
const assert = require('assert');
const { spawnSync } = require('child_process');
const http = require('http');
@@ -9,3 +9,16 @@ assert.strictEqual(http.maxHeaderSize, 8 * 1024);
const child = spawnSync(process.execPath, ['--max-http-header-size=10', '-p',
'http.maxHeaderSize']);
assert.strictEqual(+child.stdout.toString().trim(), 10);
+
+{
+ const server = http.createServer(common.mustNotCall());
+ server.listen(0, common.mustCall(() => {
+ http.get({
+ port: server.address().port,
+ headers: { foo: 'x'.repeat(http.maxHeaderSize + 1) }
+ }, common.mustNotCall()).once('error', common.mustCall((err) => {
+ assert.strictEqual(err.code, 'ECONNRESET');
+ server.close();
+ }));
+ }));
+}
diff --git a/test/parallel/test-http-parser-reuse-domain.js b/test/parallel/test-http-parser-reuse-domain.js
new file mode 100644
index 00000000000000..fd164f47d1e007
--- /dev/null
+++ b/test/parallel/test-http-parser-reuse-domain.js
@@ -0,0 +1,112 @@
+'use strict';
+
+const common = require('../common');
+const domain = require('domain');
+const http = require('http');
+
+const agent = new http.Agent({
+ // keepAlive is important here so that the underlying socket of all requests
+ // is reused and tied to the same domain.
+ keepAlive: true
+});
+
+const reqSockets = new Set();
+
+function performHttpRequest(opts, cb) {
+ const req = http.get({ port: server.address().port, agent }, (res) => {
+ if (!req.socket) {
+ console.log('missing socket property on req object, failing test');
+ markTestAsFailed();
+ req.abort();
+ cb(new Error('req.socket missing'));
+ return;
+ }
+
+ reqSockets.add(req.socket);
+
+ // Consume the data from the response to make sure the 'end' event is
+ // emitted.
+ res.on('data', function noop() {});
+ res.on('end', () => {
+ if (opts.shouldThrow) {
+ throw new Error('boom');
+ } else {
+ cb();
+ }
+ });
+
+ res.on('error', (resErr) => {
+ console.log('got error on response, failing test:', resErr);
+ markTestAsFailed();
+ });
+
+ }).on('error', (reqErr) => {
+ console.log('got error on request, failing test:', reqErr);
+ markTestAsFailed();
+ });
+
+ req.end();
+}
+
+function performHttpRequestInNewDomain(opts, cb) {
+ const d = domain.create();
+ d.run(() => {
+ performHttpRequest(opts, cb);
+ });
+
+ return d;
+}
+
+const server = http.createServer((req, res) => {
+ res.end();
+});
+
+server.listen(0, common.mustCall(function() {
+ const d1 = performHttpRequestInNewDomain({
+ shouldThrow: false
+ }, common.mustCall((firstReqErr) => {
+ if (firstReqErr) {
+ markTestAsFailed();
+ return;
+ }
+ // We want to schedule the second request on the next turn of the event
+ // loop so that the parser from the first request is actually reused.
+ setImmediate(common.mustCall(() => {
+ const d2 = performHttpRequestInNewDomain({
+ shouldThrow: true
+ }, (secondReqErr) => {
+ // Since this request throws before its callback has the chance
+ // to be called, we mark the test as failed if this callback is
+ // called.
+ console.log('second request\'s cb called unexpectedly, failing test');
+ markTestAsFailed();
+ });
+
+ // The second request throws when its response's end event is
+ // emitted. So we expect its domain to emit an error event.
+ d2.on('error', common.mustCall((d2err) => {
+ server.close();
+ if (reqSockets.size !== 1) {
+ console.log('more than 1 socket used for both requests, failing ' +
+ 'test');
+ markTestAsFailed();
+ }
+ }, 1));
+ }));
+ }));
+
+ d1.on('error', (d1err) => {
+ // d1 is the domain attached to the first request, which doesn't throw,
+ // so we don't expect its error handler to be called.
+ console.log('first request\'s domain\'s error handler called, ' +
+ 'failing test');
+ markTestAsFailed();
+ });
+}));
+
+function markTestAsFailed() {
+ if (server) {
+ server.close();
+ }
+ process.exitCode = 1;
+}
diff --git a/test/parallel/test-module-loading-error.js b/test/parallel/test-module-loading-error.js
index 68f45d774e5ce0..9747f58fe5cd45 100644
--- a/test/parallel/test-module-loading-error.js
+++ b/test/parallel/test-module-loading-error.js
@@ -30,7 +30,8 @@ const errorMessagesByPlatform = {
sunos: ['unknown file type', 'not an ELF file'],
darwin: ['file too short'],
aix: ['Cannot load module',
- 'Cannot run a file that does not have a valid format.']
+ 'Cannot run a file that does not have a valid format.',
+ 'Exec format error']
};
// If we don't know a priori what the error would be, we accept anything.
const errorMessages = errorMessagesByPlatform[process.platform] || [''];
diff --git a/test/parallel/test-repl-envvars.js b/test/parallel/test-repl-envvars.js
index d29e7b3574c1f2..c4efd184c4c91c 100644
--- a/test/parallel/test-repl-envvars.js
+++ b/test/parallel/test-repl-envvars.js
@@ -36,7 +36,7 @@ const tests = [
];
function run(test) {
- const env = Object.assign({}, process.env, test.env);
+ const env = test.env;
const expected = test.expected;
const opts = {
terminal: true,
diff --git a/test/parallel/test-stdout-cannot-be-closed-child-process-pipe.js b/test/parallel/test-stdout-cannot-be-closed-child-process-pipe.js
index d19b522e290ba9..7cd4b90c008a2f 100644
--- a/test/parallel/test-stdout-cannot-be-closed-child-process-pipe.js
+++ b/test/parallel/test-stdout-cannot-be-closed-child-process-pipe.js
@@ -24,9 +24,9 @@ function parent() {
});
child.on('close', function(code, signal) {
- assert(code);
+ assert.strictEqual(code, 0);
+ assert.strictEqual(err, '');
assert.strictEqual(out, 'foo');
- assert(/process\.stdout cannot be closed/.test(err));
console.log('ok');
});
}
diff --git a/test/parallel/test-tls-async-cb-after-socket-end-securepair.js b/test/parallel/test-tls-async-cb-after-socket-end-securepair.js
new file mode 100644
index 00000000000000..890c2b63ef5c21
--- /dev/null
+++ b/test/parallel/test-tls-async-cb-after-socket-end-securepair.js
@@ -0,0 +1,80 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+const fixtures = require('../common/fixtures');
+const SSL_OP_NO_TICKET = require('crypto').constants.SSL_OP_NO_TICKET;
+const assert = require('assert');
+const net = require('net');
+const tls = require('tls');
+
+const options = {
+ secureOptions: SSL_OP_NO_TICKET,
+ key: fixtures.readSync('test_key.pem'),
+ cert: fixtures.readSync('test_cert.pem')
+};
+
+const server = net.createServer(function(socket) {
+ const sslcontext = tls.createSecureContext(options);
+ sslcontext.context.setCiphers('RC4-SHA:AES128-SHA:AES256-SHA');
+
+ const pair = tls.createSecurePair(sslcontext, true, false, false, { server });
+
+ assert.ok(pair.encrypted.writable);
+ assert.ok(pair.cleartext.writable);
+
+ pair.encrypted.pipe(socket);
+ socket.pipe(pair.encrypted);
+
+ pair.on('error', () => {}); // Expected, client s1 closes connection.
+});
+
+let sessionCb = null;
+let client = null;
+
+server.on('newSession', common.mustCall(function(key, session, done) {
+ done();
+}));
+
+server.on('resumeSession', common.mustCall(function(id, cb) {
+ sessionCb = cb;
+
+ next();
+}));
+
+server.listen(0, function() {
+ const clientOpts = {
+ port: this.address().port,
+ rejectUnauthorized: false,
+ session: false
+ };
+
+ const s1 = tls.connect(clientOpts, function() {
+ clientOpts.session = s1.getSession();
+ console.log('1st secure');
+
+ s1.destroy();
+ const s2 = tls.connect(clientOpts, function(s) {
+ console.log('2nd secure');
+
+ s2.destroy();
+ }).on('connect', function() {
+ console.log('2nd connected');
+ client = s2;
+
+ next();
+ });
+ });
+});
+
+function next() {
+ if (!client || !sessionCb)
+ return;
+
+ client.destroy();
+ setTimeout(function() {
+ sessionCb();
+ server.close();
+ }, 100);
+}
diff --git a/test/pseudo-tty/test-stdin-write.js b/test/pseudo-tty/test-stdin-write.js
new file mode 100644
index 00000000000000..39091f20bbb745
--- /dev/null
+++ b/test/pseudo-tty/test-stdin-write.js
@@ -0,0 +1,3 @@
+'use strict';
+require('../common');
+process.stdin.end('foobar\n');
diff --git a/test/pseudo-tty/test-stdin-write.out b/test/pseudo-tty/test-stdin-write.out
new file mode 100644
index 00000000000000..323fae03f4606e
--- /dev/null
+++ b/test/pseudo-tty/test-stdin-write.out
@@ -0,0 +1 @@
+foobar
diff --git a/test/pseudo-tty/test-stdout-read.in b/test/pseudo-tty/test-stdout-read.in
new file mode 100644
index 00000000000000..10ddd6d257e013
--- /dev/null
+++ b/test/pseudo-tty/test-stdout-read.in
@@ -0,0 +1 @@
+Hello!
diff --git a/test/pseudo-tty/test-stdout-read.js b/test/pseudo-tty/test-stdout-read.js
new file mode 100644
index 00000000000000..90f017ed77b7be
--- /dev/null
+++ b/test/pseudo-tty/test-stdout-read.js
@@ -0,0 +1,3 @@
+'use strict';
+const common = require('../common');
+process.stderr.on('data', common.mustCall(console.log));
diff --git a/test/pseudo-tty/test-stdout-read.out b/test/pseudo-tty/test-stdout-read.out
new file mode 100644
index 00000000000000..3b7fda223d0e6c
--- /dev/null
+++ b/test/pseudo-tty/test-stdout-read.out
@@ -0,0 +1 @@
+
diff --git a/test/pseudo-tty/test-tty-stdin-call-end.js b/test/pseudo-tty/test-tty-stdin-call-end.js
new file mode 100644
index 00000000000000..e3c3fd9af469ba
--- /dev/null
+++ b/test/pseudo-tty/test-tty-stdin-call-end.js
@@ -0,0 +1,8 @@
+'use strict';
+
+require('../common');
+
+// This tests verifies that process.stdin.end() does not
+// crash the process with ENOTCONN
+
+process.stdin.end();
diff --git a/test/pseudo-tty/test-tty-stdin-call-end.out b/test/pseudo-tty/test-tty-stdin-call-end.out
new file mode 100644
index 00000000000000..e69de29bb2d1d6
diff --git a/test/pseudo-tty/testcfg.py b/test/pseudo-tty/testcfg.py
index 40396db247e279..c6c93c13b98340 100644
--- a/test/pseudo-tty/testcfg.py
+++ b/test/pseudo-tty/testcfg.py
@@ -35,10 +35,11 @@
class TTYTestCase(test.TestCase):
- def __init__(self, path, file, expected, arch, mode, context, config):
+ def __init__(self, path, file, expected, input, arch, mode, context, config):
super(TTYTestCase, self).__init__(context, path, arch, mode)
self.file = file
self.expected = expected
+ self.input = input
self.config = config
self.arch = arch
self.mode = mode
@@ -104,12 +105,16 @@ def GetSource(self):
+ open(self.expected).read())
def RunCommand(self, command, env):
+ input = None
+ if self.input is not None and exists(self.input):
+ input = open(self.input).read()
full_command = self.context.processor(command)
output = test.Execute(full_command,
self.context,
self.context.GetTimeout(self.mode),
env,
- True)
+ faketty=True,
+ input=input)
self.Cleanup()
return test.TestOutput(self,
full_command,
@@ -140,11 +145,12 @@ def ListTests(self, current_path, path, arch, mode):
if self.Contains(path, test):
file_prefix = join(self.root, reduce(join, test[1:], ""))
file_path = file_prefix + ".js"
+ input_path = file_prefix + ".in"
output_path = file_prefix + ".out"
if not exists(output_path):
raise Exception("Could not find %s" % output_path)
result.append(TTYTestCase(test, file_path, output_path,
- arch, mode, self.context, self))
+ input_path, arch, mode, self.context, self))
return result
def GetBuildRequirements(self):
diff --git a/tools/doc/common.js b/tools/doc/common.js
index 553b52935fd709..1a734acde719af 100644
--- a/tools/doc/common.js
+++ b/tools/doc/common.js
@@ -24,6 +24,10 @@ function extractAndParseYAML(text) {
meta.added = arrify(meta.added);
}
+ if (meta.napiVersion) {
+ meta.napiVersion = arrify(meta.napiVersion);
+ }
+
if (meta.deprecated) {
// Treat deprecated like added for consistency.
meta.deprecated = arrify(meta.deprecated);
diff --git a/tools/doc/html.js b/tools/doc/html.js
index f373206760b5df..d8da7e5364e8e1 100644
--- a/tools/doc/html.js
+++ b/tools/doc/html.js
@@ -365,6 +365,10 @@ function parseYAML(text) {
html.push(`${added.description}${deprecated.description}`);
}
+ if (meta.napiVersion) {
+ html.push(`N-API version: ${meta.napiVersion.join(', ')}\n`);
+ }
+
html.push('');
return html.join('\n');
}
diff --git a/tools/test.py b/tools/test.py
index dafcc3d2ad4122..a51b475b1f23cd 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -717,12 +717,23 @@ def CheckedUnlink(name):
PrintError("os.unlink() " + str(e))
break
-def Execute(args, context, timeout=None, env={}, faketty=False, disable_core_files=False):
+def Execute(args, context, timeout=None, env={}, faketty=False, disable_core_files=False, input=None):
if faketty:
import pty
(out_master, fd_out) = pty.openpty()
fd_in = fd_err = fd_out
pty_out = out_master
+
+ if input is not None:
+ # Before writing input data, disable echo so the input doesn't show
+ # up as part of the output.
+ import termios
+ attr = termios.tcgetattr(fd_in)
+ attr[3] = attr[3] & ~termios.ECHO
+ termios.tcsetattr(fd_in, termios.TCSADRAIN, attr)
+
+ os.write(pty_out, input)
+ os.write(pty_out, '\x04') # End-of-file marker (Ctrl+D)
else:
(fd_out, outname) = tempfile.mkstemp()
(fd_err, errname) = tempfile.mkstemp()
diff --git a/vcbuild.bat b/vcbuild.bat
index 43962324857bed..7143d841a4e2bc 100644
--- a/vcbuild.bat
+++ b/vcbuild.bat
@@ -474,6 +474,7 @@ if errorlevel 1 goto exit
if "%test_args%"=="" goto test-v8
if "%config%"=="Debug" set test_args=--mode=debug %test_args%
if "%config%"=="Release" set test_args=--mode=release %test_args%
+if not exist %config%\cctest.exe goto run-test-py
echo running 'cctest %cctest_args%'
"%config%\cctest" %cctest_args%
REM when building a static library there's no binary to run tests
|