lightpanda-io / zig-js-runtime

Add a JS runtime in your Zig project
https://lightpanda.io
Apache License 2.0
16 stars 0 forks source link
javascript-runtime v8 zig

zig-js-runtime

A fast and easy library to add a Javascript runtime into your Zig project.

With this library you can:

Features:

Currently only v8 is supported as a Javascript engine, but other engines might be added in the future.

This library is fully single-threaded to matches the nature of Javascript and avoid any cost of context switching for the Javascript engine.

Rationale

Integrate a Javascript engine into a Zig project is not just embeding an external library and making language bindings. You need to handle other stuffs:

This library takes care of all this, with no overhead thanks to Zig awesome compile time capabilities.

Getting started

In your Zig project, let's say you have this basic struct that you want to expose in Javascript:

const Person = struct {
    first_name: []u8,
    last_name: []u8,
    age: u32,

    // Constructor
    // if there is no 'constructor' defined 'new Person()' will raise a TypeError in JS
    pub fn constructor(first_name: []u8, last_name: []u8, age: u32) Person {
        return .{
            .first_name = first_name,
            .last_name = last_name,
            .age = age,
        };
    }

    // Getter, 'get_<field_name>'
    pub fn get_age(self: Person) u32 {
        return self.age;
    }

    // Setter, 'set_<field_name>'
    pub fn set_age(self: *Person, age: u32) void {
        self.age = age;
    }

    // Method, '_<method_name>'
    pub fn _lastName(self: Person) []u8 {
        return self.last_name;
    }
};

You can generate the corresponding Javascript functions at comptime with:

const jsruntime = @import("jsruntime");
pub const Types = jsruntime.reflect(.{Person});

And then use it in a Javascript script:

// Creating a new instance of Person
let p = new Person('John', 'Doe', 40);

// Getter
p.age; // => 40

// Setter
p.age = 41;
p.age; // => 41

// Method
p.lastName(); // => 'Doe'

Let's add some inheritance (ie. prototype chain):

const User = struct {
    proto: Person,
    role: u8,

    pub const prototype = *Person;

    pub fn constructor(first_name: []u8, last_name: []u8, age: u32, role: u8) User {
        const proto = Person.constructor(first_name, last_name, age);
        return .{ .proto = proto, .role = role };
    }

    pub fn get_role(self: User) u8 {
        return self.role;
    }
};

pub const Types = jsruntime.reflect(.{Person, User});

And use it in a Javascript script:

// Creating a new instance of User
let u = new User('Jane', 'Smith', 35, 1); // eg. 1 for admin

// we can use the User getters/setters/methods
u.role; // => 1

// but also the Person getters/setters/methods
u.age; // => 35
u.age = 36;
u.age; // => 36
u.lastName(); // => 'Smith'

// checking the prototype chain
u instanceof User == true;
u instanceof Person == true;
User.prototype.__proto__ === Person.prototype;

Javascript shell

A Javascript shell is provided as an example in src/main_shell.zig.

$ make shell

zig-js-runtime - Javascript Shell
exit with Ctrl+D or "exit"

>

Build

Prerequisites

zig-js-runtime is written with Zig 0.13.0. You have to install it with the right version in order to build the project.

To be able to build the v8 engine, you have to install some libs:

For Debian/Ubuntu based Linux:

sudo apt install xz-utils \
    python3 ca-certificates git \
    pkg-config libglib2.0-dev clang

For MacOS, you only need Python 3.

Install and build dependencies

The project uses git submodule for dependencies. The make install-submodule will init and update the submodules in the vendor/ directory.

make install-submodule

Build v8

The command make install-v8-dev uses zig-v8 dependency to build v8 engine lib. Be aware the build task is very long and cpu consuming.

Build v8 engine for debug/dev version, it creates vendor/v8/$ARCH/debug/libc_v8.a file.

make install-v8-dev

You should also build a release vesion of v8 with:

make install-v8

All in one build

You can run make install and make install-dev to install deps all in one.

Development

Some Javascript features are not supported yet:

Test

You can test the zig-js-runtime library by running make test.

Credits