close
The Wayback Machine - https://web.archive.org/web/20100211131517/http://blogs.msdn.com:80/jscript/
Welcome to MSDN Blogs Sign in | Join | Help

Steps Toward Creating Compatible ECMAScript 5 Implementations

As we’ve discussed in the past, Microsoft has been actively involved in the developing the specification for the next revision of the JavaScript standard, ECMAScript Fifth Edition. We expect that after ECMAScript 5’s completion and formal adoption later this year that it will be relatively quickly adopted by browser implementers as part of their ongoing release cycles.

 

When a new browser standard like this is introduced, it is important that all implementers work hard to make sure that they correctly implement the standard and are compatible with each other. Language specifications are complex technical documents and even well written specification can be misinterpreted by implementers leading to incompatibles and interoperability issues.

 

One way to avoid this is for implementers to use a common specification compliance test suite.  At the ECMA TC39 meeting this May, Microsoft announced that it was working on such test suite and made a preliminary version available to the ECMA members. We have now turned development of this test suite into a community development project hosted on Codeplex. This project is released as using the new BSD open source license and will be coordinated by TC39 members including Microsoft. Currently the test suite includes about 900 tests that mostly focus features that are new to ECMAScript 5.  This is a small fraction of the tests that will be needed to provide complete conformance coverage for the entire language.  Microsoft plans on continuing to contribute additional tests to the suite and we are working with other ECMA TC39 members to coordinate with any test suite development they may be doing.

 

Anyone who has the interest and skills for developing individual ECMAScript conformance tests are invited to participate in the project. If you’re interested check out the Codeplex site and get involved.

 

Allen Wirfs-Brock

Microsoft TC-39 Representative

Posted by GauravS | 12 Comments

Native JSON Support in IE8 and Tracking the ECMAScript Fifth Edition Draft Specification

Internet Explorer 8 was the first browser to introduce native support for encoding and decoding JSON. While we were finalizing the JSON support to be introduced in IE8, the ECMAScript Fifth Edition (ES5) Draft Specification was still under active development.

At that time, there were clearly two options in front of us:

First, to wait till the new specification is approved so that we can ship a fully compliant native JSON feature. This had a big disadvantage of not being able to provide web developers with both – performance and security benefits that native JSON offers over a script implementation of JSON in IE8 (as final approval of the standard was at least a year away).

Second, providing JSON support in IE8 based upon early drafts of the standard and then, if necessary, bringing the IE JSON support into compliance with the ES5 specification once the standard is ratified (currently expected to happen in Dec’09). This option had the disadvantage that if the specification changed after IE8 shipped there would be a period time during which the IE8 JSON support would have variances from the ES5 specification.

Weighing the two, we decided to choose the second option as it meant providing web developers the ability to start taking advantage of the native JSON constructs immediately. Moreover, the changes that were expected in the specification were not drastic and could easily be detected and worked around as needed by providing simple wrappers created in JScript itself.

Both, the candidate draft of the ES5 Specification and IE8 are now available. The list of differences that currently exist between the implementation and the draft specification is provided below with possible workarounds. It is worth noting that additional changes may still be made to the candidate ES5 specification based upon reviewer feedback. What this means is that the final form factor of JSON might be a bit different than the draft or what is covered in this article by the time ES5 is standardized.

Following is a list of differences between IE8’s behavior and the current ES5 Draft Specification of

JSON.stringify(value[,replacer[,space]])

  • If value is undefined and is not replaced using a replacer function, stringify should return undefined instead of the string "undefined".
  • If value is a function and is not replaced using a replacer function, stringify should return the value undefined rather than the string "undefined".
  • If value is a Number or String object after the toJSON() method and replacer function have been executed on value, stringify should return the primitive number or string value stored in the Number or String object.  Note: Per the latest ES5 errata (27 May’09) Boolean objects are also included in this.
  • If value is a cyclic object or array, stringify should throw a TypeError and not an Error.
  • If replacer is not a Function or an Array, stringify should ignore the replacer parameter and not throw an error.
  • If the replacer function calls a DOM method and passes the key parameter to the same, stringify should not throw an Error. Note: To work around this issue, use key.toString() when passing key to DOM methods like alert() and createElement() inside the replacer function.
  • If space is an empty string, stringify should default space to an empty string instead of newline.
  • If space is a Number object or a String object, stringify should use the primitive value of the objects instead of ignoring the same.
  • If space is set to a value greater than 100, stringify should default space to be 100 instead of the current maximum limit of 65535 after which space is defaulted to 0. Note: Per the latest ES5 errata (27 May’09) the max space should be defaulted to 10.
  • If space is a non integer numeric value, stringify should default space to be the integer part of the space value instead of defaulting it to the ceiling value of the space argument.

JSON.parse (text[, reviver])

  • If text contains control characters other than TAB, CR, or LF, throw a Syntax Error. In addition, throw a SyntaxError if TAB, CR, or LF appear in a JSON string literal. IE8 correctly rejects illegal characters outside of string literals. Note: To work around this problem for string literals, user can define a reviver method that looks for string values containing control characters. Given the performance tradeoff to find these characters in all JSON text strings, a generic reviver is not provided in the sample below.

Date.prototype.toJSON (key)

  • If the Date value is not a finite Number, toJSON should return null

Note that the majority of these differences concerning the handling of invalid argument values or other unusual situations. Most of them have no impact on the encoding or decoding of normal data.

