-
Notifications
You must be signed in to change notification settings - Fork 0
Creating a Built in function in jsc
The JavaScriptCore is the WebKit's JS engine and usually it is shipped with the WebKit or as dynamic library. It also has an executable binary that is a REPL to run JS scripts.
In this tutorial, I will show how to change the JSC and create a build-in native function that returns the number 32. It's a simple task, but useful if you want to start hack into JSC.
This file is the entry point of jsc REPL. You can find it at /Soruce/JavaScriptCore/jsc.cpp. It's the place where we will create or native function.
In JS, all clients (e.g. nodejs, browsers, etc) define a Global Object, because it's required by the language Specification (TODO: point to spec). It's not different in jsc REPL and we have a GlobalObject definition. You can find it here. All built-in functions in JS such as print, console.log and even window, in case of browsers, are actually property of GlobalObject. Given that information, if we want to expose a function to jsc users, we need to create a property and configure it properly to be a JS function that calls native C++ code. The JSC developers uses this to create test scripts that are strongly related with VM implementation aspects, like GC and JIT.
We can start do that going to GlobalObject::finishCreation. Here you can see a huge list of native functions that are available into jsc environment. For our luck, we are going to use the same infrastructure. Out first step is to add our function using jsc addFunction
. Just after Base::finishCreation(vm);
put the following line of code
addFunction(vm, "myHelloFunc", functionMyHelloFunc, 0);
The first argument is the vm instance and it's used to control vm related issues like Exception throwing or Stack access. We aren't going to see it in details in this tutorial. The second argument is a string that represents what is the property name into jsc REPL environment. The third argument is a function pointer to the native function that will be called when JS client code invokes myHelloFunc()
and the last argument is argument count for our function.
By now, the code isn't compilable, because we didn't define functionMyHelloFunc
yet. We can place it in any place in jsc.cpp, but let's follow JSC pattern. You can put it just before functionDebug
here. so you put the following code:
EncodedJSValue JSC_HOST_CALL functionMyHelloFunc(ExecState*)
{
return JSValue::encode(JSValue(32));
}
Ok, this code deserves a little explanation. As this functions is going to be called from JavaScript client, we can't simply return 32;
, because C types and JavaScript Types are totally different. As we want a function that returns 32 to JS context, we need to encode this value in a way that its context will be able to interpret. To do that, we need to use JSValue
.
In JSC, all values are encoded in 8 bytes, where 4 bytes are the payload and 4 bytes are the tag. Probably you already know that, but JS is a dynamically typed language it stores the the type of a variable in runtime. This way, every variable needs to store information of it's type somewhere and in JSC it's done into tag part of a variable. The payload then contains the value of variable. The JSValue
is the internal JSC class that encapsulate the JavaScript values interface to C++ code. So we then create a JSValue(32)
that will represent 32 in JS context and then encode it to send to JS caller.
To finish our functionMyHelloFunc
implementation, we just need to it's signature in properly place. We can go to (functionCreateProxy)[https://github.com/caiolima/webkit/blob/master/Source/JavaScriptCore/jsc.cpp#L998] definition and add:
static EncodedJSValue JSC_HOST_CALL functionMyHelloFunc(ExecState*);
Now we should be able to compile jsc.
To run our example, we can run jsc (if you download it from WebKit repo, you can use Tools/Scripts/run-jsc) and then call your function.
>>> myHelloFunc();
32
>>>
Voyla!
Now, you are able to create new native functions that will be accessible from JS Context.