December 15th, 2014
ECMAScript Evolution to Meet the Advancing Web
The web has evolved substantially in the 5 years since ES5 was ratified. Apps today are becoming more complex and demanding. Microsoft, working with the ECMA working group – TC39, has been hard at work on the next substantial update to the language. The 6th edition of ECMA-262 (ES6) is perhaps the biggest update to the language in its history.
The order we implement ECMAScript proposals depends on a few principle including whether it is necessary to ensure interoperability with other engines, enables new types of apps, provides developers with compelling value beyond saving keystrokes, has a stable specification has multiple reference implementations, and has a comprehensive test suite (called Test262) to ensure interoperability. Overall we do our best to make data driven decisions and deliver features that will give the most value to developers. Let us know what you find most valuable on User Voice or Connect!
Now that we’ve seen a simple example of what the syntax looks like, let’s have a look at the other syntactic features of ES6 classes.
The example includes a basic class construct with instance, static, getter and setter methods. A class constructor is a special method which can be customized or omitted. Omitting a constructor generates the default constructor which merely forwards arguments to its super as shown above. Classes provide inheritance through the extends keyword. A subclass has the super keyword, which allows use of the superclass properties and methods.
When implementing new language features, syntactic sugar features often map directly to older language constructs, as shown in the first code example. Classes are no exception. Underneath, classes use the same function, prototype, and instance constructs that have always been available, and Chakra uses these constructs to implement classes.
Not all of the sugar maps to pre-existing language features, however. The super keyword is one example. super ended up taking longer than the rest of classes due to its complex usage scenarios. These uses are not always apparent to the end user, but part of the fun of being a compiler developer is to come up with the corner cases that break things. For example, the use of eval() in a class makes the implementation more complex:
In the example above, both Child methods require a reference to Parent to allow execution of super(). In the case of method(), the parser is able to detect the use of super() at compile time. Chakra marks method() as having a super keyword reference during parsing, so when it reaches bytecode generation it knows to generate special bytecode to make a reference to Parent available. At runtime, this reference becomes available as Chakra consumes the bytecode at the beginning of the function, performing checks on whether it is valid to use super at that location and point in time.
In the case of methodEval(), there is a call to eval() which drastically changes the implementation. Chakra knows this is a class method, but until it executes the eval(), it has no idea if there is a super reference, or if there will ever be a super reference. This poses a design problem that we felt had two options:
- Should Chakra maintain a reference to Parent that is available in case it is needed by super somewhere inside the eval(), or
- Should Chakra wait until it is somewhere in the eval() to fetch the reference?
The first option, pre-emptively loading super, adds unnecessary bytecode to the method which could adversely affect performance.
The second option, to fetch the Parent reference when Chakra needs it, sounds simple at first. However it can’t predict the complexity of the eval(). For example, Chakra could be several levels deep in function calls in the eval(), or perhaps be in several levels of the eval() itself. Finding the Parent reference at this point could be more costly than if it had been stashed it away previously.
There isn’t necessarily a correct answer here, and the answer may change as we learn more about how programmers use super. In the current technical preview, we implemented super by pre-emptively loading the reference in situations where the trade-off in performance is justifiable. For example, using super in a subclass method is more likely to happen than in a top level class method (regardless of whether the use is valid or invalid.)
In the time since the release of the technical preview, the ES6 specification has been updated to allow super in more places. Being on the bleeding edge of implementation as standards evolve often means revisiting implementations, and the ES6 specification is no different.
Moving forward with ECMAScript
While ECMAScript 6 makes great progress, both the runtime capabilities and expressivity will continue to evolve. ECMAScript 7 should arrive more rapidly than any previous ECMAScript version because it and future editions of the standard will be moving to a more regular “train model”. In this model, proposals are built in isolation from one another and move through stages of ratification as they mature. Eventually, a proposal may make it on to a “train”, where the proposal is integrated with the other proposals on the train to form the final document that will be ratified. This model is useful for language standards because we get cohesive, complete specifications developed in an agile fashion that we don’t have to wait years for. It also gives some flexibility to make sure we get a design right. It’s a lot easier to take a bit more time when it means shipping in the specification next year rather than five years from now. For example, when the committee got substantive negative feedback on the subclassing built-ins machinery, it was relatively easy to pull it out of ES6 and get it ready for making the ES7 train. (You may also be interested to know that we were analyzing a subclassable built-ins implementation to complement classes, but as a result of the change we could not include it in this release.)
Classes are available today in the current Windows 10 Technical Preview, and you can also play with them using remote.modern.ie. We can’t wait to hear about how you will be using the feature in your ES6 code. We’re planning to deliver more articles in the future about other ES6 features. In the meantime, feel free to join the comment discussion, reach out on Twitter @IEDevChat, or on Connect.
Tom Care, Brian Terlson (@bterlson) & Suwei Chen