0

What I want to do is call already compiled functions in JS/v8 from c++. I'm doing this for a game engine I'm writing that uses V8 as the scripting back-end.

This is kinda how a script would be formatted for my engine:

function init(){ //this gets called at the startup of the game
    print("wambo");
}

var time = 0;
function tick(delta){ //this gets called every frame
    time += delta;
    print("pop");
}

I've tried looking though this compiled documentation https://v8docs.nodesource.com/node-16.13/df/d69/classv8_1_1_context.html for a function in v8::Local<v8::Context>->Global that Get functions by name, from the compiled JS, but could not wrap my head around the keys system.

I've tried reading through Calling a v8 javascript function from c++ with an argument but the examples seem outdated for the latest version of v8 in 2022.

4
  • What do you mean with "compiled JS". JavaScript is an interpreted scripting language, it doesn't get compiled. Commented Mar 7, 2022 at 9:31
  • well from what I can tell v8 makes a tree of functions and variables, and the function that does this is called compile. which is why I said "compiled JS". I really just ment is v8::Local<v8::Context>->Global, which is the tree. Commented Mar 7, 2022 at 9:41
  • What exactly doesn't work in the 2019 example with your version of V8? V8 hasn't changed much since 2019? Were you able to load the V8 engine and compile the script? Ie get a v8::Script reference? Commented Mar 7, 2022 at 10:53
  • For the most part, I wasn't able to get a v8::string into Context->Global->Get; also I couldn't find any documentation that explained what arguments v8::function->call needed and what they did. Commented Mar 7, 2022 at 16:34

1 Answer 1

7

I did port the sample from the other answer to the latest V8 version - 10.1.72

// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "libplatform/libplatform.h"
#include "v8.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const char jsCode[] = "var foo = function(arg) {"
                      "if (Math.random() > 0.5) throw new Error('er');"
                      "return arg + ' with foo from JS';"
                      "}";

int main(int argc, char *argv[]) {
    // Initialize V8.
    v8::V8::InitializeICUDefaultLocation(argv[0]);
    v8::V8::InitializeExternalStartupData(argv[0]);
    std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
    v8::V8::InitializePlatform(platform.get());
    v8::V8::Initialize();

    // Create a new Isolate and make it the current one.
    v8::Isolate::CreateParams create_params;
    create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
    v8::Isolate *isolate = v8::Isolate::New(create_params);
    {
        v8::Isolate::Scope isolate_scope(isolate);
        v8::HandleScope handle_scope(isolate);

        // Create a context and load the JS code into V8
        v8::Local<v8::Context> context = v8::Context::New(isolate);
        v8::Context::Scope context_scope(context);
        v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate, jsCode, v8::NewStringType::kNormal).ToLocalChecked();
        v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked();
        v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
        v8::String::Utf8Value utf8(isolate, result);

        // This is the result of the evaluation of the code (probably undefined)
        printf("%s\n", *utf8);

        // Take a reference to the created JS function and call it with a single string argument
        v8::Local<v8::Value> foo_value = context->Global()->Get(context, v8::String::NewFromUtf8(isolate, "foo").ToLocalChecked()).ToLocalChecked();
        if (foo_value->IsFunction()) {
            // argument (string)
            v8::Local<v8::Value> foo_arg = v8::String::NewFromUtf8(isolate, "arg from C++").ToLocalChecked();

            {
                // Method 1
                v8::TryCatch trycatch(isolate);
                v8::MaybeLocal<v8::Value> foo_ret = foo_value.As<v8::Object>()->CallAsFunction(context, context->Global(), 1, &foo_arg);
                if (!foo_ret.IsEmpty()) {
                    v8::String::Utf8Value utf8Value(isolate, foo_ret.ToLocalChecked());
                    std::cout << "CallAsFunction result: " << *utf8Value << std::endl;
                } else {
                    v8::String::Utf8Value utf8Value(isolate, trycatch.Message()->Get());
                    std::cout << "CallAsFunction didn't return a value, exception: " << *utf8Value << std::endl;
                }
            }

            {
                // Method 2
                v8::TryCatch trycatch(isolate);
                v8::Local<v8::Object> foo_object = foo_value.As<v8::Object>();
                v8::MaybeLocal<v8::Value> foo_result = v8::Function::Cast(*foo_object)->Call(context, context->Global(), 1, &foo_arg);
                if (!foo_result.IsEmpty()) {
                    std::cout << "Call result: " << *(v8::String::Utf8Value(isolate, foo_result.ToLocalChecked())) << std::endl;
                } else {
                    v8::String::Utf8Value utf8Value(isolate, trycatch.Message()->Get());
                    std::cout << "CallAsFunction didn't return a value, exception: " << *utf8Value << std::endl;
                }
            }
        } else {
            std::cerr << "foo is not a function" << std::endl;
        }
    }

    // Dispose the isolate and tear down V8.
    isolate->Dispose();
    v8::V8::Dispose();
    v8::V8::ShutdownPlatform();
    delete create_params.array_buffer_allocator;
    return 0;
}
Sign up to request clarification or add additional context in comments.

1 Comment

Nice! If you want to improve the sample code, replace most of the ToLocalChecked() with proper error handling.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.