A workaround for these differences is provided in the following patchJSON function which can be called to suitably modify the built-in JSON support methods, before using any of the JSON built-ins by a user. It places a wrapper around the built-in JSON functions.

  1. function patchJSON() {
  2.     // Check if JSON exists, or if
  3.     // JSON has already been patched or updated
  4.     if ((!this.JSON) || (JSON.stringify(undefined) !== 'undefined'))
  5.         return false;  //JSON has already been patched or updated;
  6.     var builtinStringify = JSON.stringify;
  7.     JSON.stringify = newStringify;
  8.     // Date.prototype.toJSON returns null for Dates with a non finite value
  9.     var origDateToJSON = Date.prototype.toJSON;
  10.     if ((origDateToJSON.toString()) ===
  11.     "\nfunction toJSON() {\n    [native code]\n}\n") {
  12.         Date.prototype.toJSON = function(value) {
  13.             if (isNaN(this.valueOf())) return null;
  14.             else {
  15.                 return origDateToJSON.call(this, value);
  16.             }
  17.         }
  18.     }
  19.     return true;
  20.     function newStringify(value, replacer, space) {
  21.         // Return undefined when value parameter is undefined
  22.         // or when toJSON exists and returns undefined
  23.         if ((value === undefined) && !(replacer)) return undefined;
  24.         if (value && typeof value === 'object' &&
  25.                 typeof value.toJSON === 'function' &&
  26.                 !replacer && value.toJSON("") === undefined)
  27.             return undefined;
  28.         // Return undefined when value paramenter is a function
  29.         if ((typeof value === "function") && !(replacer)) return undefined;
  30.         // Ignore the replacer and do not throw an error if the replacer
  31.         // is not a function or array.
  32.         if (replacer && (typeof replacer !== "function")
  33.             && !(replacer instanceof Array))
  34.             replacer = null;
  35.         if (space !== undefined) {
  36.             // Treat empty string as no space
  37.             if (space === "") space = 0;
  38.             else if (typeof space === "object") {
  39.                 // If space is a Number object or String object
  40.                 // use it's primitive value
  41.                 if ((space instanceof Number) || (space instanceof String))
  42.                     space = space.valueOf();
  43.                 // Ignore objects other than Number and Strings
  44.                 else space = 0;
  45.             }
  46.             // If space is set to a value greater than 10, space should be
  47.             // defaulted to 10. If space is not integral, use the floor value
  48.             // If space string has a length greater than 10,
  49.             // space should be defaulted to first 10 characters
  50.             if (typeof space === "number") space = Math.min(10,
  51.                 Math.floor(Math.abs(space)));
  52.             else if (typeof space === "string") {
  53.                 if (space.length > 10)
  54.                     space = space.slice(0, 10);
  55.             }
  56.             else space = 0;
  57.         }
  58.         // Return primitive number, string or boolean value stored
  59.         // in the Number, String or Boolean object
  60.         function newReplacer(key, value) {
  61.             // Execute the replacerand get it's return value
  62.             var tmpValue = replacer.call(this, key, value);
  63.             // If value is a Number or String or Boolean object
  64.             // use its primitive value
  65.             if (typeof tmpValue === "object") {
  66.                 if ((tmpValue instanceof Number) || (tmpValue instanceof String) ||
  67.                 (tmpValue instanceof Boolean)) tmpValue = tmpValue.valueOf();
  68.             }
  69.             return tmpValue;
  70.         }
  71.         try {
  72.             // Check if the replacer returns undefined or a function
  73.             // If it does, return undefined
  74.             var tmpValue = value;
  75.             if (replacer && typeof replacer === "function") {
  76.                 tmpValue = newReplacer.call({ "": tmpValue }, "", tmpValue);
  77.                 if ((tmpValue === undefined) ||
  78.                 (typeof tmpValue == 'function')) return undefined;
  79.                 return builtinStringify(value, newReplacer, space);
  80.             }
  81.             else return builtinStringify(value, replacer, space);
  82.         }
  83.         // Throw a Type Error if value is a cyclic objects
  84.         catch (e) {
  85.             var replacementException = e;
  86.             if (e.number === -2146823254) {
  87.                 replacementException = new TypeError();
  88.                 replacementException.description = e.description;
  89.                 replacementException.message = e.message;
  90.                 replacementException.number = e.number;
  91.             }
  92.             throw replacementException;
  93.         }
  94.     }
  95. }

Because the above patch primarily deals with errors that arise from improperly calling the JSON functions it is most useful while you are debugging your code. When you code is ready for production you may not need to use.

Note: Apart from the above differences due to the changes in the ES5 draft specifications for JSON, there is a known issue which currently exists for IE8. The details of the same along with the possible workarounds are covered in this blog post.

In case you will like to follow the discussions by the TC39 members around how the standard is taking shape, and the discussion around the changes for JSON, you can refer the discussion archives here. The latest ES5 drafts are available at the ECMAScript wiki.

Gaurav Seth, Program Manager, JScript

Serializing the value of empty DOM elements using native JSON in IE8

With native JSON support enabled in IE8, users can now take advantage of the built-in JSON.stringify and JSON.parse methods to serialize and deserialize JScript values to JSON text and vice versa. However, there is a known issue in IE8’s native JSON implementation, wherein if a user tries to read the value of an empty DOM element, and serialize the same using native JSON, the result is not the same as a user would expect while serializing "".

Here is a sample code which demonstrates the problem:

   1: var foo = document.createElement("input").value; // foo === ""
   2: var bar = ""; // bar === ""
   3:  
   4: JSON.stringify(foo); // retuns '"null"' 
   5: JSON.stringify(bar); // retuns '""' 

Another similar example is when a user serializes the value from an empty input control, as below.

   1: var foo = document.getElementById('data').value; // no value provided in the 'data' input control
   2: JSON.stringify(foo); // retuns '"null"'

In both the above cases, serialization of foo should have generated '""' instead of '"null"'. This is a bug in the production version of IE8.  The problem here is that within the DOM a special encoding is used to represent a missing string value.  Even though this special value is different from the encoding of the JScript literal "", throughout the JScript implementation the value is treated as being === to "", except for a specific case in JSON.stringify.

Since this special value only originates from accesses to DOM objects, a workaround would be to explicitly censor them on every DOM access that might return one.  For example,

   1: if (foo === "") foo = ""; //ensure that possibly bogus "" is replaced with a real ""
   2: JSON.stringify(foo); // retuns '""'

Also, since the difference is only observable via JSON.stringify, another alternative is to use the replacer function to perform the substitution. For example:

   1: JSON.stringify(foo, function(k, v) { return v === "" ? "" : v });
   2: //the above will return '""', not '"null"'

