From 9a3f6161ce02c59a654cb4e3f2bd7a4e1b730851 Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Mon, 1 Jul 2013 05:51:27 +0000 Subject: [PATCH] Fix pluggable scripting engine lookup, simplify implementation, and add unit tests. --- README.html | 3 +- examples/jse/ScriptEngineSample.java | 11 +- .../org/luaj/vm2/script/LuaScriptEngine.java | 178 ++++++------- src/jse/org/luaj/vm2/script/LuajBindings.java | 211 ---------------- src/jse/org/luaj/vm2/script/LuajContext.java | 17 -- test/junit/org/luaj/vm2/AllTests.java | 7 +- .../luaj/vm2/script/ScriptEngineTests.java | 237 ++++++++++++++++++ 7 files changed, 343 insertions(+), 321 deletions(-) delete mode 100644 src/jse/org/luaj/vm2/script/LuajBindings.java create mode 100644 test/junit/org/luaj/vm2/script/ScriptEngineTests.java diff --git a/README.html b/README.html index 5b15dbd9..2f97658b 100644 --- a/README.html +++ b/README.html @@ -301,7 +301,7 @@ The standard use of JSR-223 scripting engines may be used:
 	ScriptEngineManager mgr = new ScriptEngineManager();
-	ScriptEngine e = mgr.getEngineByExtension(".lua");
+	ScriptEngine e = mgr.getEngineByName("luaj");
 	e.put("x", 25);
 	e.eval("y = math.sqrt(x)");
 	System.out.println( "y="+e.get("y") );
@@ -846,6 +846,7 @@ Files are no longer hosted at LuaForge.
 
   3.0-beta1
 
diff --git a/examples/jse/ScriptEngineSample.java b/examples/jse/ScriptEngineSample.java
index 5dc75e63..f77ce9c1 100644
--- a/examples/jse/ScriptEngineSample.java
+++ b/examples/jse/ScriptEngineSample.java
@@ -15,8 +15,6 @@ import javax.script.SimpleBindings;
 
 import org.luaj.vm2.LuaValue;
 import org.luaj.vm2.lib.OneArgFunction;
