diff --git a/common.gypi b/common.gypi index 758d6818a63..3368530c582 100644 --- a/common.gypi +++ b/common.gypi @@ -38,7 +38,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.9', + 'v8_embedder_string': '-node.10', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/include/v8-isolate.h b/deps/v8/include/v8-isolate.h index 49bb46aee3d..332688e77b4 100644 --- a/deps/v8/include/v8-isolate.h +++ b/deps/v8/include/v8-isolate.h @@ -872,11 +872,26 @@ class V8_EXPORT Isolate { void Exit(); /** - * Disposes the isolate. The isolate must not be entered by any + * Deinitializes and frees the isolate. The isolate must not be entered by any * thread to be disposable. */ void Dispose(); + /** + * Deinitializes the isolate, but does not free the address. The isolate must + * not be entered by any thread to be deinitializable. Embedders must call + * Isolate::Free() to free the isolate afterwards. + */ + void Deinitialize(); + + /** + * Frees the memory allocated for the isolate. Can only be called after the + * Isolate has already been deinitialized with Isolate::Deinitialize(). After + * the isolate is freed, the next call to Isolate::New() or + * Isolate::Allocate() might return the same address that just get freed. + */ + static void Free(Isolate* isolate); + /** * Dumps activated low-level V8 internal stats. This can be used instead * of performing a full isolate disposal. diff --git a/deps/v8/src/api/api.cc b/deps/v8/src/api/api.cc index 545c1093ee7..0f2a526b2b3 100644 --- a/deps/v8/src/api/api.cc +++ b/deps/v8/src/api/api.cc @@ -10132,6 +10132,21 @@ void Isolate::Dispose() { i::Isolate::Delete(i_isolate); } +void Isolate::Deinitialize() { + i::Isolate* i_isolate = reinterpret_cast(this); + if (!Utils::ApiCheck( + !i_isolate->IsInUse(), "v8::Isolate::Deinitialize()", + "Deinitializing the isolate that is entered by a thread")) { + return; + } + i::Isolate::Deinitialize(i_isolate); +} + +void Isolate::Free(Isolate* isolate) { + i::Isolate* i_isolate = reinterpret_cast(isolate); + i::Isolate::Free(i_isolate); +} + void Isolate::DumpAndResetStats() { i::Isolate* i_isolate = reinterpret_cast(this); i_isolate->DumpAndResetStats(); diff --git a/deps/v8/src/execution/isolate.cc b/deps/v8/src/execution/isolate.cc index ded444525ad..960132fe553 100644 --- a/deps/v8/src/execution/isolate.cc +++ b/deps/v8/src/execution/isolate.cc @@ -4133,6 +4133,12 @@ Isolate* Isolate::Allocate(IsolateGroup* group) { // static void Isolate::Delete(Isolate* isolate) { + Deinitialize(isolate); + Free(isolate); +} + +// static +void Isolate::Deinitialize(Isolate* isolate) { DCHECK_NOT_NULL(isolate); // v8::V8::Dispose() must only be called after deleting all isolates. DCHECK_NOT_NULL(V8::GetCurrentPlatform()); @@ -4156,13 +4162,18 @@ void Isolate::Delete(Isolate* isolate) { isolate->~Isolate(); // Only release the group once all other Isolate members have been destroyed. group->Release(); - // Free the isolate itself. - base::AlignedFree(isolate); // Restore the previous current isolate. SetIsolateThreadLocals(saved_isolate, saved_data); } +// static +void Isolate::Free(Isolate* isolate) { + DCHECK_NOT_NULL(isolate); + // Free the memory allocated for the Isolate. + base::AlignedFree(isolate); +} + void Isolate::SetUpFromReadOnlyArtifacts(ReadOnlyArtifacts* artifacts) { DCHECK_NOT_NULL(artifacts); InitializeNextUniqueSfiId(artifacts->initial_next_unique_sfi_id()); diff --git a/deps/v8/src/execution/isolate.h b/deps/v8/src/execution/isolate.h index afeab908ac1..a9128ece9b4 100644 --- a/deps/v8/src/execution/isolate.h +++ b/deps/v8/src/execution/isolate.h @@ -661,10 +661,16 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { static Isolate* New(); static Isolate* New(IsolateGroup* isolate_group); - // Deletes Isolate object. Must be used instead of delete operator. - // Destroys the non-default isolates. - // Sets default isolate into "has_been_disposed" state rather then destroying, - // for legacy API reasons. + // Deinitialize Isolate object. Must be used instead of delete operator. + // Destroys the non-default isolates. Sets default isolate into + // "has_been_disposed" state rather then destroying, for legacy API reasons. + // Another Free() call must be done after the isolate is deinitialized. + // This gives the embedder a chance to clean up before the address is released + // (which maybe reused in the next allocation). + static void Deinitialize(Isolate* isolate); + // Frees the address of the isolate. Must be called after Deinitialize(). + static void Free(Isolate* isolate); + // A convenience helper that deinitializes and frees the isolate. static void Delete(Isolate* isolate); void SetUpFromReadOnlyArtifacts(ReadOnlyArtifacts* artifacts); diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index b64756086c6..0f023d15cfc 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -31344,3 +31344,22 @@ TEST(LocalCasts) { v8::MaybeLocal::Cast(no_data); } } + +TEST(IsolateFree) { + v8::Isolate* isolate1 = v8::Isolate::Allocate(); + v8::Isolate::Initialize(isolate1, CreateTestParams()); + isolate1->Deinitialize(); + + // When a new isolate is allocated immediately after one is freed, there is + // a chance that the new isolate will be allocated at the same address as the + // old one. This tests that when Deinitialize() is called, allocating a new + // isolate does not reuse the old address. + v8::Isolate* isolate2 = v8::Isolate::Allocate(); + CHECK_NE(isolate1, isolate2); + + v8::Isolate::Initialize(isolate2, CreateTestParams()); + isolate2->Deinitialize(); + + v8::Isolate::Free(isolate1); + v8::Isolate::Free(isolate2); +}