Allow access to public members of private inner classes where possible

This commit is contained in:
James Roseborough
2012-01-31 16:04:26 +00:00
parent 3506930278
commit 14d344a045
9 changed files with 150 additions and 24 deletions

View File

@@ -469,6 +469,18 @@ Time is a represented as number of milliseconds since the epoch,
and most time and date formatting, locales, and other features
are not implemented.
<h3>Coroutine Library</h3>
The <em>coroutine</em> library is implemented using one JavaThread per coroutine.
This allows <em>coroutine.yield()</em> can be called from anywhere,
as with the yield-from-anywhere patch in C-based lua.
<p>
Luaj uses WeakReferences and the OrphanedThread error to ensure that coroutines that are no longer referenced
are properly garbage collected. For thread safety, OrphanedThread should not be caught by Java code.
See <a href="http://luaj.sourceforge.net/api/2.0/org/luaj/vm2/LuaThread.html">LuaThread</a>
and <a href="http://luaj.sourceforge.net/api/2.0/org/luaj/vm2/OrphanedThread.html">OrphanedThread</a>
javadoc for details.
<h3>Debug Library</h3>
The <em>debug</em> library is not included by default by
<em>JmePlatform.standardGlobals()</em> or <em>JsePlatform.standardGlobsls()</em> .
@@ -505,7 +517,7 @@ See a longer sample in <em>examples/lua/swingapp.lua</em> for details, or try ru
</pre>
<p>
The Java ME platform does not include this library, and it cannot be made to work because of the lack of a reflection API in Java SE.
The Java ME platform does not include this library, and it cannot be made to work because of the lack of a reflection API in Java ME.
<p>
The <em>lua</em> connand line tool includes <em>luajava</em>.
@@ -734,6 +746,7 @@ and LuaForge:
<li>Fix lua command vararg values passed into main script to match what is in global arg table </li>
<li>Add arithmetic metatag processing when left hand side is a number and right hand side has metatable </li>
<li>Fix load(func) when mutiple string fragments are supplied by calls to func </li>
<li>Allow access to public members of private inner classes where possible </li>
</ul></td></tr>
</table></td></tr></table>
@@ -744,5 +757,6 @@ and LuaForge:
<li>using both version 1 and 2 libraries together in the same java vm has not been tested
<li>module() and setfenv() only partially supported for lau2java or luajc compiled lua
<li>values associated with weak keys may linger longer than expected
<li>behavior of luaj when a SecurityManager is used has not been fully characterized
</ul>

View File

