Add support for lightweight functions or "lightfuncs", a plain tagged type
that contains a Duktape/C function and 16-bit flags which contain an 8-bit
signed magic value, 4-byte argument count (with 15 indicating varargs) and
a 4-byte virtual "length" property. A lightfunc requires no heap allocations,
so it is useful in low memory environments to reduce RAM footprint.
Lightfuncs can be pushed on the value stack using the new API call
duk_push_c_lightfunc().
Also add DUK_OPT_LIGHTFUNC_BUILTINS feature option which causes Duktape to
convert all built-in functions (like Math.cos) into lightfuncs. This saves
around 14 kB of initial RAM usage (which is about 45kB before the change)
on 32-bit environments, which is useful for at least devices with 128kB
system RAM.
Lightfuncs don't interact well with the non-standard "caller" property
(DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY) so you shouldn't use them at all
if you enable that option.
If you don't use lightfuncs or enable DUK_OPT_LIGHTFUNC_BUILTINS, there
should be no visible changes, except changes in some error message strings.
Internally there are a lot of changes, especially in call handling.
* Fix outstanding FIXME issues for lightfunc semantics.
* Improve API and Ecmascript testcases to match.
* Clarify lightfunc limitations, e.g. finalizer limitations.
* Guide and API documentation changes for lightfuncs.
* Fix compile warning: duk_str_not_object unused.
A lot of changes to add preliminary lightfunc support:
* Add LIGHTFUNC tagged type to duk_tval.h and API.
* Internal changes for preliminary to support lightfuncs in call handling
and other operations (FIXMEs left in obvious places where support is
still missing after this commit)
* Preliminary Ecmascript and API testcases for lightfuncs
Detailed notes:
* Because magic is signed, reading it back involves sign extension which is
quite verbose to do in C. Use macros for reading the magic value and other
bit fields encoded in the flags.
* Function.prototype.bind(): the 'length' property of a bound function now
comes out wrong. We could simply look up the virtual 'length' property
even if h_target is NULL: no extra code and binding is relatively rare in
hot paths. Rewrite more cleanly in any case.
* The use flag DUK_USE_LIGHTFUNC_BUILTINS controls the forced lightfunc
conversion of built-ins. This results in non-compliant built-ins but
significant memory savings in very memory poor environments.
* Reject eval(), Thread.yield/resume as lightfuncs. These functions have
current assertions that they must be called as fully fledged functions.
* Lightfuncs are serialized like ordinary functions for JSON, JX, and JC
by this diff.
* Add 'magic' to activation for lightfuncs. It will be needed for lightweight
functions: we don't have the duk_tval related to the lightfunc, so we must
copy the magic value to the activation when a call is made.
* When lightfuncs are used as property lookup base values, continue property
lookup from the Function.prototype object. This is necessary to allow e.g.
``func.call()`` and ``func.apply()`` to be used.
* Call handling had to be reworked for lightfuncs, especially how bound
function chains are handled. This is a relatively large change but is
necessary to support lightweight functions properly in bound function
resolution.
The current solution is not ideal. The bytecode executor will first try an
ecma-to-ecma call setup which resolves the bound function chain first. If
the final, unbound function is not viable (a native function) the call setup
returns with an error code. The caller will then perform a normal call.
Although bound function resolution has already been done, the normal call
handling code will re-do it (and detect there is nothing to do).
This situation could be avoided by decoupling bound function handling and
effective this binding computation from the actual call setup. The caller
could then to do this prestep first, and only then decide whether to use an
ecma-to-ecma call or an ordinary heavyweight call.
Remove duk__find_nonbound_function as unused.
* Use indirect magic to allow LIGHTFUNCs for Date. Most of the built-in
functions not directly eligible as lightfuncs are the Date built-in methods,
whose magic values contain too much information to fit into the 8-bit magic
of a LIGHTFUNC value.
To work around this, add an array (duk__date_magics[]) containing the
actual control flags needed by the built-ins, and make the Date built-in
magic value an index into this table. With this change Date built-ins are
successfully converted to lightfuncs.
Testcase fixes:
- Whitespace fixes
- Print error for indirect eval error to make diagnosis easier
- Fix error string to match errmsg updated in this branch
Print out the massif graph and a grepped "maximum usage" to get some
quick idea of how much memory a certain ecmascript testcase uses at
the moment. Used for e.g. a hello world memory usage test. Usage is:
"make massif-test-dev-hello-world" or "make massif-XXX" for any other
test in ecmascript-testcases/. A few convenience targets like
massif-helloworld are included.
Set --peak-inaccuracy=0.0 for massif run to get as accurate a peak
estimate as possible. (This is not that important because the measure
itself is not 100% relevant.)
Also adds a testcase needed by massif-helloworld target.
Add a test "ajduk" command which integrates AllJoyn.js pool allocator, which
is quite useful for low memory testing. When compiling for x86, the "ajduk"
command memory usage will behave quite closely compared to a typical embedded
ARM-based system.
The "ajduk" command dumps the AllJoyn.js pool state in specific places (after
heap creation, right before destroying, etc) for comparison. There is also
a global binding "AjsHeap.dump()" which can be used to explicitly dump the
heap state from Ecmascript code.
Make Function.prototype.name writable so that user code can set a "name"
property for Duktape/C functions. Duktape/C functions don't have a "name"
property by default and a non-writable ancestor prevents creating one.
Fixes GH-79.
Add a hybrid pool allocator example to the distibution. The allocator uses
a fixed set of pools for small allocations but falls back to malloc etc for
larger sizes and when the pools are exhausted. This reduces churn on the
platform memory allocator which is beneficial for platforms where the platform
allocator is not tuned for a lot of small allocations.
Add API calls duk_get_heapptr() and duk_require_heapptr() to get a borrowed
void pointer reference to Duktape heap allocated values (objects, strings,
and buffers).
Add API call duk_push_heapptr() to recreate an object reference based on a
borrowed pointer later. However, the original value must have been reachable
all the way to this call, so it still needs to be stashed as before.