v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
gc-extension.cc
Go to the documentation of this file.
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
6
9#include "include/v8-maybe.h"
11#include "include/v8-object.h"
13#include "include/v8-platform.h"
15#include "include/v8-profiler.h"
16#include "include/v8-template.h"
17#include "src/api/api.h"
19#include "src/heap/heap.h"
22
23namespace v8::internal {
24namespace {
25
26enum class GCType { kMinor, kMajor, kMajorWithSnapshot };
27enum class ExecutionType { kAsync, kSync };
28enum class Flavor { kRegular, kLastResort };
29
30struct GCOptions {
31 static GCOptions GetDefault() {
32 return {GCType::kMajor, ExecutionType::kSync, Flavor::kRegular,
33 "heap.heapsnapshot"};
34 }
35 static GCOptions GetDefaultForTruthyWithoutOptionsBag() {
36 return {GCType::kMinor, ExecutionType::kSync, Flavor::kRegular,
37 "heap.heapsnapshot"};
38 }
39
40 // Used with Nothing<GCOptions>.
41 GCOptions() = default;
42
43 GCType type;
44 ExecutionType execution;
45 Flavor flavor;
46 std::string filename;
47
48 private:
49 GCOptions(GCType type, ExecutionType execution, Flavor flavor,
50 std::string filename)
51 : type(type), execution(execution), flavor(flavor), filename(filename) {}
52};
53
54MaybeLocal<v8::String> ReadProperty(v8::Isolate* isolate,
57 const char* key) {
58 auto k = v8::String::NewFromUtf8(isolate, key).ToLocalChecked();
59 auto maybe_property = object->Get(ctx, k);
61 if (!maybe_property.ToLocal(&property) || !property->IsString()) {
62 return {};
63 }
64 return MaybeLocal<v8::String>(property.As<v8::String>());
65}
66
67void ParseType(v8::Isolate* isolate, MaybeLocal<v8::String> maybe_type,
68 GCOptions* options, bool* found_options_object) {
69 if (maybe_type.IsEmpty()) return;
70
71 auto type = maybe_type.ToLocalChecked();
72 if (type->StrictEquals(
73 v8::String::NewFromUtf8(isolate, "minor").ToLocalChecked())) {
74 *found_options_object = true;
75 options->type = GCType::kMinor;
76 } else if (type->StrictEquals(
77 v8::String::NewFromUtf8(isolate, "major").ToLocalChecked())) {
78 *found_options_object = true;
79 options->type = GCType::kMajor;
80 } else if (type->StrictEquals(
81 v8::String::NewFromUtf8(isolate, "major-snapshot")
82 .ToLocalChecked())) {
83 *found_options_object = true;
84 options->type = GCType::kMajorWithSnapshot;
85 }
86}
87
88void ParseExecution(v8::Isolate* isolate,
89 MaybeLocal<v8::String> maybe_execution, GCOptions* options,
90 bool* found_options_object) {
91 if (maybe_execution.IsEmpty()) return;
92
93 auto type = maybe_execution.ToLocalChecked();
94 if (type->StrictEquals(
95 v8::String::NewFromUtf8(isolate, "async").ToLocalChecked())) {
96 *found_options_object = true;
97 options->execution = ExecutionType::kAsync;
98 } else if (type->StrictEquals(
99 v8::String::NewFromUtf8(isolate, "sync").ToLocalChecked())) {
100 *found_options_object = true;
101 options->execution = ExecutionType::kSync;
102 }
103}
104
105void ParseFlavor(v8::Isolate* isolate, MaybeLocal<v8::String> maybe_execution,
106 GCOptions* options, bool* found_options_object) {
107 if (maybe_execution.IsEmpty()) return;
108
109 auto type = maybe_execution.ToLocalChecked();
110 if (type->StrictEquals(
111 v8::String::NewFromUtf8(isolate, "regular").ToLocalChecked())) {
112 *found_options_object = true;
113 options->flavor = Flavor::kRegular;
114 } else if (type->StrictEquals(v8::String::NewFromUtf8(isolate, "last-resort")
115 .ToLocalChecked())) {
116 *found_options_object = true;
117 options->flavor = Flavor::kLastResort;
118 }
119}
120
121Maybe<GCOptions> Parse(v8::Isolate* isolate,
124 DCHECK_LT(0, info.Length());
125
126 // Default values.
127 auto options = GCOptions::GetDefault();
128 // This will only ever transition to true if one property is found. It will
129 // never toggle.
130 bool found_options_object = false;
131
132 if (info[0]->IsObject()) {
133 v8::HandleScope scope(isolate);
134 auto ctx = isolate->GetCurrentContext();
135 auto param = v8::Local<v8::Object>::Cast(info[0]);
136
137 v8::TryCatch catch_block(isolate);
138 ParseType(isolate, ReadProperty(isolate, ctx, param, "type"), &options,
139 &found_options_object);
140 if (catch_block.HasCaught()) {
141 catch_block.ReThrow();
142 return Nothing<GCOptions>();
143 }
144 ParseExecution(isolate, ReadProperty(isolate, ctx, param, "execution"),
145 &options, &found_options_object);
146 if (catch_block.HasCaught()) {
147 catch_block.ReThrow();
148 return Nothing<GCOptions>();
149 }
150 ParseFlavor(isolate, ReadProperty(isolate, ctx, param, "flavor"), &options,
151 &found_options_object);
152 if (catch_block.HasCaught()) {
153 catch_block.ReThrow();
154 return Nothing<GCOptions>();
155 }
156
157 if (options.type == GCType::kMajorWithSnapshot) {
158 auto maybe_filename = ReadProperty(isolate, ctx, param, "filename");
159 if (catch_block.HasCaught()) {
160 catch_block.ReThrow();
161 return Nothing<GCOptions>();
162 }
163 Local<v8::String> filename;
164 if (maybe_filename.ToLocal(&filename)) {
165 size_t buffer_size = filename->Utf8LengthV2(isolate) + 1;
166 std::unique_ptr<char[]> buffer(new char[buffer_size]);
167 filename->WriteUtf8V2(isolate, buffer.get(), buffer_size,
169 options.filename = std::string(buffer.get());
170 // Not setting found_options_object as the option only makes sense with
171 // properly set type anyways.
172 CHECK(found_options_object);
173 }
174 }
175 }
176
177 // If the parameter is not an object or if it does not define any relevant
178 // options, default to legacy behavior.
179 if (!found_options_object) {
180 return Just<GCOptions>(GCOptions::GetDefaultForTruthyWithoutOptionsBag());
181 }
182
183 return Just<GCOptions>(options);
184}
185
186void InvokeGC(v8::Isolate* isolate, const GCOptions gc_options) {
187 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
188 EmbedderStackStateScope stack_scope(
189 heap,
190 gc_options.execution == ExecutionType::kAsync
193 gc_options.execution == ExecutionType::kAsync
194 ? StackState::kNoHeapPointers
195 : StackState::kMayContainHeapPointers);
196 switch (gc_options.type) {
197 case GCType::kMinor:
198 heap->CollectGarbage(i::NEW_SPACE, i::GarbageCollectionReason::kTesting,
200 break;
201 case GCType::kMajor:
202 switch (gc_options.flavor) {
203 case Flavor::kRegular:
204 heap->PreciseCollectAllGarbage(i::GCFlag::kNoFlags,
205 i::GarbageCollectionReason::kTesting,
207 break;
208 case Flavor::kLastResort:
209 heap->CollectAllAvailableGarbage(
210 i::GarbageCollectionReason::kTesting);
211
212 break;
213 }
214 break;
215 case GCType::kMajorWithSnapshot:
216 heap->PreciseCollectAllGarbage(i::GCFlag::kNoFlags,
217 i::GarbageCollectionReason::kTesting,
219 HeapProfiler* heap_profiler = heap->heap_profiler();
220 // Since this API is intended for V8 devs, we do not treat globals as
221 // roots here on purpose.
223 options.numerics_mode =
225 options.snapshot_mode =
227 heap_profiler->TakeSnapshotToFile(options, gc_options.filename);
228 break;
229 }
230}
231
232class AsyncGC final : public CancelableTask {
233 public:
234 ~AsyncGC() final = default;
235
236 AsyncGC(v8::Isolate* isolate, v8::Local<v8::Promise::Resolver> resolver,
237 GCOptions options)
238 : CancelableTask(reinterpret_cast<Isolate*>(isolate)),
239 isolate_(isolate),
240 ctx_(isolate, isolate->GetCurrentContext()),
241 resolver_(isolate, resolver),
242 options_(options) {}
243 AsyncGC(const AsyncGC&) = delete;
244 AsyncGC& operator=(const AsyncGC&) = delete;
245
246 void RunInternal() final {
247 v8::HandleScope scope(isolate_);
248 InvokeGC(isolate_, options_);
249 auto resolver = v8::Local<v8::Promise::Resolver>::New(isolate_, resolver_);
250 auto ctx = Local<v8::Context>::New(isolate_, ctx_);
251 v8::MicrotasksScope microtasks_scope(
253 resolver->Resolve(ctx, v8::Undefined(isolate_)).ToChecked();
254 }
255
256 private:
260 GCOptions options_;
261};
262
263} // namespace
264
269
272 v8::Isolate* isolate = info.GetIsolate();
273
274 // Immediate bailout if no arguments are provided.
275 if (info.Length() == 0) {
276 InvokeGC(isolate, GCOptions::GetDefault());
277 return;
278 }
279
280 GCOptions options;
281 if (!Parse(isolate, info).To(&options)) {
282 // Parsing ran into an exception. Just bail out without GC in this case.
283 return;
284 }
285 switch (options.execution) {
286 case ExecutionType::kSync:
287 InvokeGC(isolate, options);
288 break;
289 case ExecutionType::kAsync: {
290 v8::HandleScope scope(isolate);
291 auto resolver = v8::Promise::Resolver::New(isolate->GetCurrentContext())
292 .ToLocalChecked();
293 info.GetReturnValue().Set(resolver->GetPromise());
294 auto task_runner =
296 CHECK(task_runner->NonNestableTasksEnabled());
297 task_runner->PostNonNestableTask(
298 std::make_unique<AsyncGC>(isolate, resolver, options));
299 } break;
300 }
301}
302
303} // namespace v8::internal
Isolate * isolate_
static Local< FunctionTemplate > New(Isolate *isolate, FunctionCallback callback=nullptr, Local< Value > data=Local< Value >(), Local< Signature > signature=Local< Signature >(), int length=0, ConstructorBehavior behavior=ConstructorBehavior::kAllow, SideEffectType side_effect_type=SideEffectType::kHasSideEffect, const CFunction *c_function=nullptr, uint16_t instance_type=0, uint16_t allowed_receiver_instance_type_range_start=0, uint16_t allowed_receiver_instance_type_range_end=0)
Definition api.cc:1101
static V8_INLINE Local< T > Cast(Local< S > that)
static V8_INLINE Local< T > New(Isolate *isolate, Local< T > that)
std::shared_ptr< v8::TaskRunner > GetForegroundTaskRunner(Isolate *isolate)
static V8_WARN_UNUSED_RESULT MaybeLocal< Resolver > New(Local< Context > context)
Definition api.cc:8640
static V8_WARN_UNUSED_RESULT MaybeLocal< String > NewFromUtf8(Isolate *isolate, const char *data, NewStringType type=NewStringType::kNormal, int length=-1)
Definition api.cc:7593
static void GC(const v8::FunctionCallbackInfo< v8::Value > &info)
v8::Local< v8::FunctionTemplate > GetNativeFunctionTemplate(v8::Isolate *isolate, v8::Local< v8::String > name) override
static V8_EXPORT_PRIVATE v8::Platform * GetCurrentPlatform()
Definition v8.cc:282
v8::Global< v8::Promise::Resolver > resolver_
v8::Global< v8::Context > ctx_
GCOptions options_
Flavor flavor
std::string filename
Isolate * isolate
const std::string property
DirectHandle< JSReceiver > options
bool V8_EXPORT ValidateCallbackInfo(const FunctionCallbackInfo< void > &info)
Definition api.cc:12301
V8_INLINE constexpr bool IsObject(TaggedImpl< kRefType, StorageType > obj)
Definition objects.h:661
@ kGCCallbackFlagForced
Maybe< T > Nothing()
Definition v8-maybe.h:112
V8_INLINE Local< Primitive > Undefined(Isolate *isolate)
Maybe< T > Just(const T &t)
Definition v8-maybe.h:117
#define CHECK(condition)
Definition logging.h:124
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489