-import org.luaj.vm2.script.LuaScriptEngine;
-import org.luaj.vm2.script.LuajBindings;
 
 public class ScriptEngineSample {
     
@@ -27,8 +25,7 @@ public class ScriptEngineSample {
     	// System.setProperty("luaj.debug", "true");
 
         ScriptEngineManager sem = new ScriptEngineManager();
-        ScriptEngine e = sem.getEngineByExtension(".lua");
-        e = new LuaScriptEngine();
+        ScriptEngine e = sem.getEngineByName("luaj");
         ScriptEngineFactory f = e.getFactory();
 
         // uncomment to enable the lua-to-java bytecode compiler 
@@ -73,8 +70,8 @@ public class ScriptEngineSample {
             System.out.println( "y="+b1.get("y") );
             System.out.println( "y="+b2.get("y") );
             
-            // In Luaj 3.0, client bindings must derive from LuajBindings. 
-            Bindings sb = new LuajBindings();
+            // In Luaj 3.0, client bindings can just be SimpleBindings. 
+            Bindings sb = new SimpleBindings();
             sb.put("x", 2);
             System.out.println( "eval: "+cs.eval(sb) );
 
@@ -134,7 +131,7 @@ public class ScriptEngineSample {
     	testBindings(e, e.createBindings());
     }
     public static void testClientBindings(ScriptEngine e) throws ScriptException {
-    	testBindings(e, new LuajBindings());
+    	testBindings(e, new SimpleBindings());
     }
     public static void testBindings(ScriptEngine e, Bindings b) throws ScriptException {
         CompiledScript cs = ((Compilable)e).compile(
diff --git a/src/jse/org/luaj/vm2/script/LuaScriptEngine.java b/src/jse/org/luaj/vm2/script/LuaScriptEngine.java
index 400d570d..e726caea 100644
--- a/src/jse/org/luaj/vm2/script/LuaScriptEngine.java
+++ b/src/jse/org/luaj/vm2/script/LuaScriptEngine.java
@@ -22,9 +22,13 @@
 package org.luaj.vm2.script;
 
 import java.io.*;
+
 import javax.script.*;
 
 import org.luaj.vm2.*;
+import org.luaj.vm2.lib.ThreeArgFunction;
+import org.luaj.vm2.lib.TwoArgFunction;
+import org.luaj.vm2.lib.jse.CoerceJavaToLua;
 
 /**
  * Implementation of the ScriptEngine interface which can compile and execute
@@ -37,7 +41,7 @@ import org.luaj.vm2.*;
  * and for client bindings use the default engine scoped bindings or
  * construct a {@link LuajBindings} directly.
  */
-public class LuaScriptEngine implements ScriptEngine, Compilable {
+public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngine, Compilable {
     
 	private static final String __ENGINE_VERSION__   = Lua._VERSION;
     private static final String __NAME__             = "Luaj";
@@ -67,74 +71,16 @@ public class LuaScriptEngine implements ScriptEngine, Compilable {
         put(NAME, __SHORT_NAME__);
         put("THREADING", null);
     }
-    
-    public Object eval(String script) throws ScriptException {
-        return eval(new StringReader(script));
-    }
-    
-    public Object eval(String script, ScriptContext context) throws ScriptException {
-    	return eval(new StringReader(script), context);
-    }
-    
-    public Object eval(String script, Bindings bindings) throws ScriptException {
-        return eval(new StringReader(script), bindings);
-    }
-    
-    public Object eval(Reader reader) throws ScriptException {
-        return compile(reader).eval();
-    }
-    
-    public Object eval(Reader reader, ScriptContext scriptContext) throws ScriptException {
-    	return compile(reader).eval(scriptContext);
-    }
-    
-    public Object eval(Reader reader, Bindings bindings) throws ScriptException {
-    	return compile(reader).eval(bindings);
-    }
-    
-    public void put(String key, Object value) {
-		Bindings b = getBindings(ScriptContext.ENGINE_SCOPE);
-		b.put(key, value);
-    }
-    
-    public Object get(String key) {
-		Bindings b = getBindings(ScriptContext.ENGINE_SCOPE);
-		return b.get(key);
-    }
-
-    public Bindings getBindings(int scope) {
-        return getContext().getBindings(scope);
-    }
-    
-    public void setBindings(Bindings bindings, int scope) {
-        getContext().setBindings(bindings, scope);
-    }
-    
-    public Bindings createBindings() {
-        return new LuajBindings();
-    }
-    
-    public ScriptContext getContext() {
-        return context;
-    }
-    
-    public void setContext(ScriptContext context) {
-		if (!(context instanceof LuajContext))
-			throw new IllegalArgumentException("LuaScriptEngine can only be used with LuajScriptContext");
-    	this.context = (LuajContext) context;
-    }
-    
-    public ScriptEngineFactory getFactory() {
-        return myFactory;
-    }
 
+	@Override
 	public CompiledScript compile(String script) throws ScriptException {
 		return compile(new StringReader(script));
 	}
-	
-	public CompiledScript compile(Reader reader) throws ScriptException {
+
+	@Override
+	public CompiledScript compile(Reader script) throws ScriptException {
 		try {
-	    	InputStream is = new Utf8Encoder(reader);
+	    	InputStream is = new Utf8Encoder(script);
 	    	try {
 	    		final Globals g = context.globals;
 	    		final LuaFunction f = LoadState.load(is, "script", "bt", g);
@@ -149,6 +95,29 @@ public class LuaScriptEngine implements ScriptEngine, Compilable {
 		}
 	}
 
+	@Override
+	public Bindings createBindings() {
+		return new SimpleBindings();
+	}
+
+	@Override
+	public Object eval(String script, ScriptContext context)
+			throws ScriptException {
+		return eval(new StringReader(script), context);
+	}
+
+	@Override
+	public Object eval(Reader reader, ScriptContext context)
+			throws ScriptException {
+        return compile(reader).eval();
+	}
+
+	@Override
+	public ScriptEngineFactory getFactory() {
+		return myFactory;
+	}
+
+
 	class LuajCompiledScript extends CompiledScript {
 		final LuaFunction function;
 		final Globals compiling_globals;
@@ -166,31 +135,25 @@ public class LuaScriptEngine implements ScriptEngine, Compilable {
 	    }
 	    
 	    public Object eval(Bindings bindings) throws ScriptException {
-	    	ScriptContext c = getContext();
-	        Bindings current = c.getBindings(ScriptContext.ENGINE_SCOPE);
-	        try {
-		        c.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
-		        Object result = eval(c);
-		        return result;
-	        } finally {
-		        c.setBindings(current, ScriptContext.ENGINE_SCOPE);
-	        }
+	    	return eval(((LuajContext) getContext()).globals, bindings);
 	    }
 
 	    public Object eval(ScriptContext context) throws ScriptException {
-			Globals g = ((LuajContext) context).globals;
+	    	return eval(((LuajContext) context).globals, context.getBindings(ScriptContext.ENGINE_SCOPE));
+		}
+	    
+	    private Object eval(Globals g, Bindings b) throws ScriptException {
+	    	g.setmetatable(new BindingsMetatable(b));
 			LuaFunction f = function;
-			if (g != compiling_globals) {
-				if (f.isclosure())
-					f = new LuaClosure(f.checkclosure().p, g);
-				else {
-					try {
-						f = f.getClass().newInstance();
-					} catch (Exception e) {
-						throw new ScriptException(e);
-					}
-					f.initupvalue1(g);
+			if (f.isclosure())
+				f = new LuaClosure(f.checkclosure().p, g);
+			else {
+				try {
+					f = f.getClass().newInstance();
+				} catch (Exception e) {
+					throw new ScriptException(e);
 				}
+				f.initupvalue1(g);
 			}
 			return f.invoke(LuaValue.NONE);
 		}
@@ -224,4 +187,51 @@ public class LuaScriptEngine implements ScriptEngine, Compilable {
 			}
 		}
 	}
+	
+	static class BindingsMetatable extends LuaTable {
+
+		BindingsMetatable(final Bindings bindings) {
+			this.rawset(LuaValue.INDEX, new TwoArgFunction() {
+				public LuaValue call(LuaValue table, LuaValue key) {
+					if (key.isstring()) 
+						return toLua(bindings.get(key.tojstring()));
+					else
+						return this.rawget(key);
+				}
+			});
+			this.rawset(LuaValue.NEWINDEX, new ThreeArgFunction() {
+				public LuaValue call(LuaValue table, LuaValue key, LuaValue value) {
+					if (key.isstring()) {
+						final String k = key.tojstring();
+						final Object v = toJava(value);
+						if (v == null)
+							bindings.remove(k);
+						else
+							bindings.put(k, v);
+					} else {
+						this.rawset(key, value);
+					}
+					return LuaValue.NONE;
+				}
+			});
+		}
+		
+		static private Object toJava(LuaValue luajValue) {
+			switch ( luajValue.type() ) {
+			case LuaValue.TNIL: return null;
+			case LuaValue.TSTRING: return luajValue.tojstring();
+			case LuaValue.TUSERDATA: return luajValue.checkuserdata(Object.class);
+			case LuaValue.TNUMBER: return luajValue.isinttype()? 
+					(Object) new Integer(luajValue.toint()): 
+					(Object) new Double(luajValue.todouble());
+			default: return luajValue;
+			}
+		}
+
+		static private LuaValue toLua(Object javaValue) {
+			return javaValue == null? LuaValue.NIL:
+				javaValue instanceof LuaValue? (LuaValue) javaValue:
+				CoerceJavaToLua.coerce(javaValue);
+		}
+	}
 }
diff --git a/src/jse/org/luaj/vm2/script/LuajBindings.java b/src/jse/org/luaj/vm2/script/LuajBindings.java
deleted file mode 100644
index fe262c5b..00000000
--- a/src/jse/org/luaj/vm2/script/LuajBindings.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*******************************************************************************
-* Copyright (c) 2013 LuaJ. All rights reserved.
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy
-* of this software and associated documentation files (the "Software"), to deal
-* in the Software without restriction, including without limitation the rights
-* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-* copies of the Software, and to permit persons to whom the Software is
-* furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in
-* all copies or substantial portions of the Software.
-* 
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-* THE SOFTWARE.
-******************************************************************************/
-
-package org.luaj.vm2.script;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.script.Bindings;
-
-import org.luaj.vm2.LuaTable;
-import org.luaj.vm2.LuaValue;
-import org.luaj.vm2.lib.ThreeArgFunction;
-import org.luaj.vm2.lib.jse.CoerceJavaToLua;
-
-/**
- * Implementation of Bindings that may be used with Luaj ScriptEngine implementaiton.
- * 
- * 

- * Normally this will not be created directly, but instead will be created fro the script - * engine using something like: - *

- *       ScriptEngineManager manager = new ScriptEngineManager();
- *       ScriptEngine engine = manager.getEngineByExtension(".lua");
- *       Bindings luaj_bindings = engine.createBindings();
- * 
- * - *

- * Each instance of LuajBindings holds its own global state. - * Calls to get(), put(), and so on will coerce the lua values - * into Java values on-the-fly, so that any performance or - * memory occur at that point. - * - *

- * Only Java String keys may be used for get(), put(), and similar - * operations, or ClassCastException will be thrown. - * - *

- * When iterating over the globals, only values which have string keys - * will be returned. - */ -public class LuajBindings implements Bindings { - - /** Values that are in these bindings. This table is linked to the metatable - * of the context when it is executed via the metatable. - */ - public final LuaTable env; - - /** Metatable to be used on bindings that will forards gets and sets into our table of bindings. - */ - final LuaTable metatable; - - /** Construct an empty LuajBindings. - *

- * Each LuajBindings has its own environment table, which will - * delegate to the context on global reads during execution. - */ - public LuajBindings() { - env = new LuaTable(); - metatable = new LuaTable(); - metatable.set(LuaValue.INDEX, env); - metatable.set(LuaValue.NEWINDEX, new ThreeArgFunction() { - public LuaValue call(LuaValue table, LuaValue key, LuaValue value) { - env.rawset(key, value); - return LuaValue.NONE; - } - }); - } - - /** Coerce a value from luaj types to Java types. - * @param luajValue any value that derives from LuaValue. - * @return If luajValue is: - * {@link #NIL}, null; - * {@link #LuaString}: String; - * {@link #LuaUserdata}: Object; - * {@link #LuaNumber}: Integer or Double; - * otherwise: the raw {@link #LuaValue}. - */ - public Object toJava(LuaValue luajValue) { - switch ( luajValue.type() ) { - case LuaValue.TNIL: return null; - case LuaValue.TSTRING: return luajValue.tojstring(); - case LuaValue.TUSERDATA: return luajValue.checkuserdata(Object.class); - case LuaValue.TNUMBER: return luajValue.isinttype()? - (Object) new Integer(luajValue.toint()): - (Object) new Double(luajValue.todouble()); - default: return luajValue; - } - } - - /** Coerce a value from Java types to luaj types. - * @param javaValue Any Java value, including possibly null. - * @return LuaValue for this Java Value. If javaValue is - * null: {@link #NIL}; - * {@link #LuaValue}: the value; - * String: {@link #LuaString}; - * Integer: {@link #LuaInteger}; - * Double: {@link #LuaDouble}; - * array: {@link #LuaTable} containing list of values; - * otherwise {@link #LuaUserdata} from behavior of {@link CoerceJavaToLua.coerce(Object)} - * @see CoerceJavaToLua - */ - public LuaValue toLua(Object javaValue) { - return javaValue == null? LuaValue.NIL: - javaValue instanceof LuaValue? (LuaValue) javaValue: - CoerceJavaToLua.coerce(javaValue); - } - - @Override - public void clear() { - for (LuaValue key : env.keys()) - env.rawset(key, LuaValue.NIL); - } - - @Override - public boolean containsValue(Object string_key) { - return !env.rawget((String)string_key).isnil(); - } - - @Override - public Set> entrySet() { - HashMap map = new HashMap(); - for (LuaValue key : env.keys()) - if (key.type() == LuaValue.TSTRING) - map.put(key.tojstring(), toJava(env.rawget(key))); - return map.entrySet(); - } - - @Override - public boolean isEmpty() { - return keySet().isEmpty(); - } - - @Override - public Set keySet() { - Set set = new HashSet(); - for (LuaValue key : env.keys()) - if (key.type() == LuaValue.TSTRING) - set.add(key.tojstring()); - return set; - } - - @Override - public int size() { - return keySet().size(); - } - - @Override - public Collection values() { - List values = new ArrayList(); - for (LuaValue key : env.keys()) - if (key.type() == LuaValue.TSTRING) - values.add(toJava(env.rawget(key))); - return values; - } - - @Override - public boolean containsKey(Object key) { - return keySet().contains(key); - } - - @Override - public Object get(Object string_key) { - return toJava(env.rawget((String)string_key)); - } - - @Override - public Object put(String string_key, Object java_value) { - Object previous = get(string_key); - env.rawset((String)string_key, toLua(java_value)); - return previous; - } - - @Override - public void putAll(Map values) { - for (java.util.Map.Entry e : values.entrySet()) - env.rawset(e.getKey(), toLua(e.getValue())); - } - - @Override - public Object remove(Object string_key) { - Object previous = get(string_key); - env.rawset((String)string_key, LuaValue.NIL); - return previous; - } - -} diff --git a/src/jse/org/luaj/vm2/script/LuajContext.java b/src/jse/org/luaj/vm2/script/LuajContext.java index ab7d1bdf..1b940987 100644 --- a/src/jse/org/luaj/vm2/script/LuajContext.java +++ b/src/jse/org/luaj/vm2/script/LuajContext.java @@ -83,22 +83,6 @@ public class LuajContext extends SimpleScriptContext implements ScriptContext { stderr = globals.STDERR; } - public Bindings getBindings(int scope) { - if (scope != ScriptContext.ENGINE_SCOPE) - throw new IllegalArgumentException("LuajScriptContext only supports ENGINE_SCOPE"); - return super.getBindings(ENGINE_SCOPE); - } - - public void setBindings(Bindings bindings, int scope) { - if (!(bindings instanceof LuajBindings)) - throw new IllegalArgumentException("LuajScriptContext can only be used with LuajBindings"); - if (scope != ScriptContext.ENGINE_SCOPE) - throw new IllegalArgumentException("LuajScriptContext only supports ENGINE_SCOPE"); - LuajBindings luaj_bindings = (LuajBindings) bindings; - globals.setmetatable(luaj_bindings.metatable); - super.setBindings(bindings, scope); - } - @Override public void setErrorWriter(Writer writer) { globals.STDERR = writer != null? @@ -139,5 +123,4 @@ public class LuajContext extends SimpleScriptContext implements ScriptContext { return r.read(); } } - } diff --git a/test/junit/org/luaj/vm2/AllTests.java b/test/junit/org/luaj/vm2/AllTests.java index 1abf3cb5..6fee66b8 100644 --- a/test/junit/org/luaj/vm2/AllTests.java +++ b/test/junit/org/luaj/vm2/AllTests.java @@ -35,6 +35,7 @@ import org.luaj.vm2.compiler.SimpleTests; import org.luaj.vm2.lib.jse.LuaJavaCoercionTest; import org.luaj.vm2.lib.jse.LuajavaAccessibleMembersTest; import org.luaj.vm2.lib.jse.LuajavaClassMembersTest; +import org.luaj.vm2.script.ScriptEngineTests; public class AllTests { @@ -82,10 +83,14 @@ public class AllTests { lib.addTestSuite(LuaJavaCoercionTest.class); lib.addTestSuite(RequireClassTest.class); suite.addTest(lib); + + // Script engine tests. + TestSuite script = ScriptEngineTests.suite(); + suite.addTest(script); // compatiblity tests TestSuite compat = CompatibiltyTest.suite(); - suite.addTest( compat ); + suite.addTest(compat); compat.addTestSuite(ErrorsTest.class); return suite; diff --git a/test/junit/org/luaj/vm2/script/ScriptEngineTests.java b/test/junit/org/luaj/vm2/script/ScriptEngineTests.java new file mode 100644 index 00000000..60618dc1 --- /dev/null +++ b/test/junit/org/luaj/vm2/script/ScriptEngineTests.java @@ -0,0 +1,237 @@ +/******************************************************************************* + * Copyright (c) 2013 Luaj.org. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ +package org.luaj.vm2.script; + +import java.io.CharArrayReader; +import java.io.CharArrayWriter; +import java.io.Reader; + +import javax.script.Bindings; +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import javax.script.SimpleBindings; + +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.luaj.vm2.LuaFunction; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.lib.OneArgFunction; + +public class ScriptEngineTests extends TestSuite { + + public static TestSuite suite() { + TestSuite suite = new TestSuite("Script Engine Tests"); + suite.addTest( new TestSuite( LookupEngineTestCase.class, "Lookup Engine" ) ); + suite.addTest( new TestSuite( SimpleBindingsTest.class, "Simple Bindings" ) ); + suite.addTest( new TestSuite( DefaultBindingsTest.class, "Default Bindings" ) ); + suite.addTest( new TestSuite( LuaJCBindingsTest.class, "LuaJC Bindings" ) ); + return suite; + } + + public static class LookupEngineTestCase extends TestCase { + public void testGetEngineByExtension() { + ScriptEngine e = new ScriptEngineManager().getEngineByExtension(".lua"); + assertNotNull(e); + assertEquals(LuaScriptEngine.class, e.getClass()); + } + public void testGetEngineByName() { + ScriptEngine e = new ScriptEngineManager().getEngineByName("luaj"); + assertNotNull(e); + assertEquals(LuaScriptEngine.class, e.getClass()); + } + public void testGetEngineByMimeType() { + ScriptEngine e = new ScriptEngineManager().getEngineByMimeType("text/lua"); + assertNotNull(e); + assertEquals(LuaScriptEngine.class, e.getClass()); + } + public void testFactoryMetadata() { + ScriptEngine e = new ScriptEngineManager().getEngineByName("luaj"); + ScriptEngineFactory f = e.getFactory(); + assertEquals("Luaj", f.getEngineName()); + assertEquals("Luaj 0.0", f.getEngineVersion()); + assertEquals("lua", f.getLanguageName()); + assertEquals("5.2", f.getLanguageVersion()); + } + } + + public static class SimpleBindingsTest extends EngineTestCase { + protected Bindings createBindings() { + return new SimpleBindings(); + } + public void testCompiledFunctionIsClosure() throws ScriptException { + CompiledScript cs = ((Compilable)e).compile("return 'foo'"); + assertTrue(((LuaScriptEngine.LuajCompiledScript)cs).function.isclosure()); + } + } + + public static class DefaultBindingsTest extends EngineTestCase { + protected Bindings createBindings() { + return e.createBindings(); + } + } + + public static class LuaJCBindingsTest extends EngineTestCase { + protected Bindings createBindings() { + return new SimpleBindings(); + } + public void setUp() { + super.setUp(); + org.luaj.vm2.luajc.LuaJC.install(); + } + public void testCompiledFunctionIsNotClosure() throws ScriptException { + CompiledScript cs = ((Compilable)e).compile("return 'foo'"); + assertFalse(((LuaScriptEngine.LuajCompiledScript)cs).function.isclosure()); + } + } + + abstract public static class EngineTestCase extends TestCase { + protected ScriptEngine e; + protected Bindings b; + abstract protected Bindings createBindings(); + public void setUp() { + this.e = new ScriptEngineManager().getEngineByName("luaj"); + this.b = createBindings(); + } + public void testSqrtIntResult() throws ScriptException { + e.put("x", 25); + e.eval("y = math.sqrt(x)"); + Object y = e.get("y"); + assertEquals(5, y); + } + public void testOneArgFunction() throws ScriptException { + e.put("x", 25); + e.eval("y = math.sqrt(x)"); + Object y = e.get("y"); + assertEquals(5, y); + e.put("f", new OneArgFunction() { + public LuaValue call(LuaValue arg) { + return LuaValue.valueOf(arg.toString()+"123"); + } + }); + Object r = e.eval("return f('abc')"); + assertEquals("abc123", r.toString()); + } + public void testCompiledScript() throws ScriptException { + CompiledScript cs = ((Compilable)e).compile("y = math.sqrt(x); return y"); + b.put("x", 144); + assertEquals("12", cs.eval(b).toString()); + } + public void testBuggyLuaScript() { + try { + e.eval("\n\nbuggy lua code\n\n"); + } catch ( ScriptException se ) { + assertEquals("eval threw javax.script.ScriptException: [string \"script\"]:3: syntax error", se.getMessage()); + return; + } + fail("buggy script did not throw ScriptException as expected."); + } + public void testScriptRedirection() throws ScriptException { + Reader input = new CharArrayReader("abcdefg\nhijk".toCharArray()); + CharArrayWriter output = new CharArrayWriter(); + CharArrayWriter errors = new CharArrayWriter(); + String script = + "print(\"string written using 'print'\")\n" + + "io.write(\"string written using 'io.write()'\\n\")\n" + + "io.stdout:write(\"string written using 'io.stdout:write()'\\n\")\n" + + "io.stderr:write(\"string written using 'io.stderr:write()'\\n\")\n" + + "io.write([[string read using 'io.stdin:read(\"*l\")':]]..io.stdin:read(\"*l\")..\"\\n\")\n"; + + // Evaluate script with redirection set + e.getContext().setReader(input); + e.getContext().setWriter(output); + e.getContext().setErrorWriter(errors); + e.eval(script); + final String expectedOutput = "string written using 'print'\n"+ + "string written using 'io.write()'\n"+ + "string written using 'io.stdout:write()'\n"+ + "string read using 'io.stdin:read(\"*l\")':abcdefg\n"; + assertEquals(expectedOutput, output.toString()); + final String expectedErrors = "string written using 'io.stderr:write()'\n"; + assertEquals(expectedErrors, errors.toString()); + + // Evaluate script with redirection reset + output.reset(); + errors.reset(); + // e.getContext().setReader(null); // This will block if using actual STDIN + e.getContext().setWriter(null); + e.getContext().setErrorWriter(null); + e.eval(script); + assertEquals("", output.toString()); + assertEquals("", errors.toString()); + } + public void testBindingJavaInt() throws ScriptException { + CompiledScript cs = ((Compilable)e).compile("y = x; return 'x '..type(x)..' '..tostring(x)\n"); + b.put("x", 111); + assertEquals("x number 111", cs.eval(b).toString()); + assertEquals(111, b.get("y")); + } + public void testBindingJavaDouble() throws ScriptException { + CompiledScript cs = ((Compilable)e).compile("y = x; return 'x '..type(x)..' '..tostring(x)\n"); + b.put("x", 125.125); + assertEquals("x number 125.125", cs.eval(b).toString()); + assertEquals(125.125, b.get("y")); + } + public void testBindingJavaString() throws ScriptException { + CompiledScript cs = ((Compilable)e).compile("y = x; return 'x '..type(x)..' '..tostring(x)\n"); + b.put("x", "foo"); + assertEquals("x string foo", cs.eval(b).toString()); + assertEquals("foo", b.get("y")); + } + public void testBindingJavaObject() throws ScriptException { + CompiledScript cs = ((Compilable)e).compile("y = x; return 'x '..type(x)..' '..tostring(x)\n"); + b.put("x", new SomeUserClass()); + assertEquals("x userdata some-user-value", cs.eval(b).toString()); + assertEquals(SomeUserClass.class, b.get("y").getClass()); + } + public void testBindingJavaArray() throws ScriptException { + CompiledScript cs = ((Compilable)e).compile("y = x; return 'x '..type(x)..' '..x[1]..' '..x[2]\n"); + b.put("x", new int[] { 777, 888 }); + assertEquals("x userdata 777 888", cs.eval(b).toString()); + assertEquals(int[].class, b.get("y").getClass()); + } + public void testBindingLuaFunction() throws ScriptException { + CompiledScript cs = ((Compilable)e).compile("y = function(x) return 678 + x end; return 'foo'"); + assertEquals("foo", cs.eval(b).toString()); + assertTrue(b.get("y") instanceof LuaFunction); + assertEquals(LuaValue.valueOf(801), ((LuaFunction) b.get("y")).call(LuaValue.valueOf(123))); + } + public void testUserClasses() throws ScriptException { + CompiledScript cs = ((Compilable)e).compile( + "x = x or luajava.newInstance('java.lang.String', 'test')\n" + + "return 'x ' .. type(x) .. ' ' .. tostring(x)\n"); + assertEquals("x string test", cs.eval(b).toString()); + b.put("x", new SomeUserClass()); + assertEquals("x userdata some-user-value", cs.eval(b).toString()); + } + } + + public static class SomeUserClass { + public String toString() { + return "some-user-value"; + } + } +}