Bien que difficile à mettre en place et à maintenir, un code capable de s'automodifier peut rendre d'appréciables services aux programmeurs. Dans le contexte du scripting InDesign, un problème courant est de conserver des informations persistantes, telles que les derniers réglages de l'utilisateur, sans avoir à invoquer un fichier externe ou à étendre la directive #targetengine. Lorsque vous cherchez seulement à stocker de petits volumes de données — un compteur, une date, le dernier état d'un contrôle... —, il apparaît plus simple et plus efficace d'injecter l'information et de la mettre à jour directement dans le code source.

Par chance, JavaScript vous permet d'ouvrir et de ré-écrire « à la volée » le fichier .js(x) en cours d'exécution. Cela peut paraître paradoxal à première vue. En réalité, le système de mise en cache rend cette opération possible sans occasionner véritablement une rétroaction du code. Essayons d'y voir plus clair :

1) L'utilisateur lance le script MyScript.js depuis l'interface d'InDesign.

2) InDesign lit le fichier MyScript.js et charge son contenu en mémoire (je suppose).

3) Alors, InDesign invoque sa couche JavaScript pour interpréter le code.

Dans ces conditions, si le code accède au fichier MyScript.js, ce n'est pas un problème car les instructions du script sont en réalité distillées par la mémoire de travail, le fichier lui-même restant disponible à des opérations de lecture ou d'écriture. (On suppose bien sûr que MyScript.js n'est pas configuré en lecture seule.) Par conséquent, il est possible de réécrire certains octets du fichier afin de fixer de nouveaux paramètres ou instructions. Ces changements ne prendront effet que lors du prochain appel du script.

Exemple : Mise en place d'un compteur de lancements

Supposons que la première ligne de votre code soit var cpt = 0; (à la lettre). La valeur (0) occupe le onzième octet du fichier .js, donc nous savons exactement où placer le pointeur de fichier pour opérer une réécriture. En utilisant File.open('e') (mode édition), essayons d'incrémenter cet octet en JavaScript:

var cpt = 0;
 
// la valeur de cpt est stockée
// dans ce fichier à la position 10
var cptPos = 10;
 
var updateFile = function(/*int*/n)
    { // 1 <= n <= 9
    var f = File(app.activeScript);
    if ( f.open('e') )
        {
        f.seek(cptPos);
        f.write(''+n);
        f.close();
        }
    }
 
cpt++;
alert("Vous avez appelé ce script " + cpt + " fois");
 
if (cpt < 5)
    {
    updateFile(cpt);
    // ...
    // code principal
    // ...
    alert("Procédure OK");
    }
else
    {
    alert("Script désactivé");
    }
 

La première fois que vous appelez le script ci-dessus, cpt est chargé à zéro, puis incrémenté via cpt++. La nouvelle valeur (1) est inscrite dans le fichier lui-même par l'intermédiaire de la fonction updateFile(). Ainsi, lorsque s'achève l'exécution, la première ligne du script est devenue var cpt = 1; (vous pouvez le vérifier en ouvrant le fichier dans votre éditeur de code).

Lorsqu'on relance le script, cpt est initialisé à 1, puis incrémenté, et ainsi de suite... Ce mécanisme se répète tant que cpt < 5. Lorsque cette condition n'est plus satisfaite, le script est désactivé.

Dans notre exemple, le processus de réécriture ne concerne qu'un caractère (un octet), mais il est évidemment possible de modifier autant d'octets que nécessaire. Lorsque vous réécrivez un fragment, assurez-vous seulement que vous ne détruisez pas la syntaxe JavaScript. L'exercice est quelquefois acrobatique.

Application aux fichiers « jsxbin » (Binary JavaScript)

ExtendScript Toolkit offre la possibilité d'exporter un script en « JavaScript compilé » (Fichier / Exporter en binaire...), ce qui produit un fichier avec l'extension .jsxbin ou .jsbin (Binary JavaScript File). Ce fichier binaire étant encodé dans un sabir indéchiffrable, le code source n'est plus exposé à l'utilisateur final, ce qui permet au développeur de protéger son travail et d'empêcher des modifications accidentelles.

Si vous déployez votre script sur Intenet et souhaitez implémenter une technique de réécriture de code, il est bien sûr totalement inopérant d'exposer en clair un simple fichier js(x), car tout utilisateur un tant soit peu éclairé saura modifier le code (par exemple, pour remettre un compteur à zéro). Dès lors, la question cruciale est la suivante : un script compilé peut-il également se réécrire lui-même ?

Considérons ce script surpuissant :

var s = "AAAAAAAAAA";
alert(s);
 

Sauvegardons ce code dans un fichier test-alert.jsx, exportons le script en binaire depuis l'ESTK et jetons un œil sur test-alert.jsxbin ouvert depuis un éditeur texte/hexa. Voici le résultat généré par ESTK 3.0 (CS4) :

Affichage d'un fichier binaire JavaScript (jsxbin) dans un éditeur de texte.

Vous constatez que le code compilé est globalement impénétrable, mais il n'est pas besoin d'être un pirate informatique pour comprendre que la chaîne littérale "AAAAAAAAAA" du code d'origine se traduit dans la séquence iBiBiBiBiBiBiBiBiBiB. Remplacez cette séquence par iCiCiC... et lancez le jsxbin corrigé depuis InDesign. La fonction alert affiche désormais BBBBBBBBBB. Parfait !

Dois-je aller plus loin ? Je pense que non. Si vous avez suivi la démonstration jusqu'ici, vous savez comment poursuivre l'expérience et ce que vous pourrez en tirer, n'est-ce pas ?