├── jnlua ├── native │ ├── Win32-x64 │ │ ├── javavm.dll │ │ └── jnlua52.dll │ └── Win32-x86 │ │ ├── javavm.dll │ │ └── jnlua52.dll ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── javax.script.ScriptEngineFactory │ │ ├── java │ │ │ └── com │ │ │ │ └── naef │ │ │ │ └── jnlua │ │ │ │ ├── console │ │ │ │ ├── package.html │ │ │ │ └── LuaConsole.java │ │ │ │ ├── util │ │ │ │ ├── package.html │ │ │ │ ├── AbstractTableList.java │ │ │ │ └── AbstractTableMap.java │ │ │ │ ├── script │ │ │ │ ├── package.html │ │ │ │ ├── CompiledLuaScript.java │ │ │ │ ├── LuaBindings.java │ │ │ │ ├── LuaScriptEngineFactory.java │ │ │ │ └── LuaScriptEngine.java │ │ │ │ ├── package.html │ │ │ │ ├── NamedJavaFunction.java │ │ │ │ ├── LuaValueProxy.java │ │ │ │ ├── JavaFunction.java │ │ │ │ ├── LuaSyntaxException.java │ │ │ │ ├── LuaMemoryAllocationException.java │ │ │ │ ├── LuaMessageHandlerException.java │ │ │ │ ├── LuaType.java │ │ │ │ ├── LuaGcMetamethodException.java │ │ │ │ ├── LuaException.java │ │ │ │ ├── LuaError.java │ │ │ │ ├── TypedJavaObject.java │ │ │ │ ├── Converter.java │ │ │ │ ├── NativeSupport.java │ │ │ │ ├── JavaReflector.java │ │ │ │ ├── LuaRuntimeException.java │ │ │ │ ├── LuaStackTraceElement.java │ │ │ │ ├── DefaultConverter.java │ │ │ │ └── JavaModule.java │ │ ├── c │ │ │ ├── javavm.h │ │ │ ├── MacOSX │ │ │ │ └── Makefile │ │ │ ├── Linux │ │ │ │ └── Makefile │ │ │ ├── Win32 │ │ │ │ └── Makefile │ │ │ └── javavm.c │ │ └── assembly │ │ │ └── native.xml │ └── test │ │ ├── java │ │ └── com │ │ │ └── naef │ │ │ └── jnlua │ │ │ └── test │ │ │ ├── JavaReflectionTest.java │ │ │ ├── LuaConsoleTest.java │ │ │ ├── JavaFunctionTest.java │ │ │ ├── JavaModuleTest.java │ │ │ ├── AbstractLuaTest.java │ │ │ ├── CollectionTest.java │ │ │ ├── fixture │ │ │ └── TestObject.java │ │ │ ├── LuaExceptionTest.java │ │ │ ├── LuaScriptEngineTest.java │ │ │ └── LuaStateErrorTest.java │ │ └── resources │ │ └── com │ │ └── naef │ │ └── jnlua │ │ └── test │ │ ├── Reflection.lua │ │ └── JavaModule.lua ├── .settings │ ├── org.eclipse.m2e.core.prefs │ ├── org.eclipse.ltk.core.refactoring.prefs │ ├── org.eclipse.pde.core.prefs │ ├── org.eclipse.jdt.core.prefs │ └── org.maven.ide.eclipse.prefs ├── .classpath ├── .project └── pom.xml ├── LICENSE.txt ├── README.md └── CHANGES.md /jnlua/native/Win32-x64/javavm.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antag99/jnlua/HEAD/jnlua/native/Win32-x64/javavm.dll -------------------------------------------------------------------------------- /jnlua/native/Win32-x64/jnlua52.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antag99/jnlua/HEAD/jnlua/native/Win32-x64/jnlua52.dll -------------------------------------------------------------------------------- /jnlua/native/Win32-x86/javavm.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antag99/jnlua/HEAD/jnlua/native/Win32-x86/javavm.dll -------------------------------------------------------------------------------- /jnlua/native/Win32-x86/jnlua52.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antag99/jnlua/HEAD/jnlua/native/Win32-x86/jnlua52.dll -------------------------------------------------------------------------------- /jnlua/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory: -------------------------------------------------------------------------------- 1 | # List of script engine factories in this JAR 2 | com.naef.jnlua.script.LuaScriptEngineFactory -------------------------------------------------------------------------------- /jnlua/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | #Mon Jan 02 21:14:51 CET 2012 2 | activeProfiles= 3 | eclipse.preferences.version=1 4 | resolveWorkspaceProjects=true 5 | version=1 6 | -------------------------------------------------------------------------------- /jnlua/.settings/org.eclipse.ltk.core.refactoring.prefs: -------------------------------------------------------------------------------- 1 | #Sat Feb 20 14:18:59 CET 2010 2 | eclipse.preferences.version=1 3 | org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false 4 | -------------------------------------------------------------------------------- /jnlua/.settings/org.eclipse.pde.core.prefs: -------------------------------------------------------------------------------- 1 | #Sat Feb 20 19:46:39 CET 2010 2 | eclipse.preferences.version=1 3 | pluginProject.equinox=false 4 | pluginProject.extensions=false 5 | selfhosting.binExcludes=/jnlua/target/test-classes,/jnlua/target/test-classes 6 | -------------------------------------------------------------------------------- /jnlua/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | #Sat Feb 20 18:56:33 CET 2010 2 | eclipse.preferences.version=1 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 4 | org.eclipse.jdt.core.compiler.compliance=1.6 5 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 6 | org.eclipse.jdt.core.compiler.source=1.6 7 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/console/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

Provides the JNLua console.

7 | 8 | 9 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/util/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

Provides the JNLua utility classes.

7 | 8 | 9 | -------------------------------------------------------------------------------- /jnlua/.settings/org.maven.ide.eclipse.prefs: -------------------------------------------------------------------------------- 1 | #Sat Feb 20 13:40:37 CET 2010 2 | activeProfiles= 3 | eclipse.preferences.version=1 4 | fullBuildGoals=process-test-resources 5 | includeModules=false 6 | resolveWorkspaceProjects=true 7 | resourceFilterGoals=process-resources resources\:testResources 8 | skipCompilerPlugin=true 9 | version=1 10 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/script/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

Provides the JNLua provider for JSR 223: Scripting for the Java platform.

7 | 8 | 9 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

Provides the JNLua core types, such as the Lua state class and the core interfaces.

7 | 8 | 9 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/NamedJavaFunction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Provides a named Java function. 10 | */ 11 | public interface NamedJavaFunction extends JavaFunction { 12 | /** 13 | * Returns the name of this Java function. 14 | * 15 | * @return the Java function name 16 | */ 17 | public String getName(); 18 | } 19 | -------------------------------------------------------------------------------- /jnlua/src/main/c/javavm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * Provides the Java VM module. See LICENSE.txt for license terms. 4 | */ 5 | 6 | #ifndef JNLUA_JAVAVM_INCLUDED 7 | #define JNLUA_JAVAVM_INCLUDED 8 | 9 | #include 10 | 11 | /** 12 | * Opens the Java VM module in a Lua state. 13 | * 14 | * @param L the Lua state 15 | * @return the number of results 16 | */ 17 | LUALIB_API int luaopen_javavm (lua_State *L); 18 | 19 | #endif /* JNLUA_JAVAVM_INCLUDED */ 20 | -------------------------------------------------------------------------------- /jnlua/src/test/java/com/naef/jnlua/test/JavaReflectionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.test; 7 | 8 | import org.junit.Test; 9 | 10 | /** 11 | * Contains unit tests for Java reflection. 12 | */ 13 | public class JavaReflectionTest extends AbstractLuaTest { 14 | // -- Test cases 15 | /** 16 | * Tests Java reflection from Lua. 17 | */ 18 | @Test 19 | public void testReflection() throws Exception { 20 | runTest("com/naef/jnlua/test/Reflection.lua", "Reflection"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /jnlua/src/main/c/MacOSX/Makefile: -------------------------------------------------------------------------------- 1 | # Paths 2 | JDK_DIR=/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0 3 | LUA_LIB_DIR=/usr/lib 4 | LUA_INC_DIR=/usr/include/lua 5 | VERSION=5.1 6 | 7 | # Tools 8 | CC=gcc 9 | LD=gcc 10 | 11 | # Default arguments 12 | CFLAGS=-c -fno-strict-aliasing -m64 -fPIC -O2 -Wall -DNDEBUG -D_REENTRANT -DLUA_USE_LINUX 13 | LDFLAGS=-dynamiclib -m64 14 | 15 | # Description blocks 16 | all: libjnlua$(VERSION).jnilib 17 | 18 | libjnlua$(VERSION).jnilib: jnlua.o 19 | $(LD) $(LDFLAGS) -o libjnlua$(VERSION).jnilib -L$(LUA_LIB_DIR) -lc -llua$(VERSION) jnlua.o 20 | 21 | jnlua.o: ../jnlua.c 22 | $(CC) $(CFLAGS) -I$(JDK_DIR)/Headers -I$(LUA_INC_DIR) ../jnlua.c 23 | 24 | clean: 25 | -rm libjnlua$(VERSION).jnilib jnlua.o 26 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/LuaValueProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Provides proxy access to a Lua value from Java. Lua value proxies are 10 | * acquired by invoking one of the getProxy() methods on the Lua 11 | * state. 12 | * 13 | * @see LuaState#getProxy(int) 14 | * @see LuaState#getProxy(int, Class) 15 | * @see LuaState#getProxy(int, Class[]) 16 | */ 17 | public interface LuaValueProxy { 18 | /** 19 | * Returns the Lua state of this proxy. 20 | * 21 | * @return the Lua state 22 | */ 23 | public LuaState getLuaState(); 24 | 25 | /** 26 | * Pushes the proxied Lua value on the stack of the Lua state. 27 | */ 28 | public void pushValue(); 29 | } 30 | -------------------------------------------------------------------------------- /jnlua/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /jnlua/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | jnlua 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.maven.ide.eclipse.maven2Builder 15 | 16 | 17 | 18 | 19 | org.eclipse.m2e.core.maven2Builder 20 | 21 | 22 | 23 | 24 | 25 | org.eclipse.m2e.core.maven2Nature 26 | org.eclipse.jdt.core.javanature 27 | org.maven.ide.eclipse.maven2Nature 28 | 29 | 30 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/JavaFunction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Provides a Lua function implemented in Java. 10 | */ 11 | public interface JavaFunction { 12 | /** 13 | * Invokes this Java function. The function arguments are on the stack. The 14 | * method returns the number of values on the stack which constitute the 15 | * return values of this function. 16 | * 17 | *

18 | * Java functions should indicate application errors by returning 19 | * appropriate error codes to the caller. Programming errors should be 20 | * indicated by throwing a runtime exception. 21 | *

22 | * 23 | * @param luaState 24 | * the Lua state this function has been invoked on 25 | * @return the number of return values 26 | */ 27 | public int invoke(LuaState luaState); 28 | } 29 | -------------------------------------------------------------------------------- /jnlua/src/main/assembly/native.xml: -------------------------------------------------------------------------------- 1 | 6 | native 7 | 8 | zip 9 | 10 | 11 | 12 | ${project.basedir} 13 | 14 | README* 15 | LICENSE* 16 | NOTICE* 17 | 18 | true 19 | / 20 | 21 | 22 | ${project.basedir}/native 23 | true 24 | / 25 | 26 | 27 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/LuaSyntaxException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Indicates a Lua syntax error. 10 | * 11 | *

12 | * This exception is thrown if the syntax of a Lua chunk is incorrect. 13 | *

