Skip to content

Custom MFeatures

As shown in the Getting started section, a Machine is parametrized by MFeatures that define the core functionality of the Machine and the JavaScript runtime.

An MFeature is a class, that follows the stack inheritance and provides functionality to the Machine either by providing a public C++ API that can be used by other MFeatures or the user, or by adding functionality directly to the JavaScript runtime.

The MFeature must not interact with the JavaScript runtime in any way in its constructor, as it is not yet initialized at that point. Initialization of the MFeature involving the JavaScript runtime should be done in the initialize method.

Modifying the global object

Let's create an MFeature that adds a print function to the global object of the JavaScript runtime.

#include <jac/machine.h>
#include <jac/functionFactory.h>

template<class Next>
class PrintFeature : public Next {
public:
    std::ostream out;

    void print(std::string str) {
        out << str;
    }

    void initialize() {
        Next::initialize();

        jac::Value global = this->context().getGlobalObject();
        jac::FunctionFactory ff(this->context());

        global.defineProperty("print", ff.newFunction([this](std::string str) {
            this->print(str);
        }));
    }
};

The MFeature lets the user set the output stream of the print function by setting the out field. The initialize method then adds the print function to the global JavaScript object.

The MFeature can be used like this:

using Machine = PrintFeature<jac::MachineBase>;

Machine machine;
machine.print.out = std::cout;

machine.initialize();

machine.eval("print('Hello, world!');", "<eval>", jac::EvalFlags::Global);

Defining a JavaScript module

Often, an MFeatures will want to define a JavaScript module. This can be done by using the MachineBase::newModule method.

#include <jac/machine.h>
#include <jac/functionFactory.h>

template<class Next>
class PrintFeature : public Next {
public:
    std::ostream out;

    void print(std::string str) {
        out << str;
    }

    void initialize() {
        Next::initialize();

        jac::FunctionFactory ff(this->context());

        jac::Module& mdl = this->newModule("printer");
        mdl.addExport("print", ff.newFunction([this](std::string str) {
            this->print(str);
        }));
    }
};