From b80aedf4c19b6d74d6ad303f74d157dc9ddd17e0 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Fri, 14 Jul 2017 11:05:00 -0700 Subject: [PATCH 1/5] net: support passing undefined to listen() For consistency with 4.x and 8.x. This commit also contains a forward port of https://github.com/nodejs/node/pull/14232 to confirm that 4.x and 6.x behave identically with respect to the port argument. PR-URL: https://github.com/nodejs/node/pull/14234 Refs: https://github.com/nodejs/node/issues/14205 Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Sam Roberts --- lib/net.js | 2 +- test/parallel/test-net-listen-port-option.js | 35 ++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/lib/net.js b/lib/net.js index 5e653c61d2106c..83a93d6bd42e87 100644 --- a/lib/net.js +++ b/lib/net.js @@ -1338,7 +1338,7 @@ Server.prototype.listen = function() { self.once('listening', lastArg); } - var port = toNumber(arguments[0]); + var port = typeof arguments[0] === 'undefined' ? 0 : toNumber(arguments[0]); // The third optional argument is the backlog size. // When the ip is omitted it can be the second argument. diff --git a/test/parallel/test-net-listen-port-option.js b/test/parallel/test-net-listen-port-option.js index c4851bd533dfbe..3902a709bd9549 100644 --- a/test/parallel/test-net-listen-port-option.js +++ b/test/parallel/test-net-listen-port-option.js @@ -26,3 +26,38 @@ net.Server().listen({ port: '' + common.PORT }, close); net.Server().listen({ port: port }, common.fail); }, /invalid listen argument/i); }); + +// Repeat the tests, passing port as an argument, which validates somewhat +// differently. + +net.Server().listen(undefined, close); +net.Server().listen('0', close); + +// 'nan', skip, treated as a path, not a port +//'+Infinity', skip, treated as a path, not a port +//'-Infinity' skip, treated as a path, not a port + +// 4.x treats these as 0, but 6.x treats them as invalid numbers. +[ + -1, + 123.456, + 0x10000, + 1 / 0, + -1 / 0, +].forEach(function(port) { + assert.throws(function() { + net.Server().listen(port, common.fail); + }, /"port" argument must be >= 0 and < 65536/i); +}); + +// null is treated as 0 +net.Server().listen(null, close); + +// false/true are converted to 0/1, arguably a bug, but fixing would be +// semver-major. Note that true fails because ports that low can't be listened +// on by unprivileged processes. +net.Server().listen(false, close); + +net.Server().listen(true).on('error', common.mustCall(function(err) { + assert.strictEqual(err.code, 'EACCES'); +})); From 02e0b1ac3c88dd83adafaebee91d83ea2a83d283 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Tue, 2 May 2017 14:55:58 +0200 Subject: [PATCH 2/5] src: make root_cert_vector function scoped root_cert_vector currently has file scope and external linkage, but is only used in the NewRootCertsStore function. If this is not required to be externally linked perhaps it can be changed to be static and function scoped instead. PR-URL: https://github.com/nodejs/node/pull/12788 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Shigeki Ohtsu --- src/node_crypto.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index f654dcf60cb424..7eb18ad2020a32 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -127,7 +127,6 @@ const char* const root_certs[] = { std::string extra_root_certs_file; // NOLINT(runtime/string) X509_STORE* root_cert_store; -std::vector root_certs_vector; // Just to generate static methods template class SSLWrap; @@ -710,6 +709,7 @@ static int X509_up_ref(X509* cert) { static X509_STORE* NewRootCertStore() { + static std::vector root_certs_vector; if (root_certs_vector.empty()) { for (size_t i = 0; i < arraysize(root_certs); i++) { BIO* bp = NodeBIO::NewFixed(root_certs[i], strlen(root_certs[i])); From f4691bb2f37661cfd481b46461c1a483b99f63b6 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Wed, 24 May 2017 14:37:29 +0200 Subject: [PATCH 3/5] crypto: remove root_cert_store from node_crypto.h root_cert_store is defined as extern in node_crypto.h but only used in node_crypto.cc. It is then set using SSL_CTX_set_cert_store. The only usages of SSL_CTX_get_cert_store are in node_crypto.cc which would all be accessing the same X509_STORE through the root_cert_store pointer as far as I can tell. Am I missing something here? This commit suggests removing it from the header and making it static in node_crypto.cc. PR-URL: https://github.com/nodejs/node/pull/13194 Reviewed-By: Colin Ihrig Reviewed-By: Ben Noordhuis Reviewed-By: Michael Dawson Reviewed-By: James M Snell Reviewed-By: Sam Roberts --- src/node_crypto.cc | 6 +++--- src/node_crypto.h | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 7eb18ad2020a32..aa2dafebc5cf81 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -120,13 +120,13 @@ static X509_NAME *cnnic_ev_name = static Mutex* mutexes; -const char* const root_certs[] = { +static const char* const root_certs[] = { #include "node_root_certs.h" // NOLINT(build/include_order) }; -std::string extra_root_certs_file; // NOLINT(runtime/string) +static std::string extra_root_certs_file; // NOLINT(runtime/string) -X509_STORE* root_cert_store; +static X509_STORE* root_cert_store; // Just to generate static methods template class SSLWrap; diff --git a/src/node_crypto.h b/src/node_crypto.h index 38f49ba5a05063..746c954b26fb43 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -63,8 +63,6 @@ enum CheckResult { extern int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx); -extern X509_STORE* root_cert_store; - extern void UseExtraCaCerts(const std::string& file); // Forward declaration From 14e4254f68f71a6afaf3ebe16794172b08e68d7b Mon Sep 17 00:00:00 2001 From: Yang Guo Date: Thu, 20 Jul 2017 10:15:29 +0200 Subject: [PATCH 4/5] deps: backport rehash strings after deserialization Original commit messages: v8/v8@a2ab135 [snapshot] Rehash strings after deserialization. See https://goo.gl/6aN8xA Bug: v8:6593 Change-Id: Ic8b0b57195d01d41591397d5d45de3f0f3ebc3d9 Reviewed-on: https://chromium-review.googlesource.com/574527 Reviewed-by: Camillo Bruni Reviewed-by: Jakob Gruber Reviewed-by: Ulan Degenbaev Commit-Queue: Yang Guo Cr-Commit-Position: refs/heads/master@{#46732} v8/v8@182caaf Do not track transitions for built-in objects. Objects created during bootstrapping do not need a transition tree except for elements kind transitions. Bug: v8:6596 Change-Id: I237b8b2792f201336e1c9731c815095dd06bc182 Reviewed-on: https://chromium-review.googlesource.com/571750 Reviewed-by: Igor Sheludko Commit-Queue: Yang Guo Cr-Commit-Position: refs/heads/master@{#46693} Fixes: https://github.com/nodejs/node/issues/14171 PR-URL: https://github.com/nodejs/node/pull/14385 --- configure | 8 +-- deps/v8/include/v8-version.h | 2 +- deps/v8/src/api.cc | 6 ++ deps/v8/src/bootstrapper.cc | 4 ++ deps/v8/src/flag-definitions.h | 2 + deps/v8/src/heap/heap.cc | 17 ++--- deps/v8/src/heap/heap.h | 3 + deps/v8/src/js/array.js | 2 + deps/v8/src/objects.cc | 12 ++++ deps/v8/src/snapshot/deserializer.cc | 75 ++++++++++++++++++++++ deps/v8/src/snapshot/deserializer.h | 19 +++++- deps/v8/src/snapshot/partial-serializer.cc | 33 ++++++++-- deps/v8/src/snapshot/partial-serializer.h | 8 +++ deps/v8/src/snapshot/snapshot-common.cc | 2 + deps/v8/src/snapshot/snapshot.h | 6 ++ deps/v8/src/snapshot/startup-serializer.cc | 20 +++++- deps/v8/src/snapshot/startup-serializer.h | 9 +++ deps/v8/src/transitions-inl.h | 2 - deps/v8/src/transitions.cc | 42 ++++++++++++ deps/v8/src/transitions.h | 4 +- deps/v8/test/cctest/heap/test-heap.cc | 21 ++++++ deps/v8/test/cctest/test-serialize.cc | 30 +++++++++ 22 files changed, 302 insertions(+), 25 deletions(-) diff --git a/configure b/configure index 716964e6145d98..3948f18bcac7f4 100755 --- a/configure +++ b/configure @@ -419,12 +419,12 @@ parser.add_option('--without-perfctr', # Dummy option for backwards compatibility parser.add_option('--with-snapshot', action='store_true', - dest='with_snapshot', + dest='unused_with_snapshot', help=optparse.SUPPRESS_HELP) parser.add_option('--without-snapshot', action='store_true', - dest='unused_without_snapshot', + dest='without_snapshot', help=optparse.SUPPRESS_HELP) parser.add_option('--without-ssl', @@ -802,7 +802,7 @@ def configure_node(o): cross_compiling = (options.cross_compiling if options.cross_compiling is not None else target_arch != host_arch) - want_snapshots = 1 if options.with_snapshot else 0 + want_snapshots = not options.without_snapshot o['variables']['want_separate_host_toolset'] = int( cross_compiling and want_snapshots) @@ -946,7 +946,7 @@ def configure_v8(o): o['variables']['v8_no_strict_aliasing'] = 1 # Work around compiler bugs. o['variables']['v8_optimized_debug'] = 0 # Compile with -O0 in debug builds. o['variables']['v8_random_seed'] = 0 # Use a random seed for hash tables. - o['variables']['v8_use_snapshot'] = b(options.with_snapshot) + o['variables']['v8_use_snapshot'] = 'false' if options.without_snapshot else 'true' o['variables']['node_use_v8_platform'] = b(not options.without_v8_platform) o['variables']['node_use_bundled_v8'] = b(not options.without_bundled_v8) o['variables']['force_dynamic_crt'] = 1 if options.shared else 0 diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index 55d32cc89947ce..e2c861431c1091 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 5 #define V8_MINOR_VERSION 1 #define V8_BUILD_NUMBER 281 -#define V8_PATCH_LEVEL 103 +#define V8_PATCH_LEVEL 104 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index d0c8317d4bd957..50848c780dff0c 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -391,6 +391,9 @@ StartupData SerializeIsolateAndContext( i::Isolate* internal_isolate = reinterpret_cast(isolate); + // We might rehash strings and re-sort descriptors. Clear the lookup cache. + internal_isolate->descriptor_lookup_cache()->Clear(); + // If we don't do this then we end up with a stray root pointing at the // context even after we have disposed of the context. internal_isolate->heap()->CollectAllAvailableGarbage("mksnapshot"); @@ -428,6 +431,9 @@ StartupData SerializeIsolateAndContext( context_ser.Serialize(&raw_context); ser.SerializeWeakReferencesAndDeferred(); + metadata.set_can_rehash(ser.can_be_rehashed() && + context_ser.can_be_rehashed()); + return i::Snapshot::CreateSnapshotBlob(ser, context_ser, metadata); } diff --git a/deps/v8/src/bootstrapper.cc b/deps/v8/src/bootstrapper.cc index f67065dec4b12b..39e4b22e08b423 100644 --- a/deps/v8/src/bootstrapper.cc +++ b/deps/v8/src/bootstrapper.cc @@ -655,6 +655,8 @@ Handle Genesis::GetThrowTypeErrorIntrinsic( DCHECK(false); } + JSObject::MigrateSlowToFast(function, 0, "Bootstrapping"); + return function; } @@ -1133,6 +1135,8 @@ void Genesis::InitializeGlobal(Handle global_object, sloppy_function_map_writable_prototype_->SetConstructor(*function_fun); strict_function_map_writable_prototype_->SetConstructor(*function_fun); + + JSObject::MigrateSlowToFast(function_fun, 0, "Bootstrapping"); } { // --- A r r a y --- diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index eb3dbbb4cfaf00..c3420702dda6ca 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -830,6 +830,8 @@ DEFINE_BOOL(abort_on_uncaught_exception, false, DEFINE_BOOL(randomize_hashes, true, "randomize hashes to avoid predictable hash collisions " "(with snapshots this option cannot override the baked-in seed)") +DEFINE_BOOL(rehash_snapshot, true, + "rehash strings from the snapshot to override the baked-in seed") DEFINE_INT(hash_seed, 0, "Fixed seed to use to hash property keys (0 means random)" "(with snapshots this option cannot override the baked-in seed)") diff --git a/deps/v8/src/heap/heap.cc b/deps/v8/src/heap/heap.cc index eae9695caf09af..9eda2b3b8c3614 100644 --- a/deps/v8/src/heap/heap.cc +++ b/deps/v8/src/heap/heap.cc @@ -5348,14 +5348,7 @@ bool Heap::SetUp() { // Set up the seed that is used to randomize the string hash function. DCHECK(hash_seed() == 0); - if (FLAG_randomize_hashes) { - if (FLAG_hash_seed == 0) { - int rnd = isolate()->random_number_generator()->NextInt(); - set_hash_seed(Smi::FromInt(rnd & Name::kHashBitMask)); - } else { - set_hash_seed(Smi::FromInt(FLAG_hash_seed)); - } - } + if (FLAG_randomize_hashes) InitializeHashSeed(); for (int i = 0; i < static_cast(v8::Isolate::kUseCounterFeatureCount); i++) { @@ -5393,6 +5386,14 @@ bool Heap::SetUp() { return true; } +void Heap::InitializeHashSeed() { + if (FLAG_hash_seed == 0) { + int rnd = isolate()->random_number_generator()->NextInt(); + set_hash_seed(Smi::FromInt(rnd & Name::kHashBitMask)); + } else { + set_hash_seed(Smi::FromInt(FLAG_hash_seed)); + } +} bool Heap::CreateHeapObjects() { // Create initial maps. diff --git a/deps/v8/src/heap/heap.h b/deps/v8/src/heap/heap.h index 2d2029912cd717..778572d5bb010a 100644 --- a/deps/v8/src/heap/heap.h +++ b/deps/v8/src/heap/heap.h @@ -864,6 +864,9 @@ class Heap { // without actually creating any objects. bool SetUp(); + // (Re-)Initialize hash seed from flag or RNG. + void InitializeHashSeed(); + // Bootstraps the object heap with the core set of objects required to run. // Returns whether it succeeded. bool CreateHeapObjects(); diff --git a/deps/v8/src/js/array.js b/deps/v8/src/js/array.js index 1406df336d0382..e721dd94cfec21 100644 --- a/deps/v8/src/js/array.js +++ b/deps/v8/src/js/array.js @@ -1831,6 +1831,8 @@ var unscopables = { keys: true, }; +%ToFastProperties(unscopables); + %AddNamedProperty(GlobalArray.prototype, unscopablesSymbol, unscopables, DONT_ENUM | READ_ONLY); diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 7dbc2a377c9f60..c33b065ccde614 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -9469,6 +9469,12 @@ void Map::TraceAllTransitions(Map* map) { void Map::ConnectTransition(Handle parent, Handle child, Handle name, SimpleTransitionFlag flag) { + // Do not track transitions during bootstrap except for element transitions. + Isolate* isolate = parent->GetIsolate(); + if (isolate->bootstrapper()->IsActive() && + !name.is_identical_to(isolate->factory()->elements_transition_symbol())) { + return; + } if (!parent->GetBackPointer()->IsUndefined()) { parent->set_owns_descriptors(false); } else { @@ -17520,6 +17526,12 @@ template class Dictionary; +template void +HashTable >::Rehash(Handle key); + +template void +HashTable >::Rehash(Handle key); + template Handle Dictionary:: New(Isolate*, int at_least_space_for, PretenureFlag pretenure); diff --git a/deps/v8/src/snapshot/deserializer.cc b/deps/v8/src/snapshot/deserializer.cc index 0a21feffa1484d..0a478df8afc052 100644 --- a/deps/v8/src/snapshot/deserializer.cc +++ b/deps/v8/src/snapshot/deserializer.cc @@ -109,6 +109,8 @@ void Deserializer::Deserialize(Isolate* isolate) { LOG_CODE_EVENT(isolate_, LogCodeObjects()); LOG_CODE_EVENT(isolate_, LogBytecodeHandlers()); LOG_CODE_EVENT(isolate_, LogCompiledFunctions()); + + if (FLAG_rehash_snapshot && can_rehash_) Rehash(); } MaybeHandle Deserializer::DeserializePartial( @@ -138,6 +140,9 @@ MaybeHandle Deserializer::DeserializePartial( // changed and logging should be added to notify the profiler et al of the // new code, which also has to be flushed from instruction cache. CHECK_EQ(start_address, code_space->top()); + + if (FLAG_rehash_snapshot && can_rehash_) RehashContext(Context::cast(root)); + return Handle(root, isolate); } @@ -164,6 +169,64 @@ MaybeHandle Deserializer::DeserializeCode( } } +// We only really just need HashForObject here. +class StringRehashKey : public HashTableKey { + public: + uint32_t HashForObject(Object* other) override { + return String::cast(other)->Hash(); + } + + static uint32_t StringHash(Object* obj) { + UNREACHABLE(); + return String::cast(obj)->Hash(); + } + + bool IsMatch(Object* string) override { + UNREACHABLE(); + return false; + } + + uint32_t Hash() override { + UNREACHABLE(); + return 0; + } + + Handle AsHandle(Isolate* isolate) override { + UNREACHABLE(); + return isolate->factory()->empty_string(); + } +}; + +void Deserializer::Rehash() { + DCHECK(can_rehash_); + isolate_->heap()->InitializeHashSeed(); + if (FLAG_profile_deserialization) { + PrintF("Re-initializing hash seed to %x\n", + isolate_->heap()->hash_seed()->value()); + } + StringRehashKey string_rehash_key; + isolate_->heap()->string_table()->Rehash(&string_rehash_key); + isolate_->heap()->intrinsic_function_names()->Rehash( + isolate_->factory()->empty_string()); + SortMapDescriptors(); +} + +void Deserializer::RehashContext(Context* context) { + DCHECK(can_rehash_); + for (const auto& array : transition_arrays_) array->Sort(); + Handle dummy = isolate_->factory()->empty_string(); + context->global_object()->global_dictionary()->Rehash(dummy); + SortMapDescriptors(); +} + +void Deserializer::SortMapDescriptors() { + for (const auto& map : maps_) { + if (map->instance_descriptors()->number_of_descriptors() > 1) { + map->instance_descriptors()->Sort(); + } + } +} + Deserializer::~Deserializer() { // TODO(svenpanne) Re-enable this assertion when v8 initialization is fixed. // DCHECK(source_.AtEOF()); @@ -288,6 +351,18 @@ HeapObject* Deserializer::PostProcessNewObject(HeapObject* obj, int space) { new_code_objects_.Add(Code::cast(obj)); } } + if (FLAG_rehash_snapshot && can_rehash_ && !deserializing_user_code()) { + if (obj->IsString()) { + // Uninitialize hash field as we are going to reinitialize the hash seed. + String* string = String::cast(obj); + string->set_hash_field(String::kEmptyHashField); + } else if (obj->IsTransitionArray() && + TransitionArray::cast(obj)->number_of_entries() > 1) { + transition_arrays_.Add(TransitionArray::cast(obj)); + } else if (obj->IsMap()) { + maps_.Add(Map::cast(obj)); + } + } // Check alignment. DCHECK_EQ(0, Heap::GetFillToAlign(obj->address(), obj->RequiredAlignment())); return obj; diff --git a/deps/v8/src/snapshot/deserializer.h b/deps/v8/src/snapshot/deserializer.h index 58c481cc7983d4..ec08d4a6caeebf 100644 --- a/deps/v8/src/snapshot/deserializer.h +++ b/deps/v8/src/snapshot/deserializer.h @@ -37,7 +37,8 @@ class Deserializer : public SerializerDeserializer { external_reference_table_(NULL), deserialized_large_objects_(0), deserializing_user_code_(false), - next_alignment_(kWordAligned) { + next_alignment_(kWordAligned), + can_rehash_(false) { DecodeReservation(data->Reservations()); } @@ -59,6 +60,8 @@ class Deserializer : public SerializerDeserializer { attached_objects_ = attached_objects; } + void SetRehashability(bool v) { can_rehash_ = v; } + private: void VisitPointers(Object** start, Object** end) override; @@ -113,6 +116,15 @@ class Deserializer : public SerializerDeserializer { Object** CopyInNativesSource(Vector source_vector, Object** current); + // Rehash after deserializing an isolate. + void Rehash(); + + // Rehash after deserializing a context. + void RehashContext(Context* context); + + // Sort descriptors of deserialized maps using new string hashes. + void SortMapDescriptors(); + // Cached current isolate. Isolate* isolate_; @@ -136,11 +148,16 @@ class Deserializer : public SerializerDeserializer { List new_code_objects_; List > new_internalized_strings_; List > new_scripts_; + List maps_; + List transition_arrays_; bool deserializing_user_code_; AllocationAlignment next_alignment_; + // TODO(6593): generalize rehashing, and remove this flag. + bool can_rehash_; + DISALLOW_COPY_AND_ASSIGN(Deserializer); }; diff --git a/deps/v8/src/snapshot/partial-serializer.cc b/deps/v8/src/snapshot/partial-serializer.cc index 0f1f133edc0b17..e5a81e08714c3f 100644 --- a/deps/v8/src/snapshot/partial-serializer.cc +++ b/deps/v8/src/snapshot/partial-serializer.cc @@ -15,7 +15,9 @@ PartialSerializer::PartialSerializer(Isolate* isolate, : Serializer(isolate, sink), startup_serializer_(startup_snapshot_serializer), global_object_(NULL), - next_partial_cache_index_(0) { + next_partial_cache_index_(0), + rehashable_context_(nullptr), + can_be_rehashed_(true) { InitializeCodeAddressMap(); } @@ -24,7 +26,7 @@ PartialSerializer::~PartialSerializer() { } void PartialSerializer::Serialize(Object** o) { - if ((*o)->IsContext()) { + if ((*o)->IsNativeContext()) { Context* context = Context::cast(*o); global_object_ = context->global_object(); back_reference_map()->AddGlobalProxy(context->global_proxy()); @@ -33,11 +35,14 @@ void PartialSerializer::Serialize(Object** o) { // and it's next context pointer may point to the code-stub context. Clear // it before serializing, it will get re-added to the context list // explicitly when it's loaded. - if (context->IsNativeContext()) { - context->set(Context::NEXT_CONTEXT_LINK, - isolate_->heap()->undefined_value()); - DCHECK(!context->global_object()->IsUndefined()); - } + context->set(Context::NEXT_CONTEXT_LINK, + isolate_->heap()->undefined_value()); + DCHECK(!context->global_object()->IsUndefined()); + DCHECK_NULL(rehashable_context_); + rehashable_context_ = context; + } else { + // We only do rehashing for native contexts. + can_be_rehashed_ = false; } VisitPointer(o); SerializeDeferredObjects(); @@ -89,6 +94,8 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code, for (int i = 0; i < literals->length(); i++) literals->set_undefined(i); } + if (obj->IsHashTable()) CheckRehashability(obj); + // Object has not yet been serialized. Serialize it here. ObjectSerializer serializer(this, obj, sink_, how_to_code, where_to_point); serializer.Serialize(); @@ -119,5 +126,17 @@ bool PartialSerializer::ShouldBeInThePartialSnapshotCache(HeapObject* o) { startup_serializer_->isolate()->heap()->fixed_cow_array_map(); } +void PartialSerializer::CheckRehashability(HeapObject* table) { + DCHECK(table->IsHashTable()); + if (!can_be_rehashed_) return; + // We can only correctly rehash if the global dictionary is the only hash + // table that we deserialize. + if (table == rehashable_context_->global_object()->global_dictionary()) { + return; + } + if (table == rehashable_context_->template_instantiations_cache()) return; + can_be_rehashed_ = false; +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/snapshot/partial-serializer.h b/deps/v8/src/snapshot/partial-serializer.h index 0bf61dd05558b3..01cfbf2dac5c0e 100644 --- a/deps/v8/src/snapshot/partial-serializer.h +++ b/deps/v8/src/snapshot/partial-serializer.h @@ -21,6 +21,8 @@ class PartialSerializer : public Serializer { // Serialize the objects reachable from a single object pointer. void Serialize(Object** o); + bool can_be_rehashed() const { return can_be_rehashed_; } + private: class PartialCacheIndexMap : public AddressMapBase { public: @@ -49,10 +51,16 @@ class PartialSerializer : public Serializer { int PartialSnapshotCacheIndex(HeapObject* o); bool ShouldBeInThePartialSnapshotCache(HeapObject* o); + void CheckRehashability(HeapObject* table); + Serializer* startup_serializer_; Object* global_object_; PartialCacheIndexMap partial_cache_index_map_; int next_partial_cache_index_; + Context* rehashable_context_; + // Indicates whether we only serialized hash tables that we can rehash. + // TODO(yangguo): generalize rehashing, and remove this flag. + bool can_be_rehashed_; DISALLOW_COPY_AND_ASSIGN(PartialSerializer); }; diff --git a/deps/v8/src/snapshot/snapshot-common.cc b/deps/v8/src/snapshot/snapshot-common.cc index eb3bdb56045af0..b53c43995d2e4e 100644 --- a/deps/v8/src/snapshot/snapshot-common.cc +++ b/deps/v8/src/snapshot/snapshot-common.cc @@ -58,6 +58,7 @@ bool Snapshot::Initialize(Isolate* isolate) { Vector startup_data = ExtractStartupData(blob); SnapshotData snapshot_data(startup_data); Deserializer deserializer(&snapshot_data); + deserializer.SetRehashability(ExtractMetadata(blob).can_rehash()); bool success = isolate->Init(&deserializer); if (FLAG_profile_deserialization) { double ms = timer.Elapsed().InMillisecondsF(); @@ -78,6 +79,7 @@ MaybeHandle Snapshot::NewContextFromSnapshot( Vector context_data = ExtractContextData(blob); SnapshotData snapshot_data(context_data); Deserializer deserializer(&snapshot_data); + deserializer.SetRehashability(ExtractMetadata(blob).can_rehash()); MaybeHandle maybe_context = deserializer.DeserializePartial(isolate, global_proxy); diff --git a/deps/v8/src/snapshot/snapshot.h b/deps/v8/src/snapshot/snapshot.h index c648d7595e2e25..f076dd64880d72 100644 --- a/deps/v8/src/snapshot/snapshot.h +++ b/deps/v8/src/snapshot/snapshot.h @@ -26,10 +26,16 @@ class Snapshot : public AllStatic { data_ = EmbedsScriptBits::update(data_, v); } + bool can_rehash() { return RehashabilityBits::decode(data_); } + void set_can_rehash(bool v) { + data_ = RehashabilityBits::update(data_, v); + } + uint32_t& RawValue() { return data_; } private: class EmbedsScriptBits : public BitField {}; + class RehashabilityBits : public BitField {}; uint32_t data_; }; diff --git a/deps/v8/src/snapshot/startup-serializer.cc b/deps/v8/src/snapshot/startup-serializer.cc index fab01f51f801d4..f7016b0aae6dad 100644 --- a/deps/v8/src/snapshot/startup-serializer.cc +++ b/deps/v8/src/snapshot/startup-serializer.cc @@ -15,7 +15,8 @@ StartupSerializer::StartupSerializer( FunctionCodeHandling function_code_handling) : Serializer(isolate, sink), function_code_handling_(function_code_handling), - serializing_builtins_(false) { + serializing_builtins_(false), + can_be_rehashed_(true) { InitializeCodeAddressMap(); } @@ -63,6 +64,8 @@ void StartupSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code, FlushSkip(skip); + if (obj->IsHashTable()) CheckRehashability(obj); + // Object has not yet been serialized. Serialize it here. ObjectSerializer object_serializer(this, obj, sink_, how_to_code, where_to_point); @@ -163,5 +166,20 @@ bool StartupSerializer::RootShouldBeSkipped(int root_index) { serializing_immortal_immovables_roots_; } +void StartupSerializer::CheckRehashability(HeapObject* table) { + DCHECK(table->IsHashTable()); + if (!can_be_rehashed_) return; + // We can only correctly rehash if the four hash tables below are the only + // ones that we deserialize. + if (table == isolate_->heap()->code_stubs()) return; + if (table == isolate_->heap()->non_monomorphic_cache()) return; + if (table == isolate_->heap()->empty_slow_element_dictionary()) return; + if (table == isolate_->heap()->empty_properties_dictionary()) return; + if (table == isolate_->heap()->weak_object_to_code_table()) return; + if (table == isolate_->heap()->intrinsic_function_names()) return; + if (table == isolate_->heap()->string_table()) return; + can_be_rehashed_ = false; +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/snapshot/startup-serializer.h b/deps/v8/src/snapshot/startup-serializer.h index 71b8475469a7aa..d978a76479859a 100644 --- a/deps/v8/src/snapshot/startup-serializer.h +++ b/deps/v8/src/snapshot/startup-serializer.h @@ -28,6 +28,8 @@ class StartupSerializer : public Serializer { void SerializeStrongReferences(); void SerializeWeakReferencesAndDeferred(); + bool can_be_rehashed() const { return can_be_rehashed_; } + private: // The StartupSerializer has to serialize the root array, which is slightly // different. @@ -42,10 +44,17 @@ class StartupSerializer : public Serializer { // roots. In the second pass, we serialize the rest. bool RootShouldBeSkipped(int root_index); + void CheckRehashability(HeapObject* hashtable); + FunctionCodeHandling function_code_handling_; bool serializing_builtins_; bool serializing_immortal_immovables_roots_; std::bitset root_has_been_serialized_; + + // Indicates whether we only serialized hash tables that we can rehash. + // TODO(yangguo): generalize rehashing, and remove this flag. + bool can_be_rehashed_; + DISALLOW_COPY_AND_ASSIGN(StartupSerializer); }; diff --git a/deps/v8/src/transitions-inl.h b/deps/v8/src/transitions-inl.h index ea02d61031deb9..f7f55bbacfc151 100644 --- a/deps/v8/src/transitions-inl.h +++ b/deps/v8/src/transitions-inl.h @@ -106,7 +106,6 @@ int TransitionArray::SearchName(Name* name, int* out_insertion_index) { } -#ifdef DEBUG bool TransitionArray::IsSpecialTransition(Name* name) { if (!name->IsSymbol()) return false; Heap* heap = name->GetHeap(); @@ -116,7 +115,6 @@ bool TransitionArray::IsSpecialTransition(Name* name) { name == heap->strict_function_transition_symbol() || name == heap->observed_symbol(); } -#endif int TransitionArray::CompareKeys(Name* key1, uint32_t hash1, PropertyKind kind1, diff --git a/deps/v8/src/transitions.cc b/deps/v8/src/transitions.cc index 082ebc16b015df..e0281809c591f7 100644 --- a/deps/v8/src/transitions.cc +++ b/deps/v8/src/transitions.cc @@ -549,5 +549,47 @@ int TransitionArray::Search(PropertyKind kind, Name* name, if (transition == kNotFound) return kNotFound; return SearchDetails(transition, kind, attributes, out_insertion_index); } + +void TransitionArray::Sort() { + DisallowHeapAllocation no_gc; + // In-place insertion sort. + int length = number_of_transitions(); + for (int i = 1; i < length; i++) { + Name* key = GetKey(i); + Map* target = GetTarget(i); + PropertyKind kind = kData; + PropertyAttributes attributes = NONE; + if (!IsSpecialTransition(key)) { + PropertyDetails details = GetTargetDetails(key, target); + kind = details.kind(); + attributes = details.attributes(); + } + int j; + for (j = i - 1; j >= 0; j--) { + Name* temp_key = GetKey(j); + Map* temp_target = GetTarget(j); + PropertyKind temp_kind = kData; + PropertyAttributes temp_attributes = NONE; + if (!IsSpecialTransition(temp_key)) { + PropertyDetails details = GetTargetDetails(temp_key, temp_target); + temp_kind = details.kind(); + temp_attributes = details.attributes(); + } + int cmp = + CompareKeys(temp_key, temp_key->Hash(), temp_kind, temp_attributes, + key, key->Hash(), kind, attributes); + if (cmp > 0) { + SetKey(j + 1, temp_key); + SetTarget(j + 1, temp_target); + } else { + break; + } + } + SetKey(j + 1, key); + SetTarget(j + 1, target); + } + DCHECK(IsSortedNoDuplicates()); +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/transitions.h b/deps/v8/src/transitions.h index 73aca7864ea363..28df458fd6b610 100644 --- a/deps/v8/src/transitions.h +++ b/deps/v8/src/transitions.h @@ -189,15 +189,17 @@ class TransitionArray: public FixedArray { void TransitionArrayVerify(); #endif + void Sort(); + #ifdef DEBUG bool IsSortedNoDuplicates(int valid_entries = -1); static bool IsSortedNoDuplicates(Map* map); static bool IsConsistentWithBackPointers(Map* map); +#endif // Returns true for a non-property transitions like elements kind, observed // or frozen transitions. static inline bool IsSpecialTransition(Name* name); -#endif // Constant for denoting key was not found. static const int kNotFound = -1; diff --git a/deps/v8/test/cctest/heap/test-heap.cc b/deps/v8/test/cctest/heap/test-heap.cc index 424e9870d8a0dd..f552dcb2c28bcf 100644 --- a/deps/v8/test/cctest/heap/test-heap.cc +++ b/deps/v8/test/cctest/heap/test-heap.cc @@ -6617,5 +6617,26 @@ TEST(Regress609761) { CHECK_EQ(size_after, size_before + array->Size()); } +UNINITIALIZED_TEST(ReinitializeStringHashSeed) { + // Enable rehashing and create an isolate and context. + i::FLAG_rehash_snapshot = true; + for (int i = 1; i < 3; i++) { + i::FLAG_hash_seed = 1337 * i; + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); + v8::Isolate* isolate = v8::Isolate::New(create_params); + { + v8::Isolate::Scope isolate_scope(isolate); + CHECK_EQ(1337 * i, + reinterpret_cast(isolate)->heap()->HashSeed()); + v8::HandleScope handle_scope(isolate); + v8::Local context = v8::Context::New(isolate); + CHECK(!context.IsEmpty()); + v8::Context::Scope context_scope(context); + } + isolate->Dispose(); + } +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/test/cctest/test-serialize.cc b/deps/v8/test/cctest/test-serialize.cc index cd349f9d735160..d2a7c59791e868 100644 --- a/deps/v8/test/cctest/test-serialize.cc +++ b/deps/v8/test/cctest/test-serialize.cc @@ -1834,6 +1834,36 @@ TEST(Regress503552) { } +UNINITIALIZED_TEST(ReinitializeStringHashSeedNotRehashable) { + DisableTurbofan(); + i::FLAG_rehash_snapshot = true; + i::FLAG_hash_seed = 42; + i::FLAG_allow_natives_syntax = true; + + v8::StartupData blob = v8::V8::CreateSnapshotDataBlob("var a = {};" + "a.b = 1;" + "a.c = 2;" + "delete a.b;"); + + i::FLAG_hash_seed = 1337; + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); + create_params.snapshot_blob = &blob; + v8::Isolate* isolate = v8::Isolate::New(create_params); + { + // Check that no rehashing has been performed. + CHECK_EQ(42, reinterpret_cast(isolate)->heap()->HashSeed()); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + v8::Local context = v8::Context::New(isolate); + CHECK(!context.IsEmpty()); + v8::Context::Scope context_scope(context); + ExpectInt32("a.c", 2); + } + isolate->Dispose(); + delete[] blob.data; +} + TEST(SerializationMemoryStats) { FLAG_profile_deserialization = true; FLAG_always_opt = false; From b7065690c44d831bdd08e07149fc550a3d2ae969 Mon Sep 17 00:00:00 2001 From: XadillaX Date: Thu, 10 Aug 2017 14:47:14 +0800 Subject: [PATCH 5/5] dns: fix `resolve` failed starts without network Fix the bug that you start process without network at first, but it connected lately, `dns.resolve` will stay failed with ECONNREFUSED because c-ares servers fallback to 127.0.0.1 at the very beginning. If c-ares servers "127.0.0.1" is detected and its not set by user self, and last query is not OK, recreating `ares_channel` operation will be triggered to reload servers. Fixes: https://github.com/nodejs/node/issues/1644 --- src/cares_wrap.cc | 160 +++++++++++++++++++++++++--------------------- src/env-inl.h | 18 ++++++ src/env.h | 6 ++ 3 files changed, 110 insertions(+), 74 deletions(-) diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index 44d0926ba6b178..d40d4b3256f193 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -372,6 +372,69 @@ struct CaresAsyncData { uv_async_t async_handle; }; +void SetupCaresChannel(Environment* env) { + struct ares_options options; + memset(&options, 0, sizeof(options)); + options.flags = ARES_FLAG_NOCHECKRESP; + options.sock_state_cb = ares_sockstate_cb; + options.sock_state_cb_data = env; + + /* We do the call to ares_init_option for caller. */ + int r = ares_init_options(env->cares_channel_ptr(), + &options, + ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB); + + if (r != ARES_SUCCESS) { + ares_library_cleanup(); + return env->ThrowError(ToErrorCodeString(r)); + } +} + + +/** + * This function is to check whether current servers are fallback servers + * when cares initialized. + * + * The fallback servers of cares is [ "127.0.0.1" ] with no user additional + * setting. + */ +void AresEnsureServers(Environment* env) { + /* if last query is OK or servers are set by user self, do not check */ + if (env->cares_query_last_ok() || !env->cares_is_servers_default()) { + return; + } + + ares_channel channel = env->cares_channel(); + ares_addr_node* servers = nullptr; + + ares_get_servers(channel, &servers); + + /* if no server or multi-servers, ignore */ + if (servers == nullptr) return; + if (servers->next != nullptr) { + ares_free_data(servers); + env->set_cares_is_servers_default(false); + return; + } + + /* if the only server is not 127.0.0.1, ignore */ + if (servers[0].family != AF_INET || + servers[0].addr.addr4.s_addr != htonl(INADDR_LOOPBACK)) { + ares_free_data(servers); + env->set_cares_is_servers_default(false); + return; + } + + ares_free_data(servers); + servers = nullptr; + + /* destroy channel and reset channel */ + ares_destroy(channel); + + SetupCaresChannel(env); +} + + class QueryWrap : public AsyncWrap { public: QueryWrap(Environment* env, Local req_wrap_obj) @@ -402,6 +465,13 @@ class QueryWrap : public AsyncWrap { return static_cast(this); } + static void AresQuery(Environment* env, const char* name, + int dnsclass, int type, ares_callback callback, + void* arg) { + AresEnsureServers(env); + ares_query(env->cares_channel(), name, dnsclass, type, callback, arg); + } + static void CaresAsyncClose(uv_handle_t* handle) { uv_async_t* async = reinterpret_cast(handle); auto data = static_cast(async->data); @@ -453,6 +523,7 @@ class QueryWrap : public AsyncWrap { async_handle, CaresAsyncCb)); + wrap->env()->set_cares_query_last_ok(status != ARES_ECONNREFUSED); async_handle->data = data; uv_async_send(async_handle); } @@ -478,6 +549,7 @@ class QueryWrap : public AsyncWrap { async_handle, CaresAsyncCb)); + wrap->env()->set_cares_query_last_ok(status != ARES_ECONNREFUSED); async_handle->data = data; uv_async_send(async_handle); } @@ -522,12 +594,7 @@ class QueryAWrap: public QueryWrap { } int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_a, - Callback, - GetQueryArg()); + AresQuery(env(), name, ns_c_in, ns_t_a, Callback, GetQueryArg()); return 0; } @@ -570,12 +637,7 @@ class QueryAaaaWrap: public QueryWrap { } int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_aaaa, - Callback, - GetQueryArg()); + AresQuery(env(), name, ns_c_in, ns_t_aaaa, Callback, GetQueryArg()); return 0; } @@ -618,12 +680,7 @@ class QueryCnameWrap: public QueryWrap { } int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_cname, - Callback, - GetQueryArg()); + AresQuery(env(), name, ns_c_in, ns_t_cname, Callback, GetQueryArg()); return 0; } @@ -659,12 +716,7 @@ class QueryMxWrap: public QueryWrap { } int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_mx, - Callback, - GetQueryArg()); + AresQuery(env(), name, ns_c_in, ns_t_mx, Callback, GetQueryArg()); return 0; } @@ -710,12 +762,7 @@ class QueryNsWrap: public QueryWrap { } int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_ns, - Callback, - GetQueryArg()); + AresQuery(env(), name, ns_c_in, ns_t_ns, Callback, GetQueryArg()); return 0; } @@ -748,12 +795,7 @@ class QueryTxtWrap: public QueryWrap { } int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_txt, - Callback, - GetQueryArg()); + AresQuery(env(), name, ns_c_in, ns_t_txt, Callback, GetQueryArg()); return 0; } @@ -805,12 +847,7 @@ class QuerySrvWrap: public QueryWrap { } int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_srv, - Callback, - GetQueryArg()); + AresQuery(env(), name, ns_c_in, ns_t_srv, Callback, GetQueryArg()); return 0; } @@ -861,12 +898,7 @@ class QueryPtrWrap: public QueryWrap { } int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_ptr, - Callback, - GetQueryArg()); + AresQuery(env(), name, ns_c_in, ns_t_ptr, Callback, GetQueryArg()); return 0; } @@ -904,12 +936,7 @@ class QueryNaptrWrap: public QueryWrap { } int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_naptr, - Callback, - GetQueryArg()); + AresQuery(env(), name, ns_c_in, ns_t_naptr, Callback, GetQueryArg()); return 0; } @@ -968,12 +995,7 @@ class QuerySoaWrap: public QueryWrap { } int Send(const char* name) override { - ares_query(env()->cares_channel(), - name, - ns_c_in, - ns_t_soa, - Callback, - GetQueryArg()); + AresQuery(env(), name, ns_c_in, ns_t_soa, Callback, GetQueryArg()); return 0; } @@ -1434,6 +1456,9 @@ static void SetServers(const FunctionCallbackInfo& args) { delete[] servers; + if (err == ARES_SUCCESS) + env->set_cares_is_servers_default(false); + args.GetReturnValue().Set(err); } @@ -1468,20 +1493,7 @@ static void Initialize(Local target, if (r != ARES_SUCCESS) return env->ThrowError(ToErrorCodeString(r)); - struct ares_options options; - memset(&options, 0, sizeof(options)); - options.flags = ARES_FLAG_NOCHECKRESP; - options.sock_state_cb = ares_sockstate_cb; - options.sock_state_cb_data = env; - - /* We do the call to ares_init_option for caller. */ - r = ares_init_options(env->cares_channel_ptr(), - &options, - ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB); - if (r != ARES_SUCCESS) { - ares_library_cleanup(); - return env->ThrowError(ToErrorCodeString(r)); - } + SetupCaresChannel(env); /* Initialize the timeout timer. The timer won't be started until the */ /* first socket is opened. */ diff --git a/src/env-inl.h b/src/env-inl.h index 735dbca6857fa1..e2876de44a9390 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -219,6 +219,8 @@ inline Environment::Environment(v8::Local context, : isolate_(context->GetIsolate()), isolate_data_(IsolateData::GetOrCreate(context->GetIsolate(), loop)), timer_base_(uv_now(loop)), + cares_query_last_ok_(true), + cares_is_servers_default_(true), using_domains_(false), printed_error_(false), trace_sync_io_(false), @@ -453,6 +455,22 @@ inline ares_channel* Environment::cares_channel_ptr() { return &cares_channel_; } +inline bool Environment::cares_query_last_ok() { + return cares_query_last_ok_; +} + +inline void Environment::set_cares_query_last_ok(bool ok) { + cares_query_last_ok_ = ok; +} + +inline bool Environment::cares_is_servers_default() { + return cares_is_servers_default_; +} + +inline void Environment::set_cares_is_servers_default(bool is_default) { + cares_is_servers_default_ = is_default; +} + inline node_ares_task_list* Environment::cares_task_list() { return &cares_task_list_; } diff --git a/src/env.h b/src/env.h index bb4dab070606a8..940c97fe4ddbf1 100644 --- a/src/env.h +++ b/src/env.h @@ -437,6 +437,10 @@ class Environment { inline uv_timer_t* cares_timer_handle(); inline ares_channel cares_channel(); inline ares_channel* cares_channel_ptr(); + inline bool cares_query_last_ok(); + inline void set_cares_query_last_ok(bool ok); + inline bool cares_is_servers_default(); + inline void set_cares_is_servers_default(bool is_default); inline node_ares_task_list* cares_task_list(); inline bool using_domains() const; @@ -555,6 +559,8 @@ class Environment { const uint64_t timer_base_; uv_timer_t cares_timer_handle_; ares_channel cares_channel_; + bool cares_query_last_ok_; + bool cares_is_servers_default_; node_ares_task_list cares_task_list_; bool using_domains_; bool printed_error_;