Skip to content
Craig Minihan edited this page Dec 7, 2016 · 25 revisions

rs::jsapi::Object is an implementation of a static object. Object should be used when the fields are known in advance. In this example the object has one field: 'the_answer'.

#include <iostream>
#include "libjsapi.h"

int main() {
    rs::jsapi::Context cx;

    // create an object with a single field 'the_answer' with a getter callback which always returns 42
    rs::jsapi::Value obj(cx);
    rs::jsapi::Object::Create(cx, { "the_answer" },
        [](const char* name, rs::jsapi::Value& value) { value = 42; },
        nullptr,
        {},
        nullptr,
        obj);

    // create a function which returns the value of the field 'the_answer' on the passed object
    cx.Evaluate("var myfunc=function(o){return o.the_answer;};");

    rs::jsapi::FunctionArguments args(cx);
    args.Append(obj);

    // invoke the function and get the result
    rs::jsapi::Value result(cx);
    cx.Call("myfunc", args, result);

    // output the result to the console
    std::cout << result << std::endl;
}

The getter lambda takes the parameters: const char* name, Value& value where name is the name of the field being referenced from JavaScript and the value parameter takes the return value (the field value). In this example we only have one field so we always return 42.

If we were to introduce a std::unordered_map<std::string, int> called data into the scope of main in the example then we could rewrite the lambda as:

std::unordered_map<std::string, int> data = { /* your data here */ };

rs::jsapi::Object::Create(
  /* ... */
  [&](const char* name, rs::jsapi::Value& value) {
      value = data[name];
  }
  /* ... */
}

Refer to the DynamicObject example to see how to use a map to resolve field values.

Object Lifetime

Once objects are exposed to JavaScript you must ensure that they are always valid for the lifetime of the object. In the example above the scope of the main function controls the lifetime of the SpiderMonkey runtime and default context; when they are destructed by the C++ runtime the JavaScript VM will be closed causing them to be collected.

In long running applications where you use closures you will most likely need to link the object lifespan to the SpiderMonkey garbage collector. The Create() factory function takes a finalizer callback parameter which is invoked when the object has been collected by the JavaScript GC. If the unordered_map in the example above was instead an unordered_map* then we could implement the finalizer callback as a lambda taking care to use a copy closure for data:

auto data = new std::unordered_map<std::string, int> { /* your data here */ };

rs::jsapi::Object::Create(
  /* ... */
  [=]() { delete data; data = nullptr; }
  /* ... */
}

Alternately the finalizer could be used to release a std::shared_ptr reference thereby allowing a wrapped C++ object to destruct after SpiderMonkey has finished with it.