Either of the above workarounds has the disadvantage that additional code must be written each time the value of an InputElement element is accessed or each time JSON.stringify is called. Another possible workaround that avoids this extra coding is to use the IE8 Mutable DOM Prototype features to patch HTMLInputElement.prototype.value such that the undesired value is filtered out every time value is accessed. For example, consider this HMTL file:

   1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   2: <html xmlns="http://www.w3.org/1999/xhtml" >
   3: <head>
   4:     <title>Test Page</title>
   5:     <script>
   6:     if (JSON.stringify(document.createElement("input").value)==='"null"') {
   7:        //Wrapper HTMLInputElement.prototype.value with a new get accessor
   8:        //that filters special DOM empty string value 
   9:        (function() {
  10:           var builtInInputValue = 
  11:             Object.getOwnPropertyDescriptor(
  12:                   HTMLInputElement.prototype, "value").get;
  13:             Object.defineProperty(HTMLInputElement.prototype, "value",
  14:                   { get: function() {
  15:                      //Call builtin value accessor 
  16:                      var possiblyBad = builtInInputValue.call(this);
  17:                      //Replace DOM empty string with real "" 
  18:                      return possiblyBad === "" ? "" : possiblyBad;    
  19:                      }
  20:              });
  21:            })(); // call anonymous function to install the wrapper
  22:         );
  23:     </script>
  24: </head>
  25: <body>
  26:   <input type="button" value="click to test"
  27:       onclick="alert(JSON.stringify(document.createElement('input').value));" />  
  28: </body>
  29: </html>

In the <head> of this page a test is made to see if the anomalous stringify behavior is observed. If so, HTMLInputElement.prototype.value is modified to correct the problem. Any access to the value property of an Input element within the body of the page, such as the click handler that is shown, will now return the correct "" value.

This explains the anomaly in the IE8 JSON behavior while serializing the value of certain DOM elements and show three different possible workarounds to the problem.

 

Gaurav Seth, Program Manager, JScript

Posted by GauravS | 13 Comments

Versioning Language Features in JScript

With Internet Explorer 8 we introduced several new JScript language features including native JSON support and accessor methods for Mutable DOM prototypes. Of course, any new language feature introduces compatibility risk and one of the main pieces of feedback we received was that we needed to provide a smart way for developers to opt in or out of these new language features.

 

So in the JScript engine shipped as a part of Internet Explorer 8, we introduced a versioning switch which enables developers to choose the JScript language set they want to support. Web developers can opt-in/opt-out of select JScript language features by using IE8’s Document Compatibility, which depends upon the layout (or document) mode in which web content is loaded.

 

Potential Issue: Example

As an example, let’s look at IE8’s native JSON support. We added this support to the engine by adding a global JSON object with built-in parse() and stringify() methods, as per the draft ES3.1 specification, now called the ECMAScript Fifth Edition. Now, for the sake of example, let us pretend that the JScript engine did not allow language versioning and a site implemented a JSON call like this:

 

   1: if(!this.JSON) // check for non-existence of JSON object/functions

   2: {

   3:     //<define a JSON object>

   4:     //<define a non-standard compliant encoder function – JSON.encode>

   5:     //<define a non-standard compliant parser function – JSON.parse>

   6: }

   7: 

   8: //somewhere in the code

   9: JSON.encode(myObj);                         

 

For IE7, since no JSON object existed by default, the if check would evaluate to true and the user defined JSON object and encoder/decoder methods would be defined. The call to JSON.encode() would work as wanted.

 

However, for IE8, the existence of the native JSON object would cause the if check to evaluate to false and no user defined method would be available. The call to JSON.encode() would now fail, which would produce unexpected application behavior, since no such method was defined.

 

JScript Versioning in Internet Explorer 8 (for Web Developers)

Web developers can now avoid such issues by choosing an appropriate layout (or document) mode for their content. Choosing the layout mode as “Internet Explorer 8 Standards Mode” opts in to the JScript language features supported by version 5.8 JScript engine. Choosing the layout mode to be any mode other than the “Internet Explorer 8 Standards” mode, would imply an opt-in to support JScript language feature set equivalent to the one shipped in version 5.7 of the JScript engine (equivalent to the one shipped in IE7). Going back to the example above, the site owner can either update the code to start taking advantage of native JSON support and use the “Internet Explorer 8 Standards” mode, or can choose any other layout mode to use the existing code as is, thus maintaining  compatibility with IE7.

 

While a dependency on the document mode implies that developers will need to make a switch to both IE and JScript level features and can’t single out/opt-in only to the JScript changes, we did not want to introduce a new version vector specifically for the JScript engine. We wanted to support the same system used by IE8 overall for standards support.

 

You could read more about IE8’s Compatibility View feature in Scott Dicken’s blog Just The Facts: Recap of Compatibility View and the MSDN documentation for Defining Document Compatibility.

 

JScript Versioning in other JScript Hosts (for JScript Host Developers)

Apart from Internet Explorer, there are various other hosts such as Windows Script Host, CScript etc. which host the JScript engine, To avoid similar compatibility issues and enable hosts to choose a particular JScript language feature set, the JScript engine now exposes an IActiveScriptProperty::SCRIPTPROP_INVOKEVERSIONING property.

 

To opt in to a set of language features that should be supported by the JScript script engine, the host needs to invoke the IActiveScriptProperty::SetProperty and set SCRIPTPROP_INVOKEVERSIONING to one of the values below during the engine initialization:

  • 1 (or SCRIPTLANGUAGEVERSION_5_7):  To use the language features equivalent to those shipped in Version 5.7 (or IE7) of the JScript engine
  • 2 (or SCRIPTLANGUAGEVERSION_5_8):  To use the language features available in Version 5.8 (or IE8) of the JScript script engine. This includes features available in version 5.7 and the language changes introduced in version 5.8.

 

By default, the set of language features supported by the JScript script engine is set to 0 (or SCRIPTLANGUAGEVERSION_DEFAULT), equivalent to the language feature set shipped in version 5.7 of the JScript engine, unless the host chooses to support a different default behavior.                                                                                                                                                  

 

