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 genericduk_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.