Are IJ macro variables global by default?

Hi all,

The title says it all: I thought that global variables need a var declaration, otherwise they are not modifiable within a function.
But when running this mini macro I get 6 as result but would expect 4:

i=4;
print("before:",i);
changeNumber();
print("after:",i);

function changeNumber(){
	i=6;
}

Output:

before: 4
after: 6

To be clear: I want the variable to be local. How can it be that the changeNumber does not create an internal variable i but uses the global one? Or to rephrase: How can I ensure that a variable like “i” is not being modified accidentally within a function because that function happens to use the same local name?

A related post is here but targeting the opposite question.

1 Like

Hey @noreenw,

a very interesting issue! As we had some trouble with definitions of variables in recent ImageJ/Fiji versions, I could suspect that your observation depends on the ImageJ Version. I just executed your macro on my Fiji, and it says:
image

That’s my Fiji version

On which version are you running?

Cheers,
Robert

1 Like

Thanks for checking!

And I just found the reason:

The “persistent” button in the editor is the culprit:
persist

If you have it checked in any of your open scripts, then the variables behave as global variables (in all other open macros). That’s what happened to me - I had it still checked in another macro.

Still, I find this highly unintuitive and kind of dangerous why it behaves like this.

4 Likes

This looks dangerous indeed.

This means, it’s not an ImageJ issue. It’s an issue of the Fiji Script Editor

Let’s cross check with Curtis @ctrueden: Is it an intentional feature? Should this checkbox change the behaviour of global variables? Thanks!

Also thanks @noreenw for bringing this up!

Cheers,
Robert

2 Likes

I don’t know. @albertcardona added the interpreter-related features, including the “persistent” checkbox. @albertcardona Is this how you wanted it to work?

2 Likes

All that “persistent” means in the Script Editor is that instead of creating a new blank-slate interpreter every time the script is executed, the same interpreter is reused, so that the executed code runs in the context of previously executed code.

Unchecking the checkbox should remove the persistent interpreter.

Hey @albertcardona,

thanks for the feedback! You didn’t answer our question, unfortunately. :wink: Should turning on/off this checkbox influence how global variables work?

The output of this little program changes depending on the checkbox - is this intentional? :wink:

Thanks for your support!

Cheers,
Robert

If the variables persist, because they are defined at the top-level scope, then yes: that’s the whole point, that state persists because the interpreter instance persists, i.e. is the same one at each push of “Run” or control+R.

Thanks for your explanation! But one thing stays unclear to me: Why does the function changeNumber access and modify the top-level (global) variable i if the interpreter setting is set to persistent? The function could (or in my idea should) still create a local variable i which lives within the function scope only.

3 Likes

We’ll need a macro language expert to figure this one out: @Wayne

I am not able to reproduce this problem using the following JavaScript, which runs @noreenw’s test macro twice using the same Interpreter instance.

  importPackage(Packages.ij.macro);

  macro = "i=4; print('before:',i);"
  +"changeNumber(); print('after:',i);"
  +"function changeNumber() {i=6;}";

   interp = new Interpreter();
   interp.run(macro);
   interp.run("print('persistent: '+i)"+macro);

This is the output:

before: 4
after: 4
persistent: 4
before: 4
after: 4
1 Like

Hi @wayne, thanks for looking into it. I think the issue they are referring to has to do with running macros from within the Script Editor directly.

Hi @albertcardona, how does the Script Editor run macros in “persistent” mode?

1 Like

Good question, I don’t know: generically, it runs interpreters, whatever these may be, one of them being an interpreter for macro code. In principle, if the “persistent” checkbox is ticked, it should reuse the same macro interpreter instance for subsequent runs of macro code in that Script Editor tab or prompt.

1 Like

The “persistent” checkbox gets translated to the “incremental” property of the TextEditor:

Which is then handled like this:

Anybody sees something related to global variables there?

We need to see the code that uses ij.macro.Interpreter or ij.macro.MacroRunner to run macros. I think it’s in one of the ~900 SciJava classes, but I can’t find it.

Ok, that code might live in imagej-legacy.

I think this part is relevant:

this

and this:

But it’s really not obvious where the value of the variables could have been stored…

Hi @noreenw,
Thanks to @haesleinhuepf, I see that this is a “feature” of the IJ2MacroEngine class, which the Script Editor uses to run macros. It adds ‘var’ variable declarations the second time you run a macro with “persistent” checked. Here is your macro after it has been modified by IJ2MacroEngine:

var i,; initializeSciJavaParameters(); i=4;
print("before:",i);
changeNumber();
print("after:",i);

function changeNumber(){
	i=6;
}
function initializeSciJavaParameters() {
  call("net.imagej.legacy.plugin.IJ1MacroEngine.saveInterpreter");
i = 6.0;
}
2 Likes

Thanks @Wayne for spotting this! Maybe @imagejan or @ctrueden can explain us why the IJ1MacroEngine does that and if we can disable this feature safely :upside_down_face:

https://github.com/imagej/imagej-legacy/blame/d2a00f7f8779a717eb632a7d59e1fe047a191a4a/src/main/java/net/imagej/legacy/plugin/IJ1MacroEngine.java#L138

@haesleinhuepf the code you linked is responsible for making SciJava #@ parameters available in the IJ1 macro language. It generates global variable assignments for all input parameters (that are present as “bindings” in the script engine) before script execution, then runs the macro, and reads back all macro variables in the script engine bindings after execution, so that output parameters can be processed accordingly.

I don’t know why this is causing issues when the persistent mode is on. It’s possible that the bindings from a previous run get added as macro variables that then are always global, because there’s no way to determine where they originally came from. I never used the persistent mode of the script editor, so I would have to dig as well.

2 Likes