Hello World from Javascript Core Link to heading

Continuing from my last article, today we will embed Javascript code into C++ using Apple’s Javascript Core engine that powers Safari…


Hello World from Javascript Core

Continuing from my last article, today we will embed Javascript code into C++ using Apple’s Javascript Core engine that powers Safari browser. Apple’s developer environment is unique in that they promote Objective-C and Swift, rather than conventional C or C++. The official documentation on Javascript Core is on Objective-C and Swift only, so it is not easy to find resources on how to use it on C++. Hopefully, this article will help you get started with Javascript Core on C++.

The first thing we need is Javascript Core library.

# macOS is already shipped with it
# just install command line dev tool if not already installed
xcode-select --install

# for Fedora
sudo dnf install -y webkit2gtk4.1-devel

Now, let’s write hello-world.cc file below

#include <JavaScriptCore/JavaScript.h>
#include <iostream>
#include <string>

// Function to execute JS code and return result as string
std::string execute_js_code(JSContextRef context, const std::string &code)
{
    JSStringRef js_code = JSStringCreateWithUTF8CString(code.c_str());
    JSValueRef result = JSEvaluateScript(context, js_code, nullptr, nullptr, 1, nullptr);

    // Convert result to string
    JSStringRef js_result_string = JSValueToStringCopy(context, result, nullptr);
    size_t result_length = JSStringGetMaximumUTF8CStringSize(js_result_string);
    char *result_cstr = new char[result_length];
    JSStringGetUTF8CString(js_result_string, result_cstr, result_length);
    std::string result_str(result_cstr);

    // Clean up
    delete[] result_cstr;
    JSStringRelease(js_result_string);
    JSStringRelease(js_code);

    return result_str;
}

// Function to create and invoke an "add" function in JS
double call_add_function(JSContextRef context)
{
    const char *js_code = R"(
        function add(a, b) {
            return a + b;
        }
        add(5, 7);
    )";

    JSStringRef js_code_string = JSStringCreateWithUTF8CString(js_code);
    JSEvaluateScript(context, js_code_string, nullptr, nullptr, 1, nullptr);
    JSStringRelease(js_code_string);

    // Retrieve the result of the add function
    JSValueRef result = JSEvaluateScript(context, JSStringCreateWithUTF8CString("add(5, 7);"), nullptr, nullptr, 1, nullptr);
    double result_value = JSValueToNumber(context, result, nullptr);

    return result_value;
}

int main()
{
    // Initialize JS context
    JSGlobalContextRef context = JSGlobalContextCreate(nullptr);

    // Execute JS code: 'hello' + ', world'
    std::string result = execute_js_code(context, "'hello' + ', world'");
    std::cout << "Result of JS code execution: " << result << std::endl;

    // Call add function in JS and get result
    double sum = call_add_function(context);
    std::cout << "Result of JS add function: " << sum << std::endl;

    // Clean up
    JSGlobalContextRelease(context);

    return 0;
}

Finally, let’s compile

# macOS
g++ -std=c++17 -framework JavaScriptCore hello-world.cc -o hello-world
# Fedora
g++ -std=c++17 $(pkg-config --cflags --libs javascriptcoregtk-4.1) hello-world.cc -o hello-world

# run!
./hello-world

Upon success, it should print out

Result of JS code execution: hello, world
Result of JS add function: 12