imagej / imagej-ops

ImageJ Ops: "Write once, run anywhere" image processing
https://imagej.net/libs/imagej-ops
BSD 2-Clause "Simplified" License
89 stars 42 forks source link

Equation ops fail when called with ops.run("image.equation") #597

Closed imagejan closed 5 years ago

imagejan commented 5 years ago

The image.equation ops throw exceptions when trying to use them from scripting via the ops.run("image.equation", ...) signature, whereas calling them via ops.image().equation(...) works just fine.


In Groovy:

#@ OpService ops

ops.image().equation(ops.run("create.img", [200, 100]), {x,y -> x+y}) // works
// ops.run("image.equation", ops.run("create.img", [200, 100]), {x,y -> x+y}) // fails
Exception ```text [WARNING] null javax.script.ScriptException: :5:32 Missing space after numeric literal o = Script78$_run_closure1@500de58;; ^ in at line number 5 at column number 32 at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:470) at jdk.nashorn.api.scripting.NashornScriptEngine.asCompiledScript(NashornScriptEngine.java:496) at jdk.nashorn.api.scripting.NashornScriptEngine.compile(NashornScriptEngine.java:182) at net.imagej.ops.image.equation.DefaultEquation.compute(DefaultEquation.java:100) at net.imagej.ops.image.equation.DefaultEquation.compute(DefaultEquation.java:66) at net.imagej.ops.special.hybrid.UnaryHybridCF.run(UnaryHybridCF.java:75) at net.imagej.ops.special.hybrid.UnaryHybridCF.run(UnaryHybridCF.java:97) at org.scijava.command.CommandModule.run(CommandModule.java:199) at net.imagej.ops.OpEnvironment.run(OpEnvironment.java:950) at net.imagej.ops.OpEnvironment.run(OpEnvironment.java:136) at sun.reflect.GeneratedMethodAccessor41.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSite.invoke(PojoMetaMethodSite.java:192) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:141) at Script78.run(Script78.groovy:4) at org.scijava.plugins.scripting.groovy.GroovyScriptEngine.eval(GroovyScriptEngine.java:303) at org.scijava.plugins.scripting.groovy.GroovyScriptEngine.eval(GroovyScriptEngine.java:122) at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264) at org.scijava.script.ScriptModule.run(ScriptModule.java:160) at org.scijava.module.ModuleRunner.run(ModuleRunner.java:168) at org.scijava.module.ModuleRunner.call(ModuleRunner.java:127) at org.scijava.module.ModuleRunner.call(ModuleRunner.java:66) at org.scijava.thread.DefaultThreadService$3.call(DefaultThreadService.java:238) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: jdk.nashorn.internal.runtime.ParserException: :5:32 Missing space after numeric literal o = Script78$_run_closure1@500de58;; ^ at jdk.nashorn.internal.parser.Lexer.error(Lexer.java:1700) at jdk.nashorn.internal.parser.Lexer.scanNumber(Lexer.java:1135) at jdk.nashorn.internal.parser.Lexer.lexify(Lexer.java:1614) at jdk.nashorn.internal.parser.AbstractParser.getToken(AbstractParser.java:132) at jdk.nashorn.internal.parser.AbstractParser.nextToken(AbstractParser.java:213) at jdk.nashorn.internal.parser.AbstractParser.nextOrEOL(AbstractParser.java:170) at jdk.nashorn.internal.parser.AbstractParser.next(AbstractParser.java:157) at jdk.nashorn.internal.parser.Parser.parse(Parser.java:281) at jdk.nashorn.internal.parser.Parser.parse(Parser.java:249) at jdk.nashorn.internal.runtime.Context.compile(Context.java:1284) at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:651) at jdk.nashorn.api.scripting.NashornScriptEngine.asCompiledScript(NashornScriptEngine.java:493) ... 29 more [ERROR] null javax.script.ScriptException: :1:26 Missing space after numeric literal Script78$_run_closure1@500de58; ^ in at line number 1 at column number 26 at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:470) at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:537) at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:524) at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:402) at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:155) at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264) at net.imagej.ops.image.equation.DefaultEquation.compute(DefaultEquation.java:112) at net.imagej.ops.image.equation.DefaultEquation.compute(DefaultEquation.java:66) at net.imagej.ops.special.hybrid.UnaryHybridCF.run(UnaryHybridCF.java:75) at net.imagej.ops.special.hybrid.UnaryHybridCF.run(UnaryHybridCF.java:97) at org.scijava.command.CommandModule.run(CommandModule.java:199) at net.imagej.ops.OpEnvironment.run(OpEnvironment.java:950) at net.imagej.ops.OpEnvironment.run(OpEnvironment.java:136) at sun.reflect.GeneratedMethodAccessor41.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSite.invoke(PojoMetaMethodSite.java:192) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:141) at Script78.run(Script78.groovy:4) at org.scijava.plugins.scripting.groovy.GroovyScriptEngine.eval(GroovyScriptEngine.java:303) at org.scijava.plugins.scripting.groovy.GroovyScriptEngine.eval(GroovyScriptEngine.java:122) at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264) at org.scijava.script.ScriptModule.run(ScriptModule.java:160) at org.scijava.module.ModuleRunner.run(ModuleRunner.java:168) at org.scijava.module.ModuleRunner.call(ModuleRunner.java:127) at org.scijava.module.ModuleRunner.call(ModuleRunner.java:66) at org.scijava.thread.DefaultThreadService$3.call(DefaultThreadService.java:238) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: jdk.nashorn.internal.runtime.ParserException: :1:26 Missing space after numeric literal Script78$_run_closure1@500de58; ^ at jdk.nashorn.internal.parser.Lexer.error(Lexer.java:1700) at jdk.nashorn.internal.parser.Lexer.scanNumber(Lexer.java:1135) at jdk.nashorn.internal.parser.Lexer.lexify(Lexer.java:1614) at jdk.nashorn.internal.parser.AbstractParser.getToken(AbstractParser.java:132) at jdk.nashorn.internal.parser.AbstractParser.nextToken(AbstractParser.java:213) at jdk.nashorn.internal.parser.AbstractParser.nextOrEOL(AbstractParser.java:170) at jdk.nashorn.internal.parser.AbstractParser.next(AbstractParser.java:157) at jdk.nashorn.internal.parser.Parser.parse(Parser.java:281) at jdk.nashorn.internal.parser.Parser.parse(Parser.java:249) at jdk.nashorn.internal.runtime.Context.compile(Context.java:1284) at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:1251) at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:627) at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:535) ... 32 more ```

