gc-extension.cc 5.8 KB
Newer Older
1
// Copyright 2010 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#include "src/extensions/gc-extension.h"
6

7
#include "include/v8.h"
8
#include "src/base/platform/platform.h"
9 10 11
#include "src/execution/isolate.h"
#include "src/heap/heap.h"
#include "src/tasks/cancelable-task.h"
12 13 14 15

namespace v8 {
namespace internal {

16 17 18 19 20 21 22 23 24
namespace {

enum class ExecutionType { kAsync, kSync };

struct GCOptions {
  v8::Isolate::GarbageCollectionType type;
  ExecutionType execution;
};

25 26 27
Maybe<bool> IsProperty(v8::Isolate* isolate, v8::Local<v8::Context> ctx,
                       v8::Local<v8::Object> object, const char* key,
                       const char* value) {
28 29 30
  auto k = v8::String::NewFromUtf8(isolate, key).ToLocalChecked();
  // Get will return undefined for non-existing keys which will make
  // StrictEquals fail.
31
  auto maybe_property = object->Get(ctx, k);
32 33 34
  if (maybe_property.IsEmpty()) return Nothing<bool>();
  return Just<bool>(maybe_property.ToLocalChecked()->StrictEquals(
      v8::String::NewFromUtf8(isolate, value).ToLocalChecked()));
35 36
}

37
Maybe<GCOptions> Parse(v8::Isolate* isolate,
38
                       const v8::FunctionCallbackInfo<v8::Value>& args) {
39 40 41 42 43 44
  // Default values.
  auto options =
      GCOptions{v8::Isolate::GarbageCollectionType::kFullGarbageCollection,
                ExecutionType::kSync};
  bool found_options_object = false;

45 46 47
  if (args.Length() > 0 && args[0]->IsObject()) {
    v8::HandleScope scope(isolate);
    auto ctx = isolate->GetCurrentContext();
48
    auto param = v8::Local<v8::Object>::Cast(args[0]);
49 50 51
    auto maybe_type = IsProperty(isolate, ctx, param, "type", "minor");
    if (maybe_type.IsNothing()) return Nothing<GCOptions>();
    if (maybe_type.ToChecked()) {
52 53 54 55
      found_options_object = true;
      options.type =
          v8::Isolate::GarbageCollectionType::kMinorGarbageCollection;
    }
56 57 58 59
    auto maybe_execution =
        IsProperty(isolate, ctx, param, "execution", "async");
    if (maybe_execution.IsNothing()) return Nothing<GCOptions>();
    if (maybe_execution.ToChecked()) {
60 61 62 63 64 65 66 67 68 69 70 71 72
      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;
  }

73
  return Just<GCOptions>(options);
74 75 76 77 78 79 80 81 82 83 84
}

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:
85
      heap->SetEmbedderStackStateForNextFinalization(embedder_stack_state);
86 87 88 89 90 91 92 93 94 95 96
      heap->PreciseCollectAllGarbage(i::Heap::kNoGCFlags,
                                     i::GarbageCollectionReason::kTesting,
                                     kGCCallbackFlagForced);
      break;
  }
}

class AsyncGC final : public CancelableTask {
 public:
  ~AsyncGC() final = default;

97
  AsyncGC(v8::Isolate* isolate, v8::Local<v8::Promise::Resolver> resolver,
98 99 100
          v8::Isolate::GarbageCollectionType type)
      : CancelableTask(reinterpret_cast<Isolate*>(isolate)),
        isolate_(isolate),
101
        ctx_(isolate, isolate->GetCurrentContext()),
102 103
        resolver_(isolate, resolver),
        type_(type) {}
104 105
  AsyncGC(const AsyncGC&) = delete;
  AsyncGC& operator=(const AsyncGC&) = delete;
106 107 108 109

  void RunInternal() final {
    v8::HandleScope scope(isolate_);
    InvokeGC(isolate_, type_,
110
             v8::EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers);
111 112 113 114 115 116 117 118 119 120 121 122 123
    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_;
};

}  // namespace
124

125 126
v8::Local<v8::FunctionTemplate> GCExtension::GetNativeFunctionTemplate(
    v8::Isolate* isolate, v8::Local<v8::String> str) {
127
  return v8::FunctionTemplate::New(isolate, GCExtension::GC);
128 129
}

130
void GCExtension::GC(const v8::FunctionCallbackInfo<v8::Value>& args) {
131 132
  v8::Isolate* isolate = args.GetIsolate();

133 134
  // Immediate bailout if no arguments are provided.
  if (args.Length() == 0) {
135 136 137
    InvokeGC(
        isolate, v8::Isolate::GarbageCollectionType::kFullGarbageCollection,
        v8::EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers);
138 139 140 141
    return;
  }

  auto maybe_options = Parse(isolate, args);
142 143
  if (maybe_options.IsNothing()) return;
  GCOptions options = maybe_options.ToChecked();
144 145
  switch (options.execution) {
    case ExecutionType::kSync:
146 147 148
      InvokeGC(
          isolate, options.type,
          v8::EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers);
149 150
      break;
    case ExecutionType::kAsync: {
151
      v8::HandleScope scope(isolate);
152 153 154 155 156 157 158
      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(
159
          std::make_unique<AsyncGC>(isolate, resolver, options.type));
160 161
    } break;
  }
162 163
}

164 165
}  // namespace internal
}  // namespace v8