List of features versioned

Right now, only nine JScript features are versioned like this:

  • Native JSON support – This would introduce a new built-in JSON object, and the following built-in methods – JSON.parse(), JSON.stringify(), Date.toJSON(), Number.toJSON(), Boolean.toJSON() and String.toJSON()
  • Support for accessor methods for enabling Document Object Model prototypes
  • Default the separator to “,” if the separator value is ‘undefined’ in Array.prototype.join
  • Return the length of the resultant array instead of ‘undefined’ for Array.prototype.unshift
  • Call toString() if precision is ‘undefined’ instead of throwing a “RangeError” for Number.prototype.toPrecision
  • Support for array subclassing
  • Support for trailing commas in object literals
  • Implemented Error.prototype.toString to provide better error messages
  • Support for index operator [] on string values

 

I hope JScript versioning will help ensure that the IE8 end user experience is compatible with IE7 and will give developers some time to update their code to take advantage of these new language enhancements.

 

Gaurav Seth

Program Manager, JScript

A Major Milestone in JavaScript Standardization

In the world of web standards, JavaScript is known as ECMAScript and maintenance of its definition is the responsibility of the Ecma International standards organization.  The ECMAScript standard was last updated in 1999, so it is quite significant that Ecma has announced that it has completed development of a revised ECMAScript specification and is releasing it for public review and testing in anticipation of final standardization later this year. This version is named ECMAScript, Fifth Edition Candidate Specification.

 

The goal of this revision was to update the ECMAScript specification to reflect the language as it is actually implemented in modern web browsers and to establish a foundation for the future evolutions of the language.  New features include accessor properties, reflective creation and inspection of objects, program control of property attributes, additional array manipulation functions, support for the JSON object encoding format, and a strict mode that provides enhanced error checking and program security. Many of these features standardize enhanced functionality that has been provided by individual browsers but has not yet been universally adopted.

 

So, what does it mean for this to be a “candidate specification”? All the contributors to this development wanted to make sure that revising the ECMAScript specification would improve the web for developers. To this end, we all committed to not finalizing a revision unless it had first been demonstrated that the revision can be interoperably implemented by web browsers and that such implementations can continue to compatibility handle existing web content.  The “candidate specification” is the specification revision that is going to be tested against those requirements.  Having reached the “candidate specification” milestone means that all features are frozen and that we are done with specification writing. Microsoft and others are already well along with prototype implementations that will be used for testing. We all hope and expect that the testing will be successful and completed by the end of this summer so that the ECMAScript Fifth Edition can be ratified as an official web standard by the end of this year.

 

For the average web developer the release of a candidate specification has little immediate impact because you have to create content that works with the browser versions that are actually in use today.  However, we expect that once it is finally approved, the revised ECMAScript standard to be widely and fairly rapidly adopted by browsers.  In the meantime, this new specification is already having an impact.  For example, in IE8 both the native JSON and the DOM Prototypes features are based upon APIs defined in the ECMAScript Fifth Edition Specification.

 

Microsoft was deeply involved with the development of the ECMAScript, Fifth Edition specification with Pratap Lakshman and Allen Wirfs-Brock serving as project editors.  Within the context of Ecma we worked closely with colleagues from many organizations including Google, Mozilla, Yahoo!, Opera, and Apple to jointly craft a specification that should benefit the entire web development community. The release of the ECMAScript Fifth Edition Candidate Specification is an important step towards better browser interoperability using a more powerful version of JavaScript.  We are very pleased to have helped make it happen.

 

Allen Wirfs-Brock
Pratap Lakshman
Microsoft Representatives to ECMA TC-39
Co-project Editors, ECMAScript Fifth Edition Specification

Posted by GauravS | 15 Comments
Filed under: ,

What’s new in JScript for IE8

During Beta1 and Beta2 pre-releases of IE8, we’ve blogged about the performance optimizations done in the Script engine and the addition of new language features such as native JSON support. We also provided details about the JScript Debugger and the JScript Profilerthat shipped as part IE8 Developer Tools

 

One of the big pieces of feedback during our beta cycles was for compatibility, which, for JScript, meant focusing on how we version some of the language features we’re adding. As a result, version 5.8 of the JScript engine (shipped as a part of IE8) introduced an opt-in versioning mechanism for all the new and breaking language features. The JScript engine now exposes new language features only when the layout (document) mode is set to “IE8 Standards Mode.” Versioned JScript language features available in this release include native JSON support and accessor methods for enabling Document Object Model prototypes.

 

In addition to addressing bugs that were reported during the betas and our internal testing, we have added some user requested changes in the developer tools.  A more seamless debugging experience, profiling multi-frame web pages and searching the profile reports using the search bar are some of the cool features we have added.  We think developer tools will make life a whole lot easier for you web developers out there –  we would love to hear your feedback! 

 

As outlined in the article I wrote in the Code Focus Magazine during beta2, we have made more targeted performance improvements in the JScript engine and the overall AJAX browser stack to improve end-to-end performance of real world applications. On typical AJAX applications such as GMail, commonly used operations have improved over 40% over IE7. 

 

I would like to thank all of you for your support and feedback through the IE8 product development cycle and hope that you like the JScript enhancements done in IE8.

 

Shreesh Dubey

Product Unit Manager, JScript

 

Note: The comments on this blog are blocked. Pl post your comments on the IE team blog, so that they can be collated at one place.

Posted by GauravS | 1 Comments
Filed under: , , ,

Internet Explorer 8 Final Available Now

Internet Explorer 8 Final is now available in 25 languages.

IE8 makes what real people do on the web every day faster, easier, and safer. Anyone running Windows Vista, Windows XP, and Windows Server can get 32-bit and 64-bit versions now from http://www.microsoft.com/ie8. (Windows 7 users will receive an updated IE8 as part of the next Windows 7 milestone.)

 

Read more about IE8 at the IE Blog.

 

