* Add internal wrappers also to DUK_MEMMOVE(), DUK_MEMSET(), and
DUK_MEMZERO().
* Use the wrappers everywhere for consistency: the zero-size cases will then
always be safe, and if the target is fine with invalid pointers in the zero
size case, the whole check can be omitted easily.
* Remove a few zero size checks as they're no longer necessary.
* Change duk_bool_to to duk_small_uint_t from duk_small_int_t. This may
cause some sign warnings in calling code.
* Reject attempt to unpack an array-like value whose length is 2G or over;
previously was not checked explicitly, and the length was cast to duk_idx_t
with a sign change and the unpack would then later fail. Now it fails with
a clean RangeError.
* Add wrap check for Node.js Buffer.concat().
* API DUK_TYPE_xxx, DUK_TYPE_MASK_xxx, flag constants etc are now unsigned.
Both duk_hthread and duk_context typedefs resolve to struct duk_hthread
internally. In external API duk_context resolves to struct duk_hthread
which is intentionally left undefined as the struct itself is not
dereferenced. Change internal code to use duk_hthread exclusively which
removes unnecessary and awkward thr <-> ctx casts from internals.
The basic guidelines are:
* Public API uses duk_context in prototype declarations. The intent is to
hide the internal type, and there's already a wide dependency on the
type name.
* All internal code, both declarations and definitions, use duk_hthread
exclusively. This is done even for API functions, i.e. an API function
declared as "void duk_foo(duk_context *ctx);" is then defined as
"void duk_foo(duk_hthread *thr);".
* Replace the two alternative algorithms with a single one which works for
both desktop and low memory cases.
* Basic algorithm is a hash table with size 2^N, hash mask is simply
(size - 1), e.g. if size is 0x100, mask is 0xFF. duk_hstring has a 'next'
pointer (single linked list) for chaining strings mapping to the same
slot.
* Change plain buffers to inherit from Uint8Array. This affects a lot of
small things like Object.prototype.toString() output, enumeration of plain
buffers, etc. It also changes JSON serialization for plain buffers because
the index properties are enumerable as with Uint8Array instances.
* Disable JSON stringify fastpath for plain buffers for now so that the
virtual index properties get serialized correctly.
* Remove ArrayBuffer non-standard virtual properties.
* Remove DataView non-standard virtual properties.
* Move .byteLength, .byteOffset, .BYTES_PER_ELEMENT, and .buffer into
inherited getters as required in ES6. However, the .length property
remains a virtual own property for now (it too is an inherited getter
in ES6).
* Move ArrayBuffer.allocPlain() and ArrayBuffer.plainOf() to
Uint8Array.allocPlain() and Uint8Array.plainOf() to match the
semantics change for plain buffers.
* Fix Node.js buffer .slice() behavior, the returned Node.js buffer
would have ArrayBuffer.isView == 0 which doesn't match the revised
Node.js behavior (Buffers being Uint8Array instances)
* Reject ArrayBuffers with a view offset/length in Node.js Buffer .slice()
rather than accept such ArrayBuffers without actually respecting the
view offset/length.
* Allow a plain buffer or a lightfunc as a constructor "replacement object"
return value.
* Strings with 0xFF byte prefix are considered special symbols: they have
typeof "symbol" but still mostly behave as strings (e.g. allow ToString)
so that existing code dealing with internal keys, especially inside
Duktape, can work with fewer changes.
* Strings with 0x80 byte prefix are global symbols, e.g. Symbol.for('foo')
creates the byte representatio: 0x80 "foo"
* Strings with 0x81 byte prefix are unique symbols; the 0x81 byte is followed
by the Symbol description, and an internal string component ensuring
uniqueness is separated by a 0xFF byte (which can never appear anywhere in
an extended UTF-8 string). The unique suffix is up to Duktape internals,
currently two 32-bit counters are used. For example:
0x81 "mySymbol" 0xFF "0-17".
* Well-known symbols use the 0x81 prefix but lack a unique suffix, so their
format is 0x81 <description> 0xFF.
* ES6 distinguishes between an undefined symbol description and an empty
string symbol description. This distinction is not currently visible via
Ecmascript bindings but may be visible in the future. Append an extra
0xFF to the unique suffix when the description is undefined, i.e.
0x81 0xFF <unique suffix> 0xFF.
Rename and reuse a previously internal duk_push_object_internal() which just
pushes a bare object (= object without an internal prototype) which is useful
for various dict / tracking map purposes.
* Remove 'enumerate' trap.
* Use 'ownKeys' trap for "for-in" too, as specified in ES7.
* Add enumerability check for 'ownKeys' result when used in for-in or
Object.keys(). Because there's no support for 'getOwnPropertyDescriptor'
trap yet, the check will always operate directly on the target object.
Also some small performance optimization, e.g. when getting enumerates keys
(Object.keys() et al) write the keys into a preallocated dense array which is
allocated directly to size.
These can be used whenever we're 100% certain that the value stack index
exists and the type matches expected type. When these are true, a
duk_hstring, duk_hbuffer, or duk_hobject pointer fetch can be inlined to
small code.
Saves a few hundred bytes of footprint:
* duk_dup_0() = duk_dup(ctx, 0), duk_dup_1() = duk_dup(ctx, 1), etc.
* duk_dup_m2() = duk_dup(ctx, -2), etc.
* duk_dup_m1() is not added, because duk_dup_top() is the same thing
* Plain buffers still inherit from ArrayBuffer.prototype.
* Plain buffers won't object coerce, so Object(plainBuffer) fails.
* All buffer object related methods throw an error; their function bodies
are essentially empty. Note that this includes bindings such as
String.fromBuffer(), ArrayBuffer.allocPlain(), ArrayBuffer.plainOf(),
and so on. In essence, you can index plain buffers in Ecmascript but
the buffer values must be created via the C API.
* Duktape custom bindings like Duktape.dec('hex', 'deadbeef') still work
and produce plain buffers.
Change handling of plain buffers so that they behave like ArrayBuffer
instances to Ecmascript code, with limitations such as not being
extensible and all properties being virtualized. This simplifies
Ecmascript code as plain buffers are just lightweight ArrayBuffers
(similarly to how lightfuncs appear as function objects). There are
a lot of small changes in how the built-in objects and methods, and
the C API deals with plain buffer values.
Also make a few small changes to plain pointer and lightfunc handling
to improve consistency with how plain buffers are now handled.
Improve readability by doing the following renames:
* duk_hcompiledfunction -> duk_hcompfunc
* duk_hnativefunction -> duk_hnatfunc
* duk_hbufferobject -> duk_hbufobj
Corresponding renames for all caps defines.
Reorder tags to accommodate a separate 'unused' tag so that 'undefined' can
become a single tag write (instead of tag + value like booleans). This is
good because 'undefined' values are involved in e.g. value stack resizes and
are performance relevant.
Also reorder tags so that "is heap allocated" check can be a single bit test
instead of a comparison when using non-packed duk_tval. This makes every
DECREF potentially faster because an "is heap allocated" test appears in
every DECREF.
Because "unused" is not intended to appear anywhere in actual use (e.g. as
a value stack value, as a property value, etc), "unused" values will fall
into the default clause of DUK_TAG_xxx switch case statements. Add an assert
to every such default clause that the value is not intended to be "unused".
Remove duk_push_unused() as it should no longer be used. It was only used
by the debugger protocol; refuse an inbound "unused" value in the debugger.
This is not breaking compatibility because there was no legitimate usage for
the debug client sending requests with "unused" values.
Change a few built-ins to treat length as a 32-bit quantity
even though the specification allows some operations to extend
the Array length above 2^32-1. This is not very useful, because
it will be read back through ToUint32().
Also remove mostly unused old debug code.
Debug code doesn't have access to 'heap' so it cannot decode pointers.
Cause an #error for now if both debug prints and pointer compression
are enabled at the same time.
Remove duk_debug_hobject.c from make and dist. It was out of date and
not used in practice anymore.
Memory optimization work for very low memory devices (96 to 256kB system RAM).
Overall changes are:
- 16-bit fields for various internal structures to reduce their size
- Heap pointer compression to reduce pointer size to 16 bits
When DUK_OPT_LIGHTFUNC_BUILTINS and the new low memory options are enabled,
Duktape initial heap memory usage is about 23kB (compared to baseline of
about 45kB) on x86.
Unless low memory feature options are enabled, there should be no visible
changes to Duktape behavior.
More detailed changes:
- 16-bit changes for duk_heaphdr: pointer compression, refcount
- 16-bit changes for duk_hstring: hash, blen, and clen can all be 16 bits,
use 0xFFFF as string byte length limit (call sites ensure this limit is
never exceeded)
- 16-bit changes for duk_hbuffer, use 0xFFFF as buffer length limit
- 16-bit fields for hobject size (entry part, array part), drop hash part
since it's not usually needed for extremely low memory environments
- 16-bit changes for duk_hcompiledfunction
- Heap pointer packing for stringtable
- Heap pointer packing for 'strs' built-in strings list (saves around 600
to 700 bytes but may not be a good tradeoff because call site size will
increase)
Other changes:
- Heaphdr NULL init fix. The original macros were broken: the double/single
linked macro variants were the wrong way around. Now sets through macro
to work properly with compressed pointers.
- Rename duk_hbuffer CURR_DATA_PTR -> DATA_PTR to reduce macro length
(previous name was tediously long)
- Rename buffer "usable_size" to "alloc_size" throughout as they have been
the same for a while now (they used to differ when buffer had an extra NUL).
- Add memory optimization markers to Duktape.env (pointer compression and
individual 16-bit field options)
- Rename a few internal fields for clarity: duk_hobject 'p' to 'props',
heap->st to heap->strtable
- Add a safety check for buffer alloc size (should not be triggered but
prevents wrapping if call sites don't properly check for sizes)
- Other minor cleanups
e_used is actually used to track the number of entry part entries currently
GC reachable. Some of the entries may be NULL, indicating deleted keys.
The e_used value is also used as the index where the next insertion of a new
property key happens, so e_next is probably a more appropriate name and
doesn't incorrectly imply that the e_used count indicates actually used,
non-NULL key entries.
Lots of minor string format cleanups (casts mainly).
Add a special check to detect NULL pointers with %s/%p formats in debug
logging to avoid calling the platform sprintf() with a NULL value. This
is especially important for %s + NULL, which is not portable and may crash
on some platforms. Debug logging will now format these specially as "NULL"
and avoid calling the platform for formatting, so debug log call sites don't
need to be careful with NULL pointers any more.