Consider the following code:

function test(myString)
{
   myString += " World!";
   app.doScript( "alert(myString)" );
}
 
test("Hello");
 

It simply displays a “Hello World!” message. Export it into JSXBIN, it still works fine. Now, scramble the JSXBIN through JsxBlind: the resulting script no longer works and generates the message “Error String: myString is undefined”.

Where does this error come from?

(A similar puzzle would appear from an eval(…) structure.) Why is this so?

Look closely at the original code. The part "alert(myString)" (that is, the subcode to be evaluated) is passed as a literal string to app.doScript. The string in question contains the statement alert(myString) which is then interpreted within the current scope. There is no doubt that the local argument myString makes sense in this area—as it is inherited from the test function scope.


But what about the obfuscated code? As you know, JsxBlind muddles up the names of variables, arguments, constants, and even functions if you decide so. But of course it never alters literal strings, numbers, statements, syntax, etc.

So the final script—once decoded—may look like this:

function Ïሗᘉ(çѸชऔNÇ)
{
   çѸชऔNÇ += " World!";
   app.doScript( "alert(myString)" );
}
 
Ïሗᘉ("Hello");
 

That's a faithful copy of the original code, except all arbitrary identifiers have been changed. And now you can see the problem: “myString” is no longer the name of the incoming argument—which became çѸชऔNÇ—while “myString” is still present in the literal subcode "alert(myString)" assumed to denote that very argument! At the moment the statement is interpreted from within app.doScript, the identifier myString simply refers to nothing. Hence the ExtendScript error, “myString is undefined”.

This is a typical trap for all scrambling strategies. We must make sure that the stringified part of the code does not rely on identifiers to be altered in the metacode.

Workarounds

Note. — In the vast majority of cases, passing local identifiers throughout app.doScript(…) or eval(…) is bad practice and can be avoided by rethinking the code structure. This had to be said before going further.

1. Among many possible workarounds, a costly one is to stringify myArg—using $$.JSON(myArg)—and to cleverly mix the result with the subcode string. In our example, the problem is solved even more simply by just invoking myString.toSource(), since the argument is known to be a basic string:

function test(myString)
{
   myString += " World!";
   app.doScript( "alert(" + myString.toSource() + ")" );
}
 
test("Hello");
 

Now the identifier myString is free to change, so JsxBlind will no longer break the reference. (However, any serious programmer would reject this trick because it has huge security vulnerabilities.)

2. Another, more tedious solution is to explicitly register the identifier(s) that you need to keep immutable, thanks to JsxBlindLib's blackList option:

// Options object ; 2nd argument of $$.JsxBlindLib.run( )
{
   // . . .
 
   progress:     1,
   hitFuncNames: 1,
 
   // Preserve myArg, myString, etc
   blackList:    /^myArg|myString$/,
 
   // . . .
}
 

3. My favorite technique, easy to implement without the above drawbacks, is to use the callee identifier. In ExtendScript, it turns out that callee always refer to the particular function it belongs to. Since any function is an object as well, one can create a temporary property, say callee.ARG, for storing the data we need to pass to the subcode. Then callee.ARG becomes a safe specifier within the subcode scope, because JsxBlind must preserve both callee and the property name:

function test(myString)
{
  myString += " World!";
 
  callee.ARG = myString;
  app.doScript( "alert(callee.ARG)" );
  delete callee.ARG;
}
 
test("Hello");
 

Important. — Make sure you use JsxBlindLib 2.1 for testing the above code. Previous versions had a critical bug regarding reserved tokens—including callee!

What makes this method universal is, it works with any kind of argument(s)—including complex objects—and does not involve JSON. Also, it both supports app.doScript(...) and eval(...)


Links:
— The library (new release): JsxBlindLib (GitHub)
How to use it in Extenso projects
— IdExtenso: github.com/indiscripts/IdExtenso


Below is the standalone version for InDesign or ESTK: