diff --git a/src/main/java/org/dynjs/jsr223/DynJSCompiledScript.java b/src/main/java/org/dynjs/jsr223/DynJSCompiledScript.java new file mode 100644 index 000000000..8899acb62 --- /dev/null +++ b/src/main/java/org/dynjs/jsr223/DynJSCompiledScript.java @@ -0,0 +1,39 @@ +package org.dynjs.jsr223; + +import org.dynjs.Config; +import org.dynjs.runtime.DynJS; +import org.dynjs.runtime.JSProgram; +import org.dynjs.runtime.Types; +import org.dynjs.runtime.builtins.DynJSBuiltin; + +import javax.script.CompiledScript; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptException; + +/** + * @author Bob McWhirter + */ +public class DynJSCompiledScript extends CompiledScript { + + private final DynJSScriptEngine engine; + private final JSProgram program; + + DynJSCompiledScript(DynJSScriptEngine engine, JSProgram program) { + this.engine = engine; + this.program = program; + } + + @Override + public Object eval(ScriptContext context) throws ScriptException { + ScriptEngineGlobalObject global = RuntimeHelper.getGlobalObject( context ); + DynJS runtime = RuntimeHelper.getRuntime( global, context ); + return runtime.newRunner().withSource( this.program ).execute(); + } + + @Override + public ScriptEngine getEngine() { + return this.engine; + } + +} diff --git a/src/main/java/org/dynjs/jsr223/DynJSScriptContext.java b/src/main/java/org/dynjs/jsr223/DynJSScriptContext.java deleted file mode 100644 index 2247e35d3..000000000 --- a/src/main/java/org/dynjs/jsr223/DynJSScriptContext.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.dynjs.jsr223; - -import org.dynjs.Config; -import org.dynjs.runtime.DynJS; - -import javax.script.Bindings; -import javax.script.ScriptContext; -import javax.script.ScriptEngine; -import javax.script.SimpleBindings; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.io.Writer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * @author Bob McWhirter - */ -public class DynJSScriptContext implements ScriptContext { - - private final static List SCOPES; - - static { - SCOPES = Collections.unmodifiableList(new ArrayList() {{ - add( ScriptContext.ENGINE_SCOPE ); - add( ScriptContext.GLOBAL_SCOPE ); - }}); - } - - private final DynJSScriptEngine engine; - private final ScriptEngineGlobalObject global; - private Writer writer; - private Writer errorWriter; - private Reader reader; - - public DynJSScriptContext(DynJSScriptEngine engine) { - this.engine = engine; - this.writer = new OutputStreamWriter(System.out); - this.errorWriter = new OutputStreamWriter(System.err); - this.reader = new InputStreamReader(System.in); - - this.global = new ScriptEngineGlobalObject( this ); - } - - ScriptEngineGlobalObject getGlobalObject() { - return this.global; - } - - DynJSScriptEngine getEngine() { - return engine; - } - - @Override - public void setBindings(Bindings bindings, int scope) { - if (scope == ScriptContext.GLOBAL_SCOPE) { - this.engine.getFactory().setGlobalBindings(bindings); - return; - } else if (scope == ScriptContext.ENGINE_SCOPE) { - this.global.setBindings( bindings ); - return; - } - - throw new IllegalArgumentException("Invalid scope: " + scope); - } - - @Override - public Bindings getBindings(int scope) { - if (scope == ScriptContext.GLOBAL_SCOPE) { - return this.engine.getFactory().getGlobalBindings(); - } else if (scope == ScriptContext.ENGINE_SCOPE) { - return this.global.getBindings(); - } - - throw new IllegalArgumentException("Invalid scope: " + scope); - } - - @Override - public void setAttribute(String name, Object value, int scope) { - if (scope == ScriptContext.GLOBAL_SCOPE) { - this.engine.getFactory().getGlobalBindings().put(name, value); - return; - } else if (scope == ScriptContext.ENGINE_SCOPE) { - this.global.getBindings().put(name, value); - return; - } - - throw new IllegalArgumentException("Invalid scope: " + scope); - } - - @Override - public Object getAttribute(String name, int scope) { - if (scope == ScriptContext.GLOBAL_SCOPE) { - return this.engine.getFactory().getGlobalBindings().get(name); - } else if (scope == ScriptContext.ENGINE_SCOPE) { - return this.global.getBindings().get(name); - } - throw new IllegalArgumentException("Invalid scope: " + scope); - } - - @Override - public Object removeAttribute(String name, int scope) { - if (scope == ScriptContext.GLOBAL_SCOPE) { - return this.engine.getFactory().getGlobalBindings().remove(name); - } else if (scope == ScriptContext.ENGINE_SCOPE) { - return this.global.getBindings().remove(name); - } - - throw new IllegalArgumentException("Invalid scope: " + scope); - } - - @Override - public Object getAttribute(String name) { - Object value = getAttribute(name, ScriptContext.ENGINE_SCOPE); - if (value == null) { - value = getAttribute(name, ScriptContext.GLOBAL_SCOPE); - } - return value; - } - - @Override - public int getAttributesScope(String name) { - if (getAttribute(name, ScriptContext.ENGINE_SCOPE) != null) { - return ScriptContext.ENGINE_SCOPE; - } - - if (getAttribute(name, ScriptContext.GLOBAL_SCOPE) != null) { - return ScriptContext.GLOBAL_SCOPE; - } - - return -1; - } - - @Override - public Writer getWriter() { - return this.writer; - } - - @Override - public Writer getErrorWriter() { - return this.errorWriter; - } - - @Override - public void setWriter(Writer writer) { - this.writer = writer; - } - - @Override - public void setErrorWriter(Writer writer) { - this.errorWriter = writer; - } - - @Override - public Reader getReader() { - return this.reader; - } - - @Override - public void setReader(Reader reader) { - this.reader = reader; - } - - @Override - public List getScopes() { - return SCOPES; - } -} diff --git a/src/main/java/org/dynjs/jsr223/DynJSScriptEngine.java b/src/main/java/org/dynjs/jsr223/DynJSScriptEngine.java index 24a70ed6d..1acc6f868 100644 --- a/src/main/java/org/dynjs/jsr223/DynJSScriptEngine.java +++ b/src/main/java/org/dynjs/jsr223/DynJSScriptEngine.java @@ -1,9 +1,11 @@ package org.dynjs.jsr223; import org.dynjs.Config; -import org.dynjs.runtime.DynJS; -import org.dynjs.runtime.Types; +import org.dynjs.runtime.*; +import org.dynjs.runtime.Compiler; import org.dynjs.runtime.builtins.DynJSBuiltin; +import org.dynjs.runtime.linker.DynJSBootstrapper; +import org.dynjs.runtime.linker.java.jsimpl.JSJavaImplementationManager; import javax.script.*; import java.io.Reader; @@ -11,149 +13,109 @@ /** * @author Bob McWhirter */ -public class DynJSScriptEngine implements ScriptEngine { +public class DynJSScriptEngine extends AbstractScriptEngine implements Compilable, Invocable { private final DynJSScriptEngineFactory factory; - private DynJSScriptContext context; - public DynJSScriptEngine(DynJSScriptEngineFactory factory) { + DynJSScriptEngine(DynJSScriptEngineFactory factory) { this.factory = factory; - this.context = new DynJSScriptContext(this); + this.context = new SimpleScriptContext(); + this.context.setBindings(factory.getGlobalBindings(), ScriptContext.GLOBAL_SCOPE); } @Override - public Object eval(String script, ScriptContext context) throws ScriptException { - return eval( script, context, null ); + public DynJSCompiledScript compile(String script) throws ScriptException { + Compiler compiler = new DynJS().newCompiler(); + JSProgram program = compiler.withSource(script).compile(); + return new DynJSCompiledScript(this, program); } @Override - public Object eval(Reader reader, ScriptContext context) throws ScriptException { - return eval( reader, context, null ); + public DynJSCompiledScript compile(Reader script) throws ScriptException { + Compiler compiler = new DynJS().newCompiler(); + JSProgram program = compiler.withSource(script).compile(); + return new DynJSCompiledScript(this, program); } @Override - public Object eval(String script) throws ScriptException { - return eval( script, this.context, null ); + public Object eval(String script, ScriptContext context) throws ScriptException { + DynJSCompiledScript program = compile(script); + return program.eval(context); + + //ScriptEngineGlobalObject global = getGlobalObject( context ); + //DynJS runtime = getRuntime( global, context ); + //return runtime.newRunner().withSource( script ).execute(); } @Override - public Object eval(Reader reader) throws ScriptException { - return eval( reader, this.context, null ); + public Object eval(Reader reader, ScriptContext context) throws ScriptException { + DynJSCompiledScript program = compile(reader); + return program.eval(context); } @Override - public Object eval(String script, Bindings n) throws ScriptException { - return eval( script, this.context, n ); + public Bindings createBindings() { + return new SimpleBindings(); } @Override - public Object eval(Reader reader, Bindings n) throws ScriptException { - return eval( reader, this.context, n ); + public ScriptEngineFactory getFactory() { + return this.factory; } - protected Object eval(String script, ScriptContext context, Bindings bindings) throws ScriptException { - if ( ! ( context instanceof DynJSScriptContext ) ) { - throw new IllegalArgumentException( "context must be an instance of " + DynJSScriptContext.class.getName() ); + @Override + public Object invokeMethod(Object thiz, String name, Object... args) throws ScriptException, NoSuchMethodException { + if (!(thiz instanceof JSObject)) { + throw new IllegalArgumentException("'this' should be an instance of " + JSObject.class.getName()); } - DynJSScriptContext dynjsContext = (DynJSScriptContext) context; - ScriptEngineGlobalObject global = getGlobalObject(dynjsContext, bindings); - DynJS runtime = getRuntime( global, dynjsContext ); + ScriptEngineGlobalObject global = RuntimeHelper.getGlobalObject(this.context); + DynJS runtime = RuntimeHelper.getRuntime(global, this.context); - try { - return runtime.newRunner().withSource(script).execute(); - } catch (Exception t) { - throw new ScriptException(t); - } - } + Object property = ((JSObject) thiz).get(runtime.getDefaultExecutionContext(), name); - protected Object eval(Reader reader, ScriptContext context, Bindings bindings) throws ScriptException { - if ( ! ( context instanceof DynJSScriptContext ) ) { - throw new IllegalArgumentException( "context must be an instance of " + DynJSScriptContext.class.getName() ); + if (property == Types.UNDEFINED) { + throw new NoSuchMethodException("No such property '" + name + "'"); } - DynJSScriptContext dynjsContext = (DynJSScriptContext) context; - ScriptEngineGlobalObject global = getGlobalObject(dynjsContext, bindings); - DynJS runtime = getRuntime( global, dynjsContext ); - - try { - return runtime.newRunner().withSource(reader).execute(); - } catch (Exception t) { - throw new ScriptException(t); + if (!(property instanceof JSFunction)) { + throw new ScriptException("Property '" + name + "' is not a function"); } - } - private ScriptEngineGlobalObject getGlobalObject(DynJSScriptContext context, Bindings localBindings) { - if ( localBindings == null ) { - return context.getGlobalObject(); - } + JSFunction function = (JSFunction) property; - return new ScriptEngineGlobalObject( context.getGlobalObject(), localBindings ); - } - - private DynJS getRuntime(ScriptEngineGlobalObject global, DynJSScriptContext context) { - Object builtin = global.get(null, "dynjs"); - - DynJS runtime = null; - - if ( builtin != Types.UNDEFINED ) { - runtime = ((DynJSBuiltin) builtin).getRuntime(); - } - - runtime = new DynJS( new Config(), global ); - - Object argv = context.getAttribute( ScriptEngine.ARGV ); - if ( argv != null ) { - if ( ! argv.getClass().isArray() ) { - throw new IllegalArgumentException( "ARGV must be an array" ); - } - runtime.getConfig().setArgv((Object[]) argv); - } - - return runtime; + return runtime.getDefaultExecutionContext().call(function, thiz, args); } @Override - public void put(String key, Object value) { - getBindings( ScriptContext.ENGINE_SCOPE ).put( key, value ); - } + public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException { + ScriptEngineGlobalObject global = RuntimeHelper.getGlobalObject(this.context); - @Override - public Object get(String key) { - return getBindings( ScriptContext.ENGINE_SCOPE ).get( key ); + return invokeMethod(global, name, args); } @Override - public Bindings getBindings(int scope) { - return this.context.getBindings( scope ); - } + public T getInterface(Class clasz) { + ScriptEngineGlobalObject global = RuntimeHelper.getGlobalObject(this.context); - @Override - public void setBindings(Bindings bindings, int scope) { - this.context.setBindings( bindings, scope ); + return getInterface(global, clasz); } @Override - public Bindings createBindings() { - return new SimpleBindings(); - } + public T getInterface(Object thiz, Class clasz) { + if (!(thiz instanceof JSObject)) { + throw new IllegalArgumentException("'this' should be an instance of " + JSObject.class.getName()); + } - @Override - public ScriptContext getContext() { - return this.context; - } + ScriptEngineGlobalObject global = RuntimeHelper.getGlobalObject(this.context); + DynJS runtime = RuntimeHelper.getRuntime(global, this.context); - @Override - public void setContext(ScriptContext context) { - if ( ! ( context instanceof DynJSScriptContext ) ) { - throw new IllegalArgumentException( "Context must be " + DynJSScriptContext.class.getName() ); - } - this.context = (DynJSScriptContext) context; - } + JSJavaImplementationManager manager = DynJSBootstrapper.JAVA_IMPLEMENTATION_MANAGER; - @Override - public DynJSScriptEngineFactory getFactory() { - return this.factory; + try { + return ((T) manager.getImplementationWrapper(clasz, runtime.getDefaultExecutionContext(), (JSObject) thiz)); + } catch (Exception e) { + return null; + } } - } diff --git a/src/main/java/org/dynjs/jsr223/DynJSScriptEngineFactory.java b/src/main/java/org/dynjs/jsr223/DynJSScriptEngineFactory.java index ee7ddf077..90a363c93 100644 --- a/src/main/java/org/dynjs/jsr223/DynJSScriptEngineFactory.java +++ b/src/main/java/org/dynjs/jsr223/DynJSScriptEngineFactory.java @@ -37,10 +37,6 @@ public DynJSScriptEngineFactory() { this.globalBindings = new SimpleBindings(); } - void setGlobalBindings(Bindings bindings) { - this.globalBindings = bindings; - } - Bindings getGlobalBindings() { return this.globalBindings; } diff --git a/src/main/java/org/dynjs/jsr223/RuntimeHelper.java b/src/main/java/org/dynjs/jsr223/RuntimeHelper.java new file mode 100644 index 000000000..caf4141ad --- /dev/null +++ b/src/main/java/org/dynjs/jsr223/RuntimeHelper.java @@ -0,0 +1,50 @@ +package org.dynjs.jsr223; + +import org.dynjs.Config; +import org.dynjs.runtime.DynJS; +import org.dynjs.runtime.Types; +import org.dynjs.runtime.builtins.DynJSBuiltin; + +import javax.script.ScriptContext; +import javax.script.ScriptEngine; + +/** + * @author Bob McWhirter + */ +public class RuntimeHelper { + + private static final String GLOBAL_OBJECT = "org.dynjs.global-object"; + + public static ScriptEngineGlobalObject getGlobalObject(ScriptContext context) { + ScriptEngineGlobalObject global = (ScriptEngineGlobalObject) context.getAttribute(GLOBAL_OBJECT); + + if( global == null ) { + global = new ScriptEngineGlobalObject(context); + context.setAttribute( GLOBAL_OBJECT, global, ScriptContext.ENGINE_SCOPE ); + } + + return global; + } + + public static DynJS getRuntime(ScriptEngineGlobalObject global, ScriptContext context) { + Object builtin = global.get(null, "dynjs"); + + DynJS runtime = null; + + if ( builtin != Types.UNDEFINED ) { + runtime = ((DynJSBuiltin) builtin).getRuntime(); + } else { + runtime = new DynJS(new Config(), global); + } + + Object argv = context.getAttribute( ScriptEngine.ARGV ); + if ( argv != null ) { + if ( ! argv.getClass().isArray() ) { + throw new IllegalArgumentException( "ARGV must be an array" ); + } + runtime.getConfig().setArgv((Object[]) argv); + } + + return runtime; + } +} diff --git a/src/main/java/org/dynjs/jsr223/ScriptEngineGlobalObject.java b/src/main/java/org/dynjs/jsr223/ScriptEngineGlobalObject.java index 6a1bf6f96..9d74b7805 100644 --- a/src/main/java/org/dynjs/jsr223/ScriptEngineGlobalObject.java +++ b/src/main/java/org/dynjs/jsr223/ScriptEngineGlobalObject.java @@ -6,44 +6,30 @@ import javax.script.Bindings; import javax.script.ScriptContext; -import javax.script.SimpleBindings; /** * @author Bob McWhirter */ public class ScriptEngineGlobalObject extends DynObject { - private final DynJSScriptContext context; - private Bindings bindings; + private final ScriptContext context; - public ScriptEngineGlobalObject(DynJSScriptContext context) { + public ScriptEngineGlobalObject(ScriptContext context) { this.context = context; - this.bindings = new SimpleBindings(); } public ScriptEngineGlobalObject(ScriptEngineGlobalObject parent, Bindings bindings) { super( parent ); this.context = parent.context; - this.bindings = bindings; - } - - Bindings getBindings() { - return this.bindings; - } - - void setBindings(Bindings bindings) { - this.bindings = bindings; } @Override public Object get(ExecutionContext context, String name) { Object value = super.get( context, name ); if ( value == null || value == Types.UNDEFINED ) { - value = this.bindings.get( name ); - if ( value == null ) { - value = this.context.getEngine().getFactory().getGlobalBindings().get(name); - } + value = this.context.getAttribute( name ); } + if ( value == null ) { return Types.UNDEFINED; } @@ -52,34 +38,19 @@ public Object get(ExecutionContext context, String name) { } @Override - public boolean hasProperty(ExecutionContext context, String name) { - boolean has = super.hasProperty( context, name ); - - if ( has ) { - return true; - } - - has = this.context.getEngine().getFactory().getGlobalBindings().containsKey( name ); + public Object put(String key, Object value) { + return super.put(key, value); + } - if ( has ) { + @Override + public boolean hasProperty(ExecutionContext context, String name) { + if ( super.hasProperty( context, name ) ) { return true; } - return this.bindings.containsKey( name ); + return ( this.context.getAttributesScope( name ) >= 0 ); } - /* - @Override - public void put(ExecutionContext context, String name, Object value, boolean shouldThrow) { - if ( this.bindings.containsKey( name ) ) { - this.bindings.put( name, value ); - } else if ( this.context.getEngine().getFactory().getGlobalBindings().containsKey( value ) ) { - this.context.getEngine().getFactory().getGlobalBindings().put( name, value ); - } else { - super.put( context, name, value, shouldThrow ); - } - } - */ } diff --git a/src/main/java/org/dynjs/runtime/DeclarativeEnvironmentRecord.java b/src/main/java/org/dynjs/runtime/DeclarativeEnvironmentRecord.java index b2aaca873..8284ad11f 100644 --- a/src/main/java/org/dynjs/runtime/DeclarativeEnvironmentRecord.java +++ b/src/main/java/org/dynjs/runtime/DeclarativeEnvironmentRecord.java @@ -34,7 +34,6 @@ public void createMutableBinding(final String name, final boolean configurable) @Override public void setMutableBinding(ExecutionContext context, String name, Object value, boolean strict) { - // 10.2.1.1.3 if (!hasBinding(context, name)) { throw new AssertionError("10.2.1.1.3: No binding exists for " + name); diff --git a/src/main/java/org/dynjs/runtime/linker/DynJSBootstrapper.java b/src/main/java/org/dynjs/runtime/linker/DynJSBootstrapper.java index 123439d68..0c52f7f4f 100644 --- a/src/main/java/org/dynjs/runtime/linker/DynJSBootstrapper.java +++ b/src/main/java/org/dynjs/runtime/linker/DynJSBootstrapper.java @@ -44,13 +44,15 @@ public class DynJSBootstrapper { public static DynJSCoercionMatrix COERCION_MATRIX; + public static JSJavaImplementationManager JAVA_IMPLEMENTATION_MANAGER; + static { try { LinkLogger logger = new NullLinkLogger(); ShadowObjectLinker shadowLinker = new ShadowObjectLinker(logger); - JSJavaImplementationManager implementationManager = new JSJavaImplementationManager(shadowLinker); - COERCION_MATRIX = new DynJSCoercionMatrix(implementationManager); + JAVA_IMPLEMENTATION_MANAGER = new JSJavaImplementationManager(shadowLinker); + COERCION_MATRIX = new DynJSCoercionMatrix(JAVA_IMPLEMENTATION_MANAGER); ResolverManager manager = new ResolverManager(COERCION_MATRIX); // LinkLogger logger = new FileLinkLogger("dynjs-linker.log"); @@ -68,7 +70,7 @@ public class DynJSBootstrapper { LINKER.addLinker(cacheable(new GlobalLinker(logger))); LINKER.addLinker(new JSJavaBoundMethodLinker(logger, manager)); - LINKER.addLinker(new JSJavaImplementationLinker(implementationManager, logger)); + LINKER.addLinker(new JSJavaImplementationLinker(JAVA_IMPLEMENTATION_MANAGER, logger)); LINKER.addLinker(new JSJavaClassPropertyLinker(logger, manager)); LINKER.addLinker(new JSJavaClassMethodLinker(logger, manager)); diff --git a/src/test/java/org/dynjs/jsr223/ScriptEngineTest.java b/src/test/java/org/dynjs/jsr223/ScriptEngineTest.java index a32a67364..617df4c3b 100644 --- a/src/test/java/org/dynjs/jsr223/ScriptEngineTest.java +++ b/src/test/java/org/dynjs/jsr223/ScriptEngineTest.java @@ -1,7 +1,9 @@ package org.dynjs.jsr223; +import org.dynjs.runtime.JSObject; import org.junit.Before; import org.junit.Test; +import static org.junit.Assert.fail; import javax.script.*; @@ -12,12 +14,12 @@ */ public class ScriptEngineTest { - private ScriptEngine engine; + private DynJSScriptEngine engine; @Before public void setUpEngine() { ScriptEngineManager manager = new ScriptEngineManager(); - this.engine = manager.getEngineByName( "dynjs" ); + this.engine = (DynJSScriptEngine) manager.getEngineByName( "dynjs" ); } @Test @@ -73,7 +75,7 @@ public void testEvalOverrideEngineState() throws ScriptException { override.put( "foo", "tacos" ); engine.getBindings(ScriptContext.GLOBAL_SCOPE ).put( "foo", 44L ); - engine.getBindings(ScriptContext.ENGINE_SCOPE ).put( "foo", 46L ); + engine.getBindings(ScriptContext.ENGINE_SCOPE ).put("foo", 46L); Object result = engine.eval( "foo", override ); @@ -89,4 +91,81 @@ public void testArgv() throws ScriptException { Object result = engine.eval( "dynjs.argv[1]" ); assertThat( result ).isEqualTo( "two" ); } + + @Test + public void testInvokeMethod() throws ScriptException, NoSuchMethodException { + Object o = engine.eval( "var o = { foo: function(arg) { return arg+42; }, bar: 84 }; o;"); + assertThat( o ).isNotNull(); + assertThat( o ).isInstanceOf( JSObject.class ); + + Object result = engine.invokeMethod( o, "foo", 14 ); + assertThat( result ).isEqualTo( 56L ); + + try { + engine.invokeMethod( o, "bar" ); + fail("should have thrown ScriptException"); + } catch (ScriptException t) { + // expected and correct + } + + try { + engine.invokeMethod(o, "noSuch"); + fail( "should have thrown NoSuchMethodException" ); + } catch (NoSuchMethodException e) { + // expected and correct + } + } + + @Test + public void testInvokeFunction() throws ScriptException, NoSuchMethodException { + engine.eval( "function foo(arg) { return 42+arg; }; var bar = 99;"); + + Object result = engine.invokeFunction( "foo", 12 ); + assertThat( result ).isEqualTo( 54L ); + + try { + engine.invokeFunction( "bar" ); + fail( "Should have thrown ScriptException"); + } catch (ScriptException e) { + // expected and correct + } + + try { + engine.invokeFunction( "noSuch" ); + fail("should have thrown NoSuchMethodException"); + } catch (NoSuchMethodException e) { + // expected and correct + } + } + + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + + public static interface Thingy { + int foo(); + String bar(); + } + + @Test + public void testGetInterface() throws ScriptException { + engine.eval( "function foo() { return 42; }; function bar() { return 'taco'; }" ); + + Thingy thingy = engine.getInterface( Thingy.class ); + + assertThat( thingy.foo() ).isEqualTo( 42 ); + assertThat( thingy.bar() ).isEqualTo( "taco" ); + } + + @Test + public void testGetInterfaceWithThis() throws ScriptException { + Object o = engine.eval( "var o = { foo: function() { return 99; }, bar: function() { return 'bob'; } }; o;"); + + assertThat( o ).isInstanceOf( JSObject.class ); + + Thingy thingy = engine.getInterface( o, Thingy.class ); + + assertThat( thingy.foo() ).isEqualTo( 99 ); + assertThat( thingy.bar() ).isEqualTo( "bob" ); + + } }