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.
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;
}