Note: The comments on this blog are blocked. Pl post your comments on the IE team blog, so that they can be collated at one place.

 

Gaurav Seth, Program Manager

JScript Team

Posted by GauravS | 1 Comments
Filed under: ,

Internet Explorer 8 Release Candidate Now Available

Internet Explorer 8 Release Candidate Is Now Available.

 

Here is post that would help you Upgrade to Internet Explorer 8 Release Candidate 1.

 

Note: The comments on this blog are blocked. Pl post your comments on the IE team blog, so that they can be collated at one place.

 

Gaurav Seth

Program Manager

JScript Team

 

JScript Debugger in Internet Explorer 8 Beta 2

In Internet Explorer 8 Beta 2 JScript team has focused on improving developer productivity. You can read 'What’s new in JScript for IE8 Beta 2' and check out the JScript PM Channel 9 video to learn more. As part of our focus on developer productivity we have continued to invest in the IE8 Developer Tools in IE8 and added a JScript Profiler to the developer tools. You can read 'Introducing the IE8 Developer Tools JScript Profiler' for more details on the profiler. The JScript profiler along with the JScript debugger we introduced in Internet Explorer Beta 1 will help you get your site working correctly and optimally.

In this blog I will give more details about the following key improvements we have made in debugger for beta 2, you can check my JScript Debugger in Internet Explorer 8 Beta 1 blog for more details on the features we released in Beta 1:

  • Improved script viewer and script explorer
  • More powerful script console
  • New execution control features
  • Better keyboard navigation and overall usability

One of the goals for the script debugger has been to make sure that debugger has minimal impact on your regular browsing. We made that possible by moving JScript engine to debug mode only when you press start debugging. With this, you no longer need to keep debugging enabled in the Internet Options and thus don’t suffer the performance hit. We also wanted to provide you lot of power even when you are not debugging and ensure that you don’t need to transition to debug mode unless you have to debug. Now with Beta 2 we have added some powerful features to make that possible.

Improvements in Script Viewer

The script viewer in Beta 1 showed all the sources when you are debugging but it only showed source for main page when not debugging. Now with Beta 2 you will be able to browse all the sources even when you are not debugging. You can also set breakpoints even before starting debugging. The breakpoints will persist till you close IE window. To make code easier to read, we have updated the source viewer to show sources in syntax color. This is similar to what you might expect in code editors such as Visual Studio.

Source viewer with syntax colored code

We have also updated the script explorer dropdown to make it easy to browse and select files. Now the script explorer will show the domain to which the script file belongs to. We have also separated the eval code, other dynamic scripts and scripts executed from Console into ‘Others’ submenu of the script explorer. With this change it will be easy for you to find the script file you are looking for.

Source explorer dropdown

Updated Script Console

Console panel in the script tab makes it easy for you to try out new scripts. You can execute the scripts in the console irrespective of whether you are debugging or not. In Beta 2 we have improved the look and feel of the Console so that it is easy to read the output.

To make it easy for you to fix errors on your page, Console also logs all the script errors. These errors will be logged in the Console once Developer Tools has been opened for the tab. You can also navigate to the error location in script by clicking on the error hyperlink in the Console.

To help you log message to Console from your script source we have added support for ‘console’ object. Developer Tools provides multiple level of logging with console.log(), console.info(), console.warn(), console.error() and console.assert(). Instead of using window.alert for debugging, you can use these console commands to log messages to the Console. You can call these console commands with a list of arguments that will be concatenated to generate the output string. The input parameters can also be formatted by using substitution patterns in the style of printf().

Console with runtime error and console.log output

You can also control what messages you want to see in the Console pane by using the context menu. Context menu also provide you option to clear the console of all messages. If you only want to clear console.log messages, you can use console.clear().

Context menu for Console

The console.log is extensible and you can add your own console commands through custom scripts. For example you can add console.debug() command which takes string arguments by using following script –

console.debug = function(){

                            var args = "";

                            for(var x=0; x<arguments.length; x++)

                            {

                              args += arguments[x];

                            }

                            console.log("DEBUG: " + args);

                          }

This can be easily enhanced to take formatted strings.

More execution control features

The Developer Tools in Beta 1 offered execution control features like Break All, Step In, Step Out, Step Over. In Beta 2 we have added support for Conditional Breakpoints and Break on Error. In conditional breakpoint you can give any valid JScript expression to be evaluated and debugger will only halt if the expression evaluates to true. Break on Error provides a toggle switch to control whether debugger will break on JScript runtime error or continue while ignoring the error. Regardless of the toggle state the error will be logged to the console so that you can investigate them at your convenience.

We have also extended the just-in-time debugging experience of Internet Explorer. Although with IE8 you don’t need to keep debugging enabled from Internet Options, still if you have debugging enabled in the IE and you hit a script error, you now get an option to use Developer Tools debugger. You can still use Visual Studio and other stand alone debuggers by unselecting the check-box in the message and clicking on Yes.

Just in time debugging dialog

Improved Usability

In Beta 2 we have added extensive keyboard shortcuts. If you have been using Visual Studio for debugging script in IE, you already know the keyboard shortcuts! We have kept the keyboard shortcuts as close to Visual Studio as possible. List of all the keyboard shortcuts supported by JScript Debugger is available here.

We have added search functionality to Developer Tools. Searching in Script tab will show you the search term highlighted in the source viewer. When you browse to a new site or select another file from the source explorer dropdown the search term will continue to be highlighted.

We have also made other improvements like increased tool tips to improve the usability and looks of the debugger.

More Information

For more information on the Developer Tools and JScript Debugger check out these articles:

We look forward to your feedback about the debugger so please leave us a comment in this post. Thanks, and enjoy IE8 Beta 2!

Deepak Jain

Program Manager

JScript Team

Allen Wirfs-Brock and Pratap Lakshman on "Harmony", ECMAScript 3.1, IE 8, and more

The 4th episode of the Open Web Podcast featured Allen, Pratap , Dion, and John discussing the Open Web, ECMAScript, IE 8 and more. A recording of the discussion has been published. Enjoy!

 

