Duktape supports non-standard internal properties which are
essentially hidden from user code. They can only be accessed by a
direct property read/write, and are never enumerated, serialized by
JSON.stringify()
or returned from built-in functions such
as Object.getOwnPropertyNames()
.
Duktape uses internal properties for various implementation specific
purposes, such as storing an object's finalizer reference, the internal
value held by Number
and Date
, etc. User code
can also use internal properties for its own purposes, e.g. to
store "hidden state" in objects, as long as the property names never
conflict with current or future Duktape internal keys (this is ensured
by the naming convention described below). User code should never try
to access Duktape's internal properties: the set of internal properties
used can change arbitrarily between versions.
Internal properties are distinguished from other properties by the
property key: if the byte representation of a property key begins with
a 0xFF
byte Duktape automatically treats the property as an
internal property. Such a string is referred to as an internal string.
The initial byte makes the key invalid UTF-8 (even invalid extended UTF-8),
which ensures that (1) internal properties never conflict with normal Unicode
property names and that (2) ordinary Ecmascript code cannot accidentally access
them. The initial prefix byte is often represented by an underscore in
documentation for readability, e.g. _Value
is used instead
of \xFFValue
.
The following naming convention is used. The convention ensures that Duktape and user internal properties never conflict:
Type | Example (C) | Bytes | Description |
---|---|---|---|
Duktape | "\xFF" "Value" |
ff 56 61 6c 75 65 |
First character is always uppercase, followed by [a-z0-9_]* . |
User | "\xFF" "myprop" |
ff 6d 79 70 72 6f 70 |
First character must not be uppercase to avoid conflict with current or future Duktape keys. |
User | "\xFF\xFF" <arbitrary> |
ff ff <arbitrary> |
Double 0xFF prefix followed by arbitrary data. |
In some cases the internal key needed by user code is not static, e.g.
it can be dynamically generated by serializing a pointer or perhaps the
bytes are from an external source. In this case it is safest to use
two 0xFF
prefix bytes as the example above shows.
0xFF
prefix cannot be expressed as a valid
Ecmascript string. For example, the internal string \xFFxyz
would appear as the bytes ff 78 79 7a
in memory, while the
Ecmascript string "\u00ffxyz"
would be represented as the
CESU-8 bytes c3 bf 78 79 7a
in memory.
Creating an internal string is easy from C code:
/* Create an internal string, which can then be used to read/write internal * properties, and can be passed on to Ecmascript code like any other string. * Terminating a string literal after a hex escape is safest to avoid some * ambiguous cases like "\xffab". */ duk_push_string(ctx, "\xff" "myprop");
For more discussion on C string hex escaping, see c_hex_esc.c.
Internal strings can also be created from Ecmascript code if one has access
to e.g. the Buffer constructor or Duktape.dec()
(this must be considered
in sandboxing):
// Using Duktape.Buffer() var buf = new Duktape.Buffer(1); buf[0] = 255; var key1 = buf + 'myprop'; // Using Duktape.dec() var key2 = Duktape.dec('hex', 'ff6d7970726f70'); // \xFFmyprop
There's no special access control for internal properties: if user code has
access to the property name (string), it can read/write the property value.
Any code with the ability to create or use buffers can potentially create an
internal string by converting a buffer into a string. However, standard Ecmascript
code with no access to buffer values or ability to create them cannot create internal
strings (or any invalid UTF-8 strings in general). When sandboxing, ensure that
the sandboxed code has no access to the Duktape
built-in or any
buffer values.
As a concrete example, the internal value of a Date
can be
accessed as follows:
// Print the internal timestamp of a Date instance. User code should NEVER // actually do this because the internal properties may change between // versions in an arbitrary manner! var key = Duktape.dec('hex', 'ff56616c7565'); // \xFFValue var dt = new Date(123456); print('internal value is:', dt[key]); // prints 123456