How to work with lightfuncs

How to work with lightfuncs

Lightfunc overview

When a normal native function is created using duk_push_c_function() two heap allocations are needed:

  • A Function object is created to represent the function in Ecmascript code. The function is represented by an internal duk_hnatfunc structure which is a subtype of the generic duk_hobject structure:

    • The generic duk_hobject part of the structure provides a property table allocated separately.

    • The duk_hnatfunc part of the structure points to the Duktape/C function to be called when Ecmascript code invokes the function, and stores various function parameters like the argument count and a "magic" value.

  • A property table is allocated to hold e.g. the .length property. Built-in functions also have a .name property.

In very low memory environments the memory cost of this representation adds up when multiplied for e.g. 100 functions.

The lightfunc type provides a very lightweight alternative to representing function objects. A lightfunc represents a native function using just a tagged value (duk_tval) with no heap allocations, thus taking no more space than representing e.g. a boolean value. In concrete terms:

  • When a packed duk_tval is used on 32-bit platforms, lightfuncs take 8 bytes: 2 bytes for the type tag, 4 bytes for the native function pointer, and 2 bytes for flags. The flags field (16 bits) stores the function argument count (nargs), .length field, and an 8-bit magic value.

  • When non-packed duk_tval is used, lightfuncs typically take 16 bytes. The fields are logically the same, but laid out differently.

Because there are no heap allocations related to lightfuncs, there are some limitations, with the most important being:

  • Lightfuncs cannot hold properties because they don't have a property table.

  • As a result, lightfuncs cannot have a manually set .name property. They do, however, have a virtual .name property which appears in tracebacks etc.

  • Lightfuncs also cannot hold a .prototype property. They can still be used as constructors, but the default instance created will always inherit from Object.prototype. The native function can overwrite the prototype explicitly, or return a "replacement value". See How to write a native constructor function.

Example of a lightfunc name:

duk> Math.cos.name
= "light_08067ce4_0511"

The name is autogenerated and includes the native function pointer (here 08067ce4) and the 16-bit flags field (here 0x0511).

See also: http://duktape.org/guide.html#type-lightfunc.

Creating lightfuncs

Lightfuncs can only be created from C code as:

// http://duktape.org/api.html#duk_push_c_lightfunc

duk_push_c_lightfunc(ctx, my_adder, 2 /*nargs*/, 2 /*length*/, 0 /*magic*/);

Usually nargs and length fields will have the same value. The magic field can be ignored and set to zero if the native function doesn't need it.

Making built-in functions lightfuncs

The Ecmascript built-in functions Math.cos, Array.prototype.join, etc are all by default ordinary Function objects and consume a significant amount of memory (more than 20kB on a 32-bit low memory target).

To save memory, you can enable the following option:

  • DUK_USE_LIGHTFUNC_BUILTINS

When enabled, built-in functions will be converted to lightfuncs during their initialization. A few built-in functions cannot be safely converted to lightfuncs; they will remain normal Functions.

The downside of this option is that the converted built-ins will have a less useful .name, for example.

Last edited by ,