In Python:

#@ OpService ops
#@output result

def equation(x,y):
    return x+y

from net.imglib2.type.numeric.real import DoubleType
from net.imglib2 import FinalDimensions

result = ops.image().equation(ops.run("create.img", FinalDimensions([200, 100]), DoubleType()), equation) # works
# result = ops.run("image.equation", ops.run("create.img", FinalDimensions([200, 100]), DoubleType()), equation) # fails
Exception ```text [WARNING] null javax.script.ScriptException: :5:6 Expected an operand but found < o = ;; ^ in at line number 5 at column number 6 at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:470) at jdk.nashorn.api.scripting.NashornScriptEngine.asCompiledScript(NashornScriptEngine.java:496) at jdk.nashorn.api.scripting.NashornScriptEngine.compile(NashornScriptEngine.java:182) at net.imagej.ops.image.equation.DefaultEquation.compute(DefaultEquation.java:100) at net.imagej.ops.image.equation.DefaultEquation.compute(DefaultEquation.java:66) at net.imagej.ops.special.hybrid.UnaryHybridCF.run(UnaryHybridCF.java:75) at net.imagej.ops.special.hybrid.UnaryHybridCF.run(UnaryHybridCF.java:97) at org.scijava.command.CommandModule.run(CommandModule.java:199) at net.imagej.ops.OpEnvironment.run(OpEnvironment.java:950) at net.imagej.ops.OpEnvironment.run(OpEnvironment.java:136) at sun.reflect.GeneratedMethodAccessor83.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:188) at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:206) at org.python.core.PyObject.__call__(PyObject.java:534) at org.python.core.PyObject.__call__(PyObject.java:540) at org.python.core.PyMethod.__call__(PyMethod.java:171) at org.python.pycode._pyx16.f$0(New_.py:11) at org.python.pycode._pyx16.call_function(New_.py) at org.python.core.PyTableCode.call(PyTableCode.java:171) at org.python.core.PyCode.call(PyCode.java:18) at org.python.core.Py.runCode(Py.java:1614) at org.python.core.__builtin__.eval(__builtin__.java:497) at org.python.core.__builtin__.eval(__builtin__.java:501) at org.python.util.PythonInterpreter.eval(PythonInterpreter.java:259) at org.python.jsr223.PyScriptEngine.eval(PyScriptEngine.java:57) at org.python.jsr223.PyScriptEngine.eval(PyScriptEngine.java:31) at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264) at org.scijava.script.ScriptModule.run(ScriptModule.java:160) at org.scijava.module.ModuleRunner.run(ModuleRunner.java:168) at org.scijava.module.ModuleRunner.call(ModuleRunner.java:127) at org.scijava.module.ModuleRunner.call(ModuleRunner.java:66) at org.scijava.thread.DefaultThreadService$3.call(DefaultThreadService.java:238) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: jdk.nashorn.internal.runtime.ParserException: :5:6 Expected an operand but found < o = ;; ^ at jdk.nashorn.internal.parser.AbstractParser.error(AbstractParser.java:294) at jdk.nashorn.internal.parser.AbstractParser.error(AbstractParser.java:279) at jdk.nashorn.internal.parser.Parser.unaryExpression(Parser.java:3182) at jdk.nashorn.internal.parser.Parser.expression(Parser.java:3325) at jdk.nashorn.internal.parser.Parser.expression(Parser.java:3282) at jdk.nashorn.internal.parser.Parser.expressionStatement(Parser.java:1150) at jdk.nashorn.internal.parser.Parser.statement(Parser.java:967) at jdk.nashorn.internal.parser.Parser.statement(Parser.java:863) at jdk.nashorn.internal.parser.Parser.statementList(Parser.java:1013) at jdk.nashorn.internal.parser.Parser.getBlock(Parser.java:531) at jdk.nashorn.internal.parser.Parser.getStatement(Parser.java:555) at jdk.nashorn.internal.parser.Parser.whileStatement(Parser.java:1382) at jdk.nashorn.internal.parser.Parser.statement(Parser.java:896) at jdk.nashorn.internal.parser.Parser.sourceElements(Parser.java:773) at jdk.nashorn.internal.parser.Parser.program(Parser.java:709) at jdk.nashorn.internal.parser.Parser.parse(Parser.java:283) at jdk.nashorn.internal.parser.Parser.parse(Parser.java:249) at jdk.nashorn.internal.runtime.Context.compile(Context.java:1284) at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:651) at jdk.nashorn.api.scripting.NashornScriptEngine.asCompiledScript(NashornScriptEngine.java:493) ... 36 more [ERROR] null javax.script.ScriptException: :1:0 Expected an operand but found < ; ^ in at line number 1 at column number 0 at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:470) at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:537) at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:524) at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:402) at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:155) at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264) at net.imagej.ops.image.equation.DefaultEquation.compute(DefaultEquation.java:112) at net.imagej.ops.image.equation.DefaultEquation.compute(DefaultEquation.java:66) at net.imagej.ops.special.hybrid.UnaryHybridCF.run(UnaryHybridCF.java:75) at net.imagej.ops.special.hybrid.UnaryHybridCF.run(UnaryHybridCF.java:97) at org.scijava.command.CommandModule.run(CommandModule.java:199) at net.imagej.ops.OpEnvironment.run(OpEnvironment.java:950) at net.imagej.ops.OpEnvironment.run(OpEnvironment.java:136) at sun.reflect.GeneratedMethodAccessor83.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:188) at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:206) at org.python.core.PyObject.__call__(PyObject.java:534) at org.python.core.PyObject.__call__(PyObject.java:540) at org.python.core.PyMethod.__call__(PyMethod.java:171) at org.python.pycode._pyx16.f$0(New_.py:11) at org.python.pycode._pyx16.call_function(New_.py) at org.python.core.PyTableCode.call(PyTableCode.java:171) at org.python.core.PyCode.call(PyCode.java:18) at org.python.core.Py.runCode(Py.java:1614) at org.python.core.__builtin__.eval(__builtin__.java:497) at org.python.core.__builtin__.eval(__builtin__.java:501) at org.python.util.PythonInterpreter.eval(PythonInterpreter.java:259) at org.python.jsr223.PyScriptEngine.eval(PyScriptEngine.java:57) at org.python.jsr223.PyScriptEngine.eval(PyScriptEngine.java:31) at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264) at org.scijava.script.ScriptModule.run(ScriptModule.java:160) at org.scijava.module.ModuleRunner.run(ModuleRunner.java:168) at org.scijava.module.ModuleRunner.call(ModuleRunner.java:127) at org.scijava.module.ModuleRunner.call(ModuleRunner.java:66) at org.scijava.thread.DefaultThreadService$3.call(DefaultThreadService.java:238) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: jdk.nashorn.internal.runtime.ParserException: :1:0 Expected an operand but found < ; ^ at jdk.nashorn.internal.parser.AbstractParser.error(AbstractParser.java:294) at jdk.nashorn.internal.parser.AbstractParser.error(AbstractParser.java:279) at jdk.nashorn.internal.parser.Parser.unaryExpression(Parser.java:3182) at jdk.nashorn.internal.parser.Parser.expression(Parser.java:3282) at jdk.nashorn.internal.parser.Parser.expressionStatement(Parser.java:1150) at jdk.nashorn.internal.parser.Parser.statement(Parser.java:967) at jdk.nashorn.internal.parser.Parser.sourceElements(Parser.java:773) at jdk.nashorn.internal.parser.Parser.program(Parser.java:709) at jdk.nashorn.internal.parser.Parser.parse(Parser.java:283) at jdk.nashorn.internal.parser.Parser.parse(Parser.java:249) at jdk.nashorn.internal.runtime.Context.compile(Context.java:1284) at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:1251) at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:627) at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:535) ... 39 more ```
ctrueden commented 5 years ago

This is a problem with method dispatch from scripting. When you call ops.image().equation(...), the script language looks at the method arguments of the ImageNamespace#equation methods, and tries to do a best match based on what was actually passed.

In the case of Groovy, the expression {x,y -> x+y} is an instance of a dynamically generated class like Script16$_run_closure1, which is then (amazingly!) correctly coerced into a java.util.function.BinaryDoubleOperator. But when you use ops.run(...), Groovy has no way to know that the closure object should be coerced to anything else—it simply gets added as an element of the Object[] passed to run. And then there is no SciJava converter that knows anything about Groovy closures. But there is a SciJava converter that converts the closure to a String—and since there is an equation op that accepts String, that one gets matched by the op matcher! 😆 Hence the bizarre error: Nashorn is trying to parse the stringified Groovy closure (Script78$_run_closure1@500de58;;). I find this so entertaining it warrants a second emoji: 😂

A fix is to instead write:

ops.run("image.equation", ops.run("create.img", [200, 100]), {x,y -> x+y} as java.util.function.DoubleBinaryOperator)

The problem in Python is very likely the same issue.