This section describes how type-related ECMAScript algorithms
like comparisons and coercions are extended to Duktape custom types.
Duktape specific type algorithms (ToBuffer()
and ToPointer()
)
are also discussed.
The following shorthand is used to indicate how values are compared:
Value | Description |
---|---|
t | compares to true |
f | compares to false |
s | simple compare: boolean-to-boolean, string-to-string (string contents compared) |
n | number compare: NaN values compare false, zeroes compare true regardless of sign (e.g. +0 == -0) |
N | number compare in SameValue: NaN values compare true, zeroes compare with sign (e.g. SameValue(+0,-0) is false) |
p | heap pointer compare |
L | lightfunc compare: to be considered equal, Duktape/C function pointers and internal control flags (including the "magic" value) must match |
1 | string vs. number: coerce string with ToNumber() and retry comparison |
2 | boolean vs. any: coerce boolean with ToNumber() to 0 or 1, and retry comparison |
3 | object vs. string/number: coerce object with ToPrimitive() and retry comparison |
Note that Boolean objects, String objects, and Number objects compare as any other objects instead of being automatically unboxed. For example, non-strict equality compares plain string values with a byte-by-byte comparison, but String objects are compared by object reference (like any other objects).
Non-strict equality comparison is specified in The Abstract Equality Comparison Algorithm for standard types. Custom type behavior is as follows:
The standard behavior as well as behavior for Duktape custom types is summarized in the table below:
und | nul | boo | num | str | obj | buf | ptr | lfn | |
---|---|---|---|---|---|---|---|---|---|
und | t | t | f | f | f | f | f | f | f |
nul | t | f | f | f | f | f | f | f | |
boo | s | 2 | 2 | 2 | f | f | f | ||
num | n | 1 | 3 | f | f | f | |||
str | s | 3 | f | f | f | ||||
obj | p | f | f | f | |||||
buf | p | f | f | ||||||
ptr | s | f | |||||||
lfn | L |
Strict equality is much more straightforward and preferable whenever possible for simplicity and performance. It is described in The Strict Equality Comparison Algorithm for standard types. Custom type behavior is as follows:
The standard behavior as well as behavior for Duktape custom types is summarized in the table below:
und | nul | boo | num | str | obj | buf | ptr | lfn | |
---|---|---|---|---|---|---|---|---|---|
und | t | f | f | f | f | f | f | f | f |
nul | t | f | f | f | f | f | f | f | |
boo | s | f | f | f | f | f | f | ||
num | n | f | f | f | f | f | |||
str | s | f | f | f | f | ||||
obj | p | f | f | f | |||||
buf | p | f | f | ||||||
ptr | s | f | |||||||
lfn | L |
The SameValue
algorithm is not easy to invoke from user code.
It is used by e.g. Object.defineProperty()
when checking whether
a property value is about to change. SameValue is even stricter than a
strict equality comparison, and most notably differs in how numbers are compared.
It is specified in
The SameValue algorithm
for standard types. Custom type behavior is as follows:
The standard behavior as well as behavior for Duktape custom types is summarized in the table below:
und | nul | boo | num | str | obj | buf | ptr | lfn | |
---|---|---|---|---|---|---|---|---|---|
und | t | f | f | f | f | f | f | f | f |
nul | t | f | f | f | f | f | f | f | |
boo | s | f | f | f | f | f | f | ||
num | N | f | f | f | f | f | |||
str | s | f | f | f | f | ||||
obj | p | f | f | f | |||||
buf | p | f | f | ||||||
ptr | s | f | |||||||
lfn | L |
The custom types behave as follows for ECMAScript coercions described Type Conversion and Testing (except SameValue which was already covered above):
buffer | pointer | lightfunc | |
---|---|---|---|
DefaultValue | Usually "[object Uint8Array]" ; like Uint8Array | TypeError | "light_<PTR>_<FLAGS>" (toString/valueOf) |
ToPrimitive | Usually "[object Uint8Array]" ; like Uint8Array | identity |
"light_<PTR>_<FLAGS>" (toString/valueOf) |
ToBoolean | true | false for NULL pointer, true otherwise | true |
ToNumber | ToNumber(String(buffer)), usually ToNumber("[object Uint8Array]") = NaN | 0 for NULL pointer, 1 otherwise | NaN |
ToInteger | same as ToNumber; usually 0 | same as ToNumber | 0 |
ToInt32 | same as ToNumber; usually 0 | same as ToNumber | 0 |
ToUint32 | same as ToNumber; usually 0 | same as ToNumber | 0 |
ToUint16 | same as ToNumber; usually 0 | same as ToNumber | 0 |
ToString | Usually [object Uint8Array] ; like Uint8Array | sprintf() with %p format (platform specific) | "light_<PTR>_<FLAGS>" |
ToObject | Uint8Array object (backs to argument plain buffer) | Pointer object | Function object |
CheckObjectCoercible | allow (no error) | allow (no error) | allow (no error) |
IsCallable | false | false | true |
SameValue | (covered above) | (covered above) | (covered above) |
When a buffer is string coerced it behaves like an Uint8Array, with the
result usually being "[object Uint8Array]"
. This behavior was
changed in Duktape 2.0. To create a string from buffer contents you can use
e.g. the Node.js Buffer binding or the Encoding API.
When a buffer is object coerced a new Uint8Array object is created, with a new ArrayBuffer backing to the plain buffer (no copy is made).
When a lightfunc is coerced with ToPrimitive() it behaves like an ordinary
function: it gets coerced with Function.prototype.toString()
with
the result (normally) being the same as ToString() coercion.
When a lightfunc is object coerced, a new Function object is created
and the virtual properties (name
and length
and
the internal "magic" value are copied over to the Function object.
ToBuffer() coercion is used when a value is forced into a buffer
type e.g. with the duk_to_buffer()
API call. The coercion
is as follows:
ToPointer() coercion is used e.g. by the duk_to_pointer()
call. The coercion is as follows:
void *
in a portable manner.The following table summarizes how different types are handled:
ToBuffer | ToPointer | |
---|---|---|
undefined | buffer with "undefined" | NULL |
null | buffer with "null" | NULL |
boolean | buffer with "true" or "false" | NULL |
number | buffer with string coerced number | NULL |
string | buffer with copy of string data | ptr to heap hdr |
object | buffer with ToString(value) | ptr to heap hdr |
buffer | identity | ptr to heap hdr |
pointer | sprintf() with %p format (platform specific) | identity |
lightfunc | buffer with ToString(value) | NULL |
The ECMAScript addition operator is specified in The Addition operator (+). Addition behaves specially if either argument is a string: the other argument is coerced to a string and the strings are then concatenated. This behavior is extended to custom types as follows:
ToPrimitive()
;
plain buffers and lightfuncs are normally coerced with ToString()
.
For plain buffers the result is usually "[object Uint8Array]"
and for lightfuncs "[object Function]"
.ToNumber()
and then added as numbers. NULL pointers coerce to
0, non-NULL pointers to 1, so addition results may not be very intuitive.Addition is not generally useful for custom types. For example, if two plain
buffers are added, the result is usually "[object Uint8Array][object Uint8Array]"
,
which matches how standard addition behaves for two Uint8Array instances.
If a plain buffer or pointer is used as a property access base value,
properties are looked up from the (initial) built-in prototype object
(Uint8Array.prototype
or Duktape.Pointer.prototype
).
This mimics the behavior of standard types.
For example:
duk> buf = Duktape.dec('hex', '414243'); // plain buffer = ABC duk> buf.subarray(); = function subarray() {"native"} duk> typeof buf.toString(); = string
Lightfuncs have a few non-configurable and non-writable virtual properties
(name
and length
) and inherit their remaining
properties from Function.prototype
, which allows ordinary inherited
Function methods to be called:
var bound = myLightFunc.bind('dummy', 123);