* Comment on duk_buffer_to_string() safety w.r.t. potentially pushing
Symbol values.
* Went through all duk_push_(l)string() call sites too.
* Minor footprint optimization for pushing empty strings (use more
compact internal helper).
* 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.
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
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.
These don't play well with the API currently: the Duktape specific error
codes don't have Ecmascript Error class counterparts so they don't get
represented usefully as Ecmascript objects (e.g. AllocError is a plain
Error from Ecmascript point of view).
There's no real need for Duktape specific error code. Some of the codes
had become unused; a couple were used but Ecmascript standard types can
be used instead.
Also minor error message tweaking.
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.
Eval code doesn't need an automatic prototype object, so avoid creating one.
This also avoids making the internal function which eval compiles to part of
a reference loop so that eval functions collect immediately.
* Use shared error macros and shared error handler to reduce the size of call
sites of common errors.
* Make zero argument DUK_ERROR() calls non-vararg calls to reduce call site
footprint. Non-vararg calls have smaller call sites and because there are
a lot of call sites, this turns out to be significant.
* Remove variadic macros from internal DUK_ERROR() macro set and add separate
macros for argument counts 0 to 4; this is more portable and requires less
conditional code, and works well when a non-vararg call is used for most
error call sites.
* Rework macro / function argument order for the error path, try to keep 'thr'
in the same argument slot to avoid unnecessary register moves.
* Pack linenumber and error code into a single 32-bit argument when possible,
removes one more constant load from the call site.
* Convert some internal errors to RangeErrors when the underlying cause is an
implementation limit (such as a compiler temp limit) rather than an actual
unexpected internal situation.
* Simplify and share a few error messages to reduce string count.
* Shorter setup for normalization input.
* Reuse the same stack buffer for normalization input and output. This works
because at every point in the algorithm: (1) the output is at most as long as
the input (never longer), and (2) the input is read one byte at a time and
the input (unlike the output) is never backtracked.
* Default module.{fileName,name} if not set instead of using explicit defaults.
Slightly smaller code footprint and less module properties for low memory
environments.
* Add module.fileName and initialize it to resolved module ID
* Add module.name and initialize it to the last component of the resolved
module ID
* Use module.fileName as the eval fileName which ends up in the wrapper
function's .fileName property
* Use module.name as the forced .name property of the wrapper function
which affects stack traces (but doesn't introduce an automatic binding
for the function name because the function is compiled as anonymous and
.name is only then overridden)
Also add .name for fresh require() functions created to improve stack traces
for errors in sub-modules.
GetLocals, GetVar, PutVar, and Eval will now accept an optional negative
callstack offset specifying the function activation to operate on. This
offset has the same semantics as the argument of Duktape.act(): -1 is
the topmost activation, -2 is its caller, etc.
'Leave as undefined' seems to be the best overall value stack initialization
policy. While 'leave as garbage' is marginally better in a few cases (mostly
when refcounting is disabled) it's probably not worth keeping two policies
around.
This allows an assignment to module.exports to be immediately
visible to sub-modules even when the current module is still
being loaded. This behavior matches Node.js.
Also changes modLoaded cache behavior when module loading fails: previously
a partial module would be cached, now the entry is removed so that it's
possible to attempt to reload the module. Exact behavior in this case is
underspecified in CommonJS; the revised behavior matches Node.js.
Add a C API binding for Object.defineProperty(): duk_def_prop().
In addition to Object.defineProperty() features, the API call provides a
"force" flag which allows properties to be added to non-extensible objects
and non-configurable properties to be changed (except virtual properties
which are immutable).
Because the name duk_def_prop() conflicts with internal calls, rename them
from duk_def_prop*() to duk_xdef_prop*(). This rename also makes it clearer
that the internal duk_xdef_prop*() calls have non-compliant, internal
semantics.
Also reimplement Object.defineProperty() and Object.defineProperties() (and
duk_def_prop()) so that they share the same internal helpers, and there is
no need for a temporary property descriptor object which is unnecessary churn.
Detailed changes:
- New helper to prepare (validate and normalize) property descriptors
- New helper to implement Object.defineProperty() internals, leaving
out validation of the property descriptor
- Reimplement Object.defineProperty() using the new helpers
- Reimplement Object.defineProperties() using the new helpers
- Reimplement duk_define_property() using the new helpers, so that a
temporary property descriptor object is no longer created
- Add support for "force" flag to Object.defineProperty()
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.