Type

Javascript is an untyped language. Type in the context of this blog refers to a data structure in the Chakra runtime, which holds additional information about an object. Type is also popularly known as hidden class. Type shares data about a class of objects. The sole purpose of a type in the runtime is to enhance the performance of the user script code. If you are building your own javascript runtime and didn’t care about performance or the memory, you don’t need type.

Chakra has two kinds of type, static type and dynamic type. The static type is for the primitive object which can’t store properties in it. For example: String (“hello”), Boolean (true or false), Number (10.4) etc. The [Dynamic object] (https://github.com/Microsoft/ChakraCore/blob/master/lib/Runtime/Types/DynamicObject.h#L46) which can store properties have dynamic type. Example: {} or new Point(). Let us go a bit deeper into static type and dynamic type and see how they share.

###Static Type Every RecyclableObject holds a pointer to a type. Every object class in Chakra inherits from RecyclableObject except tagged floats. Let’s take an example:

var greeting = "hello";
var message = "open source";

Here we have two literal strings greeting and message which Chakra tracks as a JavascriptString. JavascriptString class inherits from RecyclableObject. Both greeting and message point to a shared type.

Object Layout

This type has the typeId set to TypeIds_String and shared between two literal string objects. It may seem an overkill to share an integer typeId when you have to add a pointer to that type in the literal string objects. In reality, type contains a lot of additional data, not just the typeId. Following is the field dump of the type. Prototype and entrypoint are shared as well.

C++ Js::Type typeId TypeIds_String (7) Js::TypeId flags javascriptLibrary* prototype* entryPoint* propertyCache*

JavscriptString type is a static type. Dynamic objects provide a lot more opportunity to share information. Again dynamic objects have a dynamic type.

###Dynamic Type Let us take an example first:

function Point(x, y)
{ 
  this.x = x;
  this.y = y;
}
var one = new Point(10,20);
var two = new Point(40,50);

print(one.x);
print(two.x);

Above script creates two Point objects which have properties x and y. Chakra needs to store following information in the runtime:

  1. Objects one and two have properties x and y
  2. Object one has property x whose value is 10 and property y whose value is 20
  3. Object two has property x whose value is 40 and property y whose value is 50

Above information needs to be retrieved when the script wants to get or set the property. Note (1) is shareable among multiple objects. (2) & (3) are specific to individual dynamic objects. A typical way to store this is by building a property map and a slot array.

  • Property map, maps between a property and a slot number. Example: Property x is present at slot 0.
  • Slot array stores the values. Example slots[0] contains the value of property x.

Property map is stored in a dynamic type (to be specific, in a type handler which is part of dynamic type). When script does one.x, Chakra fetches property map in the one->type to figure out the slot number corresponding to x which is 0 here. Then it fetches one->slots[0] to get the value 10.

The following diagram illustrates the same.

Object Layout

A single type can be shared by all the objects created from the constructor Point. Even if you create a million Points, you just need a type to represent them all. Memory saving by type is just a tip of the iceberg. Tons of optimization are possible by the concept of type. I will note a bunch here:

  • Inline Cache
  • Object type specialization in JIT
  • Function specialization

Going deeper into these optimizations necessitates a separate post. First we need to understand how this type is built. Typehanlder which is part of a dynamic type handles how the property map is stored and how a brand vanilla dynamic object gets its type.

###Typehandler

Typehandler in a dynamic type serves two important goals:

  1. Maintains a property map which maps between a property and a slot number.
  2. Maintain successor types.

We have already seen that property map is to share between objects. But what is a successor type? Let us go back to an example:

var one = {};       
var two = {};       
//Objects one and two points to Type1

one.x = 10;     
//Object one points to Type2 and object two points to Type1
starwars1(one, two);

one.y = 20;     
//Object one points to Type3 and object two points to Type1
starwars2(one, two);

two.x = 40;    
//Object one points to Type3 and object two points to Type2
starwars3(one, two);

two.y = 50;  
//Object one and two points to Type3
starwars4(one, two);

The assumption here is functions starwars doesn’t mutate objects, it just accesses properties off them. It is optimal for one and two to share the exact same type when they are constructed. Both these objects end up with property x and y as well. So they can share the same type when the function starwars4 is invoked. In between these two states, we can see that objects one and two differ in what they contain. At the call site of starwars2 object one has x & y but object two has none at all. Obviously, they can’t have the same type. How do we make sure all the objects with same properties end up with the exact type if they are constructed at different point of time?

Successor type in typehanlder comes to the rescue. Each typehandler holds a successor type which tracks what new type to assign to an object if it gets a new property. This process is known as type promotion or type transition. Following diagram tries to illustrate the same.

Object Layout

When a property x is added to object with Type1, next type of the object is going to be Type2. Objects with just property x will share Type1.

As script adds properties to objects, type promotion occurs quickly as typehandler holds a pointer to the next type. The successor type is present to improve the speed of type promotion. One can build a type system without the successor type. Though for objects to share types, you will have to search through an entire set of types to figure out what is the next type your object needs to get when you set a new property.

To understand this in Chakra codebase take a look at SimpleTypeHandler. SimpleTypeHanlder has a property map named typePath in the base class which has a [tiny dictionary] (https://github.com/Microsoft/ChakraCore/blob/master/lib/Runtime/Types/TypePath.h#L73) to map a propertyId (synonymous to property name) to a slot number. It also consists of successorTypeWeakRef which holds the pointer to next type. A variant of SimpleTypeHandler known as [PathTypeHandler] (https://github.com/Microsoft/ChakraCore/blob/master/lib/Runtime/Types/PathTypeHandler.h#L243) holds map known as PropertySuccessorsMap to multiple successors.

###Summary

We looked at why objects have a type and how it shares data. We briefly looked at why typehandler stores a pointer to successor type. Note there are various other typehandlers which share data differently or not share at all. All have their own use cases for performance or memory.

In my opinion biggest advantage of type is inline cache. We will look into the inline cache in the next post.

Hope you liked this post and please post any feedback in the comments.

Written on December 22, 2015