• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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