Although this is difficult to set up and to maintain, a self-modifying code may be very useful to developers. In the InDesign scripting context a typical problem is to save persistent information —such as user settings— without having to use an external file or to target the script engine in a session scope. When you only need to store tiny bits of data like a counter, a time stamp or the latest state of a control, it seems more effective to take advantage of code rewriting.

Fortunately JavaScript allows you to open and (re)write “on the fly” the .js(x) file in execution —File(app.activeScript). It sounds quite paradoxal at first sight. In fact, the cache system makes this operation possible without actually giving rise to reentrant code. Let's try to understand what exactly happens:

1) The user launches a script MyScript.js from the InDesign UI.

2) InDesign reads the file MyScript.js and uploads its contents in the memory (I suppose so).

3) Then, InDesign invokes its JavaScript layer to interpret the code.

Now if the file MyScript.js is accessed by the JS code, that's not a problem because the script is actually executed from the memory and the file is free for “read/write” operations (assuming MyScript.js is not set to read only). Thus, you can rewrite or append some bytes in the file to set new values or instructions. The change will take effect next time the script is launched.

Example: Script Launch Counter

Suppose that the very first line of your code is var cpt = 0; (to the letter). The character 0 takes the 11th byte in the .js file, so we know exactly where to seek it. Using File.open('e') (edit mode), let's try to increment this byte with JavaScript:

var cpt = 0;
 
// the value of cpt is stored
// in this file at range 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("You called this script " + cpt + " times");
 
if (cpt < 5)
    {
    updateFile(cpt);
    // ...
    // your process
    // ...
    alert("Process OK");
    }
else
    {
    alert("Script disabled");
    }
 

The first time you call the script above, cpt is set to zero at the first line, then incremented (cpt++). The new value (1) is written in the script file itself using the updateFile() function. So at the end of the process the first line of the script becomes var cpt = 1; —you can check this point by opening the file in your code editor.

When you relaunch the script, cpt is initialized to 1, incremented, and so on. . . The loop keeps recurring while cpt < 5 is true. When this condition is not met, the script is disabled.

In our example the rewriting operation is limited to one character (one byte), but of course it's possible to tweak as many bytes as you need. When rewriting a fragment just make sure that you won't destroy the code syntax!

What about Binary JavaScript Files (jsxbin)?

The ExtendScript Toolkit provides the option for exporting a file as “Compiled JavaScript” (File / Export As Binary...) with the extension .jsxbin or .jsbin (Binary JavaScript File). Since a compiled JS file is encoded, the source code is not exposed. This feature lets the developer protect his/her work and prevents the end user from accidently modifying the script.

If you deploy your script over the Internet and want to implement any kind of autorewriting technique, it's absolutely ineffective to expose a simple js(x) file which could be easily modified by an advanced user. At this point, the top question is: Can a JSXBIN rewrite itself too, and how?

Let's consider this powerful script:

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

Save the code above in a test-alert.jsx, export the file as test-alert.jsxbin from the ESTK and take a look at the binary file in your text/hex editor. Here is the output generated by ESTK 3.0 (CS4):

Displaying a Binary JavaScript File in a text/hex editor.

As you can see the compiled code is quite obscure, but you don't need to be a hacker to understand that the original AAAAAAAAAA literal string has been translated into iBiBiBiBiBiBiBiBiBiB. Now replace iBiB... by iCiC... in the jsxbin file and run the modified script from InDesign. The alert will display BBBBBBBBBB. Great!

Should I go further? I don't think so. If you've followed my demonstration up to here, you know how and where to go on, don't you?