mirror of https://github.com/svaarala/duktape.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
416 lines
19 KiB
416 lines
19 KiB
======================================
|
|
Function template and instance objects
|
|
======================================
|
|
|
|
Difference between a template and an instance
|
|
=============================================
|
|
|
|
A *function template* is a Duktape internal Ecmascript Function object.
|
|
Function templates are not exposed to user code, and represent a compiled
|
|
function without a concrete surrounding environment. Function templates
|
|
are created by the compiler for every function and inner function. Because
|
|
a template is missing a surrounding lexical environment, it cannot be called
|
|
as a function.
|
|
|
|
A function template is instantiated into a concrete *function instance*
|
|
(also called a *closure*) by creating a new Function object, copying most
|
|
(but not all) template fields into the Function object, and initializing
|
|
the instance specific fields like the outer lexical environment properly.
|
|
Function instances are created for function compilation results and when
|
|
inner functions are later instantiated (with the CLOSURE instruction).
|
|
|
|
This separation is necessary because a certain function template can be
|
|
instantied multiple times with a different outer environment each time.
|
|
Consider the following::
|
|
|
|
function mkPrinter(str) {
|
|
// inner function
|
|
return function() { print(str); }
|
|
}
|
|
|
|
var p1 = mkPrinter("Hello world");
|
|
var p2 = mkPrinter("still here");
|
|
p1();
|
|
p2();
|
|
print(p1 === p2); // => false
|
|
|
|
In this example:
|
|
|
|
* The ``mkPrinter`` function is first compiled into a function template and
|
|
then immediately converted to a function instance. The function instance
|
|
has the global environment as its outer environment. The instance is then
|
|
associated with the ``mkPrinter`` property of the global object.
|
|
|
|
* The inner function inside ``mkPrinter`` is represented by a function
|
|
template stored as part of the ``mkPrinter`` function inner function table.
|
|
|
|
* The ``p1`` and ``p2`` function objects are separate Function objects
|
|
created by a CLOSURE instruction occurring in the bytecode of ``mkPrinter``.
|
|
They have their own properties and separate outer lexical environments, but
|
|
shared the same bytecode, pc-to-line conversion data, etc. The outer lexical
|
|
environment for ``p1`` and ``p2`` is the declarative environment created when
|
|
``mkPrinter`` was entered, and contains the ``str`` binding needed
|
|
to print separate texts when ``p1`` and ``p2`` are called.
|
|
|
|
A function instance does not reference a function template from a garbage
|
|
collection point of view. The function template can be collected while the
|
|
function instance remains reachable.
|
|
|
|
Properties of a function template
|
|
=================================
|
|
|
|
The E5 specification does not recognize a "function template", so there
|
|
are no standard properties for function templates. The properties can also
|
|
change from release to release because they are not exposed to user code.
|
|
The following properties are used:
|
|
|
|
+---------------+---------------------------------------------------------+
|
|
| Property | Description |
|
|
+===============+=========================================================+
|
|
| ``_varmap`` | Maps register-bound variable names to their register |
|
|
| | numbers. |
|
|
| | Example: ``{ arg1: 0, arg2: 1, myvar: 2 }``. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``_formals`` | An array of formal argument names. ``formals.length`` |
|
|
| | provides the number of formal arguments. Note that the |
|
|
| | number of formal arguments does not need to match |
|
|
| | function ``nargs``: the function might access all args |
|
|
| | through the arguments object and have ``nargs`` set to |
|
|
| | zero. This property is used to initialize the |
|
|
| | arguments object (in non-strict code); the compiler |
|
|
| | should omit this whenever possible. |
|
|
| | Example: ``[ "arg1", "arg2" ]``. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``name`` | Function name, set for function declarations and named |
|
|
| | function expressions. If DUK_HOBJECT_FLAG_NAMEBINDING |
|
|
| | is set, the value of this property is bound in the |
|
|
| | function's environment (used for named function |
|
|
| | expressions). |
|
|
| | Example: ``"func"``. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``fileName`` | Source filename (or equivalent). Used to add source |
|
|
| | file information to error objects and tracebacks. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``_source`` | Function source code. E5 specifies that the source |
|
|
| | code of a function must be valid syntax. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``_pc2line`` | Debug information: maps bytecode index to a source line |
|
|
| | number. Space-optimized binary format. |
|
|
+---------------+---------------------------------------------------------+
|
|
|
|
The compiler should omit whatever internal properties are not needed to
|
|
save space. For instance:
|
|
|
|
* ``_varmap`` is not needed if the function can never perform a slow path
|
|
identifier reference.
|
|
|
|
* ``_formals`` is not needed unless a non-strict arguments object is
|
|
potentially constructed. (However, ``_formals`` is also used for deriving
|
|
the "length" of the instance. If _formals is omitted, something else needs
|
|
to be set in the template to allow instance "length" to be initialized.)
|
|
|
|
When debugging, it may be necessary to store more function properties than
|
|
needed by plain execution. For instance, source code should be available
|
|
even for dynamically generated code.
|
|
|
|
Properties of a function instance
|
|
=================================
|
|
|
|
The creation of function instances is described in E5 Section 13.2.
|
|
Each function instance (each closure created from a function
|
|
expression or declaration) has the following standard properties:
|
|
|
|
* ``length``: set to number of formal parameters (length of ``_formals``).
|
|
|
|
* ``prototype``: points to a fresh object which has a ``constructor``
|
|
property pointing back to the function
|
|
|
|
* ``caller``: thrower (strict functions only)
|
|
|
|
* ``arguments``: thrower (strict functions only)
|
|
|
|
There is considerable variance in practical implementations:
|
|
|
|
* smjs::
|
|
|
|
// the "name" property is non-standard; "arguments" and "caller" are
|
|
// present for a non-strict function
|
|
|
|
js> f = function foo() {}
|
|
(function () {})
|
|
js> Object.getOwnPropertyNames(f)
|
|
["prototype", "length", "name", "arguments", "caller"]
|
|
|
|
// for strict mode, the same properties are present.
|
|
|
|
js> f = function foo() { "use strict"; }
|
|
(function foo() {"use strict";})
|
|
js> Object.getOwnPropertyNames(f);
|
|
["prototype", "length", "name", "arguments", "caller"]
|
|
|
|
// the "name" property contains the function expression name
|
|
|
|
js> f.name
|
|
"foo"
|
|
|
|
// "name" is non-writable, non-configurable (and non-enumerable)
|
|
// -> works as a reliable "internal" property too
|
|
|
|
js> Object.getOwnPropertyDescriptor(f, 'name')
|
|
({configurable:false, enumerable:false, value:"foo", writable:false})
|
|
|
|
* nodejs (v8)::
|
|
|
|
// "name" is non-standard; "arguments" and "caller" are present
|
|
// for even a non-strict function
|
|
|
|
> f = function foo() {}
|
|
[Function: foo]
|
|
> Object.getOwnPropertyNames(f)
|
|
[ 'length',
|
|
'caller',
|
|
'arguments',
|
|
'name',
|
|
'prototype' ]
|
|
> f.name
|
|
'foo'
|
|
|
|
// strict mode is the same
|
|
|
|
> f = function foo() { "use strict"; }
|
|
[Function: foo]
|
|
> Object.getOwnPropertyNames(f)
|
|
[ 'name',
|
|
'length',
|
|
'arguments',
|
|
'prototype',
|
|
'caller' ]
|
|
|
|
// 'name' is writable but not configurable/enumerable
|
|
|
|
> f.name
|
|
'foo'
|
|
> Object.getOwnPropertyDescriptor(f, 'name')
|
|
{ value: 'foo',
|
|
writable: true,
|
|
enumerable: false,
|
|
configurable: false }
|
|
|
|
* rhino::
|
|
|
|
// "name" is non-standard, "arity" is non-standard, "arguments"
|
|
// is present (but "caller" is not)
|
|
|
|
js> f = function foo() {}
|
|
[...]
|
|
js> Object.getOwnPropertyNames(f)
|
|
arguments,prototype,name,arity,length
|
|
|
|
// name is non-writable, non-enumerable, non-configurable
|
|
|
|
js> pd = Object.getOwnPropertyDescriptor(f, 'name')
|
|
[object Object]
|
|
js> pd.writable
|
|
false
|
|
js> pd.enumerable
|
|
false
|
|
js> pd.configurable
|
|
false
|
|
|
|
// strict mode functions are similar
|
|
|
|
Notes:
|
|
|
|
* "caller" and "arguments" would be nice as virtual properties to minimize
|
|
object property count. They can't be inherited in the ordinary way without
|
|
breaking compliance (the standard requires they be own properties).
|
|
|
|
* "prototype" would be nice as a virtual property: it's quite
|
|
expensive to have for every function instance.
|
|
|
|
The properties for function instances are (these are also documented in
|
|
user documentation for the exposed parts):
|
|
|
|
+---------------+---------------------------------------------------------+
|
|
| Property | Description |
|
|
+===============+=========================================================+
|
|
| ``length`` | Set to the number of formal parameters. For normal |
|
|
| | functions parsed from Ecmascript source code, this is |
|
|
| | set to ``_formals.length``. Built-in functions may be |
|
|
| | special. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``prototype`` | Points to a fresh object which has a ``constructor`` |
|
|
| | property pointing back to the function instance. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``caller`` | For strict functions, set to the ``[[ThrowTypeError]]`` |
|
|
| | function object defined in E5 Section 13.2.3. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``arguments`` | Like ``caller``. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``name`` | See function templates. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``fileName`` | See function templates. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``_lexenv`` | Together with DUK_HOBJECT_FLAG_NEWENV, controls the |
|
|
| | initialization of variable/lexical environment when a |
|
|
| | function call occurs. |
|
|
| | |
|
|
| | The DUK_HOBJECT_FLAG_NEWENV is set for ordinary |
|
|
| | functions, which always get a new environment record |
|
|
| | when they are called. The flag is cleared for global |
|
|
| | code and eval code, which "share" an existing |
|
|
| | environment record. |
|
|
| | |
|
|
| | If _varenv is missing, it defaults to _lexenv (which is |
|
|
| | very often the case). |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``_varenv`` | See ``_lexenv``. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``_varmap`` | See function templates. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``_formals`` | See function templates. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``_source`` | See function templates. |
|
|
+---------------+---------------------------------------------------------+
|
|
| ``_pc2line`` | See function templates. |
|
|
+---------------+---------------------------------------------------------+
|
|
|
|
Built-in functions
|
|
==================
|
|
|
|
The properties of built-in functions are a special case, because
|
|
they are not created with the algorithm in E5 Section 13.2;
|
|
instead, their properties are described explicitly in E5 Section 15.
|
|
|
|
There is considerable variance between implementations on what
|
|
properties built-in functions get.
|
|
|
|
Duktape/C functions
|
|
===================
|
|
|
|
Duktape/C functions are also represented by an Ecmascript Function object.
|
|
The properties of such functions are extremely minimal; for instance, they
|
|
are missing the ``length`` property. This is done to keep the object size
|
|
as small as possible. This means, however, that the Function objects are
|
|
non-standard.
|
|
|
|
Duktape/C functions also don't have any need for control variables such as
|
|
``_lexenv``, ``_pc2line``, etc.
|
|
|
|
_pc2line format
|
|
===============
|
|
|
|
``_pc2line`` property allows a program counter (bytecode index) to be
|
|
converted to an approximate line number of the expression which generated
|
|
the bytecode instruction in question. Logically it can be considered an
|
|
array (in fact, Lua implements a similar structure as a simple array):
|
|
|
|
+----+------+
|
|
| PC | Line |
|
|
+====+======+
|
|
| 0 | 1 |
|
|
+----+------+
|
|
| 1 | 1 |
|
|
+----+------+
|
|
| 2 | 3 |
|
|
+----+------+
|
|
| 3 | 4 |
|
|
+----+------+
|
|
| 4 | 7 |
|
|
+----+------+
|
|
|
|
If the line number is represented as a 4-byte integer, the structure would
|
|
take as much memory as the related bytecode, doubling memory usage. Clearly
|
|
a more space efficient format is desirable, as long as performance is not
|
|
impacted too much when throwing and catching errors.
|
|
|
|
Although the line number generally stays the same or increases when PC
|
|
increases, this is not always the case (e.g. in loop structures). This
|
|
rules out search structures relying on monotonicity properties. It's nice
|
|
if an arbitrary mapping can be expressed if necessary.
|
|
|
|
Error line number is needed when:
|
|
|
|
* Accessing the non-standard ``lineNumber`` property. This property can be
|
|
implemented as a getter in the Error prototype, which will get the PC
|
|
from the traceback data (if any), and do the PC-to-line conversion only
|
|
when actually needed.
|
|
|
|
* Creating a string-formatted traceback. PC-to-line conversions are needed
|
|
for most traceback lines.
|
|
|
|
The current format is based on the observation that when PC increases by one,
|
|
the typical delta for the line number is very small (and is usually zero or
|
|
positive). Deltas can be expressed efficiently with variable bit length
|
|
encoding. To provide a reasonably fast random access, explicit starting
|
|
point values are recorded for every nth bytecode instruction (currently,
|
|
every 64th; SKIP=64 below). During a lookup one can first skip close to the
|
|
desired mapping entry and then scan the bit-packed format forwards.
|
|
|
|
The format consists of a header structure followed by bit packed diff
|
|
streams (each bit packed stream begins at a byte boundary):
|
|
|
|
+--------+------+---------------------------------------------------+
|
|
| Offset | Type | Description |
|
|
+========+======+===================================================+
|
|
| 0 | u32 | PC limit (maximum PC, exclusive) |
|
|
+--------+------+---------------------------------------------------+
|
|
| 4 | u32 | Line number for PC 0*SKIP |
|
|
+--------+------+---------------------------------------------------+
|
|
| 8 | u32 | Byte offset of diff bitstream for PC 0*SKIP |
|
|
+--------+------+---------------------------------------------------+
|
|
| 12 | u32 | Line number for PC 1*SKIP |
|
|
+--------+------+---------------------------------------------------+
|
|
| 16 | u32 | Byte offset of diff bitstream for PC 1*SKIP |
|
|
+--------+------+---------------------------------------------------+
|
|
| ... | | ... a total of ceil(bytecode_length/SKIP) |
|
|
| | | line/offset entries |
|
|
+--------+------+---------------------------------------------------+
|
|
| ... | | ... diff bitstreams |
|
|
+--------+------+---------------------------------------------------+
|
|
|
|
The diff bitstream consists of SKIP-1 diff entries for a certain
|
|
starting point. Each diff entry simply encodes the line number
|
|
difference when PC increases by one; the difference may be
|
|
negative, zero, or positive. The diff is encoded as one of the
|
|
following entry types:
|
|
|
|
+-----------------+--------------------------------------------------------+
|
|
| Bits | Description |
|
|
+=================+========================================================+
|
|
| 0 | Difference is +0 |
|
|
+-----------------+--------------------------------------------------------+
|
|
| 1 0 <2 bits> | Difference is: +1, +2, +3, or +4 (encoded as 2 bits) |
|
|
+-----------------+--------------------------------------------------------+
|
|
| 1 1 0 <8 bits> | Difference is a signed 8-bit value, encoded with bias |
|
|
| | +0x80 (as unsigned 0x00 ... 0xff) |
|
|
+-----------------+--------------------------------------------------------+
|
|
| 1 1 1 <32 bits> | Fallback, linenumber encoded as absolute 32-bit value |
|
|
+-----------------+--------------------------------------------------------+
|
|
|
|
These cases are not optimized, but rather best guesses combined with some
|
|
experimentation:
|
|
|
|
* Usually multiple bytecode instructions are generated from a single line
|
|
of source code, hence the case +0 is important to encode efficiently.
|
|
|
|
* When line changes, there are either no lines without code, or there are
|
|
a few such lines (empty lines for readability, perhaps a few comment
|
|
lines). The cases +1...+4 are encoded compactly for these cases.
|
|
|
|
* The signed 8-bit offset covers large comment blocks, and the occasional
|
|
negative steps (e.g. in loop structures).
|
|
|
|
* As a fallback, an absolute 32-bit line number can be encoded. This covers
|
|
any remaining cases and provides completeness.
|
|
|
|
As an example, the bitstream for the diffs [+0, +2, +9, -3, +0] would be::
|
|
|
|
0 1001 11000001001 11011111101 0
|
|
=> 01001110 00001001 11011111 10100000 (padded with 0)
|
|
=> 0x4e 0x09 0xdf 0xa0
|
|
|
|
Typically the pc2line data is about 10-15% of the size of the corresponding
|
|
bytecode, a very modest addition to footprint compared to the 100% addition
|
|
of a straight table approach.
|
|
|
|
|