* Use same "proxy rejected" for all Proxy invariant rejects.
* #ifdef guard for proxy policy checks.
* Test coverage for 'preventExtensions' trap.
* Convert many duk_hobject.h macro calls into function calls.
* Add functions to interact with idx_props allocation.
* Compile warning fixes.
* Finalize debug log support for pointer compressed builds. Pointer
compressed builds can now debug log.
* Fix refcount leak in Uint8Array 'get'.
* Fix assertion failure (and underlying bug) for chained Proxy.
* Fix build failure when using assertions and no refcounts.
* Add stack top entry/exit assertions to duk_prop_get.c and
duk_prop_getown.c.
Isolate all char-offset-to-byte-offset and character access calls
behind helpers to help prepare for a switch to WTF-8 representation.
This change should have no visible effect yet.
While setting up an environment for running the catch clause, the
bytecode executor could trigger a memory allocation and thus a GC
within "error handling" state.
Fix this by splitting catch clause handling into two parts:
- Part 1: unwind activation(s) and catcher(s) until the desired
catcher is found (this should be side effect free). Then write
the error value and longjmp type to the value stack (at idx_base),
and set a calling scope flag to indicate we need to run part 2 too.
Finally, deactivate the "catch active" flag of the catcher.
- Part 2: runs after we're out of error handling and inside a new
setjmp. Setup the environment needed to run the catch clause and
execute it with the bytecode executor. If an error (such as out
of memory) happens here, it propagates out (or into a finally
clause) because the catcher no longer has "catch active" set.
It's as if the throw came from inside the catch code.
(Part 2 is only needed if a catch clause needs an environment for
the catch variable; currently this is always the case.)
Also add a sanity check to mark-and-sweep object compaction: in case
mark-and-sweep was called inside error handling (which should ideally
never happen), refuse to compact objects which causes side effects.
Also rename debug format code %!C to %!X, and add %!C (duk_catcher *)
and %!A (duk_activation *) format codes so that activations and
catchers can be debug printed conveniently.
* Convert memory helpers like duk_memcmp() to macros where possible: for some
reason automatic inlining with -Os doesn't do the right thing and the
footprint impact is over 1kB. The duk_memcmp() helper is an inlined
function because it returns a value and assertions would otherwise lead
to multiple evaluation of arguments.
* Differentiate between C99+ and "unsafe" call sites. For example, use
duk_memcpy() when the call site obeys C99+ restrictions, and
duk_memcpy_unsafe() when either pointer may be NULL when the size is zero.
Same for all helpers.
* 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.
Add support for 'new.target' expression without yet adding support for an
explicit newTarget which may differ from the constructor function being
called.
* Make value stack and call stack limits configurable via DUK_USE_xxx
options. Also make value stack grow/shrink constants configurable.
* Rewrite value stack grow/shrink check primitives for better hot/cold path
handling.
* Use a proportional spare for grow and shrink sizes so that applications
needing a large value stack have fewer value stack resizes.
* Grow value stack allocation when entering a call or when explicitly requested
via e.g. duk_require_stack().
* Never shrink the value stack when entering a call, so that the unwind path
is guaranteed to have value stack to handle a protected call return. This
guarantee is only needed for protected call but is now applied to all calls
for simplicity.
* Don't perform a value stack shrink check at all in function return anymore.
It would be OK from protected call semantics perspective to do a shrink
attempt without throwing if it fails.
* Perform a value stack shrink check in mark-and-sweep only for now. When
emergency GC is running, shrink to a minimal size respecting current value
stack reserve.
Remove thr->callstack as a monolithic array and replace it with a linked list
of duk_activations. thr->callstack_curr is the current call (or NULL if no
call is in progress), and act->parent chains to a previous call or NULL.
thr->callstack_top is kept because it's needed by some internals at present;
it may be removed in the future.
When the flag is set, there is either no subclass C struct for the
duk_hobject, or there is a subclass C struct but there are no references
needing DECREF/marking in the struct.
This allows DECREF and mark-and-sweep to handle duk_hobjects with less
overhead for the common cases of plain objects and arrays (and some other
less commonly collected structs like duk_hnatfunc).
Also change Duktape.Thread.prototype internal class from Thread to Object:
with the other changes internal code now assumes that if an object's class
is Thread, it has the duk_hthread memory layout which wouldn't be the case
for Duktape.Thread.prototype.
* 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.
Change dispatch to use an 8-bit main opcode instead of a 6-bit one.
This removes the need for "EXTRA" opcodes and a secondary switch
clause in the executor dispatch loop.
The new opcode layout uses four 8-bit fields: opcode, A, B, C. The
previous reg/const concept which used 9-bit B and C fields, with the
top bit reserved to denote reg vs const, is now implemented by using
four consecutive opcode slots and moving the B and C reg/const flags
into the opcode. For example:
ADD_RR reg(A) <- reg(B) + reg(C)
ADD_CR reg(A) <- const(B) + reg(C)
ADD_RC reg(A) <- reg(B) + const(C)
ADD_CC reg(A) <- const(B) + const(C)
From a footprint standpoint this allows the executor to remain roughly
the same size: four dispatched opcodes (each a function pointer in a
compiled jump table) point to the same case clause handler, which does
the reg/const decision based on an instruction bit test as before.
However, when performance is more important than footprint, each reg/const
case can be handled separately in the executor so that there's no longer
a reg/const check when the opcode executes.
Note that not all opcodes require a reg/const qualifier, so that opcode
space is effectively increased even if reg/const opcodes consume multiple
entries from the opcode table.
Other minor changes:
* Optimize behavior of several opcodes to e.g. avoid unnecessary support
for shuffling/indirection when wider register arguments are now
available.
* 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.
* Remove CSPROP(I) opcode. CSPROP usually leads to three opcodes, e.g.
LDREG + LDCONST + CSPROP. Direct loads for 'base[key]' and 'base' has
the same effect with one opcode shorter bytecode and no need for a
separate CSPROP opcode.
* Remove CSREGI (indirect) opcode, simplify CSREG opcode a bit. Extend
CSREG base register argument to be wider (BC) so that shuffling is not
needed in practice.
* Remove CSVARI (indirect) opcode and handle its equivalent by using explicit
shuffling in the compiler. This removes one opcode, and indirect target
check from CSVAR.
* Remove NEWI and extend base register argument of NEW to be wider (BC) so
that shuffling is not needed in practice.
* Remove CALLI, and split CALL into CALL, TAILCALL, and EVALCALL. This
eliminates the flags field (A) and allows the base register to be wider
(BC), eliminating the need for shuffling in practice.
* Maximum argument count to constructor and ordinary calls drops from 511
to 255 with this change.
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.
An external buffer is similar to a dynamic buffer but the external data
pointer is managed by user code rather than Duktape, and may point to an
arbitrary address. For example, user can build an external buffer which
points to a framebuffer and operate on that framebuffer from Ecmascript
code via the buffer.
Remove support for 'full tval' init. It was never enabled, and there was no
'full tval' support for non-packed duk_tval anyway.
Add fastint marker to Duktape.env.
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.