text: // Copyright 2017 the V8 project authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "include/v8.h"
  #include "src/api.h"
  #include "src/objects-inl.h"
  #include "src/objects/module.h"
  #include "src/objects/script.h"
  #include "src/objects/shared-function-info.h"
  #include "test/cctest/cctest.h"
  
  namespace v8 {
  namespace internal {
  namespace heap {
  
  namespace {
  
  v8::Local<v8::Object> ConstructTraceableJSApiObject(
      v8::Local<v8::Context> context, void* first_field, void* second_field) {
    v8::EscapableHandleScope scope(context->GetIsolate());
    v8::Local<v8::FunctionTemplate> function_t =
        v8::FunctionTemplate::New(context->GetIsolate());
    v8::Local<v8::ObjectTemplate> instance_t = function_t->InstanceTemplate();
    instance_t->SetInternalFieldCount(2);
    v8::Local<v8::Function> function =
        function_t->GetFunction(context).ToLocalChecked();
    v8::Local<v8::Object> instance =
        function->NewInstance(context).ToLocalChecked();
    instance->SetAlignedPointerInInternalField(0, first_field);
    instance->SetAlignedPointerInInternalField(1, second_field);
    CHECK(!instance.IsEmpty());
    i::Handle<i::JSReceiver> js_obj = v8::Utils::OpenHandle(*instance);
    CHECK_EQ(i::JS_API_OBJECT_TYPE, js_obj->map()->instance_type());
    return scope.Escape(instance);
  }
  
  class TestEmbedderHeapTracer final : public v8::EmbedderHeapTracer {
   public:
    explicit TestEmbedderHeapTracer(v8::Isolate* isolate) : isolate_(isolate) {}
  
    void RegisterV8References(
        const std::vector<std::pair<void*, void*>>& embedder_fields) final {
      registered_from_v8_.insert(registered_from_v8_.end(),
                                 embedder_fields.begin(), embedder_fields.end());
    }
  
    void AddReferenceForTracing(v8::Persistent<v8::Object>* persistent) {
      to_register_with_v8_.push_back(persistent);
    }
  
    bool AdvanceTracing(double deadline_in_ms,
                        AdvanceTracingActions actions) final {
      for (auto persistent : to_register_with_v8_) {
        persistent->RegisterExternalReference(isolate_);
      }
      to_register_with_v8_.clear();
      return false;
    }
  
    void TracePrologue() final {}
    void TraceEpilogue() final {}
    void AbortTracing() final {}
    void EnterFinalPause() final {}
  
    bool IsRegisteredFromV8(void* first_field) const {
      for (auto pair : registered_from_v8_) {
        if (pair.first == first_field) return true;
      }
      return false;
    }
  
   private:
    v8::Isolate* const isolate_;
    std::vector<std::pair<void*, void*>> registered_from_v8_;
    std::vector<v8::Persistent<v8::Object>*> to_register_with_v8_;
  };
  
  }  // namespace
  
  TEST(V8RegisteringEmbedderReference) {
    // Tests that wrappers are properly registered with the embedder heap
    // tracer.
    ManualGCScope manual_gc;
    CcTest::InitializeVM();
    v8::Isolate* isolate = CcTest::isolate();
    TestEmbedderHeapTracer tracer(isolate);
    isolate->SetEmbedderHeapTracer(&tracer);
    v8::HandleScope scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    v8::Context::Scope context_scope(context);
  
    void* first_field = reinterpret_cast<void*>(0x2);
    v8::Local<v8::Object> api_object =
        ConstructTraceableJSApiObject(context, first_field, nullptr);
    CHECK(!api_object.IsEmpty());
    CcTest::CollectGarbage(i::OLD_SPACE);
    CHECK(tracer.IsRegisteredFromV8(first_field));
  }
  
  TEST(EmbedderRegisteringV8Reference) {
    // Tests that references that are registered by the embedder heap tracer are
    // considered live by V8.
    ManualGCScope manual_gc;
    CcTest::InitializeVM();
    v8::Isolate* isolate = CcTest::isolate();
    TestEmbedderHeapTracer tracer(isolate);
    isolate->SetEmbedderHeapTracer(&tracer);
    v8::HandleScope scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    v8::Context::Scope context_scope(context);
  
    v8::Persistent<v8::Object> g;
    {
      v8::HandleScope inner_scope(isolate);
      v8::Local<v8::Object> o =
          v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
      g.Reset(isolate, o);
      g.SetWeak();
    }
    tracer.AddReferenceForTracing(&g);
    CcTest::CollectGarbage(i::OLD_SPACE);
    CHECK(!g.IsEmpty());
  }
  
  namespace {
  
  void ResurrectingFinalizer(
      const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
    data.GetParameter()->ClearWeak();
  }
  
  }  // namespace
  
  TEST(TracingInRevivedSubgraph) {
    // Tests that wrappers are traced when they are contained with in a subgraph
    // that is revived by a finalizer.
    ManualGCScope manual_gc;
    CcTest::InitializeVM();
    v8::Isolate* isolate = CcTest::isolate();
    TestEmbedderHeapTracer tracer(isolate);
    isolate->SetEmbedderHeapTracer(&tracer);
    v8::HandleScope scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    v8::Context::Scope context_scope(context);
  
    v8::Global<v8::Object> g;
    void* first_field = reinterpret_cast<void*>(0x4);
    {
      v8::HandleScope inner_scope(isolate);
      v8::Local<v8::Object> api_object =
          ConstructTraceableJSApiObject(context, first_field, nullptr);
      CHECK(!api_object.IsEmpty());
      v8::Local<v8::Object> o =
          v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
      o->Set(context, v8_str("link"), api_object).FromJust();
      g.Reset(isolate, o);
      g.SetWeak(&g, ResurrectingFinalizer, v8::WeakCallbackType::kFinalizer);
    }
    CcTest::CollectGarbage(i::OLD_SPACE);
    CHECK(tracer.IsRegisteredFromV8(first_field));
  }
  
  TEST(TracingInEphemerons) {
    // Tests that wrappers that are part of ephemerons are traced.
    ManualGCScope manual_gc;
    CcTest::InitializeVM();
    v8::Isolate* isolate = CcTest::isolate();
    TestEmbedderHeapTracer tracer(isolate);
    isolate->SetEmbedderHeapTracer(&tracer);
    v8::HandleScope scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    v8::Context::Scope context_scope(context);
  
    v8::Local<v8::Object> key =
        v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
    void* first_field = reinterpret_cast<void*>(0x8);
    i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
    Handle<JSWeakMap> weak_map = i_isolate->factory()->NewJSWeakMap();
    {
      v8::HandleScope inner_scope(isolate);
      v8::Local<v8::Object> api_object =
          ConstructTraceableJSApiObject(context, first_field, nullptr);
      CHECK(!api_object.IsEmpty());
      Handle<JSObject> js_key =
          handle(JSObject::cast(*v8::Utils::OpenHandle(*key)));
      Handle<JSReceiver> js_api_object = v8::Utils::OpenHandle(*api_object);
      int32_t hash = js_key->GetOrCreateHash(i_isolate)->value();
      JSWeakCollection::Set(weak_map, js_key, js_api_object, hash);
    }
    CcTest::CollectGarbage(i::OLD_SPACE);
    CHECK(tracer.IsRegisteredFromV8(first_field));
  }
  
  }  // namespace heap
  }  // namespace internal
  }  // namespace v8
  