简体   繁体   English

在V8 JavaScript引擎中,如何使每个实例重新使用ObjectTemplate的构造函数?

[英]In the V8 javascript engine, how to make a constructor function that re-uses an ObjectTemplate for each instance?

I have working code where I can create as many Point objects as I want, but it re-creates the object template each time the constructor is called, which seems like it's probably wrong. 我有工作代码,可以在其中创建任意数量的Point对象,但是每次调用构造函数时,它都会重新创建对象模板,这似乎是错误的。

Local<ObjectTemplate> global_templ = ObjectTemplate::New(isolate);

// make the Point constructor function available to JS
global_templ->Set(v8::String::NewFromUtf8(isolate, "Point"), FunctionTemplate::New(isolate, v8_Point));

and then the constructor itself: 然后是构造函数本身:

void v8_Point(const v8::FunctionCallbackInfo<v8::Value>& args) {
    HandleScope scope(args.GetIsolate());

    // this bit should probably be cached somehow
    Local<ObjectTemplate> point_template = ObjectTemplate::New(args.GetIsolate());
    point_template->SetInternalFieldCount(1);

    point_template->SetAccessor(String::NewFromUtf8(args.GetIsolate(), "x"), GetPointX, SetPointX);
    point_template->SetAccessor(String::NewFromUtf8(args.GetIsolate(), "y"), GetPointY, SetPointY);
    // end section to be cached

    Local<Object> obj = point_template->NewInstance();
    Point * p = new Point(1,1);
    obj->SetInternalField(0, External::New(args.GetIsolate(), p));
    args.GetReturnValue().Set(obj);
}

But it seems like I should be able to pass in the point_template object instead of re-creating it each time. 但是似乎我应该能够传递point_template对象,而不是每次都重新创建它。 I saw that there's a Data() field in args, but that only allows for a Value type and an ObjectTemplate is of type Template, not Value. 我看到args中有一个Data()字段,但这仅允许使用Value类型,而ObjectTemplate的类型为Template,而不是Value。

Any help on the right way to do this would be greatly appreciated. 任何以正确方式做到这一点的帮助将不胜感激。

I figured it out finally. 我终于想通了。

In javascript, when you add a function via a FunctionTemplate and then call it as a constructor (eg new MyFunction ), then in your c++ callback the args.This() will be a new object created by the using the FunctionTemplate 's InstanceTemplate object template. 在javascript中,当您通过FunctionTemplate添加一个函数,然后将其作为构造函数调用时(例如new MyFunction ),然后在c ++回调中, args.This()将是使用FunctionTemplateInstanceTemplate对象创建的新对象模板。

// Everything has to go in a single global template (as I understand)
Local<ObjectTemplate> global_templ = ObjectTemplate::New(isolate);

// create the function template and tell it the callback to use
Local<FunctionTemplate> point_constructor = FunctionTemplate::New(isolate, v8_Point);

// set the internal field count so our actual c++ object can tag along
//   with the javascript object so our accessors can use it
point_constructor->InstanceTemplate()->SetInternalFieldCount(1);

// associate getters and setters for the 'x' field on point
point_constructor->InstanceTemplate()->SetAccessor(String::NewFromUtf8(isolate, "x"), GetPointX, SetPointX);

... add any other function and object templates to the global template ...

// add the global template to the context our javascript will run in
Local<Context> x_context = Context::New(isolate, NULL, global_templ);

Then, for the actual function: 然后,对于实际功能:

void v8_Point(const v8::FunctionCallbackInfo<v8::Value>& args) {

    // (just an example of a handy utility function)
    // whether or not it was called as "new Point()" or just "Point()"
    printf("Is constructor call: %s\n", args.IsConstructCall()?"yes":"no");

    // create your c++ object that will follow the javascript object around 
    // make sure not to make it on the stack or it won't be around later when you need it
    Point * p = new Point();

    // another handy helper function example
    // see how the internal field count is what it was set to earlier
    //   in the InstanceTemplate
    printf("Internal field count: %d\n",args.This()->InternalFieldCount()); // this prints the value '1'

    // put the new Point object into the internal field
    args.This()->SetInternalField(0, External::New(args.GetIsolate(), p));

    // return the new object back to the javascript caller
    args.GetReturnValue().Set(args.This());
}

