Search code examples
arraysduktape

Duktape/C array element accessors


I am trying to implement something like a HTMLCollection which is an array that can lose/gain elements without JS action.

duk_push_object(ctx);
duk_push_string(ctx, "length");
duk_push_c_function(ctx, my_length_getter, 1);
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
duk_push_c_function(ctx, my_item_getter, 1);
duk_put_prop_string(ctx, -2, "item");

Doing it like above I get an object on which I can read the my_array.length property and get an item by calling method my_array.item(index). But I don't get an item by using my_array[index]. If I replace the first line above by

duk_push_array(ctx);

I get an error that the length property is not configurable. Is it possible to achieve what I want? An array with 'lazy' element binding? I have the impression that NetSurf somehow manages to do this but haven't quite worked out how ...


Solution

  • Ecmascript provides two main standard mechanisms for property virtualization: getters/setters (accessors) and the Proxy object. Getters/setters are limited to properties you explicitly set up beforehand so they don't always work for fully virtualizing an object, but a Proxy object can capture among other things all property reads and writes.

    You should be able to implement your use case using a Proxy. Duktape implements a subset of the Proxy traps (documented in http://duktape.org/guide.html#es6-proxy). As a minimal example of capturing all property reads and writes (but forwarding them to the target):

    var target = { foo: 'hello' };
    var proxy = new Proxy(target, {
        get: function (targ, key) {
            print('get', key);
            // may also return a virtualized value
            return targ[key];
        },
        set: function (targ, key, val) {
            print('set', key, val);
            // may also capture (virtualize) write, or reject write
            targ[key] = val;
            return true;  // indicate write was allowed
        }
    });
    print(proxy.foo);
    proxy.bar = 123;
    

    Running with "duk" this prints:

    get foo
    hello
    set bar 123