Internal properties

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.

Note that the 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