Now, when you write the getter and setter, you have access to your actual c++ object in the body of them: 现在,当您编写getter和setter时,您可以在它们的主体中访问实际的c ++对象:

void GetPointX(Local<String> property,
               const PropertyCallbackInfo<Value>& info) {
  Local<Object> self = info.Holder();

  // This is where we take the actual c++ object that was embedded
  //   into the javascript object and get it back to a useable c++ object
  Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
  void* ptr = wrap->Value();
  int value = static_cast<Point*>(ptr)->x_; //x_ is the name of the field in the c++ object

  // return the value back to javascript
  info.GetReturnValue().Set(value);
}

void SetPointX(Local<String> property, Local<Value> value,
               const PropertyCallbackInfo<void>& info) {
  Local<Object> self = info.Holder();

  // same concept here as in the "getter" above where you get access
  //   to the actual c++ object and then set the value from javascript
  //   into the actual c++ object field
  Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
  void* ptr = wrap->Value();
  static_cast<Point*>(ptr)->x_ = value->Int32Value();
}

Almost all of this came from here: https://developers.google.com/v8/embed?hl=en#accessing-dynamic-variables 几乎所有这些都来自这里: https : //developers.google.com/v8/embed?hl=zh-CN#accessing-dynamic-variables

except it doesn't talk about the proper way to make your objects in a repeatable fashion. 除了它没有讨论以可重复的方式制作对象的正确方法。

I figured out how to clean up the c++ object in the internal field, but I don't have time to put the whole answer here. 我想出了如何在内部字段中清理c ++对象的方法,但是我没有时间将整个答案放在这里。 You have to pass in a Global object into your weak callback by creating a hybrid field (a struct works well) on the heap that has both the global object and a pointer to your c++ object. 您必须通过在具有全局对象和指向您的c ++对象的指针的堆上创建一个混合字段(结构运行良好),将Global对象传递到弱回调中。 You can then delete your c++ object, call Reset() on your Global and then delete the whole thing. 然后,您可以删除c ++对象,在Global上调用Reset(),然后删除整个对象。 I'll try to add actual code, but may forget. 我将尝试添加实际的代码,但可能会忘记。

Here is a good source: https://code.google.com/p/chromium/codesearch#chromium/src/v8/src/d8.cc&l=1064 lines 1400-1441 are what you want. 这是一个很好的来源: https : //code.google.com/p/chromium/codesearch#chromium/src/v8/src/d8.cc&l=1064行是1400-1441。 (edit: line numbers seem to be wrong now - maybe the link above has changed?) (编辑:行号现在似乎是错误的-也许上面的链接已更改?)

Remember, v8 won't garbage collect small amounts of memory, so you may never see it. 请记住,v8不会垃圾收集少量内存,因此您可能永远也看不到它。 Also, just because your program ends doesn't mean the GC will run. 另外,仅因为您的程序结束并不意味着GC将运行。 You can use isolate->AdjustAmountOfExternalAllocatedMemory(length); 您可以使用isolate-> AdjustAmountOfExternalAllocatedMemory(length); to tell v8 about the size of the memory you've allocated (it includes this in its calculations about when there's too much memory in use and GC needs to run) and you can use isolate->IdleNotificationDeadline(1); 告诉v8您已分配的内存大小(它在有关何时使用过多内存和需要运行GC的计算中包括此大小),可以使用isolate->IdleNotificationDeadline(1); to give the GC a chance to run (though it may choose not to). 使GC有运行的机会(尽管它可能选择不运行)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM