Can bsh.jar be updated to latest version?

BeanShell has been updated to version 2.0b6, while Fiji is still shipping with 2.0b4 (from 2006). The latter version has bugs with classes using inheritance and static methods (read: doesn’t work), but those issues (seem) fixed in the latest version. Could bsh.jar be updated in the update site?

This seems reasonable… it looks to be actively maintained.

I made a branch to try it out and it works nicely. They added their own script engine which is great.

My only slight concern is an apparent change in behavior of handling unset variables from "void" to null.

@ctrueden any objections?

1 Like

Awesome!

At a glance, I think that change is actually good. I am not super worried about it.

Uploaded to the Java-8 update site. Thanks for pointing this out!

1 Like

It is great to see Beanshell not being abandoned, and I’m really happy for the update. However 2.0b6 is likely to brake most BeanShell scripst out there (it did with ALL of mine).

Prior to 2.0b6, this used to work as expected:

import ij.ImagePlus;
import ij.WindowManager;

imp = WindowManager.getCurrentImage();
if (imp==null)
	IJ.error("Nothing to process");
else
	ip = imp.getImageProcessor();

With the new 2.0b6, you get the following error when attempting to run the snippet above:

javax.script.ScriptException: Sourced file: eval stream : Attempt to resolve method: getImageProcessor() on undefined variable or class name: imp

There seem to be two ways around it.

  1. Check for void when assessing null
  2. Use the @parameter notation

On the first:

import ij.ImagePlus;
import ij.WindowManager;

imp = WindowManager.getCurrentImage();
if (imp==void || imp==null)
	IJ.error("Nothing to process");
else
	ip = imp.getImageProcessor();

I though that the best way would be to have a method that checks for unset variables:

/** Checks if an object remains undefined */
boolean unset(Object o) {
	return o==void || o ==null;
}

imp = WindowManager.getCurrentImage();
if (unset(imp))
	IJ.error("Nothing to process");

However, that does not seem to work either, and I am not sure why, as I get the same javax.script.ScriptException error.

Otherwise using the @parameter notation seems to avoid most issues:

// @ImagePlus imp
imp = WindowManager.getCurrentImage();
ip = imp.getImageProcessor();

@hinerm, @ctrueden, Is this correct, or am I missing something? I was going to mention this in the Scripting page, but thought that perhaps you guys could provide more knowledgable insights?

Hi @tferr! First of all, thanks for testing this stuff. It’s great to have the feedback :slight_smile:

So I think this actually is not a problem with BeanShell 2.0b6. When running your snippet from the script editor, I get the following error:

 Method getImageProcessor() not found in class'ij.ImagePlus' 

which is actually correct, because the method is just getProcessor(). I assume that this was an API change in ImageJ 1.x that coincides with your update?

With that in mind, the following non-parameterized script works for me:


import ij.IJ;
import ij.WindowManager;

imp = WindowManager.getCurrentImage();
if (imp==null)
    IJ.error("Nothing to process");
else
    IJ.log(imp.getProcessor().toString());

As does the parameterized version:

// @ImagePlus imp

import ij.IJ;

if (imp==null)
    IJ.error("Nothing to process");
else
    IJ.log(imp.getProcessor().toString());

Unfortunately if it’s a backwards-incompatible change in API there’s not much you can do but update your scripts. :frowning2:

If you run into more problems, or if I missed anything here, please let us know!

No just me typing in haste, without checking it. (sorry about that :blush:) meant getProcessor() from the beginning. Sorry about that. But that mistake apart, things still do not work here when I run your snippet in an updated Fiji installation

import ij.IJ;
import ij.WindowManager;

imp = WindowManager.getCurrentImage();
if (imp==null)
    IJ.error("Nothing to process");
else
    IJ.log(imp.getProcessor().toString());

and get the following error:

Started New_.bsh at Thu Mar 31 15:28:06 EDT 2016
javax.script.ScriptException: Sourced file: eval stream : Attempt to resolve method: getProcessor() on undefined variable or class name: imp : at Line: 8 : in file: eval stream : imp .getProcessor ( ) 
 in eval stream at line number 8
	at bsh.engine.BshScriptEngine.evalSource(BshScriptEngine.java:93)
	at bsh.engine.BshScriptEngine.eval(BshScriptEngine.java:52)
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:249)
	at org.scijava.script.ScriptModule.run(ScriptModule.java:174)
	at org.scijava.module.ModuleRunner.run(ModuleRunner.java:167)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:126)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:65)
	at org.scijava.thread.DefaultThreadService$2.call(DefaultThreadService.java:191)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

