32 votes

Le bogue de JSON.parse natif d'IE8 provoque un débordement de pile

TL;DR : L'ajout de toute fonction non intégrée à Array.prototype ET Function.prototype entraînera un dépassement de pile pour l'analyseur JSON natif d'IE8 lors de l'analyse de tout JSON contenant un tableau, mais uniquement lorsque vous passez également une fonction reviver dans JSON.parse().

Il s'agissait au départ d'une question, mais j'ai répondu à ma propre question initiale, alors je la pose maintenant : quelqu'un peut-il penser à une solution de contournement pour ce bug d'IE8 qui n'implique pas l'élimination de toutes les bibliothèques JS qui modifient Array.prototype et Function.prototype ?

Question originale :

J'ai environ 13 000 données JSON à analyser. La structure des données est un objet avec une seule valeur qui est un tableau imbriqué.

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

J'utilise json2.js, qui s'en remet à la fonction JSON.parse native du navigateur lorsqu'elle est disponible. Je passe une fonction reviver dans JSON.parse pour gérer correctement les dates. Lorsque IE8 est en mode d'émulation IE7 (ce qui l'amène à utiliser le parseur json2.js basé sur script), tout fonctionne bien. Lorsque IE8 est en mode IE8 (ce qui lui fait utiliser l'analyseur JSON natif du navigateur), il explose avec une erreur "out of stack space". Firefox et Chrome, bien sûr, fonctionnent très bien avec leurs analyseurs JSON natifs.

J'ai réduit le problème à ceci : si je passe ne serait-ce qu'une fonction reviver do-nothing dans JSON.parse, le parseur natif d'IE8 obtient le dépassement de pile. Si je ne passe aucune fonction reviver, l'analyseur natif d'IE8 fonctionne bien, sauf qu'il n'analyse pas les dates correctement.

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

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

Je vais jouer avec mes données JSON, pour voir si moins de données ou moins d'imbrication des données peuvent éviter l'erreur, mais je me demandais si quelqu'un avait déjà vu cela auparavant, ou avait d'autres suggestions de contournement. IE8 est déjà assez lent, il serait dommage de désactiver le JSON natif pour ce navigateur à cause de ce bug.

MISE À JOUR : Dans d'autres cas, avec des données JSON différentes, j'obtiens une erreur javascript "$lineinfo is undefined" lorsque j'utilise le parseur natif d'IE8 avec une fonction reviver, et aucune erreur si je n'utilise pas de fonction reviver. La chaîne "$lineinfo" n'apparaît nulle part dans mon code source.

MISE À JOUR 2 : En fait, ce problème semble être causé par Prototype 1.6.0.3. Je n'ai pas pu le reproduire dans une page de test isolée jusqu'à ce que j'ajoute la bibliothèque Prototype.

UPDATE 3 :

La raison pour laquelle prototype.js casse l'analyseur JSON natif d'IE8 est la suivante : L'ajout de toute fonction non intégrée à Array.prototype ET Function.prototype entraînera un dépassement de pile pour l'analyseur JSON natif d'IE8 lors de l'analyse de tout JSON contenant un tableau, mais uniquement si vous passez également une fonction reviver dans JSON.parse().

La bibliothèque Prototype ajoute des fonctions à Array.prototype et Function.prototype, mais cela s'applique également à toute autre bibliothèque qui fait la même chose. Ce bogue dans l'analyseur JSON d'IE est exposé par Prototype et Ext, mais pas par jQuery. Je n'ai pas testé d'autres frameworks.

Voici une reproduction complètement autonome du problème. Si vous supprimez la ligne Function.prototype, ou la ligne Array.prototype, ou si vous supprimez le tableau de la chaîne JSON, vous n'obtiendrez pas l'erreur "out of stack space".

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

<html xmlns="http://www.w3.org/1999/xhtml">
<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>

6voto

Luke Points 2389

5voto

Anders Points 1

Une solution consiste à supprimer le JSON.parse natif d'IE8 et à le remplacer par le JSON.parse de l'interface utilisateur. json2.js lib :

Ajouter :

<script type="text/javascript">
if (jQuery.browser.msie && jQuery.browser.version.indexOf("8.") === 0) {
    if (typeof JSON !== 'undefined') {
        JSON.parse = null;
    }
}
<script>

et ensuite inclure :

<script type="text/javascript" src="json2.js"></script>

Cela déclenchera json2 pour remplacer le JSON.parse avec sa propre version

// json2.js
...
if (typeof JSON.parse !== 'function') {
    JSON.parse = function (text, reviver) {
...

Après cela, l'analyse devrait fonctionner à nouveau.

Un problème avec cette approche est que la méthode d'analyse de json2.js est plus lente que la méthode native.

3voto

Joel Mueller Points 14985

J'ai cette question sans réponse acceptée depuis un certain temps, alors pour m'en débarrasser, je vais y répondre moi-même.

Eric Law de Microsoft dit :

L'équipe JavaScript signale qu'il s'agit d'un problème connu dans le moteur JavaScript.

2voto

Grant Wagner Points 14085

Cela semble fonctionner correctement ici :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<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>

Le problème vient soit d'une installation corrompue d'Internet Explorer 8 (essayez-vous d'exécuter plusieurs copies d'Internet Explorer sur la même installation Windows ?), soit d'une mauvaise saisie.

Vous pouvez également lire JSON natif dans IE8 . Ce paragraphe particulier peut être intéressant :

L'option relancer le débat est une fonction définie par l'utilisateur définie par l'utilisateur utilisée pour les changements. L'objet ou le tableau résultant est parcouru de manière récursive, le raviveur est appliquée à chaque membre. La valeur de chaque membre est remplacée par la retournée par la fonction raviveur . Si le reviver retourne null, l'objet est supprimé. La traversée et l'appel au reviver sont effectués en ordre postérieur. C'est vrai, chaque objet est 'revivifié'. après tous ses membres sont ' ranimé '.

Le paragraphe ci-dessus explique pourquoi ma fonction reviver se présente comme elle le fait. Ma première tentative de code de test était :

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

Évidemment, si cette raviveur est appliquée à la fonction address ci-dessus après avoir été appliqué à tous ses enfants, j'ai complètement détruit le noeud address objet.

Bien sûr, si le test de votre raviveur est aussi simple que vous le décrivez dans votre question, il est peu probable qu'une référence circulaire globale soit à l'origine du problème que vous rencontrez. Je continue de penser qu'il s'agit d'un Internet Explorer défectueux ou de mauvaises données.

Pouvez-vous modifier votre question et de poster un échantillon de données réelles qui présentent le problème afin que je puisse l'essayer ici ?

2voto

James Hutchison Points 13

Une extension de ce problème (qui est toujours présent dans IE9), est la fonction native JSON.stringify plante IE quand il y a :

  1. un grand graphique d'objets
  2. le graphe d'objets fait référence aux objets "data" de jQuery.
  3. le graphe des objets est circulaire.

Nous ne sommes pas sûrs du point spécifique qui cause le crash.

Dans ce cas, nous avons utilisé une fonction de remplacement de la fonction stringify pour renvoyer un résultat nul sur une propriété d'objet particulière et arrêter le parcours du graphe d'objets.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X