summaryrefslogtreecommitdiff
path: root/deps/v8/src/extensions/gc-extension.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/extensions/gc-extension.cc')
-rw-r--r--deps/v8/src/extensions/gc-extension.cc147
1 files changed, 142 insertions, 5 deletions
diff --git a/deps/v8/src/extensions/gc-extension.cc b/deps/v8/src/extensions/gc-extension.cc
index 4f446627fd..fddd40b352 100644
--- a/deps/v8/src/extensions/gc-extension.cc
+++ b/deps/v8/src/extensions/gc-extension.cc
@@ -4,23 +4,160 @@
#include "src/extensions/gc-extension.h"
+#include "include/v8.h"
#include "src/base/platform/platform.h"
+#include "src/execution/isolate.h"
+#include "src/heap/heap.h"
+#include "src/tasks/cancelable-task.h"
namespace v8 {
namespace internal {
+namespace {
+
+enum class ExecutionType { kAsync, kSync };
+
+struct GCOptions {
+ v8::Isolate::GarbageCollectionType type;
+ ExecutionType execution;
+};
+
+Maybe<bool> IsProperty(v8::Isolate* isolate, v8::Local<v8::Context> ctx,
+ v8::Local<v8::Object> object, const char* key,
+ const char* value) {
+ auto k = v8::String::NewFromUtf8(isolate, key).ToLocalChecked();
+ // Get will return undefined for non-existing keys which will make
+ // StrictEquals fail.
+ auto maybe_property = object->Get(ctx, k);
+ if (maybe_property.IsEmpty()) return Nothing<bool>();
+ return Just<bool>(maybe_property.ToLocalChecked()->StrictEquals(
+ v8::String::NewFromUtf8(isolate, value).ToLocalChecked()));
+}
+
+Maybe<GCOptions> Parse(v8::Isolate* isolate,
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // Default values.
+ auto options =
+ GCOptions{v8::Isolate::GarbageCollectionType::kFullGarbageCollection,
+ ExecutionType::kSync};
+ bool found_options_object = false;
+
+ if (args.Length() > 0 && args[0]->IsObject()) {
+ v8::HandleScope scope(isolate);
+ auto ctx = isolate->GetCurrentContext();
+ auto param = v8::Local<v8::Object>::Cast(args[0]);
+ auto maybe_type = IsProperty(isolate, ctx, param, "type", "minor");
+ if (maybe_type.IsNothing()) return Nothing<GCOptions>();
+ if (maybe_type.ToChecked()) {
+ found_options_object = true;
+ options.type =
+ v8::Isolate::GarbageCollectionType::kMinorGarbageCollection;
+ }
+ auto maybe_execution =
+ IsProperty(isolate, ctx, param, "execution", "async");
+ if (maybe_execution.IsNothing()) return Nothing<GCOptions>();
+ if (maybe_execution.ToChecked()) {
+ found_options_object = true;
+ options.execution = ExecutionType::kAsync;
+ }
+ }
+
+ // If no options object is present default to legacy behavior.
+ if (!found_options_object) {
+ options.type =
+ args[0]->BooleanValue(isolate)
+ ? v8::Isolate::GarbageCollectionType::kMinorGarbageCollection
+ : v8::Isolate::GarbageCollectionType::kFullGarbageCollection;
+ }
+
+ return Just<GCOptions>(options);
+}
+
+void InvokeGC(v8::Isolate* isolate, v8::Isolate::GarbageCollectionType type,
+ v8::EmbedderHeapTracer::EmbedderStackState embedder_stack_state) {
+ Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
+ switch (type) {
+ case v8::Isolate::GarbageCollectionType::kMinorGarbageCollection:
+ heap->CollectGarbage(i::NEW_SPACE, i::GarbageCollectionReason::kTesting,
+ kGCCallbackFlagForced);
+ break;
+ case v8::Isolate::GarbageCollectionType::kFullGarbageCollection:
+ heap->SetEmbedderStackStateForNextFinalizaton(embedder_stack_state);
+ heap->PreciseCollectAllGarbage(i::Heap::kNoGCFlags,
+ i::GarbageCollectionReason::kTesting,
+ kGCCallbackFlagForced);
+ break;
+ }
+}
+
+class AsyncGC final : public CancelableTask {
+ public:
+ ~AsyncGC() final = default;
+
+ AsyncGC(v8::Isolate* isolate, v8::Local<v8::Promise::Resolver> resolver,
+ v8::Isolate::GarbageCollectionType type)
+ : CancelableTask(reinterpret_cast<Isolate*>(isolate)),
+ isolate_(isolate),
+ ctx_(isolate, isolate->GetCurrentContext()),
+ resolver_(isolate, resolver),
+ type_(type) {}
+
+ void RunInternal() final {
+ v8::HandleScope scope(isolate_);
+ InvokeGC(isolate_, type_,
+ v8::EmbedderHeapTracer::EmbedderStackState::kEmpty);
+ auto resolver = v8::Local<v8::Promise::Resolver>::New(isolate_, resolver_);
+ auto ctx = Local<v8::Context>::New(isolate_, ctx_);
+ resolver->Resolve(ctx, v8::Undefined(isolate_)).ToChecked();
+ }
+
+ private:
+ v8::Isolate* isolate_;
+ v8::Persistent<v8::Context> ctx_;
+ v8::Persistent<v8::Promise::Resolver> resolver_;
+ v8::Isolate::GarbageCollectionType type_;
+
+ DISALLOW_COPY_AND_ASSIGN(AsyncGC);
+};
+
+} // namespace
v8::Local<v8::FunctionTemplate> GCExtension::GetNativeFunctionTemplate(
v8::Isolate* isolate, v8::Local<v8::String> str) {
return v8::FunctionTemplate::New(isolate, GCExtension::GC);
}
-
void GCExtension::GC(const v8::FunctionCallbackInfo<v8::Value>& args) {
- args.GetIsolate()->RequestGarbageCollectionForTesting(
- args[0]->BooleanValue(args.GetIsolate())
- ? v8::Isolate::kMinorGarbageCollection
- : v8::Isolate::kFullGarbageCollection);
+ v8::Isolate* isolate = args.GetIsolate();
+
+ // Immediate bailout if no arguments are provided.
+ if (args.Length() == 0) {
+ InvokeGC(isolate,
+ v8::Isolate::GarbageCollectionType::kFullGarbageCollection,
+ v8::EmbedderHeapTracer::EmbedderStackState::kUnknown);
+ return;
+ }
+
+ auto maybe_options = Parse(isolate, args);
+ if (maybe_options.IsNothing()) return;
+ GCOptions options = maybe_options.ToChecked();
+ switch (options.execution) {
+ case ExecutionType::kSync:
+ InvokeGC(isolate, options.type,
+ v8::EmbedderHeapTracer::EmbedderStackState::kUnknown);
+ break;
+ case ExecutionType::kAsync: {
+ v8::HandleScope scope(isolate);
+ auto resolver = v8::Promise::Resolver::New(isolate->GetCurrentContext())
+ .ToLocalChecked();
+ args.GetReturnValue().Set(resolver->GetPromise());
+ auto task_runner =
+ V8::GetCurrentPlatform()->GetForegroundTaskRunner(isolate);
+ CHECK(task_runner->NonNestableTasksEnabled());
+ task_runner->PostNonNestableTask(
+ std::make_unique<AsyncGC>(isolate, resolver, options.type));
+ } break;
+ }
}
} // namespace internal