Search code examples
c++v8embedded-v8

"require is not defined" error in JavaScript V8 C++ application


I have the following code that uses JavaScript V8 C++ to run a snipped of JavaScript code which uses require statement (that code is hold in the js_code variable).

// compile: g++ -std=c++14 simplest.cpp -g -o simplest -I/usr/include/v8 -L/path/to/v8/lib -lv8 -lv8_libplatform -pthread

#include <iostream>
#include <libplatform/libplatform.h>
#include <v8.h>

using namespace v8;

char const* js_code = " \
const { Jexl } = require('jexl'); \
const jexl = new Jexl(); \
const context = { \
  x: 5, \
  y: 10 \
}; \
const expression = 'x * y + 2'; \
jexl.eval(expression, context) \
  .then(result => { \
    console.log('Result:', result); \
  }) \
  .catch(error => { \
    console.error('Error:', error); \
  }); \
";

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

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

        // Create a new context.
        Local<Context> context = Context::New(isolate);

        // Enter the created context for compiling and
        // running the JavaScript code.
        Context::Scope context_scope(context);

        // Create a string containing the JavaScript source code.
        Local<String> source = String::NewFromUtf8(
            isolate, js_code,
            NewStringType::kNormal).ToLocalChecked();

        // Compile the source code.
        Local<Script> script = Script::Compile(context, source).ToLocalChecked();

        // Run the script to get the result.
        Local<Value> result = script->Run(context).ToLocalChecked();

        // Convert the result to a string and print it.
        String::Utf8Value utf8(isolate, result);
        printf("%s\n", *utf8);
    }

    // Dispose the isolate and tear down V8.
    isolate->Dispose();
    V8::Dispose();
    V8::ShutdownPlatform();
    return 0;
}

However, when I run the program I get this error:

<unknown>:18: Uncaught ReferenceError: require is not defined

#
# Fatal error in v8::ToLocalChecked
# Empty MaybeLocal.
#

I have checked that if I run the same JavaScript code in node it works as expected:

// calculate_expression.js file
const { Jexl } = require("jexl");
const jexl = new Jexl();
const context = {
  x: 5,
  y: 10
};
const expression = "x * y + 2";
jexl.eval(expression, context)
  .then(result => {
    console.log("Result:", result);
  })
  .catch(error => {
    console.error("Error:", error);
  });

Run:

$ node calculate_expression.js
Result: 52

I guess that the problem is due to for some reason the require statement (available in node context) is not available in my V8 C++ execution context, but I don't know how to solve the problem...

Some more information about my environment, in the case it helps:

  • Operating system: Debian 12.4
  • libnode-dev package version: 18.19.0+dfsg-6~deb12u1
  • node version: v18.19.0

Thanks in advance!


Solution

  • (V8 developer here.)

    This is expected: require is provided by the embedder, not by V8. In order to use it, you have to implement it yourself (possibly by importing some existing implementation into your C++ project).

    For comparison and/or inspiration, in V8's d8 shell, we have a d8.file.execute(...) function that takes a file name as a parameter. That's not as convenient/feature-rich as require, but good enough for our unit testing needs.