GETPROP: exposed property get algorithm
Background
Consider the following expression:
x = y[z]
The following happens compile time:
z
is parsed as an identifier referencey
is parsed as an identifier referencey[z]
is parsed as a property accessor (E5 Section 11.2.1)- When the simple assignment is parsed, the
y[z]
compiler knows that the property accessor is used as a right-hand-side value, so it emits whatever internal bytecode is required to read the property value during execution
The following happens run time:
- The compiled code contains the sequence described in E5 Section 11.2.1:
baseValue = GetValue(y)
, wherey
is the identifier referencepropertyNameValue = GetValue(z)
, wherez
is the identifier referenceCheckObjectCoercible(baseValue)
, which throws aTypeError
if thebaseValue
isnull
orundefined
- Create a property reference with
baseValue
as the base reference andToString(propertyNameValue)
as the property name (and strict flag based on current code strictness)
- Call
GetValue()
for the property reference. This results in the following sub-steps of E5 Section 8.7.1 to be executed:base
is the result ofGetValue(y)
(identifier lookup result directly)- The referenced name is
ToString(GetValue(z))
(identifier lookup result with coercion) - If
base
is not a primitive: use[[Get]]
directly forbase
and the referenced name - Else use a variant for
[[Get]]
The [[Get]]
variant for a primitive base is specified explicitly in E5 Section 8.7.1. This seems a bit odd, as it seems equivalent to:
- Let
O
beToObject(base)
- Call
[[Get]]
forO
and referenced name
However, this is not the case. There is a subtle difference in the case that the property is an accessor. Normally the this
binding for the getter is the object given to [[Get]]
. Here the this
binding is the uncoerced primitive value.
This leads to externally visible behavior, illustrated in the following:
// add test getter
Object.defineProperty(String.prototype, 'test', {
get: function() { print(typeof this); },
set: function(x) { print(typeof this); },
});
"foo".test; // prints 'string'
var s = new String("foo");
s.test; // prints 'object'
Behavior in ECMAScript implementations seems to vary:
- NodeJS / V8: prints 'string' and 'object' as expected
- Rhino: prints 'object' and 'object'
- Smjs: prints 'object' and 'object'
GetValue()
allows the caller to skip creation of the coerced object (which is one of: a Boolean
, a Number
, or a String
; see E5 Section 9.9, ToObject()
).
Note: the replacement [[Get]]
overrides whatever [[Get]]
function would normally be used for the target object. For instance, if there were some primitive-to-object coercion which created an arguments object, the arguments object exotic [[Get]]
behavior would be skipped. However, since the arguments and Function
objects are the only objects with non-default [[Get]]
, this is not an issue in practice.
First draft
When the property accessor is created, the base reference and property name are "coerced" to a value using GetValue()
. In the example above, this causes x
's and foo
's values to be looked up. These correspond to steps 1-4 of the property accessor expression in E5 Section 11.2.1. When compiling, these are converted into whatever code is necessary to fetch the two values into VM registers.
The relevant part begins after that in steps 5-8, which first perform some coercions and then create a property accessor. The accessor is then acted upon by GetValue()
, and ultimately [[Get]]
or its variant.
Combining all of these, we get the first draft (for base value O
and property name value P
):
- Let
orig
beO
. (Remember the uncoerced original for a possible getter call.) - Call
CheckObjectCoercible
withO
as argument. In practice: ifO
isnull
orundefined
, throw aTypeError
. - Let
P
beToString(P)
. (This may have side effects ifP
is an object.) - Let
O
beToObject(O)
. (This is side effect free.) - If
O
is anarguments
object which contains a[[ParameterMap]]
internal property: a. (Arguments object exotic behavior.) Letmap
be the value of the[[ParameterMap]]
internal property of the arguments object. b. If the result of calling the[[GetOwnProperty]]
internal method ofmap
passingP
as the argument is notundefined
: 1. Return the result of calling the[[Get]]
internal method ofmap
passingP
as the argument. - Let
desc
be the result of calling the[[GetProperty]]
internal method ofO
with property nameP
. - If
desc
isundefined
, returnundefined
. - If
IsDataDescriptor(desc)
istrue
: a. Letres
bedesc.[[Value]]
. - Otherwise,
IsAccessorDescriptor(desc)
must betrue
: a. Letgetter
bedesc.[[Get]]
. b. Ifgetter
isundefined
, returnundefined
. c. Else letres
be the result of calling the[[Call]]
internal method ofgetter
providingorig
as thethis
value and providing no arguments. (Note: the difference to a basic[[Get]]
is that the getterthis
binding is the original, uncoerced object.) - If
orig
is aFunction
object or anarguments
object which contains a[[ParameterMap]]
internal property: a. (Arguments or Function object exotic behavior.) IfP
is"caller"
andres
is a strict modeFunction
object, throw aTypeError
exception. - Return
res
.
Notes:
Steps 2-3 come from the property accessor evaluation rules in E5 Section 11.2.1. In particular,
CheckObjectCoercible()
is called before the key is coerced to a string. Since the key string coercion may have side effects, the order of evaluation matters.Note that
ToObject()
has no side effects (this can be seen from a case by case inspection), so steps 3 and 4 can be reversed.Step 4 comes from
GetValue()
.Steps 5 and forward come from
[[Get]]
; here with exotic behaviors inlined, but[[GetProperty]]
not inlined.
We could inline the [[GetProperty]]
call to the algorithm. However, because the current implementation doesn't do so, that has been omitted for now.
Improving type checking of base value
A variant where steps 3 and 4 are reversed and expanded is as follows:
- Let
orig
beO
. (Remember the uncoerced original for a possible getter call.) - Check and/or coerce
O
as follows: a. IfO
isnull
orundefined
, throw aTypeError
. (This is theCheckObjectCoercible
part.) b. Else ifO
is a boolean, a number, or a string, setO
toToObject(O)
. c. Else ifO
is an object, do nothing. d. Throw aTypeError
. (Note that this case should not happen, as steps a-c are exhaustive. However, this step is useful as a fallback, and for handling any internal types.) - Let
P
beToString(P)
. (This may have side effects ifP
is an object.) - If
O
is anarguments
object which contains a[[ParameterMap]]
internal property: a. (Arguments object exotic behavior.) Letmap
be the value of the[[ParameterMap]]
internal property of the arguments object. b. If the result of calling the[[GetOwnProperty]]
internal method ofmap
passingP
as the argument is notundefined
: 1. Return the result of calling the[[Get]]
internal method ofmap
passingP
as the argument. - Let
desc
be the result of calling the[[GetProperty]]
internal method ofO
with property nameP
. - If
desc
isundefined
, returnundefined
. - If
IsDataDescriptor(desc)
istrue
: a. Letres
bedesc.[[Value]]
. - Otherwise,
IsAccessorDescriptor(desc)
must betrue
: a. Letgetter
bedesc.[[Get]]
. b. Ifgetter
isundefined
, returnundefined
. c. Else letres
be the result of calling the[[Call]]
internal method ofgetter
providingorig
as thethis
value and providing no arguments. (Note: the difference to a basic[[Get]]
is that the getterthis
binding is the original, uncoerced object.) - If
orig
is aFunction
object or anarguments
object which contains a[[ParameterMap]]
internal property: a. (Arguments or Function object exotic behavior.) IfP
is"caller"
andres
is a strict modeFunction
object, throw aTypeError
exception. - Return
res
.
Avoiding temporary objects
If the base value is not an object, step 4 in the above algorithm creates a temporary object given to [[GetProperty]]
for a property descriptor lookup. The first object in the prototype chain is the temporary object, while the rest are already established non-temporary objects.
If we knew that the property P
could never be an own property of the temporary object, we could skip creation of the temporary object altogether. Instead, we could simply start [[GetProperty]]
from the internal prototype that the coerced object would get without actually creating the object.
Since the coerced object is created by ToObject
from a primitive value, we know that it is a Boolean
instance, a Number
instance, or a String
instance (see E5 Section 9.9). The "own properties" of these are:
Boolean
: noneNumber
: noneString
:"length"
and index properties for string characters
So, the coercion can be skipped safely for everything except String
s. This is unfortunate, because it is conceivably the string primitive value which is most likely to be accessed through a coercion, e.g. as in:
var t = "my string";
print(t.length);
In any case, avoiding temporary creation for everything but Strings
can be worked into the algorithm e.g. as follows:
- Let
orig
beO
. (Remember the uncoerced original fora possible getter call.) - Check and/or coerce
O
as follows: a. IfO
isnull
orundefined
, throw aTypeError
. (This is theCheckObjectCoercible
part.) b. IfO
is a boolean: setO
to the built-inBoolean
prototype object (skip creation of temporary) c. Else ifO
is a number: setO
to the built-inNumber
prototype object (skip creation of temporary) d. Else ifO
is a string, setO
toToObject(O)
. e. Else ifO
is an object, do nothing. f. Else, throw aTypeError
. (Note that this case should not happen, as steps a-e are exhaustive. However, this step is useful as a fallback, and for handling any internal types.) - Let
P
beToString(P)
. (This may have side effects ifP
is an object.) - If
O
is anarguments
object which contains a[[ParameterMap]]
internal property: a. (Arguments object exotic behavior.) Letmap
be the value of the[[ParameterMap]]
internal property of the arguments object. b. If the result of calling the[[GetOwnProperty]]
internal method ofmap
passingP
as the argument is notundefined
: 1. Return the result of calling the[[Get]]
internal method ofmap
passingP
as the argument. - Let
desc
be the result of calling the[[GetProperty]]
internal method ofO
with property nameP
. - If
desc
isundefined
, returnundefined
. - If
IsDataDescriptor(desc)
istrue
: a. Letres
bedesc.[[Value]]
. - Otherwise,
IsAccessorDescriptor(desc)
must betrue
: a. Letgetter
bedesc.[[Get]]
. b. Ifgetter
isundefined
, returnundefined
. c. Else letres
be the result of calling the[[Call]]
internal method ofgetter
providingorig
as thethis
value and providing no arguments. (Note: the difference to a basic[[Get]]
is that the getterthis
binding is the original, uncoerced object.) - If
orig
is aFunction
object or anarguments
object which contains a[[ParameterMap]]
internal property: a. (Arguments or Function object exotic behavior.) IfP
is"caller"
andres
is a strict modeFunction
object, throw aTypeError
exception. - Return
res
.
If we change step 2.d to get the related string value (length or character of the string) directly, no temporaries need to be created due to coercion. However, if the property name P
is checked, it needs to be string coerced which happens only later in step 3. If we add a separate coercion to step 2.d, P
will be coerced twice unless step 3 is then explicitly skipped; this is not an issue as the latter coercion is a NOP and can in any case be easily skipped.
This variant is as follows:
- Let
orig
beO
. (Remember the uncoerced original for a possible getter call.) - Check and/or coerce
O
as follows: a. IfO
isnull
orundefined
, throw aTypeError
. (This is theCheckObjectCoercible
part.) b. IfO
is a boolean: setO
to the built-inBoolean
prototype object (skip creation of temporary) c. Else ifO
is a number: setO
to the built-inNumber
prototype object (skip creation of temporary) d. Else ifO
is a string: 1. SetP
toToString(P)
. (This may have side effects ifP
is an object.) 2. IfP
islength
, return the length of the primitive string value as a number. 3. IfP
is a valid array index within the string length, return a one-character substring of the primitive string value at the specified index. 4. Else, setO
to the built-inString
prototype object (skip creation of temporary) 5. Goto LOOKUP. (Avoid double coercion ofP
.) e. Else ifO
is an object, do nothing. f. Else, throw aTypeError
. (Note that this case should not happen, as steps a-e are exhaustive. However, this step is useful as a fallback, and for handling any internal types.) - Let
P
beToString(P)
. (This may have side effects ifP
is an object.) - LOOKUP: If
O
is anarguments
object which contains a[[ParameterMap]]
internal property: a. (Arguments object exotic behavior.) Letmap
be the value of the[[ParameterMap]]
internal property of the arguments object. b. If the result of calling the[[GetOwnProperty]]
internal method ofmap
passingP
as the argument is notundefined
: 1. Return the result of calling the[[Get]]
internal method ofmap
passingP
as the argument. - Let
desc
be the result of calling the[[GetProperty]]
internal method ofO
with property nameP
. - If
desc
isundefined
, returnundefined
. - If
IsDataDescriptor(desc)
istrue
: a. Letres
bedesc.[[Value]]
. - Otherwise,
IsAccessorDescriptor(desc)
must betrue
: a. Letgetter
bedesc.[[Get]]
. b. Ifgetter
isundefined
, returnundefined
. c. Else letres
be the result of calling the[[Call]]
internal method ofgetter
providingorig
as thethis
value and providing no arguments. (Note: the difference to a basic[[Get]]
is that the getterthis
binding is the original, uncoerced object.) - If
orig
is aFunction
object or anarguments
object which contains a[[ParameterMap]]
internal property: a. (Arguments or Function object exotic behavior.) IfP
is"caller"
andres
is a strict modeFunction
object, throw aTypeError
exception. - Return
res
.
Fast path for array indices
When the property name is a number and a valid array index, we'd prefer to be able to lookup the property without coercing the number to a string. This "fast path" needs to work for the common cases; rare cases can go through the ordinary algorithm which requires a ToString()
coercion.
There are many ways to do a (compliant) fast path. The simple case we're considering here is the case when the target object has an "own property" matching the property name (a number).
A simple "shallow fast path" could be:
- If
P
is a whole number in the range [0,2**32-2] (a valid array index) ANDO
has an array part ANDO
has no conflicting "exotic behaviors", then:- Let
idx
be the array index represented byP
- If the array part of
O
containsidx
and the key exists, read and return the value. Note that the value can beundefined
- Let
- Else use normal algorithm.
Some notes:
The behavior of the fast path must match the behavior of the normal algorithm exactly (including side effects). This should be the case here, but can be verified by simulating the normal algorithm with the assumption of a number as a property name, with the target property present as an "own data property" of the target object.
The conflicting exotic behaviors are currently:
String
object exotic behavior, and arguments object exotic behavior. Array exotic behaviors are not conflicting for read operations.A certain key in the array can be defined even if the value is
undefined
. The check is whether the key has been defined, i.e.[[HasProperty]]
would be true. Internally, the value "unused" is used to denote unused entries with unused keys, while the value "undefined" represents an undefined value with a defined key. For instance, the following defines an array key:var a = []; a[10] = undefined; // "10" will now enumerate
The fast path avoids the
ToString()
coercion which may, in general, have side effects (at least for objects). However, the fast path only applies ifP
is a number, and theToString()
coercion of a number is side effect free.If the array part does not contain the key, the normal algorithm is always used, regardless of whether the ancestors contain the key or not. This means that if a non-existent key is accessed from the array (even if the index is within the current array length), string interning will be required with this fast path. For instance:
var a = []; a[0] = 'foo'; a[2] = 'bar'; // fast path ok, no string interning print(a[0]); // fast path fails, string interned but still not found print(a[1]);
Inlining the above shallow fast path with the variant which avoids temporaries altogether produces:
- Let
orig
beO
. (Remember the uncoerced original for a possible getter call.) - Check and/or coerce
O
as follows: a. IfO
isnull
orundefined
, throw aTypeError
. (This is theCheckObjectCoercible
part.) b. IfO
is a boolean: setO
to the built-inBoolean
prototype object (skip creation of temporary) c. Else ifO
is a number: setO
to the built-inNumber
prototype object (skip creation of temporary) d. Else ifO
is a string: 1. SetP
toToString(P)
. (This may have side effects ifP
is an object.) 2. IfP
islength
, return the length of the primitive string value as a number. 3. IfP
is a valid array index within the string length, return a one-character substring of the primitive string value at the specified index. 4. Else, setO
to the built-inString
prototype object (skip creation of temporary) 5. Goto LOOKUP. (Avoid double coercion ofP
.) e. Else ifO
is an object: 1. Array fast path: IfO
is an object (always true here) ANDP
is a number and a valid array index (whole number in [0,2**32-2]) ANDO
internal representation has an array part ANDO
does not have conflicting exotic behaviors (cannot haveString
or arguments exotic behaviors, may haveArray
behavior), then: a. Letidx
be the array index represented byP
b. If the array part ofO
containsidx
and the key exists, read and return that value. (Note:ToString(P)
is skipped, but it would have no side effects asP
is a number. The"caller"
check forP
is also skipped, but it would never match becauseP
is a number.) f. Else, Throw aTypeError
. (Note that this case should not happen, as steps a-e are exhaustive. However, this step is useful as a fallback, and for handling any internal types.) - Let
P
beToString(P)
. (This may have side effects ifP
is an object.) - LOOKUP: If
O
is anarguments
object which contains a[[ParameterMap]]
internal property: a. (Arguments object exotic behavior.) Letmap
be the value of the[[ParameterMap]]
internal property of the arguments object. b. If the result of calling the[[GetOwnProperty]]
internal method ofmap
passingP
as the argument is notundefined
: 1. Return the result of calling the[[Get]]
internal method ofmap
passingP
as the argument. - Let
desc
be the result of calling the[[GetProperty]]
internal method ofO
with property nameP
. - If
desc
isundefined
, returnundefined
. - If
IsDataDescriptor(desc)
istrue
: a. Letres
bedesc.[[Value]]
. - Otherwise,
IsAccessorDescriptor(desc)
must betrue
: a. Letgetter
bedesc.[[Get]]
. b. Ifgetter
isundefined
, returnundefined
. c. Else letres
be the result of calling the[[Call]]
internal method ofgetter
providingorig
as thethis
value and providing no arguments. (Note: the difference to a basic[[Get]]
is that the getterthis
binding is the original, uncoerced object.) - If
orig
is aFunction
object or anarguments
object which contains a[[ParameterMap]]
internal property: a. (Arguments or Function object exotic behavior.) IfP
is"caller"
andres
is a strict modeFunction
object, throw aTypeError
exception. - Return
res
.
We can further improve this by adding a fast path for the case where O
is a primitive string (in step 2.d):
- Let
orig
beO
. (Remember the uncoerced original fora possible getter call.) - Check and/or coerce
O
as follows: a. IfO
isnull
orundefined
, throw aTypeError
. (This is theCheckObjectCoercible
part; the throw is unconditional.) b. IfO
is a boolean: setO
to the built-inBoolean
prototype object (skip creation of temporary) c. Else ifO
is a number: setO
to the built-inNumber
prototype object (skip creation of temporary) d. Else ifO
is a string: 1. IfP
is a number, is a whole number, a valid array index, and within the string length, return a one-character substring of the primitive string value at the specified index. (Note:ToString(P)
is skipped, but it would have no side effects asP
is a number. The"caller"
check forP
is also skipped, but it would never match becauseP
is a number.) 2. SetP
toToString(P)
. (This may have side effects ifP
is an object.) 3. IfP
islength
, return the length of the primitive string value as a number. (Note: The"caller"
check forP
is skipped, but would never match.) 4. IfP
is a valid array index within the string length, return a one-character substring of the primitive string value at the specified index. (Note: The"caller"
check forP
is skipped, but would never match.) 5. Else, setO
to the built-inString
prototype object (skip creation of temporary) 6. Goto LOOKUP. (Avoid double coercion ofP
.) e. Else ifO
is an object: 1. Array fast path: IfO
is an object (always true here) ANDP
is a number and a valid array index (whole number in [0,2**32-2]) ANDO
internal representation has an array part ANDO
does not have conflicting exotic behaviors (cannot haveString
or arguments exotic behaviors, may haveArray
behavior), then: a. Letidx
be the array index represented byP
b. If the array part ofO
containsidx
and the key exists, read and return that value. (Note:ToString(P)
is skipped, but it would have no side effects asP
is a number. The"caller"
check forP
is also skipped, but it would never match becauseP
is a number.) f. Else, Throw aTypeError
. (Note that this case should not happen, as steps a-e are exhaustive. However, this step is useful as a fallback, and for handling any internal types.) - Let
P
beToString(P)
. (This may have side effects ifP
is an object.) - LOOKUP: If
O
is anarguments
object which contains a[[ParameterMap]]
internal property: a. (Arguments object exotic behavior.) Letmap
be the value of the[[ParameterMap]]
internal property of the arguments object. b. If the result of calling the[[GetOwnProperty]]
internal method ofmap
passingP
as the argument is notundefined
: 1. Return the result of calling the[[Get]]
internal method ofmap
passingP
as the argument. - Let
desc
be the result of calling the[[GetProperty]]
internal method ofO
with property nameP
. - If
desc
isundefined
, returnundefined
. - If
IsDataDescriptor(desc)
istrue
: a. Letres
bedesc.[[Value]]
. - Otherwise,
IsAccessorDescriptor(desc)
must betrue
: a. Letgetter
bedesc.[[Get]]
. b. Ifgetter
isundefined
, returnundefined
. c. Else letres
be the result of calling the[[Call]]
internal method ofgetter
providingorig
as thethis
value and providing no arguments. (Note: the difference to a basic[[Get]]
is that the getterthis
binding is the original, uncoerced object.) - If
orig
is aFunction
object or anarguments
object which contains a[[ParameterMap]]
internal property: a. (Arguments or Function object exotic behavior.) IfP
is"caller"
andres
is a strict modeFunction
object, throw aTypeError
exception. - Return
res
.
We can also move step 4 (arguments exotic behavior) to step 2.e. This has the problem that step 4 assumes P
has been string coerced already. So, a duplicate coercion is needed (like for strings):
- Let
orig
beO
. (Remember the uncoerced original for a possible getter call.) - Check and/or coerce
O
as follows: a. IfO
isnull
orundefined
, throw aTypeError
. (This is theCheckObjectCoercible
part; the throw is unconditional.) b. IfO
is a boolean: setO
to the built-inBoolean
prototype object (skip creation of temporary) c. Else ifO
is a number: setO
to the built-inNumber
prototype object (skip creation of temporary) d. Else ifO
is a string: 1. IfP
is a number, is a whole number, a valid array index, and within the string length, return a one-character substring of the primitive string value at the specified index. (Note:ToString(P)
is skipped, but it would have no side effects asP
is a number. The"caller"
check forP
is also skipped, but it would never match becauseP
is a number.) 2. SetP
toToString(P)
. (This may have side effects ifP
is an object.) 3. IfP
islength
, return the length of the primitive string value as a number. (Note: The"caller"
check forP
is skipped, but would never match.) 4. IfP
is a valid array index within the string length, return a one-character substring of the primitive string value at the specified index. (Note: The"caller"
check forP
is skipped, but would never match.) 5. SetO
to the built-inString
prototype object (skip creation of temporary) 6. Goto LOOKUP. (Avoid double coercion ofP
.) e. Else ifO
is an object: 1. Array fast path: IfO
is an object (always true here) ANDP
is a number and a valid array index (whole number in [0,2**32-2]) ANDO
internal representation has an array part ANDO
does not have conflicting exotic behaviors (cannot haveString
or arguments exotic behaviors, may haveArray
behavior), then: a. Letidx
be the array index represented byP
. b. If the array part ofO
containsidx
and the key exists, read and return that value. (Note:ToString(P)
is skipped, but it would have no side effects asP
is a number. The"caller"
check forP
is also skipped, but it would never match becauseP
is a number.) 2. IfO
is anarguments
object which contains a[[ParameterMap]]
internal property: a. SetP
toToString(P)
. b. (Arguments object exotic behavior.) Letmap
be the value of the[[ParameterMap]]
internal property of the arguments object. c. If the result of calling the[[GetOwnProperty]]
internal method ofmap
passingP
as the argument is notundefined
: 1. Return the result of calling the[[Get]]
internal method ofmap
passingP
as the argument. d. Else, goto LOOKUP. (Avoid double coercion ofP
.) f. Else, Throw aTypeError
. (Note that this case should not happen, as steps a-e are exhaustive. However, this step is useful as a fallback, and for handling any internal types.) - Let
P
beToString(P)
. (This may have side effects ifP
is an object.) - LOOKUP: Let
desc
be the result of calling the[[GetProperty]]
internal method ofO
with property nameP
. - If
desc
isundefined
, returnundefined
. - If
IsDataDescriptor(desc)
istrue
: a. Letres
bedesc.[[Value]]
. - Otherwise,
IsAccessorDescriptor(desc)
must betrue
: a. Letgetter
bedesc.[[Get]]
. b. Ifgetter
isundefined
, returnundefined
. c. Else letres
be the result of calling the[[Call]]
internal method ofgetter
providingorig
as thethis
value and providing no arguments. (Note: the difference to a basic[[Get]]
is that the getterthis
binding is the original, uncoerced object.) - If
orig
is aFunction
object or anarguments
object which contains a[[ParameterMap]]
internal property: a. (Arguments or Function object exotic behavior.) IfP
is"caller"
andres
is a strict modeFunction
object, throw aTypeError
exception. - Return
res
.
::: note ::: title Note :::
The above is the current "shallow fast path" approach, which has a couple of annoying limitations. For instance, if the array index is not used, the key will be coerced to string (regardless of whether ancestors have the key or not). Many improvements are possible; these are future work. :::
Inlining GetProperty
Inlining [[GetProperty]]
(but not [[GetOwnProperty]]
), maintaining the original input value in O
instead of orig
, and using curr
instead of O
otherwise, we get:
- Check and/or coerce
O
as follows: a. IfO
isnull
orundefined
, throw aTypeError
. (This is theCheckObjectCoercible
part; the throw is unconditional.) b. IfO
is a boolean: setcurr
to the built-inBoolean
prototype object (skip creation of temporary) c. Else ifO
is a number: setcurr
to the built-inNumber
prototype object (skip creation of temporary) d. Else ifO
is a string: 1. IfP
is a number, is a whole number, a valid array index, and within the string length, return a one-character substring of the primitive string value at the specified index. (Note:ToString(P)
is skipped, but it would have no side effects asP
is a number. The"caller"
check forP
is also skipped, but it would never match becauseP
is a number.) 2. SetP
toToString(P)
. (This may have side effects ifP
is an object.) 3. IfP
islength
, return the length of the primitive string value as a number. (Note: The"caller"
check forP
is skipped, but would never match.) 4. IfP
is a valid array index within the string length, return a one-character substring of the primitive string value at the specified index. (Note: The"caller"
check forP
is skipped, but would never match.) 5. Setcurr
to the built-inString
prototype object (skip creation of temporary) 6. Goto NEXT. (Avoid double coercion ofP
.) e. Else ifO
is an object: 1. Setcurr
toO
. 2. Array fast path: IfO
is an object (always true here) ANDP
is a number and a valid array index (whole number in [0,2**32-2]) ANDO
internal representation has an array part ANDO
does not have conflicting exotic behaviors (cannot haveString
or arguments exotic behaviors, may haveArray
behavior), then: a. Letidx
be the array index represented byP
. b. If the array part ofO
containsidx
and the key exists, read and return that value. (Note:ToString(P)
is skipped, but it would have no side effects asP
is a number. The"caller"
check forP
is also skipped, but it would never match becauseP
is a number.) 3. IfO
is anarguments
object which contains a[[ParameterMap]]
internal property: a. SetP
toToString(P)
. b. (Arguments object exotic behavior.) Letmap
be the value of the[[ParameterMap]]
internal property of the arguments object. c. If the result of calling the[[GetOwnProperty]]
internal method ofmap
passingP
as the argument is notundefined
: 1. Return the result of calling the[[Get]]
internal method ofmap
passingP
as the argument. d. Else, goto NEXT. (Avoid double coercion ofP
.) f. Else, Throw aTypeError
. (Note that this case should not happen, as steps a-e are exhaustive. However, this step is useful as a fallback, and for handling any internal types.) - Let
P
beToString(P)
. (This may have side effects ifP
is an object.) - NEXT: Let
desc
be the result of calling the [[GetOwnProperty]] internal method ofcurr
with property nameP
. - If
desc
isundefined
: a. Letcurr
be the value of the[[Prototype]]
internal property ofcurr
. b. Ifcurr
is notnull
, goto NEXT. c. Returnundefined
. - If
IsDataDescriptor(desc)
istrue
: a. Letres
bedesc.[[Value]]
. - Otherwise,
IsAccessorDescriptor(desc)
must betrue
: a. Letgetter
bedesc.[[Get]]
. b. Ifgetter
isundefined
, returnundefined
. c. Else letres
be the result of calling the[[Call]]
internal method ofgetter
providingO
as thethis
value and providing no arguments. (Note: the difference to a basic[[Get]]
is that the getterthis
binding is the original, uncoerced object.) - If
O
is aFunction
object or anarguments
object which contains a[[ParameterMap]]
internal property: a. (Arguments or Function object exotic behavior.) IfP
is"caller"
andres
is a strict modeFunction
object, throw aTypeError
exception. - Return
res
.
Final version
(See above.)