SquirrelFish Objects and Property Access

Fetching and setting the value of a JavaScript object is an extensive task. The type of an object can change at any time. Because this rarely occurs, it would be nice to optimize for the standard case where object properties are assumed to type stable. SquirrelFish Extreme does this by having multiple methods of getting/setting a property depending on a few conditions. However, before we can learn about accessing properties, we have to check out how SquirrelFish implements JavaScript objects.

Conceptually, an object in SquirrelFish is a wrapper around a hashtable. All strings have a hash associated with them. When accessing a property, the requested property name's hash is used as a key within a hashtable. The value of the hashtable leads us to the address in memory where the property exists.

Ahh if life was so simple. The time consuming process is always learning the details. So let's look at the following JavaScript source code:

var test = new Object();
test.message = "SFX FTW";
print(test.message);
The resulting bytecode simplified with all moves deleted:
[   0] enter
[ 13] get_by_id r5, r4, prototype // Returns an empty object
[ 21] construct r-14, r4, 1, 15, r5, r6 // Construct the object
[ 31] put_by_id r-14, message,"SFX" // Set property message value to "SFX"
[ 42] resolve_func r4, r3, print // Find the print function
[ 46] get_by_id r5, r-14, message // Get the value of property "message"
[ 54] call r3, r3, 2, 14 // Call the method print
[ 59] end r3

Identifiers:
id0 = Object
id1 = prototype
id2 = message
id3 = print

Take a look at the Identifiers. Identifier objects are created during the parsing of the JavaScript source. Identifiers are really wrappers around another class UString for U(nicode) string. The UString class contains both the hash of the string it represents and a pointer to the actual string data.

When a property access occurs, there are three important items required: The object containing the property, the identifier which contains a hash representing the string, and the value of the property. This data is used by three data structures in SquirrelFish:

  1. JsObject - A class that represents an actual JavaScript Object.
  2. Structure - a field within a JsObject that contains all the methods necessary to get/set values in a hashtable.
  3. PropertySlots - holds all the necessary information to directly access a property's value.
As a general workflow, manipulating a property requires looking for the actual JsObject where the property exists. The JsObject contains a Structure which does the hash look up to find the property's address in memory. This information is then stored into a PropertySlot object. Finally, the JsObject reads/writes to the property through the PropertySlot object. JsObject and Structure are intricately linked to actually store and fetch property values. These data structures are defined as follows:
JsObject.h
typedef JSValuePtr* PropertyStorage;
PropertyStorage m_propertyStorage; // Location where property values are stored

JsCell.h
Structure* m_structure; // Access to the Hashtable

Structure.h:
PropertyMapHashTable* m_propertyTable; // The actual hash table

Actual variable locations in memory are represented by a PropertySlot:
PropertySlot.h
JSValuePtr m_value; // Not used if a slot property
size_t m_offset; // offset from JsObject.m_propertyStorage
JSValuePtr m_slotBase; // the object this property is located on

union {
JSObject* getterFunc;
JSValuePtr* valueSlot;
Register* registerSlot;
unsigned index;
} m_data; // The actual value of the property

SquirrelFish Extreme has multiple types of PropertySlot. "PutPropertySlot" is used when assigning a value to a property. It contains a few additional fields:
PutPropertySlot.h
Type m_type; // New, Existing, or Invalid Property type
JSObject* m_base; // The object this property belongs to
bool m_wasTransition; // Not sure yet
size_t m_offset; // Offset from JsObject.m_
propertyStorage
The thing to note is that the actual address where values are stored is in JsObject Instance->m_propertyStorage[PropertySlot->offset]. So the Structure in JsObject is really a key->value pair mapping PropertyName->offset. We finally have enough information to actually store a value into a property on an object. When we want to add the "message" property to the "test" object, memory is allocated for a PutPropertySlot instance. The PutPropertySlot instance is used for the "test" object where the property should be added and passed into the following method:
JsObject.cpp
void JSObject::put(ExecState* exec, Identifier& propertyName, JSValuePtr value, PutPropertySlot& slot)
{
// Check if there are any getters or setters in the prototype chain
JSValuePtr prototype;
for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
prototype = obj->prototype();

// No Getters/Setters so set the property on "this"
if (prototype.isNull()) {
// slot is still an empty structure with no meaningful data
// putDirect("message", "SFX", 0, true, slot);
putDirect(propertyName, value, 0, true, slot); // Slot filled, value entered
return;
}
}
}

The putDirect method creates the hashtable for "this" object. Space is allocated for the property on the JsObject through the m_propertyStorage. This information is written into the PutPropertySlot object, which finally stores the value to (m_propertyStorage + offset).

Actually retrieving the value uses the standard PropertySlot object. Retrieving the value calls the get method in JsObject:

JsObject.cpp
JSValuePtr JSValuePtr::get(ExecState* exec, Identifier& propertyName, PropertySlot& slot)
{
if (// Unlikely Event "this" is not a heap allocated object) { }
else {
JSCell* cell = asCell();
while (true) {
if (cell->fastGetOwnPropertySlot(exec, propertyName, slot)) // Fill the slot information
return slot.getValue(exec, propertyName); // Fetch the actual value
}
}
}
It follows the same control flow of finding the offset from the hashtable, storing that information in a PropertySlot, and finally retrieving the value. You may wonder why slot.getValue needs the property name variable. For this particular example, there is no need but is instead used in other property access cases. Thus, there are many other conditions where this execution path is invalid. I'll be getting into other topics such as what happens when the type of a property changes, or if you have a getter/setter at a later date.

* SFX uses http://www.azillionmonkeys.com/qed/hash.html for their hash function