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
- Fix bug that didn't read package.path from environment.
+- Fix pluggable scripting engine lookup, simplify implementation, and add unit tests.
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