Posted by don.raman | 2 Comments

ECMAScript 3 and beyond - the road to "Harmony"

The current ECMAScript specification was completed in 1999 and certainly needed to be updated to reflect today’s web environment and practices. Some participants in the standards process wanted to use what they thought of as a once-in-a-decade opportunity to make some significant changes that they felt would make the language better. Other participants, including Microsoft, were concerned about the ability of the web to “digest” such large changes and favoured a smaller set of revisions. There were passionate and well thought opinions on both sides of these issues and everybody seemed truly motivated by what they thought would be “best for the web”.  However, the net effect of the disagreement was to block any real chance of actually achieving an approved revision to the ECMAScript specification any time soon.

 

Most of these points of disagreement have now been resolved. Rather than re-telling this story, we’d like to point  to ECMA's press release and the accompanying white paper that  lay out the agreements that have been reached in the ECMAScript technical committee. We think that these agreements set the stage for real progress in advancing standards-based web scripting and Microsoft is committed to being an active participant in making these advances. We want to thank and congratulate our colleagues at Adobe, Apple, Google, IBM, Mozilla, Opera, Yahoo!, and  other organizations for working hard to get past these differences in order to find a common path forward for ECMAScript.

 

Pratap Lakshman

Allen Wirfs-Brock

 

Posted by don.raman | 10 Comments

GC Improvements in JScript for Internet Explorer 8 Beta 1

Hello Friends,

Today I am going to talk about some of the Garbage Collector improvement we have done. Actually the original fix was done in Script 5.7 (shipped with IE7/Vista and also available on down level platforms), which we further enhanced in JScript shipped with IE8 Beta1.

So if you have gone through the Eric’s post on JScript Garbage Collector, you must have noticed following lines…

Actually what we do is keep track of the number of strings, objects and array slots allocated.  We check the current tallies at the beginning of each statement, and when the numbers exceed certain thresholds we trigger a collection. “

“However, there are some down sides as well.  Performance is potentially not good on large-working-set applications”.

The three thresholds he talked about were fixed in previous versions of JScript. After each GC cycle, counters were reset to zero. Next time when they hit the thresholds, again GC was triggered and so on.

This was alright for small scripts as they never create lot of strings and objects and don’t take much time to execute. But in modern AJAX applications, lots of objects /strings/array entries are created and they live for long enough time. Since rate of object/string/array entry creation is too high in these applications, thresholds are hit quite often, GC is triggered but not able to collect anything because things are still alive and there is no garbage. Counters are reset, but within few statements they again hit the thresholds. GC is triggered again but very less is collected and so on.

So as you see, GC is not able to collect significantly, however it is triggered at fixed intervals (as thresholds are fixed). Each GC cycle proves to be costlier than previous one as more objects have been created since last GC cycle happened.

So to fix this problem, we made the three thresholds adaptive. After each GC cycle, we check if GC was profitable or not, meaning significant collection happened or not. If it was not, then we double the thresholds. If it was, then the thresholds are not changed. Obviously there is an upper bound on value of thresholds, beyond which they are not doubled even if GC cycle was not profitable. Also if GC cycle collected everything, thresholds are set to their initial values.

That’s it for now. Hope you enjoyed reading it.

-JP

Performance Optimization of Arrays - Part II

Hello Friends,

Hope you have read part I of this topic which I posted few days ago. If not then I would suggest going through that first as this post is just a continuation of that one.

So coming to the point, here is the second reason -

2. “JavaScript arrays are sparse arrays”– JavaScript allows arrays to be sparse therefore, when you write arrayObj = new Array(100); unlike C runtime, JScript doesn’t allocate any slot/memory for those 100 entries up front. It allocates slot for an index only when an index is to be populated with a value, for example…

arrayObj = new Array(100);
 
arrayObj[10] = 10;
 

In this example, the array size is 100, but in the actual physical memory there is only one slot allocated for this array. Remaining 99 slots don’t exist at all.

Wondering how this factor contributes to the bad performance? Not allocating memory for all the slots up front is good design. Isn’t it?

Well, JScript always assumed that all arrays are sparse. So even if you had a fully dense array in your code, JScript runtime would treat it like a sparse array only. So if you are going to do a pop() operation on a dense JScript array, JScript just won’t go and delete the last indexed entry and update the length attribute. It does something which is extremely performant for sparse arrays, but equally under-performant for dense arrays. Let‘s see what it is all about.

Internally, for each Array object, JScript runtime maintains a table (different from HashTable, let’s call it TrackerTable) which is nothing but a list of pointers to actual entries in the hash table. So if you just create an Array Object of size 100, and add 10 entries (indexed or named), the TrackerTable will have 10 pointers pointing to actual entries in the hash table. Let’s take one simple example…

arrayObj = new Array(100);
 
arrayObj[10] = 10;
 
arrayObj.length = 90;
 

In this example, I just create an Array of size 100, and populated index 10. Next I reduce length of array to 90. Since the length has been reduced, entries from 90 to 99 have to be deleted. The ideal thing to do would be to delete <key, value> pair from hash table for key = 90 to 99. That means 10 operations on Hash table. JScript is smart here and saves 9 out of 10 operations.

What JScript actually does is that it goes to the TrackerTable, iterates through it, for every entry it checks if it falls between deletion range (90-99 in this case), if yes then delete it. In above example, TrackerTable had only one entry. So instead of 10 operations on hash table, JScript does only one iteration and performs better.

However what if the array was a dense array and all the indexed entries from 0 to 100 were populated? Unfortunately in this case too, JScript would follow the same logic. Therefore it would do 100 iterations over TrackerTable and end up doing 90 more operations.

How it was fixed– As I said in the last post, in IE8 JScript we have the mechanism in place to decide whether an array is a sparse or dense. So we have changed the implementation to take sparse path only when the array is actually sparse. That means for operations on dense arrays, we don’t use TrackerTable anymore. We directly go to the right storage and get the things done in fast manner.

