File: test_cppgc.cc

package info (click to toggle)
nodejs 20.19.2%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 219,072 kB
  • sloc: cpp: 1,277,408; javascript: 565,332; ansic: 129,476; python: 58,536; sh: 3,841; makefile: 2,725; asm: 1,732; perl: 248; lisp: 222; xml: 42
file content (127 lines) | stat: -rw-r--r-- 4,385 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <cppgc/allocation.h>
#include <cppgc/garbage-collected.h>
#include <cppgc/heap.h>
#include <node.h>
#include <v8-cppgc.h>
#include <v8.h>
#include "node_test_fixture.h"

// This tests that Node.js can work with an existing CppHeap.

// Mimic the Blink layout.
static int kWrappableTypeIndex = 0;
static int kWrappableInstanceIndex = 1;
static uint16_t kEmbedderID = 0x1;

// Mimic a class that does not know about Node.js.
class CppGCed : public cppgc::GarbageCollected<CppGCed> {
 public:
  static int kConstructCount;
  static int kDestructCount;
  static int kTraceCount;

  static void New(const v8::FunctionCallbackInfo<v8::Value>& args) {
    v8::Isolate* isolate = args.GetIsolate();
    v8::Local<v8::Object> js_object = args.This();
    CppGCed* gc_object = cppgc::MakeGarbageCollected<CppGCed>(
        isolate->GetCppHeap()->GetAllocationHandle());
    js_object->SetAlignedPointerInInternalField(kWrappableTypeIndex,
                                                &kEmbedderID);
    js_object->SetAlignedPointerInInternalField(kWrappableInstanceIndex,
                                                gc_object);
    kConstructCount++;
    args.GetReturnValue().Set(js_object);
  }

  static v8::Local<v8::Function> GetConstructor(
      v8::Local<v8::Context> context) {
    auto ft = v8::FunctionTemplate::New(context->GetIsolate(), New);
    auto ot = ft->InstanceTemplate();
    ot->SetInternalFieldCount(2);
    return ft->GetFunction(context).ToLocalChecked();
  }

  CppGCed() = default;

  ~CppGCed() { kDestructCount++; }

  void Trace(cppgc::Visitor* visitor) const { kTraceCount++; }
};

int CppGCed::kConstructCount = 0;
int CppGCed::kDestructCount = 0;
int CppGCed::kTraceCount = 0;

TEST_F(NodeZeroIsolateTestFixture, ExistingCppHeapTest) {
  v8::Isolate* isolate =
      node::NewIsolate(allocator.get(), &current_loop, platform.get());

  // Create and attach the CppHeap before we set up the IsolateData so that
  // it recognizes the existing heap.
  std::unique_ptr<v8::CppHeap> cpp_heap = v8::CppHeap::Create(
      platform.get(),
      v8::CppHeapCreateParams(
          {},
          v8::WrapperDescriptor(
              kWrappableTypeIndex, kWrappableInstanceIndex, kEmbedderID)));
  isolate->AttachCppHeap(cpp_heap.get());

  // Try creating Context + IsolateData + Environment.
  {
    v8::Isolate::Scope isolate_scope(isolate);
    v8::HandleScope handle_scope(isolate);

    std::unique_ptr<node::IsolateData, decltype(&node::FreeIsolateData)>
        isolate_data{
            node::CreateIsolateData(isolate, &current_loop, platform.get()),
            node::FreeIsolateData};
    CHECK(isolate_data);

    {
      auto context = node::NewContext(isolate);
      CHECK(!context.IsEmpty());
      v8::Context::Scope context_scope(context);

      // An Environment should already contain a few BaseObjects that are
      // supposed to have non-cppgc IDs.
      std::unique_ptr<node::Environment, decltype(&node::FreeEnvironment)>
          environment{
              node::CreateEnvironment(isolate_data.get(), context, {}, {}),
              node::FreeEnvironment};
      CHECK(environment);

      context->Global()
          ->Set(context,
                v8::String::NewFromUtf8(isolate, "CppGCed").ToLocalChecked(),
                CppGCed::GetConstructor(context))
          .FromJust();

      const char* code =
          "globalThis.array = [];"
          "for (let i = 0; i < 100; ++i) { array.push(new CppGCed()); }";
      node::LoadEnvironment(environment.get(), code).ToLocalChecked();
      // Request a GC and check if CppGCed::Trace() is invoked.
      isolate->LowMemoryNotification();
      int exit_code = SpinEventLoop(environment.get()).FromJust();
      EXPECT_EQ(exit_code, 0);
    }

    platform->DrainTasks(isolate);

    // Cleanup.
    isolate->DetachCppHeap();
    cpp_heap->Terminate();
    platform->DrainTasks(isolate);
  }

  platform->UnregisterIsolate(isolate);
  isolate->Dispose();

  // Check that all the objects are created and destroyed properly.
  EXPECT_EQ(CppGCed::kConstructCount, 100);
  EXPECT_EQ(CppGCed::kDestructCount, 100);
  // GC does not have to feel pressured enough to visit all of them to
  // reclaim memory before we are done with the isolate and the entire
  // heap can be reclaimed. So just check at least some of them are traced.
  EXPECT_GT(CppGCed::kTraceCount, 0);
}