1 // Copyright 2010 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/extensions/gc-extension.h"
6
7 #include "include/v8-isolate.h"
8 #include "include/v8-object.h"
9 #include "include/v8-persistent-handle.h"
10 #include "include/v8-primitive.h"
11 #include "include/v8-template.h"
12 #include "src/base/optional.h"
13 #include "src/base/platform/platform.h"
14 #include "src/execution/isolate.h"
15 #include "src/heap/heap.h"
16 #include "src/tasks/cancelable-task.h"
17
18 namespace v8 {
19 namespace internal {
20
21 namespace {
22
23 enum class ExecutionType { kAsync, kSync };
24
25 struct GCOptions {
26 v8::Isolate::GarbageCollectionType type;
27 ExecutionType execution;
28 };
29
IsProperty(v8::Isolate * isolate,v8::Local<v8::Context> ctx,v8::Local<v8::Object> object,const char * key,const char * value)30 Maybe<bool> IsProperty(v8::Isolate* isolate, v8::Local<v8::Context> ctx,
31 v8::Local<v8::Object> object, const char* key,
32 const char* value) {
33 auto k = v8::String::NewFromUtf8(isolate, key).ToLocalChecked();
34 // Get will return undefined for non-existing keys which will make
35 // StrictEquals fail.
36 auto maybe_property = object->Get(ctx, k);
37 if (maybe_property.IsEmpty()) return Nothing<bool>();
38 return Just<bool>(maybe_property.ToLocalChecked()->StrictEquals(
39 v8::String::NewFromUtf8(isolate, value).ToLocalChecked()));
40 }
41
Parse(v8::Isolate * isolate,const v8::FunctionCallbackInfo<v8::Value> & args)42 Maybe<GCOptions> Parse(v8::Isolate* isolate,
43 const v8::FunctionCallbackInfo<v8::Value>& args) {
44 // Default values.
45 auto options =
46 GCOptions{v8::Isolate::GarbageCollectionType::kFullGarbageCollection,
47 ExecutionType::kSync};
48 bool found_options_object = false;
49
50 if (args.Length() > 0 && args[0]->IsObject()) {
51 v8::HandleScope scope(isolate);
52 auto ctx = isolate->GetCurrentContext();
53 auto param = v8::Local<v8::Object>::Cast(args[0]);
54 auto maybe_type = IsProperty(isolate, ctx, param, "type", "minor");
55 if (maybe_type.IsNothing()) return Nothing<GCOptions>();
56 if (maybe_type.ToChecked()) {
57 found_options_object = true;
58 options.type =
59 v8::Isolate::GarbageCollectionType::kMinorGarbageCollection;
60 }
61 auto maybe_execution =
62 IsProperty(isolate, ctx, param, "execution", "async");
63 if (maybe_execution.IsNothing()) return Nothing<GCOptions>();
64 if (maybe_execution.ToChecked()) {
65 found_options_object = true;
66 options.execution = ExecutionType::kAsync;
67 }
68 }
69
70 // If no options object is present default to legacy behavior.
71 if (!found_options_object) {
72 options.type =
73 args[0]->BooleanValue(isolate)
74 ? v8::Isolate::GarbageCollectionType::kMinorGarbageCollection
75 : v8::Isolate::GarbageCollectionType::kFullGarbageCollection;
76 }
77
78 return Just<GCOptions>(options);
79 }
80
InvokeGC(v8::Isolate * isolate,ExecutionType execution_type,v8::Isolate::GarbageCollectionType type)81 void InvokeGC(v8::Isolate* isolate, ExecutionType execution_type,
82 v8::Isolate::GarbageCollectionType type) {
83 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
84 switch (type) {
85 case v8::Isolate::GarbageCollectionType::kMinorGarbageCollection:
86 heap->CollectGarbage(i::NEW_SPACE, i::GarbageCollectionReason::kTesting,
87 kGCCallbackFlagForced);
88 break;
89 case v8::Isolate::GarbageCollectionType::kFullGarbageCollection:
90 EmbedderStackStateScope stack_scope(
91 heap,
92 execution_type == ExecutionType::kAsync
93 ? EmbedderStackStateScope::kImplicitThroughTask
94 : EmbedderStackStateScope::kExplicitInvocation,
95 execution_type == ExecutionType::kAsync
96 ? v8::EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers
97 : v8::EmbedderHeapTracer::EmbedderStackState::
98 kMayContainHeapPointers);
99 heap->PreciseCollectAllGarbage(i::Heap::kNoGCFlags,
100 i::GarbageCollectionReason::kTesting,
101 kGCCallbackFlagForced);
102 break;
103 }
104 }
105
106 class AsyncGC final : public CancelableTask {
107 public:
108 ~AsyncGC() final = default;
109
AsyncGC(v8::Isolate * isolate,v8::Local<v8::Promise::Resolver> resolver,v8::Isolate::GarbageCollectionType type)110 AsyncGC(v8::Isolate* isolate, v8::Local<v8::Promise::Resolver> resolver,
111 v8::Isolate::GarbageCollectionType type)
112 : CancelableTask(reinterpret_cast<Isolate*>(isolate)),
113 isolate_(isolate),
114 ctx_(isolate, isolate->GetCurrentContext()),
115 resolver_(isolate, resolver),
116 type_(type) {}
117 AsyncGC(const AsyncGC&) = delete;
118 AsyncGC& operator=(const AsyncGC&) = delete;
119
RunInternal()120 void RunInternal() final {
121 v8::HandleScope scope(isolate_);
122 InvokeGC(isolate_, ExecutionType::kAsync, type_);
123 auto resolver = v8::Local<v8::Promise::Resolver>::New(isolate_, resolver_);
124 auto ctx = Local<v8::Context>::New(isolate_, ctx_);
125 resolver->Resolve(ctx, v8::Undefined(isolate_)).ToChecked();
126 }
127
128 private:
129 v8::Isolate* isolate_;
130 v8::Persistent<v8::Context> ctx_;
131 v8::Persistent<v8::Promise::Resolver> resolver_;
132 v8::Isolate::GarbageCollectionType type_;
133 };
134
135 } // namespace
136
GetNativeFunctionTemplate(v8::Isolate * isolate,v8::Local<v8::String> str)137 v8::Local<v8::FunctionTemplate> GCExtension::GetNativeFunctionTemplate(
138 v8::Isolate* isolate, v8::Local<v8::String> str) {
139 return v8::FunctionTemplate::New(isolate, GCExtension::GC);
140 }
141
GC(const v8::FunctionCallbackInfo<v8::Value> & args)142 void GCExtension::GC(const v8::FunctionCallbackInfo<v8::Value>& args) {
143 v8::Isolate* isolate = args.GetIsolate();
144
145 // Immediate bailout if no arguments are provided.
146 if (args.Length() == 0) {
147 InvokeGC(isolate, ExecutionType::kSync,
148 v8::Isolate::GarbageCollectionType::kFullGarbageCollection);
149 return;
150 }
151
152 auto maybe_options = Parse(isolate, args);
153 if (maybe_options.IsNothing()) return;
154 GCOptions options = maybe_options.ToChecked();
155 switch (options.execution) {
156 case ExecutionType::kSync:
157 InvokeGC(isolate, ExecutionType::kSync, options.type);
158 break;
159 case ExecutionType::kAsync: {
160 v8::HandleScope scope(isolate);
161 auto resolver = v8::Promise::Resolver::New(isolate->GetCurrentContext())
162 .ToLocalChecked();
163 args.GetReturnValue().Set(resolver->GetPromise());
164 auto task_runner =
165 V8::GetCurrentPlatform()->GetForegroundTaskRunner(isolate);
166 CHECK(task_runner->NonNestableTasksEnabled());
167 task_runner->PostNonNestableTask(
168 std::make_unique<AsyncGC>(isolate, resolver, options.type));
169 } break;
170 }
171 }
172
173 } // namespace internal
174 } // namespace v8
175