views:

3209

answers:

4

TL;DR: Adding any non-built-in functions to Array.prototype AND Function.prototype will cause the IE8 native JSON parser to get a stack overflow when parsing any JSON that contains an array, but only when you also pass a reviver function into JSON.parse().

This started out as a question, but I answered my own original question, so now I'll ask: can anyone think of a work-around for this IE8 bug that doesn't involve eliminating all JS libraries that modify Array.prototype and Function.prototype?

Original question:

I have about 13k of JSON data to parse. The structure of the data is an object with a single value that is a nested array.

{ 'value':[[ stuff ], [ more stuff], [ etc ]] }

I'm using json2.js, which defers to the browser native JSON.parse when available. I'm passing a reviver function into JSON.parse to handle dates properly. When IE8 is in IE7 emulation mode (which causes it to use the script-based json2.js parser) everything works fine. When IE8 is in IE8 mode (which causes it to use the browser-native JSON parser) it blows up with an "out of stack space" error. Firefox and Chrome, of course, work just fine with their browser-native JSON parsers.

I've narrowed it down to this: if I pass even a do-nothing reviver function into JSON.parse, the IE8 native parser gets the stack overflow. If I pass in no reviver function, the IE8 native parser works fine, except it doesn't parse dates correctly.

// no error:
JSON.parse(stuff);

// "out of stack space" error:
JSON.parse(stuff, function(key, val) { return val; });

I'm going to play with my JSON data, to see if less data or less nesting of the data can avoid the error, but I was wondering if anyone had seen this before, or had any other suggested work-arounds. IE8 is slow enough already, it would be a shame to disable native JSON for that browser because of this bug.

UPDATE: In other cases, with different JSON data, I'm getting a javascript error "$lineinfo is undefined" when I use the IE8 native parser with a reviver function, and no error if I use no reviver function. The string "$lineinfo" does not appear anywhere in any of my source code.

UPDATE 2: Actually, this problem seems to be caused by Prototype 1.6.0.3. I was unable to reproduce it in an isolated test page until I added in the Prototype library.

UPDATE 3:

The reason prototype.js breaks the IE8 native JSON parser is this: Adding any non-built-in functions to Array.prototype AND Function.prototype will cause the IE8 native JSON parser to get a stack overflow when parsing any JSON that contains an array, but only when you also pass a reviver function into JSON.parse().

The Prototype library adds functions to both Array.prototype and Function.prototype, but this applies equally to any other library that does the same thing. This bug in the IE JSON parser is exposed by Prototype and Ext, but not jQuery. I haven't tested any other frameworks.

Here is a completely stand-alone reproduction of the problem. If you remove the Function.prototype line, or the Array.prototype line, or remove the array from the JSON string, you won't get the "out of stack space" error.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;

<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head>
    <title></title>
<script type="text/javascript">

Function.prototype.test1 = function() { };
Array.prototype.test2 = function() { };

window.onload = function()
{
    alert(JSON.parse('{ "foo": [1,2,3] }', function(k,v) { return v; }));
}

</script>
</head>
<body>

</body>
</html>
+1  A: 

It seems to work okay here:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html>
<head>
<title>Test</title>
</head>
<body>
<pre>
<script type="text/javascript">
document.writeln('');
var o = {
    "firstName": "cyra",
    "lastName": "richardson",
    "address": {
        "streetAddress": "1 Microsoft way",
        "city": "Redmond",
        "state": "WA",
        "postalCode": 98052
    },
    "phoneNumbers": [
        "425-777-7777", 
        "206-777-7777"
     ]
};
var s = JSON.stringify(o);
document.writeln(s);
var p = JSON.parse(s, function (key, val) {
    if (typeof val === 'string') return val + '-reviver!';
    else return val;
});
dump(p);

function dump(o) {
    for (var a in o) {
        if (typeof o[a] === 'object') {
            document.writeln(a + ':');
            dump(o[a]);
        } else {
            document.writeln(a + ' = ' + o[a]);
        }
    }
}
</script>
</pre>
</body>
</html>

The problem is either a corrupt Internet Explorer 8 install (are you trying to run multiple copies of Internet Explorer on the same Windows install?) or your input is bad.

You may also want to read Native JSON in IE8. This particular paragraph may be of interest:

The optional revive argument is a user defined function used for post parse changes. The resulting object or array is traversed recursively, the reviver function is applied to every member. Each member value is replaced with the value returned by the reviver. If the reviver returns null, the object member is deleted. The traversal and the call on reviver are done in postorder. That’s right; every object is ‘revived´ after all its members are ‘revived´.

The above paragraph explains why my reviver function looks the way it does. My first attempt at test code was:

function (key, val) {
    return val + '-reviver!';
}

Obviously if this reviver function is applied to the address node above after being applied to all its children, I've completely destroyed the address object.

Of course, if the test of your reviver is as simple as you describe in your question, it is unlikely that some global circular reference is leading to the problem you're seeing. I still think it points to a broken Internet Explorer or bad data.

Can you edit your question and post a sample of actual data that exhibits the problem so I can try it here?

Grant Wagner
Thanks - it turns out Prototype is somehow the culprit. +1 for the thorough response!
Joel Mueller
+1  A: 

I've had this question with no accepted answers sitting around for quite a while, so just to get rid of it, I'll answer it myself.

Eric Law at Microsoft says:

The JavaScript team reports that this is a known issue in the JavaScript engine.

Joel Mueller
A: 

The code below works perfectly on FF, Safari and Chrome but wont work on IE8 when i updated my jquery. Dont really know what is wrong with IE. Hope someone can help. Thanks!

this function calls a php script to look for users online store the info in an array (items).

function startOnlineSession(){
$.ajax({ url: "whosonline.php?action=startonlinesession", cache: false, dataType: "json", success: function(data) {

    $("#onlinechatbox_ .onlinechatboxcontent").html('');
    $.each(data.items, function(i,item){
        if (item){ 
            if(item.on == 1){
            $("#onlinechatbox_ .onlinechatboxcontent").append('<div class="selectChat"><a title="'+item.f+'" href="javascript:void(0)" onClick="javascript:chatMe(\''+item.d+'\', \''+item.f+'\')"><img src="images/smiley_happy.gif" align="top" alt="scared smileys" border="0">&nbsp;&nbsp;<span class="selectChatTxt">'+item.str+'</span><span class="onlineTxt">Online</span></a></div>');
            }else{
            $("#onlinechatbox_ .onlinechatboxcontent").append('<div class="selectChat"><a href="#" title="'+item.f+'"><img src="images/smiley_happy.gif" align="top" alt="scared smileys" border="0">&nbsp;&nbsp;'+item.str+'<span class="offlineTxt">Offline</span></a></div>');
            }
        }
    });

}});
setTimeout('startOnlineSession();',10000);

}

andsien
Maybe you should consider posting this as a brand new question, instead of adding your question as an answer to an unrelated question.
Joel Mueller