Skip to content

Example: DynamicObject

Craig Minihan edited this page Dec 7, 2016 · 14 revisions

DynamicObject implements a truly dynamic object instance which you can expose to JavaScript. The properties of the object do not need to be known in advance. For example:

   alert(msg.text);

If the msg object was a libjsapi DynamicObject then the reference to the text field would be routed to the GetCallback supplied in the object Create() call. In the get call you can return any value you like to SpiderMonkey including undefined, null or the classic "hello world".

Because the fields are not known in advance DynamicObject exposes an EnumeratorCallback parameter which you should implement if you expect your JavaScript code to enumerate your objects with a for (prop in obj).

The following example demonstrates field values being resolved by a std::unordered_map, any fields not found return undefined as you would expect from a real JavaScript object.

#include <iostream>
#include <vector>
#include <unordered_map>

#include "libjsapi.h"

int main() {
    std::unordered_map<std::string, std::string> data = {
        { "hello", "world" }, { "lorem", "ipsum" },
        { "foo", "bar" } };

    rs::jsapi::Context cx;

    // create a dynamic object which returns a string from the map
    rs::jsapi::Value obj(cx);
    rs::jsapi::DynamicObject::Create(cx,
        [&](const char* name, rs::jsapi::Value& value) {
            auto result = data.find(name);
            if (result != data.cend()) {
                value = result->second;
            } else {
                value.setUndefined();
            }
        },
        nullptr,
        nullptr,
        nullptr,
        obj);

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

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

    // invoke the function and get the value of the field 'hello'
    rs::jsapi::Value result(cx);
    cx.Call("myfunc", args, result);
    std::cout << result << std::endl;

    // invoke the function and get the value of the field 'lorem'
    args[1] = rs::jsapi::Value(cx, "lorem");
    cx.Call("myfunc", args, result);
    std::cout << result << std::endl;

    // invoke the function and get the value of the field 'foo'
    args[1] = rs::jsapi::Value(cx, "foo");
    cx.Call("myfunc", args, result);
    std::cout << result << std::endl;

    // invoke the function and get the value of the field 'xyz'
    args[1] = rs::jsapi::Value(cx, "xyz");
    cx.Call("myfunc", args, result);
    std::cout << result << std::endl;
}

Running the example yields the following output on the console:

world
ipsum
bar
undefined

Once exposed to JavaScript the lifetime of your DynamicObject instance is controlled by the SpiderMonkey garbage collector. See the Object example for details on how to implement a finalizer for your objects and arrays.