From dc84bc9e8db707c5d7b6aed8d15bb77f4b3b1b4d Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Mon, 21 Jun 2010 01:31:40 +0000 Subject: [PATCH] Refactor weak tables including proper weak key semantics and improved userdata handling. --- src/core/org/luaj/vm2/LuaTable.java | 167 ++++----- src/core/org/luaj/vm2/LuaValue.java | 2 + src/core/org/luaj/vm2/WeakTable.java | 377 ++++++++++++-------- test/junit/org/luaj/vm2/AllTests.java | 3 +- test/junit/org/luaj/vm2/TableArrayTest.java | 26 +- test/junit/org/luaj/vm2/TableHashTest.java | 12 +- test/junit/org/luaj/vm2/TableTest.java | 64 ++-- test/junit/org/luaj/vm2/WeakTableTest.java | 286 +++++++-------- 8 files changed, 523 insertions(+), 414 deletions(-) diff --git a/src/core/org/luaj/vm2/LuaTable.java b/src/core/org/luaj/vm2/LuaTable.java index 280eb1be..66a7a1fe 100644 --- a/src/core/org/luaj/vm2/LuaTable.java +++ b/src/core/org/luaj/vm2/LuaTable.java @@ -21,6 +21,8 @@ ******************************************************************************/ package org.luaj.vm2; +import java.util.Vector; + public class LuaTable extends LuaValue { private static final int MIN_HASH_CAPACITY = 2; private static final LuaString N = valueOf("n"); @@ -29,28 +31,28 @@ public class LuaTable extends LuaValue { protected LuaValue[] hashKeys; protected LuaValue[] hashValues; private int hashEntries; - private LuaValue m_metatable; + protected LuaValue m_metatable; public LuaTable() { array = NOVALS; hashKeys = NOVALS; hashValues = NOVALS; } - + public LuaTable(int narray, int nhash) { presize(narray, nhash); } - + public LuaTable(LuaValue[] named, LuaValue[] unnamed, Varargs lastarg) { int nn = (named!=null? named.length: 0); int nu = (unnamed!=null? unnamed.length: 0); int nl = (lastarg!=null? lastarg.narg(): 0); presize(nu+nl, nn-(nn>>1)); for ( int i=0; i 0 && nhash < MIN_HASH_CAPACITY ) - nhash = MIN_HASH_CAPACITY; - array = (narray>0? new LuaValue[narray]: NOVALS); - hashKeys = (nhash>0? new LuaValue[nhash]: NOVALS); - hashValues = (nhash>0? new LuaValue[nhash]: NOVALS); - hashEntries = 0; - } public int type() { return LuaValue.TTABLE; @@ -98,9 +91,18 @@ public class LuaTable extends LuaValue { return this; } - public void presize( int i ) { - if ( i > array.length ) - array = resize( array, i ); + public void presize( int narray ) { + if ( narray > array.length ) + array = resize( array, narray ); + } + + public void presize(int narray, int nhash) { + if ( nhash > 0 && nhash < MIN_HASH_CAPACITY ) + nhash = MIN_HASH_CAPACITY; + array = (narray>0? new LuaValue[narray]: NOVALS); + hashKeys = (nhash>0? new LuaValue[nhash]: NOVALS); + hashValues = (nhash>0? new LuaValue[nhash]: NOVALS); + hashEntries = 0; } private static LuaValue[] resize( LuaValue[] old, int n ) { @@ -109,6 +111,14 @@ public class LuaTable extends LuaValue { return v; } + protected int getArrayLength() { + return array.length; + } + + protected int getHashLength() { + return hashValues.length; + } + public LuaValue getmetatable() { if ( m_metatable!=null ) return m_metatable.rawget(METATABLE).optvalue(m_metatable); @@ -130,25 +140,11 @@ public class LuaTable extends LuaValue { } protected LuaTable changemode(boolean weakkeys, boolean weakvalues) { - if ( weakkeys || weakvalues ) { - return recreateas(weakkeys, weakvalues); - } + if ( weakkeys || weakvalues ) + return new WeakTable(weakkeys, weakvalues, this); return this; } - - protected LuaTable recreateas(boolean weakkeys, boolean weakvalues) { - LuaTable t = weakkeys||weakvalues? - new WeakTable(weakkeys, weakvalues): - new LuaTable(); - t.presize(array.length,hashKeys.length); - Varargs n; - LuaValue k = NIL; - while ( !(k = ((n = next(k)).arg1())).isnil() ) - t.rawset(k, n.arg(2)); - t.m_metatable = m_metatable; - return t; - } - + public LuaValue get( int key ) { LuaValue v = rawget(key); return v.isnil() && m_metatable!=null? gettable(this,valueOf(key)): v; @@ -173,7 +169,7 @@ public class LuaTable extends LuaValue { } return hashget( key ); } - + private LuaValue hashget(LuaValue key) { if ( hashEntries > 0 ) { LuaValue v = hashValues[hashFindSlot(key)]; @@ -181,7 +177,7 @@ public class LuaTable extends LuaValue { } return NIL; } - + public void set( int key, LuaValue value ) { if ( m_metatable==null || ! rawget(key).isnil() || ! settable(this,LuaInteger.valueOf(key),value) ) rawset(key, value); @@ -234,8 +230,6 @@ public class LuaTable extends LuaValue { public LuaValue remove(int pos) { if ( pos == 0 ) pos = length(); - if ( pos < 1 || pos > array.length ) - return NONE; LuaValue v = rawget(pos); for ( LuaValue r=v; !r.isnil(); ) { r = rawget(pos+1); @@ -267,9 +261,9 @@ public class LuaTable extends LuaValue { } public LuaValue getn() { - for ( int n=array.length; --n>0; ) - if ( array[n]!=null ) - return LuaInteger.valueOf(n+1); + for ( int n=getArrayLength(); n>0; --n ) + if ( !rawget(n).isnil() ) + return LuaInteger.valueOf(n); return ZERO; } @@ -277,10 +271,11 @@ public class LuaTable extends LuaValue { * Get the length of this table, as lua defines it. */ public int length() { - int n=array.length+1,m=0; + int a = getArrayLength(); + int n = a+1,m=0; while ( !rawget(n).isnil() ) { m = n; - n += array.length+hashEntries+1; + n += a+getHashLength()+1; } while ( n > m+1 ) { int k = (n+m) / 2; @@ -312,7 +307,10 @@ public class LuaTable extends LuaValue { return n; } - + /** + * Get the next element after a particular key in the table + * @return key,value or nil + */ /** * Get the next element after a particular key in the table * @return key,value or nil @@ -352,7 +350,7 @@ public class LuaTable extends LuaValue { // nothing found, push nil, return nil. return NIL; } - + /** * Get the next element after a particular key in the * contiguous array part of a table @@ -371,16 +369,13 @@ public class LuaTable extends LuaValue { * @param func function to call */ public LuaValue foreach(LuaValue func) { - LuaValue v = NIL; - for ( int i=0; i"; + } + + public LuaValue strongkey() { + Object o = ref.get(); + return o!=null? (LuaValue)o: NIL; + } + public LuaValue strongvalue() { Object o = ref.get(); return o!=null? (LuaValue)o: NIL; } - public String tojstring() { - return strongvalue().tojstring(); - } - } - - private static class WeakUserdata extends LuaValue { - private WeakReference ref; - private WeakReference mt; - public WeakUserdata(Object val, LuaValue metatable) { - this.ref = new WeakReference(val); - this.mt = new WeakReference(metatable); - } - public int type() { - return TVALUE; - } - public String typename() { - return "weakuserdata"; - } - public LuaValue strongvalue() { - if ( ref != null ) { - Object o = ref.get(); - if ( o != null ) - return userdataOf( o, (LuaValue) mt.get() ); - } - ref = mt = null; - return NIL; - } - } - - private static class WeakEntry extends LuaValue { - private LuaValue key; - private LuaValue val; - private WeakEntry(LuaValue key, LuaValue val) { - this.key = key; - this.val = val; - } - public int type() { - return LuaValue.TNIL; - } - public String typename() { - return "weakentry"; - } - public LuaValue strongkey() { - LuaValue k = key.strongvalue(); - LuaValue v = val.strongvalue(); - if ( k.isnil() || v.isnil() ) - return key = val = NIL; - return k; - } - public LuaValue strongvalue() { - LuaValue k = key.strongvalue(); - LuaValue v = val.strongvalue(); - if ( k.isnil() || v.isnil() ) - return key = val = NIL; - return v; - } + public boolean eq_b(LuaValue rhs) { - return strongkey().eq_b(rhs); - } - public int hashCode() { - return strongkey().hashCode(); + Object o = ref.get(); + return o!=null && rhs.eq_b((LuaValue)o); } } - - private boolean shouldWeaken( LuaValue value ) { - switch ( value.type() ) { - case LuaValue.TFUNCTION: - case LuaValue.TTHREAD: - case LuaValue.TTABLE: - case LuaValue.TUSERDATA: + static final class WeakUserdata extends WeakValue { + private final WeakReference ob; + private final WeakReference mt; + + private WeakUserdata(LuaValue value) { + super(value); + ob = new WeakReference(value.touserdata()); + LuaValue udmt = value.getmetatable(); + mt = udmt!=null? new WeakReference(udmt): null; + } + + public LuaValue strongvalue() { + Object u = ref.get(); + if ( u != null ) + return (LuaValue) u; + Object o = ob.get(); + Object m = mt!=null? mt.get(): null; + return o!=null? m!=null? userdataOf(o,(LuaValue)m): userdataOf(o): NIL; + } + + public boolean eq_b(LuaValue rhs) { + return rhs.isuserdata() && (rhs.touserdata() == ob.get()); + } + + public boolean isuserdata() { return true; } - return false; - } - - private LuaValue toWeak( LuaValue value ) { - switch ( value.type() ) { - case LuaValue.TFUNCTION: - case LuaValue.TTHREAD: - case LuaValue.TTABLE: return new WeakValue( value ); - case LuaValue.TUSERDATA: return new WeakUserdata( value.checkuserdata(), value.getmetatable() ); - default: return value; + + public Object touserdata() { + return ob.get(); } } - public LuaValue rawget(int key) { - LuaValue v = super.rawget(key); - if ( v.isnil() ) - return NIL; - v = v.strongvalue(); - if ( v.isnil() ) { - // TODO: mark table for culling? - super.rawset(key, NIL); - } - return v; - } + static final class WeakEntry extends LuaValue { + final WeakTable table; + final LuaValue weakkey; + final int keyhash; - public LuaValue rawget(LuaValue key) { - LuaValue v = super.rawget(key); - if ( v.isnil() ) - return NIL; - v = v.strongvalue(); - if ( v.isnil() ) { - // TODO: mark table for culling? - super.rawset(key, NIL); - } - return v; - } - - public void rawset(int key, LuaValue val) { - if ( val.isnil() || !weakValues || !shouldWeaken(val) ) { - super.rawset(key, val); - } else { - super.rawset(key, toWeak(val)); - } - } + private WeakEntry(WeakTable table, LuaValue key, LuaValue weakvalue) { + this.table = table; + this.weakkey = table.weaken(key); + this.keyhash = key.hashCode(); - public void rawset(LuaValue key, LuaValue val) { - if ( val.isnil() ) { - super.rawset(key, val); - } else { - boolean weakenKey = weakKeys && shouldWeaken(key); - boolean weakenVal = weakValues && shouldWeaken(val); - if ( weakenKey ) { - WeakEntry e = new WeakEntry( toWeak(key), weakenVal? toWeak(val): val); - super.rawset(e, e); - } else if ( weakenVal ) { - super.rawset(key, toWeak(val)); - } else { - super.rawset(key, val); - } + // store an association from table to value in the key's metatable + LuaValue mt = key.getmetatable(); + if ( mt == null ) + key.setmetatable(mt=new LuaTable(0,1)); + mt.set(table, weakvalue); + } + + // when looking up the value, look in the keys metatable + public LuaValue strongvalue() { + LuaValue key = weakkey.strongkey(); + if ( key.isnil() ) + return NIL; + LuaValue mt = key.getmetatable(); + if ( mt == null ) + return NIL; + LuaValue weakvalue = mt.get(table); + return weakvalue.strongvalue(); } - } - - protected LuaTable changemode(boolean k, boolean v) { - if ( k!=this.weakKeys || v!=weakValues ) - return recreateas(k,v); - return this; - } + public int type() { + illegal("type","weak entry"); + return 0; + } + + public String typename() { + illegal("typename","weak entry"); + return null; + } + + public String toString() { + return "weak<"+strongkey()+","+strongvalue()+">"; + } + + public int hashCode() { + return keyhash; + } + + public boolean eq_b(LuaValue rhs) { + return rhs.eq_b(weakkey.strongkey()); + } + } } diff --git a/test/junit/org/luaj/vm2/AllTests.java b/test/junit/org/luaj/vm2/AllTests.java index 6b3ca58e..44fc74ab 100644 --- a/test/junit/org/luaj/vm2/AllTests.java +++ b/test/junit/org/luaj/vm2/AllTests.java @@ -26,6 +26,7 @@ import junit.framework.TestSuite; import org.luaj.vm2.WeakTableTest.WeakKeyTableTest; import org.luaj.vm2.WeakTableTest.WeakKeyValueTableTest; +import org.luaj.vm2.WeakTableTest.WeakValueTableTest; import org.luaj.vm2.compiler.CompilerUnitTests; import org.luaj.vm2.compiler.DumpLoadEndianIntTest; import org.luaj.vm2.compiler.RegressionTests; @@ -52,7 +53,7 @@ public class AllTests { table.addTestSuite(TableTest.class); table.addTestSuite(TableArrayTest.class); table.addTestSuite(TableHashTest.class); - table.addTestSuite(WeakTableTest.class); + table.addTestSuite(WeakValueTableTest.class); table.addTestSuite(WeakKeyTableTest.class); table.addTestSuite(WeakKeyValueTableTest.class); suite.addTest(table); diff --git a/test/junit/org/luaj/vm2/TableArrayTest.java b/test/junit/org/luaj/vm2/TableArrayTest.java index 1873f229..3ce96ea4 100644 --- a/test/junit/org/luaj/vm2/TableArrayTest.java +++ b/test/junit/org/luaj/vm2/TableArrayTest.java @@ -57,10 +57,10 @@ public class TableArrayTest extends TestCase { } // Ensure capacities make sense - assertEquals( 0, t.hashCapacity() ); + assertEquals( 0, t.getHashLength() ); - assertTrue( t.arrayCapacity() >= 32 ); - assertTrue( t.arrayCapacity() <= 64 ); + assertTrue( t.getArrayLength() >= 32 ); + assertTrue( t.getArrayLength() <= 64 ); } @@ -79,8 +79,8 @@ public class TableArrayTest extends TestCase { assertEquals(LuaInteger.valueOf(i), t.get(i)); } - assertTrue( t.arrayCapacity() >= 0 && t.arrayCapacity() <= 2 ); - assertTrue( t.hashCapacity() >= 4 ); + assertTrue( t.getArrayLength() >= 0 && t.getArrayLength() <= 2 ); + assertTrue( t.getHashLength() >= 4 ); } public void testOutOfOrderIntegerKeyInsertion() { @@ -96,11 +96,11 @@ public class TableArrayTest extends TestCase { } // Ensure capacities make sense - assertTrue( t.arrayCapacity() >= 0 ); - assertTrue( t.arrayCapacity() <= 6 ); + assertTrue( t.getArrayLength() >= 0 ); + assertTrue( t.getArrayLength() <= 6 ); - assertTrue( t.hashCapacity() >= 16 ); - assertTrue( t.hashCapacity() <= 64 ); + assertTrue( t.getHashLength() >= 16 ); + assertTrue( t.getHashLength() <= 64 ); } @@ -113,10 +113,10 @@ public class TableArrayTest extends TestCase { t.set( str, LuaInteger.valueOf( i ) ); } - assertTrue( t.arrayCapacity() >= 9 ); // 1, 2, ..., 9 - assertTrue( t.arrayCapacity() <= 18 ); - assertTrue( t.hashCapacity() >= 11 ); // 0, "0", "1", ..., "9" - assertTrue( t.hashCapacity() <= 33 ); + assertTrue( t.getArrayLength() >= 9 ); // 1, 2, ..., 9 + assertTrue( t.getArrayLength() <= 18 ); + assertTrue( t.getHashLength() >= 11 ); // 0, "0", "1", ..., "9" + assertTrue( t.getHashLength() <= 33 ); LuaValue[] keys = t.keys(); diff --git a/test/junit/org/luaj/vm2/TableHashTest.java b/test/junit/org/luaj/vm2/TableHashTest.java index ce10d8be..1f643896 100644 --- a/test/junit/org/luaj/vm2/TableHashTest.java +++ b/test/junit/org/luaj/vm2/TableHashTest.java @@ -44,7 +44,7 @@ public class TableHashTest extends TestCase { public void testSetRemove() { LuaTable t = new_Table(); - assertEquals( 0, t.hashCapacity() ); + assertEquals( 0, t.getHashLength() ); assertEquals( 0, t.length() ); assertEquals( 0, t.keyCount() ); @@ -52,13 +52,13 @@ public class TableHashTest extends TestCase { "cd", "ef", "g", "hi", "jk", "lm", "no", "pq", "rs", }; int[] capacities = { 0, 2, 4, 4, 7, 7, 7, 10, 10, 14, 14, 14, 14, 19, 19, 19, 19, 25, 25, 25 }; for ( int i = 0; i < keys.length; ++i ) { - assertEquals( capacities[i], t.hashCapacity() ); + assertEquals( capacities[i], t.getHashLength() ); String si = "Test Value! "+i; t.set( keys[i], si ); assertEquals( 0, t.length() ); assertEquals( i+1, t.keyCount() ); } - assertEquals( capacities[keys.length], t.hashCapacity() ); + assertEquals( capacities[keys.length], t.getHashLength() ); for ( int i = 0; i < keys.length; ++i ) { LuaValue vi = LuaString.valueOf( "Test Value! "+i ); assertEquals( vi, t.get( keys[i] ) ); @@ -72,7 +72,7 @@ public class TableHashTest extends TestCase { t.set( keys[i], LuaString.valueOf( "Replacement Value! "+i ) ); assertEquals( 0, t.length() ); assertEquals( keys.length, t.keyCount() ); - assertEquals( capacities[keys.length], t.hashCapacity() ); + assertEquals( capacities[keys.length], t.getHashLength() ); } for ( int i = 0; i < keys.length; ++i ) { LuaValue vi = LuaString.valueOf( "Replacement Value! "+i ); @@ -85,9 +85,9 @@ public class TableHashTest extends TestCase { assertEquals( 0, t.length() ); assertEquals( keys.length-i-1, t.keyCount() ); if ( i l = new ArrayList(); + LuaValue k = LuaValue.NIL; + while ( true ) { + Varargs n = t.next(k); + if ( (k = n.arg1()).isnil() ) + break; + l.add( k ); + } + return l.toArray(new LuaValue[t.length()]); + } + + public void testInOrderIntegerKeyInsertion() { LuaTable t = new_Table(); @@ -48,10 +66,10 @@ public class TableTest extends TestCase { } // Ensure capacities make sense - assertEquals( 0, t.hashCapacity() ); + assertEquals( 0, t.getHashLength() ); - assertTrue( t.arrayCapacity() >= 32 ); - assertTrue( t.arrayCapacity() <= 64 ); + assertTrue( t.getArrayLength() >= 32 ); + assertTrue( t.getArrayLength() <= 64 ); } @@ -70,8 +88,8 @@ public class TableTest extends TestCase { assertEquals(LuaInteger.valueOf(i), t.get(i)); } - assertTrue( t.arrayCapacity() >= 0 && t.arrayCapacity() <= 2 ); - assertTrue( t.hashCapacity() >= 4 ); + assertTrue( t.getArrayLength() >= 0 && t.getArrayLength() <= 2 ); + assertTrue( t.getHashLength() >= 4 ); } public void testOutOfOrderIntegerKeyInsertion() { @@ -87,11 +105,11 @@ public class TableTest extends TestCase { } // Ensure capacities make sense - assertTrue( t.arrayCapacity() >= 0 ); - assertTrue( t.arrayCapacity() <= 6 ); + assertTrue( t.getArrayLength() >= 0 ); + assertTrue( t.getArrayLength() <= 6 ); - assertTrue( t.hashCapacity() >= 16 ); - assertTrue( t.hashCapacity() <= 64 ); + assertTrue( t.getHashLength() >= 16 ); + assertTrue( t.getHashLength() <= 64 ); } @@ -104,12 +122,12 @@ public class TableTest extends TestCase { t.set( str, LuaInteger.valueOf( i ) ); } - assertTrue( t.arrayCapacity() >= 9 ); // 1, 2, ..., 9 - assertTrue( t.arrayCapacity() <= 18 ); - assertTrue( t.hashCapacity() >= 11 ); // 0, "0", "1", ..., "9" - assertTrue( t.hashCapacity() <= 33 ); + assertTrue( t.getArrayLength() >= 9 ); // 1, 2, ..., 9 + assertTrue( t.getArrayLength() <= 18 ); + assertTrue( t.getHashLength() >= 11 ); // 0, "0", "1", ..., "9" + assertTrue( t.getHashLength() <= 33 ); - LuaValue[] keys = t.keys(); + LuaValue[] keys = keys(t); int intKeys = 0; int stringKeys = 0; @@ -145,7 +163,7 @@ public class TableTest extends TestCase { t.set( "test", LuaValue.valueOf("foo") ); t.set( "explode", LuaValue.valueOf("explode") ); - assertEquals( 2, t.keyCount() ); + assertEquals( 2, keyCount(t) ); } public void testRemove0() { @@ -173,11 +191,11 @@ public class TableTest extends TestCase { t.set( 42, LuaValue.NIL ); t.set( new_Table(), LuaValue.NIL ); t.set( "test", LuaValue.NIL ); - assertEquals( 0, t.keyCount() ); + assertEquals( 0, keyCount(t) ); t.set( 10, LuaInteger.valueOf( 5 ) ); t.set( 10, LuaValue.NIL ); - assertEquals( 0, t.keyCount() ); + assertEquals( 0, keyCount(t) ); } public void testRemove2() { @@ -185,23 +203,23 @@ public class TableTest extends TestCase { t.set( "test", LuaValue.valueOf("foo") ); t.set( "string", LuaInteger.valueOf( 10 ) ); - assertEquals( 2, t.keyCount() ); + assertEquals( 2, keyCount(t) ); t.set( "string", LuaValue.NIL ); t.set( "three", LuaValue.valueOf( 3.14 ) ); - assertEquals( 2, t.keyCount() ); + assertEquals( 2, keyCount(t) ); t.set( "test", LuaValue.NIL ); - assertEquals( 1, t.keyCount() ); + assertEquals( 1, keyCount(t) ); t.set( 10, LuaInteger.valueOf( 5 ) ); - assertEquals( 2, t.keyCount() ); + assertEquals( 2, keyCount(t) ); t.set( 10, LuaValue.NIL ); - assertEquals( 1, t.keyCount() ); + assertEquals( 1, keyCount(t) ); t.set( "three", LuaValue.NIL ); - assertEquals( 0, t.keyCount() ); + assertEquals( 0, keyCount(t) ); } public void testInOrderLuaLength() { diff --git a/test/junit/org/luaj/vm2/WeakTableTest.java b/test/junit/org/luaj/vm2/WeakTableTest.java index 27965de1..e64cbdbc 100644 --- a/test/junit/org/luaj/vm2/WeakTableTest.java +++ b/test/junit/org/luaj/vm2/WeakTableTest.java @@ -22,64 +22,9 @@ package org.luaj.vm2; import java.lang.ref.WeakReference; -import java.util.Random; -public class WeakTableTest extends TableTest { +abstract public class WeakTableTest extends TableTest { - protected LuaTable new_Table() { return new WeakTable(false, true); } - protected LuaTable new_Table(int n,int m) { return new WeakTable(false, true); } - - public static class WeakKeyTableTest extends TableTest { - protected LuaTable new_Table() { return new WeakTable(true, false); } - protected LuaTable new_Table(int n,int m) { return new WeakTable(true, false); } - } - - public static class WeakKeyValueTableTest extends TableTest { - protected LuaTable new_Table() { return new WeakTable(true, true); } - protected LuaTable new_Table(int n,int m) { return new WeakTable(true, true); } - } - - - public void testWeakValuesTable() { - LuaTable t = new_Table(); - - Object obj = new Object(); - LuaTable tableValue = new LuaTable(); - LuaString stringValue = LuaString.valueOf("this is a test"); - - t.set("table", tableValue); - t.set("userdata", LuaValue.userdataOf(obj, null)); - t.set("string", stringValue); - t.set("string2", LuaString.valueOf("another string")); - assertTrue("table must have at least 4 elements", t.hashKeys.length > 4); - - // check that table can be used to get elements - assertEquals(tableValue, t.get("table")); - assertEquals(stringValue, t.get("string")); - assertEquals(obj, t.get("userdata").checkuserdata()); - - // nothing should be collected, since we have strong references here - System.gc(); - - // check that elements are still there - assertEquals(tableValue, t.get("table")); - assertEquals(stringValue, t.get("string")); - assertEquals(obj, t.get("userdata").checkuserdata()); - - // drop our strong references - obj = null; - tableValue = null; - stringValue = null; - - // Garbage collection should cause weak entries to be dropped. - System.gc(); - - // check that they are dropped - assertEquals(LuaValue.NIL, t.get("table")); - assertEquals(LuaValue.NIL, t.get("userdata")); - assertFalse("strings should not be in weak references", t.get("string").isnil()); - } - public static class MyData { public final int value; public MyData( int value ) { @@ -91,98 +36,161 @@ public class WeakTableTest extends TableTest { public boolean equals( Object o ) { return (o instanceof MyData) && ((MyData)o).value == value; } - public String toSting() { + public String toString() { return "mydata-"+value; } } - - public void testWeakKeysTable() { - LuaTable t = new WeakTable(true, false); - - LuaValue key = LuaValue.userdataOf(new MyData(111)); - LuaValue val = LuaValue.userdataOf(new MyData(222)); - - // set up the table - t.set( key, val ); - assertEquals( val, t.get(key) ); - System.gc(); - assertEquals( val, t.get(key) ); - // drop key and value references, replace them with new ones - WeakReference origkey = new WeakReference(key); - WeakReference origval = new WeakReference(val); - key = LuaValue.userdataOf(new MyData(111)); - val = LuaValue.userdataOf(new MyData(222)); - - // new key and value should be interchangeable (feature of this test class - assertEquals( key, origkey.get() ); - assertEquals( val, origval.get() ); - assertEquals( val, t.get(key) ); - assertEquals( val, t.get((LuaValue) origkey.get()) ); - assertEquals( origval.get(), t.get(key) ); - - - // value should not be reachable after gc - System.gc(); - assertEquals( null, origkey.get() ); - assertEquals( LuaValue.NIL, t.get(key) ); - - // value should also be gone after gc after access! - System.gc(); - assertEquals( null, origkey.get() ); - assertEquals( null, origval.get() ); + static void collectGarbage() { + Runtime rt = Runtime.getRuntime(); + rt.gc(); + try { + Thread.sleep(20); + rt.gc(); + Thread.sleep(20); + } catch ( Exception e ) { + e.printStackTrace(); + } + rt.gc(); } - public void testWeakKeysValuesTable() { - LuaTable t = new WeakTable(true, true); + public static class WeakValueTableTest extends WeakTableTest { + protected LuaTable new_Table() { return new WeakTable(false, true); } + protected LuaTable new_Table(int n,int m) { return new WeakTable(false, true); } + + public void testWeakValuesTable() { + LuaTable t = new_Table(); + + Object obj = new Object(); + LuaTable tableValue = new LuaTable(); + LuaString stringValue = LuaString.valueOf("this is a test"); + + t.set("table", tableValue); + t.set("userdata", LuaValue.userdataOf(obj, null)); + t.set("string", stringValue); + t.set("string2", LuaString.valueOf("another string")); + assertTrue("table must have at least 4 elements", t.getHashLength() > 4); + + // check that table can be used to get elements + assertEquals(tableValue, t.get("table")); + assertEquals(stringValue, t.get("string")); + assertEquals(obj, t.get("userdata").checkuserdata()); + + // nothing should be collected, since we have strong references here + collectGarbage(); + + // check that elements are still there + assertEquals(tableValue, t.get("table")); + assertEquals(stringValue, t.get("string")); + assertEquals(obj, t.get("userdata").checkuserdata()); + + // drop our strong references + obj = null; + tableValue = null; + stringValue = null; + + // Garbage collection should cause weak entries to be dropped. + collectGarbage(); + + // check that they are dropped + assertEquals(LuaValue.NIL, t.get("table")); + assertEquals(LuaValue.NIL, t.get("userdata")); + assertFalse("strings should not be in weak references", t.get("string").isnil()); + } + } + + public static class WeakKeyTableTest extends WeakTableTest { + protected LuaTable new_Table() { return new WeakTable(true, false); } + protected LuaTable new_Table(int n,int m) { return new WeakTable(true, false); } - LuaValue key = LuaValue.userdataOf(new MyData(111)); - LuaValue val = LuaValue.userdataOf(new MyData(222)); - LuaValue key2 = LuaValue.userdataOf(new MyData(333)); - LuaValue val2 = LuaValue.userdataOf(new MyData(444)); - LuaValue key3 = LuaValue.userdataOf(new MyData(555)); - LuaValue val3 = LuaValue.userdataOf(new MyData(666)); + public void testWeakKeysTable() { + LuaTable t = new WeakTable(true, false); + + LuaValue key = LuaValue.userdataOf(new MyData(111)); + LuaValue val = LuaValue.userdataOf(new MyData(222)); + + // set up the table + t.set( key, val ); + assertEquals( val, t.get(key) ); + System.gc(); + assertEquals( val, t.get(key) ); + + // drop key and value references, replace them with new ones + WeakReference origkey = new WeakReference(key); + WeakReference origval = new WeakReference(val); + key = LuaValue.userdataOf(new MyData(111)); + val = LuaValue.userdataOf(new MyData(222)); + + // new key and value should be interchangeable (feature of this test class + assertEquals( key, origkey.get() ); + assertEquals( val, origval.get() ); + assertEquals( val, t.get(key) ); + assertEquals( val, t.get((LuaValue) origkey.get()) ); + assertEquals( origval.get(), t.get(key) ); + + // value should not be reachable after gc + collectGarbage(); + assertEquals( null, origkey.get() ); + assertEquals( null, origval.get() ); + assertEquals( LuaValue.NIL, t.get(key) ); + } + } + + public static class WeakKeyValueTableTest extends WeakTableTest { + protected LuaTable new_Table() { return new WeakTable(true, true); } + protected LuaTable new_Table(int n,int m) { return new WeakTable(true, true); } - // set up the table - t.set( key, val ); - t.set( key2, val2 ); - t.set( key3, val3 ); - assertEquals( val, t.get(key) ); - assertEquals( val2, t.get(key2) ); - assertEquals( val3, t.get(key3) ); - System.gc(); - assertEquals( val, t.get(key) ); - assertEquals( val2, t.get(key2) ); - assertEquals( val3, t.get(key3) ); + public void testWeakKeysValuesTable() { + LuaTable t = new WeakTable(true, true); + + LuaValue key = LuaValue.userdataOf(new MyData(111)); + LuaValue val = LuaValue.userdataOf(new MyData(222)); + LuaValue key2 = LuaValue.userdataOf(new MyData(333)); + LuaValue val2 = LuaValue.userdataOf(new MyData(444)); + LuaValue key3 = LuaValue.userdataOf(new MyData(555)); + LuaValue val3 = LuaValue.userdataOf(new MyData(666)); + + // set up the table + t.set( key, val ); + t.set( key2, val2 ); + t.set( key3, val3 ); + assertEquals( val, t.get(key) ); + assertEquals( val2, t.get(key2) ); + assertEquals( val3, t.get(key3) ); + System.gc(); + assertEquals( val, t.get(key) ); + assertEquals( val2, t.get(key2) ); + assertEquals( val3, t.get(key3) ); - // drop key and value references, replace them with new ones - WeakReference origkey = new WeakReference(key); - WeakReference origval = new WeakReference(val); - WeakReference origkey2 = new WeakReference(key2); - WeakReference origval2 = new WeakReference(val2); - WeakReference origkey3 = new WeakReference(key3); - WeakReference origval3 = new WeakReference(val3); - key = LuaValue.userdataOf(new MyData(111)); - val = LuaValue.userdataOf(new MyData(222)); - key2 = LuaValue.userdataOf(new MyData(333)); - // don't drop val2, or key3 - val3 = LuaValue.userdataOf(new MyData(666)); + // drop key and value references, replace them with new ones + WeakReference origkey = new WeakReference(key); + WeakReference origval = new WeakReference(val); + WeakReference origkey2 = new WeakReference(key2); + WeakReference origval2 = new WeakReference(val2); + WeakReference origkey3 = new WeakReference(key3); + WeakReference origval3 = new WeakReference(val3); + key = LuaValue.userdataOf(new MyData(111)); + val = LuaValue.userdataOf(new MyData(222)); + key2 = LuaValue.userdataOf(new MyData(333)); + // don't drop val2, or key3 + val3 = LuaValue.userdataOf(new MyData(666)); - // no values should be reachable after gc - System.gc(); - assertEquals( null, origkey.get() ); - assertEquals( null, origval.get() ); - assertEquals( null, origkey2.get() ); - assertEquals( null, origval3.get() ); - assertEquals( LuaValue.NIL, t.get(key) ); - assertEquals( LuaValue.NIL, t.get(key2) ); - assertEquals( LuaValue.NIL, t.get(key3) ); + // no values should be reachable after gc + collectGarbage(); + assertEquals( null, origkey.get() ); + assertEquals( null, origval.get() ); + assertEquals( null, origkey2.get() ); + assertEquals( null, origval3.get() ); + assertEquals( LuaValue.NIL, t.get(key) ); + assertEquals( LuaValue.NIL, t.get(key2) ); + assertEquals( LuaValue.NIL, t.get(key3) ); - // all originals should be gone after gc, then access - val2 = null; - key3 = null; - System.gc(); - assertEquals( null, origval2.get() ); - assertEquals( null, origkey3.get() ); -} + // all originals should be gone after gc, then access + val2 = null; + key3 = null; + collectGarbage(); + assertEquals( null, origval2.get() ); + assertEquals( null, origkey3.get() ); + } + } }