electron/shell/common/gin_helper/wrappable.cc
Calvin 4fff74b73e
chore: move gin::DeprecatedWrappable to gin_helper (#47996)
chore: move gin::DeprecatedWrappable to gin_helper (#47958)

* chore: move gin::DeprecatedWrappable to gin_helper

This is in preparation for migrating to gin::Wrappable
based on cppgc #47922
The upstream class will be deleted soon via roller PR but
the cppgc migration should happen outside the roll, this
change retains the current functionality by copying the
implementation into //electron/shell/common/gin_helper.
The class can be deleted once the cppgc migration is complete.

* chore: fix lint:cpp

Co-authored-by: Robo <hop2deep@gmail.com>
2025-08-09 13:00:45 +09:00

213 lines
6.6 KiB
C++

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE.chromium file.
#include "shell/common/gin_helper/wrappable.h"
#include "gin/object_template_builder.h"
#include "gin/public/isolate_holder.h"
#include "shell/common/gin_helper/dictionary.h"
#include "v8/include/v8-function.h"
namespace gin_helper {
bool IsValidWrappable(const v8::Local<v8::Value>& val,
const gin::DeprecatedWrapperInfo* wrapper_info) {
if (!val->IsObject())
return false;
v8::Local<v8::Object> port = val.As<v8::Object>();
if (port->InternalFieldCount() != gin::kNumberOfInternalFields)
return false;
const gin::DeprecatedWrapperInfo* info =
static_cast<gin::DeprecatedWrapperInfo*>(
port->GetAlignedPointerFromInternalField(gin::kWrapperInfoIndex));
if (info != wrapper_info)
return false;
return true;
}
WrappableBase::WrappableBase() = default;
WrappableBase::~WrappableBase() {
if (wrapper_.IsEmpty())
return;
v8::HandleScope scope(isolate());
GetWrapper()->SetAlignedPointerInInternalField(0, nullptr);
wrapper_.ClearWeak();
wrapper_.Reset();
}
v8::Local<v8::Object> WrappableBase::GetWrapper() const {
if (!wrapper_.IsEmpty())
return v8::Local<v8::Object>::New(isolate_, wrapper_);
else
return {};
}
void WrappableBase::InitWithArgs(gin::Arguments* args) {
v8::Local<v8::Object> holder;
args->GetHolder(&holder);
InitWith(args->isolate(), holder);
}
void WrappableBase::InitWith(v8::Isolate* isolate,
v8::Local<v8::Object> wrapper) {
CHECK(wrapper_.IsEmpty());
isolate_ = isolate;
wrapper->SetAlignedPointerInInternalField(0, this);
wrapper_.Reset(isolate, wrapper);
wrapper_.SetWeak(this, FirstWeakCallback,
v8::WeakCallbackType::kInternalFields);
// Call object._init if we have one.
v8::Local<v8::Function> init;
if (Dictionary(isolate, wrapper).Get("_init", &init))
init->Call(isolate->GetCurrentContext(), wrapper, 0, nullptr).IsEmpty();
}
// static
void WrappableBase::FirstWeakCallback(
const v8::WeakCallbackInfo<WrappableBase>& data) {
WrappableBase* wrappable = data.GetParameter();
auto* wrappable_from_field =
static_cast<WrappableBase*>(data.GetInternalField(0));
if (wrappable && wrappable == wrappable_from_field) {
wrappable->wrapper_.Reset();
data.SetSecondPassCallback(SecondWeakCallback);
}
}
// static
void WrappableBase::SecondWeakCallback(
const v8::WeakCallbackInfo<WrappableBase>& data) {
if (gin::IsolateHolder::DestroyedMicrotasksRunner()) {
return;
}
delete static_cast<WrappableBase*>(data.GetInternalField(0));
}
DeprecatedWrappableBase::DeprecatedWrappableBase() = default;
DeprecatedWrappableBase::~DeprecatedWrappableBase() {
if (!wrapper_.IsEmpty())
wrapper_.ClearWeak();
wrapper_.Reset();
}
gin::ObjectTemplateBuilder DeprecatedWrappableBase::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return gin::ObjectTemplateBuilder(isolate, GetTypeName());
}
const char* DeprecatedWrappableBase::GetTypeName() {
return nullptr;
}
void DeprecatedWrappableBase::FirstWeakCallback(
const v8::WeakCallbackInfo<DeprecatedWrappableBase>& data) {
DeprecatedWrappableBase* wrappable = data.GetParameter();
DeprecatedWrappableBase* wrappable_from_field =
static_cast<DeprecatedWrappableBase*>(data.GetInternalField(1));
if (wrappable && wrappable == wrappable_from_field) {
wrappable->dead_ = true;
wrappable->wrapper_.Reset();
data.SetSecondPassCallback(SecondWeakCallback);
}
}
void DeprecatedWrappableBase::SecondWeakCallback(
const v8::WeakCallbackInfo<DeprecatedWrappableBase>& data) {
if (gin::IsolateHolder::DestroyedMicrotasksRunner())
return;
DeprecatedWrappableBase* wrappable = data.GetParameter();
if (wrappable)
delete wrappable;
}
v8::MaybeLocal<v8::Object> DeprecatedWrappableBase::GetWrapperImpl(
v8::Isolate* isolate,
gin::DeprecatedWrapperInfo* info) {
if (!wrapper_.IsEmpty()) {
return v8::MaybeLocal<v8::Object>(
v8::Local<v8::Object>::New(isolate, wrapper_));
}
if (dead_) {
return v8::MaybeLocal<v8::Object>();
}
gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
v8::Local<v8::ObjectTemplate> templ = data->DeprecatedGetObjectTemplate(info);
if (templ.IsEmpty()) {
templ = GetObjectTemplateBuilder(isolate).Build();
CHECK(!templ.IsEmpty());
data->DeprecatedSetObjectTemplate(info, templ);
}
CHECK_EQ(gin::kNumberOfInternalFields, templ->InternalFieldCount());
v8::Local<v8::Object> wrapper;
// |wrapper| may be empty in some extreme cases, e.g., when
// Object.prototype.constructor is overwritten.
if (!templ->NewInstance(isolate->GetCurrentContext()).ToLocal(&wrapper)) {
// The current wrappable object will be no longer managed by V8. Delete this
// now.
delete this;
return v8::MaybeLocal<v8::Object>(wrapper);
}
int indices[] = {gin::kWrapperInfoIndex, gin::kEncodedValueIndex};
void* values[] = {info, this};
wrapper->SetAlignedPointerInInternalFields(2, indices, values);
wrapper_.Reset(isolate, wrapper);
wrapper_.SetWeak(this, FirstWeakCallback,
v8::WeakCallbackType::kInternalFields);
return v8::MaybeLocal<v8::Object>(wrapper);
}
void DeprecatedWrappableBase::ClearWeak() {
if (!wrapper_.IsEmpty())
wrapper_.ClearWeak();
}
namespace internal {
void* FromV8Impl(v8::Isolate* isolate, v8::Local<v8::Value> val) {
if (!val->IsObject())
return nullptr;
v8::Local<v8::Object> obj = val.As<v8::Object>();
if (obj->InternalFieldCount() != 1)
return nullptr;
return obj->GetAlignedPointerFromInternalField(0);
}
void* FromV8Impl(v8::Isolate* isolate,
v8::Local<v8::Value> val,
gin::DeprecatedWrapperInfo* wrapper_info) {
if (!val->IsObject()) {
return nullptr;
}
v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(val);
gin::DeprecatedWrapperInfo* info = gin::DeprecatedWrapperInfo::From(obj);
// If this fails, the object is not managed by Gin. It is either a normal JS
// object that's not wrapping any external C++ object, or it is wrapping some
// C++ object, but that object isn't managed by Gin (maybe Blink).
if (!info) {
return nullptr;
}
// If this fails, the object is managed by Gin, but it's not wrapping an
// instance of the C++ class associated with wrapper_info.
if (info != wrapper_info) {
return nullptr;
}
return obj->GetAlignedPointerFromInternalField(gin::kEncodedValueIndex);
}
} // namespace internal
} // namespace gin_helper