avadan: Opera 10.50 RC Scoping/Rekursion-Bug?

Hi,

ich habe ein Problem mit einem JavaScript im neuen Opera 10.50 (Beta/RC getestet). Ich weiß, es ist noch keine Final, trotzdem wollte ich mal klären, ob ich etwas falsch verstehe oder ob es sich um einen Fehler im Opera handelt (für letzteres spräche das verhalten anderer Browser).

Nun ein einfaches Beispiel bei dem der „Fehler“ auftritt:

window.myObject = function()  
{  
    this.test = function()  
    {  
        func();  
    };  
  
    function func()  
    {  
        (function func(msg)  
        {  
            if(!msg)  
                func("this message is never displayed");  
            else  
                alert(msg);  
        })();  
    }  
};  
  
var obj = new window.myObject();  
obj.test();

Opera gibt die Meldung „this message is never displayed“ _nicht_ aus, sollte es aber m.E., stattdessen listet die Fehlerkonsole die Exception „RangeError: Maximum recursion depth exceeded“.
Liege ich mit meiner Vermutung richtig, dass es sich dabei um einen Bug (bezüglich Scoping oder rekursive Aufrufe) handelt?

Gruß

  1. Nun, in diesem Fall geht es Opera wohl um rekursives Scoping: Du definierst eine Funktion innerhalb einer Funktion neu, während Du Dich in deren Scope befindest - das impliziert einen Zugriff aus dem gerade laufenden Scope auf den gerade laufenden Scope, was zu Memory Leaks führen könnte, wenn nicht der Parser an dieser Stelle eine Exception schmeißen würde.

    Gruß, LX

    --
    RFC 1925, Satz 6a: Es ist immer möglich, einen weiteren Umweg einzufügen.
    RFC 1925, Satz 11a: Siehe Regel 6a
    1. Erstmal danke für die Antwort,

      Du definierst eine Funktion innerhalb einer Funktion neu, während Du Dich in deren Scope befindest - das impliziert einen Zugriff aus dem gerade laufenden Scope auf den gerade laufenden Scope, was zu Memory Leaks führen könnte

      Könntest du das nochmal kurz erläutern. Mir ist bekannt, dass JavaScript-Engines, die Referenzzähler für Garbage Collection benutzen, Probleme mit zyklischen Abhängigkeiten haben können; das ist hier aber nicht der Fall, soweit ich sehe.

      wenn nicht der Parser an dieser Stelle eine Exception schmeißen würde.

      Vielleicht ist das nicht klar herübergekommen, aber der Fehler stammt nicht vom Parser, sondern tritt zur Laufzeit auf.

      Noch eine Sache, die mir aufgefallen ist:
      Warum wird die Meldung angeziegt, wenn ich statt

      function func()  
          {  
              (function func(msg)  
              {  
                  if(!msg)  
                      func("this message is never displayed");  
                  else  
                      alert(msg);  
              })();  
          }

      das hier schreibe:

      function func()  
          {  
              function func(msg)  
              {  
                  if(!msg)  
                      func("this message is never displayed");  
                  else  
                      alert(msg);  
              }  
              func();  
          }

      ?

  2. Hi,

    ich habe ein Problem mit einem JavaScript im neuen Opera 10.50 (Beta/RC getestet). Ich weiß, es ist noch keine Final, trotzdem wollte ich mal klären, ob ich etwas falsch verstehe oder ob es sich um einen Fehler im Opera handelt (für letzteres spräche das verhalten anderer Browser).

    Ich habe ein ähnliches Problem, inzwischen leider mit der 10.50 Final unter Windows 7.

    Opera gibt die Meldung „this message is never displayed“ _nicht_ aus, sollte es aber m.E., stattdessen listet die Fehlerkonsole die Exception „RangeError: Maximum recursion depth exceeded“.

    Ein einfacher Test mit einer Funktion

    function rec(v) {  
    	v++;  
    	document.title = v;  
    	rec(v);  
    }
    

    die ich onload mit rec(0) aufrufe, zeigt mir als Titel des Dokumentes 21747 an, bevor Opera mit dem Fehler
    Uncaught exception: RangeError: Maximum recursion depth exceeded
    aussteigt - also erst mal nichts zu meckern, 21747 Rekursionsebenen sind wohl fur JavaScript-typische Anwendungsfälle erst mal mehr als genug.

    Die Fehlerconsole zeigt mir dann folgendes an,
    Uncaught exception: RangeError: Maximum recursion depth exceeded
    Error thrown at line 9, column 1 in rec(v) in http://test/opera_recursion_depth.htm:
        document.title = v;
    called from line 10, column 1 in rec(v) in http://test/opera_recursion_depth.htm:
        rec(v);
    called from line 10, column 1 in rec(v) in http://test/opera_recursion_depth.htm:
        rec(v);
    called from line 10, column 1 in rec(v) in http://test/opera_recursion_depth.htm:
        rec(v);
    called from line 10, column 1 in rec(v) in http://test/opera_recursion_depth.htm:
        rec(v);

    ... und noch ein paar mal mehr Wiederholungen der called-from-line-Meldung.

    Mein Script, wo das ganze problematisch wird, bearbeitet das innerHTML aller SPAN-Elemente mit einer bestimmten Klasse, im Testfall sind das nur drei oder vier Stück.
    Dieses innerHTML splitte ich mit einem regulären Ausdruck
    var parts = spanElement.innerHTML.match(/(<[^>]+>)|([^\s<>]+)|(\s+)/g)
    um Kindelement-Tags und Textknoten einzeln zu erhalten, weil ich um die Wörter im Text HTML-Elemente einfügen will. Anschliessend werden die einzelnen Treffer dieses Regxp.match-Aufrufes in einer Schleife durchlaufen, ein paar Tests mit ihnen gemacht, und dabei ggf. parts[i] mit einem neuen textNode-Element ersetzt. (textNode ist ein selbstdefiniertes JS-Objekt, dass den jeweiligen Textinhalt des Knotens im Konstruktor übergeben bekommt, und im wesentlichen nur per eigener toString-Methode dafür sorgt, dass dieser Text mit einem weiteren HTML-Tag umgeben wieder zurückgegeben wird, wenn ich nach der Verarbeitung die einzelnen Parts mit parts.join("") wieder zusammenfüge, um sie der innerHTML-Eigenschaft des SPAN-Elements wieder zuzuweisen.)
    Das hat bisher immer so funktioniert, im Opera 10.50 jetzt aber nicht mehr.

    Was mich wundert, ist dass die Meldung in der Fehlerkonsole jetzt nicht mehr mehrere Stellen auflistet, von denen (angeblich) ein rekursiver Aufruf stattfindet, wie im obigen Beispiel, sondern nur noch genau einen Aufruf auflistet:
    Uncaught exception: RangeError: Maximum recursion depth exceeded
    Error thrown at line 48, column 3 in <anonymous function: splitTextElem>(elem) in http://test/wordspace.htm:
        opera.postError(typeof bChar);
    called from line 29, column 4 in <anonymous function: init>() in http://test/wordspace.htm:
        Self.splitTextElem(spans[i]);

    Wo ich mit diesem Script also angeblich die maximale Rekursionstiefe überschreite, leuchtet mir nicht ein.
    Zumal eigentlich überhaupt keine Rekursion stattfindet - die Funktion zum Splitten des HTMLs der SPAN-Elemente wird in einer Schleife aufgerufen, aber die SPANs mit der Klasse, die den Aufruf triggert, sind nicht ineinander verschachtelt.

    Meine Vermutung ist, dass der Fehler an dieser Stelle liegt, bzw. davon ausgelöst wird:

    var spans = document.getElementById("test").getElementsByTagName("span");  
    for(var i=spans.length-1; i>=0; i--) {  
    	if(spans[i].className && spans[i].className === "line") {  
    		Self.splitTextElem(spans[i]);  
    	}  
    }
    

    Da ich beim Bearbeiten des Textinhaltes SPAN-Elemente zum innerHTML des gerade bearbeiteten SPAN-Elements hinzufüge, ändert sich natürlich der Umfang der NodeList, die getElementsByTagName mir zurückliefert, ständig (und auch in „erheblichem“ Umfang, da ich ja jedes Wort im Text mit einem neuen SPAN umkleide). Dass dies problematisch wird, wollte ich eigentlich dadurch vermeiden, dass ich mit der FOR-Schleife rückwärts durch die Elemente laufe. Ich bearbeite zuerst das x-te SPAN-Element, füge dabei einige SPAN-Elemente zu dessen innerHTML hinzu - und nehme mir danach das x-minus-ein-te SPAN-Element vor, so dass die im vorherigen Schritt neu hinzugekommmenen erst mal von der Verarbeitungslogik her keine Rolle spielen.
    Meine Vermutung ist aber, dass die Veränderung des Umfangs der NodeList, die mir getElementsByTagName("span") zurückliefert, hierbei das Problem darstellt - dass Opera damit irgendwann einfach nicht mehr klar kommt, dass diese ständig wächst, noch während ich über sie iteriere.

    Das muss ich als nächstes mal testen, ob ich den Fehler vermeiden kann, wenn ich bspw. nicht mehr alle SPAN-Elemente auswähle, sondern auf getElementsByClassName umschwenke (was ich bisher aus Gründen der Abwärts- und vor allem IE-Kompabilität vermieden habe).
    Wenn das auch nichts hilft, dann muss ich schauen, ob's was hilft, wenn ich das innerHTML der SPANs erst nach der eigentlichen Verarbeitung update (wobei ich das damit verbundene Datenvolumen, erst mal alle Inhalte in Variablen vorhalten zu müssen, eigentlich auch vermeiden wollte).

    MfG ChrisB

    --
    “Whoever best describes the problem is the person most likely to solve the problem.” [Dan Roam]