This section summarizes Duktape behavior which deviates from the E5.1 or other relevant specifications.
The Duktape
built-in is (of course) non-standard and provides
access to Duktape specific features. Also the buffer, pointer, and lightfunc
types are custom.
Objects may have internal properties which
are essentially hidden from normal code: they won't be enumerated or returned
even by e.g. Object.getOwnPropertyNames()
. Ordinary Ecmascript
code cannot refer to such properties because the property keys intentionally
use invalid UTF-8 (0xFF
prefix byte).
The "use duk notail" directive is non-standard. It prevents a function from being tail called.
The require()
built-in is non-standard, and provided for
CommonJS-based module loading, see Modules.
See Error objects and Function objects.
Non-strict function instances don't have a caller
property in the
E5/E5.1 specification. Some real world code expects to have this property, so it
can be enabled with the feature option DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY
.
E5.1 does not allow a function declaration to appear outside program or function top level:
function test() { // point A try { throw new Error('test'); } catch (e) { // This is a SyntaxError in E5.1 function func() { print(typeof e); } // point B } // point C }
These declarations are also referred to as "function statements", and appear quite often in real world code (including the test262 test suite), so they are allowed by Duktape. Unfortunately there are several semantics used by different Javascript engines. Duktape follows the V8 behavior for function statements:
As an illustration, the above example would behave as the following:
function test() { function func() { print(typeof e); } try { throw new Error('test'); } catch (e) { } }
func()
in the above example would already be declared
and callable in point A, and would not have access to the e
binding in any of the points A, B, or C.
Although not allowed by E5.1, the following escape is allowed in RegExp syntax:
/\$/ /* matches dollar literally, non-standard */ /\u0024/ /* same, standard */
This escape occurs in real world code so it is allowed. (More leniency will be added in future versions to deal with real world RegExps; dollar escapes are not the only issue.)
When deleteCount (the 2nd argument) is not given to Array.prototype.splice()
,
the standard behavior is to work as if the 2nd argument was undefined
(or 0, which has the same behavior after coercions). A more real world compatible
behavior is to treat the missing argument like positive infinity, i.e. to extend
the splice operation to the end of the array.
Because the non-standard real world behavior is expected by much existing code,
Duktape uses this behavior by default. The strict standards compliant behavior can
be enabled with the feature option DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT
.
When the result of an array concat()
would have trailing non-existent
elements, the standard behavior is to ignore them so that they are not reflected in
the result length
. Real world behavior is to include them in the result
value length
. See
test-bi-array-proto-concat-nonstd-trailing.js.
The real world behavior seems consistent in other engines (V8, Rhino, Spidermonkey
at least), so Duktape uses the real world behavior by default. The strict standards compliant
behavior can be enabled with the feature option DUK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER
.
Similar issue as with Array.prototype.concat()
, see
test-bi-array-proto-map-nonstd-trailing.js. The strict standards compliant behavior can be
enabled with the feature option DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER
.
Ecmascript standard behavior is that setters and getters are not given the name of the property being accessed. This prevents reusing a single setter or a getter for multiple properties; separate functions are needed for each property which is sometimes inconvenient and wastes memory.
Duktape provides the property key name as a non-standard additional
argument to setter and getter functions. See
test-dev-nonstd-setget-key-argument.js
and Property virtualization
for more discussion. The strict standards compliant behavior can be enabled
with the feature option DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT
.
See Object.setPrototypeOf and Object.prototype.__proto__.
JSON.stringify()
standard behavior is to output U+2028 and
U+2029 without escaping. This leads to counterintuitive behavior when the
output is used in a web page or parsed with eval()
: the U+2028
and U+2029 characters are considered line terminators which leads to a syntax
error (unterminated string). Duktape escapes U+2028 and U+2029 by default
to avoid this issue; you can turn on the compliant behavior with the feature
option DUK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029
.
String.fromCharCode()
standard behavior is to use ToUInt16()
coercion for codepoint values. Duktape uses ToUint32() by default to better
support non-BMP strings. You can force the compliant behavior with the
feature option DUK_OPT_NO_NONSTD_STRING_FROMCHARCODE_32BIT
.
By default Duktape provides a fast path for writing to Array instances.
The fast path is active when numeric indices are used (e.g. arr[7] = 'foo'
)
and a few internal conditions are met. When the fast path is taken, Duktape
doesn't check Array.prototype for conflicting properties (these are very rare in
practical code), which makes common array writes faster. The behavior is
non-compliant, but there's no outward difference unless Array.prototype has
properties with numeric keys. You can turn on the compliant behavior with
the feature option DUK_OPT_NO_NONSTD_ARRAY_WRITE
. See the
following for more details on the fast path behavior:
test-misc-array-fast-write.js.
Duktape provides a Khronos/ES6 TypedArray binding. The scope of the binding is the Khronos specification, not the full ES6 specification. There are some differences to Khronos/ES6, including:
length
(matching byteLength
),
byteOffset
(= 0), and BYTES_PER_ELEMENT
(= 1)
properties.subarray()
returns a typed array object whose
internal prototype is copied from the argument rather than being set
to the default prototype for that view, such as Uint8Array.prototype
.Duktape provides a Node.js-like Buffer
binding. There are
some differences between the Node.js behavior and Duktape behavior. These
differences include:
totalLength
exceeds combined size of input buffers.noAssert
is true. Failed
reads return NaN and failed writes return 0.writeUInt8()
silently coerces to
0x00 rather than throwing a TypeError.utf8
encoding (and accepts no
spelling variants). Most API calls ignore an encoding argument, and
use the Duktape internal string representation (CESU-8 / extended UTF-8)
for string-to-buffer conversion.byteLength
(matching
length
), byteOffset
(= 0), and
BYTES_PER_ELEMENT
(= 1) properties.Object.prototype.toString.call(buf)
outputs
[object Buffer]
rather than [object Object]
.