Search code examples
c++v8

v8::ObjectTemplate::SetAccessor and v8::Template::Set - Difference


I'm confused by the difference between V8 ObjectTemplate's Set and SetAccessor methods. My question has a simple context and 4 concrete sub-questions.

Context

Let's say I have code snippet that wants to provide a global object global to the targeting JS context. global has a property x, whose value takes int value. global also has a property log, which is a function. All the snippets are taken from V8's source, process.cc and Embedder's Guide to be precise.

HandleScope handle_scope(GetIsolate());    

// Create a template for the global object where we set the
// built-in global functions.
Local<ObjectTemplate> global = ObjectTemplate::New(GetIsolate());
global->Set(String::NewFromUtf8(GetIsolate(), "log", 
          NewStringType::kNormal).ToLocalChecked(),
          FunctionTemplate::New(GetIsolate(), LogCallback));

So this code snippet provides function log to the global. Then from the Embedder's Guide to accessors, it says

An accessor is a C++ callback that calculates and returns a value when an object property is accessed by a JavaScript script. Accessors are configured through an object template, using the SetAccessor method.

The code snippet follows:

void XGetter(Local<String> property,
          const PropertyCallbackInfo<Value>& info) {
      info.GetReturnValue().Set(x);
}

void XSetter(Local<String> property, Local<Value> value,
         const PropertyCallbackInfo<Value>& info) {
      x = value->Int32Value();
}

// YGetter/YSetter are so similar they are omitted for brevity

Local<ObjectTemplate> global_templ = ObjectTemplate::New(isolate);
global_templ->SetAccessor(String::NewFromUtf8(isolate, "x"), XGetter, XSetter);
global_templ->SetAccessor(String::NewFromUtf8(isolate, "y"), YGetter, YSetter);
Persistent<Context> context = Context::New(isolate, NULL, global_templ);

As I understand this code snippet, it's providing some integer value x to the global as the description goes.

Now,from the source of V8, I see ObjectTemplate doesn't have a Set method, instead, it's inherited from parent class Template. From Template's source code, it says:

/**
 * Adds a property to each instance created by this template.
 *
 * The property must be defined either as a primitive value, or a template.
 */
 void Set(Local<Name> name, Local<Data> value,
         propertyAttribute attributes = None);

Questions

  1. Template's Set method says it can set a primitive value to the instance of the template, then can I use Set to set x in the second code snippet instead of using SetAccessor?

  2. If the answer to question 1 is true, then what's the difference for setting x between using SetMethod and Set? Is the difference being that any modification in JS to the property set by Set will not be reflected in C++?

  3. If the answer to question 1 is false, then why can't I use Set on X?

  4. From the description of accessors, it says it computes and return value. So does it mean we don't use SetAccessor to return functions? I'm confused because I mainly write JS and Haskell. Both languages spoils me to take functions as values.

Now I know it should be easy to verify all my assumptions by actually building the samples, but I have difficulties compiling the V8 source, hence I'm asking for any help.

Thank you in advanced for any effort!


Solution

  • 1. Yes.

    2. Set is the C++ equivalent (modulo property attributes) of:

    Object.defineProperty(global, "x", {value: 3})
    

    SetAccessor is the C++ equivalent of:

    Object.defineProperty(global, "x", {get: function XGetter() { return ...; }, 
                                        set: function XSetter(val) { ... }});
    

    As you suggest, a consequence is that in case of Set, the C++ side has no way of knowing whether the value was changed from the JavaScript side.

    3. n/a

    4. The getter can return any value you want; in particular the value can be a function.