Hi, I'm sorry if this has been said before or if this is not the place to in fact say it, I'm not too much of a github guy.
So, there are some short but useful examples in the overview, but this is my first time doing stuff with interpreters, and I've found out I'm not as comfortable with javascript as I thought. All I'm trying to do is an interface for my c++ stuff. Allow anyone to be able to put together a script real quick and have a decent personalized tool. Use it as a console also.
I've gotten "far", I've only been at it for 2 days, but from what I can see documentation is scarce, I'm using the JRST reference, but I keep finding that some functions are missing, such as JsCreateExternalObjectWithPrototype or JsObjectDefineProperty.
Googling stuff yeilds no results (not that I've ever been brilliant at googling), and right now I've put together all my objects and their members, but I can't seem to get getters to work. I'm looking to do it with c++ natives, as I have been doing with most the other stuff.
I know it likely has something to do with JsDefineProperty, but I really have no clue on how to use it, and after 6 hours I've made no progress.
So I guess my question is how do you set a native getter, and where else should I ask these noob questions?
Thank you.
(Note I'm an external contributor and a user of CC, I'm not a member of the team here)
Firstly: the wiki documentation is basically the comments from ChakraCore.h and ChakraCommon.h but in a slightly nicer format so generally any missing bits can be found in those headers.
As for how to do getters:
JsObjectDefineProperty is probably the easiest option here (it's the same as JsDefineProperty but takes a JsValueRef for the propertyID instead of a JsPropertyId so one less step of setup).
JsObjectDefineProperty is basically a native version of Object.defineProperty from within JS.
JsObjectDefineProperty(
_In_ JsValueRef object,
_In_ JsValueRef key,
_In_ JsValueRef propertyDescriptor,
_Out_ bool *result);
So the parameters are:
object - a JsValueRef for the object you want to set your getter on
key - a JsValueRef for the property key (e.g. a JavaScriptString or Number or the like)
propertyDescriptor - a JsValueRef for an object that is the property descriptor, property descriptors within JS look like either:
//for an accessor property:
const accessorDescriptor = {
enumerable : true,//or false defaults to false if not provided - should this property be enumerated by e.g. for...in loops in JS
configurable : true, //or false - defaults to false - can this property be deleted/replaced
get : getMethod, //defaults to undefined - getter method
set : setMethod, //defaults to undefined - setter method
}
//or for a data property
const dataDescriptor = {
enumerable : true,//or false defaults to false if not provided - should this property be enumerated by e.g. for...in loops in JS
configurable : true, //or false - defaults to false - can this property be deleted/replaced
writeable : true, // or false - defaults to false - can this property be assigned to
values : someValue // defaults to undefined
}
So putting it together to make a property that is a read only getter from native you would:
Putting it together a C function to add a getter (assuming you already have your Object made) would look something like this (note the below is not tested but should give the idea):
void AddGetter(JsValueRef object, const char* propertyName, JsNativeFunction callback, void *callbackState)
{
JsValueRef getter;
JsValueRef descriptor;
JsValueRef get;
JsValueRef key;
bool result;
JsCreateObject(&descriptor); // step 2
JsCreateFunction(callback, callbackState, &getter); // step 3
JsCreateString("get", 3, &get); // step 4
JsObjectSetProperty(descriptor, get, getter, true); // step 5
JsCreateString(propertyName, strlen(propertyName), &key); // step 6
JsObjectDefineProperty(object, key, descriptor, &result); // step 7
if (!result) { /* it failed? consider if this is possible in your programme if yes handle it`*/}
}
EDIT: corrected mistake in example
@rhuanjl Thank you so much.
Though you made a mistake on your code at step 5, object is the parent, you'd wanna set the getter on the descriptor, then the descriptor to the parent's key-"represented" object.
Still took me like 20 minutes to figure it out :P Wasn't until I went looking for how the deprecated __defineGetter__ did it that I managed to figure out that "key" was actually what you set the descriptor of. So if you had an object in the main scope you'd wanna do AddGetter(globalObject, "NameOfTheObject", getter, cbState);.
There's also another issue. The this pointer (arguments[0]) on the get native, points to the object used to set the getter on it's property. So in the above example, arguments[0] == globalObject, is there maybe some way to have it be the "NameOfObject" object? I've managed to get around the issue by using callbackstate, but that's kinda dirty.
I guess it kinda makes sense since you might want to use something from the parent object, but it becomes an inconvenience when you use the same native for multiple objects.
Anyhow here's it working:

The getter for PEH reads the pe header from the process if it hadn't already.
Side notes for anyone going through the same process:
Getters get stuff, so if you return say a number from the getter, but your code is objectxyz.toString(); the toString will be called on the JsNumber you returned, and not your object, in fact, no function will be called on your object because the getter has precedence.
For this purpose, valueOf is the way to go, since js will call it by default on stuff like arithmetic, and you can still call whatever you want on your other member functions.
In my code, the code that prints the result automatically tries to convert to string, which is why you see a 0x000004550, it calls the toString method of that object, but in the second, it calls valueOf for arithmetic, adds a zero to it, and a JsNumber is printed. Had my getter returned a JsNumber, both would return JsNumber 7.
Again, thank you so much (should I close this?).
With the code that @rhuanjl provided above, the native call AddGetter(jsObj, "name", func, data) is roughly equivalent to the javascript Object.defineProperty(jsObj, "name", {get: func }) where func is also passed data as well. That will result in jsObj.name invoking the func getter when you try to read from it in script.
We have a couple of ways that native functions can be called from script, depending on how you define them. In the code above, it uses JsCreateFunction which expects a JsNativeFunction passed in, which should accept parameters JsValueRef callee, bool isConstructCall, JsValueRef * arguments, unsigned short argumentCount, void *callbackState. For this kind of function, callee is a reference to the javascript function object that was invoked, isConstructCall is whether this is being new'd, and the arguments are the arguments of the function where arguments[0] corresponds to the this of the function; if you called jsObj.name then the getter will be invoked with jsObj as the this.
We also have a newer API where you instead call JsCreateEnhancedFunction and pass in a JsEnhancedNativeFunction which accepts different arguments. The primary difference is that it also takes a JsNativeFunctionInfo which explicitly indicates the this argument and a new.target argument if it is a construct call.
To return to your question, what exactly do you mean by the NameOfObject object? When you define a property, the property itself isn't an object. In the example I give above, there is no jsObj.name "object", although jsObj.name may return an object if that is what the getter chooses to do. If you want your getter to always operate on a particular object, you could use the callback state for that purpose: When creating the getter, you could also create a new object, JsAddRef it since we are about to store a pointer to it in a place that isn't tracked by the GC, and then pass that pointer as the callback state. Then in your getter, the callback state will always be that same pointer, so you can operate on the object that you created up front.
@RealTrisT sorry for the error in the example (I've updated it in case anyone else reads it)
@liminzhu can you double check the wiki for any documentation present in the source but omitted from the wiki?
@MSLaguana I am JsSetPropertying what would be an external object name to jsObj (which would make it a property that is also an object), and then basically doing addgetter, and passing to it as a key the string "name", with the external object as the callbackstate.
So what I meant with NameOfObject was what is effectively name.
Which seems to work because then the getter gets called, and if I return said external object I can call it's member methods.
Though it would also make a lot of sense if addgetter was overriding said external object, because then I would still be returning it on the getter from callbackstate, yielding the same result.
Then said external object would have no references to it in which case JsAddRef would I guess, in fact be the sensible thing to do (I haven't messed with garbage collection yet).
My main issue with the getters having jsObj as the this was that, if for example I had a c++ class with 3 DWORDs, I'd have to get a different getter for each, though now that I think about it, I could just pass their pointers to callbackstate, or if the object itself was a pointer, I guess I could do some offsetof magic.
Sorry for all the speculation, I'm not home atm, though I am going to try all this as soon as I am.
Edit:

So it does in fact override. Guess I'll have to rethink :D
@dilijev there are quite a few actually. I guess we should've added other docs as we put APIs on the header.
When I get some time, I will write a sync tool between wiki and header (I know I've been saying this for a while...)
Just gonna leave this here:
JsRelease has "The object to add a reference to." as a description for the first parameter in both the header and the reference.
I've opened a PR to add the missing APIs to the wiki.
I've also opened a PR to fix a few typos in the headers including the JsRelease one mentioned by RealTristT above.
I think this was answered/completed above. Further documentation review can be considered later.
Most helpful comment
(Note I'm an external contributor and a user of CC, I'm not a member of the team here)
Firstly: the wiki documentation is basically the comments from ChakraCore.h and ChakraCommon.h but in a slightly nicer format so generally any missing bits can be found in those headers.
As for how to do getters:
JsObjectDefineProperty is probably the easiest option here (it's the same as JsDefineProperty but takes a JsValueRef for the propertyID instead of a JsPropertyId so one less step of setup).
JsObjectDefineProperty is basically a native version of Object.defineProperty from within JS.
So the parameters are:
object - a JsValueRef for the object you want to set your getter on
key - a JsValueRef for the property key (e.g. a JavaScriptString or Number or the like)
propertyDescriptor - a JsValueRef for an object that is the property descriptor, property descriptors within JS look like either:
So putting it together to make a property that is a read only getter from native you would:
Putting it together a C function to add a getter (assuming you already have your Object made) would look something like this (note the below is not tested but should give the idea):
EDIT: corrected mistake in example