14 | */ 15 | public class LuaSyntaxException extends LuaException { 16 | // -- Static 17 | private static final long serialVersionUID = 1L; 18 | 19 | // -- Construction 20 | /** 21 | * Creates a new instance. 22 | * 23 | * @param msg 24 | * the message 25 | */ 26 | public LuaSyntaxException(String msg) { 27 | super(msg); 28 | } 29 | 30 | /** 31 | * Creates a new instance. 32 | * 33 | * @param msg 34 | * the message 35 | * @param cause 36 | * the cause of this exception 37 | */ 38 | public LuaSyntaxException(String msg, Throwable cause) { 39 | super(msg, cause); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/LuaMemoryAllocationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Indicates a Lua memory allocation error. 10 | * 11 | *

12 | * The exception is thrown if the Lua memory allocator runs out of memory or if 13 | * a JNI allocation fails. 14 | *

15 | */ 16 | public class LuaMemoryAllocationException extends LuaException { 17 | // -- Static 18 | private static final long serialVersionUID = 1L; 19 | 20 | // -- Construction 21 | /** 22 | * Creates a new instance. 23 | * 24 | * @param msg 25 | * the message 26 | */ 27 | public LuaMemoryAllocationException(String msg) { 28 | super(msg); 29 | } 30 | 31 | /** 32 | * Creates a new instance. 33 | * 34 | * @param msg 35 | * the message 36 | * @param cause 37 | * the cause of this exception 38 | */ 39 | public LuaMemoryAllocationException(String msg, Throwable cause) { 40 | super(msg, cause); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/LuaMessageHandlerException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Indicates a Lua message handler error. 10 | * 11 | *

12 | * This exception is thrown if an error occurs running the message handler of a 13 | * protected call. 14 | *

15 | * 16 | * @since JNLua 1.0.0 17 | */ 18 | public class LuaMessageHandlerException extends LuaException { 19 | // -- Static 20 | private static final long serialVersionUID = 1L; 21 | 22 | // -- Construction 23 | /** 24 | * Creates a new instance. 25 | * 26 | * @param msg 27 | * the message 28 | */ 29 | public LuaMessageHandlerException(String msg) { 30 | super(msg); 31 | } 32 | 33 | /** 34 | * Creates a new instance. 35 | * 36 | * @param msg 37 | * the message 38 | * @param cause 39 | * the cause of this exception 40 | */ 41 | public LuaMessageHandlerException(String msg, Throwable cause) { 42 | super(msg, cause); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/LuaType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Represents a Lua type. 10 | */ 11 | public enum LuaType { 12 | // -- Values 13 | /** 14 | * Nil. 15 | */ 16 | NIL, 17 | 18 | /** 19 | * Boolean. 20 | */ 21 | BOOLEAN, 22 | 23 | /** 24 | * Light user data (pointer). 25 | */ 26 | LIGHTUSERDATA, 27 | 28 | /** 29 | * Number. 30 | */ 31 | NUMBER, 32 | 33 | /** 34 | * String. 35 | */ 36 | STRING, 37 | 38 | /** 39 | * Table. 40 | */ 41 | TABLE, 42 | 43 | /** 44 | * Function. 45 | */ 46 | FUNCTION, 47 | 48 | /** 49 | * User data. 50 | */ 51 | USERDATA, 52 | 53 | /** 54 | * Thread. 55 | */ 56 | THREAD; 57 | 58 | // -- Properties 59 | /** 60 | * Returns the display text of this Lua type. The display text is the type 61 | * name in lower case. 62 | * 63 | * @return the display text 64 | */ 65 | public String displayText() { 66 | return toString().toLowerCase(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/LuaGcMetamethodException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Indicates a Lua garbage collection metamethod error. 10 | * 11 | *

12 | * This exception is thrown if an error occurs running a __gc 13 | * metamethod during garbage collection. 14 | *

15 | * 16 | * @since JNLua 1.0.0 17 | */ 18 | public class LuaGcMetamethodException extends LuaException { 19 | // -- Static 20 | private static final long serialVersionUID = 1L; 21 | 22 | // -- Construction 23 | /** 24 | * Creates a new instance. 25 | * 26 | * @param msg 27 | * the message 28 | */ 29 | public LuaGcMetamethodException(String msg) { 30 | super(msg); 31 | } 32 | 33 | /** 34 | * Creates a new instance. 35 | * 36 | * @param msg 37 | * the message 38 | * @param cause 39 | * the cause of this exception 40 | */ 41 | public LuaGcMetamethodException(String msg, Throwable cause) { 42 | super(msg, cause); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /jnlua/src/main/c/Linux/Makefile: -------------------------------------------------------------------------------- 1 | # Paths 2 | JDK_DIR=/usr/lib/jvm/java-6-openjdk-i386 3 | LUA_LIB_DIR=/usr/lib 4 | LUA_INC_DIR=/usr/include/lua5.2 5 | LUA_VERSION=5.2 6 | VERSION=52 7 | ARCH=amd64 8 | 9 | # Tools 10 | CC=gcc 11 | LD=gcc 12 | 13 | # Default arguments 14 | CFLAGS=-c -fno-strict-aliasing -fPIC -O2 -Wall -DNDEBUG -D_REENTRANT -DLUA_USE_LINUX 15 | LDFLAGS=-shared 16 | 17 | # Description blocks 18 | all: libjnlua$(VERSION).so javavm.so 19 | 20 | libjnlua$(VERSION).so: jnlua.o 21 | $(LD) $(LDFLAGS) -Wl,-soname=libjnlua$(VERSION).so -olibjnlua$(VERSION).so -L$(LUA_LIB_DIR) jnlua.o -lc -llua$(LUA_VERSION) 22 | 23 | jnlua.o: ../jnlua.c 24 | $(CC) $(CFLAGS) -I$(JDK_DIR)/include -I$(JDK_DIR)/include/linux -I$(LUA_INC_DIR) ../jnlua.c 25 | 26 | javavm.so: javavm.o 27 | $(LD) $(LDFLAGS) -Wl,-soname=javavm.so -ojavavm.so -L$(LUA_LIB_DIR) -L$(JDK_DIR)/jre/lib/$(ARCH)/server javavm.o -llua$(LUA_VERSION) -ljvm 28 | 29 | javavm.o: ../javavm.c ../javavm.h 30 | $(CC) $(CFLAGS) -I$(JDK_DIR)/include -I$(JDK_DIR)/include/linux -I$(LUA_INC_DIR) ../javavm.c 31 | 32 | clean: 33 | -rm libjnlua$(VERSION).so jnlua.o 34 | -rm javavm.so javavm.o 35 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2008, 2012 Andre Naef 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/LuaException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Abstract base class for Lua error conditions. Lua exceptions are unchecked 10 | * runtime exceptions. 11 | */ 12 | public abstract class LuaException extends RuntimeException { 13 | // -- Static 14 | private static final long serialVersionUID = 1L; 15 | 16 | // -- Construction 17 | /** 18 | * Creates a new instance. 19 | * 20 | * @param msg 21 | * the message 22 | */ 23 | public LuaException(String msg) { 24 | super(msg); 25 | } 26 | 27 | /** 28 | * Creates a new instance. 29 | * 30 | * @param msg 31 | * the message 32 | * @param cause 33 | * the cause of this exception 34 | */ 35 | public LuaException(String msg, Throwable cause) { 36 | super(msg, cause); 37 | } 38 | 39 | /** 40 | * Creates a new instance. 41 | * 42 | * @param cause 43 | * the cause of this exception 44 | */ 45 | public LuaException(Throwable cause) { 46 | super(cause); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /jnlua/src/test/java/com/naef/jnlua/test/LuaConsoleTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: AbstractLuaTest.java 38 2012-01-04 22:44:15Z andre@naef.com $ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.test; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | import static org.junit.Assert.assertNotNull; 10 | import static org.junit.Assert.assertTrue; 11 | 12 | import org.junit.Test; 13 | 14 | import com.naef.jnlua.LuaState; 15 | import com.naef.jnlua.console.LuaConsole; 16 | 17 | /** 18 | * Contains tests for the Lua console. 19 | */ 20 | public class LuaConsoleTest { 21 | // -- Test cases 22 | /** 23 | * Tests the Lua console. 24 | */ 25 | @Test 26 | public void testLuaConsole () 27 | { 28 | LuaConsole luaConsole = new LuaConsole(new String[] { "a", "b" }); 29 | LuaState luaState = luaConsole.getLuaState(); 30 | assertNotNull(luaState); 31 | assertTrue(luaState.isOpen()); 32 | luaState.getGlobal("argv"); 33 | assertTrue(luaState.isTable(1)); 34 | assertEquals(2, luaState.rawLen(1)); 35 | luaState.rawGet(1, 1); 36 | assertTrue(luaState.isString(2)); 37 | assertEquals("a", luaState.toString(2)); 38 | luaState.pop(2); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /jnlua/src/main/c/Win32/Makefile: -------------------------------------------------------------------------------- 1 | # Paths 2 | JDK_DIR=C:\Program Files\Java\jdk1.6.0_19 3 | LUA_DIR=C:\Users\Public\Apps\Lua\5.2\lib64 4 | LUA_VERSION=52 5 | VERSION=52 6 | 7 | # Tools 8 | CC=cl 9 | LD=link 10 | MT=mt 11 | 12 | # Default arguments 13 | CFLAGS=/nologo /c /O2 /GL /W3 /DNDEBUG 14 | LDFLAGS=/nologo /DLL /LTCG 15 | MTFLAGS=-nologo 16 | 17 | # Description blocks 18 | all: jnlua$(VERSION).dll javavm.dll 19 | 20 | jnlua$(VERSION).dll: jnlua.obj 21 | $(LD) $(LDFLAGS) /OUT:jnlua$(VERSION).dll /LIBPATH:"$(LUA_DIR)" jnlua.obj lua$(LUA_VERSION).lib 22 | 23 | jnlua.obj: ..\jnlua.c 24 | $(CC) $(CFLAGS) /MD /DLUA_BUILD_AS_DLL /I"$(JDK_DIR)\include" /I"$(JDK_DIR)\include\win32" /I"$(LUA_DIR)\include" ..\jnlua.c 25 | 26 | javavm.dll: javavm.obj 27 | $(LD) $(LDFLAGS) /OUT:javavm.dll /LIBPATH:"$(LUA_DIR)" /LIBPATH:"$(JDK_DIR)\lib" javavm.obj lua$(LUA_VERSION).lib jvm.lib 28 | 29 | javavm.obj: ..\javavm.c ..\javavm.h 30 | $(CC) $(CFLAGS) /MD /DLUA_BUILD_AS_DLL /DLUA_LIB /I"$(JDK_DIR)\include" /I"$(JDK_DIR)\include\win32" /I"$(LUA_DIR)\include" ..\javavm.c 31 | 32 | clean: 33 | -del jnlua$(VERSION).dll jnlua$(VERSION).dll.manifest jnlua$(VERSION).exp jnlua$(VERSION).lib jnlua.obj 34 | -del javavm.dll javavm.dll.manifest javavm.exp javavm.lib javavm.obj 35 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/script/CompiledLuaScript.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.script; 7 | 8 | import java.io.ByteArrayInputStream; 9 | 10 | import javax.script.CompiledScript; 11 | import javax.script.ScriptContext; 12 | import javax.script.ScriptEngine; 13 | import javax.script.ScriptException; 14 | 15 | /** 16 | * Compiled script implementation conforming to JSR 223: Scripting for the Java 17 | * Platform. 18 | */ 19 | class CompiledLuaScript extends CompiledScript { 20 | // -- State 21 | private LuaScriptEngine engine; 22 | private byte[] script; 23 | 24 | // -- Construction 25 | /** 26 | * Creates a new instance. 27 | */ 28 | public CompiledLuaScript(LuaScriptEngine engine, byte[] script) { 29 | this.engine = engine; 30 | this.script = script; 31 | } 32 | 33 | // -- CompiledScript methods 34 | @Override 35 | public Object eval(ScriptContext context) throws ScriptException { 36 | synchronized (engine.getLuaState()) { 37 | engine.loadChunk(new ByteArrayInputStream(script), context, "b"); 38 | return engine.callChunk(context); 39 | } 40 | } 41 | 42 | @Override 43 | public ScriptEngine getEngine() { 44 | return engine; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jnlua/src/test/java/com/naef/jnlua/test/JavaFunctionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.test; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | import org.junit.Test; 11 | 12 | import com.naef.jnlua.JavaFunction; 13 | import com.naef.jnlua.LuaState; 14 | 15 | /** 16 | * Contains unit tests for Java functions. 17 | */ 18 | public class JavaFunctionTest extends AbstractLuaTest { 19 | // -- Test cases 20 | /** 21 | * Tests the call of a Lua function implemented in Java. 22 | */ 23 | @Test 24 | public void testJavaFunction() throws Exception { 25 | // Push function 26 | luaState.pushJavaFunction(new Add()); 27 | 28 | // Push arguments 29 | luaState.pushNumber(1); 30 | luaState.pushNumber(1); 31 | luaState.call(2, 1); 32 | 33 | // Test result 34 | assertEquals(2.0, luaState.toNumber(1), 0.0); 35 | luaState.pop(1); 36 | 37 | // Finish 38 | assertEquals(0, luaState.getTop()); 39 | } 40 | 41 | // -- Private classes 42 | /** 43 | * A simple Lua function. 44 | */ 45 | private static class Add implements JavaFunction { 46 | public int invoke(LuaState luaState) { 47 | double a = luaState.toNumber(1); 48 | double b = luaState.toNumber(2); 49 | luaState.setTop(0); 50 | luaState.pushNumber(a + b); 51 | return 1; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /jnlua/src/test/java/com/naef/jnlua/test/JavaModuleTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.test; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import org.junit.Test; 16 | 17 | import com.naef.jnlua.JavaModule; 18 | 19 | /** 20 | * Contains unit tests for the Java module. 21 | */ 22 | public class JavaModuleTest extends AbstractLuaTest { 23 | // ---- Test cases 24 | /** 25 | * Tests the toTable method. 26 | */ 27 | @Test 28 | public void testToTable() { 29 | // Map 30 | Map map = new HashMap(); 31 | luaState.pushJavaObject(JavaModule.getInstance().toTable(map)); 32 | luaState.setGlobal("map"); 33 | luaState.load("map.x = 1", "=testToTable"); 34 | luaState.call(0, 0); 35 | assertEquals(Double.valueOf(1.0), map.get("x")); 36 | 37 | // List 38 | List list = new ArrayList(); 39 | luaState.pushJavaObject(JavaModule.getInstance().toTable(list)); 40 | luaState.setGlobal("list"); 41 | luaState.load("list[1] = 1", "=testToList"); 42 | luaState.call(0, 0); 43 | assertEquals(Double.valueOf(1.0), list.get(0)); 44 | } 45 | 46 | /** 47 | * Tests the Java module from Lua. 48 | */ 49 | @Test 50 | public void testJavaModule() throws Exception { 51 | runTest("com/naef/jnlua/test/JavaModule.lua", "JavaModule"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/LuaError.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Contains information about a Lua error condition. This object is created in 10 | * the native library. 11 | */ 12 | class LuaError { 13 | // -- State 14 | private String message; 15 | private LuaStackTraceElement[] luaStackTrace; 16 | private Throwable cause; 17 | 18 | // -- Construction 19 | /** 20 | * Creates a new instance. 21 | */ 22 | public LuaError(String message, Throwable cause) { 23 | this.message = message; 24 | this.cause = cause; 25 | } 26 | 27 | // -- Properties 28 | /** 29 | * Returns the message. 30 | */ 31 | public String getMessage() { 32 | return message; 33 | } 34 | 35 | /** 36 | * Returns the Lua stack trace. 37 | */ 38 | public LuaStackTraceElement[] getLuaStackTrace() { 39 | return luaStackTrace; 40 | } 41 | 42 | /** 43 | * Returns the cause. 44 | */ 45 | public Throwable getCause() { 46 | return cause; 47 | } 48 | 49 | // -- Object methods 50 | @Override 51 | public String toString() { 52 | StringBuffer sb = new StringBuffer(); 53 | if (message != null) { 54 | sb.append(message); 55 | } 56 | if (cause != null) { 57 | sb.append(cause); 58 | } 59 | return sb.toString(); 60 | } 61 | 62 | // -- Package private methods 63 | /** 64 | * Sets the Lua stack trace. 65 | */ 66 | void setLuaStackTrace(LuaStackTraceElement[] luaStackTrace) { 67 | this.luaStackTrace = luaStackTrace; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /jnlua/src/test/java/com/naef/jnlua/test/AbstractLuaTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.test; 7 | 8 | import java.io.InputStream; 9 | 10 | import org.junit.After; 11 | import org.junit.Before; 12 | 13 | import com.naef.jnlua.LuaState; 14 | 15 | /** 16 | * Abstract base class for JNLua unit tests. 17 | */ 18 | public abstract class AbstractLuaTest { 19 | // -- State 20 | protected LuaState luaState; 21 | 22 | // -- Setup 23 | /** 24 | * Performs setup. 25 | */ 26 | @Before 27 | public void setup() throws Exception { 28 | luaState = new LuaState(); 29 | } 30 | 31 | /** 32 | * Performs teardown. 33 | */ 34 | @After 35 | public void teardown() throws Throwable { 36 | if (luaState != null) { 37 | try { 38 | luaState.close(); 39 | } catch (Throwable e) { 40 | e.printStackTrace(); 41 | throw e; 42 | } 43 | } 44 | } 45 | 46 | // -- Protected method 47 | /** 48 | * Runs a Lua-based test. 49 | */ 50 | protected void runTest(String source, String moduleName) throws Exception { 51 | // Open libraries 52 | luaState.openLibs(); 53 | 54 | // Load 55 | InputStream inputStream = getClass().getClassLoader() 56 | .getResourceAsStream(source); 57 | luaState.load(inputStream, "=" + moduleName, "t"); 58 | luaState.pushString(moduleName); 59 | luaState.call(1, 0); 60 | 61 | // Run all module functions beginning with "test" 62 | luaState.getGlobal(moduleName); 63 | luaState.pushNil(); 64 | while (luaState.next(1)) { 65 | String key = luaState.toString(-2); 66 | if (key.startsWith("test") && luaState.isFunction(-1)) { 67 | luaState.call(0, 0); 68 | } else { 69 | luaState.pop(1); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/TypedJavaObject.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Represents a Java object with an explicit type. 10 | * 11 | *

12 | * The interface is implemented by objects needing to specify an explicit type 13 | * for a wrapped object. This typically occurs in casting situations. Such typed 14 | * Java object are considered weak since they have no representative 15 | * value of their own. Weak typed Java objects always convert to wrapped object. 16 | *

17 | * 18 | *

19 | * The interface is also implemented by objects wrapping another object and 20 | * offering transparent conversion to the wrapped object if needed. This 21 | * situation for example occurs when an object implements the 22 | * {@link com.naef.jnlua.JavaReflector} interface to provide custom Java 23 | * reflection for a wrapped object and at the same time wants to ensure 24 | * transparent conversion to the wrapped object if needed. Such typed Java 25 | * objects are considered strong since they have a representative value 26 | * of their own. Strong typed Java objects convert to wrapped object only if 27 | * this is required to satisfy a type conversion. 28 | *

29 | */ 30 | public interface TypedJavaObject { 31 | /** 32 | * Returns the object. 33 | * 34 | * @return the object 35 | */ 36 | public Object getObject(); 37 | 38 | /** 39 | * Returns the type. 40 | * 41 | * @return the type 42 | */ 43 | public Class getType(); 44 | 45 | /** 46 | * Returns whether this is a strong typed Java object. 47 | * 48 | * @return true if this typed Java object is strong, and 49 | * false if it is weak 50 | */ 51 | public boolean isStrong(); 52 | } 53 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/script/LuaBindings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.script; 7 | 8 | import javax.script.Bindings; 9 | 10 | import com.naef.jnlua.LuaState; 11 | import com.naef.jnlua.util.AbstractTableMap; 12 | 13 | /** 14 | * Lua bindings implementation conforming to JSR 223: Scripting for the Java 15 | * Platform. 16 | */ 17 | class LuaBindings extends AbstractTableMap implements Bindings { 18 | // -- State 19 | private LuaScriptEngine scriptEngine; 20 | 21 | // -- Construction 22 | public LuaBindings(LuaScriptEngine scriptEngine) { 23 | this.scriptEngine = scriptEngine; 24 | } 25 | 26 | // -- AbstractTableMap methods 27 | @Override 28 | protected void checkKey(Object key) { 29 | super.checkKey(key); 30 | if (!(key instanceof String)) { 31 | throw new IllegalArgumentException("key must be a string"); 32 | } 33 | if (((String) key).length() == 0) { 34 | throw new IllegalArgumentException("key must not be empty"); 35 | } 36 | } 37 | 38 | @Override 39 | protected boolean filterKeys() { 40 | return true; 41 | } 42 | 43 | @Override 44 | protected boolean acceptKey(int index) { 45 | return getLuaState().isString(index) 46 | && getLuaState().toString(index).length() > 0; 47 | } 48 | 49 | @Override 50 | protected String convertKey(int index) { 51 | return getLuaState().toString(index); 52 | } 53 | 54 | // -- LuaProxy methods 55 | @Override 56 | public LuaState getLuaState() { 57 | return scriptEngine.getLuaState(); 58 | } 59 | 60 | @Override 61 | public void pushValue() { 62 | getLuaState().rawGet(LuaState.REGISTRYINDEX, LuaState.RIDX_GLOBALS); 63 | } 64 | 65 | // -- Package-private methods 66 | /** 67 | * Returns the script engine. 68 | */ 69 | LuaScriptEngine getScriptEngine() { 70 | return scriptEngine; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/Converter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Converts between Lua values and Java objects. 10 | */ 11 | public interface Converter { 12 | /** 13 | * Returns the type distance between a Lua value and a formal Java type. 14 | * Distances are comparable for the same Lua value only. If a Lua value 15 | * cannot be converted to the specified formal type, the method returns 16 | * Integer.MAX_VALUE. 17 | * 18 | * @param luaState 19 | * the Lua state 20 | * @param index 21 | * the stack index containing the value 22 | * @param formalType 23 | * the formal Java type 24 | * @return the type distance, or Integer.MAX_VALUE if the 25 | * conversion is not supported 26 | */ 27 | public int getTypeDistance(LuaState luaState, int index, Class formalType); 28 | 29 | /** 30 | * Converts a Lua value to a Java object of the specified formal type. 31 | * 32 | *

33 | * If the Lua value is nil, the method returns 34 | * null. 35 | *

36 | * 37 | * @param luaState 38 | * the Lua state 39 | * @param index 40 | * the stack index containing the value 41 | * @return the Java object, or null 42 | * @param formalType 43 | * the formal Java type 44 | * @throws ClassCastException 45 | * if the conversion is not possible 46 | */ 47 | public T convertLuaValue(LuaState luaState, int index, 48 | Class formalType); 49 | 50 | /** 51 | * Converts a Java object to a Lua value and pushes that value on the stack. 52 | * 53 | *

54 | * If the object is null, the method pushes nil. 55 | *

56 | * 57 | * @param luaState 58 | * the Lua state 59 | * @param object 60 | * the Java object, or null 61 | */ 62 | public void convertJavaObject(LuaState luaState, Object object); 63 | } 64 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/NativeSupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Loads the JNLua native library. 10 | * 11 | * The class provides and configures a default loader implementation that loads 12 | * the JNLua native library by means of the System.loadLibrary 13 | * method. In some situations, you may want to override this behavior. For 14 | * example, when using JNLua as an OSGi bundle, the native library is loaded by 15 | * the OSGi runtime. Therefore, the OSGi bundle activator replaces the loader by 16 | * a no-op implementaion. Note that the loader must be configured before 17 | * LuaState is accessed. 18 | */ 19 | public final class NativeSupport { 20 | // -- Static 21 | private static final NativeSupport INSTANCE = new NativeSupport(); 22 | 23 | // -- State 24 | private Loader loader = new DefaultLoader(); 25 | 26 | /** 27 | * Returns the instance. 28 | * 29 | * @return the instance 30 | */ 31 | public static NativeSupport getInstance() { 32 | return INSTANCE; 33 | } 34 | 35 | // -- Construction 36 | /** 37 | * Private constructor to prevent external instantiation. 38 | */ 39 | private NativeSupport() { 40 | } 41 | 42 | // -- Properties 43 | /** 44 | * Return the native library loader. 45 | * 46 | * @return the loader 47 | */ 48 | public Loader getLoader() { 49 | return loader; 50 | } 51 | 52 | /** 53 | * Sets the native library loader. 54 | * 55 | * @param loader 56 | * the loader 57 | */ 58 | public void setLoader(Loader loader) { 59 | if (loader == null) { 60 | throw new NullPointerException("loader must not be null"); 61 | } 62 | this.loader = loader; 63 | } 64 | 65 | // -- Member types 66 | /** 67 | * Loads the library. 68 | */ 69 | public interface Loader { 70 | public void load(); 71 | } 72 | 73 | private class DefaultLoader implements Loader { 74 | @Override 75 | public void load() { 76 | System.loadLibrary("jnlua52"); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /jnlua/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.naef 5 | jnlua 6 | 1.0.4 7 | jar 8 | JNLua 9 | Java Native Lua 10 | http://code.google.com/p/jnlua/ 11 | 12 | scm:svn:http://jnlua.googlecode.com/svn/trunk/ 13 | http://jnlua.googlecode.com/svn/trunk/ 14 | 15 | 16 | 17 | MIT License 18 | /LICENSE.txt 19 | manual 20 | 21 | 22 | 23 | André Naef 24 | http://www.naef.com/ 25 | 26 | 27 | 28 | junit 29 | junit 30 | 4.7 31 | jar 32 | test 33 | 34 | 35 | 36 | 37 | 38 | org.apache.maven.plugins 39 | maven-compiler-plugin 40 | 41 | 1.6 42 | 1.6 43 | 44 | 45 | 46 | org.apache.maven.plugins 47 | maven-surefire-plugin 48 | 49 | 50 | **/*Test.java 51 | 52 | 53 | 54 | 55 | org.apache.maven.plugins 56 | maven-javadoc-plugin 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-assembly-plugin 63 | 64 | 65 | bin 66 | src 67 | 68 | 69 | src/main/assembly/native.xml 70 | 71 | 72 | 73 | 74 | make-assembly 75 | package 76 | 77 | single 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/util/AbstractTableList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.util; 7 | 8 | import java.util.AbstractList; 9 | import java.util.RandomAccess; 10 | 11 | import com.naef.jnlua.LuaState; 12 | import com.naef.jnlua.LuaValueProxy; 13 | 14 | /** 15 | * Abstract list implementation backed by a Lua table. 16 | */ 17 | public abstract class AbstractTableList extends AbstractList implements 18 | RandomAccess, LuaValueProxy { 19 | // -- Construction 20 | /** 21 | * Creates a new instance. 22 | */ 23 | public AbstractTableList() { 24 | } 25 | 26 | // -- List methods 27 | @Override 28 | public void add(int index, Object element) { 29 | LuaState luaState = getLuaState(); 30 | synchronized (luaState) { 31 | int size = size(); 32 | if (index < 0 || index > size) { 33 | throw new IndexOutOfBoundsException("index: " + index 34 | + ", size: " + size); 35 | } 36 | pushValue(); 37 | luaState.tableMove(-1, index + 1, index + 2, size - index); 38 | luaState.pushJavaObject(element); 39 | luaState.rawSet(-2, index + 1); 40 | luaState.pop(1); 41 | } 42 | } 43 | 44 | @Override 45 | public Object get(int index) { 46 | LuaState luaState = getLuaState(); 47 | synchronized (luaState) { 48 | int size = size(); 49 | if (index < 0 || index >= size) { 50 | throw new IndexOutOfBoundsException("index: " + index 51 | + ", size: " + size); 52 | } 53 | pushValue(); 54 | luaState.rawGet(-1, index + 1); 55 | try { 56 | return luaState.toJavaObject(-1, Object.class); 57 | } finally { 58 | luaState.pop(2); 59 | } 60 | } 61 | } 62 | 63 | @Override 64 | public Object remove(int index) { 65 | LuaState luaState = getLuaState(); 66 | synchronized (luaState) { 67 | int size = size(); 68 | if (index < 0 || index >= size) { 69 | throw new IndexOutOfBoundsException("index: " + index 70 | + ", size: " + size); 71 | } 72 | Object oldValue = get(index); 73 | pushValue(); 74 | luaState.tableMove(-1, index + 2, index + 1, size - index - 1); 75 | luaState.pushNil(); 76 | luaState.rawSet(-2, size); 77 | luaState.pop(1); 78 | return oldValue; 79 | } 80 | } 81 | 82 | @Override 83 | public Object set(int index, Object element) { 84 | LuaState luaState = getLuaState(); 85 | synchronized (luaState) { 86 | int size = size(); 87 | if (index < 0 || index >= size) { 88 | throw new IndexOutOfBoundsException("index: " + index 89 | + ", size: " + size); 90 | } 91 | Object oldValue = get(index); 92 | pushValue(); 93 | luaState.pushJavaObject(element); 94 | luaState.rawSet(-2, index + 1); 95 | luaState.pop(1); 96 | return oldValue; 97 | } 98 | } 99 | 100 | @Override 101 | public int size() { 102 | LuaState luaState = getLuaState(); 103 | synchronized (luaState) { 104 | pushValue(); 105 | try { 106 | return luaState.rawLen(-1); 107 | } finally { 108 | luaState.pop(1); 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/JavaReflector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Reflects Java objects for access from Lua. 10 | * 11 | *

12 | * The interface can be implemented to provide a generic Java reflector that is 13 | * then configured in a Lua state. It can also be implemented by individual Java 14 | * classes to provide class-specific Java reflection. If an object implements 15 | * the Java reflector interface, its own Java reflector is queried first for a 16 | * requested metamethod. Only if the metamethod requested is not supported, the 17 | * Java reflector configured in the Lua state is queried. 18 | *

19 | */ 20 | public interface JavaReflector { 21 | /** 22 | * Returns the metamethod implementation of this Java reflector for the 23 | * specified metamethod. If this reflector does not support the metamethod, 24 | * the method returns null. 25 | * 26 | * @param metamethod 27 | * the metamethod 28 | * @return the implementation, or null if this Java reflector 29 | * does not support the metamethod 30 | */ 31 | public JavaFunction getMetamethod(Metamethod metamethod); 32 | 33 | // -- Nested types 34 | /** 35 | * Lua metamethod. 36 | */ 37 | public enum Metamethod { 38 | /** 39 | * __index metamethod. 40 | */ 41 | INDEX, 42 | 43 | /** 44 | * __newindex metamethod. 45 | */ 46 | NEWINDEX, 47 | 48 | /** 49 | * __len metamethod. 50 | */ 51 | LEN, 52 | 53 | /** 54 | * __eq metamethod. 55 | */ 56 | EQ, 57 | 58 | /** 59 | * __lt metamethod. 60 | */ 61 | LT, 62 | 63 | /** 64 | * __le metamethod. 65 | */ 66 | LE, 67 | 68 | /** 69 | * __unm metamethod. 70 | */ 71 | UNM, 72 | 73 | /** 74 | * __add metamethod. 75 | */ 76 | ADD, 77 | 78 | /** 79 | * __sub metamethod. 80 | */ 81 | SUB, 82 | 83 | /** 84 | * __mul metamethod. 85 | */ 86 | MUL, 87 | 88 | /** 89 | * __div metamethod. 90 | */ 91 | DIV, 92 | 93 | /** 94 | * __mod metamethod. 95 | */ 96 | MOD, 97 | 98 | /** 99 | * __pow metamethod. 100 | */ 101 | POW, 102 | 103 | /** 104 | * __concat metamethod. 105 | */ 106 | CONCAT, 107 | 108 | /** 109 | * __call metamethod. 110 | */ 111 | CALL, 112 | 113 | /** 114 | * __tostring metamethod. 115 | */ 116 | TOSTRING, 117 | 118 | /** 119 | * __pairs metamethod, 120 | */ 121 | PAIRS, 122 | 123 | /** 124 | * __ipairs metamethod, 125 | */ 126 | IPAIRS, 127 | 128 | /** 129 | * __javafields metamethod. 130 | */ 131 | JAVAFIELDS, 132 | 133 | /** 134 | * __javamethods metamethod. 135 | */ 136 | JAVAMETHODS, 137 | 138 | /** 139 | * __javaproperties metamethod. 140 | */ 141 | JAVAPROPERTIES; 142 | 143 | // -- Operations 144 | /** 145 | * Returns the Lua metamethod name. 146 | * 147 | * @return the metamethod name 148 | */ 149 | public String getMetamethodName() { 150 | return "__" + toString().toLowerCase(); 151 | } 152 | }; 153 | } 154 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/LuaRuntimeException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | import java.io.PrintStream; 9 | import java.io.PrintWriter; 10 | 11 | /** 12 | * Indicates a Lua runtime error. 13 | * 14 | *

15 | * This exception is thrown if a Lua runtime error occurs. The class provides 16 | * access to the Lua stack trace by means of the {@link #getLuaStackTrace()} 17 | * method. 18 | *

19 | */ 20 | public class LuaRuntimeException extends LuaException { 21 | // -- Static 22 | private static final long serialVersionUID = 1L; 23 | private static final LuaStackTraceElement[] EMPTY_LUA_STACK_TRACE = new LuaStackTraceElement[0]; 24 | 25 | // -- State 26 | private LuaStackTraceElement[] luaStackTrace; 27 | 28 | // -- Construction 29 | /** 30 | * Creates a new instance. The instance is created with an empty Lua stack 31 | * trace. 32 | * 33 | * @param msg 34 | * the message 35 | */ 36 | public LuaRuntimeException(String msg) { 37 | super(msg); 38 | luaStackTrace = EMPTY_LUA_STACK_TRACE; 39 | } 40 | 41 | /** 42 | * Creates a new instance. The instance is created with an empty Lua stack 43 | * trace. 44 | * 45 | * @param msg 46 | * the message 47 | * @param cause 48 | * the cause of this exception 49 | */ 50 | public LuaRuntimeException(String msg, Throwable cause) { 51 | super(msg, cause); 52 | luaStackTrace = EMPTY_LUA_STACK_TRACE; 53 | } 54 | 55 | /** 56 | * Creates a new instance. The instance is created with an empty Lua stack 57 | * trace. 58 | * 59 | * @param cause 60 | * the cause of this exception 61 | */ 62 | public LuaRuntimeException(Throwable cause) { 63 | super(cause); 64 | luaStackTrace = EMPTY_LUA_STACK_TRACE; 65 | } 66 | 67 | // -- Properties 68 | /** 69 | * Returns the Lua stack trace of this runtime exception. 70 | */ 71 | public LuaStackTraceElement[] getLuaStackTrace() { 72 | return luaStackTrace.clone(); 73 | } 74 | 75 | // -- Operations 76 | /** 77 | * Prints this exception and its Lua stack trace to the standard error 78 | * stream. 79 | */ 80 | public void printLuaStackTrace() { 81 | printLuaStackTrace(System.err); 82 | } 83 | 84 | /** 85 | * Prints this exception and its Lua stack trace to the specified print 86 | * stream. 87 | * 88 | * @param s 89 | * the print stream 90 | */ 91 | public void printLuaStackTrace(PrintStream s) { 92 | synchronized (s) { 93 | s.println(this); 94 | for (int i = 0; i < luaStackTrace.length; i++) { 95 | s.println("\tat " + luaStackTrace[i]); 96 | } 97 | } 98 | } 99 | 100 | /** 101 | * Prints this exception and its Lua stack trace to the specified print 102 | * writer. 103 | * 104 | * @param s 105 | * the print writer 106 | */ 107 | public void printLuaStackTrace(PrintWriter s) { 108 | synchronized (s) { 109 | s.println(this); 110 | for (int i = 0; i < luaStackTrace.length; i++) { 111 | s.println("\tat " + luaStackTrace[i]); 112 | } 113 | } 114 | } 115 | 116 | // -- Package private methods 117 | /** 118 | * Sets the Lua error in this exception. The method in invoked from the 119 | * native library. 120 | */ 121 | void setLuaError(LuaError luaError) { 122 | initCause(luaError.getCause()); 123 | luaStackTrace = luaError.getLuaStackTrace(); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/LuaStackTraceElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | /** 9 | * Represents an execution point in a Lua stack trace. 10 | */ 11 | public class LuaStackTraceElement { 12 | // -- State 13 | private String functionName; 14 | private String sourceName; 15 | private int lineNumber; 16 | 17 | // -- Construction 18 | /** 19 | * Creates a new instance. 20 | * 21 | * @param sourceName 22 | * the source name, or null if unavailable 23 | * @param functionName 24 | * the function name, or null if unavailable 25 | * @param lineNumber 26 | * the line number, or a negative number if unavailable 27 | */ 28 | public LuaStackTraceElement(String functionName, String sourceName, 29 | int lineNumber) { 30 | this.functionName = functionName; 31 | this.sourceName = sourceName; 32 | this.lineNumber = lineNumber; 33 | } 34 | 35 | // -- Properties 36 | /** 37 | * Returns the name of the function containing the execution point 38 | * represented by this stack trace element. If there is no function name for 39 | * the execution point, the method returns null. 40 | * 41 | * @return the name of the function containing the execution point 42 | * represented by this stack trace element, or null 43 | */ 44 | public String getFunctionName() { 45 | return functionName; 46 | } 47 | 48 | /** 49 | * Returns the name of the source containing the execution point represented 50 | * by this this stack trace element. The source name is passed to the Lua 51 | * state when the Lua source code is loaded. If there is no source name for 52 | * the execution point, the method returns null. 53 | * 54 | * @return the source name, or null 55 | * @see LuaState#load(java.io.InputStream, String, String) 56 | * @see LuaState#load(String, String) 57 | */ 58 | public String getSourceName() { 59 | return sourceName; 60 | } 61 | 62 | /** 63 | * Returns the line number in the source containing the execution point 64 | * represented by this stack trace element. If there is no line number for 65 | * the execution point, the method returns a negative number. 66 | * 67 | * @return the line number, or a negative number if there is no line number 68 | */ 69 | public int getLineNumber() { 70 | return lineNumber; 71 | } 72 | 73 | // Object methods 74 | @Override 75 | public int hashCode() { 76 | int result = functionName != null ? functionName.hashCode() : 0; 77 | result = result * 65599 + sourceName != null ? sourceName.hashCode() 78 | : 0; 79 | result = result * 65599 + lineNumber; 80 | return result; 81 | } 82 | 83 | @Override 84 | public boolean equals(Object obj) { 85 | if (obj == this) { 86 | return true; 87 | } 88 | if (!(obj instanceof LuaStackTraceElement)) { 89 | return false; 90 | } 91 | LuaStackTraceElement other = (LuaStackTraceElement) obj; 92 | return safeEquals(functionName, other.functionName) 93 | && safeEquals(sourceName, other.sourceName) 94 | && lineNumber == other.lineNumber; 95 | } 96 | 97 | @Override 98 | public String toString() { 99 | StringBuffer sb = new StringBuffer(); 100 | if (functionName != null) { 101 | sb.append(functionName); 102 | } else { 103 | sb.append("(Unknown Function)"); 104 | } 105 | sb.append(" ("); 106 | if (sourceName != null) { 107 | sb.append(sourceName); 108 | if (lineNumber >= 0) { 109 | sb.append(':'); 110 | sb.append(lineNumber); 111 | } 112 | } else { 113 | sb.append("External Function"); 114 | } 115 | sb.append(')'); 116 | return sb.toString(); 117 | } 118 | 119 | // -- Private methods 120 | /** 121 | * Returns whether two objects are equal, handling null. 122 | */ 123 | private boolean safeEquals(Object a, Object b) { 124 | return a == b || a != null && a.equals(b); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /jnlua/src/test/java/com/naef/jnlua/test/CollectionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.test; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | import static org.junit.Assert.assertFalse; 10 | import static org.junit.Assert.assertNull; 11 | import static org.junit.Assert.assertTrue; 12 | 13 | import java.util.ArrayList; 14 | import java.util.HashMap; 15 | import java.util.Iterator; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | import org.junit.Test; 20 | 21 | /** 22 | * Contains unit tests for collections backed by Lua tables. 23 | */ 24 | public class CollectionTest extends AbstractLuaTest { 25 | // -- Test cases 26 | /** 27 | * Tests the map. 28 | */ 29 | @SuppressWarnings("unchecked") 30 | @Test 31 | public void testMap() throws Exception { 32 | // Get a map backed by Lua 33 | luaState.newTable(); 34 | Map map = luaState.toJavaObject(-1, Map.class); 35 | 36 | // isEmpty(), size() 37 | assertTrue(map.isEmpty()); 38 | assertEquals(0, map.size()); 39 | 40 | // put() 41 | map.put("t", "test"); 42 | assertFalse(map.isEmpty()); 43 | assertEquals(1, map.size()); 44 | luaState.getField(-1, "t"); 45 | assertEquals("test", luaState.toString(-1)); 46 | luaState.pop(1); 47 | 48 | // containsKey() 49 | assertTrue(map.containsKey("t")); 50 | 51 | // containsValue() 52 | assertTrue(map.containsValue("test")); 53 | 54 | // get() 55 | assertEquals("test", map.get("t")); 56 | 57 | // putAll() 58 | Map map2 = new HashMap(); 59 | map2.put("v", "test2"); 60 | map.putAll(map2); 61 | assertEquals("test2", map.get("v")); 62 | luaState.getField(-1, "v"); 63 | assertEquals("test2", luaState.toString(-1)); 64 | luaState.pop(1); 65 | 66 | // remove() 67 | map.remove("v"); 68 | assertNull(map.get("v")); 69 | luaState.getField(-1, "v"); 70 | assertTrue(luaState.isNil(-1)); 71 | luaState.pop(1); 72 | 73 | // entrySet() 74 | int count = 0; 75 | for (Map.Entry entry : map.entrySet()) { 76 | if (entry.getKey() != null) { 77 | count++; 78 | } 79 | } 80 | assertEquals(map.size(), count); 81 | 82 | // values() 83 | boolean found = false; 84 | for (Object object : map.values()) { 85 | if (object.equals("test")) { 86 | found = true; 87 | break; 88 | } 89 | } 90 | assertTrue(found); 91 | 92 | // keySet() 93 | assertTrue(map.keySet().contains("t")); 94 | Iterator iterator = map.keySet().iterator(); 95 | while (iterator.hasNext()) { 96 | if (iterator.next().equals("t")) { 97 | iterator.remove(); 98 | } 99 | } 100 | assertFalse(map.containsKey("t")); 101 | 102 | // clear() 103 | map.clear(); 104 | assertEquals(0, map.size()); 105 | assertTrue(map.isEmpty()); 106 | 107 | // Finish 108 | luaState.pop(1); 109 | assertEquals(0, luaState.getTop()); 110 | } 111 | 112 | /** 113 | * Tests the list. 114 | */ 115 | @SuppressWarnings("unchecked") 116 | @Test 117 | public void testList() throws Exception { 118 | // Get a list backed by Lua 119 | luaState.newTable(); 120 | List list = luaState.toJavaObject(-1, List.class); 121 | 122 | // isEmpty(), size() 123 | assertTrue(list.isEmpty()); 124 | assertEquals(0, list.size()); 125 | 126 | // add() 127 | list.add("test"); 128 | assertFalse(list.isEmpty()); 129 | assertEquals(1, list.size()); 130 | luaState.rawGet(-1, 1); 131 | assertEquals("test", luaState.toString(-1)); 132 | luaState.pop(1); 133 | 134 | // contains() 135 | assertTrue(list.contains("test")); 136 | 137 | // get() 138 | assertEquals("test", list.get(0)); 139 | 140 | // addAll() 141 | List list2 = new ArrayList(); 142 | list2.add("test2"); 143 | list.addAll(0, list2); 144 | assertEquals("test2", list.get(0)); 145 | luaState.rawGet(-1, 1); 146 | assertEquals("test2", luaState.toString(-1)); 147 | luaState.pop(1); 148 | 149 | // remove() 150 | list.remove(0); 151 | assertEquals(1, list.size()); 152 | assertEquals(1, luaState.rawLen(-1)); 153 | luaState.rawGet(-1, 1); 154 | assertEquals("test", luaState.toString(-1)); 155 | luaState.pop(1); 156 | 157 | // iterator() 158 | int count = 0; 159 | for (Object object : list) { 160 | assertEquals("test", object); 161 | count++; 162 | } 163 | assertEquals(list.size(), count); 164 | 165 | // clear() 166 | list.clear(); 167 | assertEquals(0, list.size()); 168 | assertTrue(list.isEmpty()); 169 | 170 | // Finish 171 | luaState.pop(1); 172 | assertEquals(0, luaState.getTop()); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /jnlua/src/test/java/com/naef/jnlua/test/fixture/TestObject.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.test.fixture; 7 | 8 | import java.math.BigDecimal; 9 | import java.math.BigInteger; 10 | 11 | /** 12 | * A test object for reflection testing. 13 | */ 14 | public class TestObject implements Comparable { 15 | // -- Static 16 | /** 17 | * A public static field. 18 | */ 19 | public static String TEST_FIELD = "test"; 20 | 21 | /* 22 | * Public test fields for all types. 23 | */ 24 | public boolean booleanField; 25 | public byte byteField; 26 | public byte[] byteArrayField; 27 | public short shortField; 28 | public int intField; 29 | public long longField; 30 | public float floatField; 31 | public double doubleField; 32 | public BigInteger bigIntegerField; 33 | public BigDecimal bigDecimalField; 34 | public char charField; 35 | public String stringField; 36 | 37 | /** 38 | * A private field. 39 | */ 40 | private String foo; 41 | 42 | /** 43 | * The value of this object. 44 | */ 45 | private int value; 46 | 47 | // -- Static methods 48 | /** 49 | * A public static method. 50 | */ 51 | public static String testStatic() { 52 | return "test"; 53 | } 54 | 55 | // -- State 56 | /** 57 | * A public field. 58 | */ 59 | public String testField = "test"; 60 | 61 | // -- Construction 62 | /** 63 | * Creates a new instance. 64 | */ 65 | public TestObject() { 66 | } 67 | 68 | /** 69 | * Creates a new instance. 70 | */ 71 | public TestObject(int value) { 72 | this.value = value; 73 | } 74 | 75 | // -- Methods 76 | /** 77 | * A public method. 78 | */ 79 | public String test() { 80 | return "test"; 81 | } 82 | 83 | // -- Properties 84 | /** 85 | * A property reader. 86 | */ 87 | public String getFoo() { 88 | return foo; 89 | } 90 | 91 | /** 92 | * A property writer. 93 | */ 94 | public void setFoo(String foo) { 95 | this.foo = foo; 96 | } 97 | 98 | /** 99 | * Returns the value of this object. 100 | */ 101 | public int getValue() { 102 | return value; 103 | } 104 | 105 | /** 106 | * Sets the value of this object. 107 | */ 108 | public void setValue(int value) { 109 | this.value = value; 110 | } 111 | 112 | // -- Overloaded methods 113 | /** 114 | * Overloaded method dispatch test method. 115 | */ 116 | public String overloadedSub(TestObject testObject) { 117 | return "super"; 118 | } 119 | 120 | /** 121 | * Overloaded method dispatch test method. 122 | */ 123 | public String overloadedSub(Sub sub) { 124 | return "sub"; 125 | } 126 | 127 | /** 128 | * Overloaded method dispatch test method. 129 | */ 130 | public String overloadedSibling(B b) { 131 | return "b"; 132 | } 133 | 134 | /** 135 | * Overloaded method dispatch test method. 136 | */ 137 | public String overloadedSibling(C c) { 138 | return "c"; 139 | } 140 | 141 | /** 142 | * Overloaded method dispatch test method. 143 | */ 144 | public String overloadedParentChild(A a) { 145 | return "a"; 146 | } 147 | 148 | /** 149 | * Overloaded method dispatch test method. 150 | */ 151 | public String overloadedParentChild(B c) { 152 | return "b"; 153 | } 154 | 155 | // -- Object methods 156 | @Override 157 | public int hashCode() { 158 | return value; 159 | } 160 | 161 | @Override 162 | public boolean equals(Object obj) { 163 | if (!(obj instanceof TestObject)) { 164 | return false; 165 | } 166 | TestObject other = (TestObject) obj; 167 | return value == other.value; 168 | } 169 | 170 | @Override 171 | public String toString() { 172 | return String.valueOf(value); 173 | } 174 | 175 | // -- Comparable methods 176 | @Override 177 | public int compareTo(TestObject o) { 178 | if (value < o.value) { 179 | return -1; 180 | } 181 | if (value > o.value) { 182 | return 1; 183 | } 184 | return 0; 185 | } 186 | 187 | // -- Nested classes 188 | /** 189 | * Subclass for method dispatch testing. 190 | */ 191 | public static class Sub extends TestObject { 192 | } 193 | 194 | /** 195 | * Class for method dispatch testing. 196 | */ 197 | public static class AB implements A, B { 198 | } 199 | 200 | /** 201 | * Class for method dispatch testing. 202 | */ 203 | public static class AC implements A, C { 204 | } 205 | 206 | /** 207 | * Class for method dispatch testing. 208 | */ 209 | public static class BC implements B, C { 210 | } 211 | 212 | // -- Nested interfaces 213 | /** 214 | * Overloaded method dispatch test interface. 215 | */ 216 | public interface A { 217 | } 218 | 219 | /** 220 | * Overloaded method dispatch test interface. 221 | */ 222 | public interface B extends A { 223 | } 224 | 225 | /** 226 | * Overloaded method dispatch test interface. 227 | */ 228 | public interface C extends A { 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /jnlua/src/test/resources/com/naef/jnlua/test/Reflection.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | $Id$ 3 | See LICENSE.txt for license terms. 4 | ]] 5 | 6 | module(..., package.seeall) 7 | 8 | -- General reflection test 9 | function testReflection () 10 | -- Static field 11 | local TestObject = java.require("com.naef.jnlua.test.fixture.TestObject") 12 | assert(TestObject.TEST_FIELD == "test") 13 | TestObject.TEST_FIELD = "" 14 | assert(TestObject.TEST_FIELD == "") 15 | TestObject.TEST_FIELD = "test" 16 | 17 | -- Static method 18 | assert(TestObject:testStatic() == "test") 19 | 20 | -- Field 21 | local testObject = TestObject:new() 22 | assert(testObject.testField == "test") 23 | testObject.testField = "" 24 | assert(testObject.testField == "") 25 | 26 | -- Method 27 | assert(testObject:test() == "test") 28 | 29 | -- Property 30 | assert(testObject.foo == nil) 31 | testObject.foo = "bar" 32 | assert(testObject.foo == "bar") 33 | end 34 | 35 | -- Type test 36 | function testTypes () 37 | -- Create 38 | local TestObject = java.require("com.naef.jnlua.test.fixture.TestObject") 39 | local testObject = TestObject:new() 40 | 41 | -- Test 42 | testObject.booleanField = true 43 | assert(testObject.booleanField) 44 | testObject.byteField = 1 45 | assert(testObject.byteField == 1) 46 | testObject.byteArrayField = "test" 47 | assert(testObject.byteArrayField == "test") 48 | testObject.shortField = 1 49 | assert(testObject.shortField == 1) 50 | testObject.intField = 1 51 | assert(testObject.intField == 1) 52 | testObject.longField = 1 53 | assert(testObject.longField == 1) 54 | testObject.floatField = 1 55 | assert(testObject.floatField == 1) 56 | testObject.doubleField = 1 57 | assert(testObject.doubleField == 1) 58 | testObject.bigIntegerField = 1 59 | assert(testObject.bigIntegerField == 1) 60 | testObject.bigDecimalField = 1 61 | assert(testObject.bigDecimalField == 1) 62 | testObject.charField = 1 63 | assert(testObject.charField == 1) 64 | testObject.stringField = "test" 65 | assert(testObject.stringField == "test") 66 | end 67 | 68 | -- Meta test 69 | function testMeta () 70 | -- Create 71 | local TestObject = java.require("com.naef.jnlua.test.fixture.TestObject") 72 | local testObject1 = TestObject:new(1) 73 | local testObject2 = TestObject:new(2) 74 | 75 | -- __index 76 | local int = java.require("int") 77 | local intArray = java.new(int, 2) 78 | assert(intArray[1] == 0) 79 | assert(intArray[2] == 0) 80 | 81 | -- __newindex 82 | intArray[1] = 1 83 | assert(intArray[1] == 1) 84 | 85 | -- __len 86 | assert(#intArray == 2) 87 | 88 | -- __eq 89 | assert(testObject1 ~= testObject2) 90 | testObject2.value = 1 91 | assert(testObject1 == testObject2) 92 | testObject2.value = 2 93 | 94 | -- __lt, __le 95 | assert(testObject1 < testObject2) 96 | assert(testObject2 <= testObject2) 97 | testObject1.value = 2 98 | testObject2.value = 1 99 | assert(testObject1 > testObject2) 100 | assert(testObject2 >= testObject2) 101 | testObject1.value = 1 102 | testObject2.value = 2 103 | 104 | -- __tostring 105 | assert(tostring(testObject1) == "1") 106 | assert(tostring(testObject2) == "2") 107 | 108 | -- __pairs 109 | local HashMap = java.require("java.util.HashMap") 110 | local hashMap = HashMap:new() 111 | hashMap:put("k", "v") 112 | local cnt = 0 113 | for k, v in pairs(hashMap) do 114 | if k == "k" and v == "v" then cnt = cnt + 1 end 115 | end 116 | assert(cnt == 1) 117 | 118 | -- ipairs 119 | cnt = 0 120 | for i, j in ipairs(intArray) do 121 | cnt = cnt + 1 122 | end 123 | assert(cnt == 2) 124 | end 125 | 126 | -- Overloaded method dispatch test 127 | function testMethodDispatch () 128 | -- Subclass test 129 | local TestObject = java.require("com.naef.jnlua.test.fixture.TestObject") 130 | local testObject = TestObject:new() 131 | local Sub = java.require("com.naef.jnlua.test.fixture.TestObject$Sub") 132 | local sub = Sub:new() 133 | assert(testObject:overloadedSub(testObject) == "super") 134 | assert(testObject:overloadedSub(sub) == "sub") 135 | 136 | -- Subinterface test 137 | local AB = java.require("com.naef.jnlua.test.fixture.TestObject$AB") 138 | local ab = AB:new() 139 | local AC = java.require("com.naef.jnlua.test.fixture.TestObject$AC") 140 | local ac = AC:new() 141 | local BC = java.require("com.naef.jnlua.test.fixture.TestObject$BC") 142 | local bc = BC:new() 143 | assert(testObject:overloadedSibling(ab) == "b") 144 | assert(testObject:overloadedSibling(ac) == "c") 145 | local status, msg = pcall(testObject.overloadedSibling, testObject, bc) 146 | assert(not status) 147 | assert(string.find(tostring(msg), "ambivalent")) 148 | assert(testObject:overloadedParentChild(ab) == "b") 149 | assert(testObject:overloadedParentChild(ac) == "a") 150 | assert(testObject:overloadedParentChild(bc) == "b") 151 | assert(testObject:overloadedParentChild(bc) == "b") 152 | end 153 | 154 | -- VarArgs method test 155 | function testVarargs () 156 | local String = java.require("java.lang.String") 157 | assert(String:format("%s%.0f", "test", 1) == "test1") 158 | assert(true) 159 | end 160 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/script/LuaScriptEngineFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.script; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | import javax.script.ScriptEngine; 13 | import javax.script.ScriptEngineFactory; 14 | 15 | import com.naef.jnlua.LuaState; 16 | 17 | /** 18 | * Lua script engine factory implementation conforming to JSR 223: Scripting for 19 | * the Java Platform. 20 | */ 21 | public class LuaScriptEngineFactory implements ScriptEngineFactory { 22 | // -- Static 23 | private static final String ENGINE_NAME = "JNLua"; 24 | private static final String LANGUAGE_NAME = "Lua"; 25 | private static final List EXTENSIONS; 26 | private static final List MIME_TYPES; 27 | private static final List NAMES; 28 | static { 29 | // Extensions 30 | List extensions = new ArrayList(); 31 | extensions.add("lua"); 32 | EXTENSIONS = Collections.unmodifiableList(extensions); 33 | 34 | // MIME types 35 | List mimeTypes = new ArrayList(); 36 | mimeTypes.add("application/x-lua"); 37 | mimeTypes.add("text/x-lua"); 38 | MIME_TYPES = Collections.unmodifiableList(mimeTypes); 39 | 40 | // Names 41 | List names = new ArrayList(); 42 | names.add("lua"); 43 | names.add("Lua"); 44 | names.add("jnlua"); 45 | names.add("JNLua"); 46 | NAMES = Collections.unmodifiableList(names); 47 | } 48 | 49 | // -- Construction 50 | /** 51 | * Creates a new instance. 52 | */ 53 | public LuaScriptEngineFactory() { 54 | } 55 | 56 | // -- ScriptEngineFactory methods 57 | @Override 58 | public String getEngineName() { 59 | return ENGINE_NAME; 60 | } 61 | 62 | @Override 63 | public String getEngineVersion() { 64 | return LuaState.VERSION; 65 | } 66 | 67 | @Override 68 | public List getExtensions() { 69 | return EXTENSIONS; 70 | } 71 | 72 | @Override 73 | public List getMimeTypes() { 74 | return MIME_TYPES; 75 | } 76 | 77 | @Override 78 | public List getNames() { 79 | return NAMES; 80 | } 81 | 82 | @Override 83 | public String getLanguageName() { 84 | return LANGUAGE_NAME; 85 | } 86 | 87 | @Override 88 | public String getLanguageVersion() { 89 | return LuaState.LUA_VERSION; 90 | } 91 | 92 | @Override 93 | public Object getParameter(String key) { 94 | if (key.equals(ScriptEngine.ENGINE)) { 95 | return getEngineName(); 96 | } 97 | if (key.equals(ScriptEngine.ENGINE_VERSION)) { 98 | return getEngineVersion(); 99 | } 100 | if (key.equals(ScriptEngine.NAME)) { 101 | return getNames().get(0); 102 | } 103 | if (key.equals(ScriptEngine.LANGUAGE)) { 104 | return getLanguageName(); 105 | } 106 | if (key.equals(ScriptEngine.LANGUAGE_VERSION)) { 107 | return getLanguageVersion(); 108 | } 109 | if (key.equals("THREADING")) { 110 | return "MULTITHREADED"; 111 | } 112 | return null; 113 | } 114 | 115 | @Override 116 | public String getMethodCallSyntax(String obj, String m, String... args) { 117 | StringBuffer sb = new StringBuffer(); 118 | sb.append(obj); 119 | sb.append(':'); 120 | sb.append(m); 121 | sb.append('('); 122 | for (int i = 0; i < args.length; i++) { 123 | if (i > 0) { 124 | sb.append(", "); 125 | } 126 | sb.append(args[i]); 127 | } 128 | sb.append(')'); 129 | return sb.toString(); 130 | } 131 | 132 | @Override 133 | public String getOutputStatement(String toDisplay) { 134 | StringBuffer sb = new StringBuffer(); 135 | sb.append("print("); 136 | quoteString(sb, toDisplay); 137 | sb.append(')'); 138 | return sb.toString(); 139 | } 140 | 141 | @Override 142 | public String getProgram(String... statements) { 143 | StringBuffer sb = new StringBuffer(); 144 | for (int i = 0; i < statements.length; i++) { 145 | sb.append(statements[i]); 146 | sb.append("\n"); 147 | } 148 | return sb.toString(); 149 | } 150 | 151 | @Override 152 | public ScriptEngine getScriptEngine() { 153 | return new LuaScriptEngine(this); 154 | } 155 | 156 | // --Private methods 157 | /** 158 | * Quotes a string in double quotes. 159 | */ 160 | private void quoteString(StringBuffer sb, String s) { 161 | sb.append('"'); 162 | for (int i = 0; i < s.length(); i++) { 163 | switch (s.charAt(i)) { 164 | case '\u0007': 165 | sb.append("\\a"); 166 | break; 167 | case '\b': 168 | sb.append("\\b"); 169 | break; 170 | case '\f': 171 | sb.append("\\f"); 172 | break; 173 | case '\n': 174 | sb.append("\\n"); 175 | break; 176 | case '\r': 177 | sb.append("\\r"); 178 | break; 179 | case '\t': 180 | sb.append("\\t"); 181 | break; 182 | case '\u000b': 183 | sb.append("\\v"); 184 | break; 185 | case '\\': 186 | sb.append("\\\\"); 187 | break; 188 | case '"': 189 | sb.append("\\\""); 190 | break; 191 | default: 192 | sb.append(s.charAt(i)); 193 | } 194 | } 195 | sb.append('"'); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JNLua (Java Native Lua) integrates the [Lua Scripting Language](http://www.lua.org/) into Java. The integration is based on the original implementation of Lua which is written in ANSI C. The Lua C code is integrated into Java using the Java Native API (JNI). 2 | 3 | ### Features ### 4 | 5 | JNLua provides the following features: 6 | 7 | - **Full Lua support with full Java type-safety.** JNLua provides the full functionality of Lua C API including large parts of the Lua Auxiliary Library. All Lua Standard Libraries are supported, including the coroutine functions. At the same time, JNLua maintains the type-safety of the Java VM by performing rigorous checks in its native library. 8 | - **Two-way integration.** With JNLua, you can access Java from Lua and Lua from Java. From Lua, JNLua provides full Java object access with intuitive syntax and the abilitiy to implement Java interfaces in Lua. From Java, JNLua provides full Lua access including the ability to implement Lua functions in Java. The integration works transparently in both directions and on each end conforms to the common principles of the respective platform. 9 | - **Dual bootstrapping.** JNLua can be started from both the Java and the Lua side. If started from the Java side, a Lua state is attached to the calling Java virtual machine; if started from the Lua side, a Java virtual machine is attached to the calling Lua process. 10 | - **Extensive language bindings.** The bindings between Lua and Java are abstracted into the domains of *Java reflection* and *conversion*. The *default Java reflector* supports field, method and property access on Java classes and objects. For overloaded methods, it provides a dispatch logic that mimics the behavior described in Java Language Specification. The *default converter* handles the bidirectional conversion of primitive types, such as numbers and strings. For complex types, it supports the bidirectional mapping of Lua tables to Java maps, lists and arrays. These mappings are generally implemented with proxy objects, that is, they work by *reference*. Both the Java reflector and converter can be specialized to fit custom needs. 11 | - **Java module.** The JNLua Java module provides a small but comprehensive set of Lua functions providing Java language support for Lua. 12 | - **Java VM module.** The Java VM module is a Lua module written in C that allows a Lua process to create a Java Virtual Machine and run Java code in that machine. 13 | - **Transparent error handling.** Java does error handling by exceptions; Lua uses mechanics such as `error()` and `pcall()`. JNLua ensures a seamless translation of error conditions between the two domains. Lua errors are reported as exceptions to Java. Java exceptions generate errors on the Lua side. 14 | - **JSR 223: Scripting for the Java Platform provider.** JNLua includes a provider that conforms to the [JSR 223: Scripting for the Java Platform](http://www.jcp.org/en/jsr/detail?id=223) specification. This allows the use of Lua as a scripting language for Java in a standardized way. The JSR 223 provider also supports the optional Compilable and Invocable interfaces. 15 | - **JNLua Console.** A simple console implemented in Java for experimenting with JNLua. 16 | 17 | 18 | ### Design Goals ### 19 | 20 | The following goals drive the design of JNLua: 21 | 22 | - **Stability.** Both Java and Lua are mature and stable platforms. However, the underlying Java VM is a type-safe in a way that ANSI C is not. When loading C code into the Java VM, one must be careful not to corrupt the type-safety of the VM. JNLua achieves this goal by rigorous argument checking, stack checking and a sufficient amount of internal synchronization. 23 | - **Performance.** Lua has a reputation of being a very fast scripting language. Basically, JNLua simply tries not to get into the way of that performance ;) In support of good performance, JNLua avoids copying values between Java and Lua. Instead, JNLua uses proxy objects where possible. 24 | - **Simplicity.** JNLua aims at being simple and easy to learn by adhering to well-known patterns in both the Java and the Lua world. Lua provides a large spectrum of flexibility with relatively few, but well-designed features. JNLua tries to adhere to that spirit. 25 | 26 | 27 | ### Architecture ### 28 | 29 | The figure below depicts the architecture of JNLua: 30 | ![Architecture](http://jnlua.googlecode.com/svn/wiki/Architecture.png) 31 | 32 | When JNLua is bootstrapped from Java, the Java code calls the JNLua Java library. The JNLua Java library invokes the JNLua native library via JNI, and the JNLua native library calls the Lua C API. Lua code can use the JNLua Java module to access Java functionality. 33 | 34 | When JNLua is bootstrapped from Lua, the Lua code uses the JNLua Java VM module to create a Java virtual machine with the JNLua Java library, and then calls the JNLua Java module to access Java functionality. 35 | Requirements 36 | 37 | JNLua 0.9 is based on the Java 6 Platform (JDK 1.6) and Lua 5.1. 38 | 39 | JNLua 1.0 is based on the Java 6 Platform (JDK 1.6) and Lua 5.2. 40 | 41 | Max Raskin has done a port of JNLua for the [Android platform](https://code.google.com/p/jnlua-android). 42 | 43 | ### Documentation ### 44 | 45 | Documentation is provided in the [wiki](https://github.com/Antag99/jnlua/wiki/) as well as in the form of generated Javadoc. 46 | 47 | ### License ### 48 | 49 | JNLua is licensed under the MIT license which at the time of this writing is the same license as the one of Lua. 50 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | 2 | ### Release 1.0.5 ### 3 | 4 | - Corrected an issue where an array access error message would fail to format. 5 | 6 | 7 | ### Release 1.0.4 (2013-07-28) ### 8 | 9 | - Corrected an issue where method dispatch would enter an endless loop on 10 | non-public superclasses. 11 | 12 | 13 | ### Release 1.0.3 (2012-10-09) ### 14 | 15 | - Added transparent conversion between Lua strings and Java byte arrays. 16 | This may break existing code that passes byte arrays between Java and Lua. 17 | A compatibility property, com.naef.jnlua.rawByteArray=true, has been provided. 18 | 19 | - Fixed an issue where method dispatch would incorrectly fail on public 20 | methods found on non-public classes with public superclasses. Thanks 21 | Ignazio Di Napoli for the analysis. 22 | 23 | 24 | ### Release 1.0.2 (2012-01-29) ### 25 | 26 | - Added toIntegerX and toNumberX methods to LuaState. 27 | 28 | - Added a 'new' method to interfaces in the default Java reflector. The method 29 | accepts a table providing the methods of the interface and returns a proxy 30 | that implements the interface. 31 | 32 | - Changed the absIndex method in LuaState to accept non-valid indexes. 33 | 34 | - Changed the rawEqual method to return false on non-valid indexes. 35 | 36 | - Changed the setMetatable method in LuaState to no longer return a value, 37 | adapting to the Lua 5.2 API. 38 | 39 | - Improved the diagnostics in the javavm module. In case of JNI errors, the 40 | Java exception string is now included in the error message. 41 | 42 | - Refactored the error handling in the JNLua native library. 43 | 44 | 45 | ### Release 1.0.1 (2012-01-12) ### 46 | 47 | - Added a javavm module, allowing to create a Java VM from Lua. 48 | 49 | - Javadoc corrections. 50 | 51 | - Corrected an issue where the native library would pass an invalid handle to 52 | the ReleaseStringUTFChars function. 53 | 54 | - Corrected an issue where the native library would not properly catch Lua 55 | errors, leading to uncontrolled transitions between Java code and native code. 56 | 57 | - Corrected an issue where the native library would exit incorrectly from the 58 | lua_tojavafunction function. 59 | 60 | 61 | ### Release 1.0.0 (2012-01-05) ### 62 | 63 | - Adapted to Lua 5.2. 64 | 65 | - Added the RIDX_MAINTHREAD and RIDX_GLOBALS constants to LuaState. 66 | 67 | - Added a compare method to LuaState, and deprecated the equal and lessThan methods. 68 | 69 | - Added a rawLen method to LuaState, and deprecated the length method. 70 | 71 | - Added an absIndex method to LuaState. 72 | 73 | - Added an arith method to LuaState. 74 | 75 | - Added a copy method to LuaState. 76 | 77 | - Added a len method to LuaState. 78 | 79 | - Added support for the bit32 library in both the openLib and openLibs methods 80 | of LuaState. 81 | 82 | - Added support for additional garbage collection actions, allowing to query 83 | whether the collector is running and choosing the collector mode. 84 | 85 | - Added the exception classes LuaGcMetamethodException and 86 | LuaMessageHandlerException, indicating errors running the __gc metamethod 87 | during garbage collection and errors running the message handler function in a 88 | protected call. 89 | 90 | - Added support for the __pairs and __ipairs metamethods on Java objects. The 91 | behavior is equivalent to the pairs and ipairs functions provided by the Java 92 | module. 93 | 94 | - Removed the GLOBALSINDEX and ENVIRONINDEX constants from LuaState. You can 95 | use the getGlobal and setGlobal methods, or call rawget(REGISTRYINDEX, 96 | RIDX_GLOBALS) to push the global environment onto the stack. 97 | 98 | - Removed the checkBoolean methods from LuaState. You can use the toBoolean 99 | method to evaluate any value in a boolean context. Also changed the toBoolean 100 | method to accept non-valid stack indexes. 101 | 102 | - Removed the setFEnv and getFEnv methods from LuaState. 103 | 104 | - Changed the input stream based load method in LuaState to accept an 105 | additional mode argument. Also, the source (aka chunkname) argument is 106 | no longer auto-prefixed with an equals sign. 107 | 108 | - Changed the behavior of the library opening methods. The openLib method 109 | in LuaState and the open method in JavaModule now leave the loaded module on 110 | the stack. 111 | 112 | - Changed the behavior of the register method in LuaState to follow that of 113 | luaL_requiref, no longer specifically supporting dots in module names. An 114 | optional boolean argument now controls whether a global variable with the 115 | module name is created. 116 | 117 | - Changed the argument checking methods in LuaState to call the Lua standard 118 | implementations, reducing the number of native transitions and taking advantage 119 | of tonumberx and tointegerx improvements. Also changed the checkOption 120 | methods to return an integer index instead of a string and added checkEnum 121 | methods suitable for use with Java enums. 122 | 123 | - Corrected an issue where the type method in LuaState would not return null 124 | for non-valid stack indexes. 125 | 126 | - Corrected an issue where the default converter would not properly handle 127 | non-valid stack indexes. 128 | 129 | - Corrected an issue where the setJavaReflector method in LuaState would allow 130 | a null value to be set. 131 | 132 | 133 | ### Release 0.9.1 Beta (2010-04-05) ### 134 | 135 | - Added NativeSupport for more explicit control over the native library 136 | loading process. 137 | 138 | - Migrated build system to Maven. 139 | 140 | 141 | ### Release 0.9.0 Beta (2008-10-27) ### 142 | 143 | - Initial public release. 144 | -------------------------------------------------------------------------------- /jnlua/src/test/java/com/naef/jnlua/test/LuaExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.test; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | import static org.junit.Assert.assertNotNull; 10 | import static org.junit.Assert.assertTrue; 11 | 12 | import org.junit.Test; 13 | 14 | import com.naef.jnlua.JavaFunction; 15 | import com.naef.jnlua.LuaGcMetamethodException; 16 | import com.naef.jnlua.LuaRuntimeException; 17 | import com.naef.jnlua.LuaStackTraceElement; 18 | import com.naef.jnlua.LuaState; 19 | import com.naef.jnlua.LuaSyntaxException; 20 | import com.naef.jnlua.LuaState.Library; 21 | 22 | /** 23 | * Contains unit tests for Lua exceptions. 24 | */ 25 | public class LuaExceptionTest extends AbstractLuaTest { 26 | // -- Test cases 27 | /** 28 | * Tests the call of a Lua function which invokes the Lua error function. 29 | */ 30 | @Test 31 | public void testLuaError() throws Exception { 32 | // Load program 33 | luaState.openLibs(); 34 | StringBuffer sb = new StringBuffer(); 35 | sb.append("function A ()\n"); 36 | sb.append(" B()\n"); 37 | sb.append("end\n"); 38 | sb.append("\n"); 39 | sb.append("function B ()\n"); 40 | sb.append(" C()\n"); 41 | sb.append("end\n"); 42 | sb.append("\n"); 43 | sb.append("function C ()\n"); 44 | sb.append(" error(\"msg\")\n"); 45 | sb.append("end\n"); 46 | sb.append("\n"); 47 | sb.append("A()\n"); 48 | luaState.load(sb.toString(), "=testLuaError"); 49 | 50 | // Run 51 | LuaRuntimeException luaRuntimeException = null; 52 | try { 53 | luaState.call(0, 0); 54 | } catch (LuaRuntimeException e) { 55 | luaRuntimeException = e; 56 | } 57 | assertTrue(luaRuntimeException.getMessage().endsWith("msg")); 58 | LuaStackTraceElement[] luaStackTrace = luaRuntimeException 59 | .getLuaStackTrace(); 60 | assertEquals(5, luaStackTrace.length); 61 | assertEquals(new LuaStackTraceElement("error", null, -1), 62 | luaStackTrace[0]); 63 | assertEquals(new LuaStackTraceElement("C", "testLuaError", 10), 64 | luaStackTrace[1]); 65 | assertEquals(new LuaStackTraceElement("B", "testLuaError", 6), luaStackTrace[2]); 66 | assertEquals(new LuaStackTraceElement("A", "testLuaError", 2), luaStackTrace[3]); 67 | assertEquals(new LuaStackTraceElement(null, "testLuaError", 13), 68 | luaStackTrace[4]); 69 | } 70 | 71 | /** 72 | * Tests the call of a Java function which throws a Java runtime exception. 73 | */ 74 | @Test 75 | public void testRuntimeException() throws Exception { 76 | // Push function 77 | luaState.pushJavaFunction(new RuntimeExceptionFunction()); 78 | 79 | // Push arguments 80 | LuaRuntimeException luaRuntimeException = null; 81 | try { 82 | luaState.call(0, 0); 83 | } catch (LuaRuntimeException e) { 84 | luaRuntimeException = e; 85 | } 86 | assertNotNull(luaRuntimeException); 87 | Throwable cause = luaRuntimeException.getCause(); 88 | assertNotNull(cause); 89 | assertTrue(cause instanceof ArithmeticException); 90 | } 91 | 92 | /** 93 | * Tests the call of a Java function which throws a Lua runtime exception. 94 | */ 95 | @Test 96 | public void testLuaRuntimeException() throws Exception { 97 | // Push function 98 | luaState.pushJavaFunction(new LuaRuntimeExceptionFunction()); 99 | 100 | // Push arguments 101 | LuaRuntimeException luaRuntimeException = null; 102 | try { 103 | luaState.call(0, 0); 104 | } catch (LuaRuntimeException e) { 105 | luaRuntimeException = e; 106 | } 107 | assertNotNull(luaRuntimeException); 108 | Throwable cause = luaRuntimeException.getCause(); 109 | assertNotNull(cause); 110 | assertTrue(cause instanceof LuaRuntimeException); 111 | } 112 | 113 | /** 114 | * Tests the generation of a Lua syntax exception on Lua code with invalid 115 | * syntax. 116 | */ 117 | @Test 118 | public void testLuaSyntaxException() throws Exception { 119 | LuaSyntaxException luaSyntaxException = null; 120 | try { 121 | luaState.load("An invalid chunk of Lua.", "=testLuaSyntaxException"); 122 | } catch (LuaSyntaxException e) { 123 | luaSyntaxException = e; 124 | } 125 | assertNotNull(luaSyntaxException); 126 | } 127 | 128 | /** 129 | * Tests the generation of a Lua GC metamethod exception on a Lua value 130 | * raising an error in its __gc metamethod. 131 | */ 132 | @Test 133 | public void testLuaGcMetamethodException() throws Exception { 134 | LuaGcMetamethodException luaGcMetamethodException = null; 135 | luaState.openLib(Library.BASE); 136 | luaState.pop(1); 137 | luaState.load( 138 | "setmetatable({}, { __gc = function() error(\"gc\") end })\n" 139 | + "collectgarbage()", "=testLuaGcMetamethodException"); 140 | try { 141 | luaState.call(0, 0); 142 | } catch (LuaGcMetamethodException e) { 143 | luaGcMetamethodException = e; 144 | } 145 | assertNotNull(luaGcMetamethodException); 146 | } 147 | 148 | // -- Private classes 149 | /** 150 | * Provides a function throwing a Java runtime exception. 151 | */ 152 | private class RuntimeExceptionFunction implements JavaFunction { 153 | public int invoke(LuaState luaState) throws LuaRuntimeException { 154 | @SuppressWarnings("unused") 155 | int a = 0 / 0; 156 | return 0; 157 | } 158 | } 159 | 160 | /** 161 | * Provides a function throwing a Lua runtime exception with a cause. 162 | */ 163 | private class LuaRuntimeExceptionFunction implements JavaFunction { 164 | public int invoke(LuaState luaState) throws LuaRuntimeException { 165 | try { 166 | @SuppressWarnings("unused") 167 | int a = 0 / 0; 168 | } catch (ArithmeticException e) { 169 | throw new LuaRuntimeException(e.getMessage(), e); 170 | } 171 | return 0; 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/console/LuaConsole.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.console; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.ByteArrayInputStream; 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.io.InputStreamReader; 14 | import java.io.OutputStreamWriter; 15 | 16 | import com.naef.jnlua.LuaException; 17 | import com.naef.jnlua.LuaRuntimeException; 18 | import com.naef.jnlua.LuaState; 19 | 20 | /** 21 | * A simple Lua console. 22 | * 23 | *

24 | * The console collects input until a line with the sole content of the word 25 | * go is encountered. At that point, the collected input is run as a Lua 26 | * chunk. If the Lua chunk loads and runs successfully, the console displays the 27 | * returned values of the chunk as well as the execution time based on a 28 | * System.nanoTime() measurement. Otherwise, the console shows the 29 | * error that has occurred. 30 | *

31 | * 32 | *

33 | * Expressions can be printed by prepending = to the expression at the 34 | * beginning of a chunk. The console translates = into 35 | * return followed by a space and executes the chunk immediately. 36 | * No separate go is required. Therefore, expressions printed this way 37 | * must be entered on a single line. 38 | *

39 | */ 40 | public class LuaConsole { 41 | // -- Static 42 | private static final String[] EMPTY_ARGS = new String[0]; 43 | 44 | /** 45 | * Main routine. 46 | * 47 | * @param args 48 | * the command line arguments 49 | */ 50 | public static void main(String[] args) { 51 | LuaConsole luaConsole = new LuaConsole(args); 52 | luaConsole.run(); 53 | System.exit(0); 54 | } 55 | 56 | // -- State 57 | private LuaState luaState; 58 | 59 | // -- Construction 60 | /** 61 | * Creates a new instance. 62 | */ 63 | public LuaConsole() { 64 | this(EMPTY_ARGS); 65 | } 66 | 67 | /** 68 | * Creates a new instance with the specified command line arguments. The 69 | * arguments are passed to Lua as the argv global variable. 70 | * 71 | * @param args 72 | */ 73 | public LuaConsole(String[] args) { 74 | luaState = new LuaState(); 75 | 76 | // Process arguments 77 | luaState.newTable(args.length, 0); 78 | for (int i = 0; i < args.length; i++) { 79 | luaState.pushString(args[i]); 80 | luaState.rawSet(-2, i + 1); 81 | } 82 | luaState.setGlobal("argv"); 83 | 84 | // Open standard libraries 85 | luaState.openLibs(); 86 | 87 | // Set buffer mode 88 | luaState.load("io.stdout:setvbuf(\"no\")", "=consoleInitStdout"); 89 | luaState.call(0, 0); 90 | luaState.load("io.stderr:setvbuf(\"no\")", "=consoleInitStderr"); 91 | luaState.call(0, 0); 92 | } 93 | 94 | // -- Properties 95 | /** 96 | * Returns the Lua state of this console. 97 | * 98 | * @return the Lua state 99 | */ 100 | public LuaState getLuaState() { 101 | return luaState; 102 | } 103 | 104 | // -- Operations 105 | /** 106 | * Runs the console. 107 | */ 108 | public void run() { 109 | // Banner 110 | System.out.println(String.format("JNLua %s Console using Lua %s.", 111 | LuaState.VERSION, LuaState.LUA_VERSION)); 112 | System.out.print("Type 'go' on an empty line to evaluate a chunk. "); 113 | System.out.println("Type = to print an expression."); 114 | 115 | // Prepare reader 116 | BufferedReader bufferedReader = new BufferedReader( 117 | new InputStreamReader(System.in)); 118 | try { 119 | // Process chunks 120 | chunk: while (true) { 121 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 122 | OutputStreamWriter outWriter = new OutputStreamWriter(out, 123 | "UTF-8"); 124 | boolean firstLine = true; 125 | 126 | // Process lines 127 | while (true) { 128 | String line = bufferedReader.readLine(); 129 | if (line == null) { 130 | break chunk; 131 | } 132 | if (line.equals("go")) { 133 | outWriter.flush(); 134 | InputStream in = new ByteArrayInputStream(out 135 | .toByteArray()); 136 | runChunk(in); 137 | continue chunk; 138 | } 139 | if (firstLine && line.startsWith("=")) { 140 | outWriter.write("return " + line.substring(1)); 141 | outWriter.flush(); 142 | InputStream in = new ByteArrayInputStream(out 143 | .toByteArray()); 144 | runChunk(in); 145 | continue chunk; 146 | } 147 | outWriter.write(line); 148 | outWriter.write('\n'); 149 | firstLine = false; 150 | } 151 | } 152 | } catch (IOException e) { 153 | System.out.print("IO error: "); 154 | System.out.print(e.getMessage()); 155 | System.out.println(); 156 | } 157 | } 158 | 159 | /** 160 | * Runs a chunk of Lua code from an input stream. 161 | */ 162 | protected void runChunk(InputStream in) throws IOException { 163 | try { 164 | long start = System.nanoTime(); 165 | luaState.setTop(0); 166 | luaState.load(in, "=console", "t"); 167 | luaState.call(0, LuaState.MULTRET); 168 | long stop = System.nanoTime(); 169 | for (int i = 1; i <= luaState.getTop(); i++) { 170 | if (i > 1) { 171 | System.out.print(", "); 172 | } 173 | switch (luaState.type(i)) { 174 | case BOOLEAN: 175 | System.out.print(Boolean.valueOf(luaState.toBoolean(i))); 176 | break; 177 | case NUMBER: 178 | case STRING: 179 | System.out.print(luaState.toString(i)); 180 | break; 181 | default: 182 | System.out.print(luaState.typeName(i)); 183 | } 184 | } 185 | System.out.print("\t#msec="); 186 | System.out.print(String.format("%.3f", (stop - start) / 1000000.0)); 187 | System.out.println(); 188 | } catch (LuaRuntimeException e) { 189 | e.printLuaStackTrace(); 190 | } catch (LuaException e) { 191 | System.err.println(e.getMessage()); 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /jnlua/src/test/resources/com/naef/jnlua/test/JavaModule.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | $Id$ 3 | See LICENSE.txt for license terms. 4 | ]] 5 | 6 | module(..., package.seeall) 7 | 8 | -- java.require() 9 | function testRequire () 10 | -- No import 11 | local class, imported = java.require("java.lang.System") 12 | assert(class) 13 | assert(not imported) 14 | 15 | -- Import 16 | class, imported = java.require("java.lang.System", true) 17 | assert(class) 18 | assert(class == java.lang.System) 19 | assert(imported) 20 | 21 | -- Primitive type, no import 22 | class, imported = java.require("byte") 23 | assert(class) 24 | assert(not imported) 25 | 26 | -- Primitive type, import 27 | class, imported = java.require("byte", true) 28 | assert(class) 29 | assert(class == byte) 30 | assert(imported) 31 | end 32 | 33 | -- java.new 34 | function testNew () 35 | local byte = java.require("byte") 36 | assert(byte) 37 | 38 | -- Simple byte array 39 | local byteArray = java.new(byte, 10) 40 | assert(#byteArray == 10) 41 | local byteArray = java.new("byte", 10) 42 | assert(#byteArray == 10) 43 | 44 | -- Multi-dimensional byte array 45 | byteArray = java.new(byte, 10, 10) 46 | assert(#byteArray == 10) 47 | assert(#byteArray[1] == 10) 48 | end 49 | 50 | -- java.instanceof 51 | function testInstanceOf () 52 | local TestObject = java.require("com.naef.jnlua.test.fixture.TestObject") 53 | local testObject = TestObject:new() 54 | assert(java.instanceof(testObject, TestObject)) 55 | assert(java.instanceof(testObject, "com.naef.jnlua.test.fixture.TestObject")) 56 | assert(not java.instanceof(testObject, "java.lang.System")) 57 | end 58 | 59 | -- java.cast 60 | function testCast () 61 | local StringBuilder = java.require("java.lang.StringBuilder") 62 | local sb = StringBuilder:new() 63 | sb:append(java.cast(1, "int")) 64 | assert(sb:toString() == "1") 65 | end 66 | 67 | -- java.proxy 68 | function testProxy () 69 | local privilegedAction = { hasRun = false } 70 | function privilegedAction:run() 71 | self.hasRun = true 72 | end 73 | local proxy = java.proxy(privilegedAction, "java.security.PrivilegedAction") 74 | assert(not privilegedAction.hasRun) 75 | local AccessController = java.require("java.security.AccessController") 76 | AccessController:doPrivileged(proxy) 77 | assert(privilegedAction.hasRun) 78 | end 79 | 80 | -- java.pairs 81 | function testPairs () 82 | -- Create map 83 | local HashMap = java.require("java.util.HashMap") 84 | local hashMap = HashMap:new() 85 | hashMap:put("k", "v") 86 | 87 | -- Iterate 88 | local count = 0 89 | for k, v in java.pairs(hashMap) do 90 | assert(k == "k") 91 | assert(v == "v") 92 | count = count + 1 93 | end 94 | assert(count == 1) 95 | 96 | -- Create map (navigable) 97 | local TreeMap = java.require("java.util.TreeMap") 98 | local treeMap = TreeMap:new() 99 | treeMap:put("k", "v") 100 | 101 | -- Iterate 102 | local count = 0 103 | for k, v in java.pairs(treeMap) do 104 | assert(k == "k") 105 | assert(v == "v") 106 | count = count + 1 107 | end 108 | assert(count == 1) 109 | end 110 | 111 | -- java.ipairs 112 | function testIPairs () 113 | -- Create list 114 | local list = java.new("java.util.ArrayList") 115 | for i = 1, 10 do 116 | list:add(i) 117 | end 118 | 119 | -- Iterate 120 | local count = 0 121 | local sum = 0 122 | for k, v in java.ipairs(list) do 123 | count = count + 1 124 | assert(k == count) 125 | sum = sum + v 126 | end 127 | assert(count == 10) 128 | assert(sum == 55) 129 | 130 | -- Create array 131 | local array = java.new("int", 10) 132 | for i = 1, 10 do 133 | array[i] = i 134 | end 135 | 136 | -- Iterate 137 | local count = 0 138 | local sum = 0 139 | for k, v in java.ipairs(array) do 140 | count = count + 1 141 | assert(k == count) 142 | sum = sum + v 143 | end 144 | assert(count == 10) 145 | assert(sum == 55) 146 | end 147 | 148 | -- java.tottable 149 | function testToTable () 150 | -- Test list 151 | local arrayList = java.new("java.util.ArrayList") 152 | local list = java.totable(arrayList) 153 | for i = 1, 10 do 154 | list[i] = i 155 | end 156 | for i = 1, 10 do 157 | assert(arrayList:get(i - 1) == i) 158 | end 159 | assert(#list == 10) 160 | local count = 0 161 | for k, v in java.ipairs(list) do 162 | count = count + 1 163 | end 164 | assert(count == 10) 165 | 166 | -- Test map 167 | local hashMap = java.new("java.util.HashMap") 168 | local map = java.totable(hashMap) 169 | map["k"] = "v" 170 | assert(hashMap:get("k") == "v") 171 | local count = 0 172 | for k, v in java.pairs(map) do 173 | count = count + 1 174 | end 175 | assert(count == 1) 176 | end 177 | 178 | -- java.elements 179 | function testElements() 180 | local set = java.new("java.util.HashSet") 181 | for i = 1, 10 do 182 | set:add(i) 183 | end 184 | sum = 0 185 | for value in java.elements(set) do 186 | sum = sum + value 187 | end 188 | assert(sum == 55) 189 | end 190 | 191 | -- java.fields 192 | function testFields() 193 | -- Static 194 | local TestObject = java.require("com.naef.jnlua.test.fixture.TestObject") 195 | local fields = {} 196 | local count = 0 197 | for k, v in java.fields(TestObject) do 198 | count = count + 1 199 | fields[k] = v 200 | end 201 | assert(fields["TEST_FIELD"]) 202 | assert(count == 1) 203 | 204 | -- Non-static 205 | local testObject = java.new(TestObject) 206 | fields = {} 207 | count = 0 208 | for k, v in java.fields(testObject) do 209 | count = count + 1 210 | fields[k] = v 211 | end 212 | assert(fields["testField"]) 213 | assert(count == 13) 214 | end 215 | 216 | -- java.methods 217 | function testMethods() 218 | -- Static 219 | local TestObject = java.require("com.naef.jnlua.test.fixture.TestObject") 220 | local methods = {} 221 | local count = 0 222 | for k, v in java.methods(TestObject) do 223 | count = count + 1 224 | methods[k] = v 225 | end 226 | assert(methods["testStatic"]) 227 | assert(count == 2) 228 | 229 | -- Non-static 230 | local testObject = java.new(TestObject) 231 | methods = {} 232 | count = 0 233 | for k, v in java.methods(testObject) do 234 | count = count + 1 235 | methods[k] = v 236 | end 237 | assert(methods["test"]) 238 | assert(count == 16) 239 | end 240 | 241 | -- java.properties 242 | function testProperties() 243 | local TestObject = java.require("com.naef.jnlua.test.fixture.TestObject") 244 | local testObject = java.new(TestObject) 245 | local properties = {} 246 | local count = 0 247 | for k, v in java.properties(testObject) do 248 | count = count + 1 249 | properties[k] = v 250 | end 251 | assert(properties["value"]) 252 | assert(count == 3) 253 | end 254 | -------------------------------------------------------------------------------- /jnlua/src/main/c/javavm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * Provides the Java VM module. See LICENSE.txt for license terms. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #ifdef LUA_WIN 11 | #include 12 | #pragma warning(disable : 4996) 13 | #endif 14 | #ifdef LUA_USE_POSIX 15 | #include 16 | #endif 17 | #include "javavm.h" 18 | 19 | /* 20 | * Java VM parameters. 21 | */ 22 | #define JAVAVM_METATABLE "javavm.metatable" 23 | #define JAVAVM_VM "javavm.vm" 24 | #define JAVAVM_MAXOPTIONS 128 25 | #define JAVAVM_JNIVERSION JNI_VERSION_1_6 26 | 27 | /* 28 | * VM record. 29 | */ 30 | typedef struct vm_rec { 31 | JavaVM *vm; 32 | jobject luastate; 33 | int num_options; 34 | JavaVMOption options[JAVAVM_MAXOPTIONS]; 35 | } vm_rec; 36 | 37 | /* 38 | * Raises an error from JNI. 39 | */ 40 | static int error (lua_State *L, JNIEnv *env, const char *msg) { 41 | jthrowable throwable; 42 | jclass throwable_class; 43 | jmethodID tostring_id; 44 | jstring string; 45 | const char *extramsg = NULL; 46 | 47 | throwable = (*env)->ExceptionOccurred(env); 48 | if (throwable) { 49 | throwable_class = (*env)->GetObjectClass(env, throwable); 50 | if ((tostring_id = (*env)->GetMethodID(env, throwable_class, "toString", "()Ljava/lang/String;"))) { 51 | string = (*env)->CallObjectMethod(env, throwable, tostring_id); 52 | if (string) { 53 | extramsg = (*env)->GetStringUTFChars(env, string, NULL); 54 | } 55 | } 56 | } 57 | if (extramsg) { 58 | lua_pushfstring(L, "%s (%s)", msg, extramsg); 59 | (*env)->ReleaseStringUTFChars(env, string, extramsg); 60 | } else { 61 | lua_pushstring(L, msg); 62 | } 63 | return luaL_error(L, lua_tostring(L, -1)); 64 | } 65 | 66 | /* 67 | * Releases a VM. 68 | */ 69 | static int release_vm (lua_State *L) { 70 | vm_rec *vm; 71 | jclass luastate_class; 72 | jmethodID close_id; 73 | JNIEnv *env; 74 | int res; 75 | int i; 76 | 77 | /* Get VM */ 78 | vm = luaL_checkudata(L, 1, JAVAVM_METATABLE); 79 | 80 | /* Already released? */ 81 | if (!vm->vm) { 82 | return 0; 83 | } 84 | 85 | /* Check thread */ 86 | if ((*vm->vm)->GetEnv(vm->vm, (void **) &env, JAVAVM_JNIVERSION) != JNI_OK) { 87 | return luaL_error(L, "invalid thread"); 88 | } 89 | 90 | /* Close the Lua state in the Java VM */ 91 | if (vm->luastate) { 92 | luastate_class = (*env)->GetObjectClass(env, vm->luastate); 93 | if (!(close_id = (*env)->GetMethodID(env, luastate_class, "close", "()V"))) { 94 | return error(L, env, "close method not found"); 95 | } 96 | (*env)->CallVoidMethod(env, vm->luastate, close_id); 97 | (*env)->DeleteGlobalRef(env, vm->luastate); 98 | vm->luastate = NULL; 99 | } 100 | 101 | /* Destroy the Java VM */ 102 | res = (*vm->vm)->DestroyJavaVM(vm->vm); 103 | if (res < 0) { 104 | return luaL_error(L, "error destroying Java VM: %d", res); 105 | } 106 | vm->vm = NULL; 107 | 108 | /* Free options */ 109 | for (i = 0; i < vm->num_options; i++) { 110 | free(vm->options[i].optionString); 111 | vm->options[i].optionString = NULL; 112 | } 113 | vm->num_options = 0; 114 | 115 | return 0; 116 | } 117 | 118 | /* 119 | * Returns a string representation of a VM. 120 | */ 121 | static int tostring_vm (lua_State *L) { 122 | vm_rec *vm; 123 | int i; 124 | 125 | vm = luaL_checkudata(L, 1, JAVAVM_METATABLE); 126 | lua_pushfstring(L, "Java VM (%p)", vm->vm); 127 | luaL_checkstack(L, vm->num_options, NULL); 128 | for (i = 0; i < vm->num_options; i++) { 129 | lua_pushfstring(L, "\n\t%s", vm->options[i].optionString); 130 | } 131 | lua_concat(L, vm->num_options + 1); 132 | return 1; 133 | } 134 | 135 | /* 136 | * Creates a VM. 137 | */ 138 | static int create_vm (lua_State *L) { 139 | vm_rec *vm; 140 | int i; 141 | const char *option; 142 | JavaVMInitArgs vm_args; 143 | int res; 144 | JNIEnv *env; 145 | jclass luastate_class, library_class; 146 | jmethodID init_id, openlib_id; 147 | jfieldID java_id; 148 | jobject luastate, java; 149 | 150 | /* Check for existing VM */ 151 | lua_getfield(L, LUA_REGISTRYINDEX, JAVAVM_VM); 152 | if (!lua_isnil(L, -1)) { 153 | return luaL_error(L, "VM already created"); 154 | } 155 | lua_pop(L, 1); 156 | 157 | /* Create VM */ 158 | vm = lua_newuserdata(L, sizeof(vm_rec)); 159 | memset(vm, 0, sizeof(vm_rec)); 160 | luaL_getmetatable(L, JAVAVM_METATABLE); 161 | lua_setmetatable(L, -2); 162 | 163 | /* Process options */ 164 | vm->num_options = lua_gettop(L) - 1; 165 | if (vm->num_options > JAVAVM_MAXOPTIONS) { 166 | return luaL_error(L, "%d options limit, got %d", JAVAVM_MAXOPTIONS, vm->num_options); 167 | } 168 | for (i = 1; i <= vm->num_options; i++) { 169 | option = luaL_checkstring(L, i); 170 | if (strcmp(option, "vfprintf") == 0 171 | || strcmp(option, "exit") == 0 172 | || strcmp(option, "abort") == 0) { 173 | return luaL_error(L, "unsupported option '%s'", option); 174 | } 175 | vm->options[i - 1].optionString = strdup(option); 176 | if (!vm->options[i - 1].optionString) { 177 | return luaL_error(L, "out of memory"); 178 | } 179 | } 180 | 181 | /* Create Java VM */ 182 | vm_args.version = JAVAVM_JNIVERSION; 183 | vm_args.options = vm->options; 184 | vm_args.nOptions = vm->num_options; 185 | vm_args.ignoreUnrecognized = JNI_TRUE; 186 | res = JNI_CreateJavaVM(&vm->vm, (void**) &env, &vm_args); 187 | if (res < 0) { 188 | return luaL_error(L, "error creating Java VM: %d", res); 189 | } 190 | 191 | /* Create a LuaState in the Java VM */ 192 | if (!(luastate_class = (*env)->FindClass(env, "com/naef/jnlua/LuaState")) 193 | || !(init_id = (*env)->GetMethodID(env, luastate_class, "", "(J)V"))) { 194 | return error(L, env, "JNLua not found"); 195 | } 196 | luastate = (*env)->NewObject(env, luastate_class, init_id, (jlong) (uintptr_t) L); 197 | if (!luastate) { 198 | return error(L, env, "error creating LuaState"); 199 | } 200 | vm->luastate = (*env)->NewGlobalRef(env, luastate); 201 | if (!vm->luastate) { 202 | return luaL_error(L, "error referencing LuaState"); 203 | } 204 | 205 | /* Load the Java module */ 206 | if (!(library_class = (*env)->FindClass(env, "com/naef/jnlua/LuaState$Library")) 207 | || !(openlib_id = (*env)->GetMethodID(env, luastate_class, "openLib", "(Lcom/naef/jnlua/LuaState$Library;)V")) 208 | || !(java_id = (*env)->GetStaticFieldID(env, library_class, "JAVA", "Lcom/naef/jnlua/LuaState$Library;")) 209 | || !(java = (*env)->GetStaticObjectField(env, library_class, java_id))) { 210 | return error(L, env, "Java module not found"); 211 | } 212 | (*env)->CallVoidMethod(env, luastate, openlib_id, java); 213 | if ((*env)->ExceptionCheck(env)) { 214 | return error(L, env, "error loading Java module"); 215 | } 216 | lua_pop(L, 1); 217 | 218 | /* Store VM */ 219 | lua_pushvalue(L, -1); 220 | lua_setfield(L, LUA_REGISTRYINDEX, JAVAVM_VM); 221 | 222 | return 1; 223 | } 224 | 225 | /* 226 | * Destroys the VM. 227 | */ 228 | static int destroy_vm (lua_State *L) { 229 | /* Release VM, if any */ 230 | lua_pushcfunction(L, release_vm); 231 | lua_getfield(L, LUA_REGISTRYINDEX, JAVAVM_VM); 232 | if (lua_isnil(L, -1)) { 233 | /* No VM to destroy */ 234 | lua_pushboolean(L, 0); 235 | return 1; 236 | } 237 | lua_call(L, 1, 0); 238 | 239 | /* Clear VM */ 240 | lua_pushnil(L); 241 | lua_setfield(L, LUA_REGISTRYINDEX, JAVAVM_VM); 242 | 243 | /* Success */ 244 | lua_pushboolean(L, 1); 245 | return 1; 246 | } 247 | 248 | /* 249 | * Returns the VM, if any. 250 | */ 251 | static int get_vm (lua_State *L) { 252 | lua_getfield(L, LUA_REGISTRYINDEX, JAVAVM_VM); 253 | return 1; 254 | } 255 | 256 | /* 257 | * Java VM module functions. 258 | */ 259 | static const luaL_Reg functions[] = { 260 | { "create", create_vm }, 261 | { "destroy", destroy_vm }, 262 | { "get", get_vm }, 263 | { NULL, NULL } 264 | }; 265 | 266 | /* 267 | * Exported functions. 268 | */ 269 | 270 | LUALIB_API int luaopen_javavm (lua_State *L) { 271 | /* Create module */ 272 | luaL_newlib(L, functions); 273 | 274 | /* Create metatable */ 275 | luaL_newmetatable(L, JAVAVM_METATABLE); 276 | lua_pushcfunction(L, release_vm); 277 | lua_setfield(L, -2, "__gc"); 278 | lua_pushcfunction(L, tostring_vm); 279 | lua_setfield(L, -2, "__tostring"); 280 | lua_pop(L, 1); 281 | 282 | return 1; 283 | } 284 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/util/AbstractTableMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.util; 7 | 8 | import java.util.AbstractMap; 9 | import java.util.AbstractSet; 10 | import java.util.Iterator; 11 | import java.util.Map; 12 | import java.util.NoSuchElementException; 13 | import java.util.Set; 14 | 15 | import com.naef.jnlua.LuaState; 16 | import com.naef.jnlua.LuaValueProxy; 17 | 18 | /** 19 | * Abstract map implementation backed by a Lua table. 20 | */ 21 | public abstract class AbstractTableMap extends AbstractMap 22 | implements LuaValueProxy { 23 | // -- State 24 | private Set> entrySet; 25 | 26 | // -- Construction 27 | /** 28 | * Creates a new instance. 29 | */ 30 | public AbstractTableMap() { 31 | } 32 | 33 | // -- Map methods 34 | @Override 35 | public Set> entrySet() { 36 | if (entrySet == null) { 37 | entrySet = new EntrySet(); 38 | } 39 | return entrySet; 40 | } 41 | 42 | @Override 43 | public boolean isEmpty() { 44 | return entrySet().isEmpty(); 45 | } 46 | 47 | @Override 48 | public boolean containsKey(Object key) { 49 | checkKey(key); 50 | LuaState luaState = getLuaState(); 51 | synchronized (luaState) { 52 | pushValue(); 53 | luaState.pushJavaObject(key); 54 | luaState.getTable(-2); 55 | try { 56 | return !luaState.isNil(-1); 57 | } finally { 58 | luaState.pop(2); 59 | } 60 | } 61 | } 62 | 63 | @Override 64 | public Object get(Object key) { 65 | checkKey(key); 66 | LuaState luaState = getLuaState(); 67 | synchronized (luaState) { 68 | pushValue(); 69 | luaState.pushJavaObject(key); 70 | luaState.getTable(-2); 71 | try { 72 | return luaState.toJavaObject(-1, Object.class); 73 | } finally { 74 | luaState.pop(2); 75 | } 76 | } 77 | } 78 | 79 | @Override 80 | public Object put(K key, Object value) { 81 | checkKey(key); 82 | LuaState luaState = getLuaState(); 83 | synchronized (luaState) { 84 | Object oldValue = get(key); 85 | pushValue(); 86 | luaState.pushJavaObject(key); 87 | luaState.pushJavaObject(value); 88 | luaState.setTable(-3); 89 | luaState.pop(1); 90 | return oldValue; 91 | } 92 | } 93 | 94 | @Override 95 | public Object remove(Object key) { 96 | checkKey(key); 97 | LuaState luaState = getLuaState(); 98 | synchronized (luaState) { 99 | Object oldValue = get(key); 100 | pushValue(); 101 | luaState.pushJavaObject(key); 102 | luaState.pushNil(); 103 | luaState.setTable(-3); 104 | luaState.pop(1); 105 | return oldValue; 106 | } 107 | } 108 | 109 | // -- Protected methods 110 | /** 111 | * Checks a key for validity. If the key is not valid, the method throws an 112 | * appropriate runtime exception. The method is invoked for all input keys. 113 | * 114 | *

115 | * This implementation checks that the key is not null. Lua 116 | * does not allow nil as a table key. Subclasses may implement 117 | * more restrictive checks. 118 | *

119 | * 120 | * @param key 121 | * the key 122 | * @throws NullPointerException 123 | * if the key is null 124 | */ 125 | protected void checkKey(Object key) { 126 | if (key == null) { 127 | throw new NullPointerException("key must not be null"); 128 | } 129 | } 130 | 131 | /** 132 | * Indicates if this table map filters keys from the Lua table. If the 133 | * method returns true, the table map invokes 134 | * {@link #acceptKey(int)} on each key retrieved from the underlying table 135 | * to determine whether the key is accepted or rejected. 136 | * 137 | *

138 | * This implementation returns false. Subclasses may override 139 | * the method alongside {@link #acceptKey(int)} to implement key filtering. 140 | *

141 | * 142 | * @return whether this table map filters keys from the Lua table 143 | */ 144 | protected boolean filterKeys() { 145 | return false; 146 | } 147 | 148 | /** 149 | * Accepts or rejects a key from the Lua table. Only table keys that are 150 | * accepted are processed. The method allows subclasses to filter the Lua 151 | * table. The method is called only if {@link #filterKeys()} returns 152 | * true. 153 | * 154 | *

155 | * This implementation returns true regardless of the input, 156 | * thus accepting all keys. Subclasses may override the method alongside 157 | * {@link #filterKeys()} to implement key filtering. 158 | *

159 | * 160 | * @param index 161 | * the stack index containing the candidate key 162 | * @return whether the key is accepted 163 | */ 164 | protected boolean acceptKey(int index) { 165 | return true; 166 | } 167 | 168 | /** 169 | * Converts the key at the specified stack index to a Java object. If this 170 | * table maps performs key filtering, the method is invoked only for keys it 171 | * has accepted. 172 | * 173 | * @param index 174 | * the stack index containing the key 175 | * @return the Java object representing the key 176 | * @see #filterKeys() 177 | * @see #acceptKey(int) 178 | */ 179 | protected abstract K convertKey(int index); 180 | 181 | // -- Nested types 182 | /** 183 | * Lua table entry set. 184 | */ 185 | private class EntrySet extends AbstractSet> { 186 | // -- Set methods 187 | @Override 188 | public Iterator> iterator() { 189 | return new EntryIterator(); 190 | } 191 | 192 | @Override 193 | public boolean isEmpty() { 194 | LuaState luaState = getLuaState(); 195 | synchronized (luaState) { 196 | pushValue(); 197 | luaState.pushNil(); 198 | while (luaState.next(-2)) { 199 | if (!filterKeys() || acceptKey(-2)) { 200 | luaState.pop(3); 201 | return false; 202 | } 203 | } 204 | luaState.pop(1); 205 | return true; 206 | } 207 | } 208 | 209 | @Override 210 | public int size() { 211 | LuaState luaState = getLuaState(); 212 | synchronized (luaState) { 213 | int count = 0; 214 | pushValue(); 215 | if (filterKeys()) { 216 | luaState.pushNil(); 217 | while (luaState.next(-2)) { 218 | if (acceptKey(-2)) { 219 | count++; 220 | } 221 | luaState.pop(1); 222 | } 223 | } else { 224 | count = luaState.tableSize(-1); 225 | } 226 | luaState.pop(1); 227 | return count; 228 | } 229 | } 230 | 231 | @Override 232 | public boolean contains(Object object) { 233 | checkKey(object); 234 | if (!(object instanceof AbstractTableMap.Entry)) { 235 | return false; 236 | } 237 | @SuppressWarnings("unchecked") 238 | Entry luaTableEntry = (Entry) object; 239 | if (luaTableEntry.getLuaState() != getLuaState()) { 240 | return false; 241 | } 242 | return containsKey(luaTableEntry.key); 243 | } 244 | 245 | @Override 246 | public boolean remove(Object object) { 247 | if (!(object instanceof AbstractTableMap.Entry)) { 248 | return false; 249 | } 250 | @SuppressWarnings("unchecked") 251 | Entry luaTableEntry = (Entry) object; 252 | if (luaTableEntry.getLuaState() != getLuaState()) { 253 | return false; 254 | } 255 | LuaState luaState = getLuaState(); 256 | synchronized (luaState) { 257 | pushValue(); 258 | luaState.pushJavaObject(object); 259 | luaState.getTable(-2); 260 | boolean contains = !luaState.isNil(-1); 261 | luaState.pop(1); 262 | if (contains) { 263 | luaState.pushJavaObject(object); 264 | luaState.pushNil(); 265 | luaState.setTable(-3); 266 | } 267 | luaState.pop(1); 268 | return contains; 269 | } 270 | } 271 | } 272 | 273 | /** 274 | * Lua table iterator. 275 | */ 276 | private class EntryIterator implements Iterator> { 277 | // -- State 278 | private K key; 279 | 280 | // -- Iterator methods 281 | @Override 282 | public boolean hasNext() { 283 | LuaState luaState = getLuaState(); 284 | synchronized (luaState) { 285 | pushValue(); 286 | luaState.pushJavaObject(key); 287 | while (luaState.next(-2)) { 288 | if (!filterKeys() || acceptKey(-2)) { 289 | luaState.pop(3); 290 | return true; 291 | } 292 | } 293 | luaState.pop(1); 294 | return false; 295 | } 296 | } 297 | 298 | @Override 299 | public Map.Entry next() { 300 | LuaState luaState = getLuaState(); 301 | synchronized (luaState) { 302 | pushValue(); 303 | luaState.pushJavaObject(key); 304 | while (luaState.next(-2)) { 305 | if (!filterKeys() || acceptKey(-2)) { 306 | key = convertKey(-2); 307 | luaState.pop(3); 308 | return new Entry(key); 309 | } 310 | } 311 | luaState.pop(1); 312 | throw new NoSuchElementException(); 313 | } 314 | } 315 | 316 | @Override 317 | public void remove() { 318 | LuaState luaState = getLuaState(); 319 | synchronized (luaState) { 320 | pushValue(); 321 | luaState.pushJavaObject(key); 322 | luaState.pushNil(); 323 | luaState.setTable(-3); 324 | luaState.pop(1); 325 | } 326 | } 327 | } 328 | 329 | /** 330 | * Bindings entry. 331 | */ 332 | private class Entry implements Map.Entry { 333 | // -- State 334 | private K key; 335 | 336 | // -- Construction 337 | /** 338 | * Creates a new instance. 339 | */ 340 | public Entry(K key) { 341 | this.key = key; 342 | } 343 | 344 | // -- Map.Entry methods 345 | @Override 346 | public K getKey() { 347 | return key; 348 | } 349 | 350 | @Override 351 | public Object getValue() { 352 | return get(key); 353 | } 354 | 355 | @Override 356 | public Object setValue(Object value) { 357 | return put(key, value); 358 | } 359 | 360 | // -- Object methods 361 | @Override 362 | public boolean equals(Object obj) { 363 | if (!(obj instanceof AbstractTableMap.Entry)) { 364 | return false; 365 | } 366 | @SuppressWarnings("unchecked") 367 | Entry other = (Entry) obj; 368 | return getLuaState() == other.getLuaState() 369 | && key.equals(other.key); 370 | } 371 | 372 | @Override 373 | public int hashCode() { 374 | return getLuaState().hashCode() * 65599 + key.hashCode(); 375 | } 376 | 377 | @Override 378 | public String toString() { 379 | return key.toString(); 380 | } 381 | 382 | // -- Private methods 383 | /** 384 | * Returns the Lua script engine. 385 | */ 386 | private LuaState getLuaState() { 387 | return AbstractTableMap.this.getLuaState(); 388 | } 389 | } 390 | } -------------------------------------------------------------------------------- /jnlua/src/test/java/com/naef/jnlua/test/LuaScriptEngineTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.test; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | import static org.junit.Assert.assertNotNull; 10 | import static org.junit.Assert.assertNull; 11 | import static org.junit.Assert.assertSame; 12 | import static org.junit.Assert.assertTrue; 13 | 14 | import java.io.StringReader; 15 | import java.util.List; 16 | 17 | import javax.script.Bindings; 18 | import javax.script.Compilable; 19 | import javax.script.CompiledScript; 20 | import javax.script.Invocable; 21 | import javax.script.ScriptContext; 22 | import javax.script.ScriptEngine; 23 | import javax.script.ScriptEngineFactory; 24 | import javax.script.ScriptEngineManager; 25 | import javax.script.ScriptException; 26 | import javax.script.SimpleBindings; 27 | import javax.script.SimpleScriptContext; 28 | 29 | import org.junit.Before; 30 | import org.junit.Test; 31 | 32 | public class LuaScriptEngineTest { 33 | // -- State 34 | private ScriptEngineManager scriptEngineManager; 35 | private ScriptEngine scriptEngine; 36 | 37 | // -- Setup 38 | /** 39 | * Performs setup. 40 | */ 41 | @Before 42 | public void setup() throws Exception { 43 | scriptEngineManager = new ScriptEngineManager(); 44 | scriptEngine = scriptEngineManager.getEngineByName("Lua"); 45 | } 46 | 47 | // -- Test cases 48 | /** 49 | * Tests the acquisition of the Lua script engine from the manager. 50 | */ 51 | @Test 52 | public void testAcquisition() { 53 | assertNotNull(scriptEngineManager.getEngineByExtension("lua")); 54 | assertNotNull(scriptEngineManager 55 | .getEngineByMimeType("application/x-lua")); 56 | assertNotNull(scriptEngineManager.getEngineByMimeType("text/x-lua")); 57 | assertNotNull(scriptEngineManager.getEngineByName("lua")); 58 | assertNotNull(scriptEngineManager.getEngineByName("Lua")); 59 | assertNotNull(scriptEngineManager.getEngineByName("jnlua")); 60 | assertNotNull(scriptEngineManager.getEngineByName("JNLua")); 61 | } 62 | 63 | /** 64 | * Tests the Lua script engine factory. 65 | */ 66 | @Test 67 | public void testScriptEngineFactory() { 68 | // Get factory 69 | ScriptEngineFactory factory = scriptEngine.getFactory(); 70 | 71 | // getEngineName() 72 | assertEquals("JNLua", factory.getEngineName()); 73 | 74 | // getEngineVersion() 75 | assertEquals("1.0", factory.getEngineVersion()); 76 | 77 | // getNames() 78 | List names = factory.getNames(); 79 | assertTrue(names.contains("lua")); 80 | assertTrue(names.contains("Lua")); 81 | assertTrue(names.contains("jnlua")); 82 | assertTrue(names.contains("JNLua")); 83 | 84 | // getLanguageName() 85 | assertEquals("Lua", factory.getLanguageName()); 86 | 87 | // getLanguageVersion() 88 | assertEquals("5.2", factory.getLanguageVersion()); 89 | 90 | // getExtensions() 91 | List extensions = factory.getExtensions(); 92 | assertTrue(extensions.contains("lua")); 93 | 94 | // getMimeTypes() 95 | List mimeTypes = factory.getMimeTypes(); 96 | assertTrue(mimeTypes.contains("application/x-lua")); 97 | assertTrue(mimeTypes.contains("text/x-lua")); 98 | 99 | // getParameter() 100 | assertEquals(factory.getEngineName(), factory 101 | .getParameter(ScriptEngine.ENGINE)); 102 | assertEquals(factory.getEngineVersion(), factory 103 | .getParameter(ScriptEngine.ENGINE_VERSION)); 104 | assertTrue(factory.getNames().contains( 105 | factory.getParameter(ScriptEngine.NAME))); 106 | assertEquals(factory.getLanguageName(), factory 107 | .getParameter(ScriptEngine.LANGUAGE)); 108 | assertEquals(factory.getLanguageVersion(), factory 109 | .getParameter(ScriptEngine.LANGUAGE_VERSION)); 110 | 111 | // getScriptEngine() 112 | assertNotNull(factory.getScriptEngine()); 113 | 114 | // getMethodCallSyntax() 115 | assertEquals("process:execute(a, b)", factory.getMethodCallSyntax( 116 | "process", "execute", "a", "b")); 117 | 118 | // getOutputStatement() 119 | assertEquals("print(\"test\")", factory.getOutputStatement("test")); 120 | assertEquals("print(\"\\\"quoted\\\"\")", factory 121 | .getOutputStatement("\"quoted\"")); 122 | 123 | // getProgram() 124 | assertEquals("a = 1\nreturn b\n", factory.getProgram("a = 1", 125 | "return b")); 126 | } 127 | 128 | /** 129 | * Tests the Lua script engine. 130 | */ 131 | @Test 132 | public void testScriptEngine() throws Exception { 133 | // eval(String) 134 | assertEquals(Double.valueOf(1.0), scriptEngine.eval("return 1")); 135 | 136 | // eval(Reader) 137 | assertEquals(Double.valueOf(1.0), scriptEngine.eval(new StringReader( 138 | "return 1"))); 139 | 140 | // createBindings() 141 | Bindings bindings = scriptEngine.createBindings(); 142 | assertNotNull(bindings); 143 | 144 | // eval(String, Bindings) 145 | bindings.put("t", "test1"); 146 | assertEquals("test1", scriptEngine.eval("return t", bindings)); 147 | 148 | // eval(Reader, Bindings) 149 | bindings.put("t", "test2"); 150 | assertEquals("test2", scriptEngine.eval(new StringReader("return t"), 151 | bindings)); 152 | 153 | // eval(String, ScriptContext) 154 | ScriptContext context = new SimpleScriptContext(); 155 | context.setBindings(new SimpleBindings(), ScriptContext.GLOBAL_SCOPE); 156 | context.setBindings(bindings, ScriptContext.ENGINE_SCOPE); 157 | bindings.put("t", "test3"); 158 | assertEquals("test3", scriptEngine.eval("return t", context)); 159 | 160 | // eval(Reader, ScriptContext) 161 | bindings.put("t", "test4"); 162 | assertEquals("test4", scriptEngine.eval("return t", context)); 163 | 164 | // getBindings(), setBindings() 165 | scriptEngine.setBindings(bindings, ScriptContext.ENGINE_SCOPE); 166 | assertSame(bindings, scriptEngine 167 | .getBindings(ScriptContext.ENGINE_SCOPE)); 168 | scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE).put("t", "test5"); 169 | assertEquals("test5", scriptEngine.eval("return t")); 170 | 171 | // get(), put() 172 | scriptEngine.put("t", "test6"); 173 | assertEquals("test6", scriptEngine.get("t")); 174 | assertEquals("test6", bindings.get("t")); 175 | assertEquals("test6", scriptEngine.eval("return t")); 176 | 177 | // getContext(), setContext() 178 | scriptEngine.setContext(context); 179 | assertSame(context, scriptEngine.getContext()); 180 | context.setAttribute("t", "test7", ScriptContext.GLOBAL_SCOPE); 181 | context.removeAttribute("t", ScriptContext.ENGINE_SCOPE); 182 | assertEquals("test7", scriptEngine.eval("return t")); 183 | 184 | // getFactory() 185 | assertNotNull(scriptEngine.getFactory()); 186 | 187 | // Special variables 188 | scriptEngine.put(ScriptEngine.ARGV, new Object[] { "test8" }); 189 | assertEquals("test8", scriptEngine.eval("return ...")); 190 | assertSame(context.getReader(), scriptEngine.eval("return reader")); 191 | assertSame(context.getWriter(), scriptEngine.eval("return writer")); 192 | assertSame(context.getErrorWriter(), scriptEngine 193 | .eval("return errorWriter")); 194 | 195 | // Syntax error diagnostics 196 | scriptEngine.put(ScriptEngine.FILENAME, "test9"); 197 | ScriptException scriptException = null; 198 | try { 199 | scriptEngine.eval("a bad script"); 200 | } catch (ScriptException e) { 201 | scriptException = e; 202 | } 203 | assertNotNull(scriptException); 204 | assertEquals("test9", scriptException.getFileName()); 205 | assertEquals(1, scriptException.getLineNumber()); 206 | 207 | // Runtime error diagnostics 208 | scriptException = null; 209 | try { 210 | scriptEngine.eval("error(\"error\")"); 211 | } catch (ScriptException e) { 212 | scriptException = e; 213 | } 214 | assertNotNull(scriptException); 215 | assertEquals("test9", scriptException.getFileName()); 216 | assertEquals(1, scriptException.getLineNumber()); 217 | } 218 | 219 | /** 220 | * Tests the compilable interface. 221 | */ 222 | @Test 223 | public void testCompilable() throws Exception { 224 | // Check 225 | assertTrue(scriptEngine instanceof Compilable); 226 | Compilable compilable = (Compilable) scriptEngine; 227 | 228 | // Setup 229 | Bindings bindings = new SimpleBindings(); 230 | bindings.put("t", Double.valueOf(2.0)); 231 | ScriptContext scriptContext = new SimpleScriptContext(); 232 | scriptContext.setBindings(new SimpleBindings(), 233 | ScriptContext.GLOBAL_SCOPE); 234 | scriptContext.setAttribute("t", Double.valueOf(3.0), 235 | ScriptContext.GLOBAL_SCOPE); 236 | 237 | // Compile(String) 238 | CompiledScript compiledScript = compilable.compile("return t"); 239 | scriptEngine.put("t", Double.valueOf(1.0)); 240 | assertEquals(Double.valueOf(1.0), compiledScript.eval()); 241 | assertEquals(Double.valueOf(2.0), compiledScript.eval(bindings)); 242 | assertEquals(Double.valueOf(3.0), compiledScript.eval(scriptContext)); 243 | 244 | // Compile(Reader) 245 | compiledScript = compilable.compile(new StringReader("return t")); 246 | scriptEngine.put("t", Double.valueOf(1.0)); 247 | assertEquals(Double.valueOf(1.0), compiledScript.eval()); 248 | assertEquals(Double.valueOf(2.0), compiledScript.eval(bindings)); 249 | assertEquals(Double.valueOf(3.0), compiledScript.eval(scriptContext)); 250 | } 251 | 252 | /** 253 | * Tests the invocable interface. 254 | */ 255 | @Test 256 | public void testIncocable() throws Exception { 257 | // Check 258 | assertTrue(scriptEngine instanceof Invocable); 259 | Invocable invocable = (Invocable) scriptEngine; 260 | 261 | // Setup 262 | scriptEngine.eval("function echo(s) return s end"); 263 | scriptEngine.eval("function run() hasRun = true end"); 264 | scriptEngine.eval("runnable = { run = run }"); 265 | 266 | // invokeFunction() 267 | assertEquals("test", invocable.invokeFunction("echo", 268 | new Object[] { "test" })); 269 | 270 | // invokeMethod() 271 | scriptEngine.put("hasRun", Boolean.FALSE); 272 | assertEquals(Boolean.FALSE, scriptEngine.get("hasRun")); 273 | Object runnableObj = scriptEngine.get("runnable"); 274 | invocable.invokeMethod(runnableObj, "run", new Object[0]); 275 | assertEquals(Boolean.TRUE, scriptEngine.get("hasRun")); 276 | 277 | // getInterface() 278 | scriptEngine.put("hasRun", Boolean.FALSE); 279 | assertEquals(Boolean.FALSE, scriptEngine.get("hasRun")); 280 | Runnable runnable = invocable.getInterface(Runnable.class); 281 | Thread thread = new Thread(runnable); 282 | thread.start(); 283 | thread.join(); 284 | assertEquals(Boolean.TRUE, scriptEngine.get("hasRun")); 285 | 286 | // getInterface(Object) 287 | scriptEngine.put("hasRun", Boolean.FALSE); 288 | assertEquals(Boolean.FALSE, scriptEngine.get("hasRun")); 289 | runnable = invocable.getInterface(runnableObj, Runnable.class); 290 | thread = new Thread(runnable); 291 | thread.start(); 292 | thread.join(); 293 | assertEquals(Boolean.TRUE, scriptEngine.get("hasRun")); 294 | } 295 | 296 | /** 297 | * Tests the bindings. 298 | */ 299 | @Test 300 | public void testBindings() throws Exception { 301 | // Get bindings 302 | Bindings bindings = scriptEngine 303 | .getBindings(ScriptContext.ENGINE_SCOPE); 304 | 305 | // Put 306 | bindings.put("t", "test"); 307 | assertEquals("test", scriptEngine.eval("return t")); 308 | 309 | // Get 310 | scriptEngine.eval("t = \"test2\""); 311 | assertEquals("test2", bindings.get("t")); 312 | 313 | // Remove 314 | bindings.remove("t"); 315 | assertNull(scriptEngine.eval("return t")); 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/script/LuaScriptEngine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.script; 7 | 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.OutputStream; 12 | import java.io.Reader; 13 | import java.nio.ByteBuffer; 14 | import java.nio.CharBuffer; 15 | import java.nio.charset.Charset; 16 | import java.nio.charset.CharsetEncoder; 17 | import java.util.Map; 18 | import java.util.regex.Matcher; 19 | import java.util.regex.Pattern; 20 | 21 | import javax.script.AbstractScriptEngine; 22 | import javax.script.Bindings; 23 | import javax.script.Compilable; 24 | import javax.script.CompiledScript; 25 | import javax.script.Invocable; 26 | import javax.script.ScriptContext; 27 | import javax.script.ScriptEngineFactory; 28 | import javax.script.ScriptException; 29 | 30 | import com.naef.jnlua.LuaException; 31 | import com.naef.jnlua.LuaState; 32 | 33 | /** 34 | * Lua script engine implementation conforming to JSR 223: Scripting for the 35 | * Java Platform. 36 | */ 37 | class LuaScriptEngine extends AbstractScriptEngine implements Compilable, 38 | Invocable { 39 | // -- Static 40 | private static final String READER = "reader"; 41 | private static final String WRITER = "writer"; 42 | private static final String ERROR_WRITER = "errorWriter"; 43 | private static final Pattern LUA_ERROR_MESSAGE = Pattern 44 | .compile("^(.+):(\\d+):"); 45 | 46 | // -- State 47 | private LuaScriptEngineFactory factory; 48 | private LuaState luaState; 49 | 50 | // -- Construction 51 | /** 52 | * Creates a new instance. 53 | */ 54 | LuaScriptEngine(LuaScriptEngineFactory factory) { 55 | super(); 56 | this.factory = factory; 57 | luaState = new LuaState(); 58 | 59 | // Configuration 60 | context.setBindings(createBindings(), ScriptContext.ENGINE_SCOPE); 61 | luaState.openLibs(); 62 | } 63 | 64 | // -- ScriptEngine methods 65 | @Override 66 | public Bindings createBindings() { 67 | return new LuaBindings(this); 68 | } 69 | 70 | @Override 71 | public Object eval(String script, ScriptContext context) 72 | throws ScriptException { 73 | synchronized (luaState) { 74 | loadChunk(script, context); 75 | return callChunk(context); 76 | } 77 | } 78 | 79 | @Override 80 | public Object eval(Reader reader, ScriptContext context) 81 | throws ScriptException { 82 | synchronized (luaState) { 83 | loadChunk(reader, context); 84 | return callChunk(context); 85 | } 86 | } 87 | 88 | @Override 89 | public ScriptEngineFactory getFactory() { 90 | return factory; 91 | } 92 | 93 | // -- Compilable method 94 | @Override 95 | public CompiledScript compile(String script) throws ScriptException { 96 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 97 | synchronized (luaState) { 98 | loadChunk(script, null); 99 | try { 100 | dumpChunk(out); 101 | } finally { 102 | luaState.pop(1); 103 | } 104 | } 105 | return new CompiledLuaScript(this, out.toByteArray()); 106 | } 107 | 108 | @Override 109 | public CompiledScript compile(Reader script) throws ScriptException { 110 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 111 | synchronized (luaState) { 112 | loadChunk(script, null); 113 | try { 114 | dumpChunk(out); 115 | } finally { 116 | luaState.pop(1); 117 | } 118 | } 119 | return new CompiledLuaScript(this, out.toByteArray()); 120 | } 121 | 122 | // -- Invocable methods 123 | @Override 124 | public T getInterface(Class clasz) { 125 | synchronized (luaState) { 126 | getLuaState().rawGet(LuaState.REGISTRYINDEX, LuaState.RIDX_GLOBALS); 127 | try { 128 | return luaState.getProxy(-1, clasz); 129 | } finally { 130 | luaState.pop(1); 131 | } 132 | } 133 | } 134 | 135 | @Override 136 | public T getInterface(Object thiz, Class clasz) { 137 | synchronized (luaState) { 138 | luaState.pushJavaObject(thiz); 139 | try { 140 | if (!luaState.isTable(-1)) { 141 | throw new IllegalArgumentException("object is not a table"); 142 | } 143 | return luaState.getProxy(-1, clasz); 144 | } finally { 145 | luaState.pop(1); 146 | } 147 | } 148 | } 149 | 150 | @Override 151 | public Object invokeFunction(String name, Object... args) 152 | throws ScriptException, NoSuchMethodException { 153 | synchronized (luaState) { 154 | luaState.getGlobal(name); 155 | if (!luaState.isFunction(-1)) { 156 | luaState.pop(1); 157 | throw new NoSuchMethodException(String.format( 158 | "function '%s' is undefined", name)); 159 | } 160 | for (int i = 0; i < args.length; i++) { 161 | luaState.pushJavaObject(args[i]); 162 | } 163 | luaState.call(args.length, 1); 164 | try { 165 | return luaState.toJavaObject(-1, Object.class); 166 | } finally { 167 | luaState.pop(1); 168 | } 169 | } 170 | } 171 | 172 | @Override 173 | public Object invokeMethod(Object thiz, String name, Object... args) 174 | throws ScriptException, NoSuchMethodException { 175 | synchronized (luaState) { 176 | luaState.pushJavaObject(thiz); 177 | try { 178 | if (!luaState.isTable(-1)) { 179 | throw new IllegalArgumentException("object is not a table"); 180 | } 181 | luaState.getField(-1, name); 182 | if (!luaState.isFunction(-1)) { 183 | luaState.pop(1); 184 | throw new NoSuchMethodException(String.format( 185 | "method '%s' is undefined", name)); 186 | } 187 | luaState.pushValue(-2); 188 | for (int i = 0; i < args.length; i++) { 189 | luaState.pushJavaObject(args[i]); 190 | } 191 | luaState.call(args.length + 1, 1); 192 | try { 193 | return luaState.toJavaObject(-1, Object.class); 194 | } finally { 195 | luaState.pop(1); 196 | } 197 | } finally { 198 | luaState.pop(1); 199 | } 200 | } 201 | } 202 | 203 | // -- Package private methods 204 | /** 205 | * Returns the Lua state. 206 | */ 207 | LuaState getLuaState() { 208 | return luaState; 209 | } 210 | 211 | /** 212 | * Loads a chunk from a string. 213 | */ 214 | void loadChunk(String string, ScriptContext scriptContext) 215 | throws ScriptException { 216 | try { 217 | luaState.load(string, getChunkName(scriptContext)); 218 | } catch (LuaException e) { 219 | throw getScriptException(e); 220 | } 221 | } 222 | 223 | /** 224 | * Loads a chunk from a reader. 225 | */ 226 | void loadChunk(Reader reader, ScriptContext scriptContext) 227 | throws ScriptException { 228 | loadChunk(new ReaderInputStream(reader), scriptContext, "t"); 229 | } 230 | 231 | /** 232 | * Loads a chunk from an input stream. 233 | */ 234 | void loadChunk(InputStream inputStream, ScriptContext scriptContext, 235 | String mode) throws ScriptException { 236 | try { 237 | luaState.load(inputStream, getChunkName(scriptContext), mode); 238 | } catch (LuaException e) { 239 | throw getScriptException(e); 240 | } catch (IOException e) { 241 | throw new ScriptException(e); 242 | } 243 | } 244 | 245 | /** 246 | * Calls a loaded chunk. 247 | */ 248 | Object callChunk(ScriptContext context) throws ScriptException { 249 | try { 250 | // Apply context 251 | Object[] argv; 252 | if (context != null) { 253 | // Global bindings 254 | Bindings bindings; 255 | bindings = context.getBindings(ScriptContext.GLOBAL_SCOPE); 256 | if (bindings != null) { 257 | applyBindings(bindings); 258 | } 259 | 260 | // Engine bindings 261 | bindings = context.getBindings(ScriptContext.ENGINE_SCOPE); 262 | if (bindings != null) { 263 | if (bindings instanceof LuaBindings 264 | && ((LuaBindings) bindings).getScriptEngine() == this) { 265 | // No need to apply our own live bindings 266 | } else { 267 | applyBindings(bindings); 268 | } 269 | } 270 | 271 | // Readers and writers 272 | put(READER, context.getReader()); 273 | put(WRITER, context.getWriter()); 274 | put(ERROR_WRITER, context.getErrorWriter()); 275 | 276 | // Arguments 277 | argv = (Object[]) context.getAttribute(ARGV); 278 | } else { 279 | argv = null; 280 | } 281 | 282 | // Push arguments 283 | int argCount = argv != null ? argv.length : 0; 284 | for (int i = 0; i < argCount; i++) { 285 | luaState.pushJavaObject(argv[i]); 286 | } 287 | 288 | // Call 289 | luaState.call(argCount, 1); 290 | 291 | // Return 292 | try { 293 | return luaState.toJavaObject(1, Object.class); 294 | } finally { 295 | luaState.pop(1); 296 | } 297 | } catch (LuaException e) { 298 | throw getScriptException(e); 299 | } 300 | } 301 | 302 | /** 303 | * Dumps a loaded chunk into an output stream. The chunk is left on the 304 | * stack. 305 | */ 306 | void dumpChunk(OutputStream out) throws ScriptException { 307 | try { 308 | luaState.dump(out); 309 | } catch (LuaException e) { 310 | throw new ScriptException(e); 311 | } catch (IOException e) { 312 | throw new ScriptException(e); 313 | } 314 | 315 | } 316 | 317 | // -- Private methods 318 | /** 319 | * Sets a single binding in a Lua state. 320 | */ 321 | private void applyBindings(Bindings bindings) { 322 | for (Map.Entry binding : bindings.entrySet()) { 323 | luaState.pushJavaObject(binding.getValue()); 324 | String variableName = binding.getKey(); 325 | int lastDotIndex = variableName.lastIndexOf('.'); 326 | if (lastDotIndex >= 0) { 327 | variableName = variableName.substring(lastDotIndex + 1); 328 | } 329 | luaState.setGlobal(variableName); 330 | } 331 | } 332 | 333 | /** 334 | * Returns the Lua chunk name from a script context. 335 | */ 336 | private String getChunkName(ScriptContext context) { 337 | if (context != null) { 338 | Object fileName = context.getAttribute(FILENAME); 339 | if (fileName != null) { 340 | return "@" + fileName.toString(); 341 | } 342 | } 343 | return "=null"; 344 | } 345 | 346 | /** 347 | * Returns a script exception for a Lua exception. 348 | */ 349 | private ScriptException getScriptException(LuaException e) { 350 | Matcher matcher = LUA_ERROR_MESSAGE.matcher(e.getMessage()); 351 | if (matcher.find()) { 352 | String fileName = matcher.group(1); 353 | int lineNumber = Integer.parseInt(matcher.group(2)); 354 | return new ScriptException(e.getMessage(), fileName, lineNumber); 355 | } else { 356 | return new ScriptException(e); 357 | } 358 | } 359 | 360 | // -- Private classes 361 | /** 362 | * Provides an UTF-8 input stream based on a reader. 363 | */ 364 | private static class ReaderInputStream extends InputStream { 365 | // -- Static 366 | private static final Charset UTF8 = Charset.forName("UTF-8"); 367 | 368 | // -- State 369 | private Reader reader; 370 | private CharsetEncoder encoder; 371 | private boolean flushed; 372 | private CharBuffer charBuffer = CharBuffer.allocate(1024); 373 | private ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 374 | 375 | /** 376 | * Creates a new instance. 377 | */ 378 | public ReaderInputStream(Reader reader) { 379 | this.reader = reader; 380 | encoder = UTF8.newEncoder(); 381 | charBuffer.limit(0); 382 | byteBuffer.limit(0); 383 | } 384 | 385 | @Override 386 | public int read() throws IOException { 387 | if (!byteBuffer.hasRemaining()) { 388 | if (!charBuffer.hasRemaining()) { 389 | charBuffer.clear(); 390 | reader.read(charBuffer); 391 | charBuffer.flip(); 392 | } 393 | byteBuffer.clear(); 394 | if (charBuffer.hasRemaining()) { 395 | if (encoder.encode(charBuffer, byteBuffer, false).isError()) { 396 | throw new IOException("Encoding error"); 397 | } 398 | } else { 399 | if (!flushed) { 400 | if (encoder.encode(charBuffer, byteBuffer, true) 401 | .isError()) { 402 | throw new IOException("Encoding error"); 403 | } 404 | if (encoder.flush(byteBuffer).isError()) { 405 | throw new IOException("Encoding error"); 406 | } 407 | flushed = true; 408 | } 409 | } 410 | byteBuffer.flip(); 411 | if (!byteBuffer.hasRemaining()) { 412 | return -1; 413 | } 414 | } 415 | return byteBuffer.get(); 416 | } 417 | } 418 | } 419 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/DefaultConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | import java.lang.reflect.Array; 9 | import java.math.BigDecimal; 10 | import java.math.BigInteger; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import com.naef.jnlua.util.AbstractTableList; 16 | import com.naef.jnlua.util.AbstractTableMap; 17 | 18 | /** 19 | * Default implementation of the Converter interface. 20 | */ 21 | public class DefaultConverter implements Converter { 22 | // -- Static 23 | /** 24 | * Raw byte array. 25 | */ 26 | private static final boolean RAW_BYTE_ARRAY = Boolean.parseBoolean(System 27 | .getProperty(DefaultConverter.class.getPackage().getName() 28 | + ".rawByteArray")); 29 | 30 | /** 31 | * Static instance. 32 | */ 33 | private static final DefaultConverter INSTANCE = new DefaultConverter(); 34 | 35 | /** 36 | * Boolean distance map. 37 | */ 38 | private static final Map, Integer> BOOLEAN_DISTANCE_MAP = new HashMap, Integer>(); 39 | static { 40 | BOOLEAN_DISTANCE_MAP.put(Boolean.class, new Integer(1)); 41 | BOOLEAN_DISTANCE_MAP.put(Boolean.TYPE, new Integer(1)); 42 | BOOLEAN_DISTANCE_MAP.put(Object.class, new Integer(2)); 43 | } 44 | 45 | /** 46 | * Number distance map. 47 | */ 48 | private static final Map, Integer> NUMBER_DISTANCE_MAP = new HashMap, Integer>(); 49 | static { 50 | NUMBER_DISTANCE_MAP.put(Byte.class, new Integer(1)); 51 | NUMBER_DISTANCE_MAP.put(Byte.TYPE, new Integer(1)); 52 | NUMBER_DISTANCE_MAP.put(Short.class, new Integer(1)); 53 | NUMBER_DISTANCE_MAP.put(Short.TYPE, new Integer(1)); 54 | NUMBER_DISTANCE_MAP.put(Integer.class, new Integer(1)); 55 | NUMBER_DISTANCE_MAP.put(Integer.TYPE, new Integer(1)); 56 | NUMBER_DISTANCE_MAP.put(Long.class, new Integer(1)); 57 | NUMBER_DISTANCE_MAP.put(Long.TYPE, new Integer(1)); 58 | NUMBER_DISTANCE_MAP.put(Float.class, new Integer(1)); 59 | NUMBER_DISTANCE_MAP.put(Float.TYPE, new Integer(1)); 60 | NUMBER_DISTANCE_MAP.put(Double.class, new Integer(1)); 61 | NUMBER_DISTANCE_MAP.put(Double.TYPE, new Integer(1)); 62 | NUMBER_DISTANCE_MAP.put(BigInteger.class, new Integer(1)); 63 | NUMBER_DISTANCE_MAP.put(BigDecimal.class, new Integer(1)); 64 | NUMBER_DISTANCE_MAP.put(Character.class, new Integer(1)); 65 | NUMBER_DISTANCE_MAP.put(Character.TYPE, new Integer(1)); 66 | NUMBER_DISTANCE_MAP.put(Object.class, new Integer(2)); 67 | NUMBER_DISTANCE_MAP.put(String.class, new Integer(3)); 68 | if (!RAW_BYTE_ARRAY) { 69 | NUMBER_DISTANCE_MAP.put(byte[].class, new Integer(3)); 70 | } 71 | } 72 | 73 | /** 74 | * String distance map. 75 | */ 76 | private static final Map, Integer> STRING_DISTANCE_MAP = new HashMap, Integer>(); 77 | static { 78 | STRING_DISTANCE_MAP.put(String.class, new Integer(1)); 79 | if (!RAW_BYTE_ARRAY) { 80 | STRING_DISTANCE_MAP.put(byte[].class, new Integer(1)); 81 | } 82 | STRING_DISTANCE_MAP.put(Object.class, new Integer(2)); 83 | STRING_DISTANCE_MAP.put(Byte.class, new Integer(3)); 84 | STRING_DISTANCE_MAP.put(Byte.TYPE, new Integer(3)); 85 | STRING_DISTANCE_MAP.put(Short.class, new Integer(3)); 86 | STRING_DISTANCE_MAP.put(Short.TYPE, new Integer(3)); 87 | STRING_DISTANCE_MAP.put(Integer.class, new Integer(3)); 88 | STRING_DISTANCE_MAP.put(Integer.TYPE, new Integer(3)); 89 | STRING_DISTANCE_MAP.put(Long.class, new Integer(3)); 90 | STRING_DISTANCE_MAP.put(Long.TYPE, new Integer(3)); 91 | STRING_DISTANCE_MAP.put(Float.class, new Integer(3)); 92 | STRING_DISTANCE_MAP.put(Float.TYPE, new Integer(3)); 93 | STRING_DISTANCE_MAP.put(Double.class, new Integer(3)); 94 | STRING_DISTANCE_MAP.put(Double.TYPE, new Integer(3)); 95 | STRING_DISTANCE_MAP.put(BigInteger.class, new Integer(3)); 96 | STRING_DISTANCE_MAP.put(BigDecimal.class, new Integer(3)); 97 | STRING_DISTANCE_MAP.put(Character.class, new Integer(3)); 98 | STRING_DISTANCE_MAP.put(Character.TYPE, new Integer(3)); 99 | } 100 | 101 | /** 102 | * Function distance map. 103 | */ 104 | private static final Map, Integer> FUNCTION_DISTANCE_MAP = new HashMap, Integer>(); 105 | static { 106 | FUNCTION_DISTANCE_MAP.put(JavaFunction.class, new Integer(1)); 107 | FUNCTION_DISTANCE_MAP.put(Object.class, new Integer(2)); 108 | } 109 | 110 | /** 111 | * Lua value converters. 112 | */ 113 | private static final Map, LuaValueConverter> LUA_VALUE_CONVERTERS = new HashMap, LuaValueConverter>(); 114 | static { 115 | LuaValueConverter booleanConverter = new LuaValueConverter() { 116 | @Override 117 | public Boolean convert(LuaState luaState, int index) { 118 | return Boolean.valueOf(luaState.toBoolean(index)); 119 | } 120 | }; 121 | LUA_VALUE_CONVERTERS.put(Boolean.class, booleanConverter); 122 | LUA_VALUE_CONVERTERS.put(Boolean.TYPE, booleanConverter); 123 | 124 | LuaValueConverter byteConverter = new LuaValueConverter() { 125 | @Override 126 | public Byte convert(LuaState luaState, int index) { 127 | return Byte.valueOf((byte) luaState.toInteger(index)); 128 | } 129 | }; 130 | LUA_VALUE_CONVERTERS.put(Byte.class, byteConverter); 131 | LUA_VALUE_CONVERTERS.put(Byte.TYPE, byteConverter); 132 | LuaValueConverter shortConverter = new LuaValueConverter() { 133 | @Override 134 | public Short convert(LuaState luaState, int index) { 135 | return Short.valueOf((short) luaState.toInteger(index)); 136 | } 137 | }; 138 | LUA_VALUE_CONVERTERS.put(Short.class, shortConverter); 139 | LUA_VALUE_CONVERTERS.put(Short.TYPE, shortConverter); 140 | LuaValueConverter integerConverter = new LuaValueConverter() { 141 | @Override 142 | public Integer convert(LuaState luaState, int index) { 143 | return Integer.valueOf(luaState.toInteger(index)); 144 | } 145 | }; 146 | LUA_VALUE_CONVERTERS.put(Integer.class, integerConverter); 147 | LUA_VALUE_CONVERTERS.put(Integer.TYPE, integerConverter); 148 | LuaValueConverter longConverter = new LuaValueConverter() { 149 | @Override 150 | public Long convert(LuaState luaState, int index) { 151 | return Long.valueOf((long) luaState.toNumber(index)); 152 | } 153 | }; 154 | LUA_VALUE_CONVERTERS.put(Long.class, longConverter); 155 | LUA_VALUE_CONVERTERS.put(Long.TYPE, longConverter); 156 | LuaValueConverter floatConverter = new LuaValueConverter() { 157 | @Override 158 | public Float convert(LuaState luaState, int index) { 159 | return Float.valueOf((float) luaState.toNumber(index)); 160 | } 161 | }; 162 | LUA_VALUE_CONVERTERS.put(Float.class, floatConverter); 163 | LUA_VALUE_CONVERTERS.put(Float.TYPE, floatConverter); 164 | LuaValueConverter doubleConverter = new LuaValueConverter() { 165 | @Override 166 | public Double convert(LuaState luaState, int index) { 167 | return Double.valueOf(luaState.toNumber(index)); 168 | } 169 | }; 170 | LUA_VALUE_CONVERTERS.put(Double.class, doubleConverter); 171 | LUA_VALUE_CONVERTERS.put(Double.TYPE, doubleConverter); 172 | LuaValueConverter bigIntegerConverter = new LuaValueConverter() { 173 | @Override 174 | public BigInteger convert(LuaState luaState, int index) { 175 | return BigDecimal.valueOf(luaState.toNumber(index)) 176 | .setScale(0, BigDecimal.ROUND_HALF_EVEN).toBigInteger(); 177 | } 178 | }; 179 | LUA_VALUE_CONVERTERS.put(BigInteger.class, bigIntegerConverter); 180 | LuaValueConverter bigDecimalConverter = new LuaValueConverter() { 181 | @Override 182 | public BigDecimal convert(LuaState luaState, int index) { 183 | return BigDecimal.valueOf(luaState.toNumber(index)); 184 | } 185 | }; 186 | LUA_VALUE_CONVERTERS.put(BigDecimal.class, bigDecimalConverter); 187 | LuaValueConverter characterConverter = new LuaValueConverter() { 188 | @Override 189 | public Character convert(LuaState luaState, int index) { 190 | return Character.valueOf((char) luaState.toInteger(index)); 191 | } 192 | }; 193 | LUA_VALUE_CONVERTERS.put(Character.class, characterConverter); 194 | LUA_VALUE_CONVERTERS.put(Character.TYPE, characterConverter); 195 | LuaValueConverter stringConverter = new LuaValueConverter() { 196 | @Override 197 | public String convert(LuaState luaState, int index) { 198 | return luaState.toString(index); 199 | } 200 | }; 201 | LUA_VALUE_CONVERTERS.put(String.class, stringConverter); 202 | if (!RAW_BYTE_ARRAY) { 203 | LuaValueConverter byteArrayConverter = new LuaValueConverter() { 204 | @Override 205 | public byte[] convert(LuaState luaState, int index) { 206 | return luaState.toByteArray(index); 207 | } 208 | }; 209 | LUA_VALUE_CONVERTERS.put(byte[].class, byteArrayConverter); 210 | } 211 | } 212 | 213 | /** 214 | * Java object converters. 215 | */ 216 | private static final Map, JavaObjectConverter> JAVA_OBJECT_CONVERTERS = new HashMap, JavaObjectConverter>(); 217 | static { 218 | JavaObjectConverter booleanConverter = new JavaObjectConverter() { 219 | @Override 220 | public void convert(LuaState luaState, Boolean booleanValue) { 221 | luaState.pushBoolean(booleanValue.booleanValue()); 222 | } 223 | }; 224 | JAVA_OBJECT_CONVERTERS.put(Boolean.class, booleanConverter); 225 | JAVA_OBJECT_CONVERTERS.put(Boolean.TYPE, booleanConverter); 226 | JavaObjectConverter numberConverter = new JavaObjectConverter() { 227 | @Override 228 | public void convert(LuaState luaState, Number number) { 229 | luaState.pushNumber(number.doubleValue()); 230 | } 231 | }; 232 | JAVA_OBJECT_CONVERTERS.put(Byte.class, numberConverter); 233 | JAVA_OBJECT_CONVERTERS.put(Byte.TYPE, numberConverter); 234 | JAVA_OBJECT_CONVERTERS.put(Short.class, numberConverter); 235 | JAVA_OBJECT_CONVERTERS.put(Short.TYPE, numberConverter); 236 | JAVA_OBJECT_CONVERTERS.put(Integer.class, numberConverter); 237 | JAVA_OBJECT_CONVERTERS.put(Integer.TYPE, numberConverter); 238 | JAVA_OBJECT_CONVERTERS.put(Long.class, numberConverter); 239 | JAVA_OBJECT_CONVERTERS.put(Long.TYPE, numberConverter); 240 | JAVA_OBJECT_CONVERTERS.put(Float.class, numberConverter); 241 | JAVA_OBJECT_CONVERTERS.put(Float.TYPE, numberConverter); 242 | JAVA_OBJECT_CONVERTERS.put(Double.class, numberConverter); 243 | JAVA_OBJECT_CONVERTERS.put(Double.TYPE, numberConverter); 244 | JAVA_OBJECT_CONVERTERS.put(BigInteger.class, numberConverter); 245 | JAVA_OBJECT_CONVERTERS.put(BigDecimal.class, numberConverter); 246 | JavaObjectConverter characterConverter = new JavaObjectConverter() { 247 | @Override 248 | public void convert(LuaState luaState, Character character) { 249 | luaState.pushInteger(character.charValue()); 250 | } 251 | }; 252 | JAVA_OBJECT_CONVERTERS.put(Character.class, characterConverter); 253 | JAVA_OBJECT_CONVERTERS.put(Character.TYPE, characterConverter); 254 | JavaObjectConverter stringConverter = new JavaObjectConverter() { 255 | @Override 256 | public void convert(LuaState luaState, String string) { 257 | luaState.pushString(string); 258 | } 259 | }; 260 | JAVA_OBJECT_CONVERTERS.put(String.class, stringConverter); 261 | if (!RAW_BYTE_ARRAY) { 262 | JavaObjectConverter byteArrayConverter = new JavaObjectConverter() { 263 | @Override 264 | public void convert(LuaState luaState, byte[] byteArray) { 265 | luaState.pushByteArray(byteArray); 266 | } 267 | }; 268 | JAVA_OBJECT_CONVERTERS.put(byte[].class, byteArrayConverter); 269 | } 270 | } 271 | 272 | // -- Static methods 273 | /** 274 | * Returns the instance of this class. 275 | * 276 | * @return the instance 277 | */ 278 | public static DefaultConverter getInstance() { 279 | return INSTANCE; 280 | } 281 | 282 | // -- Construction 283 | /** 284 | * Singleton. 285 | */ 286 | private DefaultConverter() { 287 | } 288 | 289 | // -- Java converter methods 290 | @Override 291 | public int getTypeDistance(LuaState luaState, int index, Class formalType) { 292 | // Handle none 293 | LuaType luaType = luaState.type(index); 294 | if (luaType == null) { 295 | return Integer.MAX_VALUE; 296 | } 297 | 298 | // Handle void 299 | if (formalType == Void.TYPE) { 300 | return Integer.MAX_VALUE; 301 | } 302 | 303 | // Handle Lua value proxy 304 | if (formalType == LuaValueProxy.class) { 305 | return 0; 306 | } 307 | 308 | // Handle Lua types 309 | switch (luaType) { 310 | case NIL: 311 | return 1; 312 | case BOOLEAN: 313 | Integer distance = BOOLEAN_DISTANCE_MAP.get(formalType); 314 | if (distance != null) { 315 | return distance.intValue(); 316 | } 317 | break; 318 | case NUMBER: 319 | distance = NUMBER_DISTANCE_MAP.get(formalType); 320 | if (distance != null) { 321 | return distance.intValue(); 322 | } 323 | break; 324 | case STRING: 325 | distance = STRING_DISTANCE_MAP.get(formalType); 326 | if (distance != null) { 327 | return distance.intValue(); 328 | } 329 | break; 330 | case TABLE: 331 | if (formalType == Map.class || formalType == List.class 332 | || formalType.isArray()) { 333 | return 1; 334 | } 335 | if (formalType == Object.class) { 336 | return 2; 337 | } 338 | break; 339 | case FUNCTION: 340 | if (luaState.isJavaFunction(index)) { 341 | distance = FUNCTION_DISTANCE_MAP.get(formalType); 342 | if (distance != null) { 343 | return distance.intValue(); 344 | } 345 | } 346 | break; 347 | case USERDATA: 348 | Object object = luaState.toJavaObjectRaw(index); 349 | if (object != null) { 350 | Class type; 351 | if (object instanceof TypedJavaObject) { 352 | TypedJavaObject typedJavaObject = (TypedJavaObject) object; 353 | if (typedJavaObject.isStrong()) { 354 | if (formalType.isAssignableFrom(typedJavaObject 355 | .getClass())) { 356 | return 1; 357 | } 358 | } 359 | type = typedJavaObject.getType(); 360 | } else { 361 | type = object.getClass(); 362 | } 363 | if (formalType.isAssignableFrom(type)) { 364 | return 1; 365 | } 366 | } 367 | break; 368 | } 369 | 370 | // Handle object 371 | if (formalType == Object.class) { 372 | return Integer.MAX_VALUE - 1; 373 | } 374 | 375 | // Unsupported conversion 376 | return Integer.MAX_VALUE; 377 | } 378 | 379 | @SuppressWarnings("unchecked") 380 | @Override 381 | public T convertLuaValue(LuaState luaState, int index, 382 | Class formalType) { 383 | // Handle none 384 | LuaType luaType = luaState.type(index); 385 | if (luaType == null) { 386 | throw new IllegalArgumentException("undefined index: " + index); 387 | } 388 | 389 | // Handle void 390 | if (formalType == Void.TYPE) { 391 | throw new ClassCastException(String.format( 392 | "cannot convert %s to %s", luaState.typeName(index), 393 | formalType.getCanonicalName())); 394 | } 395 | 396 | // Handle Lua value proxy 397 | if (formalType == LuaValueProxy.class) { 398 | return (T) luaState.getProxy(index); 399 | } 400 | 401 | // Handle Lua types 402 | switch (luaType) { 403 | case NIL: 404 | return null; 405 | case BOOLEAN: 406 | LuaValueConverter luaValueConverter; 407 | luaValueConverter = LUA_VALUE_CONVERTERS.get(formalType); 408 | if (luaValueConverter != null) { 409 | return (T) luaValueConverter.convert(luaState, index); 410 | } 411 | if (formalType == Object.class) { 412 | return (T) Boolean.valueOf(luaState.toBoolean(index)); 413 | } 414 | break; 415 | case NUMBER: 416 | luaValueConverter = LUA_VALUE_CONVERTERS.get(formalType); 417 | if (luaValueConverter != null) { 418 | return (T) luaValueConverter.convert(luaState, index); 419 | } 420 | if (formalType == Object.class) { 421 | return (T) Double.valueOf(luaState.toNumber(index)); 422 | } 423 | break; 424 | case STRING: 425 | luaValueConverter = LUA_VALUE_CONVERTERS.get(formalType); 426 | if (luaValueConverter != null) { 427 | return (T) luaValueConverter.convert(luaState, index); 428 | } 429 | if (formalType == Object.class) { 430 | return (T) luaState.toString(index); 431 | } 432 | break; 433 | case TABLE: 434 | if (formalType == Map.class || formalType == Object.class) { 435 | final LuaValueProxy luaValueProxy = luaState.getProxy(index); 436 | return (T) new AbstractTableMap() { 437 | @Override 438 | protected Object convertKey(int index) { 439 | return getLuaState().toJavaObject(index, Object.class); 440 | } 441 | 442 | @Override 443 | public LuaState getLuaState() { 444 | return luaValueProxy.getLuaState(); 445 | } 446 | 447 | @Override 448 | public void pushValue() { 449 | luaValueProxy.pushValue(); 450 | } 451 | }; 452 | } 453 | if (formalType == List.class) { 454 | final LuaValueProxy luaValueProxy = luaState.getProxy(index); 455 | return (T) new AbstractTableList() { 456 | @Override 457 | public LuaState getLuaState() { 458 | return luaValueProxy.getLuaState(); 459 | } 460 | 461 | @Override 462 | public void pushValue() { 463 | luaValueProxy.pushValue(); 464 | } 465 | }; 466 | } 467 | if (formalType.isArray()) { 468 | int length = luaState.rawLen(index); 469 | Class componentType = formalType.getComponentType(); 470 | Object array = Array.newInstance(formalType.getComponentType(), 471 | length); 472 | for (int i = 0; i < length; i++) { 473 | luaState.rawGet(index, i + 1); 474 | try { 475 | Array.set(array, i, 476 | convertLuaValue(luaState, -1, componentType)); 477 | } finally { 478 | luaState.pop(1); 479 | } 480 | } 481 | return (T) array; 482 | } 483 | break; 484 | case FUNCTION: 485 | if (luaState.isJavaFunction(index)) { 486 | if (formalType == JavaFunction.class 487 | || formalType == Object.class) { 488 | return (T) luaState.toJavaFunction(index); 489 | } 490 | } 491 | break; 492 | case USERDATA: 493 | Object object = luaState.toJavaObjectRaw(index); 494 | if (object != null) { 495 | if (object instanceof TypedJavaObject) { 496 | TypedJavaObject typedJavaObject = (TypedJavaObject) object; 497 | if (typedJavaObject.isStrong()) { 498 | if (formalType.isAssignableFrom(typedJavaObject 499 | .getClass())) { 500 | return (T) typedJavaObject; 501 | } 502 | } 503 | return (T) ((TypedJavaObject) object).getObject(); 504 | } else { 505 | return (T) object; 506 | } 507 | } 508 | break; 509 | } 510 | 511 | // Handle object 512 | if (formalType == Object.class) { 513 | return (T) luaState.getProxy(index); 514 | } 515 | 516 | // Unsupported conversion 517 | throw new ClassCastException(String.format("cannot convert %s to %s", 518 | luaState.typeName(index), formalType.getCanonicalName())); 519 | } 520 | 521 | @SuppressWarnings("unchecked") 522 | @Override 523 | public void convertJavaObject(LuaState luaState, Object object) { 524 | // Handle null 525 | if (object == null) { 526 | luaState.pushNil(); 527 | return; 528 | } 529 | 530 | // Handle known Java types 531 | JavaObjectConverter javaObjectConverter = (JavaObjectConverter) JAVA_OBJECT_CONVERTERS 532 | .get(object.getClass()); 533 | if (javaObjectConverter != null) { 534 | javaObjectConverter.convert(luaState, object); 535 | return; 536 | } 537 | if (object instanceof JavaFunction) { 538 | luaState.pushJavaFunction((JavaFunction) object); 539 | return; 540 | } 541 | if (object instanceof LuaValueProxy) { 542 | LuaValueProxy luaValueProxy = (LuaValueProxy) object; 543 | if (!luaValueProxy.getLuaState().equals(luaState)) { 544 | throw new IllegalArgumentException( 545 | "Lua value proxy is from a different Lua state"); 546 | } 547 | luaValueProxy.pushValue(); 548 | return; 549 | } 550 | 551 | // Push as is 552 | luaState.pushJavaObjectRaw(object); 553 | } 554 | 555 | // -- Nested types 556 | /** 557 | * Converts Lua values. 558 | */ 559 | private interface LuaValueConverter { 560 | /** 561 | * Converts a Lua value to a Java object. 562 | */ 563 | public T convert(LuaState luaState, int index); 564 | } 565 | 566 | /** 567 | * Converts Java object. 568 | */ 569 | private interface JavaObjectConverter { 570 | /** 571 | * Converts a Java object to a Lua value. 572 | */ 573 | public void convert(LuaState luaState, T object); 574 | } 575 | } 576 | -------------------------------------------------------------------------------- /jnlua/src/main/java/com/naef/jnlua/JavaModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua; 7 | 8 | import java.lang.reflect.Array; 9 | import java.util.HashMap; 10 | import java.util.Iterator; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | import com.naef.jnlua.JavaReflector.Metamethod; 15 | 16 | /** 17 | * Provides the Java module for Lua. The Java module contains Java functions for 18 | * using Java in Lua. 19 | */ 20 | public class JavaModule { 21 | // -- Static 22 | private static final JavaModule INSTANCE = new JavaModule(); 23 | private static final Map> PRIMITIVE_TYPES = new HashMap>(); 24 | static { 25 | PRIMITIVE_TYPES.put("boolean", Boolean.TYPE); 26 | PRIMITIVE_TYPES.put("byte", Byte.TYPE); 27 | PRIMITIVE_TYPES.put("char", Character.TYPE); 28 | PRIMITIVE_TYPES.put("double", Double.TYPE); 29 | PRIMITIVE_TYPES.put("float", Float.TYPE); 30 | PRIMITIVE_TYPES.put("int", Integer.TYPE); 31 | PRIMITIVE_TYPES.put("long", Long.TYPE); 32 | PRIMITIVE_TYPES.put("short", Short.TYPE); 33 | PRIMITIVE_TYPES.put("void", Void.TYPE); 34 | } 35 | 36 | // -- State 37 | private final NamedJavaFunction[] functions = { new Require(), new New(), 38 | new InstanceOf(), new Cast(), new Proxy(), new Pairs(), 39 | new IPairs(), new ToTable(), new Elements(), new Fields(), 40 | new Methods(), new Properties() }; 41 | 42 | // -- Static methods 43 | /** 44 | * Returns the instance of the Java module. 45 | * 46 | * @return the instance 47 | */ 48 | public static JavaModule getInstance() { 49 | return INSTANCE; 50 | } 51 | 52 | // -- Construction 53 | /** 54 | * Singleton. 55 | */ 56 | private JavaModule() { 57 | } 58 | 59 | // -- Operations 60 | /** 61 | * Opens this module in a Lua state. The method is invoked by 62 | * {@link LuaState#openLibs()} or by 63 | * {@link LuaState#openLib(com.naef.jnlua.LuaState.Library)} if 64 | * {@link LuaState.Library#JAVA} is passed. The module is pushed onto the 65 | * stack. 66 | * 67 | * @param luaState 68 | * the Lua state to open in 69 | */ 70 | public void open(LuaState luaState) { 71 | luaState.register("java", functions, true); 72 | } 73 | 74 | /** 75 | * Returns a table-like Lua value for the specified map. The returned value 76 | * corresponds to the return value of the totable() function 77 | * provided by this Java module. 78 | * 79 | * @param map 80 | * the map 81 | * @return the table-like Lua value 82 | */ 83 | public TypedJavaObject toTable(Map map) { 84 | return ToTable.toTable(map); 85 | } 86 | 87 | /** 88 | * Returns a table-like Lua value for the specified list. The returned value 89 | * corresponds to the return value of the totable() function 90 | * provided by this Java module. 91 | * 92 | * @param list 93 | * the list 94 | * @return the table-like Lua value 95 | */ 96 | public TypedJavaObject toTable(List list) { 97 | return ToTable.toTable(list); 98 | } 99 | 100 | // -- Private methods 101 | /** 102 | * Loads a type. The named type is a primitive type or a class. 103 | */ 104 | private static Class loadType(LuaState luaState, String typeName) { 105 | Class clazz; 106 | if ((clazz = PRIMITIVE_TYPES.get(typeName)) != null) { 107 | return clazz; 108 | } 109 | try { 110 | clazz = luaState.getClassLoader().loadClass(typeName); 111 | return clazz; 112 | } catch (ClassNotFoundException e) { 113 | throw new RuntimeException(e); 114 | } 115 | } 116 | 117 | // -- Nested types 118 | /** 119 | * Imports a Java class into the Lua namespace. Returns the class and a 120 | * status code. The status code indicates if the class was stored in the Lua 121 | * namespace. Primitive types and classes without a package are not stored 122 | * in the Lua namespace. 123 | */ 124 | private static class Require implements NamedJavaFunction { 125 | // -- JavaFunction methods 126 | @Override 127 | public int invoke(LuaState luaState) { 128 | // Check arguments 129 | String className = luaState.checkString(1); 130 | boolean doImport = luaState.toBoolean(2); 131 | 132 | // Load 133 | Class clazz = loadType(luaState, className); 134 | luaState.pushJavaObject(clazz); 135 | 136 | // Import 137 | if (doImport) { 138 | luaState.rawGet(LuaState.REGISTRYINDEX, LuaState.RIDX_GLOBALS); 139 | String name = clazz.getName(); 140 | int index = name.indexOf('.'); 141 | while (index >= 0) { 142 | String part = name.substring(0, index); 143 | luaState.getField(-1, part); 144 | if (!luaState.isTable(-1)) { 145 | luaState.pop(1); 146 | luaState.newTable(); 147 | luaState.pushValue(-1); 148 | luaState.setField(-3, part); 149 | } 150 | luaState.remove(-2); 151 | name = name.substring(index + 1); 152 | index = name.indexOf('.'); 153 | } 154 | luaState.pushValue(-2); 155 | luaState.setField(-2, name); 156 | luaState.pop(1); 157 | } 158 | luaState.pushBoolean(doImport); 159 | 160 | // Return 161 | return 2; 162 | } 163 | 164 | @Override 165 | public String getName() { 166 | return "require"; 167 | } 168 | } 169 | 170 | /** 171 | * Creates and returns a new Java object or array thereof. The first 172 | * argument designates the type to instantiate, either as a class or a 173 | * string. The remaining arguments are the dimensions. 174 | */ 175 | private static class New implements NamedJavaFunction { 176 | // -- JavaFunction methods 177 | @Override 178 | public int invoke(LuaState luaState) { 179 | // Find class 180 | Class clazz; 181 | if (luaState.isJavaObject(1, Class.class)) { 182 | clazz = luaState.checkJavaObject(1, Class.class); 183 | } else { 184 | String className = luaState.checkString(1); 185 | clazz = loadType(luaState, className); 186 | } 187 | 188 | // Instantiate 189 | Object object; 190 | int dimensionCount = luaState.getTop() - 1; 191 | switch (dimensionCount) { 192 | case 0: 193 | try { 194 | object = clazz.newInstance(); 195 | } catch (InstantiationException e) { 196 | throw new RuntimeException(e); 197 | } catch (IllegalAccessException e) { 198 | throw new RuntimeException(e); 199 | } 200 | break; 201 | case 1: 202 | object = Array.newInstance(clazz, luaState.checkInteger(2)); 203 | break; 204 | default: 205 | int[] dimensions = new int[dimensionCount]; 206 | for (int i = 0; i < dimensionCount; i++) { 207 | dimensions[i] = luaState.checkInteger(i + 2); 208 | } 209 | object = Array.newInstance(clazz, dimensions); 210 | } 211 | 212 | // Return 213 | luaState.pushJavaObject(object); 214 | return 1; 215 | } 216 | 217 | @Override 218 | public String getName() { 219 | return "new"; 220 | } 221 | } 222 | 223 | /** 224 | * Returns whether an object is an instance of a type. The object is given 225 | * as the first argument. the type is given as the second argument, either 226 | * as a class or as a type name. 227 | */ 228 | private static class InstanceOf implements NamedJavaFunction { 229 | // -- JavaFunction methods 230 | @Override 231 | public int invoke(LuaState luaState) { 232 | // Get the object 233 | Object object = luaState.checkJavaObject(1, Object.class); 234 | 235 | // Find class 236 | Class clazz; 237 | if (luaState.isJavaObject(2, Class.class)) { 238 | clazz = luaState.checkJavaObject(2, Class.class); 239 | } else { 240 | String className = luaState.checkString(2); 241 | clazz = loadType(luaState, className); 242 | } 243 | 244 | // Type check 245 | luaState.pushBoolean(clazz.isInstance(object)); 246 | return 1; 247 | } 248 | 249 | @Override 250 | public String getName() { 251 | return "instanceof"; 252 | } 253 | } 254 | 255 | /** 256 | * Creates a typed Java object. 257 | */ 258 | private static class Cast implements NamedJavaFunction { 259 | // -- NamedJavaFunction methods 260 | @Override 261 | public int invoke(LuaState luaState) { 262 | // Find class 263 | final Class clazz; 264 | if (luaState.isJavaObject(2, Class.class)) { 265 | clazz = luaState.checkJavaObject(2, Class.class); 266 | } else { 267 | String className = luaState.checkString(2); 268 | clazz = loadType(luaState, className); 269 | } 270 | 271 | // Get the object 272 | final Object object = luaState.checkJavaObject(1, clazz); 273 | 274 | // Push result 275 | luaState.pushJavaObject(new TypedJavaObject() { 276 | @Override 277 | public Object getObject() { 278 | return object; 279 | } 280 | 281 | @Override 282 | public Class getType() { 283 | return clazz; 284 | } 285 | 286 | @Override 287 | public boolean isStrong() { 288 | return false; 289 | } 290 | }); 291 | return 1; 292 | } 293 | 294 | @Override 295 | public String getName() { 296 | return "cast"; 297 | } 298 | } 299 | 300 | /** 301 | * Creates a dynamic proxy object the implements a set of Java interfaces in 302 | * Lua. 303 | */ 304 | private static class Proxy implements NamedJavaFunction { 305 | // -- JavaFunction methods 306 | @Override 307 | public int invoke(LuaState luaState) { 308 | // Check table 309 | luaState.checkType(1, LuaType.TABLE); 310 | 311 | // Get interfaces 312 | int interfaceCount = luaState.getTop() - 1; 313 | luaState.checkArg(2, interfaceCount > 0, "no interface specified"); 314 | Class[] interfaces = new Class[interfaceCount]; 315 | for (int i = 0; i < interfaceCount; i++) { 316 | if (luaState.isJavaObject(i + 2, Class.class)) { 317 | interfaces[i] = luaState 318 | .checkJavaObject(i + 2, Class.class); 319 | } else { 320 | String interfaceName = luaState.checkString(i + 2); 321 | interfaces[i] = loadType(luaState, interfaceName); 322 | } 323 | } 324 | 325 | // Create proxy 326 | luaState.pushJavaObjectRaw(luaState.getProxy(1, interfaces)); 327 | return 1; 328 | } 329 | 330 | @Override 331 | public String getName() { 332 | return "proxy"; 333 | } 334 | } 335 | 336 | /** 337 | * Provides the pairs iterator from the Java reflector. 338 | */ 339 | private static class Pairs implements NamedJavaFunction { 340 | // -- NamedJavaFunction methods 341 | @Override 342 | public int invoke(LuaState luaState) { 343 | luaState.checkArg( 344 | 1, 345 | luaState.isJavaObjectRaw(1), 346 | String.format("Java object expected, got %s", 347 | luaState.typeName(1))); 348 | JavaFunction metamethod = luaState.getMetamethod( 349 | luaState.toJavaObjectRaw(1), Metamethod.PAIRS); 350 | return metamethod.invoke(luaState); 351 | } 352 | 353 | @Override 354 | public String getName() { 355 | return "pairs"; 356 | } 357 | } 358 | 359 | /** 360 | * Provides the ipairs iterator from the Java reflector. 361 | */ 362 | private static class IPairs implements NamedJavaFunction { 363 | // -- NamedJavaFunction methods 364 | @Override 365 | public int invoke(LuaState luaState) { 366 | luaState.checkArg( 367 | 1, 368 | luaState.isJavaObjectRaw(1), 369 | String.format("Java object expected, got %s", 370 | luaState.typeName(1))); 371 | JavaFunction metamethod = luaState.getMetamethod( 372 | luaState.toJavaObjectRaw(1), Metamethod.IPAIRS); 373 | return metamethod.invoke(luaState); 374 | } 375 | 376 | @Override 377 | public String getName() { 378 | return "ipairs"; 379 | } 380 | } 381 | 382 | /** 383 | * Provides a wrapper object for table-like map and list access from Lua. 384 | */ 385 | private static class ToTable implements NamedJavaFunction { 386 | // -- Static methods 387 | /** 388 | * Returns a table-like Lua value for the specified map. 389 | */ 390 | @SuppressWarnings("unchecked") 391 | public static TypedJavaObject toTable(Map map) { 392 | return new LuaMap((Map) map); 393 | } 394 | 395 | /** 396 | * Returns a table-list Lua value for the specified list. 397 | */ 398 | @SuppressWarnings("unchecked") 399 | public static TypedJavaObject toTable(List list) { 400 | return new LuaList((List) list); 401 | } 402 | 403 | // -- JavaFunction methods 404 | @SuppressWarnings("unchecked") 405 | @Override 406 | public int invoke(LuaState luaState) { 407 | if (luaState.isJavaObject(1, Map.class)) { 408 | Map map = luaState.toJavaObject(1, Map.class); 409 | luaState.pushJavaObject(new LuaMap(map)); 410 | } else if (luaState.isJavaObject(1, List.class)) { 411 | List list = luaState.toJavaObject(1, List.class); 412 | luaState.pushJavaObject(new LuaList(list)); 413 | } else { 414 | luaState.checkArg( 415 | 1, 416 | false, 417 | String.format("expected map or list, got %s", 418 | luaState.typeName(1))); 419 | } 420 | return 1; 421 | } 422 | 423 | @Override 424 | public String getName() { 425 | return "totable"; 426 | } 427 | 428 | // -- Member types 429 | /** 430 | * Provides table-like access in Lua to a Java map. 431 | */ 432 | private static class LuaMap implements JavaReflector, TypedJavaObject { 433 | // -- Static 434 | private static final JavaFunction INDEX = new Index(); 435 | private static final JavaFunction NEW_INDEX = new NewIndex(); 436 | 437 | // -- State 438 | private Map map; 439 | 440 | // -- Construction 441 | /** 442 | * Creates a new instance. 443 | */ 444 | public LuaMap(Map map) { 445 | this.map = map; 446 | } 447 | 448 | // -- Properties 449 | /** 450 | * Returns the map. 451 | */ 452 | public Map getMap() { 453 | return map; 454 | } 455 | 456 | // -- JavaReflector methods 457 | @Override 458 | public JavaFunction getMetamethod(Metamethod metamethod) { 459 | switch (metamethod) { 460 | case INDEX: 461 | return INDEX; 462 | case NEWINDEX: 463 | return NEW_INDEX; 464 | default: 465 | return null; 466 | } 467 | } 468 | 469 | // -- TypedJavaObject methods 470 | @Override 471 | public Object getObject() { 472 | return map; 473 | } 474 | 475 | @Override 476 | public Class getType() { 477 | return Map.class; 478 | } 479 | 480 | @Override 481 | public boolean isStrong() { 482 | return true; 483 | } 484 | 485 | // -- Member types 486 | /** 487 | * __index implementation for maps. 488 | */ 489 | private static class Index implements JavaFunction { 490 | // -- JavaFunction methods 491 | @Override 492 | public int invoke(LuaState luaState) { 493 | LuaMap luaMap = (LuaMap) luaState.toJavaObjectRaw(1); 494 | Object key = luaState.toJavaObject(2, Object.class); 495 | if (key == null) { 496 | throw new LuaRuntimeException(String.format( 497 | "attempt to read map with %s accessor", 498 | luaState.typeName(2))); 499 | } 500 | luaState.pushJavaObject(luaMap.getMap().get(key)); 501 | return 1; 502 | } 503 | } 504 | 505 | /** 506 | * __newindex implementation for maps. 507 | */ 508 | private static class NewIndex implements JavaFunction { 509 | // -- JavaFunction methods 510 | @Override 511 | public int invoke(LuaState luaState) { 512 | LuaMap luaMap = (LuaMap) luaState.toJavaObjectRaw(1); 513 | Object key = luaState.toJavaObject(2, Object.class); 514 | if (key == null) { 515 | throw new LuaRuntimeException(String.format( 516 | "attempt to write map with %s accessor", 517 | luaState.typeName(2))); 518 | } 519 | Object value = luaState.toJavaObject(3, Object.class); 520 | if (value != null) { 521 | luaMap.getMap().put(key, value); 522 | } else { 523 | luaMap.getMap().remove(key); 524 | } 525 | return 0; 526 | } 527 | } 528 | } 529 | 530 | /** 531 | * Provides table-like access in Lua to a Java list. 532 | */ 533 | private static class LuaList implements JavaReflector, TypedJavaObject { 534 | // -- Static 535 | private static final JavaFunction INDEX = new Index(); 536 | private static final JavaFunction NEW_INDEX = new NewIndex(); 537 | private static final JavaFunction LENGTH = new Length(); 538 | 539 | // -- State 540 | private List list; 541 | 542 | // -- Construction 543 | /** 544 | * Creates a new instance. 545 | */ 546 | public LuaList(List list) { 547 | this.list = list; 548 | } 549 | 550 | // -- Properties 551 | /** 552 | * Returns the map. 553 | */ 554 | public List getList() { 555 | return list; 556 | } 557 | 558 | // -- JavaReflector methods 559 | @Override 560 | public JavaFunction getMetamethod(Metamethod metamethod) { 561 | switch (metamethod) { 562 | case INDEX: 563 | return INDEX; 564 | case NEWINDEX: 565 | return NEW_INDEX; 566 | case LEN: 567 | return LENGTH; 568 | default: 569 | return null; 570 | } 571 | } 572 | 573 | // -- TypedJavaObject methods 574 | @Override 575 | public Object getObject() { 576 | return list; 577 | } 578 | 579 | @Override 580 | public Class getType() { 581 | return List.class; 582 | } 583 | 584 | @Override 585 | public boolean isStrong() { 586 | return true; 587 | } 588 | 589 | // -- Member types 590 | /** 591 | * __index implementation for lists. 592 | */ 593 | private static class Index implements JavaFunction { 594 | // -- JavaFunction methods 595 | @Override 596 | public int invoke(LuaState luaState) { 597 | LuaList luaList = (LuaList) luaState.toJavaObjectRaw(1); 598 | if (!luaState.isNumber(2)) { 599 | throw new LuaRuntimeException(String.format( 600 | "attempt to read list with %s accessor", 601 | luaState.typeName(2))); 602 | } 603 | int index = luaState.toInteger(2); 604 | luaState.pushJavaObject(luaList.getList().get(index - 1)); 605 | return 1; 606 | } 607 | } 608 | 609 | /** 610 | * __newindex implementation for lists. 611 | */ 612 | private static class NewIndex implements JavaFunction { 613 | // -- JavaFunction methods 614 | @Override 615 | public int invoke(LuaState luaState) { 616 | LuaList luaList = (LuaList) luaState.toJavaObjectRaw(1); 617 | if (!luaState.isNumber(2)) { 618 | throw new LuaRuntimeException(String.format( 619 | "attempt to write list with %s accessor", 620 | luaState.typeName(2))); 621 | } 622 | int index = luaState.toInteger(2); 623 | Object value = luaState.toJavaObject(3, Object.class); 624 | if (value != null) { 625 | int size = luaList.getList().size(); 626 | if (index - 1 != size) { 627 | luaList.getList().set(index - 1, value); 628 | } else { 629 | luaList.getList().add(value); 630 | } 631 | } else { 632 | luaList.getList().remove(index - 1); 633 | } 634 | return 0; 635 | } 636 | } 637 | 638 | /** 639 | * __len implementation for lists. 640 | */ 641 | private static class Length implements JavaFunction { 642 | // -- JavaFunction methods 643 | @Override 644 | public int invoke(LuaState luaState) { 645 | LuaList luaList = (LuaList) luaState.toJavaObjectRaw(1); 646 | luaState.pushInteger(luaList.getList().size()); 647 | return 1; 648 | } 649 | } 650 | } 651 | } 652 | 653 | /** 654 | * Provides an iterator for Iterable objects. 655 | */ 656 | private static class Elements implements NamedJavaFunction { 657 | // -- NamedJavaFunction methods 658 | @Override 659 | public int invoke(LuaState luaState) { 660 | Iterable iterable = luaState.checkJavaObject(1, Iterable.class); 661 | luaState.pushJavaObject(new ElementIterator(iterable.iterator())); 662 | luaState.pushJavaObject(iterable); 663 | luaState.pushNil(); 664 | return 3; 665 | } 666 | 667 | @Override 668 | public String getName() { 669 | return "elements"; 670 | } 671 | 672 | // -- Member types 673 | private static class ElementIterator implements JavaFunction { 674 | // -- State 675 | private Iterator iterator; 676 | 677 | // -- Construction 678 | /** 679 | * Creates a new instance. 680 | */ 681 | public ElementIterator(Iterator iterator) { 682 | this.iterator = iterator; 683 | } 684 | 685 | // -- JavaFunction methods 686 | @Override 687 | public int invoke(LuaState luaState) { 688 | if (iterator.hasNext()) { 689 | luaState.pushJavaObject(iterator.next()); 690 | } else { 691 | luaState.pushNil(); 692 | } 693 | return 1; 694 | } 695 | } 696 | } 697 | 698 | /** 699 | * Provides an iterator for Java object fields. 700 | */ 701 | private static class Fields implements NamedJavaFunction { 702 | // -- NamedJavaFunction methods 703 | @Override 704 | public int invoke(LuaState luaState) { 705 | luaState.checkArg( 706 | 1, 707 | luaState.isJavaObjectRaw(1), 708 | String.format("expected Java object, got %s", 709 | luaState.typeName(1))); 710 | JavaFunction metamethod = luaState.getMetamethod( 711 | luaState.toJavaObjectRaw(1), Metamethod.JAVAFIELDS); 712 | return metamethod.invoke(luaState); 713 | } 714 | 715 | @Override 716 | public String getName() { 717 | return "fields"; 718 | } 719 | } 720 | 721 | /** 722 | * Provides an iterator for Java methods. 723 | */ 724 | private static class Methods implements NamedJavaFunction { 725 | // -- NamedJavaFunction methods 726 | @Override 727 | public int invoke(LuaState luaState) { 728 | luaState.checkArg( 729 | 1, 730 | luaState.isJavaObjectRaw(1), 731 | String.format("expected Java object, got %s", 732 | luaState.typeName(1))); 733 | JavaFunction metamethod = luaState.getMetamethod( 734 | luaState.toJavaObjectRaw(1), Metamethod.JAVAMETHODS); 735 | return metamethod.invoke(luaState); 736 | } 737 | 738 | @Override 739 | public String getName() { 740 | return "methods"; 741 | } 742 | } 743 | 744 | /** 745 | * Provides an iterator for Java object properties. 746 | */ 747 | private static class Properties implements NamedJavaFunction { 748 | // -- NamedJavaFunction methods 749 | @Override 750 | public int invoke(LuaState luaState) { 751 | luaState.checkArg( 752 | 1, 753 | luaState.isJavaObjectRaw(1), 754 | String.format("expected Java object, got %s", 755 | luaState.typeName(1))); 756 | JavaFunction metamethod = luaState.getMetamethod( 757 | luaState.toJavaObjectRaw(1), Metamethod.JAVAPROPERTIES); 758 | return metamethod.invoke(luaState); 759 | } 760 | 761 | @Override 762 | public String getName() { 763 | return "properties"; 764 | } 765 | } 766 | } 767 | -------------------------------------------------------------------------------- /jnlua/src/test/java/com/naef/jnlua/test/LuaStateErrorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * See LICENSE.txt for license terms. 4 | */ 5 | 6 | package com.naef.jnlua.test; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | import java.io.ByteArrayInputStream; 11 | import java.io.ByteArrayOutputStream; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.io.OutputStream; 15 | 16 | import org.junit.Test; 17 | 18 | import com.naef.jnlua.JavaFunction; 19 | import com.naef.jnlua.LuaRuntimeException; 20 | import com.naef.jnlua.LuaState; 21 | import com.naef.jnlua.LuaState.ArithOperator; 22 | import com.naef.jnlua.LuaState.GcAction; 23 | import com.naef.jnlua.LuaState.Library; 24 | import com.naef.jnlua.LuaState.RelOperator; 25 | import com.naef.jnlua.LuaValueProxy; 26 | import com.naef.jnlua.NamedJavaFunction; 27 | 28 | /** 29 | * Throws illegal arguments at the Lua state for error testing. 30 | */ 31 | public class LuaStateErrorTest extends AbstractLuaTest { 32 | // -- Properties tests 33 | /** 34 | * setClassLodaer(ClassLoader) with null class loader. 35 | */ 36 | @Test(expected = NullPointerException.class) 37 | public void setNullClassLoader() { 38 | luaState.setClassLoader(null); 39 | } 40 | 41 | /** 42 | * setJavaReflector(JavaReflector) with null Java reflector. 43 | */ 44 | @Test(expected = NullPointerException.class) 45 | public void setNullJavaReflector() { 46 | luaState.setJavaReflector(null); 47 | } 48 | 49 | /** 50 | * getMetamethod(Object, Metamethod) with null metamethod. 51 | */ 52 | @Test(expected = NullPointerException.class) 53 | public void testNullGetMetamethod() { 54 | luaState.getMetamethod(null, null); 55 | } 56 | 57 | /** 58 | * setConverter(Converter) with null converter. 59 | */ 60 | @Test(expected = NullPointerException.class) 61 | public void setNullConverter() { 62 | luaState.setConverter(null); 63 | } 64 | 65 | // -- Life cycle tests 66 | /** 67 | * Tests closing the Lua state while running. 68 | */ 69 | @Test(expected = LuaRuntimeException.class) 70 | public void testIllegalClose() { 71 | luaState.pushJavaFunction(new JavaFunction() { 72 | @Override 73 | public int invoke(LuaState luaState) { 74 | luaState.close(); 75 | return 0; 76 | } 77 | }); 78 | luaState.call(0, 0); 79 | } 80 | 81 | /** 82 | * Tests invoking a method after the Lua state has been closed. 83 | */ 84 | @Test(expected = IllegalStateException.class) 85 | public void testPostClose() { 86 | luaState.close(); 87 | luaState.pushInteger(1); 88 | } 89 | 90 | /** 91 | * gc(GcAction, int) null action. 92 | */ 93 | @Test(expected = NullPointerException.class) 94 | public void testNullGc() { 95 | luaState.gc(null, 0); 96 | } 97 | 98 | // -- Registration tests 99 | /** 100 | * openLib(Library) with null library. 101 | */ 102 | @Test(expected = NullPointerException.class) 103 | public void testNullOpenLib() { 104 | luaState.openLib(null); 105 | } 106 | 107 | /** 108 | * register(JavaFunction[]) with null function. 109 | */ 110 | @Test(expected = NullPointerException.class) 111 | public void testNullFunctionRegister() { 112 | luaState.register(null); 113 | } 114 | 115 | /** 116 | * register(String, JavaFunction[]) with null string. 117 | */ 118 | @Test(expected = NullPointerException.class) 119 | public void testNullNameRegister() { 120 | luaState.register(null, new NamedJavaFunction[0], true); 121 | } 122 | 123 | /** 124 | * register(String, JavaFunction[]) with null functions. 125 | */ 126 | @Test(expected = NullPointerException.class) 127 | public void testNullFunctionsRegister() { 128 | luaState.register("", null, true); 129 | } 130 | 131 | // -- Load and dump tests 132 | /** 133 | * load(InputStream, String) with null input stream. 134 | */ 135 | @Test(expected = NullPointerException.class) 136 | public void testNullStreamLoad() throws Exception { 137 | luaState.load((InputStream) null, "=testNullStreamLoad", "bt"); 138 | } 139 | 140 | /** 141 | * load(InputStream, String) with null string. 142 | */ 143 | @Test(expected = NullPointerException.class) 144 | public void testNullChunkLoad1() throws Exception { 145 | luaState.load(new ByteArrayInputStream(new byte[0]), null, "bt"); 146 | } 147 | 148 | /** 149 | * load(String, String) with null string 1. 150 | */ 151 | @Test(expected = NullPointerException.class) 152 | public void testNullStringLoad() throws Exception { 153 | luaState.load((String) null, "=testNullStringLoad"); 154 | } 155 | 156 | /** 157 | * load(String, String) with null string 2. 158 | */ 159 | @Test(expected = NullPointerException.class) 160 | public void testNullChunkLoad2() throws Exception { 161 | luaState.load("", null); 162 | } 163 | 164 | /** 165 | * load(InputStream, String) with input stream throwing IO exception. 166 | */ 167 | @Test(expected = IOException.class) 168 | public void testIoExceptionLoad() throws Exception { 169 | luaState.load(new InputStream() { 170 | @Override 171 | public int read() throws IOException { 172 | throw new IOException(); 173 | } 174 | }, "=testIoExceptionLoad", "bt"); 175 | } 176 | 177 | /** 178 | * dump(OutputStream) with null output stream. 179 | */ 180 | @Test(expected = NullPointerException.class) 181 | public void testNullDump() throws Exception { 182 | luaState.load("return 0", "=testNullDump"); 183 | luaState.dump(null); 184 | } 185 | 186 | /** 187 | * dump(OutputStream) with an output stream throwing a IO exception. 188 | */ 189 | @Test(expected = IOException.class) 190 | public void testIoExceptionDump() throws Exception { 191 | luaState.load("return 0", "=testIoExceptionDump"); 192 | luaState.dump(new OutputStream() { 193 | @Override 194 | public void write(int b) throws IOException { 195 | throw new IOException(); 196 | } 197 | }); 198 | } 199 | 200 | /** 201 | * dump(OutputStream) with insufficient arguments. 202 | */ 203 | @Test(expected = IllegalStateException.class) 204 | public void testUnderflowDump() throws Exception { 205 | luaState.dump(new ByteArrayOutputStream()); 206 | } 207 | 208 | // -- Call tests 209 | /** 210 | * Call(int, int) with insufficient arguments. 211 | */ 212 | @Test(expected = IllegalStateException.class) 213 | public void testUnderflowCall() { 214 | luaState.openLibs(); 215 | luaState.getGlobal("print"); 216 | luaState.call(1, 1); 217 | } 218 | 219 | /** 220 | * Call(int, int) with an extremely high number of returns. 221 | */ 222 | @Test(expected = IllegalStateException.class) 223 | public void testOverflowCall() { 224 | luaState.openLibs(); 225 | luaState.getGlobal("print"); 226 | luaState.pushString(""); 227 | luaState.call(1, Integer.MAX_VALUE); 228 | } 229 | 230 | /** 231 | * Call(int, int) with an illegal number of arguments. 232 | */ 233 | @Test(expected = IllegalArgumentException.class) 234 | public void testIllegalCall1() { 235 | luaState.openLibs(); 236 | luaState.getGlobal("print"); 237 | luaState.call(-1, 1); 238 | } 239 | 240 | /** 241 | * Call(int, int) with an illegal number of returns. 242 | */ 243 | @Test(expected = IllegalArgumentException.class) 244 | public void testIllegalCall2() { 245 | luaState.openLibs(); 246 | luaState.getGlobal("print"); 247 | luaState.pushString(""); 248 | luaState.call(1, -2); 249 | assertEquals(0, luaState.getTop()); 250 | } 251 | 252 | // -- Global tests 253 | /** 254 | * getGlobal(String) with null. 255 | */ 256 | @Test(expected = NullPointerException.class) 257 | public void testNullGetGlobal() { 258 | luaState.getGlobal(null); 259 | } 260 | 261 | /** 262 | * setGlobal(String) with null. 263 | */ 264 | @Test(expected = NullPointerException.class) 265 | public void testNullSetGlobal() { 266 | luaState.pushNumber(0.0); 267 | luaState.setGlobal(null); 268 | } 269 | 270 | /** 271 | * setGlobal(String) with insufficient arguments. 272 | */ 273 | @Test(expected = IllegalStateException.class) 274 | public void testUnderflowSetGlobal() { 275 | luaState.setGlobal("global"); 276 | } 277 | 278 | /** 279 | * setGlobal(String) with insufficient arguments. 280 | */ 281 | @Test(expected = IllegalStateException.class) 282 | public void testIllegalSetGlobal() { 283 | luaState.setGlobal("illegal"); 284 | } 285 | 286 | // -- Stack push tests 287 | /** 288 | * pushJavaFunction(JavaFunction) with null argument. 289 | */ 290 | @Test(expected = NullPointerException.class) 291 | public void testNullPushJavaFunction() { 292 | luaState.pushJavaFunction(null); 293 | } 294 | 295 | /** 296 | * pushJavaObjectRaw(Object) with null argument. 297 | */ 298 | @Test(expected = NullPointerException.class) 299 | public void testNullPushJavaObjectRaw() { 300 | luaState.pushJavaObjectRaw(null); 301 | } 302 | 303 | /** 304 | * pushString(String) with null argument. 305 | */ 306 | @Test(expected = NullPointerException.class) 307 | public void testNullPushString() { 308 | luaState.pushString(null); 309 | } 310 | 311 | /** 312 | * pushNumber(Double) until stack overflow. 313 | */ 314 | @Test(expected = IllegalStateException.class) 315 | public void testStackOverflow() { 316 | for (int i = 0; i < Integer.MAX_VALUE; i++) { 317 | luaState.pushNumber(0.0); 318 | } 319 | } 320 | 321 | // -- Stack query tests 322 | /** 323 | * compare(int, int, RelOperator) with number and nil for less than. 324 | */ 325 | @Test(expected = LuaRuntimeException.class) 326 | public void testIllegalCompareLt() { 327 | luaState.pushNumber(1); 328 | luaState.pushNil(); 329 | luaState.compare(1, 2, RelOperator.LT); 330 | } 331 | 332 | /** 333 | * compare(int, int, RelOperator) with number and nil for less or equal. 334 | */ 335 | @Test(expected = LuaRuntimeException.class) 336 | public void testIllegalCompareLe() { 337 | luaState.pushNumber(1); 338 | luaState.pushNil(); 339 | luaState.compare(1, 2, RelOperator.LE); 340 | } 341 | 342 | /** 343 | * rawLen(int) with illegal index. 344 | */ 345 | @Test(expected = IllegalArgumentException.class) 346 | public void testIllegalRawLen() { 347 | luaState.rawLen(getIllegalIndex()); 348 | } 349 | 350 | /** 351 | * toInteger(int) with illegal index. 352 | */ 353 | @Test(expected = IllegalArgumentException.class) 354 | public void testIllegalToInteger() { 355 | luaState.toInteger(getIllegalIndex()); 356 | } 357 | 358 | /** 359 | * toJavaFunction(int) with illegal index. 360 | */ 361 | @Test(expected = IllegalArgumentException.class) 362 | public void testIllegalToIJavaFunction() { 363 | luaState.toJavaFunction(getIllegalIndex()); 364 | } 365 | 366 | /** 367 | * toJavaObject(int) with illegal index and LuaValueProxy type. 368 | */ 369 | @Test(expected = IllegalArgumentException.class) 370 | public void testIllegalToIJavaObject() { 371 | luaState.toJavaObject(getIllegalIndex(), LuaValueProxy.class); 372 | } 373 | 374 | /** 375 | * toJavaObjectRaw(int) with illegal index. 376 | */ 377 | @Test(expected = IllegalArgumentException.class) 378 | public void testIllegalToIJavaObjectRaw() { 379 | luaState.toJavaObjectRaw(getIllegalIndex()); 380 | } 381 | 382 | /** 383 | * toNumber(int) with illegal index. 384 | */ 385 | @Test(expected = IllegalArgumentException.class) 386 | public void testIllegalToNumber() { 387 | luaState.toNumber(getIllegalIndex()); 388 | } 389 | 390 | /** 391 | * toNumber(int) with maximum index. 392 | */ 393 | @Test(expected = IllegalArgumentException.class) 394 | public void testMaxToNumber() { 395 | luaState.toNumber(Integer.MAX_VALUE); 396 | } 397 | 398 | /** 399 | * toNumber(int) with minimum index. 400 | */ 401 | @Test(expected = IllegalArgumentException.class) 402 | public void testMinToNumbern() { 403 | luaState.toNumber(Integer.MIN_VALUE); 404 | } 405 | 406 | /** 407 | * toPointer(int) with illegal index. 408 | */ 409 | @Test(expected = IllegalArgumentException.class) 410 | public void testIllegalToPointer() { 411 | luaState.toPointer(getIllegalIndex()); 412 | } 413 | 414 | /** 415 | * toString(int) with illegal index. 416 | */ 417 | @Test(expected = IllegalArgumentException.class) 418 | public void testIllegalToString() { 419 | luaState.toString(getIllegalIndex()); 420 | } 421 | 422 | // -- Stack operation test 423 | /** 424 | * arith(ArithOperator) with two missing arguments for addition. 425 | */ 426 | @Test(expected = IllegalStateException.class) 427 | public void testUnderflowArith1() { 428 | luaState.arith(ArithOperator.ADD); 429 | } 430 | 431 | /** 432 | * arith(ArithOperator) with one missing argument for addition. 433 | */ 434 | @Test(expected = IllegalStateException.class) 435 | public void testUnderflowArith2() { 436 | luaState.pushNumber(1); 437 | luaState.arith(ArithOperator.ADD); 438 | } 439 | 440 | /** 441 | * arith(ArithOperator) with one missing argument for mathematical negation. 442 | */ 443 | @Test(expected = IllegalStateException.class) 444 | public void testUnderflowArith3() { 445 | luaState.arith(ArithOperator.UNM); 446 | } 447 | 448 | /** 449 | * concat(int) with insufficient arguments. 450 | */ 451 | @Test(expected = IllegalStateException.class) 452 | public void testUnderflowConcat1() { 453 | luaState.concat(1); 454 | } 455 | 456 | /** 457 | * concat(int) with insufficient arguments. 458 | */ 459 | @Test(expected = IllegalStateException.class) 460 | public void testUnderflowConcat2() { 461 | luaState.pushString(""); 462 | luaState.pushString(""); 463 | luaState.concat(3); 464 | } 465 | 466 | /** 467 | * concat(int) with an illegal number of arguments. 468 | */ 469 | @Test(expected = IllegalArgumentException.class) 470 | public void testIllegalConcat() { 471 | luaState.concat(-1); 472 | } 473 | 474 | /** 475 | * copy(int, int) with two illegal indexes. 476 | */ 477 | @Test(expected = IllegalArgumentException.class) 478 | public void testIllegalCopy1() { 479 | luaState.copy(getIllegalIndex(), getIllegalIndex()); 480 | } 481 | 482 | /** 483 | * copy(int, int) with one illegal index. 484 | */ 485 | @Test(expected = IllegalArgumentException.class) 486 | public void testIllegalCopy2() { 487 | luaState.pushInteger(1); 488 | luaState.copy(1, getIllegalIndex()); 489 | } 490 | 491 | /** 492 | * len(int) with illegal index. 493 | */ 494 | @Test(expected = IllegalArgumentException.class) 495 | public void testIllegalLen() { 496 | luaState.len(getIllegalIndex()); 497 | } 498 | 499 | /** 500 | * insert(int) with illegal index. 501 | */ 502 | @Test(expected = IllegalArgumentException.class) 503 | public void testIllegalInsert() { 504 | luaState.insert(getIllegalIndex()); 505 | } 506 | 507 | /** 508 | * pop(int) with insufficient arguments. 509 | */ 510 | @Test(expected = IllegalArgumentException.class) 511 | public void testUnderflowPop() { 512 | luaState.pop(1); 513 | } 514 | 515 | /** 516 | * pop(int) with an illegal number of arguments. 517 | */ 518 | @Test(expected = IllegalArgumentException.class) 519 | public void testIllegalPop() { 520 | luaState.pop(-1); 521 | } 522 | 523 | /** 524 | * pushValue(int) with illegal index. 525 | */ 526 | @Test(expected = IllegalArgumentException.class) 527 | public void testIllegalPushValue() { 528 | luaState.pushValue(getIllegalIndex()); 529 | } 530 | 531 | /** 532 | * remove(int) with illegal index. 533 | */ 534 | @Test(expected = IllegalArgumentException.class) 535 | public void testIllegalRemove() { 536 | luaState.remove(getIllegalIndex()); 537 | } 538 | 539 | /** 540 | * replace(int) with illegal index. 541 | */ 542 | @Test(expected = IllegalArgumentException.class) 543 | public void testIllegalReplace() { 544 | luaState.replace(getIllegalIndex()); 545 | } 546 | 547 | /** 548 | * setTop(int) with an illegal argument. 549 | */ 550 | @Test(expected = IllegalArgumentException.class) 551 | public void testIllegalSetTop() { 552 | luaState.setTop(-1); 553 | } 554 | 555 | // -- Table tests 556 | /** 557 | * getTable(int) with illegal index. 558 | */ 559 | @Test(expected = IllegalArgumentException.class) 560 | public void testIllegalGetTable1() { 561 | luaState.pushString(""); 562 | luaState.getTable(getIllegalIndex()); 563 | } 564 | 565 | /** 566 | * getTable(int) with invalid table. 567 | */ 568 | @Test(expected = IllegalArgumentException.class) 569 | public void testIllegalGetTable2() { 570 | luaState.pushNumber(0.0); 571 | luaState.pushString(""); 572 | luaState.getTable(1); 573 | } 574 | 575 | /** 576 | * getField(int, String) with illegal index. 577 | */ 578 | @Test(expected = IllegalArgumentException.class) 579 | public void testIllegalGetField1() { 580 | luaState.getField(getIllegalIndex(), ""); 581 | } 582 | 583 | /** 584 | * getField(int, String) with invalid table. 585 | */ 586 | @Test(expected = IllegalArgumentException.class) 587 | public void testIllegalGetField2() { 588 | luaState.pushNumber(0.0); 589 | luaState.getField(1, ""); 590 | } 591 | 592 | /** 593 | * newTable(int, int) with negative array count. 594 | */ 595 | @Test(expected = IllegalArgumentException.class) 596 | public void testIllegalNewTable1() { 597 | luaState.newTable(-1, 0); 598 | } 599 | 600 | /** 601 | * newTable(int, int) with negative record count. 602 | */ 603 | @Test(expected = IllegalArgumentException.class) 604 | public void testIllegalNewTable2() { 605 | luaState.newTable(0, -1); 606 | } 607 | 608 | /** 609 | * next(int) with illegal index. 610 | */ 611 | @Test(expected = IllegalArgumentException.class) 612 | public void testIllegalNext1() { 613 | luaState.pushNil(); 614 | luaState.next(getIllegalIndex()); 615 | } 616 | 617 | /** 618 | * next(int) with invalid table. 619 | */ 620 | @Test(expected = IllegalArgumentException.class) 621 | public void testIllegalNext2() { 622 | luaState.pushNumber(0.0); 623 | luaState.pushNil(); 624 | luaState.next(1); 625 | } 626 | 627 | /** 628 | * rawGet(int) with illegal index. 629 | */ 630 | @Test(expected = IllegalArgumentException.class) 631 | public void testIllegalRawGet1() { 632 | luaState.rawGet(getIllegalIndex()); 633 | } 634 | 635 | /** 636 | * rawGet(int) with invalid table. 637 | */ 638 | @Test(expected = IllegalArgumentException.class) 639 | public void testIllegalRawGet2() { 640 | luaState.pushNumber(0.0); 641 | luaState.pushString(""); 642 | luaState.rawGet(1); 643 | } 644 | 645 | /** 646 | * rawGet(int, int) with illegal index. 647 | */ 648 | @Test(expected = IllegalArgumentException.class) 649 | public void testIllegalRawGet3() { 650 | luaState.rawGet(getIllegalIndex(), 1); 651 | } 652 | 653 | /** 654 | * rawGet(int, int) with invalid table. 655 | */ 656 | @Test(expected = IllegalArgumentException.class) 657 | public void testIllegalRawGet4() { 658 | luaState.pushNumber(0.0); 659 | luaState.rawGet(1, 1); 660 | } 661 | 662 | /** 663 | * rawSet(int) with insufficient arguments. 664 | */ 665 | @Test(expected = IllegalStateException.class) 666 | public void testUnderflowRawSet() { 667 | luaState.newTable(); 668 | luaState.rawSet(1); 669 | } 670 | 671 | /** 672 | * rawSet(int) with nil index. 673 | */ 674 | @Test(expected = LuaRuntimeException.class) 675 | public void testNilRawSet() { 676 | luaState.newTable(); 677 | luaState.pushNil(); 678 | luaState.pushString("value"); 679 | luaState.rawSet(1); 680 | } 681 | 682 | /** 683 | * rawSet(int) with illegal index. 684 | */ 685 | @Test(expected = IllegalArgumentException.class) 686 | public void testIllegalRawSet1() { 687 | luaState.pushString("key"); 688 | luaState.pushString("value"); 689 | luaState.rawSet(getIllegalIndex()); 690 | } 691 | 692 | /** 693 | * rawSet(int) with invalid table. 694 | */ 695 | @Test(expected = IllegalArgumentException.class) 696 | public void testIllegalRawSet2() { 697 | luaState.pushNumber(0.0); 698 | luaState.pushString("key"); 699 | luaState.pushString("value"); 700 | luaState.rawSet(1); 701 | } 702 | 703 | /** 704 | * rawSet(int, int) with invalid table. 705 | */ 706 | @Test(expected = IllegalArgumentException.class) 707 | public void testIllegalRawSet3() { 708 | luaState.pushNumber(0.0); 709 | luaState.pushString("value"); 710 | luaState.rawSet(1, 1); 711 | } 712 | 713 | /** 714 | * setTable(int) with insufficient arguments. 715 | */ 716 | @Test(expected = IllegalStateException.class) 717 | public void testUnderflowSetTable() { 718 | luaState.newTable(); 719 | luaState.setTable(1); 720 | } 721 | 722 | /** 723 | * setTable(int) with nil index. 724 | */ 725 | @Test(expected = LuaRuntimeException.class) 726 | public void testNilSetTable() { 727 | luaState.newTable(); 728 | luaState.pushNil(); 729 | luaState.pushString(""); 730 | luaState.setTable(1); 731 | } 732 | 733 | /** 734 | * setTable(int) with illegal index. 735 | */ 736 | @Test(expected = IllegalArgumentException.class) 737 | public void testIllegalSetTable() { 738 | luaState.pushNil(); 739 | luaState.pushString(""); 740 | luaState.setTable(getIllegalIndex()); 741 | } 742 | 743 | /** 744 | * setField(int, String) with null key. 745 | */ 746 | @Test(expected = NullPointerException.class) 747 | public void testNullSetField() { 748 | luaState.newTable(); 749 | luaState.pushString("value"); 750 | luaState.setField(1, null); 751 | } 752 | 753 | /** 754 | * setField(int, String) with illegal index. 755 | */ 756 | @Test(expected = IllegalArgumentException.class) 757 | public void testIllegalSetField1() { 758 | luaState.pushString(""); 759 | luaState.setField(getIllegalIndex(), "key"); 760 | } 761 | 762 | /** 763 | * setField(int, String) with invalid table. 764 | */ 765 | @Test(expected = IllegalArgumentException.class) 766 | public void testIllegalSetField2() { 767 | luaState.pushNumber(0.0); 768 | luaState.pushString(""); 769 | luaState.setField(1, "key"); 770 | } 771 | 772 | // -- Metatable tests 773 | /** 774 | * setMetatable(int) with invalid table. 775 | */ 776 | @Test(expected = IllegalArgumentException.class) 777 | public void testIllegalSetMetatable() { 778 | luaState.newTable(); 779 | luaState.pushNumber(0.0); 780 | luaState.setMetatable(1); 781 | } 782 | 783 | // -- Thread tests 784 | /** 785 | * resume(int, int) with insufficient arguments. 786 | */ 787 | @Test(expected = IllegalStateException.class) 788 | public void testUnderflowResume() { 789 | luaState.openLibs(); 790 | luaState.getGlobal("print"); 791 | luaState.newThread(); 792 | luaState.resume(1, 1); 793 | } 794 | 795 | /** 796 | * resume(int, int) with invalid thread. 797 | */ 798 | @Test(expected = IllegalArgumentException.class) 799 | public void testIllegalResume1() { 800 | luaState.pushNumber(0.0); 801 | luaState.resume(1, 0); 802 | } 803 | 804 | /** 805 | * resume(int, int) with an illegal number of returns. 806 | */ 807 | @Test(expected = IllegalArgumentException.class) 808 | public void testIllegalResume2() { 809 | luaState.openLibs(); 810 | luaState.getGlobal("print"); 811 | luaState.newThread(); 812 | luaState.resume(1, -1); 813 | } 814 | 815 | /** 816 | * status(int) with illegal thread. 817 | */ 818 | @Test(expected = IllegalArgumentException.class) 819 | public void testIllegalStatus() { 820 | luaState.pushNumber(0.0); 821 | luaState.status(1); 822 | } 823 | 824 | /** 825 | * yield(int) with no running thread. 826 | */ 827 | @Test(expected = LuaRuntimeException.class) 828 | public void testIllegalYield1() { 829 | luaState.register(new NamedJavaFunction() { 830 | @Override 831 | public int invoke(LuaState luaState) { 832 | return luaState.yield(0); 833 | } 834 | 835 | @Override 836 | public String getName() { 837 | return "yieldfunc"; 838 | } 839 | }); 840 | luaState.load("return yieldfunc()", "=testIllegalYield1"); 841 | luaState.call(0, 0); 842 | } 843 | 844 | /** 845 | * yield across C-call boundary. 846 | */ 847 | @Test(expected=LuaRuntimeException.class) 848 | public void testIllegalYield2() { 849 | luaState.openLib(Library.COROUTINE); 850 | luaState.pop(1); 851 | JavaFunction yieldFunction = new JavaFunction() { 852 | @Override 853 | public int invoke(LuaState luaState) { 854 | luaState.load("return coroutine.yield()", "=testIllegalYield2"); 855 | luaState.call(0, 0); 856 | return 0; 857 | } 858 | }; 859 | luaState.pushJavaFunction(yieldFunction); 860 | luaState.newThread(); 861 | luaState.resume(1, 0); 862 | } 863 | 864 | /** 865 | * yield(int) with insufficient arguments. 866 | */ 867 | @Test(expected = LuaRuntimeException.class) 868 | public void testUnderflowYield() { 869 | luaState.register(new NamedJavaFunction() { 870 | @Override 871 | public int invoke(LuaState luaState) { 872 | return luaState.yield(1); 873 | } 874 | 875 | @Override 876 | public String getName() { 877 | return "yieldfunc"; 878 | } 879 | }); 880 | luaState.load("yieldfunc()", "=testUnderflowYield"); 881 | luaState.newThread(); 882 | luaState.resume(1, 0); 883 | } 884 | 885 | // -- Reference tests 886 | /** 887 | * ref(int) with illegal index. 888 | */ 889 | @Test(expected = IllegalArgumentException.class) 890 | public void testIllegalRef1() { 891 | luaState.pushNumber(0.0); 892 | luaState.ref(getIllegalIndex()); 893 | } 894 | 895 | /** 896 | * ref(int) with illegal table. 897 | */ 898 | @Test(expected = IllegalArgumentException.class) 899 | public void testIllegalRef2() { 900 | luaState.pushNumber(0.0); 901 | luaState.pushNumber(0.0); 902 | luaState.ref(1); 903 | } 904 | 905 | /** 906 | * unref(int, int) with illegal index. 907 | */ 908 | @Test(expected = IllegalArgumentException.class) 909 | public void testIllegalUnref1() { 910 | luaState.newTable(); 911 | luaState.pushNumber(0.0); 912 | int reference = luaState.ref(1); 913 | luaState.unref(getIllegalIndex(), reference); 914 | } 915 | 916 | /** 917 | * unref(int, int) with illegal table. 918 | */ 919 | @Test(expected = IllegalArgumentException.class) 920 | public void testIllegalUnref2() { 921 | luaState.pushNumber(0.0); 922 | luaState.pushNumber(0.0); 923 | luaState.unref(1, 1); 924 | } 925 | 926 | // -- Optimization tests 927 | /** 928 | * tableSize(int) with illegal table. 929 | */ 930 | @Test(expected = IllegalArgumentException.class) 931 | public void testIllegalTableSize1() { 932 | luaState.pushNumber(0.0); 933 | luaState.tableSize(1); 934 | } 935 | 936 | /** 937 | * tableSize(int) with illegal index. 938 | */ 939 | @Test(expected = IllegalArgumentException.class) 940 | public void testIllegalTableSize2() { 941 | luaState.tableSize(1); 942 | } 943 | 944 | /** 945 | * tableMove(int, int, int, int) with illegal index. 946 | */ 947 | @Test(expected = IllegalArgumentException.class) 948 | public void testIllegalTableMove1() { 949 | luaState.tableMove(getIllegalIndex(), 1, 1, 0); 950 | } 951 | 952 | /** 953 | * tableMove(int, int, int, int) with illegal count. 954 | */ 955 | @Test(expected = IllegalArgumentException.class) 956 | public void testIllegalTableMove2() { 957 | luaState.newTable(); 958 | luaState.tableMove(1, 1, 1, -1); 959 | } 960 | 961 | // -- Argument checking tests 962 | /** 963 | * checkArg(int, boolean, String) with false condition. 964 | */ 965 | @Test(expected = LuaRuntimeException.class) 966 | public void testIllegalCheckArg() { 967 | luaState.pushBoolean(false); 968 | luaState.checkArg(1, false, ""); 969 | } 970 | 971 | /** 972 | * checkEnum(int, T[]) with null values. 973 | */ 974 | @Test(expected = NullPointerException.class) 975 | public void testNullCheckEnum1() { 976 | luaState.pushString(""); 977 | luaState.checkEnum(1, (GcAction[]) null); 978 | } 979 | 980 | /** 981 | * checkEnum(int, T[], T) with null values. 982 | */ 983 | @Test(expected = NullPointerException.class) 984 | public void testNullCheckEnum2() { 985 | luaState.pushString(""); 986 | luaState.checkEnum(1, null, GcAction.STOP); 987 | } 988 | 989 | /** 990 | * checkEnum(int, T[]) with illegal argument. 991 | */ 992 | @Test(expected = LuaRuntimeException.class) 993 | public void testIllegalCheckEnum1() { 994 | luaState.pushBoolean(false); 995 | luaState.checkEnum(1, GcAction.values()); 996 | } 997 | 998 | /** 999 | * checkEnum(int, T[], T) with illegal argument. 1000 | */ 1001 | @Test(expected = LuaRuntimeException.class) 1002 | public void testIllegalCheckEnum2() { 1003 | luaState.pushBoolean(false); 1004 | luaState.checkEnum(1, GcAction.values(), GcAction.STOP); 1005 | } 1006 | 1007 | /** 1008 | * checkInteger(int) with illegal argument. 1009 | */ 1010 | @Test(expected = LuaRuntimeException.class) 1011 | public void testIllegalCheckInteger1() { 1012 | luaState.pushBoolean(false); 1013 | luaState.checkInteger(1); 1014 | } 1015 | 1016 | /** 1017 | * checkInteger(int, int) with illegal argument. 1018 | */ 1019 | @Test(expected = LuaRuntimeException.class) 1020 | public void testIllegalCheckInteger2() { 1021 | luaState.pushBoolean(false); 1022 | luaState.checkInteger(1, 2); 1023 | } 1024 | 1025 | /** 1026 | * checkJavaObject(int) with illegal argument. 1027 | */ 1028 | @Test(expected = LuaRuntimeException.class) 1029 | public void testIllegalCheckJavaObject1() { 1030 | luaState.pushBoolean(false); 1031 | luaState.checkJavaObject(1, Integer.class); 1032 | } 1033 | 1034 | /** 1035 | * checkJavaObject(int, int) with illegal argument. 1036 | */ 1037 | @Test(expected = LuaRuntimeException.class) 1038 | public void testIllegalCheckJavaFunction2() { 1039 | luaState.pushBoolean(false); 1040 | luaState.checkJavaObject(1, Integer.class, Integer.valueOf(0)); 1041 | } 1042 | 1043 | /** 1044 | * checkNumber(int) with illegal argument. 1045 | */ 1046 | @Test(expected = LuaRuntimeException.class) 1047 | public void testIllegalCheckNumber1() { 1048 | luaState.pushBoolean(false); 1049 | luaState.checkNumber(1); 1050 | } 1051 | 1052 | /** 1053 | * checkNumber(int, double) with illegal argument. 1054 | */ 1055 | @Test(expected = LuaRuntimeException.class) 1056 | public void testIllegalCheckNumber2() { 1057 | luaState.pushBoolean(false); 1058 | luaState.checkNumber(1, 2.0); 1059 | } 1060 | 1061 | /** 1062 | * checkOption(int, String[]) with null values. 1063 | */ 1064 | @Test(expected = NullPointerException.class) 1065 | public void testNullCheckOption1() { 1066 | luaState.pushInteger(1); 1067 | luaState.checkOption(1, null); 1068 | } 1069 | 1070 | /** 1071 | * checkOption(int, String[], String) with null values. 1072 | */ 1073 | @Test(expected = NullPointerException.class) 1074 | public void testNullCheckOption2() { 1075 | luaState.pushInteger(1); 1076 | luaState.checkOption(1, null, ""); 1077 | } 1078 | 1079 | /** 1080 | * checkOption(int, String[]) with illegal argument. 1081 | */ 1082 | @Test(expected = LuaRuntimeException.class) 1083 | public void testIllegalCheckOption1() { 1084 | luaState.pushInteger(1); 1085 | luaState.checkOption(1, new String[] { "test" }); 1086 | } 1087 | 1088 | /** 1089 | * checkOption(int, String[], String) with illegal argument. 1090 | */ 1091 | @Test(expected = LuaRuntimeException.class) 1092 | public void testIllegalCheckOption2() { 1093 | luaState.pushInteger(1); 1094 | luaState.checkOption(1, new String[] { "test" }, "test"); 1095 | } 1096 | 1097 | /** 1098 | * checkOption(int, String[], String) with illegal default option. 1099 | */ 1100 | @Test(expected = LuaRuntimeException.class) 1101 | public void testIllegalCheckOption3() { 1102 | luaState.checkOption(1, new String[] { "test" }, ""); 1103 | } 1104 | 1105 | /** 1106 | * checkString(int) with illegal argument. 1107 | */ 1108 | @Test(expected = LuaRuntimeException.class) 1109 | public void testIllegalCheckString1() { 1110 | luaState.pushBoolean(false); 1111 | luaState.checkString(1); 1112 | } 1113 | 1114 | /** 1115 | * checkString(int, String) with illegal argument. 1116 | */ 1117 | @Test(expected = LuaRuntimeException.class) 1118 | public void testIllegalCheckString2() { 1119 | luaState.pushBoolean(false); 1120 | luaState.checkString(1, ""); 1121 | } 1122 | 1123 | // -- Proxy tests 1124 | /** 1125 | * getProxy(int, Class[]) with null interface. 1126 | */ 1127 | @Test(expected = NullPointerException.class) 1128 | public void testNullGetProxy() { 1129 | luaState.newTable(); 1130 | luaState.getProxy(1, new Class[] { null }); 1131 | } 1132 | 1133 | /** 1134 | * getProxy(int) with illegal index. 1135 | */ 1136 | @Test(expected = IllegalArgumentException.class) 1137 | public void testIllegalGetProxy1() { 1138 | luaState.getProxy(getIllegalIndex()); 1139 | } 1140 | 1141 | /** 1142 | * getProxy(int, Class) with illegal index. 1143 | */ 1144 | @Test(expected = IllegalArgumentException.class) 1145 | public void testIllegalGetProxy2() { 1146 | luaState.getProxy(getIllegalIndex(), Runnable.class); 1147 | } 1148 | 1149 | /** 1150 | * getProxy(int, Class) with illegal table. 1151 | */ 1152 | @Test(expected = IllegalArgumentException.class) 1153 | public void testIllegalGetProxy3() { 1154 | luaState.pushNumber(0.0); 1155 | luaState.getProxy(1, Runnable.class); 1156 | } 1157 | 1158 | /** 1159 | * getProxy(int, Class[]) with illegal index. 1160 | */ 1161 | @Test(expected = IllegalArgumentException.class) 1162 | public void testIllegalGetProxy4() { 1163 | luaState.getProxy(getIllegalIndex(), new Class[] { Runnable.class }); 1164 | } 1165 | 1166 | /** 1167 | * getProxy(int, Class[]) with illegal table. 1168 | */ 1169 | @Test(expected = IllegalArgumentException.class) 1170 | public void testIllegalGetProxy5() { 1171 | luaState.pushNumber(0.0); 1172 | luaState.getProxy(1, new Class[] { Runnable.class }); 1173 | } 1174 | 1175 | // -- Private methods 1176 | /** 1177 | * Returns an illegal index. 1178 | */ 1179 | private int getIllegalIndex() { 1180 | return luaState.getTop() + 1; 1181 | } 1182 | } 1183 | --------------------------------------------------------------------------------