So this is all I had to tell about array improvements. Hope you got an idea of what was/is happening under the hood? If not, do leave a note and I would try to address that. And yes, I have lot many interesting things to share with you all, so keep checking this blog.

-JP

Reading XML File With JScript

I am Titus working as a SDET in JScript team. Sometime back I came across a situation where the requirement was to pass a XML file and get a Tree Listing back. The Tree Listing should have all nodes in the file along with proper parent/child relationship as well as a good way to differentiate between nodes with/without values. Let’s call nodes with value as properties. I achieved this by using JScript. In this blog you will learn how to read/parse XML file using Microsoft’s XML DOM and use this to create the Tree Listing. 

Let’s take a sample XML file, say test.xml (can be a URL or a file on your system) to get a clear picture of the kind of Tree Listing required and later we will look at the actual code.

Image

 

The XML file can be looked as

Root Node, name is BookList, has 2 child nodes

childnode0, name is Book and has two properties,

Prop0: Author has a value Paul

Prop1: Price has a value 10.3

childnode1, name is Book and has three properties

Prop0: Author has a value Joe

Prop1: Price has a value 20.95

Prop2: Title has a value Web 2.0

The Required Tree Listing after parsing test.xml is

Image

nName

NodeName

nValue

NodeValue

cNodes

List of Child Nodes

cProps

List of Child Properties

 

The ReadXMLFile function in the code listing below returns the Tree Listing as required.

Many a times you know the XML file contents and are interested in the list of only a specific node. Making a call to ReadXMLFile with second argument as the node name gives just such a list.

Referring test.xml, a call to ReadXMLFile(“test.xml”, “Author”) gives a list like

Image

Whereas a call to ReadXMLFile(“test.xml”, “Book”), returns the list like the below oneImage

If you have carefully noticed the Tree listing, cNodes as well as cProps is an Array. so by using the proper index value, one can reach the desired node.

Here goes the actual code:

var NODE_ELEMENT = 1;
 
var NODE_ATTRIBUTE = 2;
 
var NODE_TEXT = 3;
 
/**** INTERNALLY USED FUNCTIONS ****/
 
/*
* Builds up xmlNode list on parentXMLNode
* by iterating over each node in childNodesLst
*/
 
function getXMLNodeList_1(childNodesLst,
 
parentXMLNode)
 
{
 
    var i;
 
    var curNode;
 
    var arrLen
 
    //traverse nodelist to get nodevalues and all child nodes
 
    for (i = 0; i < childNodesLst.length; i++) {
 
        //we will ignore all other node types like
 
        //NODE_ATTRIBUTE, NODE_CDATA_SECTION, …
 
        if (childNodesLst[i].nodeType == NODE_ELEMENT
 
        || childNodesLst[i].nodeType == NODE_TEXT) {
 
            if (childNodesLst[i].nodeType == NODE_TEXT) {
 
                //we got the value of the parent node, populate
 
                //parent node and return back
 
                parentXMLNode.nValue = childNodesLst[i].nodeValue;
 
                return;
 
            }
 
            //we have a new NODE_ELEMENT node
 
            curNode = new XMLNode(childNodesLst[i].nodeName, childNodesLst[i].nodeValue);
 
            if (childNodesLst[i].hasChildNodes) {
 
                getXMLNodeList_1(childNodesLst[i].childNodes, curNode);
 
                if (curNode.nValue != null) {
 
                    //we need to add this as a property to the parent node
 
                    if (parentXMLNode.cProps == null) {
 
                        parentXMLNode.cProps = new Array();
 
                        parentXMLNode.hasCProps = true;
 
                    }
 
                    arrLen = parentXMLNode.cProps.length;
 
                    parentXMLNode.cProps[arrLen] = curNode;
 
                } else {
 
                    //we need to add this as child node to the parent node
 
                    if (parentXMLNode.cNodes == null) {
 
                        parentXMLNode.cNodes = new Array();
 
                        parentXMLNode.hasCNodes = true;
 
                    }
 
                    arrLen = parentXMLNode.cNodes.length;
 
                    parentXMLNode.cNodes[arrLen] = curNode;
 
                }
 
            } else {
 
                //no use of such a node
 
                //mark currNode as null for GC collection
 
                curNode = null;
 
            }
 
        }
 
    }
 
    return;
 
}
 
/*
* Generates appropriate XMLNodeList from nodes
* in childNodes
*/
 
function getXMLNodeList(childNodes)
 
{
 
    var xmlNode = new XMLNode(null, null);
 
    getXMLNodeList_1(childNodes, xmlNode);
 
    var xmlNodeList = null;
 
    if (xmlNode.hasCNodes) {
 
        xmlNodeList = xmlNode.cNodes;
 
    } else if (xmlNode.hasCProps) {
 
        xmlNodeList = xmlNode.cProps;
 
    }
 
    return xmlNodeList;
 
}
 
/**** INTERNALLY USED FUNCTIONS ****/
 
/* XMLNde DataStruct */
 
functionXMLNode(ndName, ndVal)
 
{
 
    this.nName = ndName; //XMLNode name
 
    this.nValue = ndVal; //the value(if any) associated with XMLNode
 
    //As of now only property nodes have associated values
 
    this.hasCNodes = false; //Bool to mark presense of Child Nodes
 
    this.cNodes = null; //List of child nodes (of type XMLNode)
 
    this.hasCProps = false; //Bool to mark presense of Property Nodes
 
    this.cProps = null; //List of property nodes (of type XMLNode)
 
}
 
/* Exposed Functions */
 
function ReadXMLFile(fileName, tagName)
 
{
 
    if (arguments.length < 1 || arguments.length > 2)
 
    return null;
 
    var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
 
    //load the file sync'ly
 
    xmlDoc.async = false
 
    try {
 
        xmlDoc.load(fileName);
 
    } catch(e) {
 
        //failed to load xml file
 
        return null;
 
    }
 
    //lets get the child nodes
 
    var childNodes = null;
 
    if (arguments.length == 2) {
 
        try {
 
            childNodes = xmlDoc.getElementsByTagName(tagName);
 
        } catch(e) {
 
            return null;
 
        }
 
    } else {
 
        childNodes = xmlDoc.childNodes;
 
    }
 
    return (getXMLNodeList(childNodes));
 
}
 
