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.
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);
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?
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++?
If the answer to question 1 is false, then why can't I use Set on X
?
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!
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.