~aperezdc/kiln

JavaScriptCore native module loader
8417f110 — Adrian Perez de Castro 3 years ago
Support GLib version older than 2.64
faa9ca31 — Adrian Perez de Castro 3 years ago
Allow customizing the WebExtension installation path
f83212a2 — Adrian Perez de Castro 3 years ago
Allow extras installation: shells, WebExtension

refs

master
browse  log 

clone

read-only
https://git.sr.ht/~aperezdc/kiln
read/write
git@git.sr.ht:~aperezdc/kiln

You can also use your local clone with git send-email.

#Kiln

Kiln is an utility library to ease the development of modules for JavaScriptCore (JSC) with a focus on providing mechanisms for embedders to dynamically load native modules which use the JSC GLib API to provide bindings.

Kiln can be used with the following JSC implementations:

  • The javascriptcoregtk-4.0 library provided by the WebKitGTK port.
  • The wpe-webkit-1.0 library provided by the WPE WebKit port.

A kiln is a large oven for making bricks. One could think about each module loaded using Kiln as a small brick used to make a complete building.

  1. Building
  2. Library Usage
    1. Built-in modules
    2. Plug-in modules
  3. Utilities
    1. Loader Module
    2. WebExtension
    3. JSC Shell
    4. GTK shell
  4. Licensing

#Building

Kiln uses Meson as its build system. The following options are available:

Option Description
-Dwebkit_port=gtk Use the WebKitGTK JSC library.
-Dwebkit_port=wpe Use WPE WebKit as the JSC library.
-Djsc_shell=… Whether to build the kn-jsc program, a JSC shell that can run JavaScript making use of Kiln plug-in modules, or run as an interactive interpreter. Needs libedit.
-Dgtk_shell=… Whether to build the kn-gtk program, a bare-bones application which embeds a web view and can make use of the Kiln WebExtension. This is only available when building with -Dwebkit_port=gtk and is mostly useful for development.
-Dweb_extension=… Whether to build the Kiln WebExtension.
-Ddocumentation=… Whether to build the Kiln manual using HotDoc.

#Library Usage

Once you have a JSCContext created (either manually, or by WebKit itself as part of a web view) a KilnLoader instance can be created to load modules for that context:

g_autoptr(JSCContext) context = jsc_context_new ();
g_autoptr(KilnLoader) loader = kiln_loader_new (context);

Modules can then be added to the loader in two ways: by instructing the loader to look for loadable shared libraries (plug-in modules) in the file system, or by manually specifying the module properties (name, initialization function, etc.).

#Built-in Modules

Manually specifying modules allows providing bindings for built-in functionality without the need for building and installing plug-ins. Built-in modules need to be registered with kiln_loader_add_module() before trying to open them. The following defines a module consisting of a single function to be exposed to JavaScript:

/* Implementation of the native function. */
static void
builtin_print (JSCValue *value)
{
    g_autofree char *value_string = jsc_value_to_string (value);
    printf ("%s\n", value_string);
}

/* Function that initializes (opens) the module. */
static JSCValue*
open_builtin_print (JSCContext *context, GError **error, void *userdata)
{
    /*
     * If the module cannot be initialized, return NULL and set the GError.
     * The "userdata" parameter will be specified when opening the module.
     * The returned value are the module contents.
     */
    return jsc_value_new_function (context,
                                   "print",
                                   G_CALLBACK (builtin_print),
                                   NULL,            /* userdata */
                                   NULL,            /* destroy_notify */
                                   G_TYPE_NONE,     /* return type */
                                   1,               /* num. of parameters */
                                   JSC_TYPE_VALUE); /* parameter type */
}

With the above in place, the module can be registered with the loader:

g_autoptr(GError) error = NULL;
if (!kiln_loader_add_module (loader,
                             "print",            /* module name */
                             open_builtin_print, /* module init function */
                             NULL,               /* userdata */
                             &error))
    g_error ("Cannot register module: %s", error->message);

Finally, the module can be “opened” to make the functionality available to the JavaScript world:

if (!kiln_loader_open_module (loader,
                              "print", /* module name */
                              &error))
    g_error ("Cannot open module: %s", error->message);

This last step takes the value returned from the module initialization function and assigns it to an attribute in the JS global object named after the module:

// Prints "number" using the "builtin_print()" C function.
print(typeof (1 + Number.parseInt("41")));

#Plug-in Modules

Plug-ins are loadable shared libraries (on ELF systems, a .so file) that contain a public function named kiln_<name>_module_open, where <name> is the module name that it implements, and the signature is as follows:

JSCValue* kiln_<name>_module_open (JSCContext*, GError**, void*);

This means that the open_builtin_print() function from the built-ins example could be moved into its own file and renamed as follows to make it into a loadable plug-in:

G_MODULE_EXPORT JSCValue*
kiln_print_module_open (JSCContext *context, GError **error, void *userdata)

This module must be built on its own and installed as lib<name>.so, in the case of the example print module:

$CC -shared -o libprint.so print.c $(pkg-config wpe-webkit-1.0 --libs --cflags)

Once the loadable plug-in module is built, the loader can be taught where to look for loadable modules:

/* Use the current directory for finding plug-in modules. */
kiln_loader_append_path (loader, ".");

/* Open it. This is exactly the same as for built-in modules. */
if (!kiln_loader_open_module (loader, "print", &error))
    g_error ("Cannot load module: %s", error->message);

Note that module names must be unique: if a built-in module was already added, a loadable one with the same name will not be used.

#Utilities

#Loader Module

Using the kiln_loader_bootstrap function will register the loader itself in the JSCContext as a module, making it possible to change the module search paths and load further modules from JavaScript. In the native side, the following will make the JavaScript module available under the kiln name:

kiln_loader_bootstrap (loader, "kiln", NULL);
kiln_loader_open_module (loader, "kiln", NULL);

Then, on the JavaScript side:

kiln.appendPath("path/to/modules/");
const console = kiln.loadModule("console");
console.log("This was loaded dynamically");

#WebExtension

Kiln provides a Web Extension which allows to load Kiln modules to be used by content loaded in a WebKitWebView. The built libkiln-webkit-*.so library must be installed in a location where your application is configured to look for WebExtensions.

The WebExtension is built when configuring the build with -Dweb_extension=enabled.

#JSC Shell

A simple JSC shell named kn-jsc is included with Kiln and built when configuring the build with -Djsc_shell=enabled. This shell can load Kiln plug-in modules, run JavaScript scripts in batch mode, and also be used as an interactive REPL.

The shell expects a console module to be available in the module path. The implementation can be found in the modules/ subdirectory and is built by default. Use kn-jsc --help for more information on how to specify module paths, or to disable this “built-in” (in the sense of always available).

#GTK Shell

The GTK shell is built when configuring the build with -Dgtk_shell=enabled and -Dwebkit_port=gtk. The program is called kn-gtk, and is most useful when the WebExtension is also built to make it able to load Kiln modules. The program consists of bare bones browser window which opens the URL specified in the command line. Check kn-gtk --help for more information on how to specify module paths and the location where the WebExtension is located.

#Licensing

Kiln is distributed under the terms of the MIT license.