Fix pluggable scripting engine lookup, simplify implementation, and add unit tests.
This commit is contained in:
@@ -301,7 +301,7 @@ The standard use of JSR-223 scripting engines may be used:
|
|||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
ScriptEngineManager mgr = new ScriptEngineManager();
|
ScriptEngineManager mgr = new ScriptEngineManager();
|
||||||
ScriptEngine e = mgr.getEngineByExtension(".lua");
|
ScriptEngine e = mgr.getEngineByName("luaj");
|
||||||
e.put("x", 25);
|
e.put("x", 25);
|
||||||
e.eval("y = math.sqrt(x)");
|
e.eval("y = math.sqrt(x)");
|
||||||
System.out.println( "y="+e.get("y") );
|
System.out.println( "y="+e.get("y") );
|
||||||
@@ -846,6 +846,7 @@ Files are no longer hosted at LuaForge.
|
|||||||
|
|
||||||
<tr valign="top"><td> <b>3.0-beta1</b></td><td><ul>
|
<tr valign="top"><td> <b>3.0-beta1</b></td><td><ul>
|
||||||
<li>Fix bug that didn't read package.path from environment.</li>
|
<li>Fix bug that didn't read package.path from environment.</li>
|
||||||
|
<li>Fix pluggable scripting engine lookup, simplify implementation, and add unit tests.</li>
|
||||||
|
|
||||||
</ul></td></tr>
|
</ul></td></tr>
|
||||||
</table></td></tr></table>
|
</table></td></tr></table>
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ import javax.script.SimpleBindings;
|
|||||||
|
|
||||||
import org.luaj.vm2.LuaValue;
|
import org.luaj.vm2.LuaValue;
|
||||||
import org.luaj.vm2.lib.OneArgFunction;
|
import org.luaj.vm2.lib.OneArgFunction;
|
||||||
import org.luaj.vm2.script.LuaScriptEngine;
|
|
||||||
import org.luaj.vm2.script.LuajBindings;
|
|
||||||
|
|
||||||
public class ScriptEngineSample {
|
public class ScriptEngineSample {
|
||||||
|
|
||||||
@@ -27,8 +25,7 @@ public class ScriptEngineSample {
|
|||||||
// System.setProperty("luaj.debug", "true");
|
// System.setProperty("luaj.debug", "true");
|
||||||
|
|
||||||
ScriptEngineManager sem = new ScriptEngineManager();
|
ScriptEngineManager sem = new ScriptEngineManager();
|
||||||
ScriptEngine e = sem.getEngineByExtension(".lua");
|
ScriptEngine e = sem.getEngineByName("luaj");
|
||||||
e = new LuaScriptEngine();
|
|
||||||
ScriptEngineFactory f = e.getFactory();
|
ScriptEngineFactory f = e.getFactory();
|
||||||
|
|
||||||
// uncomment to enable the lua-to-java bytecode compiler
|
// 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="+b1.get("y") );
|
||||||
System.out.println( "y="+b2.get("y") );
|
System.out.println( "y="+b2.get("y") );
|
||||||
|
|
||||||
// In Luaj 3.0, client bindings must derive from LuajBindings.
|
// In Luaj 3.0, client bindings can just be SimpleBindings.
|
||||||
Bindings sb = new LuajBindings();
|
Bindings sb = new SimpleBindings();
|
||||||
sb.put("x", 2);
|
sb.put("x", 2);
|
||||||
System.out.println( "eval: "+cs.eval(sb) );
|
System.out.println( "eval: "+cs.eval(sb) );
|
||||||
|
|
||||||
@@ -134,7 +131,7 @@ public class ScriptEngineSample {
|
|||||||
testBindings(e, e.createBindings());
|
testBindings(e, e.createBindings());
|
||||||
}
|
}
|
||||||
public static void testClientBindings(ScriptEngine e) throws ScriptException {
|
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 {
|
public static void testBindings(ScriptEngine e, Bindings b) throws ScriptException {
|
||||||
CompiledScript cs = ((Compilable)e).compile(
|
CompiledScript cs = ((Compilable)e).compile(
|
||||||
|
|||||||
@@ -22,9 +22,13 @@
|
|||||||
package org.luaj.vm2.script;
|
package org.luaj.vm2.script;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
import javax.script.*;
|
import javax.script.*;
|
||||||
|
|
||||||
import org.luaj.vm2.*;
|
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
|
* 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
|
* and for client bindings use the default engine scoped bindings or
|
||||||
* construct a {@link LuajBindings} directly.
|
* 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 __ENGINE_VERSION__ = Lua._VERSION;
|
||||||
private static final String __NAME__ = "Luaj";
|
private static final String __NAME__ = "Luaj";
|
||||||
@@ -68,73 +72,15 @@ public class LuaScriptEngine implements ScriptEngine, Compilable {
|
|||||||
put("THREADING", null);
|
put("THREADING", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object eval(String script) throws ScriptException {
|
@Override
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompiledScript compile(String script) throws ScriptException {
|
public CompiledScript compile(String script) throws ScriptException {
|
||||||
return compile(new StringReader(script));
|
return compile(new StringReader(script));
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompiledScript compile(Reader reader) throws ScriptException {
|
@Override
|
||||||
|
public CompiledScript compile(Reader script) throws ScriptException {
|
||||||
try {
|
try {
|
||||||
InputStream is = new Utf8Encoder(reader);
|
InputStream is = new Utf8Encoder(script);
|
||||||
try {
|
try {
|
||||||
final Globals g = context.globals;
|
final Globals g = context.globals;
|
||||||
final LuaFunction f = LoadState.load(is, "script", "bt", g);
|
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 {
|
class LuajCompiledScript extends CompiledScript {
|
||||||
final LuaFunction function;
|
final LuaFunction function;
|
||||||
final Globals compiling_globals;
|
final Globals compiling_globals;
|
||||||
@@ -166,31 +135,25 @@ public class LuaScriptEngine implements ScriptEngine, Compilable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Object eval(Bindings bindings) throws ScriptException {
|
public Object eval(Bindings bindings) throws ScriptException {
|
||||||
ScriptContext c = getContext();
|
return eval(((LuajContext) getContext()).globals, bindings);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object eval(ScriptContext context) throws ScriptException {
|
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;
|
LuaFunction f = function;
|
||||||
if (g != compiling_globals) {
|
if (f.isclosure())
|
||||||
if (f.isclosure())
|
f = new LuaClosure(f.checkclosure().p, g);
|
||||||
f = new LuaClosure(f.checkclosure().p, g);
|
else {
|
||||||
else {
|
try {
|
||||||
try {
|
f = f.getClass().newInstance();
|
||||||
f = f.getClass().newInstance();
|
} catch (Exception e) {
|
||||||
} catch (Exception e) {
|
throw new ScriptException(e);
|
||||||
throw new ScriptException(e);
|
|
||||||
}
|
|
||||||
f.initupvalue1(g);
|
|
||||||
}
|
}
|
||||||
|
f.initupvalue1(g);
|
||||||
}
|
}
|
||||||
return f.invoke(LuaValue.NONE);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Normally this will not be created directly, but instead will be created fro the script
|
|
||||||
* engine using something like:
|
|
||||||
* <pre>
|
|
||||||
* ScriptEngineManager manager = new ScriptEngineManager();
|
|
||||||
* ScriptEngine engine = manager.getEngineByExtension(".lua");
|
|
||||||
* Bindings luaj_bindings = engine.createBindings();
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Only Java String keys may be used for get(), put(), and similar
|
|
||||||
* operations, or ClassCastException will be thrown.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* 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.
|
|
||||||
* <p>
|
|
||||||
* 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<Class>: {@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<java.util.Map.Entry<String, Object>> entrySet() {
|
|
||||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
|
||||||
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<String> keySet() {
|
|
||||||
Set<String> set = new HashSet<String>();
|
|
||||||
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<Object> values() {
|
|
||||||
List<Object> values = new ArrayList<Object>();
|
|
||||||
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<? extends String, ? extends Object> values) {
|
|
||||||
for (java.util.Map.Entry<? extends String, ? extends Object> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -83,22 +83,6 @@ public class LuajContext extends SimpleScriptContext implements ScriptContext {
|
|||||||
stderr = globals.STDERR;
|
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
|
@Override
|
||||||
public void setErrorWriter(Writer writer) {
|
public void setErrorWriter(Writer writer) {
|
||||||
globals.STDERR = writer != null?
|
globals.STDERR = writer != null?
|
||||||
@@ -139,5 +123,4 @@ public class LuajContext extends SimpleScriptContext implements ScriptContext {
|
|||||||
return r.read();
|
return r.read();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import org.luaj.vm2.compiler.SimpleTests;
|
|||||||
import org.luaj.vm2.lib.jse.LuaJavaCoercionTest;
|
import org.luaj.vm2.lib.jse.LuaJavaCoercionTest;
|
||||||
import org.luaj.vm2.lib.jse.LuajavaAccessibleMembersTest;
|
import org.luaj.vm2.lib.jse.LuajavaAccessibleMembersTest;
|
||||||
import org.luaj.vm2.lib.jse.LuajavaClassMembersTest;
|
import org.luaj.vm2.lib.jse.LuajavaClassMembersTest;
|
||||||
|
import org.luaj.vm2.script.ScriptEngineTests;
|
||||||
|
|
||||||
public class AllTests {
|
public class AllTests {
|
||||||
|
|
||||||
@@ -83,9 +84,13 @@ public class AllTests {
|
|||||||
lib.addTestSuite(RequireClassTest.class);
|
lib.addTestSuite(RequireClassTest.class);
|
||||||
suite.addTest(lib);
|
suite.addTest(lib);
|
||||||
|
|
||||||
|
// Script engine tests.
|
||||||
|
TestSuite script = ScriptEngineTests.suite();
|
||||||
|
suite.addTest(script);
|
||||||
|
|
||||||
// compatiblity tests
|
// compatiblity tests
|
||||||
TestSuite compat = CompatibiltyTest.suite();
|
TestSuite compat = CompatibiltyTest.suite();
|
||||||
suite.addTest( compat );
|
suite.addTest(compat);
|
||||||
compat.addTestSuite(ErrorsTest.class);
|
compat.addTestSuite(ErrorsTest.class);
|
||||||
|
|
||||||
return suite;
|
return suite;
|
||||||
|
|||||||
237
test/junit/org/luaj/vm2/script/ScriptEngineTests.java
Normal file
237
test/junit/org/luaj/vm2/script/ScriptEngineTests.java
Normal file
@@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user