I am having some trouble making the following code work. It is a slight extension of the material found at https://github.com/v8/v8/wiki/Embedders-Guide#accessing-dynamic-variables. It differs in that it has a constructor for the object, in addition to just the object. The code will run successfully, however will leak the native |PointAndPersistent| allocated in |PointConstructor|. I have attempted to provide a callback to clean it up with |SetWeak|, based on some code I found in d8 (https://cs.chromium.org/chromium/src/v8/src/d8.cc?q=DataAndPersistent&sq=package:chromium&l=215), however it is never executed. Does anyone know what the proper way to accomplish this is?
#include <cstdio>
#include "v8/include/libplatform/libplatform.h"
#include "v8/include/v8.h"
using namespace v8;
struct PointAndPersistent {
int x;
int y;
Global<Object> wrapper;
};
void PointAndPersistentCallback(
const WeakCallbackInfo<PointAndPersistent>& data) {
data.GetParameter()->wrapper.Reset();
delete data.GetParameter();
}
void PointConstructor(const FunctionCallbackInfo<Value>& args) {
PointAndPersistent* point_and_persistent = new PointAndPersistent();
if (args.Length() > 0) {
point_and_persistent->x = args[0]->Int32Value();
}
if (args.Length() > 1) {
point_and_persistent->y = args[1]->Int32Value();
}
point_and_persistent->wrapper.Reset(args.GetIsolate(), args.This());
point_and_persistent->wrapper.SetWeak(point_and_persistent,
PointAndPersistentCallback,
v8::WeakCallbackType::kParameter);
point_and_persistent->wrapper.MarkIndependent();
args.This()->SetInternalField(0,
External::New(args.GetIsolate(), point_and_persistent));
args.GetReturnValue().Set(args.This());
}
void GetPointX(Local<String> property,
const PropertyCallbackInfo<Value>& info) {
Local<Object> self = info.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
int value = static_cast<PointAndPersistent*>(ptr)->x;
info.GetReturnValue().Set(value);
}
void SetPointX(Local<String> property, Local<Value> value,
const PropertyCallbackInfo<void>& info) {
Local<Object> self = info.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
static_cast<PointAndPersistent*>(ptr)->x = value->Int32Value();
}
void GetPointY(Local<String> property,
const PropertyCallbackInfo<Value>& info) {
Local<Object> self = info.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
int value = static_cast<PointAndPersistent*>(ptr)->y;
info.GetReturnValue().Set(value);
}
void SetPointY(Local<String> property, Local<Value> value,
const PropertyCallbackInfo<void>& info) {
Local<Object> self = info.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
static_cast<PointAndPersistent*>(ptr)->y = value->Int32Value();
}
int main() {
Platform* platform = platform::CreateDefaultPlatform();
V8::InitializePlatform(platform);
V8::Initialize();
Isolate::CreateParams params;
params.array_buffer_allocator = ArrayBuffer::Allocator::NewDefaultAllocator();
Isolate* isolate = Isolate::New(params);
{
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
Local<FunctionTemplate> point_constructor_template =
FunctionTemplate::New(isolate, PointConstructor);
point_constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
point_constructor_template->InstanceTemplate()->SetAccessor(
String::NewFromUtf8(isolate, "x"), GetPointX, SetPointX);
point_constructor_template->InstanceTemplate()->SetAccessor(
String::NewFromUtf8(isolate, "y"), GetPointY, SetPointY);
Local<ObjectTemplate> point_template =
ObjectTemplate::New(isolate, point_constructor_template);
Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
Local<String> name =
String::NewFromUtf8(isolate, "Point", NewStringType::kNormal)
.ToLocalChecked();
global_template->Set(name, point_constructor_template);
Local<Context> context = Context::New(isolate, nullptr, global_template);
Context::Scope context_scope(context);
Local<String> source = String::NewFromUtf8(isolate, "new Point(1, 2).x;",
NewStringType::kNormal)
.ToLocalChecked();
Local<Script> script = Script::Compile(context, source).ToLocalChecked();
Local<Value> result = script->Run(context).ToLocalChecked();
String::Utf8Value utf8(isolate, result);
printf("%s\n", *utf8);
}
isolate->Dispose();
V8::Dispose();
V8::ShutdownPlatform();
delete platform;
delete params.array_buffer_allocator;
}
Thanks!
Weak callbacks are invoked when the garbage collector determines that the object can be freed. A short-running program like your example has no need to ever run the garbage collector.
You can try to force a GC cycle before shutting down your app. Of course that will make your shutdown slower.
For the record, the documentation of SetWeak
says:
NOTE: There is no guarantee as to when or even if the callback is invoked. The invocation is performed solely on a best effort basis. As always, GC-based finalization should not be relied upon for any critical form of resource management!