SetInternalFieldCount on constructor->InstanceTemplate() did not work for the instantiated object

吃可爱长大的小学妹 提交于 2019-12-25 00:52:53

问题


This post is about exposing C++ objects to the v8 javascript engine. To attach a C++ object to a javascript object, I make use of the GetInternalField() and External APIs. Before you can set or get any internal field, you have to call SetInternalFieldCount() on the corresponding ObjectTemplate. Since I want to expose a constructor function to the JS, I created a FunctionTemplate, set a C++ function that attache the native object to the JS object to that template, and finally SetInternalCount() on the InstanceTemplate() of that function template. Too much words for the description, here is what I did:

struct Point {
  int x, y;
  Local<FunctionTemplate> CreatePointContext(Isolate* isolate) {
     Local<FunctionTemplate> constructor = FunctionTemplate::New(isolate, &ConstructorCallback);
constructor->InstanceTemplate()->SetInternalFieldCount(1); // I set internal field count here.
    constructor->SetClassName(String::NewFromUtf8(isolate, "Point", NewStringType::kInternalized).ToLocalChecked());
    auto prototype_t = constructor->PrototypeTemplate();
    prototype_t->SetAccessor(String::NewFromUtf8(isolate, "x", NewStringType::kInternalized).ToLocalChecked(),
                             XGetterCallback);
    return constructor;
};

// This callback is bound to the constructor to attach a C++ Point instance to js object.
static void ConstructorCallback(const FunctionCallbackInfo<Value>& args) {
  auto isolate = args.GetIsolate();
  Local<External> external = External::New(isolate, new Point);
  args.Holder()->SetInternalField(0, external);
}

// This callback retrieves the C++ object and extract its 'x' field.
static void XGetterCallback(Local<String> property, const PropertyCallbackInfo<Value>& info) {
  auto external = Local<External>::Cast(info.Holder()->GetInternalField(0)); // This line triggers an out-of-bound error.
  auto point = reinterpret_cast<Point*>(external->Value());
  info.GetReturnValue().Set(static_cast< double>(point->x));
}

// This function creates a context that install the Point function template.
Local<Context> CreatePointContext(Isolate* isolate) {
  auto global = ObjectTemplate::New(isolate);
  auto point_ctor = Point::CreateClassTemplate(isolate);
  global->Set(isolate, "Point", point_ctor);
  return Context::New(isolate, nullptr, global);
}


When I tried to run the following JS code with the exposed C++ object, I got Internal field out of bounds error.

var p = new Point();
p.x;

I wonder setting internal field count on the instance template of a function template has nothing to do with the object created by the new expression. If so, what is the correct way to set the internal field count of the object created by new while exposing the constructor function to javascript? I want to achieve the following 2 things:

  • In javascript, a Point function is avaible so we can var p = new Point;.
  • In C++ I can make sure the JS object has 1 internal field for our C++ Point to live in.

Edit: As @snek pointed out, I changed Holder() to This() and everything started to work. But later When I changed SetAccessor to SetAccessorProperty, it worked even with Holder.

Although the behaviour are very confusing, I think the major problem may not lie in the difference between Holder and This, but rather in SetAccessor and SetAccessorProperty. Why? Because in many docs I have read, Holder should be identical to This in most cases and I believe without using Signature and given that my testing js code is so simple (not with any magic property moving), in my case This should just be Holder.

Thus I decided to post another question about SetAccessor and SetAccessorProperty and leave this post as a reference.

For why I am so sure about in my case This() == Holder() should hold, here are some old threads:

  • https://groups.google.com/forum/#!topic/v8-users/fK9PBWxJxtQ
  • https://groups.google.com/forum/#!topic/v8-users/Axf4hF_RfZo

And what does the docs say?

/**
   * If the callback was created without a Signature, this is the same
   * value as This(). If there is a signature, and the signature didn't match
   * This() but one of its hidden prototypes, this will be the respective
   * hidden prototype.
   *
   * Note that this is not the prototype of This() on which the accessor
   * referencing this callback was found (which in V8 internally is often
   * referred to as holder [sic]).
   */
  V8_INLINE Local<Object> Holder() const;

Note in my code there is not Signature, literally. So This and Holder should make no difference, but with SetAccessor, they made a difference.

来源:https://stackoverflow.com/questions/57675376/setinternalfieldcount-on-constructor-instancetemplate-did-not-work-for-the-in

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!