Matti Mäkitalo: Closure Compiler: Umbennenen von Properties von Typen verhindern

Hi,

ich stehe gerade ein wenig auf dem Schlauch und erhoffe mir ein wenig Hilfe.

Ich arbeite mit dem Closure Compiler und versuche, meinen Code mit dem Modus "ADVANCED_OPTIMIZATIONS" zu übersetzen.

Weiterhin kommuniziere ich per AJAX und bekomme von dort JSON zurück. Die Felder des daraus entstehenden JS-Objektes sind natürlicherweise nicht "obfuscated", sondern liegen im Klartext vor.

Nun ersetzt der Compiler die Aufrufe auf Felder des JS-Objekts beim kompilieren durch die Kurzschreibweise. Gibt es eine Möglichkeit, dies zu verhindern?

Mit exportSymbol kann ich m.W. nur erreichen, dass der Name eines Typs o.ä. nicht umbenannt wird, eine Lösung sehe ich darin momentan nicht.

Eine Möglichkeit, welche zwar auch technisch "sauber" ist, sich aber ein wenig unhandlich anfühlt, wäre eine "Übersetzung".

Beim callback würde ich das zu erhaltende Objekt 1:1 in ein lokales Objekt umkopieren, wobei durch den Compiler die Eigenschaften des lokalen Objekts umbenannt werden können.

/** @typedef { { id : number, name : string } } */  
example.MeinLokalerTyp;  
  
goog.net.XhrIo.send(  
  "http://www.example.org/remoteObject/123",  
  function(e) {  
     var xhr = /** @type {goog.net.XhrIo} */ (e.target);  
     var remoteObject = xhr.getResponseJSON();  
     /** @type { example.MeinLokalerTyp} */  
     var localeObject = {  
       id : remoteObject.id,  
       name : remoteObject.name  
     };  
     // further processing ...  
  }  
);

Technisch sauber deshalb, weil (s)ich das Remote-Interface weiterentwickeln könnte, so dass remoteObject und example.MeinLokalerTyp nicht mehr zueinander passen, und ich dies hier abfangen könnte.
Allerdings halte ich dies in Anbetracht der Tatsachen, dass
 (a) der Typedef eh nur ein Minimum beschreibt und ich daher serverseitig beliebige Felder hinzufügen kann sowie
 (b) ich Kontrolle sowohl über Client als auch Server-Interface habe und als wichtigstes
 (c) ich zu faul bin, jedesmal den Code zu schreiben,
für unzweckmäßig.

Wie löst ihr solche Probleme?
Gibt es vielleicht doch eine Möglichkeit, den Compiler zu hindern, alle Vorkommnisse von example.MeinLokalerTyp.* umzubenennen?