@@ -47,12 +47,12 @@ import org.luaj.vm2.lib.DebugLib;
* The behavior of coroutine threads matches closely the behavior
* of C coroutine library. However, because of the use of Java threads
* to manage call state, it is possible to yield from anywhere in luaj.
* On the other hand, if a {@link LuaThread} is created, then yields
* without ever entering a completed state, then the garbage collector
* may not be able to determine that the thread needs to be collected,
* and this could cause a memory and resource leak. It is recommended
* that all coroutines that are created are resumed until they are in
* a completed state.
* <p>
* Each Java thread wakes up at regular intervals and checks a weak reference
* to determine if it can ever be resumed. If not, it throws
* {@link OrphanedThread} which is an {@link java.lang.Error}.
* Applications should not catch {@link OrphanedThread}, because it can break
* the thread safety of luaj.
*
* @see LuaValue
* @see JsePlatform
@@ -64,7 +64,10 @@ public class LuaThread extends LuaValue {
public static LuaValue s_metatable;
public static int coroutine_count = 0;
/** Interval at which to check for lua threads that are no longer referenced.
* This can be changed by Java startup code if desired.
*/
static long thread_orphan_check_interval = 30000;
private static final int STATUS_INITIAL = 0;

View File

@@ -21,19 +21,20 @@
******************************************************************************/
package org.luaj.vm2;
/**
* Error sublcass that indicates a lua thread that is no longer referenced has been detected.
*
* The java thread in which this is thrown should correspond to a LuaThread being used as a
* coroutine that could not possibly be resumed again because there are no more references
* to the LuaThread with which it is associated. Rather than locking up resources forever,
* this error is thrown, and should fall through all the way to the thread's run() method.
*
* Java code mixed with the luaj vm should not catch this error because it may occur when
* the coroutine is not running, so any processing done during error handling could break
* the thread-safety of the application because other lua processing could be going on in
* a different thread.
/**
* {@link java.lang.Error} sublcass that indicates a lua thread that is no
* longer referenced has been detected.
* <p>
* The java thread in which this is thrown should correspond to a
* {@link LuaThread} being used as a coroutine that could not possibly be
* resumed again because there are no more references to the LuaThread with
* which it is associated. Rather than locking up resources forever, this error
* is thrown, and should fall through all the way to the thread's {@link Thread.run}() method.
* <p>
* Java code mixed with the luaj vm should not catch this error because it may
* occur when the coroutine is not running, so any processing done during error
* handling could break the thread-safety of the application because other lua
* processing could be going on in a different thread.
*/
public class OrphanedThread extends Error {

View File

@@ -76,9 +76,17 @@ class JavaClass extends JavaInstance implements CoerceJavaToLua.Coercion {
if ( fields == null ) {
Map m = new HashMap();
Field[] f = ((Class)m_instance).getFields();
for ( int i=0; i<f.length; i++ )
if ( Modifier.isPublic(f[i].getModifiers()) )
m.put( LuaValue.valueOf(f[i].getName()), f[i] );
for ( int i=0; i<f.length; i++ ) {
Field fi = f[i];
if ( Modifier.isPublic(fi.getModifiers()) ) {
m.put( LuaValue.valueOf(fi.getName()), fi );
try {
if (!fi.isAccessible())
fi.setAccessible(true);
} catch (SecurityException s) {
}
}
}
fields = m;
}
return (Field) fields.get(key);

View File

@@ -63,6 +63,11 @@ class JavaMethod extends JavaMember {
private JavaMethod(Method m) {
super( m.getParameterTypes(), m.getModifiers() );
this.method = m;
try {
if (!m.isAccessible())
m.setAccessible(true);
} catch (SecurityException s) {
}
}
public LuaValue call() {

View File

@@ -32,6 +32,7 @@ import org.luaj.vm2.compiler.DumpLoadEndianIntTest;
import org.luaj.vm2.compiler.RegressionTests;
import org.luaj.vm2.compiler.SimpleTests;
import org.luaj.vm2.lib.jse.LuaJavaCoercionTest;
import org.luaj.vm2.lib.jse.LuajavaAccessibleMembersTest;
import org.luaj.vm2.lib.jse.LuajavaClassMembersTest;
import org.luaj.vm2.vm1.Luajvm1CompatibilityTest;
@@ -74,6 +75,7 @@ public class AllTests {
// library tests
TestSuite lib = new TestSuite("Library Tests");
lib.addTestSuite(LuajavaAccessibleMembersTest.class);
lib.addTestSuite(LuajavaClassMembersTest.class);
lib.addTestSuite(LuaJavaCoercionTest.class);
lib.addTestSuite(RequireClassTest.class);

View File

@@ -0,0 +1,70 @@
package org.luaj.vm2.lib.jse;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.Permission;
import junit.framework.TestCase;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.compiler.LuaC;
import org.luaj.vm2.lib.jse.JsePlatform;
public class LuajavaAccessibleMembersTest extends TestCase {
private LuaTable _G;
protected void setUp() throws Exception {
super.setUp();
_G = JsePlatform.standardGlobals();
}
private String invokeScript(String script) {
try {
InputStream is = new ByteArrayInputStream( script.getBytes("UTF8") );
LuaFunction c = LuaC.instance.load( is, "script", _G );
return c.call().tojstring();
} catch ( Exception e ) {
fail("exception: "+e );
return "failed";
}
}
public void testAccessFromPrivateClassImplementedMethod() {
assertEquals("privateImpl-aaa-interface_method(bar)", invokeScript(
"b = luajava.newInstance('"+TestClass.class.getName()+"');" +
"a = b:create_PrivateImpl('aaa');" +
"return a:interface_method('bar');"));
}
public void testAccessFromPrivateClassPublicMethod() {
assertEquals("privateImpl-aaa-public_method", invokeScript(
"b = luajava.newInstance('"+TestClass.class.getName()+"');" +
"a = b:create_PrivateImpl('aaa');" +
"return a:public_method();"));
}
public void testAccessFromPrivateClassGetPublicField() {
assertEquals("aaa", invokeScript(
"b = luajava.newInstance('"+TestClass.class.getName()+"');" +
"a = b:create_PrivateImpl('aaa');" +
"return a.public_field;"));
}
public void testAccessFromPrivateClassSetPublicField() {
assertEquals("foo", invokeScript(
"b = luajava.newInstance('"+TestClass.class.getName()+"');" +
"a = b:create_PrivateImpl('aaa');" +
"a.public_field = 'foo';" +
"return a.public_field;"));
}
public void testAccessFromPrivateClassPublicConcstructor() {
assertEquals("privateImpl-constructor", invokeScript(
"b = luajava.newInstance('"+TestClass.class.getName()+"');" +
"c = b:get_PrivateImplClass();" +
"return luajava.new(c);"));
}
}

View File

@@ -0,0 +1,18 @@
package org.luaj.vm2.lib.jse;
public class TestClass {
private static class PrivateImpl implements TestInterface {
public String public_field;
public PrivateImpl() {
this.public_field = "privateImpl-constructor";
}
PrivateImpl(String f) {
this.public_field = f;
}
public String public_method() { return "privateImpl-"+public_field+"-public_method"; }
public String interface_method(String x) { return "privateImpl-"+public_field+"-interface_method("+x+")"; }
public String toString() { return public_field; }
}
public TestInterface create_PrivateImpl(String f) { return new PrivateImpl(f); }
public Class get_PrivateImplClass() { return PrivateImpl.class; }
}

View File

@@ -0,0 +1,5 @@
package org.luaj.vm2.lib.jse;
public interface TestInterface {
String interface_method(String x);
}