From 2fb138167691fcad9225e4c77152000cf485ff60 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Wed, 7 Jun 2017 12:49:00 -0700 Subject: [PATCH 1/6] doc: document and test that methods return this Also, add tests to ensure they will always return this, and to confirm they return this when these doc changes are back-ported to earlier release lines. PR-URL: https://github.com/nodejs/node/pull/13553 Reviewed-By: Colin Ihrig Reviewed-By: Roman Reiss Reviewed-By: Luigi Pinca Reviewed-By: Gibson Fahnestock Reviewed-By: James M Snell --- test/parallel/test-net-end-close.js | 2 +- test/parallel/test-net-server-close.js | 6 +++--- test/parallel/test-net-socket-local-address.js | 9 ++++++--- test/parallel/test-net-stream.js | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/test/parallel/test-net-end-close.js b/test/parallel/test-net-end-close.js index d9f33fd7d8d1cf..44a539a3e800a6 100644 --- a/test/parallel/test-net-end-close.js +++ b/test/parallel/test-net-end-close.js @@ -14,7 +14,7 @@ const s = new net.Socket({ }, writable: false }); -s.resume(); +assert.strictEqual(s, s.resume()); const events = []; diff --git a/test/parallel/test-net-server-close.js b/test/parallel/test-net-server-close.js index 28519fdafdbc9c..5affadfac42930 100644 --- a/test/parallel/test-net-server-close.js +++ b/test/parallel/test-net-server-close.js @@ -23,7 +23,7 @@ let server = net.createServer(function(c) { sockets.push(c); if (sockets.length === 2) { - server.close(); + assert.strictEqual(server.close(), server); sockets.forEach(function(c) { c.destroy(); }); } }); @@ -32,7 +32,7 @@ server.on('close', function() { events.push('server'); }); -server.listen(0, function() { +assert.strictEqual(server, server.listen(0, function() { net.createConnection(this.address().port); net.createConnection(this.address().port); -}); +})); diff --git a/test/parallel/test-net-socket-local-address.js b/test/parallel/test-net-socket-local-address.js index e888667eab4c4f..19749bde9552b6 100644 --- a/test/parallel/test-net-socket-local-address.js +++ b/test/parallel/test-net-socket-local-address.js @@ -34,7 +34,10 @@ function connect() { conns++; client.once('close', connect); - client.connect(server.address().port, common.localhostIPv4, () => { - clientLocalPorts.push(client.localPort); - }); + assert.strictEqual( + client, + client.connect(server.address().port, common.localhostIPv4, () => { + clientLocalPorts.push(client.localPort); + }) + ); } diff --git a/test/parallel/test-net-stream.js b/test/parallel/test-net-stream.js index 9e1ca670e1e145..2d3cdd76be788f 100644 --- a/test/parallel/test-net-stream.js +++ b/test/parallel/test-net-stream.js @@ -39,7 +39,7 @@ const server = net.createServer(function(socket) { }).listen(0, function() { const conn = net.connect(this.address().port); conn.on('data', function(buf) { - conn.pause(); + assert.strictEqual(conn, conn.pause()); setTimeout(function() { conn.destroy(); }, 20); From dce4a7507094a9b3ad27275ca4fe8d567074737f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Reis?= Date: Thu, 29 Jun 2017 16:40:27 +0100 Subject: [PATCH 2/6] test: mark test-fs-readdir-ucs2 flaky PR-URL: https://github.com/nodejs/node/pull/13989 Reviewed-By: Refael Ackermann --- test/parallel/parallel.status | 1 + 1 file changed, 1 insertion(+) diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status index 5b84bb1a250ee2..82c8c90a90824f 100644 --- a/test/parallel/parallel.status +++ b/test/parallel/parallel.status @@ -13,6 +13,7 @@ prefix parallel [$system==macos] [$arch==arm || $arch==arm64] +test-fs-readdir-ucs2 : PASS,FLAKY [$system==solaris] # Also applies to SmartOS test-debug-signal-cluster : PASS,FLAKY From 448c8d4d6495258f8416c2d2ea3a5086525b3064 Mon Sep 17 00:00:00 2001 From: Refael Ackermann Date: Sat, 1 Jul 2017 23:40:24 -0400 Subject: [PATCH 3/6] test: mark test-npm-install flaky on arm PR-URL: https://github.com/nodejs/node/pull/14035 Refs: https://github.com/nodejs/node/issues/14015 Reviewed-By: Gibson Fahnestock Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca --- test/parallel/parallel.status | 1 + 1 file changed, 1 insertion(+) diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status index 82c8c90a90824f..97e7803f4d40a5 100644 --- a/test/parallel/parallel.status +++ b/test/parallel/parallel.status @@ -14,6 +14,7 @@ prefix parallel [$arch==arm || $arch==arm64] test-fs-readdir-ucs2 : PASS,FLAKY +test-npm-install: PASS,FLAKY [$system==solaris] # Also applies to SmartOS test-debug-signal-cluster : PASS,FLAKY From 3b2c79190ff00d6935838472de1d191c65a51d1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Thu, 13 Jul 2017 16:02:37 +0200 Subject: [PATCH 4/6] build: run test-hash-seed at the end of test-v8 The v8 and test-hash-seed targets cannot be run in parallel because they need different copies of the deps/v8 directory. Ref: https://github.com/nodejs/node/pull/14004#issuecomment-314774773 PR-URL: https://github.com/nodejs/node/pull/14219 Reviewed-By: Michael Dawson Reviewed-By: Refael Ackermann Reviewed-By: Anna Henningsen --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6872ce2c7b8ba0..52bd44fa2e9227 100644 --- a/Makefile +++ b/Makefile @@ -296,13 +296,15 @@ test-timers-clean: ifneq ("","$(wildcard deps/v8/tools/run-tests.py)") -test-v8: v8 test-hash-seed +test-v8: v8 # note: performs full test unless QUICKCHECK is specified deps/v8/tools/run-tests.py --arch=$(V8_ARCH) \ --mode=$(BUILDTYPE_LOWER) $(V8_TEST_OPTIONS) $(QUICKCHECK_ARG) \ --no-presubmit \ --shell-dir=$(PWD)/deps/v8/out/$(V8_ARCH).$(BUILDTYPE_LOWER) \ $(TAP_V8) + @echo Testing hash seed + $(MAKE) test-hash-seed test-v8-intl: v8 # note: performs full test unless QUICKCHECK is specified From ddc8050c905197a04c1bfe667e040a62253dab0d Mon Sep 17 00:00:00 2001 From: Vse Mozhet Byt Date: Thu, 8 Jun 2017 02:00:33 +0300 Subject: [PATCH 5/6] cluster, dns, repl, tls, util: fix RegExp nits * Take RegExp creation out of cycles. * Use test(), not match() in boolean context. * Remove redundant RegExp parts. Backport-PR-URL: https://github.com/nodejs/node/pull/14348 PR-URL: https://github.com/nodejs/node/pull/13536 Reviewed-By: Anna Henningsen --- lib/_tls_wrap.js | 2 +- lib/cluster.js | 5 ++--- lib/dns.js | 6 ++++-- lib/repl.js | 7 ++++--- lib/util.js | 10 ++++++---- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index a4bda406f817cc..1960910258bd7d 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -954,7 +954,7 @@ function SNICallback(servername, callback) { var ctx; this.server._contexts.some(function(elem) { - if (servername.match(elem[0]) !== null) { + if (elem[0].test(servername)) { ctx = elem[1]; return true; } diff --git a/lib/cluster.js b/lib/cluster.js index 5307e09d6b5566..4bfa624a57e8c2 100644 --- a/lib/cluster.js +++ b/lib/cluster.js @@ -298,14 +298,13 @@ function masterInit() { var workerEnv = util._extend({}, process.env); var execArgv = cluster.settings.execArgv.slice(); var debugPort = 0; + var debugArgvRE = /^(--inspect|--debug|--debug-(brk|port))(=\d+)?$/; workerEnv = util._extend(workerEnv, env); workerEnv.NODE_UNIQUE_ID = '' + id; for (var i = 0; i < execArgv.length; i++) { - var match = execArgv[i].match( - /^(--inspect|--debug|--debug-(brk|port))(=\d+)?$/ - ); + var match = execArgv[i].match(debugArgvRE); if (match) { if (debugPort === 0) { diff --git a/lib/dns.js b/lib/dns.js index a86f498960b254..5ff28e4baceb5b 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -297,13 +297,15 @@ exports.setServers = function(servers) { // servers cares won't have any servers available for resolution const orig = cares.getServers(); const newSet = []; + const IPv6RE = /\[(.*)\]/; + const addrSplitRE = /:\d+$/; servers.forEach((serv) => { var ipVersion = isIP(serv); if (ipVersion !== 0) return newSet.push([ipVersion, serv]); - const match = serv.match(/\[(.*)\](?::\d+)?/); + const match = serv.match(IPv6RE); // we have an IPv6 in brackets if (match) { ipVersion = isIP(match[1]); @@ -311,7 +313,7 @@ exports.setServers = function(servers) { return newSet.push([ipVersion, match[1]]); } - const s = serv.split(/:\d+$/)[0]; + const s = serv.split(addrSplitRE)[0]; ipVersion = isIP(s); if (ipVersion !== 0) diff --git a/lib/repl.js b/lib/repl.js index 52bb1b6b53986c..2dfcc2899344a6 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -861,6 +861,7 @@ function complete(line, callback) { const exts = Object.keys(this.context.require.extensions); var indexRe = new RegExp('^index(' + exts.map(regexpEscape).join('|') + ')$'); + var versionedFileNamesRe = /-\d+\.\d+/; completeOn = match[1]; var subdir = match[2] || ''; @@ -879,7 +880,7 @@ function complete(line, callback) { name = files[f]; ext = path.extname(name); base = name.slice(0, -ext.length); - if (base.match(/-\d+\.\d+(\.\d+)?/) || name === '.npm') { + if (versionedFileNamesRe.test(base) || name === '.npm') { // Exclude versioned names that 'npm' installs. continue; } @@ -923,7 +924,7 @@ function complete(line, callback) { // spam.eggs.<|> # completions for 'spam.eggs' with filter '' // foo<|> # all scope vars with filter 'foo' // foo.<|> # completions for 'foo' with filter '' - } else if (line.length === 0 || line[line.length - 1].match(/\w|\.|\$/)) { + } else if (line.length === 0 || /\w|\.|\$/.test(line[line.length - 1])) { match = simpleExpressionRE.exec(line); if (line.length === 0 || match) { var expr; @@ -1175,7 +1176,7 @@ REPLServer.prototype.memory = function memory(cmd) { self.lines.level.push({ line: self.lines.length - 1, depth: depth, - isFunction: /\s*function\s*/.test(cmd) + isFunction: /\bfunction\b/.test(cmd) }); } else if (depth < 0) { // going... up. diff --git a/lib/util.js b/lib/util.js index b96c5121ed9610..635144818d8f63 100644 --- a/lib/util.js +++ b/lib/util.js @@ -17,6 +17,8 @@ const inspectDefaultOptions = Object.seal({ breakLength: 60 }); +const numbersOnlyRE = /^\d+$/; + var Debug; var simdFormatters; @@ -668,7 +670,7 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`); } keys.forEach(function(key) { - if (typeof key === 'symbol' || !key.match(/^\d+$/)) { + if (typeof key === 'symbol' || !numbersOnlyRE.test(key)) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, key, true)); } @@ -687,7 +689,7 @@ function formatTypedArray(ctx, value, recurseTimes, visibleKeys, keys) { output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`); } for (const key of keys) { - if (typeof key === 'symbol' || !key.match(/^\d+$/)) { + if (typeof key === 'symbol' || !numbersOnlyRE.test(key)) { output.push( formatProperty(ctx, value, recurseTimes, visibleKeys, key, true)); } @@ -801,11 +803,11 @@ function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { } } if (name === undefined) { - if (array && key.match(/^\d+$/)) { + if (array && numbersOnlyRE.test(key)) { return str; } name = JSON.stringify('' + key); - if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + if (/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/.test(name)) { name = name.substr(1, name.length - 2); name = ctx.stylize(name, 'name'); } else { From adcab4de0ab09017bf850cae10218d4739f5c57f Mon Sep 17 00:00:00 2001 From: Yang Guo Date: Thu, 20 Jul 2017 10:15:29 +0200 Subject: [PATCH 6/6] 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;