var xmlNodes;
 
xmlNodes = ReadXMLFile("http://www.noweb.com/test.xml");
 
//For a file on you system
 
//xmlNodes = ReadXMLFile ("C:\\My Documents\\test.xml");
 
//root node name is
 
var RootNodeName = xmlNodes[0].nName;
 
xmlNodes = ReadXMLFile("http://www.noweb.com/test.xml", "Book");
 
var cntBooks = xmlNodes.length;
 
xmlNodes = ReadXMLFile("http://www.noweb.com/test.xml", "Author");
 
var authorName = xmlNodes[0].nValue;

Hope you enjoyed the blog!

Thanks,

Titus

Posted by JSBlog | 11 Comments
Filed under: , ,

Performance Optimization of Arrays - Part I

Hello Friends,

Have you observed better performance of Array operations in IE8 than IE7? If not, try the following code in both IE7 & IE8 and I can bet you would not leave this page without reading till the last word.

var arrObj = new Array();
var count = 10000;
var before, after;
for(var i = 0; i < count; i ++)
{
   arrObj.push(i);
}
before = new Date();
for(var i = 0; i < count; i ++)
{
    arrObj.pop();
}
after = new Date();
alert(after- before);

On my machine, IE7 took 7640 ms while IE8 took just 18 ms. As you see the above example is doing nothing but populating an array with 10000 entries and then popping them one by one, and just the pop operation is taking so much time in IE7.

This was just one example. Pick any array operation, and you would notice a huge difference between IE7 and IE8 performance.

So, won’t it be interesting to know why array operations in IE7 were taking so much time? Won’t you like to know how we dealt with all those issues? I am sure the answer is ‘yes’ and in the next few paragraphs that is what I have tried to explain. So keep reading.

Why Array operations were slow?

There were two main reasons which made Array operations so less performant. I will be explaining one of them in this post and leave other one for the next post, otherwise it would be so long to read that I would surely lose the bet.

1. JScript didn’t treat Arrays as Arrays – What does this mean? Arrays, in general sense, are considered a contiguous memory storage which enables fast random access to indexes. In JScript world, Arrays can be sparse, that means an Array can be of length 1 million but it may not have storage committed for all those 1 million indexes. But in real world scenarios, Arrays are hardly used as sparse arrays. They are mostly dense

Unfortunately JScript runtime was not handling arrays for real word usage. It always handled them as sparse arrays. Therefore it never committed any contiguous space for them, resulting into slower access to indexes.

Instead it used to insert indexed entries into a property bag which is nothing but a classic hash table whose keys are strings.

So if you are doing something like the following in your code…

var arrObj = new Array();
for (i = 0; i < 100; i ++)
arrObj[i] = i;
 

… and expecting that JScript runtime internally would allocate a C-Style contiguous array, then sorry to say, it doesn’t meet your expectation at all. Instead what it does internally is…

a) Allocates a generic object, which is nothing but the HashTable.

b) Since the generic object (HashTable) has to be treated like an array object, associate a special attribute “length” with it and set its value to 0.

c) In the for loop, for each value of i

a. Convert ‘i’ to string (e.g. for i = 10, it would be “10”)

b. Add <key value> pair of <string equivalent of i, i> (e.g. <”10” , 10>) to the hash table.

c. Set the “length“attribute’s value properly.

So every time you access an indexed entry of an array, it first converts the index to string, computes hash value for the string and then looks up the hash table.

How we fixed it

No rewards if you already guessed it.

Now JScript runtime treats an Array Object as an special object, different from other JScript objects. Basically it maintains two storage areas for arrays. One is the old HashTable, which is used for storing named entries. The other one is special one which is used exclusively for indexed entries and resembles a C-Style Array.

So if you have some code like following…

arrObj = new Array(20);
for(var i = 0; i < 20; i ++ )
    arrObj[i] = i;
    arrObj.Name = “IE8 Array”;
 

… then for loop is adding entries to a different storage and “Name” is added to the different storage.

Ok. So looks like all indexed entries always go to new storage. No. That is not the case. There is a condition which should be met before we put an indexed entry to the new storage. The condition is that new entry must not make the array sparse and to decide that whether a particular indexed entry would make the array sparse or not, we have certain heuristics, for example…

arrObj = new Array();
arrObj[10] = 20;
arrObj[50000] = 500000;

In above snippet, indexed entry 10 satisfies our heuristics and is added to new storage. But the indexed entry 50000 will not meet them and will be added to old HashTable as a normal named entry.

Cool. looks fine if you always populate the array such that it meets the heuristics, e.g. in an incrementing for loop starting from 0. But what if you want to populate it in a decrementing for loop starting from max length…

arrObj = new Array();
for(var i = 2000; i >=0 ; i -- )
  arrObj[i] = i;
 

or you want to populate the array from both ends …

arrObj = new Array();
var length = 2000;
for(var i = 0; i < length/2 ; i ++ )
{
  arrObj[i] = i;
  arrObj[length – i - 1] = length – i;
}

In such scenarios, not all of your indexed entries will go to new storage and performance of further operations on this array would not be as great as it can be.

For such scenarios, to get working in most performant way, what you have to do is to pass the actual array size to constructor when you create the object. If you pass the size, JScript runtime assumes that you are sure that your array would be dense and don’t want our heuristics to ensure that. We will not exercise those heuristics for any indexed entry added within that size range, in whatsoever order they are added. So if you write something like following…

arrObj = new Array(50000);
arrObj[10] = 20;
arrObj[50000] = 500000;

… both will go to the new storage even though the last indexed entry doesn’t satisfy heuristics. But do remember that anything beyond 50000, will have to meet the heuristics, else it would go to HashTable.

Time out. Hope you enjoyed reading it and would like to read second part as well. See you soon.

-JP

Posted by JSBlog | 13 Comments
More Posts Next page »
 
Page view tracker