Danke für die Hilfe,
Matti

  1. Hi,

    der Fehlerteufel hast sich eingeschlichen. Das Listing müsste natürlich so lauten, damit der Compiler nicht auf remoteObject umbenennt:

         var localeObject = {  
           id : remoteObject["id"],  
           name : remoteObject["name"]  
         };
    

    Bis die Tage,
    Matti

  2. Hi,

    Ich arbeite mit dem Closure Compiler und versuche, meinen Code mit dem Modus "ADVANCED_OPTIMIZATIONS" zu übersetzen.

    Weiterhin kommuniziere ich per AJAX und bekomme von dort JSON zurück. Die Felder des daraus entstehenden JS-Objektes sind natürlicherweise nicht "obfuscated", sondern liegen im Klartext vor.

    ich habe das Problem für mich so gelöst, dass ich für jeden empfangbaren Typ einen Kopierkonstruktur schreibe, welcher die Felder umkopiert (wie in meinem zweiten Posting beschrieben einmal in Punkt-Notation und einmal in Literalschreibweise, so daß der Compiler nur die Punkt-Notation ändert).

    Ich habe mir vorgenommen, die übertragbaren Klassen automatisch generieren zu lassen, damit kann ich das wenigstens noch zu Dokumentationszwecken gebrauchen, der Mehraufwand hält sich daher in Grenzen.

    Bis die Tage,
    Matti

  3. Hohoh. Aus Interesse mal nachgefragt:

    So ganz kann ich Dir nicht folgen. Wenn Du serverseitig beliebige Daten hinzufügen willst, wie soll dann der "beliebige" Zugriff innerhalb des JS erfolgen? Entweder du reichst die Daten z.B. als dekodiertes JSON-Objekt "einfach durch", dann interessiert es den Compiler nicht. Oder aber du willst auf Details des Objekts zugreifen, dann kann es sich doch aber nur um einen festen Teil der Daten handeln, die das JS auch wirklich "kennt". Genau für den Fall bietet sich ja dann die von Dir schon genannte Schreibweise objekt['key'] an, davon lässt der Compiler per Definition die Finger.

    1. Hi,

      So ganz kann ich Dir nicht folgen. Wenn Du serverseitig beliebige Daten hinzufügen willst, wie soll dann der "beliebige" Zugriff innerhalb des JS erfolgen? Entweder du reichst die Daten z.B. als dekodiertes JSON-Objekt "einfach durch", dann interessiert es den Compiler nicht. Oder aber du willst auf Details des Objekts zugreifen, dann kann es sich doch aber nur um einen festen Teil der Daten handeln, die das JS auch wirklich "kennt". Genau für den Fall bietet sich ja dann die von Dir schon genannte Schreibweise objekt['key'] an, davon lässt der Compiler per Definition die Finger.

      öhm, gute Frage, welche ich mir bislang nicht gestellt habe.
      Hat wohl etwas mit Gewohnheit zu tun, dass ich erwarte, dass ich rein client-seitig (also in einer x-beliebigen Funktion, welche erstmal nichts mit dem Server-Interface zu tun hat, aber mit deren Daten hantiert) ein "normales" Objekt erwarte, bei dem ich "wie üblich" mit der Punkt-Notation auf Eigenschaften zugreifen kann.

      Was ich persönlich nicht tun würde, wäre, für Datenstrukturen, welche vom Server kommen (könnten) mit der []- und für reine Client-Datenstrukturen die Punkt-Notation nehmen.
      Dann müsste ich jedesmal, wenn ich irgendwo drauf zugreife, überlegen, wo die Daten herkommen, was auch potentiell fehlerträchtig ist.

      Bliebe also nur, den von dir genannten Weg zu Ende zu gehen und generell mit der Klammer-Notation zugreifen mit dem Nachteil, dass der Compiler dann wiederum die Namen nicht verkürzen kann und ich weniger Code-Einsparung habe. Ansonsten sehe ich da erstmal keine Nachteile dabei, als Vorteil hätte man, dass man den Kopierkonstruktor nicht mehr braucht.

      Was ich mir erhoffte, war, dass ich dem Compiler sagen kann: aber hier, bei diesem Typedef, da benennst du das Zeugs aber nicht um (also genau das, was die Klammer-Notation erreicht, aber so daß ich bei der Punkt-Notation bleiben kann). Da habe ich bisher keinen Weg dazu gefunden, und in Anbetracht meiner weitergehenden Überlegungen bzgl Dokumentation habe ich von der einfachen Lösung sowieso Abstand genommen.

      Wenn ich die Datenstrukturen erzeugen lasse, kann ich gleichzeitig einen Erzeuger für die Schnittstellen-Dokumentation schreiben und spare mir eben dort Arbeit.
      Serverseitig muss ich diese Datenstrukturen (derzeit) nicht erzeugen, weil diese meistens gar nicht explizit beschrieben sind*.

      *: Häufig mache ich sowas wie (in PHP, nur verkürzter Pseudocode):

        
      $dbh = mysqli_connect(/* */);  
        
      $q = $dbh->query("SELECT bla, blub, ttt FROM mytable");  
      $ret = array();  
      while ($item = $r->fetch_object()) {  
        $ret[] = $item;  
      }  
        
      echo json_encode($ret);  
      
      

      In anderen Programmiersprachen, bei denen man stdclass (oder Object, oder wie die Basisklasse eben heißt) nicht einfach bevölkern darf, müsste ich diese Datenklassen ja auch erzeugen und tlw. mit einem JSON-Serialisierer ausstatten, dies ist also kein wesentlich neues Konstrukt.

      Bis die Tage,
      Matti

      1. Was ich persönlich nicht tun würde, wäre, für Datenstrukturen, welche vom Server kommen (könnten) mit der []- und für reine Client-Datenstrukturen die Punkt-Notation nehmen.

        Damit sind "Daten von außen" aber schön als solche getagged.

        Dann müsste ich jedesmal, wenn ich irgendwo drauf zugreife, überlegen, wo die Daten herkommen, was auch potentiell fehlerträchtig ist.

        Nur wenn man die Daten blind durchreicht: fänd ich auch schlecht.

        Bliebe also nur, den von dir genannten Weg zu Ende zu gehen und generell mit der Klammer-Notation zugreifen mit dem Nachteil, dass der Compiler dann wiederum die Namen nicht verkürzen kann und ich weniger Code-Einsparung habe. Ansonsten sehe ich da erstmal keine Nachteile dabei, als Vorteil hätte man, dass man den Kopierkonstruktor nicht mehr braucht.

        Die Nachteile wiegen den kleinen Vorteil mehr als auf. Ob in einem Konstruktor oder wo auch immer: Ich würde auch stets bevorzugen, die Daten manuell in ein dem Compiler bekanntes Konstrukt zu kopieren.

        Was ich mir erhoffte, war, dass ich dem Compiler sagen kann: aber hier, bei diesem Typedef, da benennst du das Zeugs aber nicht um (also genau das, was die Klammer-Notation erreicht, aber so daß ich bei der Punkt-Notation bleiben kann).

        Letztlich macht der Compiler doch eh wieder Punkt-Notation draus ;-) Im Ernst: Wenn Du das wirklich willst, könntest Du ein Objekt als External deklarieren. Die Doku rät davon zu Recht ab, finde ich.