I’ll double check in a clean install, and report back. Maybe I have fiddle with my installation somehow and forgot about it. Thanks for looking into it. Really appreciated.

Nope, you’re right. The script does fail if you don’t have an image open.

if (imp==null)

always returns false, because imp is now void.

So you are correct, scripts are broken. You would need to update null checks to:

if (imp==void)

If you use @Parameters you could remove the null/void checks completely…

What is odd is that I thought this logic was covered here

Yes… weird… Do you know why this does not seem to work also?

/** Checks if an object remains undefined */
boolean unset(Object o) {
	return o==void || o ==null;
}

imp = WindowManager.getCurrentImage();
if (unset(imp))
	IJ.error("Nothing to process");

Amusingly it looks like you can’t pass an undefined variable as a method parameter…

Ah also I just realized that the decode method I linked to is probably only applied to the output of the script and not called on each individual variable lookup. So things that call beanshell scripts don’t have to differentiate null and void, but internally the script does need to consider the difference (which is reasonable since they are part of the language).

@tferr actually the I can’t find a way to actually create null values, or any mention of null in the current BeanShell docs.

So in scripts it may be enough to just forget null completely and solely check for void.

@hinerm I am sorry I missed this horrible issue when reviewing the update to BeanShell. IIRC, you specifically mentioned the changeover to void and I didn’t understand the ramifications then.

Personally, I would call this new behavior a critical bug in BeanShell. The thing is that BeanShell is intended to be able to evaluate Java code verbatim, and has been very useful for that for many years. This change from null to void is a catastrophic breakage when viewed from that perspective.

So… I guess the next step is to ask the BeanShell dev(s) whether this change is intentional—in which case we may want to downgrade ImageJ back to the previous version—or unintentional, in which case we can investigate a bug-fix and then upgrade again once it is released.

EDIT: Actually, I notice that we moved away from our own BeanshellScriptEngine in favor of 2.0b6’s BshScriptEngine as well. So my first cut at fixing this will be to restore our custom ScriptEngine and see if that is enough to fix things. If I’m still stumped, I’ll file an issue at beanshell/beanshell.

@ctrueden, thanks for looking into it. I am actually surprised nobody else came across it yet. Meanwhile I’ve just been edited my scripts to check also for void every time null is evaluated. I guess it is a good suggestion no matter how the issue will be solved? I’ll try to add to the wiki later today.

In case it helps, I just found that this actually works:

boolean unset(Object o) {
	return o==void || o ==null;
}

ij.IJ.doCommand("Close All");
if (unset(ij.WindowManager.getCurrentImage()))
	ij.IJ.error("Nothing to process");

What does not work is the following:


boolean unset(Object o) {
	return o==void || o ==null;
}

ij.IJ.doCommand("Close All");
imp = ij.WindowManager.getCurrentImage();
if (unset(imp))
	ij.IJ.error("Nothing to process");

Here I haven’t notice much changing with this bsh-2.0b6 update. This seems to work as expected:


Object nullObject() {
	return null;
}

ij.IJ.doCommand("Close All");
if (ij.WindowManager.getCurrentImage()== nullObject())
	ij.IJ.error("Nothing to process");
1 Like

Please hold off—I have a fix that will keep us at 2.0b6 while restoring the old sane null behavior. You shouldn’t have to worry about void in your scripts.

I’m pushing the updates out now—should have things uploaded within the next 30 minutes I hope?

Unfortunately, I want to hold off on uploading the fix until early next week.

Rationale: I released scripting-beanshell version 0.3.1 which fixes the problem. It relies on scijava-common version 2.53.1 (also already released). But that now depends on scijava-expression-parser (SJEP) version 3.0.0 which is a breaking change from version 2.x. The imagej-ops project needs a new release to upgrade to that version of SJEP. I have the work done on a branch, but it is not merged yet.

I will try to get everything taken care of either this weekend or on Monday. Apologies for the delay.

I am cutting releases for components across the entire SciJava software stack. So it’s taken a bit longer, but there will be a whole bunch of bug-fixes rolling out very soon—hopefully this afternoon (U.S. time).