From a6363389456e19448b9507271b68ccf3f40a3f98 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Fri, 19 Apr 2019 12:50:38 +0800 Subject: [PATCH 01/49] src: allow creating NodeMainInstance that does not own the isolate Allows instantiating a NodeMainInstance with an isolate whose initialization and disposal are controlled by the caller. PR-URL: https://github.com/nodejs/node/pull/27321 Refs: https://github.com/nodejs/node/issues/17058 Reviewed-By: Anna Henningsen Reviewed-By: Refael Ackermann Reviewed-By: James M Snell Reviewed-By: Ben Noordhuis --- src/api/environment.cc | 36 +++++++++++++++------ src/node.cc | 8 +++-- src/node_internals.h | 3 ++ src/node_main_instance.cc | 67 +++++++++++++++++++++++++++++++-------- src/node_main_instance.h | 48 ++++++++++++++++++++++++---- 5 files changed, 131 insertions(+), 31 deletions(-) diff --git a/src/api/environment.cc b/src/api/environment.cc index 0543e0323d8ee2..9a29ad1e5e1ff0 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -183,17 +183,33 @@ void SetIsolateCreateParamsForNode(Isolate::CreateParams* params) { #endif } +void SetIsolateUpForNode(v8::Isolate* isolate, IsolateSettingCategories cat) { + switch (cat) { + case IsolateSettingCategories::kErrorHandlers: + isolate->AddMessageListenerWithErrorLevel( + OnMessage, + Isolate::MessageErrorLevel::kMessageError | + Isolate::MessageErrorLevel::kMessageWarning); + isolate->SetAbortOnUncaughtExceptionCallback( + ShouldAbortOnUncaughtException); + isolate->SetFatalErrorHandler(OnFatalError); + break; + case IsolateSettingCategories::kMisc: + isolate->SetMicrotasksPolicy(MicrotasksPolicy::kExplicit); + isolate->SetAllowWasmCodeGenerationCallback( + AllowWasmCodeGenerationCallback); + isolate->SetPromiseRejectCallback(task_queue::PromiseRejectCallback); + v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate); + break; + default: + UNREACHABLE(); + break; + } +} + void SetIsolateUpForNode(v8::Isolate* isolate) { - isolate->AddMessageListenerWithErrorLevel( - OnMessage, - Isolate::MessageErrorLevel::kMessageError | - Isolate::MessageErrorLevel::kMessageWarning); - isolate->SetAbortOnUncaughtExceptionCallback(ShouldAbortOnUncaughtException); - isolate->SetMicrotasksPolicy(MicrotasksPolicy::kExplicit); - isolate->SetFatalErrorHandler(OnFatalError); - isolate->SetAllowWasmCodeGenerationCallback(AllowWasmCodeGenerationCallback); - isolate->SetPromiseRejectCallback(task_queue::PromiseRejectCallback); - v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate); + SetIsolateUpForNode(isolate, IsolateSettingCategories::kErrorHandlers); + SetIsolateUpForNode(isolate, IsolateSettingCategories::kMisc); } Isolate* NewIsolate(ArrayBufferAllocator* allocator, uv_loop_t* event_loop) { diff --git a/src/node.cc b/src/node.cc index 061d6bb424431f..9e969d33dc1e9f 100644 --- a/src/node.cc +++ b/src/node.cc @@ -886,8 +886,12 @@ int Start(int argc, char** argv) { } { - NodeMainInstance main_instance( - uv_default_loop(), result.args, result.exec_args); + Isolate::CreateParams params; + NodeMainInstance main_instance(¶ms, + uv_default_loop(), + per_process::v8_platform.Platform(), + result.args, + result.exec_args); result.exit_code = main_instance.Run(); } diff --git a/src/node_internals.h b/src/node_internals.h index 3fc6ea07361a27..fc924e3e1637ce 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -319,6 +319,9 @@ struct InitializationResult { }; InitializationResult InitializeOncePerProcess(int argc, char** argv); void TearDownOncePerProcess(); +enum class IsolateSettingCategories { kErrorHandlers, kMisc }; +void SetIsolateUpForNode(v8::Isolate* isolate, IsolateSettingCategories cat); +void SetIsolateCreateParamsForNode(v8::Isolate::CreateParams* params); #if HAVE_INSPECTOR namespace profiler { diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc index 6ef992d006f79c..4b05149b23c163 100644 --- a/src/node_main_instance.cc +++ b/src/node_main_instance.cc @@ -12,30 +12,70 @@ using v8::Local; using v8::Locker; using v8::SealHandleScope; -NodeMainInstance::NodeMainInstance(uv_loop_t* event_loop, +NodeMainInstance::NodeMainInstance(Isolate* isolate, + uv_loop_t* event_loop, + MultiIsolatePlatform* platform, + const std::vector& args, + const std::vector& exec_args) + : args_(args), + exec_args_(exec_args), + array_buffer_allocator_(nullptr), + isolate_(isolate), + platform_(platform), + isolate_data_(nullptr), + owns_isolate_(false) { + isolate_data_.reset(new IsolateData(isolate_, event_loop, platform, nullptr)); + SetIsolateUpForNode(isolate_, IsolateSettingCategories::kMisc); +} + +NodeMainInstance* NodeMainInstance::Create( + Isolate* isolate, + uv_loop_t* event_loop, + MultiIsolatePlatform* platform, + const std::vector& args, + const std::vector& exec_args) { + return new NodeMainInstance(isolate, event_loop, platform, args, exec_args); +} + +NodeMainInstance::NodeMainInstance(Isolate::CreateParams* params, + uv_loop_t* event_loop, + MultiIsolatePlatform* platform, const std::vector& args, const std::vector& exec_args) : args_(args), exec_args_(exec_args), array_buffer_allocator_(ArrayBufferAllocator::Create()), isolate_(nullptr), - isolate_data_(nullptr) { - // TODO(joyeecheung): when we implement snapshot integration this needs to - // set params.external_references. - Isolate::CreateParams params; - params.array_buffer_allocator = array_buffer_allocator_.get(); - isolate_ = - NewIsolate(¶ms, event_loop, per_process::v8_platform.Platform()); + platform_(platform), + isolate_data_(nullptr), + owns_isolate_(true) { + params->array_buffer_allocator = array_buffer_allocator_.get(); + isolate_ = Isolate::Allocate(); CHECK_NOT_NULL(isolate_); - isolate_data_.reset(CreateIsolateData(isolate_, - event_loop, - per_process::v8_platform.Platform(), - array_buffer_allocator_.get())); + // Register the isolate on the platform before the isolate gets initialized, + // so that the isolate can access the platform during initialization. + platform->RegisterIsolate(isolate_, event_loop); + SetIsolateCreateParamsForNode(params); + Isolate::Initialize(isolate_, *params); + + isolate_data_.reset(new IsolateData( + isolate_, event_loop, platform, array_buffer_allocator_.get())); + SetIsolateUpForNode(isolate_, IsolateSettingCategories::kMisc); + SetIsolateUpForNode(isolate_, IsolateSettingCategories::kErrorHandlers); +} + +void NodeMainInstance::Dispose() { + CHECK(!owns_isolate_); + platform_->DrainTasks(isolate_); + delete this; } NodeMainInstance::~NodeMainInstance() { + if (!owns_isolate_) { + return; + } isolate_->Dispose(); - per_process::v8_platform.Platform()->UnregisterIsolate(isolate_); + platform_->UnregisterIsolate(isolate_); } int NodeMainInstance::Run() { @@ -120,6 +160,7 @@ std::unique_ptr NodeMainInstance::CreateMainEnvironment( } Local context = NewContext(isolate_); + CHECK(!context.IsEmpty()); Context::Scope context_scope(context); std::unique_ptr env = std::make_unique( diff --git a/src/node_main_instance.h b/src/node_main_instance.h index f140edcfd72277..0a8be137a0d3ee 100644 --- a/src/node_main_instance.h +++ b/src/node_main_instance.h @@ -3,6 +3,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include #include "node.h" #include "util.h" #include "uv.h" @@ -14,12 +15,35 @@ namespace node { // We may be able to create an abstract class to reuse some of the routines. class NodeMainInstance { public: - NodeMainInstance(const NodeMainInstance&) = delete; - NodeMainInstance& operator=(const NodeMainInstance&) = delete; - NodeMainInstance(NodeMainInstance&&) = delete; - NodeMainInstance& operator=(NodeMainInstance&&) = delete; + // To create a main instance that does not own the isoalte, + // The caller needs to do: + // + // Isolate* isolate = Isolate::Allocate(); + // platform->RegisterIsolate(isolate, loop); + // isolate->Initialize(...); + // isolate->Enter(); + // NodeMainInstance* main_instance = + // NodeMainInstance::Create(isolate, loop, args, exec_args); + // + // When tearing it down: + // + // main_instance->Cleanup(); // While the isolate is entered + // isolate->Exit(); + // isolate->Dispose(); + // platform->UnregisterIsolate(isolate); + // + // After calling Dispose() the main_instance is no longer accessible. + static NodeMainInstance* Create(v8::Isolate* isolate, + uv_loop_t* event_loop, + MultiIsolatePlatform* platform, + const std::vector& args, + const std::vector& exec_args); + void Dispose(); - NodeMainInstance(uv_loop_t* event_loop, + // Create a main instance that owns the isolate + NodeMainInstance(v8::Isolate::CreateParams* params, + uv_loop_t* event_loop, + MultiIsolatePlatform* platform, const std::vector& args, const std::vector& exec_args); ~NodeMainInstance(); @@ -27,16 +51,28 @@ class NodeMainInstance { // Start running the Node.js instances, return the exit code when finished. int Run(); - private: // TODO(joyeecheung): align this with the CreateEnvironment exposed in node.h // and the environment creation routine in workers somehow. std::unique_ptr CreateMainEnvironment(int* exit_code); + private: + NodeMainInstance(const NodeMainInstance&) = delete; + NodeMainInstance& operator=(const NodeMainInstance&) = delete; + NodeMainInstance(NodeMainInstance&&) = delete; + NodeMainInstance& operator=(NodeMainInstance&&) = delete; + + NodeMainInstance(v8::Isolate* isolate, + uv_loop_t* event_loop, + MultiIsolatePlatform* platform, + const std::vector& args, + const std::vector& exec_args); std::vector args_; std::vector exec_args_; std::unique_ptr array_buffer_allocator_; v8::Isolate* isolate_; + MultiIsolatePlatform* platform_; std::unique_ptr isolate_data_; + bool owns_isolate_ = false; }; } // namespace node From 631bea8fd2c3e9a344a8e2c53b6c48007c650685 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 18 Apr 2019 16:25:32 +0800 Subject: [PATCH 02/49] src: implement IsolateData serialization and deserialization This patch allows serializing per-isolate data into an isolate snapshot and deserializing them from an isolate snapthot. PR-URL: https://github.com/nodejs/node/pull/27321 Refs: https://github.com/nodejs/node/issues/17058 Reviewed-By: Anna Henningsen Reviewed-By: Refael Ackermann Reviewed-By: James M Snell Reviewed-By: Ben Noordhuis --- src/env.cc | 147 +++++++++++++++++++++++++++----------- src/env.h | 8 ++- src/node_main_instance.cc | 25 +++++-- src/node_main_instance.h | 19 ++++- 4 files changed, 149 insertions(+), 50 deletions(-) diff --git a/src/env.cc b/src/env.cc index cbb8ab6fe27c02..5ef32e2b0ef80c 100644 --- a/src/env.cc +++ b/src/env.cc @@ -37,6 +37,7 @@ using v8::NewStringType; using v8::Number; using v8::Object; using v8::Private; +using v8::SnapshotCreator; using v8::StackTrace; using v8::String; using v8::Symbol; @@ -49,22 +50,58 @@ int const Environment::kNodeContextTag = 0x6e6f64; void* const Environment::kNodeContextTagPtr = const_cast( static_cast(&Environment::kNodeContextTag)); -IsolateData::IsolateData(Isolate* isolate, - uv_loop_t* event_loop, - MultiIsolatePlatform* platform, - ArrayBufferAllocator* node_allocator) - : isolate_(isolate), - event_loop_(event_loop), - allocator_(isolate->GetArrayBufferAllocator()), - node_allocator_(node_allocator == nullptr ? - nullptr : node_allocator->GetImpl()), - uses_node_allocator_(allocator_ == node_allocator_), - platform_(platform) { - CHECK_NOT_NULL(allocator_); +std::vector IsolateData::Serialize(SnapshotCreator* creator) { + Isolate* isolate = creator->GetIsolate(); + std::vector indexes; + HandleScope handle_scope(isolate); + // XXX(joyeecheung): technically speaking, the indexes here should be + // consecutive and we could just return a range instead of an array, + // but that's not part of the V8 API contract so we use an array + // just to be safe. + +#define VP(PropertyName, StringValue) V(v8::Private, PropertyName) +#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName) +#define VS(PropertyName, StringValue) V(v8::String, PropertyName) +#define V(TypeName, PropertyName) \ + indexes.push_back(creator->AddData(PropertyName##_.Get(isolate))); + PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) + PER_ISOLATE_SYMBOL_PROPERTIES(VY) + PER_ISOLATE_STRING_PROPERTIES(VS) +#undef V +#undef VY +#undef VS +#undef VP - options_.reset( - new PerIsolateOptions(*(per_process::cli_options->per_isolate))); + return indexes; +} +void IsolateData::DeserializeProperties( + const NodeMainInstance::IndexArray* indexes) { + size_t i = 0; + HandleScope handle_scope(isolate_); + +#define VP(PropertyName, StringValue) V(v8::Private, PropertyName) +#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName) +#define VS(PropertyName, StringValue) V(v8::String, PropertyName) +#define V(TypeName, PropertyName) \ + do { \ + MaybeLocal field = \ + isolate_->GetDataFromSnapshotOnce(indexes->Get(i++)); \ + if (field.IsEmpty()) { \ + fprintf(stderr, "Failed to deserialize " #PropertyName "\n"); \ + } \ + PropertyName##_.Set(isolate_, field.ToLocalChecked()); \ + } while (0); + PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) + PER_ISOLATE_SYMBOL_PROPERTIES(VY) + PER_ISOLATE_STRING_PROPERTIES(VS) +#undef V +#undef VY +#undef VS +#undef VP +} + +void IsolateData::CreateProperties() { // Create string and private symbol properties as internalized one byte // strings after the platform is properly initialized. // @@ -76,44 +113,68 @@ IsolateData::IsolateData(Isolate* isolate, // One byte because our strings are ASCII and we can safely skip V8's UTF-8 // decoding step. - HandleScope handle_scope(isolate); + HandleScope handle_scope(isolate_); -#define V(PropertyName, StringValue) \ - PropertyName ## _.Set( \ - isolate, \ - Private::New( \ - isolate, \ - String::NewFromOneByte( \ - isolate, \ - reinterpret_cast(StringValue), \ - NewStringType::kInternalized, \ - sizeof(StringValue) - 1).ToLocalChecked())); +#define V(PropertyName, StringValue) \ + PropertyName##_.Set( \ + isolate_, \ + Private::New(isolate_, \ + String::NewFromOneByte( \ + isolate_, \ + reinterpret_cast(StringValue), \ + NewStringType::kInternalized, \ + sizeof(StringValue) - 1) \ + .ToLocalChecked())); PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V) #undef V -#define V(PropertyName, StringValue) \ - PropertyName ## _.Set( \ - isolate, \ - Symbol::New( \ - isolate, \ - String::NewFromOneByte( \ - isolate, \ - reinterpret_cast(StringValue), \ - NewStringType::kInternalized, \ - sizeof(StringValue) - 1).ToLocalChecked())); +#define V(PropertyName, StringValue) \ + PropertyName##_.Set( \ + isolate_, \ + Symbol::New(isolate_, \ + String::NewFromOneByte( \ + isolate_, \ + reinterpret_cast(StringValue), \ + NewStringType::kInternalized, \ + sizeof(StringValue) - 1) \ + .ToLocalChecked())); PER_ISOLATE_SYMBOL_PROPERTIES(V) #undef V -#define V(PropertyName, StringValue) \ - PropertyName ## _.Set( \ - isolate, \ - String::NewFromOneByte( \ - isolate, \ - reinterpret_cast(StringValue), \ - NewStringType::kInternalized, \ - sizeof(StringValue) - 1).ToLocalChecked()); +#define V(PropertyName, StringValue) \ + PropertyName##_.Set( \ + isolate_, \ + String::NewFromOneByte(isolate_, \ + reinterpret_cast(StringValue), \ + NewStringType::kInternalized, \ + sizeof(StringValue) - 1) \ + .ToLocalChecked()); PER_ISOLATE_STRING_PROPERTIES(V) #undef V } +IsolateData::IsolateData(Isolate* isolate, + uv_loop_t* event_loop, + MultiIsolatePlatform* platform, + ArrayBufferAllocator* node_allocator, + const NodeMainInstance::IndexArray* indexes) + : isolate_(isolate), + event_loop_(event_loop), + allocator_(isolate->GetArrayBufferAllocator()), + node_allocator_(node_allocator == nullptr ? nullptr + : node_allocator->GetImpl()), + uses_node_allocator_(allocator_ == node_allocator_), + platform_(platform) { + CHECK_NOT_NULL(allocator_); + + options_.reset( + new PerIsolateOptions(*(per_process::cli_options->per_isolate))); + + if (indexes == nullptr) { + CreateProperties(); + } else { + DeserializeProperties(indexes); + } +} + void IsolateData::MemoryInfo(MemoryTracker* tracker) const { #define V(PropertyName, StringValue) \ tracker->TrackField(#PropertyName, PropertyName(isolate())); diff --git a/src/env.h b/src/env.h index def7cba3886ac2..24050cc9f4404f 100644 --- a/src/env.h +++ b/src/env.h @@ -33,6 +33,7 @@ #include "node.h" #include "node_binding.h" #include "node_http2_state.h" +#include "node_main_instance.h" #include "node_options.h" #include "req_wrap.h" #include "util.h" @@ -418,10 +419,12 @@ class IsolateData : public MemoryRetainer { IsolateData(v8::Isolate* isolate, uv_loop_t* event_loop, MultiIsolatePlatform* platform = nullptr, - ArrayBufferAllocator* node_allocator = nullptr); + ArrayBufferAllocator* node_allocator = nullptr, + const NodeMainInstance::IndexArray* indexes = nullptr); SET_MEMORY_INFO_NAME(IsolateData); SET_SELF_SIZE(IsolateData); void MemoryInfo(MemoryTracker* tracker) const override; + std::vector Serialize(v8::SnapshotCreator* creator); inline uv_loop_t* event_loop() const; inline MultiIsolatePlatform* platform() const; @@ -451,6 +454,9 @@ class IsolateData : public MemoryRetainer { IsolateData& operator=(const IsolateData&) = delete; private: + void DeserializeProperties(const NodeMainInstance::IndexArray* indexes); + void CreateProperties(); + #define VP(PropertyName, StringValue) V(v8::Private, PropertyName) #define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName) #define VS(PropertyName, StringValue) V(v8::String, PropertyName) diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc index 4b05149b23c163..11dfadabb9422d 100644 --- a/src/node_main_instance.cc +++ b/src/node_main_instance.cc @@ -23,7 +23,8 @@ NodeMainInstance::NodeMainInstance(Isolate* isolate, isolate_(isolate), platform_(platform), isolate_data_(nullptr), - owns_isolate_(false) { + owns_isolate_(false), + deserialize_mode_(false) { isolate_data_.reset(new IsolateData(isolate_, event_loop, platform, nullptr)); SetIsolateUpForNode(isolate_, IsolateSettingCategories::kMisc); } @@ -41,7 +42,8 @@ NodeMainInstance::NodeMainInstance(Isolate::CreateParams* params, uv_loop_t* event_loop, MultiIsolatePlatform* platform, const std::vector& args, - const std::vector& exec_args) + const std::vector& exec_args, + const IndexArray* per_isolate_data_indexes) : args_(args), exec_args_(exec_args), array_buffer_allocator_(ArrayBufferAllocator::Create()), @@ -58,10 +60,20 @@ NodeMainInstance::NodeMainInstance(Isolate::CreateParams* params, SetIsolateCreateParamsForNode(params); Isolate::Initialize(isolate_, *params); - isolate_data_.reset(new IsolateData( - isolate_, event_loop, platform, array_buffer_allocator_.get())); + deserialize_mode_ = per_isolate_data_indexes != nullptr; + // If the indexes are not nullptr, we are not deserializing + CHECK_IMPLIES(deserialize_mode_, params->external_references != nullptr); + isolate_data_.reset(new IsolateData(isolate_, + event_loop, + platform, + array_buffer_allocator_.get(), + per_isolate_data_indexes)); SetIsolateUpForNode(isolate_, IsolateSettingCategories::kMisc); - SetIsolateUpForNode(isolate_, IsolateSettingCategories::kErrorHandlers); + if (!deserialize_mode_) { + // If in deserialize mode, delay until after the deserialization is + // complete. + SetIsolateUpForNode(isolate_, IsolateSettingCategories::kErrorHandlers); + } } void NodeMainInstance::Dispose() { @@ -160,6 +172,9 @@ std::unique_ptr NodeMainInstance::CreateMainEnvironment( } Local context = NewContext(isolate_); + if (deserialize_mode_) { + SetIsolateUpForNode(isolate_, IsolateSettingCategories::kErrorHandlers); + } CHECK(!context.IsEmpty()); Context::Scope context_scope(context); diff --git a/src/node_main_instance.h b/src/node_main_instance.h index 0a8be137a0d3ee..615fda1f4945c3 100644 --- a/src/node_main_instance.h +++ b/src/node_main_instance.h @@ -15,6 +15,18 @@ namespace node { // We may be able to create an abstract class to reuse some of the routines. class NodeMainInstance { public: + // An array of indexes that can be used to deserialize data from a V8 + // snapshot. + struct IndexArray { + const size_t* data; + size_t length; + + size_t Get(size_t index) const { + DCHECK_LT(index, length); + return data[index]; + } + }; + // To create a main instance that does not own the isoalte, // The caller needs to do: // @@ -45,12 +57,15 @@ class NodeMainInstance { uv_loop_t* event_loop, MultiIsolatePlatform* platform, const std::vector& args, - const std::vector& exec_args); + const std::vector& exec_args, + const IndexArray* per_isolate_data_indexes = nullptr); ~NodeMainInstance(); // Start running the Node.js instances, return the exit code when finished. int Run(); + IsolateData* isolate_data() { return isolate_data_.get(); } + // TODO(joyeecheung): align this with the CreateEnvironment exposed in node.h // and the environment creation routine in workers somehow. std::unique_ptr CreateMainEnvironment(int* exit_code); @@ -66,6 +81,7 @@ class NodeMainInstance { MultiIsolatePlatform* platform, const std::vector& args, const std::vector& exec_args); + std::vector args_; std::vector exec_args_; std::unique_ptr array_buffer_allocator_; @@ -73,6 +89,7 @@ class NodeMainInstance { MultiIsolatePlatform* platform_; std::unique_ptr isolate_data_; bool owns_isolate_ = false; + bool deserialize_mode_ = false; }; } // namespace node From b44323f3de8b3ec6d5b834bdcc9362bb5fe6d6a1 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 18 Apr 2019 16:26:48 +0800 Subject: [PATCH 03/49] tools: implement node_mksnapshot Implements a node_mksnapshot target that generates a snapshot blob from a Node.js main instance's isolate, and serializes the data blob with other additional data into a C++ file that can be embedded into the Node.js binary. PR-URL: https://github.com/nodejs/node/pull/27321 Refs: https://github.com/nodejs/node/issues/17058 Reviewed-By: Anna Henningsen Reviewed-By: Refael Ackermann Reviewed-By: James M Snell Reviewed-By: Ben Noordhuis --- Makefile | 2 + node.gyp | 42 +++++++++- tools/snapshot/node_mksnapshot.cc | 51 ++++++++++++ tools/snapshot/snapshot_builder.cc | 122 +++++++++++++++++++++++++++++ tools/snapshot/snapshot_builder.h | 15 ++++ 5 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 tools/snapshot/node_mksnapshot.cc create mode 100644 tools/snapshot/snapshot_builder.cc create mode 100644 tools/snapshot/snapshot_builder.h diff --git a/Makefile b/Makefile index d04aac3515a181..d41bf1601fb599 100644 --- a/Makefile +++ b/Makefile @@ -1226,6 +1226,8 @@ LINT_CPP_FILES = $(filter-out $(LINT_CPP_EXCLUDE), $(wildcard \ tools/icu/*.h \ tools/code_cache/*.cc \ tools/code_cache/*.h \ + tools/snapshot/*.cc \ + tools/snapshot/*.h \ )) # Code blocks don't have newline at the end, diff --git a/node.gyp b/node.gyp index 7f534d82080907..8019a16599a20f 100644 --- a/node.gyp +++ b/node.gyp @@ -1156,7 +1156,47 @@ }], ], }, # mkcodecache - ], # end targets + { + 'target_name': 'node_mksnapshot', + 'type': 'executable', + + 'dependencies': [ + '<(node_lib_target_name)', + 'deps/histogram/histogram.gyp:histogram', + ], + + 'includes': [ + 'node.gypi' + ], + + 'include_dirs': [ + 'src', + 'tools/msvs/genfiles', + 'deps/v8/include', + 'deps/cares/include', + 'deps/uv/include', + ], + + 'defines': [ 'NODE_WANT_INTERNALS=1' ], + + 'sources': [ + 'src/node_code_cache_stub.cc', + 'tools/snapshot/node_mksnapshot.cc', + 'tools/snapshot/snapshot_builder.cc', + 'tools/snapshot/snapshot_builder.h', + ], + + 'conditions': [ + [ 'node_report=="true"', { + 'conditions': [ + ['OS=="win"', { + 'libraries': [ 'Ws2_32' ], + }], + ], + }], + ], + }, # node_mksnapshot + ], # end targets 'conditions': [ ['OS=="aix" and node_shared=="true"', { diff --git a/tools/snapshot/node_mksnapshot.cc b/tools/snapshot/node_mksnapshot.cc new file mode 100644 index 00000000000000..c273ba20b610e1 --- /dev/null +++ b/tools/snapshot/node_mksnapshot.cc @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include + +#include "libplatform/libplatform.h" +#include "node_internals.h" +#include "snapshot_builder.h" +#include "v8.h" + +#ifdef _WIN32 +#include + +int wmain(int argc, wchar_t* argv[]) { +#else // UNIX +int main(int argc, char* argv[]) { +#endif // _WIN32 + + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " \n"; + return 1; + } + + std::ofstream out; + out.open(argv[1], std::ios::out | std::ios::binary); + if (!out.is_open()) { + std::cerr << "Cannot open " << argv[1] << "\n"; + return 1; + } + + int node_argc = 1; + char argv0[] = "node"; + char* node_argv[] = {argv0, nullptr}; + + node::InitializationResult result = + node::InitializeOncePerProcess(node_argc, node_argv); + CHECK(!result.early_return); + CHECK_EQ(result.exit_code, 0); + + { + std::string snapshot = + node::SnapshotBuilder::Generate(result.args, result.exec_args); + out << snapshot; + out.close(); + } + + node::TearDownOncePerProcess(); + return 0; +} diff --git a/tools/snapshot/snapshot_builder.cc b/tools/snapshot/snapshot_builder.cc new file mode 100644 index 00000000000000..d1ec17e302ae97 --- /dev/null +++ b/tools/snapshot/snapshot_builder.cc @@ -0,0 +1,122 @@ +#include "snapshot_builder.h" +#include +#include +#include "env-inl.h" +#include "node_internals.h" +#include "node_main_instance.h" +#include "node_v8_platform-inl.h" + +namespace node { + +using v8::Context; +using v8::HandleScope; +using v8::Isolate; +using v8::Local; +using v8::Locker; +using v8::SnapshotCreator; +using v8::StartupData; + +std::string FormatBlob(v8::StartupData* blob, + const std::vector& isolate_data_indexes) { + std::stringstream ss; + size_t isolate_data_indexes_size = isolate_data_indexes.size(); + + ss << R"(#include +#include "node_main_instance.h" +#include "v8.h" + +// This file is generated by tools/snapshot. Do not edit. + +namespace node { + +static const uint8_t blob_data[] = { +)"; + + for (int i = 0; i < blob->raw_size; i++) { + uint8_t ch = blob->data[i]; + ss << std::to_string(ch) << ((i == blob->raw_size - 1) ? '\n' : ','); + } + + ss << R"(}; + +static const int blob_size = )" + << blob->raw_size << R"(; +static v8::StartupData blob = { + reinterpret_cast(blob_data), + blob_size +}; +)"; + + ss << R"(v8::StartupData* +NodeMainInstance::GetEmbeddedSnapshotBlob() { + return &blob; +} + +static const size_t isolate_data_indexes_raw[] = { +)"; + for (size_t i = 0; i < isolate_data_indexes_size; i++) { + ss << std::to_string(isolate_data_indexes[i]) + << ((i == isolate_data_indexes_size - 1) ? '\n' : ','); + } + ss << "};\n\n"; + + ss << "static const size_t isolate_data_indexes_size = " + << isolate_data_indexes_size << R"(; + +NodeMainInstance::IndexArray isolate_data_indexes { + isolate_data_indexes_raw, + isolate_data_indexes_size +}; + +const NodeMainInstance::IndexArray* +NodeMainInstance::GetIsolateDataIndexes() { + return &isolate_data_indexes; +} +} // namespace node +)"; + + return ss.str(); +} + +std::string SnapshotBuilder::Generate( + const std::vector args, + const std::vector exec_args) { + // TODO(joyeecheung): collect external references and set it in + // params.external_references. + std::vector external_references = { + reinterpret_cast(nullptr)}; + Isolate* isolate = Isolate::Allocate(); + per_process::v8_platform.Platform()->RegisterIsolate(isolate, + uv_default_loop()); + NodeMainInstance* main_instance = nullptr; + std::string result; + + { + std::vector isolate_data_indexes; + SnapshotCreator creator(isolate, external_references.data()); + { + main_instance = + NodeMainInstance::Create(isolate, + uv_default_loop(), + per_process::v8_platform.Platform(), + args, + exec_args); + HandleScope scope(isolate); + creator.SetDefaultContext(Context::New(isolate)); + isolate_data_indexes = main_instance->isolate_data()->Serialize(&creator); + } + + // Must be out of HandleScope + StartupData blob = + creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kClear); + // Must be done while the snapshot creator isolate is entered i.e. the + // creator is still alive. + main_instance->Dispose(); + result = FormatBlob(&blob, isolate_data_indexes); + delete blob.data; + } + + per_process::v8_platform.Platform()->UnregisterIsolate(isolate); + return result; +} +} // namespace node diff --git a/tools/snapshot/snapshot_builder.h b/tools/snapshot/snapshot_builder.h new file mode 100644 index 00000000000000..2e587d078b9bcd --- /dev/null +++ b/tools/snapshot/snapshot_builder.h @@ -0,0 +1,15 @@ +#ifndef TOOLS_SNAPSHOT_SNAPSHOT_BUILDER_H_ +#define TOOLS_SNAPSHOT_SNAPSHOT_BUILDER_H_ + +#include +#include + +namespace node { +class SnapshotBuilder { + public: + static std::string Generate(const std::vector args, + const std::vector exec_args); +}; +} // namespace node + +#endif // TOOLS_SNAPSHOT_SNAPSHOT_BUILDER_H_ From 45d610612981223a9b4e4a8d1d8f21caecceb700 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 18 Apr 2019 22:09:28 +0800 Subject: [PATCH 04/49] src: enable snapshot with per-isolate data Enable serializing the isolate from an isolate snapshot generated by node_mksnapshot with per-isolate data. PR-URL: https://github.com/nodejs/node/pull/27321 Refs: https://github.com/nodejs/node/issues/17058 Reviewed-By: Anna Henningsen Reviewed-By: Refael Ackermann Reviewed-By: James M Snell Reviewed-By: Ben Noordhuis --- node.gyp | 29 +++++++++++++++++++++++++++++ src/node.cc | 15 ++++++++++++++- src/node_main_instance.cc | 1 + src/node_main_instance.h | 5 +++++ src/node_snapshot_stub.cc | 13 +++++++++++++ 5 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/node_snapshot_stub.cc diff --git a/node.gyp b/node.gyp index 8019a16599a20f..82efba25d62efa 100644 --- a/node.gyp +++ b/node.gyp @@ -223,6 +223,7 @@ 'deps/acorn/acorn/dist/acorn.js', 'deps/acorn/acorn-walk/dist/walk.js', ], + 'node_mksnapshot_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)node_mksnapshot<(EXECUTABLE_SUFFIX)', 'mkcodecache_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)mkcodecache<(EXECUTABLE_SUFFIX)', 'conditions': [ [ 'node_shared=="true"', { @@ -430,6 +431,31 @@ 'src/node_code_cache_stub.cc' ], }], + ['want_separate_host_toolset==0', { + 'dependencies': [ + 'node_mksnapshot', + ], + 'actions': [ + { + 'action_name': 'node_mksnapshot', + 'process_outputs_as_sources': 1, + 'inputs': [ + '<(node_mksnapshot_exec)', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/node_snapshot.cc', + ], + 'action': [ + '<@(_inputs)', + '<@(_outputs)', + ], + }, + ], + }, { + 'sources': [ + 'src/node_snapshot_stub.cc' + ], + }], ], }, # node_core_target_name { @@ -1038,6 +1064,7 @@ 'defines': [ 'NODE_WANT_INTERNALS=1' ], 'sources': [ + 'src/node_snapshot_stub.cc', 'src/node_code_cache_stub.cc', 'test/cctest/gtest/gtest-all.cc', 'test/cctest/gtest/gtest_main.cc', @@ -1131,6 +1158,7 @@ 'NODE_WANT_INTERNALS=1' ], 'sources': [ + 'src/node_snapshot_stub.cc', 'src/node_code_cache_stub.cc', 'tools/code_cache/mkcodecache.cc', 'tools/code_cache/cache_builder.cc', @@ -1180,6 +1208,7 @@ 'defines': [ 'NODE_WANT_INTERNALS=1' ], 'sources': [ + 'src/node_snapshot_stub.cc', 'src/node_code_cache_stub.cc', 'tools/snapshot/node_mksnapshot.cc', 'tools/snapshot/snapshot_builder.cc', diff --git a/src/node.cc b/src/node.cc index 9e969d33dc1e9f..66e6db74724a32 100644 --- a/src/node.cc +++ b/src/node.cc @@ -887,11 +887,24 @@ int Start(int argc, char** argv) { { Isolate::CreateParams params; + // TODO(joyeecheung): collect external references and set it in + // params.external_references. + std::vector external_references = { + reinterpret_cast(nullptr)}; + v8::StartupData* blob = NodeMainInstance::GetEmbeddedSnapshotBlob(); + const NodeMainInstance::IndexArray* indexes = + NodeMainInstance::GetIsolateDataIndexes(); + if (blob != nullptr) { + params.external_references = external_references.data(); + params.snapshot_blob = blob; + } + NodeMainInstance main_instance(¶ms, uv_default_loop(), per_process::v8_platform.Platform(), result.args, - result.exec_args); + result.exec_args, + indexes); result.exit_code = main_instance.Run(); } diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc index 11dfadabb9422d..419fd767720e4b 100644 --- a/src/node_main_instance.cc +++ b/src/node_main_instance.cc @@ -175,6 +175,7 @@ std::unique_ptr NodeMainInstance::CreateMainEnvironment( if (deserialize_mode_) { SetIsolateUpForNode(isolate_, IsolateSettingCategories::kErrorHandlers); } + CHECK(!context.IsEmpty()); Context::Scope context_scope(context); diff --git a/src/node_main_instance.h b/src/node_main_instance.h index 615fda1f4945c3..98c17cc8f011dd 100644 --- a/src/node_main_instance.h +++ b/src/node_main_instance.h @@ -70,6 +70,11 @@ class NodeMainInstance { // and the environment creation routine in workers somehow. std::unique_ptr CreateMainEnvironment(int* exit_code); + // If nullptr is returned, the binary is not built with embedded + // snapshot. + static const IndexArray* GetIsolateDataIndexes(); + static v8::StartupData* GetEmbeddedSnapshotBlob(); + private: NodeMainInstance(const NodeMainInstance&) = delete; NodeMainInstance& operator=(const NodeMainInstance&) = delete; diff --git a/src/node_snapshot_stub.cc b/src/node_snapshot_stub.cc new file mode 100644 index 00000000000000..c0604237d537c1 --- /dev/null +++ b/src/node_snapshot_stub.cc @@ -0,0 +1,13 @@ +#include "node_main_instance.h" + +namespace node { + +v8::StartupData* NodeMainInstance::GetEmbeddedSnapshotBlob() { + return nullptr; +} + +const NodeMainInstance::IndexArray* NodeMainInstance::GetIsolateDataIndexes() { + return nullptr; +} + +} // namespace node From 228127fc67d9c4da48b78ff5f6a71a4cf883e2cf Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sat, 20 Apr 2019 17:18:34 +0800 Subject: [PATCH 05/49] src: enable context snapshot after running per-context scripts At build time, snapshot the context after running per-context scripts in the main instance, and in the final build, deserialize the context in the main instance. This provides a ~5% in the misc/startup benchmark when the instance is launched within a process that runs test/fixtures/semicolon.js. PR-URL: https://github.com/nodejs/node/pull/27321 Refs: https://github.com/nodejs/node/issues/17058 Reviewed-By: Anna Henningsen Reviewed-By: Refael Ackermann Reviewed-By: James M Snell Reviewed-By: Ben Noordhuis --- src/node_main_instance.cc | 6 +++++- src/node_main_instance.h | 2 ++ tools/snapshot/snapshot_builder.cc | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc index 419fd767720e4b..212c6bf5f4498a 100644 --- a/src/node_main_instance.cc +++ b/src/node_main_instance.cc @@ -171,9 +171,13 @@ std::unique_ptr NodeMainInstance::CreateMainEnvironment( isolate_->GetHeapProfiler()->StartTrackingHeapObjects(true); } - Local context = NewContext(isolate_); + Local context; if (deserialize_mode_) { + context = + Context::FromSnapshot(isolate_, kNodeContextIndex).ToLocalChecked(); SetIsolateUpForNode(isolate_, IsolateSettingCategories::kErrorHandlers); + } else { + context = NewContext(isolate_); } CHECK(!context.IsEmpty()); diff --git a/src/node_main_instance.h b/src/node_main_instance.h index 98c17cc8f011dd..531c919f052016 100644 --- a/src/node_main_instance.h +++ b/src/node_main_instance.h @@ -75,6 +75,8 @@ class NodeMainInstance { static const IndexArray* GetIsolateDataIndexes(); static v8::StartupData* GetEmbeddedSnapshotBlob(); + static const size_t kNodeContextIndex = 0; + private: NodeMainInstance(const NodeMainInstance&) = delete; NodeMainInstance& operator=(const NodeMainInstance&) = delete; diff --git a/tools/snapshot/snapshot_builder.cc b/tools/snapshot/snapshot_builder.cc index d1ec17e302ae97..835fca9f4609cb 100644 --- a/tools/snapshot/snapshot_builder.cc +++ b/tools/snapshot/snapshot_builder.cc @@ -104,6 +104,9 @@ std::string SnapshotBuilder::Generate( HandleScope scope(isolate); creator.SetDefaultContext(Context::New(isolate)); isolate_data_indexes = main_instance->isolate_data()->Serialize(&creator); + + size_t index = creator.AddContext(NewContext(isolate)); + CHECK_EQ(index, NodeMainInstance::kNodeContextIndex); } // Must be out of HandleScope From ad42cd69cf9c655a6977a8beac2f09a85e7bf51d Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sun, 21 Apr 2019 20:03:21 +0800 Subject: [PATCH 06/49] src: use std::vector instead of IndexArray PR-URL: https://github.com/nodejs/node/pull/27321 Refs: https://github.com/nodejs/node/issues/17058 Reviewed-By: Anna Henningsen Reviewed-By: Refael Ackermann Reviewed-By: James M Snell Reviewed-By: Ben Noordhuis --- src/env.cc | 7 ++--- src/env.h | 4 +-- src/node.cc | 2 +- src/node_main_instance.cc | 13 +++++---- src/node_main_instance.h | 27 ++++++------------ src/node_snapshot_stub.cc | 2 +- tools/snapshot/snapshot_builder.cc | 45 ++++++++++-------------------- 7 files changed, 37 insertions(+), 63 deletions(-) diff --git a/src/env.cc b/src/env.cc index 5ef32e2b0ef80c..ad43b13607006e 100644 --- a/src/env.cc +++ b/src/env.cc @@ -75,8 +75,7 @@ std::vector IsolateData::Serialize(SnapshotCreator* creator) { return indexes; } -void IsolateData::DeserializeProperties( - const NodeMainInstance::IndexArray* indexes) { +void IsolateData::DeserializeProperties(const std::vector* indexes) { size_t i = 0; HandleScope handle_scope(isolate_); @@ -86,7 +85,7 @@ void IsolateData::DeserializeProperties( #define V(TypeName, PropertyName) \ do { \ MaybeLocal field = \ - isolate_->GetDataFromSnapshotOnce(indexes->Get(i++)); \ + isolate_->GetDataFromSnapshotOnce((*indexes)[i++]); \ if (field.IsEmpty()) { \ fprintf(stderr, "Failed to deserialize " #PropertyName "\n"); \ } \ @@ -155,7 +154,7 @@ IsolateData::IsolateData(Isolate* isolate, uv_loop_t* event_loop, MultiIsolatePlatform* platform, ArrayBufferAllocator* node_allocator, - const NodeMainInstance::IndexArray* indexes) + const std::vector* indexes) : isolate_(isolate), event_loop_(event_loop), allocator_(isolate->GetArrayBufferAllocator()), diff --git a/src/env.h b/src/env.h index 24050cc9f4404f..a871f8fcc1357f 100644 --- a/src/env.h +++ b/src/env.h @@ -420,7 +420,7 @@ class IsolateData : public MemoryRetainer { uv_loop_t* event_loop, MultiIsolatePlatform* platform = nullptr, ArrayBufferAllocator* node_allocator = nullptr, - const NodeMainInstance::IndexArray* indexes = nullptr); + const std::vector* indexes = nullptr); SET_MEMORY_INFO_NAME(IsolateData); SET_SELF_SIZE(IsolateData); void MemoryInfo(MemoryTracker* tracker) const override; @@ -454,7 +454,7 @@ class IsolateData : public MemoryRetainer { IsolateData& operator=(const IsolateData&) = delete; private: - void DeserializeProperties(const NodeMainInstance::IndexArray* indexes); + void DeserializeProperties(const std::vector* indexes); void CreateProperties(); #define VP(PropertyName, StringValue) V(v8::Private, PropertyName) diff --git a/src/node.cc b/src/node.cc index 66e6db74724a32..10ef0d5bc7c522 100644 --- a/src/node.cc +++ b/src/node.cc @@ -892,7 +892,7 @@ int Start(int argc, char** argv) { std::vector external_references = { reinterpret_cast(nullptr)}; v8::StartupData* blob = NodeMainInstance::GetEmbeddedSnapshotBlob(); - const NodeMainInstance::IndexArray* indexes = + const std::vector* indexes = NodeMainInstance::GetIsolateDataIndexes(); if (blob != nullptr) { params.external_references = external_references.data(); diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc index 212c6bf5f4498a..977a689a359474 100644 --- a/src/node_main_instance.cc +++ b/src/node_main_instance.cc @@ -38,12 +38,13 @@ NodeMainInstance* NodeMainInstance::Create( return new NodeMainInstance(isolate, event_loop, platform, args, exec_args); } -NodeMainInstance::NodeMainInstance(Isolate::CreateParams* params, - uv_loop_t* event_loop, - MultiIsolatePlatform* platform, - const std::vector& args, - const std::vector& exec_args, - const IndexArray* per_isolate_data_indexes) +NodeMainInstance::NodeMainInstance( + Isolate::CreateParams* params, + uv_loop_t* event_loop, + MultiIsolatePlatform* platform, + const std::vector& args, + const std::vector& exec_args, + const std::vector* per_isolate_data_indexes) : args_(args), exec_args_(exec_args), array_buffer_allocator_(ArrayBufferAllocator::Create()), diff --git a/src/node_main_instance.h b/src/node_main_instance.h index 531c919f052016..2719b49e976ff6 100644 --- a/src/node_main_instance.h +++ b/src/node_main_instance.h @@ -15,18 +15,6 @@ namespace node { // We may be able to create an abstract class to reuse some of the routines. class NodeMainInstance { public: - // An array of indexes that can be used to deserialize data from a V8 - // snapshot. - struct IndexArray { - const size_t* data; - size_t length; - - size_t Get(size_t index) const { - DCHECK_LT(index, length); - return data[index]; - } - }; - // To create a main instance that does not own the isoalte, // The caller needs to do: // @@ -53,12 +41,13 @@ class NodeMainInstance { void Dispose(); // Create a main instance that owns the isolate - NodeMainInstance(v8::Isolate::CreateParams* params, - uv_loop_t* event_loop, - MultiIsolatePlatform* platform, - const std::vector& args, - const std::vector& exec_args, - const IndexArray* per_isolate_data_indexes = nullptr); + NodeMainInstance( + v8::Isolate::CreateParams* params, + uv_loop_t* event_loop, + MultiIsolatePlatform* platform, + const std::vector& args, + const std::vector& exec_args, + const std::vector* per_isolate_data_indexes = nullptr); ~NodeMainInstance(); // Start running the Node.js instances, return the exit code when finished. @@ -72,7 +61,7 @@ class NodeMainInstance { // If nullptr is returned, the binary is not built with embedded // snapshot. - static const IndexArray* GetIsolateDataIndexes(); + static const std::vector* GetIsolateDataIndexes(); static v8::StartupData* GetEmbeddedSnapshotBlob(); static const size_t kNodeContextIndex = 0; diff --git a/src/node_snapshot_stub.cc b/src/node_snapshot_stub.cc index c0604237d537c1..91bc37121d61fe 100644 --- a/src/node_snapshot_stub.cc +++ b/src/node_snapshot_stub.cc @@ -6,7 +6,7 @@ v8::StartupData* NodeMainInstance::GetEmbeddedSnapshotBlob() { return nullptr; } -const NodeMainInstance::IndexArray* NodeMainInstance::GetIsolateDataIndexes() { +const std::vector* NodeMainInstance::GetIsolateDataIndexes() { return nullptr; } diff --git a/tools/snapshot/snapshot_builder.cc b/tools/snapshot/snapshot_builder.cc index 835fca9f4609cb..1faa5330ae118f 100644 --- a/tools/snapshot/snapshot_builder.cc +++ b/tools/snapshot/snapshot_builder.cc @@ -16,10 +16,16 @@ using v8::Locker; using v8::SnapshotCreator; using v8::StartupData; +template +void WriteVector(std::stringstream* ss, const T* vec, size_t size) { + for (size_t i = 0; i < size; i++) { + *ss << std::to_string(vec[i]) << (i == size - 1 ? '\n' : ','); + } +} + std::string FormatBlob(v8::StartupData* blob, const std::vector& isolate_data_indexes) { std::stringstream ss; - size_t isolate_data_indexes_size = isolate_data_indexes.size(); ss << R"(#include #include "node_main_instance.h" @@ -29,47 +35,26 @@ std::string FormatBlob(v8::StartupData* blob, namespace node { -static const uint8_t blob_data[] = { +static const char blob_data[] = { )"; - - for (int i = 0; i < blob->raw_size; i++) { - uint8_t ch = blob->data[i]; - ss << std::to_string(ch) << ((i == blob->raw_size - 1) ? '\n' : ','); - } - + WriteVector(&ss, blob->data, blob->raw_size); ss << R"(}; static const int blob_size = )" << blob->raw_size << R"(; -static v8::StartupData blob = { - reinterpret_cast(blob_data), - blob_size -}; +static v8::StartupData blob = { blob_data, blob_size }; )"; - ss << R"(v8::StartupData* -NodeMainInstance::GetEmbeddedSnapshotBlob() { + ss << R"(v8::StartupData* NodeMainInstance::GetEmbeddedSnapshotBlob() { return &blob; } -static const size_t isolate_data_indexes_raw[] = { +static const std::vector isolate_data_indexes { )"; - for (size_t i = 0; i < isolate_data_indexes_size; i++) { - ss << std::to_string(isolate_data_indexes[i]) - << ((i == isolate_data_indexes_size - 1) ? '\n' : ','); - } - ss << "};\n\n"; - - ss << "static const size_t isolate_data_indexes_size = " - << isolate_data_indexes_size << R"(; - -NodeMainInstance::IndexArray isolate_data_indexes { - isolate_data_indexes_raw, - isolate_data_indexes_size -}; + WriteVector(&ss, isolate_data_indexes.data(), isolate_data_indexes.size()); + ss << R"(}; -const NodeMainInstance::IndexArray* -NodeMainInstance::GetIsolateDataIndexes() { +const std::vector* NodeMainInstance::GetIsolateDataIndexes() { return &isolate_data_indexes; } } // namespace node From a6d1fa59541eec0bb630cb00d338edfebe306b89 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Mon, 22 Apr 2019 12:20:03 -0700 Subject: [PATCH 07/49] doc: simplify Collaborator pre-nomination text Split one very long sentence into three shorter sentences. PR-URL: https://github.com/nodejs/node/pull/27348 Reviewed-By: Vse Mozhet Byt Reviewed-By: Jeremiah Senkpiel Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater Reviewed-By: Anatoli Papirovski --- GOVERNANCE.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index af61aed758f956..dadbdadb33b9bc 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -139,12 +139,10 @@ the nomination. The nomination passes if no Collaborators oppose it after one week. Otherwise, the nomination fails. -Prior to the public nomination, the Collaborator initiating it can seek -feedback from other Collaborators in private using -[the GitHub discussion page][collaborators-discussions] of the -Collaborators team, and work with the nominee to improve the nominee's -contribution profile, in order to make the nomination as frictionless -as possible. +There are steps a nominator can take in advance to make a nomination as +frictionless as possible. Use the [Collaborators discussion page][] to request +feedback from other Collaborators in private. A nominator may also work with the +nominee to improve their contribution profile. If individuals making valuable contributions do not believe they have been considered for a nomination, they may log an issue or contact a Collaborator @@ -162,7 +160,7 @@ completed within a month after the nomination is accepted. The TSC follows a [Consensus Seeking][] decision-making model as described by the [TSC Charter][]. -[collaborators-discussions]: https://github.com/orgs/nodejs/teams/collaborators/discussions +[Collaborators discussion page]: https://github.com/orgs/nodejs/teams/collaborators/discussions [Consensus Seeking]: https://en.wikipedia.org/wiki/Consensus-seeking_decision-making [TSC Charter]: https://github.com/nodejs/TSC/blob/master/TSC-Charter.md [nodejs/node]: https://github.com/nodejs/node From d62a3243b1841437e201331e9df09dad7d2db39e Mon Sep 17 00:00:00 2001 From: Ujjwal Sharma Date: Tue, 23 Apr 2019 18:51:42 +0530 Subject: [PATCH 08/49] tools: update tools/license-builder.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update tools/license-builder.sh in order to work normally after jinja2 and markupsafe were moved from tools/ to tools/inspector_protocol/ in an earlier commit. Refs: https://github.com/nodejs/node/pull/25614 PR-URL: https://github.com/nodejs/node/pull/27362 Reviewed-By: Michaël Zasso Reviewed-By: Refael Ackermann Reviewed-By: Richard Lau --- tools/license-builder.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/license-builder.sh b/tools/license-builder.sh index 8c51b43e53895f..b29e8435228ea8 100755 --- a/tools/license-builder.sh +++ b/tools/license-builder.sh @@ -75,8 +75,8 @@ addlicense "npm" "deps/npm" "$(cat ${rootdir}/deps/npm/LICENSE)" # Build tools addlicense "GYP" "tools/gyp" "$(cat ${rootdir}/tools/gyp/LICENSE)" addlicense "inspector_protocol" "tools/inspector_protocol" "$(cat ${rootdir}/tools/inspector_protocol/LICENSE)" -addlicense "jinja2" "tools/jinja2" "$(cat ${rootdir}/tools/jinja2/LICENSE)" -addlicense "markupsafe" "tools/markupsafe" "$(cat ${rootdir}/tools/markupsafe/LICENSE)" +addlicense "jinja2" "tools/inspector_protocol/jinja2" "$(cat ${rootdir}/tools/inspector_protocol/jinja2/LICENSE)" +addlicense "markupsafe" "tools/inspector_protocol/markupsafe" "$(cat ${rootdir}/tools/inspector_protocol/markupsafe/LICENSE)" # Testing tools addlicense "cpplint.py" "tools/cpplint.py" \ From 50234460f9c39f50504fcef067ad0730eca65414 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 23 Apr 2019 22:21:52 +0800 Subject: [PATCH 09/49] build: disable custom v8 snapshot by default This currently breaks `test/pummel/test-hash-seed.js` PR-URL: https://github.com/nodejs/node/pull/27365 Refs: https://github.com/nodejs/node/pull/27321 Reviewed-By: Refael Ackermann Reviewed-By: Ujjwal Sharma --- configure.py | 12 ++++++++++++ node.gyp | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/configure.py b/configure.py index 1a0819e74113bc..12d68ccfeb2419 100755 --- a/configure.py +++ b/configure.py @@ -421,6 +421,11 @@ dest='with_ltcg', help='Use Link Time Code Generation. This feature is only available on Windows.') +parser.add_option('--with-node-snapshot', + action='store_true', + dest='with_node_snapshot', + help='Turn on V8 snapshot integration. Currently experimental.') + intl_optgroup.add_option('--download', action='store', dest='download_list', @@ -928,6 +933,13 @@ def configure_node(o): o['variables']['want_separate_host_toolset'] = int( cross_compiling and want_snapshots) + if options.with_node_snapshot: + o['variables']['node_use_node_snapshot'] = 'true' + else: + # Default to false for now. + # TODO(joyeecheung): enable it once we fix the hashseed uniqueness + o['variables']['node_use_node_snapshot'] = 'false' + if target_arch == 'arm': configure_arm(o) diff --git a/node.gyp b/node.gyp index 82efba25d62efa..53bd954afb3e39 100644 --- a/node.gyp +++ b/node.gyp @@ -6,6 +6,7 @@ 'node_use_dtrace%': 'false', 'node_use_etw%': 'false', 'node_no_browser_globals%': 'false', + 'node_use_node_snapshot%': 'false', 'node_use_v8_platform%': 'true', 'node_use_bundled_v8%': 'true', 'node_shared%': 'false', @@ -431,7 +432,7 @@ 'src/node_code_cache_stub.cc' ], }], - ['want_separate_host_toolset==0', { + ['node_use_node_snapshot=="true"', { 'dependencies': [ 'node_mksnapshot', ], From f70588fb6746e4af424188b7126fb9021b6df20b Mon Sep 17 00:00:00 2001 From: Beth Griggs Date: Tue, 23 Apr 2019 16:58:35 +0100 Subject: [PATCH 10/49] doc: fix v12.0.0 changelog id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/27368 Reviewed-By: Refael Ackermann Reviewed-By: Richard Lau Reviewed-By: Michaël Zasso --- doc/changelogs/CHANGELOG_V12.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changelogs/CHANGELOG_V12.md b/doc/changelogs/CHANGELOG_V12.md index 32b1056179f5d1..ff00f3a41027e5 100644 --- a/doc/changelogs/CHANGELOG_V12.md +++ b/doc/changelogs/CHANGELOG_V12.md @@ -28,7 +28,7 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) - + ## 2019-04-23, Version 12.0.0 (Current), @BethGriggs ### Notable Changes From 8ca110cc6f723cca50e1971265363fc895426757 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Tue, 23 Apr 2019 05:46:46 -0700 Subject: [PATCH 11/49] benchmark: fix http bench-parser.js The internal HTTParser `reinitialize()` function was removed in ece507394a and replaced with an `initialize()` function. This broke benchmark/http/bench-parser.js. This change updates the benchmark so that it runs again. PR-URL: https://github.com/nodejs/node/pull/27359 Reviewed-By: Refael Ackermann Reviewed-By: Benjamin Gruenbaum --- benchmark/http/bench-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/http/bench-parser.js b/benchmark/http/bench-parser.js index a54f0efa75e3c1..271ac55d007028 100644 --- a/benchmark/http/bench-parser.js +++ b/benchmark/http/bench-parser.js @@ -24,7 +24,7 @@ function main({ len, n }) { bench.start(); for (var i = 0; i < n; i++) { parser.execute(header, 0, header.length); - parser.reinitialize(REQUEST, i > 0); + parser.initialize(REQUEST, header); } bench.end(n); } From 8486917b9a2006ee26b7003c5425c5e8586c9b8f Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Sat, 20 Apr 2019 21:01:17 -0700 Subject: [PATCH 12/49] test: increase coverage in lib/internal/dns/promises.js Add a test for the only uncovered code in lib/internal/dns/promises.js. PR-URL: https://github.com/nodejs/node/pull/27330 Reviewed-By: Yongsheng Zhang Reviewed-By: Ruben Bridgewater Reviewed-By: Michael Dawson --- test/parallel/test-dns-lookupService.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/parallel/test-dns-lookupService.js b/test/parallel/test-dns-lookupService.js index 92fce6cbd3f189..f78e98766063f7 100644 --- a/test/parallel/test-dns-lookupService.js +++ b/test/parallel/test-dns-lookupService.js @@ -18,3 +18,12 @@ assert.throws( syscall: 'getnameinfo' } ); + +assert.rejects( + dns.promises.lookupService('127.0.0.1', 80), + { + code: 'ENOENT', + message: 'getnameinfo ENOENT 127.0.0.1', + syscall: 'getnameinfo' + } +); From ee80a210ffd37eab3b660be6f55fd161ea044ea1 Mon Sep 17 00:00:00 2001 From: MaleDong Date: Mon, 22 Apr 2019 09:41:33 +0800 Subject: [PATCH 13/49] dgram: change 'this' to 'self' for 'isConnected' The function 'isConnected' is directly called by many 'Socket' instance, so we shouldn't directly use 'this' because 'this' will be the self of function itself, and we should use 'self' as the instance of 'Socket' function. PR-URL: https://github.com/nodejs/node/pull/27338 Reviewed-By: Santiago Gimeno Reviewed-By: Joyee Cheung Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig Reviewed-By: Franziska Hinkelmann Reviewed-By: Yongsheng Zhang Reviewed-By: Benjamin Gruenbaum --- lib/dgram.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dgram.js b/lib/dgram.js index 5a6d5aca2c3cf4..b38aa39c215aeb 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -519,7 +519,7 @@ function clearQueue() { function isConnected(self) { try { - this.remoteAddress(); + self.remoteAddress(); return true; } catch { return false; From d84a6d05a175688bf2f2ba4c48d855dde8f105ed Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Sun, 21 Apr 2019 22:25:37 -0700 Subject: [PATCH 14/49] test: make test-worker-esm-missing-main more robust test-worker-esm-missing-main failed in CI recently in a way that suggests that maybe the `does-not-exist.js` file did in fact exist. Maybe that isn't what happened at all, but let's rule it out by changing the use of `does-not-exist.js` from a file expected to be missing from the current working directory to a file in the temp directory, which the test will remove and recreate at the outset. PR-URL: https://github.com/nodejs/node/pull/27340 Reviewed-By: Luigi Pinca Reviewed-By: Beth Griggs --- test/parallel/test-worker-esm-missing-main.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/parallel/test-worker-esm-missing-main.js b/test/parallel/test-worker-esm-missing-main.js index 8d2cf8f3f3e6c5..8f4cfb0fe7fa9b 100644 --- a/test/parallel/test-worker-esm-missing-main.js +++ b/test/parallel/test-worker-esm-missing-main.js @@ -1,11 +1,14 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); +const path = require('path'); const { Worker } = require('worker_threads'); -const worker = new Worker('./does-not-exist.js', { - execArgv: ['--experimental-modules'], -}); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +const missing = path.join(tmpdir.path, 'does-not-exist.js'); + +const worker = new Worker(missing, { execArgv: ['--experimental-modules'] }); worker.on('error', common.mustCall((err) => { // eslint-disable-next-line node-core/no-unescaped-regexp-dot From f0b2992f5c2be1afc781b57cfa34f6294c277b1f Mon Sep 17 00:00:00 2001 From: Masashi Hirano Date: Sun, 21 Apr 2019 17:04:04 +0900 Subject: [PATCH 15/49] test: fix ineffective error tests Fix tests whether errors are thrown correctly because they are successful when error doesn't get thrown. PR-URL: https://github.com/nodejs/node/pull/27333 Fixes: https://github.com/nodejs/node/issues/26385 Reviewed-By: Ruben Bridgewater Reviewed-By: Luigi Pinca --- .../non-node-context/test-make-buffer.js | 21 +++++----- test/es-module/test-esm-error-cache.js | 12 +++--- test/parallel/test-assert.js | 30 +++++++------- test/parallel/test-util-inspect.js | 13 +++--- test/parallel/test-vm-codegen.js | 29 +++++--------- test/sequential/test-module-loading.js | 40 +++++++++++-------- 6 files changed, 73 insertions(+), 72 deletions(-) diff --git a/test/addons/non-node-context/test-make-buffer.js b/test/addons/non-node-context/test-make-buffer.js index 9b17fa4ee930ae..d134f63b77283c 100644 --- a/test/addons/non-node-context/test-make-buffer.js +++ b/test/addons/non-node-context/test-make-buffer.js @@ -9,14 +9,15 @@ const { // Because the `Buffer` function and its protoype property only (currently) // exist in a Node.js instance’s main context, trying to create buffers from // another context throws an exception. +assert.throws( + () => makeBufferInNewContext(), + (exception) => { + assert.strictEqual(exception.constructor.name, 'Error'); + assert(!(exception.constructor instanceof Error)); -try { - makeBufferInNewContext(); -} catch (exception) { - assert.strictEqual(exception.constructor.name, 'Error'); - assert(!(exception.constructor instanceof Error)); - - assert.strictEqual(exception.code, 'ERR_BUFFER_CONTEXT_NOT_AVAILABLE'); - assert.strictEqual(exception.message, - 'Buffer is not available for the current Context'); -} + assert.strictEqual(exception.code, 'ERR_BUFFER_CONTEXT_NOT_AVAILABLE'); + assert.strictEqual(exception.message, + 'Buffer is not available for the current Context'); + return true; + } +); diff --git a/test/es-module/test-esm-error-cache.js b/test/es-module/test-esm-error-cache.js index 79f76357eca3ea..26e0d170ac2e1b 100644 --- a/test/es-module/test-esm-error-cache.js +++ b/test/es-module/test-esm-error-cache.js @@ -18,9 +18,11 @@ let error; assert(error); - try { - await import(file); - } catch (e) { - assert.strictEqual(error, e); - } + await assert.rejects( + () => import(file), + (e) => { + assert.strictEqual(error, e); + return true; + } + ); })(); diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js index 4c2b1f978d081f..62ed50f6a43fa9 100644 --- a/test/parallel/test-assert.js +++ b/test/parallel/test-assert.js @@ -297,23 +297,21 @@ testAssertionMessage({ a: NaN, b: Infinity, c: -Infinity }, '{\n+ a: NaN,\n+ b: Infinity,\n+ c: -Infinity\n+ }'); // https://github.com/nodejs/node-v0.x-archive/issues/5292 -try { - assert.strictEqual(1, 2); -} catch (e) { - assert.strictEqual( - e.message, - `${strictEqualMessageStart}\n1 !== 2\n` - ); - assert.ok(e.generatedMessage, 'Message not marked as generated'); -} +assert.throws( + () => assert.strictEqual(1, 2), + { + message: `${strictEqualMessageStart}\n1 !== 2\n`, + generatedMessage: true + } +); -try { - assert.strictEqual(1, 2, 'oh no'); -} catch (e) { - assert.strictEqual(e.message, 'oh no'); - // Message should not be marked as generated. - assert.strictEqual(e.generatedMessage, false); -} +assert.throws( + () => assert.strictEqual(1, 2, 'oh no'), + { + message: 'oh no', + generatedMessage: false + } +); { let threw = false; diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index c1b6e3f98d4480..eeda567e0d3e22 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -538,11 +538,14 @@ assert.strictEqual(util.inspect(-5e-324), '-5e-324'); ].forEach((err) => { assert.strictEqual(util.inspect(err), err.stack); }); - try { - undef(); // eslint-disable-line no-undef - } catch (e) { - assert.strictEqual(util.inspect(e), e.stack); - } + assert.throws( + () => undef(), // eslint-disable-line no-undef + (e) => { + assert.strictEqual(util.inspect(e), e.stack); + return true; + } + ); + const ex = util.inspect(new Error('FAIL'), true); assert(ex.includes('Error: FAIL')); assert(ex.includes('[stack]')); diff --git a/test/parallel/test-vm-codegen.js b/test/parallel/test-vm-codegen.js index ebafe30c076e01..0136e143abd1d2 100644 --- a/test/parallel/test-vm-codegen.js +++ b/test/parallel/test-vm-codegen.js @@ -8,19 +8,6 @@ const { createContext, runInContext, runInNewContext } = require('vm'); const WASM_BYTES = Buffer.from( [0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]); - -function expectsError(fn, type) { - try { - fn(); - assert.fail('expected fn to error'); - } catch (err) { - if (typeof type === 'string') - assert.strictEqual(err.name, type); - else - assert(err instanceof type); - } -} - { const ctx = createContext({ WASM_BYTES }); const test = 'eval(""); new WebAssembly.Module(WASM_BYTES);'; @@ -39,7 +26,7 @@ function expectsError(fn, type) { }); const EvalError = runInContext('EvalError', ctx); - expectsError(() => { + assert.throws(() => { runInContext('eval("x")', ctx); }, EvalError); } @@ -52,26 +39,30 @@ function expectsError(fn, type) { }); const CompileError = runInContext('WebAssembly.CompileError', ctx); - expectsError(() => { + assert.throws(() => { runInContext('new WebAssembly.Module(WASM_BYTES)', ctx); }, CompileError); } -expectsError(() => { +assert.throws(() => { runInNewContext('eval("x")', {}, { contextCodeGeneration: { strings: false, }, }); -}, 'EvalError'); +}, { + name: 'EvalError' +}); -expectsError(() => { +assert.throws(() => { runInNewContext('new WebAssembly.Module(WASM_BYTES)', { WASM_BYTES }, { contextCodeGeneration: { wasm: false, }, }); -}, 'CompileError'); +}, { + name: 'CompileError' +}); common.expectsError(() => { createContext({}, { diff --git a/test/sequential/test-module-loading.js b/test/sequential/test-module-loading.js index 5bd84086129cc4..612cde3ab0261f 100644 --- a/test/sequential/test-module-loading.js +++ b/test/sequential/test-module-loading.js @@ -201,27 +201,33 @@ assert.throws( assert.strictEqual(require(`${loadOrder}file1`).file1, 'file1'); assert.strictEqual(require(`${loadOrder}file2`).file2, 'file2.js'); - try { - require(`${loadOrder}file3`); - } catch (e) { - // Not a real .node module, but we know we require'd the right thing. - if (common.isOpenBSD) // OpenBSD errors with non-ELF object error - assert.ok(/File not an ELF object/.test(e.message.replace(backslash, '/'))); - else - assert.ok(/file3\.node/.test(e.message.replace(backslash, '/'))); - } + assert.throws( + () => require(`${loadOrder}file3`), + (e) => { + // Not a real .node module, but we know we require'd the right thing. + if (common.isOpenBSD) { // OpenBSD errors with non-ELF object error + assert.ok(/File not an ELF object/.test(e.message.replace(backslash, '/'))); + } else { + assert.ok(/file3\.node/.test(e.message.replace(backslash, '/'))); + } + return true; + } + ); assert.strictEqual(require(`${loadOrder}file4`).file4, 'file4.reg'); assert.strictEqual(require(`${loadOrder}file5`).file5, 'file5.reg2'); assert.strictEqual(require(`${loadOrder}file6`).file6, 'file6/index.js'); - try { - require(`${loadOrder}file7`); - } catch (e) { - if (common.isOpenBSD) - assert.ok(/File not an ELF object/.test(e.message.replace(backslash, '/'))); - else - assert.ok(/file7\/index\.node/.test(e.message.replace(backslash, '/'))); - } + assert.throws( + () => require(`${loadOrder}file7`), + (e) => { + if (common.isOpenBSD) { + assert.ok(/File not an ELF object/.test(e.message.replace(backslash, '/'))); + } else { + assert.ok(/file7\/index\.node/.test(e.message.replace(backslash, '/'))); + } + return true; + } + ); assert.strictEqual(require(`${loadOrder}file8`).file8, 'file8/index.reg'); assert.strictEqual(require(`${loadOrder}file9`).file9, 'file9/index.reg2'); From 481789c235cbf77e0a0f991567acae0ab15dc5d5 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 21 Apr 2019 09:50:59 +0200 Subject: [PATCH 16/49] tools: fix use-after-free mkcodecache warning Call `v8::Platform::ShutdownPlatform()` to fix a Coverity warning about the `v8::Platform` instance being deleted when it's still in use. PR-URL: https://github.com/nodejs/node/pull/27332 Reviewed-By: Joyee Cheung Reviewed-By: Richard Lau Reviewed-By: Colin Ihrig --- tools/code_cache/mkcodecache.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/code_cache/mkcodecache.cc b/tools/code_cache/mkcodecache.cc index 24f7e05e1f19f7..defa1462ce45ec 100644 --- a/tools/code_cache/mkcodecache.cc +++ b/tools/code_cache/mkcodecache.cc @@ -58,5 +58,6 @@ int main(int argc, char* argv[]) { out.close(); } + v8::V8::ShutdownPlatform(); return 0; } From 36762883a020f8f9acc71c67bcc62b6242f28e3f Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Wed, 17 Apr 2019 10:04:30 -0700 Subject: [PATCH 17/49] doc: clarify behaviour of writeFile(fd) This is a continuing source of confusion, attempt to make it even more clear. Fixes: https://github.com/nodejs/node/issues/24923 PR-URL: https://github.com/nodejs/node/pull/27282 Reviewed-By: Luigi Pinca Reviewed-By: Vse Mozhet Byt --- doc/api/fs.md | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/doc/api/fs.md b/doc/api/fs.md index 0ce63f98ce2548..9e3b0df9a487b9 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -3592,8 +3592,12 @@ changes: * `callback` {Function} * `err` {Error} -Asynchronously writes data to a file, replacing the file if it already exists. -`data` can be a string or a buffer. +When `file` is a filename, asynchronously writes data to the file, replacing the +file if it already exists. `data` can be a string or a buffer. + +When `file` is a file descriptor, the behavior is similar to calling +`fs.write()` directly (which is recommended). See the notes below on using +a file descriptor. The `encoding` option is ignored if `data` is a buffer. @@ -3615,15 +3619,30 @@ It is unsafe to use `fs.writeFile()` multiple times on the same file without waiting for the callback. For this scenario, [`fs.createWriteStream()`][] is recommended. -### File Descriptors -1. Any specified file descriptor has to support writing. -2. If a file descriptor is specified as the `file`, it will not be closed -automatically. -3. The writing will begin at the current position. For example, if the string -`'Hello'` is written to the file descriptor, and if `', World'` is written with -`fs.writeFile()` to the same file descriptor, the contents of the file would -become `'Hello, World'`, instead of just `', World'`. +### Using `fs.writeFile()` with File Descriptors + +When `file` is a file descriptor, the behavior is almost identical to directly +calling `fs.write()` like: +```javascript +fs.write(fd, Buffer.from(data, options.encoding), callback); +``` +The difference from directly calling `fs.write()` is that under some unusual +conditions, `fs.write()` may write only part of the buffer and will need to be +retried to write the remaining data, whereas `fs.writeFile()` will retry until +the data is entirely written (or an error occurs). + +Since the implications of this are a common source of confusion, note that in +the file descriptor case the file is not replaced! The data is not necessarily +written to the beginning of the file, and the file's original data may remain +before and/or after the newly written data. + +For example, if `fs.writeFile()` is called twice in a row, first to write the +string `'Hello'`, then to write the string `', World'`, the file would contain +`'Hello, World'`, and might contain some of the file's original data (depending +on the size of the original file, and the position of the file descriptor). If +a file name had been used instead of a descriptor, the file would be guaranteed +to contain only `', World'`. ## fs.writeFileSync(file, data[, options]) default_loc codepage +-------------------------------------------------------- + ab.CD ab CD + ab@CD ab__CD - + ab@CD.EF ab__CD EF + + ab_CD.EF@GH ab_CD_GH EF + +Some 'improper' ways to do the same as above: + ! ab_CD@GH.EF ab_CD_GH EF + ! ab_CD.EF@GH.IJ ab_CD_GH EF + ! ab_CD@ZZ.EF@GH.IJ ab_CD_GH EF + + _CD@GH _CD_GH - + _CD.EF@GH _CD_GH EF + +The variant cannot have dots in it. +The 'rightmost' variant (@xxx) wins. +The leftmost codepage (.xxx) wins. +*/ + const char* posixID = uprv_getPOSIXIDForDefaultLocale(); + + /* Format: (no spaces) + ll [ _CC ] [ . MM ] [ @ VV] + + l = lang, C = ctry, M = charmap, V = variant + */ + + if (gCorrectedPOSIXLocale != nullptr) { + return gCorrectedPOSIXLocale; + } + + // Copy the ID into owned memory. + // Over-allocate in case we replace "C" with "en_US_POSIX" (+10), + null termination + char *correctedPOSIXLocale = static_cast(uprv_malloc(uprv_strlen(posixID) + 10 + 1)); + if (correctedPOSIXLocale == nullptr) { + return nullptr; + } + uprv_strcpy(correctedPOSIXLocale, posixID); + + char *limit; + if ((limit = uprv_strchr(correctedPOSIXLocale, '.')) != nullptr) { + *limit = 0; + } + if ((limit = uprv_strchr(correctedPOSIXLocale, '@')) != nullptr) { + *limit = 0; + } + + if ((uprv_strcmp("C", correctedPOSIXLocale) == 0) // no @ variant + || (uprv_strcmp("POSIX", correctedPOSIXLocale) == 0)) { + // Raw input was C.* or POSIX.*, Give it a nice POSIX default value. + // (The "C"/"POSIX" case is handled in uprv_getPOSIXIDForCategory()) + uprv_strcpy(correctedPOSIXLocale, "en_US_POSIX"); + } + + /* Note that we scan the *uncorrected* ID. */ + const char *p; + if ((p = uprv_strrchr(posixID, '@')) != nullptr) { + p++; + + /* Take care of any special cases here.. */ + if (!uprv_strcmp(p, "nynorsk")) { + p = "NY"; + /* Don't worry about no__NY. In practice, it won't appear. */ + } + + if (uprv_strchr(correctedPOSIXLocale,'_') == nullptr) { + uprv_strcat(correctedPOSIXLocale, "__"); /* aa@b -> aa__b (note this can make the new locale 1 char longer) */ + } + else { + uprv_strcat(correctedPOSIXLocale, "_"); /* aa_CC@b -> aa_CC_b */ + } + + const char *q; + if ((q = uprv_strchr(p, '.')) != nullptr) { + /* How big will the resulting string be? */ + int32_t len = (int32_t)(uprv_strlen(correctedPOSIXLocale) + (q-p)); + uprv_strncat(correctedPOSIXLocale, p, q-p); // do not include charset + correctedPOSIXLocale[len] = 0; + } + else { + /* Anything following the @ sign */ + uprv_strcat(correctedPOSIXLocale, p); + } + + /* Should there be a map from 'no@nynorsk' -> no_NO_NY here? + * How about 'russian' -> 'ru'? + * Many of the other locales using ISO codes will be handled by the + * canonicalization functions in uloc_getDefault. + */ + } + + if (gCorrectedPOSIXLocale == nullptr) { + gCorrectedPOSIXLocale = correctedPOSIXLocale; + gCorrectedPOSIXLocaleHeapAllocated = true; + ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup); + correctedPOSIXLocale = nullptr; + } + posixID = gCorrectedPOSIXLocale; + + if (correctedPOSIXLocale != nullptr) { /* Was already set - clean up. */ + uprv_free(correctedPOSIXLocale); + } + + return posixID; + +#elif U_PLATFORM_USES_ONLY_WIN32_API +#define POSIX_LOCALE_CAPACITY 64 + UErrorCode status = U_ZERO_ERROR; + char *correctedPOSIXLocale = nullptr; + + // If we have already figured this out just use the cached value + if (gCorrectedPOSIXLocale != nullptr) { + return gCorrectedPOSIXLocale; + } + + // No cached value, need to determine the current value + static WCHAR windowsLocale[LOCALE_NAME_MAX_LENGTH] = {}; + int length = GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, windowsLocale, LOCALE_NAME_MAX_LENGTH); + + // Now we should have a Windows locale name that needs converted to the POSIX style. + if (length > 0) // If length is 0, then the GetLocaleInfoEx failed. + { + // First we need to go from UTF-16 to char (and also convert from _ to - while we're at it.) + char modifiedWindowsLocale[LOCALE_NAME_MAX_LENGTH] = {}; + + int32_t i; + for (i = 0; i < UPRV_LENGTHOF(modifiedWindowsLocale); i++) + { + if (windowsLocale[i] == '_') + { + modifiedWindowsLocale[i] = '-'; + } + else + { + modifiedWindowsLocale[i] = static_cast(windowsLocale[i]); + } + + if (modifiedWindowsLocale[i] == '\0') + { + break; + } + } + + if (i >= UPRV_LENGTHOF(modifiedWindowsLocale)) + { + // Ran out of room, can't really happen, maybe we'll be lucky about a matching + // locale when tags are dropped + modifiedWindowsLocale[UPRV_LENGTHOF(modifiedWindowsLocale) - 1] = '\0'; + } + + // Now normalize the resulting name + correctedPOSIXLocale = static_cast(uprv_malloc(POSIX_LOCALE_CAPACITY + 1)); + /* TODO: Should we just exit on memory allocation failure? */ + if (correctedPOSIXLocale) + { + int32_t posixLen = uloc_canonicalize(modifiedWindowsLocale, correctedPOSIXLocale, POSIX_LOCALE_CAPACITY, &status); + if (U_SUCCESS(status)) + { + *(correctedPOSIXLocale + posixLen) = 0; + gCorrectedPOSIXLocale = correctedPOSIXLocale; + gCorrectedPOSIXLocaleHeapAllocated = true; + ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup); + } + else + { + uprv_free(correctedPOSIXLocale); + } + } + } + + // If unable to find a locale we can agree upon, use en-US by default + if (gCorrectedPOSIXLocale == nullptr) { + gCorrectedPOSIXLocale = "en_US"; + } + return gCorrectedPOSIXLocale; + +#elif U_PLATFORM == U_PF_OS400 + /* locales are process scoped and are by definition thread safe */ + static char correctedLocale[64]; + const char *localeID = getenv("LC_ALL"); + char *p; + + if (localeID == NULL) + localeID = getenv("LANG"); + if (localeID == NULL) + localeID = setlocale(LC_ALL, NULL); + /* Make sure we have something... */ + if (localeID == NULL) + return "en_US_POSIX"; + + /* Extract the locale name from the path. */ + if((p = uprv_strrchr(localeID, '/')) != NULL) + { + /* Increment p to start of locale name. */ + p++; + localeID = p; + } + + /* Copy to work location. */ + uprv_strcpy(correctedLocale, localeID); + + /* Strip off the '.locale' extension. */ + if((p = uprv_strchr(correctedLocale, '.')) != NULL) { + *p = 0; + } + + /* Upper case the locale name. */ + T_CString_toUpperCase(correctedLocale); + + /* See if we are using the POSIX locale. Any of the + * following are equivalent and use the same QLGPGCMA + * (POSIX) locale. + * QLGPGCMA2 means UCS2 + * QLGPGCMA_4 means UTF-32 + * QLGPGCMA_8 means UTF-8 + */ + if ((uprv_strcmp("C", correctedLocale) == 0) || + (uprv_strcmp("POSIX", correctedLocale) == 0) || + (uprv_strncmp("QLGPGCMA", correctedLocale, 8) == 0)) + { + uprv_strcpy(correctedLocale, "en_US_POSIX"); + } + else + { + int16_t LocaleLen; + + /* Lower case the lang portion. */ + for(p = correctedLocale; *p != 0 && *p != '_'; p++) + { + *p = uprv_tolower(*p); + } + + /* Adjust for Euro. After '_E' add 'URO'. */ + LocaleLen = uprv_strlen(correctedLocale); + if (correctedLocale[LocaleLen - 2] == '_' && + correctedLocale[LocaleLen - 1] == 'E') + { + uprv_strcat(correctedLocale, "URO"); + } + + /* If using Lotus-based locale then convert to + * equivalent non Lotus. + */ + else if (correctedLocale[LocaleLen - 2] == '_' && + correctedLocale[LocaleLen - 1] == 'L') + { + correctedLocale[LocaleLen - 2] = 0; + } + + /* There are separate simplified and traditional + * locales called zh_HK_S and zh_HK_T. + */ + else if (uprv_strncmp(correctedLocale, "zh_HK", 5) == 0) + { + uprv_strcpy(correctedLocale, "zh_HK"); + } + + /* A special zh_CN_GBK locale... + */ + else if (uprv_strcmp(correctedLocale, "zh_CN_GBK") == 0) + { + uprv_strcpy(correctedLocale, "zh_CN"); + } + + } + + return correctedLocale; +#endif + +} + +#if !U_CHARSET_IS_UTF8 +#if U_POSIX_LOCALE +/* +Due to various platform differences, one platform may specify a charset, +when they really mean a different charset. Remap the names so that they are +compatible with ICU. Only conflicting/ambiguous aliases should be resolved +here. Before adding anything to this function, please consider adding unique +names to the ICU alias table in the data directory. +*/ +static const char* +remapPlatformDependentCodepage(const char *locale, const char *name) { + if (locale != NULL && *locale == 0) { + /* Make sure that an empty locale is handled the same way. */ + locale = NULL; + } + if (name == NULL) { + return NULL; + } +#if U_PLATFORM == U_PF_AIX + if (uprv_strcmp(name, "IBM-943") == 0) { + /* Use the ASCII compatible ibm-943 */ + name = "Shift-JIS"; + } + else if (uprv_strcmp(name, "IBM-1252") == 0) { + /* Use the windows-1252 that contains the Euro */ + name = "IBM-5348"; + } +#elif U_PLATFORM == U_PF_SOLARIS + if (locale != NULL && uprv_strcmp(name, "EUC") == 0) { + /* Solaris underspecifies the "EUC" name. */ + if (uprv_strcmp(locale, "zh_CN") == 0) { + name = "EUC-CN"; + } + else if (uprv_strcmp(locale, "zh_TW") == 0) { + name = "EUC-TW"; + } + else if (uprv_strcmp(locale, "ko_KR") == 0) { + name = "EUC-KR"; + } + } + else if (uprv_strcmp(name, "eucJP") == 0) { + /* + ibm-954 is the best match. + ibm-33722 is the default for eucJP (similar to Windows). + */ + name = "eucjis"; + } + else if (uprv_strcmp(name, "646") == 0) { + /* + * The default codepage given by Solaris is 646 but the C library routines treat it as if it was + * ISO-8859-1 instead of US-ASCII(646). + */ + name = "ISO-8859-1"; + } +#elif U_PLATFORM_IS_DARWIN_BASED + if (locale == NULL && *name == 0) { + /* + No locale was specified, and an empty name was passed in. + This usually indicates that nl_langinfo didn't return valid information. + Mac OS X uses UTF-8 by default (especially the locale data and console). + */ + name = "UTF-8"; + } + else if (uprv_strcmp(name, "CP949") == 0) { + /* Remap CP949 to a similar codepage to avoid issues with backslash and won symbol. */ + name = "EUC-KR"; + } + else if (locale != NULL && uprv_strcmp(locale, "en_US_POSIX") != 0 && uprv_strcmp(name, "US-ASCII") == 0) { + /* + * For non C/POSIX locale, default the code page to UTF-8 instead of US-ASCII. + */ + name = "UTF-8"; + } +#elif U_PLATFORM == U_PF_BSD + if (uprv_strcmp(name, "CP949") == 0) { + /* Remap CP949 to a similar codepage to avoid issues with backslash and won symbol. */ + name = "EUC-KR"; + } +#elif U_PLATFORM == U_PF_HPUX + if (locale != NULL && uprv_strcmp(locale, "zh_HK") == 0 && uprv_strcmp(name, "big5") == 0) { + /* HP decided to extend big5 as hkbig5 even though it's not compatible :-( */ + /* zh_TW.big5 is not the same charset as zh_HK.big5! */ + name = "hkbig5"; + } + else if (uprv_strcmp(name, "eucJP") == 0) { + /* + ibm-1350 is the best match, but unavailable. + ibm-954 is mostly a superset of ibm-1350. + ibm-33722 is the default for eucJP (similar to Windows). + */ + name = "eucjis"; + } +#elif U_PLATFORM == U_PF_LINUX + if (locale != NULL && uprv_strcmp(name, "euc") == 0) { + /* Linux underspecifies the "EUC" name. */ + if (uprv_strcmp(locale, "korean") == 0) { + name = "EUC-KR"; + } + else if (uprv_strcmp(locale, "japanese") == 0) { + /* See comment below about eucJP */ + name = "eucjis"; + } + } + else if (uprv_strcmp(name, "eucjp") == 0) { + /* + ibm-1350 is the best match, but unavailable. + ibm-954 is mostly a superset of ibm-1350. + ibm-33722 is the default for eucJP (similar to Windows). + */ + name = "eucjis"; + } + else if (locale != NULL && uprv_strcmp(locale, "en_US_POSIX") != 0 && + (uprv_strcmp(name, "ANSI_X3.4-1968") == 0 || uprv_strcmp(name, "US-ASCII") == 0)) { + /* + * For non C/POSIX locale, default the code page to UTF-8 instead of US-ASCII. + */ + name = "UTF-8"; + } + /* + * Linux returns ANSI_X3.4-1968 for C/POSIX, but the call site takes care of + * it by falling back to 'US-ASCII' when NULL is returned from this + * function. So, we don't have to worry about it here. + */ +#endif + /* return NULL when "" is passed in */ + if (*name == 0) { + name = NULL; + } + return name; +} + +static const char* +getCodepageFromPOSIXID(const char *localeName, char * buffer, int32_t buffCapacity) +{ + char localeBuf[100]; + const char *name = NULL; + char *variant = NULL; + + if (localeName != NULL && (name = (uprv_strchr(localeName, '.'))) != NULL) { + size_t localeCapacity = uprv_min(sizeof(localeBuf), (name-localeName)+1); + uprv_strncpy(localeBuf, localeName, localeCapacity); + localeBuf[localeCapacity-1] = 0; /* ensure NULL termination */ + name = uprv_strncpy(buffer, name+1, buffCapacity); + buffer[buffCapacity-1] = 0; /* ensure NULL termination */ + if ((variant = const_cast(uprv_strchr(name, '@'))) != NULL) { + *variant = 0; + } + name = remapPlatformDependentCodepage(localeBuf, name); + } + return name; +} +#endif + +static const char* +int_getDefaultCodepage() +{ +#if U_PLATFORM == U_PF_OS400 + uint32_t ccsid = 37; /* Default to ibm-37 */ + static char codepage[64]; + Qwc_JOBI0400_t jobinfo; + Qus_EC_t error = { sizeof(Qus_EC_t) }; /* SPI error code */ + + EPT_CALL(QUSRJOBI)(&jobinfo, sizeof(jobinfo), "JOBI0400", + "* ", " ", &error); + + if (error.Bytes_Available == 0) { + if (jobinfo.Coded_Char_Set_ID != 0xFFFF) { + ccsid = (uint32_t)jobinfo.Coded_Char_Set_ID; + } + else if (jobinfo.Default_Coded_Char_Set_Id != 0xFFFF) { + ccsid = (uint32_t)jobinfo.Default_Coded_Char_Set_Id; + } + /* else use the default */ + } + sprintf(codepage,"ibm-%d", ccsid); + return codepage; + +#elif U_PLATFORM == U_PF_OS390 + static char codepage[64]; + + strncpy(codepage, nl_langinfo(CODESET),63-strlen(UCNV_SWAP_LFNL_OPTION_STRING)); + strcat(codepage,UCNV_SWAP_LFNL_OPTION_STRING); + codepage[63] = 0; /* NULL terminate */ + + return codepage; + +#elif U_PLATFORM_USES_ONLY_WIN32_API + static char codepage[64]; + DWORD codepageNumber = 0; + +#if U_PLATFORM_HAS_WINUWP_API > 0 + // UWP doesn't have a direct API to get the default ACP as Microsoft would rather + // have folks use Unicode than a "system" code page, however this is the same + // codepage as the system default locale codepage. (FWIW, the system locale is + // ONLY used for codepage, it should never be used for anything else) + GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, + (LPWSTR)&codepageNumber, sizeof(codepageNumber) / sizeof(WCHAR)); +#else + // Win32 apps can call GetACP + codepageNumber = GetACP(); +#endif + // Special case for UTF-8 + if (codepageNumber == 65001) + { + return "UTF-8"; + } + // Windows codepages can look like windows-1252, so format the found number + // the numbers are eclectic, however all valid system code pages, besides UTF-8 + // are between 3 and 19999 + if (codepageNumber > 0 && codepageNumber < 20000) + { + sprintf(codepage, "windows-%ld", codepageNumber); + return codepage; + } + // If the codepage number call failed then return UTF-8 + return "UTF-8"; + +#elif U_POSIX_LOCALE + static char codesetName[100]; + const char *localeName = NULL; + const char *name = NULL; + + localeName = uprv_getPOSIXIDForDefaultCodepage(); + uprv_memset(codesetName, 0, sizeof(codesetName)); + /* On Solaris nl_langinfo returns C locale values unless setlocale + * was called earlier. + */ +#if (U_HAVE_NL_LANGINFO_CODESET && U_PLATFORM != U_PF_SOLARIS) + /* When available, check nl_langinfo first because it usually gives more + useful names. It depends on LC_CTYPE. + nl_langinfo may use the same buffer as setlocale. */ + { + const char *codeset = nl_langinfo(U_NL_LANGINFO_CODESET); +#if U_PLATFORM_IS_DARWIN_BASED || U_PLATFORM_IS_LINUX_BASED + /* + * On Linux and MacOSX, ensure that default codepage for non C/POSIX locale is UTF-8 + * instead of ASCII. + */ + if (uprv_strcmp(localeName, "en_US_POSIX") != 0) { + codeset = remapPlatformDependentCodepage(localeName, codeset); + } else +#endif + { + codeset = remapPlatformDependentCodepage(NULL, codeset); + } + + if (codeset != NULL) { + uprv_strncpy(codesetName, codeset, sizeof(codesetName)); + codesetName[sizeof(codesetName)-1] = 0; + return codesetName; + } + } +#endif + + /* Use setlocale in a nice way, and then check some environment variables. + Maybe the application used setlocale already. + */ + uprv_memset(codesetName, 0, sizeof(codesetName)); + name = getCodepageFromPOSIXID(localeName, codesetName, sizeof(codesetName)); + if (name) { + /* if we can find the codeset name from setlocale, return that. */ + return name; + } + + if (*codesetName == 0) + { + /* Everything failed. Return US ASCII (ISO 646). */ + (void)uprv_strcpy(codesetName, "US-ASCII"); + } + return codesetName; +#else + return "US-ASCII"; +#endif +} + + +U_CAPI const char* U_EXPORT2 +uprv_getDefaultCodepage() +{ + static char const *name = NULL; + umtx_lock(NULL); + if (name == NULL) { + name = int_getDefaultCodepage(); + } + umtx_unlock(NULL); + return name; +} +#endif /* !U_CHARSET_IS_UTF8 */ + + +/* end of platform-specific implementation -------------- */ + +/* version handling --------------------------------------------------------- */ + +U_CAPI void U_EXPORT2 +u_versionFromString(UVersionInfo versionArray, const char *versionString) { + char *end; + uint16_t part=0; + + if(versionArray==NULL) { + return; + } + + if(versionString!=NULL) { + for(;;) { + versionArray[part]=(uint8_t)uprv_strtoul(versionString, &end, 10); + if(end==versionString || ++part==U_MAX_VERSION_LENGTH || *end!=U_VERSION_DELIMITER) { + break; + } + versionString=end+1; + } + } + + while(partU_MAX_VERSION_STRING_LENGTH) { + len = U_MAX_VERSION_STRING_LENGTH; + } + u_UCharsToChars(versionString, versionChars, len); + versionChars[len]=0; + u_versionFromString(versionArray, versionChars); + } +} + +U_CAPI void U_EXPORT2 +u_versionToString(const UVersionInfo versionArray, char *versionString) { + uint16_t count, part; + uint8_t field; + + if(versionString==NULL) { + return; + } + + if(versionArray==NULL) { + versionString[0]=0; + return; + } + + /* count how many fields need to be written */ + for(count=4; count>0 && versionArray[count-1]==0; --count) { + } + + if(count <= 1) { + count = 2; + } + + /* write the first part */ + /* write the decimal field value */ + field=versionArray[0]; + if(field>=100) { + *versionString++=(char)('0'+field/100); + field%=100; + } + if(field>=10) { + *versionString++=(char)('0'+field/10); + field%=10; + } + *versionString++=(char)('0'+field); + + /* write the following parts */ + for(part=1; part=100) { + *versionString++=(char)('0'+field/100); + field%=100; + } + if(field>=10) { + *versionString++=(char)('0'+field/10); + field%=10; + } + *versionString++=(char)('0'+field); + } + + /* NUL-terminate */ + *versionString=0; +} + +U_CAPI void U_EXPORT2 +u_getVersion(UVersionInfo versionArray) { + (void)copyright; // Suppress unused variable warning from clang. + u_versionFromString(versionArray, U_ICU_VERSION); +} + +/** + * icucfg.h dependent code + */ + +#if U_ENABLE_DYLOAD && HAVE_DLOPEN && !U_PLATFORM_USES_ONLY_WIN32_API + +#if HAVE_DLFCN_H +#ifdef __MVS__ +#ifndef __SUSV3 +#define __SUSV3 1 +#endif +#endif +#include +#endif /* HAVE_DLFCN_H */ + +U_INTERNAL void * U_EXPORT2 +uprv_dl_open(const char *libName, UErrorCode *status) { + void *ret = NULL; + if(U_FAILURE(*status)) return ret; + ret = dlopen(libName, RTLD_NOW|RTLD_GLOBAL); + if(ret==NULL) { +#ifdef U_TRACE_DYLOAD + printf("dlerror on dlopen(%s): %s\n", libName, dlerror()); +#endif + *status = U_MISSING_RESOURCE_ERROR; + } + return ret; +} + +U_INTERNAL void U_EXPORT2 +uprv_dl_close(void *lib, UErrorCode *status) { + if(U_FAILURE(*status)) return; + dlclose(lib); +} + +U_INTERNAL UVoidFunction* U_EXPORT2 +uprv_dlsym_func(void *lib, const char* sym, UErrorCode *status) { + union { + UVoidFunction *fp; + void *vp; + } uret; + uret.fp = NULL; + if(U_FAILURE(*status)) return uret.fp; + uret.vp = dlsym(lib, sym); + if(uret.vp == NULL) { +#ifdef U_TRACE_DYLOAD + printf("dlerror on dlsym(%p,%s): %s\n", lib,sym, dlerror()); +#endif + *status = U_MISSING_RESOURCE_ERROR; + } + return uret.fp; +} + +#elif U_ENABLE_DYLOAD && U_PLATFORM_USES_ONLY_WIN32_API && !U_PLATFORM_HAS_WINUWP_API + +/* Windows API implementation. */ +// Note: UWP does not expose/allow these APIs, so the UWP version gets the null implementation. */ + +U_INTERNAL void * U_EXPORT2 +uprv_dl_open(const char *libName, UErrorCode *status) { + HMODULE lib = NULL; + + if(U_FAILURE(*status)) return NULL; + + lib = LoadLibraryA(libName); + + if(lib==NULL) { + *status = U_MISSING_RESOURCE_ERROR; + } + + return (void*)lib; +} + +U_INTERNAL void U_EXPORT2 +uprv_dl_close(void *lib, UErrorCode *status) { + HMODULE handle = (HMODULE)lib; + if(U_FAILURE(*status)) return; + + FreeLibrary(handle); + + return; +} + +U_INTERNAL UVoidFunction* U_EXPORT2 +uprv_dlsym_func(void *lib, const char* sym, UErrorCode *status) { + HMODULE handle = (HMODULE)lib; + UVoidFunction* addr = NULL; + + if(U_FAILURE(*status) || lib==NULL) return NULL; + + addr = (UVoidFunction*)GetProcAddress(handle, sym); + + if(addr==NULL) { + DWORD lastError = GetLastError(); + if(lastError == ERROR_PROC_NOT_FOUND) { + *status = U_MISSING_RESOURCE_ERROR; + } else { + *status = U_UNSUPPORTED_ERROR; /* other unknown error. */ + } + } + + return addr; +} + +#else + +/* No dynamic loading, null (nonexistent) implementation. */ + +U_INTERNAL void * U_EXPORT2 +uprv_dl_open(const char *libName, UErrorCode *status) { + (void)libName; + if(U_FAILURE(*status)) return NULL; + *status = U_UNSUPPORTED_ERROR; + return NULL; +} + +U_INTERNAL void U_EXPORT2 +uprv_dl_close(void *lib, UErrorCode *status) { + (void)lib; + if(U_FAILURE(*status)) return; + *status = U_UNSUPPORTED_ERROR; + return; +} + +U_INTERNAL UVoidFunction* U_EXPORT2 +uprv_dlsym_func(void *lib, const char* sym, UErrorCode *status) { + (void)lib; + (void)sym; + if(U_SUCCESS(*status)) { + *status = U_UNSUPPORTED_ERROR; + } + return (UVoidFunction*)NULL; +} + +#endif + +/* + * Hey, Emacs, please set the following: + * + * Local Variables: + * indent-tabs-mode: nil + * End: + * + */ From dc510fb435931328129b65a145fc44857a7ddc5a Mon Sep 17 00:00:00 2001 From: gengjiawen Date: Tue, 23 Apr 2019 22:50:54 +0800 Subject: [PATCH 47/49] report: print common items first for readability PR-URL: https://github.com/nodejs/node/pull/27367 Reviewed-By: Richard Lau Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Yongsheng Zhang --- doc/api/report.md | 34 +++++++++++++-------------- src/node_report_utils.cc | 10 ++++---- test/report/test-report-uv-handles.js | 16 +++++++++++++ 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/doc/api/report.md b/doc/api/report.md index 90ec1b7f1807e8..40e3050ee4d24d 100644 --- a/doc/api/report.md +++ b/doc/api/report.md @@ -190,13 +190,13 @@ is provided below for reference. "details": "" }, { - "repeat": 0, - "firesInMsFromNow": 94403548320796, - "expired": true, "type": "timer", "is_active": false, "is_referenced": false, - "address": "0x00007fff5fbfeab0" + "address": "0x00007fff5fbfeab0", + "repeat": 0, + "firesInMsFromNow": 94403548320796, + "expired": true }, { "type": "check", @@ -229,36 +229,36 @@ is provided below for reference. "address": "0x000000010188f2e0" }, { + "type": "tty", + "is_active": false, + "is_referenced": true, + "address": "0x000055b581db0e18", "width": 204, "height": 55, "fd": 17, "writeQueueSize": 0, "readable": true, - "writable": true, - "type": "tty", - "is_active": false, - "is_referenced": true, - "address": "0x000055b581db0e18" + "writable": true }, { - "signum": 28, - "signal": "SIGWINCH", "type": "signal", "is_active": true, "is_referenced": false, - "address": "0x000055b581d80010" + "address": "0x000055b581d80010", + "signum": 28, + "signal": "SIGWINCH" }, { + "type": "tty", + "is_active": true, + "is_referenced": true, + "address": "0x000055b581df59f8", "width": 204, "height": 55, "fd": 19, "writeQueueSize": 0, "readable": true, - "writable": true, - "type": "tty", - "is_active": true, - "is_referenced": true, - "address": "0x000055b581df59f8" + "writable": true }, { "type": "loop", diff --git a/src/node_report_utils.cc b/src/node_report_utils.cc index 81fdeda0405ea4..24f3b6175d5ad6 100644 --- a/src/node_report_utils.cc +++ b/src/node_report_utils.cc @@ -126,6 +126,11 @@ void WalkHandle(uv_handle_t* h, void* arg) { uv_any_handle* handle = reinterpret_cast(h); writer->json_start(); + writer->json_keyvalue("type", type); + writer->json_keyvalue("is_active", static_cast(uv_is_active(h))); + writer->json_keyvalue("is_referenced", static_cast(uv_has_ref(h))); + writer->json_keyvalue("address", + ValueToHexString(reinterpret_cast(h))); switch (h->type) { case UV_FS_EVENT: @@ -216,11 +221,6 @@ void WalkHandle(uv_handle_t* h, void* arg) { static_cast(uv_is_writable(&handle->stream))); } - writer->json_keyvalue("type", type); - writer->json_keyvalue("is_active", static_cast(uv_is_active(h))); - writer->json_keyvalue("is_referenced", static_cast(uv_has_ref(h))); - writer->json_keyvalue("address", - ValueToHexString(reinterpret_cast(h))); writer->json_end(); } diff --git a/test/report/test-report-uv-handles.js b/test/report/test-report-uv-handles.js index f03791f13461bc..51677168947bf4 100644 --- a/test/report/test-report-uv-handles.js +++ b/test/report/test-report-uv-handles.js @@ -97,6 +97,22 @@ if (process.argv[2] === 'child') { const reports = helper.findReports(child.pid, tmpdir.path); assert.deepStrictEqual(reports, [], report_msg, reports); + // Test libuv handle key order + { + const get_libuv = /"libuv":\s\[([\s\S]*?)\]/gm; + const get_handle_inner = /{([\s\S]*?),*?}/gm; + const libuv_handles_str = get_libuv.exec(stdout)[1]; + const libuv_handles_array = libuv_handles_str.match(get_handle_inner); + for (const i of libuv_handles_array) { + // Exclude nested structure + if (i.includes('type')) { + const handle_keys = i.match(/(".*"):/gm); + assert(handle_keys[0], 'type'); + assert(handle_keys[1], 'is_active'); + } + } + } + const report = JSON.parse(stdout); const prefix = common.isWindows ? '\\\\?\\' : ''; const expected_filename = `${prefix}${__filename}`; From d00014e5999c41d54b3287498316157a7e64366b Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 25 Apr 2019 03:03:48 +0800 Subject: [PATCH 48/49] process: reduce the number of internal frames in async stack trace Previously, we call the JS land `runNextTicks` implementation immediately from JS land after evaluating the main module or the input, so these synchronous JS call frames would show up in the stack trace of the async errors, which can be confusing. This patch moves those calls into C++ so that more of these internal scheduler implementation details can be hidden and the users can see a cleaner a cleaner async JS stack trace. PR-URL: https://github.com/nodejs/node/pull/27392 Reviewed-By: Ruben Bridgewater Reviewed-By: Gus Caplan Reviewed-By: Minwoo Jung Reviewed-By: Benjamin Gruenbaum --- lib/internal/modules/cjs/loader.js | 4 -- lib/internal/process/execution.js | 4 -- lib/internal/process/task_queues.js | 1 + src/node.cc | 10 +++-- src/node_process.h | 10 +++++ src/node_task_queue.cc | 15 +++++++ test/fixtures/async-error.js | 27 ++++++++++++ test/message/async_error_eval_cjs.js | 39 +++++++++++++++++ test/message/async_error_eval_cjs.out | 6 +++ test/message/async_error_eval_esm.js | 42 +++++++++++++++++++ test/message/async_error_eval_esm.out | 7 ++++ test/message/async_error_microtask_main.js | 13 ++++++ test/message/async_error_microtask_main.out | 6 +++ test/message/async_error_nexttick_main.js | 13 ++++++ test/message/async_error_nexttick_main.out | 7 ++++ test/message/async_error_sync_esm.mjs | 14 +++++++ test/message/async_error_sync_esm.out | 7 ++++ test/message/async_error_sync_main.js | 13 ++++++ test/message/async_error_sync_main.out | 6 +++ .../events_unhandled_error_nexttick.out | 3 -- test/message/nexttick_throw.out | 3 -- .../unhandled_promise_trace_warnings.out | 6 --- 22 files changed, 233 insertions(+), 23 deletions(-) create mode 100644 test/fixtures/async-error.js create mode 100644 test/message/async_error_eval_cjs.js create mode 100644 test/message/async_error_eval_cjs.out create mode 100644 test/message/async_error_eval_esm.js create mode 100644 test/message/async_error_eval_esm.out create mode 100644 test/message/async_error_microtask_main.js create mode 100644 test/message/async_error_microtask_main.out create mode 100644 test/message/async_error_nexttick_main.js create mode 100644 test/message/async_error_nexttick_main.out create mode 100644 test/message/async_error_sync_esm.mjs create mode 100644 test/message/async_error_sync_esm.out create mode 100644 test/message/async_error_sync_main.js create mode 100644 test/message/async_error_sync_main.out diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index a1e5046d406a1a..96b5e8b7b881b6 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -819,13 +819,9 @@ Module.runMain = function() { true /* fromPromise */ ); }); - // Handle any nextTicks added in the first tick of the program - process._tickCallback(); return; } Module._load(process.argv[1], null, true); - // Handle any nextTicks added in the first tick of the program - process._tickCallback(); }; Module.createRequireFromPath = (filename) => { diff --git a/lib/internal/process/execution.js b/lib/internal/process/execution.js index addd450ed3551c..19866d17226982 100644 --- a/lib/internal/process/execution.js +++ b/lib/internal/process/execution.js @@ -50,8 +50,6 @@ function evalModule(source, print) { error(e); process.exit(1); }); - // Handle any nextTicks added in the first tick of the program. - process._tickCallback(); } function evalScript(name, body, breakFirstLine, print) { @@ -83,8 +81,6 @@ function evalScript(name, body, breakFirstLine, print) { const { log } = require('internal/console/global'); log(result); } - // Handle any nextTicks added in the first tick of the program. - process._tickCallback(); } const exceptionHandlerState = { captureFn: null }; diff --git a/lib/internal/process/task_queues.js b/lib/internal/process/task_queues.js index 12e34b7ff79068..309e27e6ecd0fa 100644 --- a/lib/internal/process/task_queues.js +++ b/lib/internal/process/task_queues.js @@ -49,6 +49,7 @@ function setHasTickScheduled(value) { const queue = new FixedQueue(); +// Should be in sync with RunNextTicksNative in node_task_queue.cc function runNextTicks() { if (!hasTickScheduled() && !hasRejectionToWarn()) runMicrotasks(); diff --git a/src/node.cc b/src/node.cc index 10ef0d5bc7c522..636a92eab3f760 100644 --- a/src/node.cc +++ b/src/node.cc @@ -379,9 +379,13 @@ MaybeLocal StartExecution(Environment* env, const char* main_script_id) { ->GetFunction(env->context()) .ToLocalChecked()}; - MaybeLocal result = - ExecuteBootstrapper(env, main_script_id, ¶meters, &arguments); - return scope.EscapeMaybe(result); + Local result; + if (!ExecuteBootstrapper(env, main_script_id, ¶meters, &arguments) + .ToLocal(&result) || + !task_queue::RunNextTicksNative(env)) { + return MaybeLocal(); + } + return scope.Escape(result); } MaybeLocal StartMainThreadExecution(Environment* env) { diff --git a/src/node_process.h b/src/node_process.h index cb6ad85828b608..f01445b3c515d0 100644 --- a/src/node_process.h +++ b/src/node_process.h @@ -36,6 +36,16 @@ v8::MaybeLocal CreateProcessObject( const std::vector& args, const std::vector& exec_args); void PatchProcessObject(const v8::FunctionCallbackInfo& args); + +namespace task_queue { +// Handle any nextTicks added in the first tick of the program. +// We use the native version here for once so that any microtasks +// created by the main module is then handled from C++, and +// the call stack of the main script does not show up in the async error +// stack trace. +bool RunNextTicksNative(Environment* env); +} // namespace task_queue + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // SRC_NODE_PROCESS_H_ diff --git a/src/node_task_queue.cc b/src/node_task_queue.cc index b125f5d01ece66..af1d96fab22810 100644 --- a/src/node_task_queue.cc +++ b/src/node_task_queue.cc @@ -2,6 +2,7 @@ #include "node.h" #include "node_errors.h" #include "node_internals.h" +#include "node_process.h" #include "v8.h" #include @@ -38,6 +39,20 @@ static void EnqueueMicrotask(const FunctionCallbackInfo& args) { isolate->EnqueueMicrotask(args[0].As()); } +// Should be in sync with runNextTicks in internal/process/task_queues.js +bool RunNextTicksNative(Environment* env) { + TickInfo* tick_info = env->tick_info(); + if (!tick_info->has_tick_scheduled() && !tick_info->has_rejection_to_warn()) + env->isolate()->RunMicrotasks(); + if (!tick_info->has_tick_scheduled() && !tick_info->has_rejection_to_warn()) + return true; + + Local callback = env->tick_callback_function(); + CHECK(!callback.IsEmpty()); + return !callback->Call(env->context(), env->process_object(), 0, nullptr) + .IsEmpty(); +} + static void RunMicrotasks(const FunctionCallbackInfo& args) { args.GetIsolate()->RunMicrotasks(); } diff --git a/test/fixtures/async-error.js b/test/fixtures/async-error.js new file mode 100644 index 00000000000000..48b19b40d8fb54 --- /dev/null +++ b/test/fixtures/async-error.js @@ -0,0 +1,27 @@ +'use strict'; + +async function one() { + throw new Error('test'); +} + +async function breaker() { + return true; +} + +async function stack() { + await breaker(); +} + +async function two() { + await stack(); + await one(); +} +async function three() { + await two(); +} + +async function four() { + await three(); +} + +module.exports = four; diff --git a/test/message/async_error_eval_cjs.js b/test/message/async_error_eval_cjs.js new file mode 100644 index 00000000000000..a2caaa0937745e --- /dev/null +++ b/test/message/async_error_eval_cjs.js @@ -0,0 +1,39 @@ +'use strict'; + +require('../common'); +const { spawnSync } = require('child_process'); + +const four = require('../common/fixtures') + .readSync('async-error.js') + .toString() + .split('\n') + .slice(2, -2) + .join('\n'); + +const main = `${four} + +async function main() { + try { + await four(); + } catch (e) { + console.log(e); + } +} + +main(); +`; + +// --eval CJS +{ + const child = spawnSync(process.execPath, [ + '-e', + main + ], { + env: { ...process.env } + }); + + if (child.status !== 0) { + console.error(child.stderr.toString()); + } + console.error(child.stdout.toString()); +} diff --git a/test/message/async_error_eval_cjs.out b/test/message/async_error_eval_cjs.out new file mode 100644 index 00000000000000..a8d89eebcb6ada --- /dev/null +++ b/test/message/async_error_eval_cjs.out @@ -0,0 +1,6 @@ +Error: test + at one ([eval]:2:9) + at two ([eval]:15:9) + at async three ([eval]:18:3) + at async four ([eval]:22:3) + at async main ([eval]:28:5) \ No newline at end of file diff --git a/test/message/async_error_eval_esm.js b/test/message/async_error_eval_esm.js new file mode 100644 index 00000000000000..33675b5a9db286 --- /dev/null +++ b/test/message/async_error_eval_esm.js @@ -0,0 +1,42 @@ +'use strict'; + +require('../common'); +const { spawnSync } = require('child_process'); + +const four = require('../common/fixtures') + .readSync('async-error.js') + .toString() + .split('\n') + .slice(2, -2) + .join('\n'); + +const main = `${four} + +async function main() { + try { + await four(); + } catch (e) { + console.log(e); + } +} + +main(); +`; + +// --eval ESM +{ + const child = spawnSync(process.execPath, [ + '--experimental-modules', + '--input-type', + 'module', + '-e', + main + ], { + env: { ...process.env } + }); + + if (child.status !== 0) { + console.error(child.stderr.toString()); + } + console.error(child.stdout.toString()); +} diff --git a/test/message/async_error_eval_esm.out b/test/message/async_error_eval_esm.out new file mode 100644 index 00000000000000..91ce0ce21cd456 --- /dev/null +++ b/test/message/async_error_eval_esm.out @@ -0,0 +1,7 @@ +Error: test + at one (eval:1:2:9) + at two (eval:1:15:9) + at processTicksAndRejections (internal/process/task_queues.js:*:*) + at async three (eval:1:18:3) + at async four (eval:1:22:3) + at async main (eval:1:28:5) diff --git a/test/message/async_error_microtask_main.js b/test/message/async_error_microtask_main.js new file mode 100644 index 00000000000000..5520c650addeac --- /dev/null +++ b/test/message/async_error_microtask_main.js @@ -0,0 +1,13 @@ +'use strict'; +require('../common'); +const four = require('../fixtures/async-error'); + +async function main() { + try { + await four(); + } catch (e) { + console.error(e); + } +} + +queueMicrotask(main); diff --git a/test/message/async_error_microtask_main.out b/test/message/async_error_microtask_main.out new file mode 100644 index 00000000000000..9512270730a976 --- /dev/null +++ b/test/message/async_error_microtask_main.out @@ -0,0 +1,6 @@ +Error: test + at one (*fixtures*async-error.js:4:9) + at two (*fixtures*async-error.js:17:9) + at async three (*fixtures*async-error.js:20:3) + at async four (*fixtures*async-error.js:24:3) + at async main (*message*async_error_microtask_main.js:7:5) diff --git a/test/message/async_error_nexttick_main.js b/test/message/async_error_nexttick_main.js new file mode 100644 index 00000000000000..ecd0531852da8f --- /dev/null +++ b/test/message/async_error_nexttick_main.js @@ -0,0 +1,13 @@ +'use strict'; +require('../common'); +const four = require('../fixtures/async-error'); + +async function main() { + try { + await four(); + } catch (e) { + console.error(e); + } +} + +process.nextTick(main); diff --git a/test/message/async_error_nexttick_main.out b/test/message/async_error_nexttick_main.out new file mode 100644 index 00000000000000..c6ac1e418086ac --- /dev/null +++ b/test/message/async_error_nexttick_main.out @@ -0,0 +1,7 @@ +Error: test + at one (*fixtures*async-error.js:4:9) + at two (*fixtures*async-error.js:17:9) + at processTicksAndRejections (internal/process/task_queues.js:*:*) + at async three (*fixtures*async-error.js:20:3) + at async four (*fixtures*async-error.js:24:3) + at async main (*message*async_error_nexttick_main.js:7:5) diff --git a/test/message/async_error_sync_esm.mjs b/test/message/async_error_sync_esm.mjs new file mode 100644 index 00000000000000..86a901a1225572 --- /dev/null +++ b/test/message/async_error_sync_esm.mjs @@ -0,0 +1,14 @@ +// Flags: --experimental-modules +/* eslint-disable node-core/required-modules */ +import '../common/index.mjs'; +import four from '../fixtures/async-error.js'; + +async function main() { + try { + await four(); + } catch (e) { + console.error(e); + } +} + +main(); diff --git a/test/message/async_error_sync_esm.out b/test/message/async_error_sync_esm.out new file mode 100644 index 00000000000000..99ce9ed0b21c2f --- /dev/null +++ b/test/message/async_error_sync_esm.out @@ -0,0 +1,7 @@ +(node:*) ExperimentalWarning: The ESM module loader is experimental. +Error: test + at one (*fixtures*async-error.js:4:9) + at two (*fixtures*async-error.js:17:9) + at async three (*fixtures*async-error.js:20:3) + at async four (*fixtures*async-error.js:24:3) + at async main (*message*async_error_sync_esm.mjs:8:5) diff --git a/test/message/async_error_sync_main.js b/test/message/async_error_sync_main.js new file mode 100644 index 00000000000000..fa33c86d2cf911 --- /dev/null +++ b/test/message/async_error_sync_main.js @@ -0,0 +1,13 @@ +'use strict'; +require('../common'); +const four = require('../fixtures/async-error'); + +async function main() { + try { + await four(); + } catch (e) { + console.error(e); + } +} + +main(); diff --git a/test/message/async_error_sync_main.out b/test/message/async_error_sync_main.out new file mode 100644 index 00000000000000..565affbaf750d9 --- /dev/null +++ b/test/message/async_error_sync_main.out @@ -0,0 +1,6 @@ +Error: test + at one (*fixtures*async-error.js:4:9) + at two (*fixtures*async-error.js:17:9) + at async three (*fixtures*async-error.js:20:3) + at async four (*fixtures*async-error.js:24:3) + at async main (*message*async_error_sync_main.js:7:5) diff --git a/test/message/events_unhandled_error_nexttick.out b/test/message/events_unhandled_error_nexttick.out index ddb13807eea5b5..f592d91b52c099 100644 --- a/test/message/events_unhandled_error_nexttick.out +++ b/test/message/events_unhandled_error_nexttick.out @@ -13,6 +13,3 @@ Error Emitted 'error' event at: at *events_unhandled_error_nexttick.js:*:* at processTicksAndRejections (internal/process/task_queues.js:*:*) - at process.runNextTicks [as _tickCallback] (internal/process/task_queues.js:*:*) - at Function.Module.runMain (internal/modules/cjs/loader.js:*:*) - at internal/main/run_main_module.js:*:* diff --git a/test/message/nexttick_throw.out b/test/message/nexttick_throw.out index f180c4ab55a7c0..d0a45b3cec0f2b 100644 --- a/test/message/nexttick_throw.out +++ b/test/message/nexttick_throw.out @@ -5,6 +5,3 @@ ReferenceError: undefined_reference_error_maker is not defined at *test*message*nexttick_throw.js:*:* at processTicksAndRejections (internal/process/task_queues.js:*:*) - at process.runNextTicks [as _tickCallback] (internal/process/task_queues.js:*:*) - at Function.Module.runMain (internal/modules/cjs/loader.js:*:*) - at internal/main/run_main_module.js:*:* diff --git a/test/message/unhandled_promise_trace_warnings.out b/test/message/unhandled_promise_trace_warnings.out index 13778fb798aa6c..c9fff40efb72bd 100644 --- a/test/message/unhandled_promise_trace_warnings.out +++ b/test/message/unhandled_promise_trace_warnings.out @@ -9,9 +9,6 @@ at * at * at * - at * - at * - at * (node:*) Error: This was rejected at * (*test*message*unhandled_promise_trace_warnings.js:*) at * @@ -25,9 +22,6 @@ at * at * at * - at * - at * - at * (node:*) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1) at handledRejection (internal/process/promises.js:*) at promiseRejectHandler (internal/process/promises.js:*) From bf12414bbf4295cb47c4c8b69bc2aa6828778453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Sat, 27 Apr 2019 12:26:24 +0200 Subject: [PATCH 49/49] 2019-04-29, Version 12.1.0 (Current) Notable changes: * intl: * Update ICU to 64.2. This adds support for Japanese Era (Reiwa). https://github.com/nodejs/node/pull/27361 * Fixes a bug in ICU that affected Node.js 12.0.0 in the case where `new Date().toLocaleString()` was called with a non-default locale. https://github.com/nodejs/node/pull/27415 * C++ API: * Added an overload of `EmitAsyncDestroy` that can be used during garbage collection. https://github.com/nodejs/node/pull/27255 PR-URL: https://github.com/nodejs/node/pull/27440 --- CHANGELOG.md | 3 +- doc/changelogs/CHANGELOG_V12.md | 63 +++++++++++++++++++++++++++++++++ src/node_version.h | 6 ++-- 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77128cf9b4933b..7b9bcd9e768a73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,8 @@ release. -12.0.0
+12.1.0
+12.0.0
11.14.0
diff --git a/doc/changelogs/CHANGELOG_V12.md b/doc/changelogs/CHANGELOG_V12.md index ff00f3a41027e5..7ad04db5bcbafa 100644 --- a/doc/changelogs/CHANGELOG_V12.md +++ b/doc/changelogs/CHANGELOG_V12.md @@ -9,6 +9,7 @@ +12.1.0
12.0.0
@@ -28,6 +29,68 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + +## 2019-04-29, Version 12.1.0 (Current), @targos + +### Notable changes + +* **intl**: + * Update ICU to 64.2. This adds support for Japanese Era (Reiwa) (Ujjwal Sharma) [#27361](https://github.com/nodejs/node/pull/27361). + * Fixes a bug in ICU that affected Node.js 12.0.0 in the case where `new Date().toLocaleString()` was called with a non-default locale (Steven R. Loomis) [#27415](https://github.com/nodejs/node/pull/27415). +* **C++ API**: + * Added an overload `EmitAsyncDestroy` that can be used during garbage collection (Anna Henningsen) [#27255](https://github.com/nodejs/node/pull/27255). + +### Commits + +* [[`8ca110cc6f`](https://github.com/nodejs/node/commit/8ca110cc6f)] - **benchmark**: fix http bench-parser.js (Rich Trott) [#27359](https://github.com/nodejs/node/pull/27359) +* [[`2f9bafba08`](https://github.com/nodejs/node/commit/2f9bafba08)] - **bootstrap**: delay the instantiation of maps in per-context scripts (Joyee Cheung) [#27371](https://github.com/nodejs/node/pull/27371) +* [[`e7026f1428`](https://github.com/nodejs/node/commit/e7026f1428)] - **build**: allow icu download to use other hashes besides md5 (Steven R. Loomis) [#27370](https://github.com/nodejs/node/pull/27370) +* [[`50234460f9`](https://github.com/nodejs/node/commit/50234460f9)] - **build**: disable custom v8 snapshot by default (Joyee Cheung) [#27365](https://github.com/nodejs/node/pull/27365) +* [[`b21b28f653`](https://github.com/nodejs/node/commit/b21b28f653)] - **crypto**: update root certificates (Sam Roberts) [#27374](https://github.com/nodejs/node/pull/27374) +* [[`3282ccb845`](https://github.com/nodejs/node/commit/3282ccb845)] - **deps**: backport ICU-20575 to fix err/crasher (Steven R. Loomis) [#27435](https://github.com/nodejs/node/pull/27435) +* [[`e922a22725`](https://github.com/nodejs/node/commit/e922a22725)] - **deps**: backport ICU-20558 to fix Intl crasher (Steven R. Loomis) [#27415](https://github.com/nodejs/node/pull/27415) +* [[`d852d9e904`](https://github.com/nodejs/node/commit/d852d9e904)] - **deps**: update ICU to 64.2 (Ujjwal Sharma) [#27361](https://github.com/nodejs/node/pull/27361) +* [[`ee80a210ff`](https://github.com/nodejs/node/commit/ee80a210ff)] - **dgram**: change 'this' to 'self' for 'isConnected' (MaleDong) [#27338](https://github.com/nodejs/node/pull/27338) +* [[`8302148c83`](https://github.com/nodejs/node/commit/8302148c83)] - **doc**: add Node 12 to the first list of versions (Rivaldo Junior) [#27414](https://github.com/nodejs/node/pull/27414) +* [[`f6ceefa4bd`](https://github.com/nodejs/node/commit/f6ceefa4bd)] - **doc**: update comment in bootstrap for primordials (Myles Borins) [#27398](https://github.com/nodejs/node/pull/27398) +* [[`9c30806fb5`](https://github.com/nodejs/node/commit/9c30806fb5)] - **doc**: simplify GOVERNANCE.md text (Rich Trott) [#27354](https://github.com/nodejs/node/pull/27354) +* [[`453510c7ef`](https://github.com/nodejs/node/commit/453510c7ef)] - **doc**: fix pull request number (Ruben Bridgewater) [#27336](https://github.com/nodejs/node/pull/27336) +* [[`36762883a0`](https://github.com/nodejs/node/commit/36762883a0)] - **doc**: clarify behaviour of writeFile(fd) (Sam Roberts) [#27282](https://github.com/nodejs/node/pull/27282) +* [[`f70588fb67`](https://github.com/nodejs/node/commit/f70588fb67)] - **doc**: fix v12.0.0 changelog id (Beth Griggs) [#27368](https://github.com/nodejs/node/pull/27368) +* [[`a6d1fa5954`](https://github.com/nodejs/node/commit/a6d1fa5954)] - **doc**: simplify Collaborator pre-nomination text (Rich Trott) [#27348](https://github.com/nodejs/node/pull/27348) +* [[`dd709fc84a`](https://github.com/nodejs/node/commit/dd709fc84a)] - **lib**: throw a special error in internal/assert (Joyee Cheung) [#26635](https://github.com/nodejs/node/pull/26635) +* [[`4dfe54a89a`](https://github.com/nodejs/node/commit/4dfe54a89a)] - **module**: initialize module\_wrap.callbackMap during pre-execution (Joyee Cheung) [#27323](https://github.com/nodejs/node/pull/27323) +* [[`8b5d73867f`](https://github.com/nodejs/node/commit/8b5d73867f)] - **(SEMVER-MINOR)** **n-api**: do not require JS Context for `napi\_async\_destroy()` (Anna Henningsen) [#27255](https://github.com/nodejs/node/pull/27255) +* [[`d00014e599`](https://github.com/nodejs/node/commit/d00014e599)] - **process**: reduce the number of internal frames in async stack trace (Joyee Cheung) [#27392](https://github.com/nodejs/node/pull/27392) +* [[`dc510fb435`](https://github.com/nodejs/node/commit/dc510fb435)] - **report**: print common items first for readability (gengjiawen) [#27367](https://github.com/nodejs/node/pull/27367) +* [[`3614a00276`](https://github.com/nodejs/node/commit/3614a00276)] - **src**: refactor deprecated UVException in node\_file.cc (gengjiawen) [#27280](https://github.com/nodejs/node/pull/27280) +* [[`071300b39d`](https://github.com/nodejs/node/commit/071300b39d)] - **src**: move OnMessage to node\_errors.cc (Joyee Cheung) [#27304](https://github.com/nodejs/node/pull/27304) +* [[`81e7b49c8f`](https://github.com/nodejs/node/commit/81e7b49c8f)] - **src**: use predefined AliasedBuffer types in the code base (Joyee Cheung) [#27334](https://github.com/nodejs/node/pull/27334) +* [[`8089d29567`](https://github.com/nodejs/node/commit/8089d29567)] - **src**: apply clang-tidy modernize-deprecated-headers found by Jenkins CI (gengjiawen) [#27279](https://github.com/nodejs/node/pull/27279) +* [[`619c5b6eb3`](https://github.com/nodejs/node/commit/619c5b6eb3)] - **(SEMVER-MINOR)** **src**: do not require JS Context for `\~AsyncResoure()` (Anna Henningsen) [#27255](https://github.com/nodejs/node/pull/27255) +* [[`809cf595eb`](https://github.com/nodejs/node/commit/809cf595eb)] - **(SEMVER-MINOR)** **src**: add `Environment` overload of `EmitAsyncDestroy` (Anna Henningsen) [#27255](https://github.com/nodejs/node/pull/27255) +* [[`7bc47cba2e`](https://github.com/nodejs/node/commit/7bc47cba2e)] - **src**: apply clang-tidy rule modernize-use-equals-default (gengjiawen) [#27264](https://github.com/nodejs/node/pull/27264) +* [[`ad42cd69cf`](https://github.com/nodejs/node/commit/ad42cd69cf)] - **src**: use std::vector\ instead of IndexArray (Joyee Cheung) [#27321](https://github.com/nodejs/node/pull/27321) +* [[`228127fc67`](https://github.com/nodejs/node/commit/228127fc67)] - **src**: enable context snapshot after running per-context scripts (Joyee Cheung) [#27321](https://github.com/nodejs/node/pull/27321) +* [[`45d6106129`](https://github.com/nodejs/node/commit/45d6106129)] - **src**: enable snapshot with per-isolate data (Joyee Cheung) [#27321](https://github.com/nodejs/node/pull/27321) +* [[`631bea8fd2`](https://github.com/nodejs/node/commit/631bea8fd2)] - **src**: implement IsolateData serialization and deserialization (Joyee Cheung) [#27321](https://github.com/nodejs/node/pull/27321) +* [[`a636338945`](https://github.com/nodejs/node/commit/a636338945)] - **src**: allow creating NodeMainInstance that does not own the isolate (Joyee Cheung) [#27321](https://github.com/nodejs/node/pull/27321) +* [[`50732c1b3f`](https://github.com/nodejs/node/commit/50732c1b3f)] - **test**: refactor net-connect-handle-econnrefused (Luigi Pinca) [#27014](https://github.com/nodejs/node/pull/27014) +* [[`e9021cc498`](https://github.com/nodejs/node/commit/e9021cc498)] - **test**: move test-net-connect-handle-econnrefused (Luigi Pinca) [#27014](https://github.com/nodejs/node/pull/27014) +* [[`ebbed6047d`](https://github.com/nodejs/node/commit/ebbed6047d)] - **test**: rework to remove flakiness, and be parallel (Sam Roberts) [#27300](https://github.com/nodejs/node/pull/27300) +* [[`f0b2992f5c`](https://github.com/nodejs/node/commit/f0b2992f5c)] - **test**: fix ineffective error tests (Masashi Hirano) [#27333](https://github.com/nodejs/node/pull/27333) +* [[`d84a6d05a1`](https://github.com/nodejs/node/commit/d84a6d05a1)] - **test**: make test-worker-esm-missing-main more robust (Rich Trott) [#27340](https://github.com/nodejs/node/pull/27340) +* [[`8486917b9a`](https://github.com/nodejs/node/commit/8486917b9a)] - **test**: increase coverage in lib/internal/dns/promises.js (Rich Trott) [#27330](https://github.com/nodejs/node/pull/27330) +* [[`6ca0270320`](https://github.com/nodejs/node/commit/6ca0270320)] - **tls**: include invalid method name in thrown error (Sam Roberts) [#27390](https://github.com/nodejs/node/pull/27390) +* [[`dcbe5b9dff`](https://github.com/nodejs/node/commit/dcbe5b9dff)] - **tools**: update certdata.txt (Sam Roberts) [#27374](https://github.com/nodejs/node/pull/27374) +* [[`53f0ef36c0`](https://github.com/nodejs/node/commit/53f0ef36c0)] - **tools**: update LICENSE and tools/icu/current\_ver.dep (Ujjwal Sharma) [#27361](https://github.com/nodejs/node/pull/27361) +* [[`481789c235`](https://github.com/nodejs/node/commit/481789c235)] - **tools**: fix use-after-free mkcodecache warning (Ben Noordhuis) [#27332](https://github.com/nodejs/node/pull/27332) +* [[`d62a3243b1`](https://github.com/nodejs/node/commit/d62a3243b1)] - **tools**: update tools/license-builder.sh (Ujjwal Sharma) [#27362](https://github.com/nodejs/node/pull/27362) +* [[`b44323f3de`](https://github.com/nodejs/node/commit/b44323f3de)] - **tools**: implement node\_mksnapshot (Joyee Cheung) [#27321](https://github.com/nodejs/node/pull/27321) +* [[`ae2333db65`](https://github.com/nodejs/node/commit/ae2333db65)] - **util**: add prototype support for boxed primitives (Ruben Bridgewater) [#27351](https://github.com/nodejs/node/pull/27351) +* [[`8f3442809a`](https://github.com/nodejs/node/commit/8f3442809a)] - **util**: rename setIteratorBraces to getIteratorBraces (Ruben Bridgewater) [#27342](https://github.com/nodejs/node/pull/27342) +* [[`973d705aa9`](https://github.com/nodejs/node/commit/973d705aa9)] - **util**: improve `Symbol.toStringTag` handling (Ruben Bridgewater) [#27342](https://github.com/nodejs/node/pull/27342) + ## 2019-04-23, Version 12.0.0 (Current), @BethGriggs diff --git a/src/node_version.h b/src/node_version.h index b9fd77fc673831..c51249b2bb124b 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -23,13 +23,13 @@ #define SRC_NODE_VERSION_H_ #define NODE_MAJOR_VERSION 12 -#define NODE_MINOR_VERSION 0 -#define NODE_PATCH_VERSION 1 +#define NODE_MINOR_VERSION 1 +#define NODE_PATCH_VERSION 0 #define NODE_VERSION_IS_LTS 0 #define NODE_VERSION_LTS_CODENAME "" -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)