Refactor weak tables including proper weak key semantics and improved userdata handling.
This commit is contained in:
@@ -21,6 +21,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package org.luaj.vm2;
|
package org.luaj.vm2;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
public class LuaTable extends LuaValue {
|
public class LuaTable extends LuaValue {
|
||||||
private static final int MIN_HASH_CAPACITY = 2;
|
private static final int MIN_HASH_CAPACITY = 2;
|
||||||
private static final LuaString N = valueOf("n");
|
private static final LuaString N = valueOf("n");
|
||||||
@@ -29,7 +31,7 @@ public class LuaTable extends LuaValue {
|
|||||||
protected LuaValue[] hashKeys;
|
protected LuaValue[] hashKeys;
|
||||||
protected LuaValue[] hashValues;
|
protected LuaValue[] hashValues;
|
||||||
private int hashEntries;
|
private int hashEntries;
|
||||||
private LuaValue m_metatable;
|
protected LuaValue m_metatable;
|
||||||
|
|
||||||
public LuaTable() {
|
public LuaTable() {
|
||||||
array = NOVALS;
|
array = NOVALS;
|
||||||
@@ -47,10 +49,10 @@ public class LuaTable extends LuaValue {
|
|||||||
int nl = (lastarg!=null? lastarg.narg(): 0);
|
int nl = (lastarg!=null? lastarg.narg(): 0);
|
||||||
presize(nu+nl, nn-(nn>>1));
|
presize(nu+nl, nn-(nn>>1));
|
||||||
for ( int i=0; i<nu; i++ )
|
for ( int i=0; i<nu; i++ )
|
||||||
array[i] = unnamed[i].optvalue(null);
|
rawset(i+1,unnamed[i].optvalue(null));
|
||||||
if ( lastarg != null )
|
if ( lastarg != null )
|
||||||
for ( int i=0,n=lastarg.narg(); i<n; ++i )
|
for ( int i=1,n=lastarg.narg(); i<=n; ++i )
|
||||||
array[nu+i] = lastarg.arg(i+1).optvalue(null);
|
rawset(nu+i,lastarg.arg(i).optvalue(null));
|
||||||
for ( int i=0; i<nn; i+=2 )
|
for ( int i=0; i<nn; i+=2 )
|
||||||
if (!named[i+1].isnil())
|
if (!named[i+1].isnil())
|
||||||
rawset(named[i], named[i+1]);
|
rawset(named[i], named[i+1]);
|
||||||
@@ -69,15 +71,6 @@ public class LuaTable extends LuaValue {
|
|||||||
set(i, varargs.arg(i+nskip));
|
set(i, varargs.arg(i+nskip));
|
||||||
}
|
}
|
||||||
|
|
||||||
private 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int type() {
|
public int type() {
|
||||||
return LuaValue.TTABLE;
|
return LuaValue.TTABLE;
|
||||||
}
|
}
|
||||||
@@ -98,9 +91,18 @@ public class LuaTable extends LuaValue {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void presize( int i ) {
|
public void presize( int narray ) {
|
||||||
if ( i > array.length )
|
if ( narray > array.length )
|
||||||
array = resize( array, i );
|
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 ) {
|
private static LuaValue[] resize( LuaValue[] old, int n ) {
|
||||||
@@ -109,6 +111,14 @@ public class LuaTable extends LuaValue {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected int getArrayLength() {
|
||||||
|
return array.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getHashLength() {
|
||||||
|
return hashValues.length;
|
||||||
|
}
|
||||||
|
|
||||||
public LuaValue getmetatable() {
|
public LuaValue getmetatable() {
|
||||||
if ( m_metatable!=null )
|
if ( m_metatable!=null )
|
||||||
return m_metatable.rawget(METATABLE).optvalue(m_metatable);
|
return m_metatable.rawget(METATABLE).optvalue(m_metatable);
|
||||||
@@ -130,25 +140,11 @@ public class LuaTable extends LuaValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected LuaTable changemode(boolean weakkeys, boolean weakvalues) {
|
protected LuaTable changemode(boolean weakkeys, boolean weakvalues) {
|
||||||
if ( weakkeys || weakvalues ) {
|
if ( weakkeys || weakvalues )
|
||||||
return recreateas(weakkeys, weakvalues);
|
return new WeakTable(weakkeys, weakvalues, this);
|
||||||
}
|
|
||||||
return 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 ) {
|
public LuaValue get( int key ) {
|
||||||
LuaValue v = rawget(key);
|
LuaValue v = rawget(key);
|
||||||
return v.isnil() && m_metatable!=null? gettable(this,valueOf(key)): v;
|
return v.isnil() && m_metatable!=null? gettable(this,valueOf(key)): v;
|
||||||
@@ -234,8 +230,6 @@ public class LuaTable extends LuaValue {
|
|||||||
public LuaValue remove(int pos) {
|
public LuaValue remove(int pos) {
|
||||||
if ( pos == 0 )
|
if ( pos == 0 )
|
||||||
pos = length();
|
pos = length();
|
||||||
if ( pos < 1 || pos > array.length )
|
|
||||||
return NONE;
|
|
||||||
LuaValue v = rawget(pos);
|
LuaValue v = rawget(pos);
|
||||||
for ( LuaValue r=v; !r.isnil(); ) {
|
for ( LuaValue r=v; !r.isnil(); ) {
|
||||||
r = rawget(pos+1);
|
r = rawget(pos+1);
|
||||||
@@ -267,9 +261,9 @@ public class LuaTable extends LuaValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LuaValue getn() {
|
public LuaValue getn() {
|
||||||
for ( int n=array.length; --n>0; )
|
for ( int n=getArrayLength(); n>0; --n )
|
||||||
if ( array[n]!=null )
|
if ( !rawget(n).isnil() )
|
||||||
return LuaInteger.valueOf(n+1);
|
return LuaInteger.valueOf(n);
|
||||||
return ZERO;
|
return ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,10 +271,11 @@ public class LuaTable extends LuaValue {
|
|||||||
* Get the length of this table, as lua defines it.
|
* Get the length of this table, as lua defines it.
|
||||||
*/
|
*/
|
||||||
public int length() {
|
public int length() {
|
||||||
int n=array.length+1,m=0;
|
int a = getArrayLength();
|
||||||
|
int n = a+1,m=0;
|
||||||
while ( !rawget(n).isnil() ) {
|
while ( !rawget(n).isnil() ) {
|
||||||
m = n;
|
m = n;
|
||||||
n += array.length+hashEntries+1;
|
n += a+getHashLength()+1;
|
||||||
}
|
}
|
||||||
while ( n > m+1 ) {
|
while ( n > m+1 ) {
|
||||||
int k = (n+m) / 2;
|
int k = (n+m) / 2;
|
||||||
@@ -312,7 +307,10 @@ public class LuaTable extends LuaValue {
|
|||||||
return n;
|
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
|
* Get the next element after a particular key in the table
|
||||||
* @return key,value or nil
|
* @return key,value or nil
|
||||||
@@ -371,16 +369,13 @@ public class LuaTable extends LuaValue {
|
|||||||
* @param func function to call
|
* @param func function to call
|
||||||
*/
|
*/
|
||||||
public LuaValue foreach(LuaValue func) {
|
public LuaValue foreach(LuaValue func) {
|
||||||
LuaValue v = NIL;
|
Varargs n;
|
||||||
for ( int i=0; i<array.length; i++ )
|
LuaValue k = NIL;
|
||||||
if ( array[i] != null )
|
LuaValue v;
|
||||||
if ( !(v = func.call(LuaInteger.valueOf(i+1), array[i])).isnil() )
|
while ( !(k = ((n = next(k)).arg1())).isnil() )
|
||||||
return v;
|
if ( ! (v = func.call(k, n.arg(2))).isnil() )
|
||||||
for ( int i=0; i<hashKeys.length; i++ )
|
return v;
|
||||||
if ( hashKeys[i] != null )
|
return NIL;
|
||||||
if ( !(v = func.call(hashKeys[i], hashValues[i])).isnil() )
|
|
||||||
return v;
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -390,46 +385,15 @@ public class LuaTable extends LuaValue {
|
|||||||
* @param func
|
* @param func
|
||||||
*/
|
*/
|
||||||
public LuaValue foreachi(LuaValue func) {
|
public LuaValue foreachi(LuaValue func) {
|
||||||
LuaValue v = NIL;
|
Varargs n;
|
||||||
for ( int i=0; i<array.length && array[i]!=null; i++ )
|
LuaValue k = NIL;
|
||||||
if ( !(v = func.call(LuaInteger.valueOf(i+1), array[i])).isnil() )
|
LuaValue v;
|
||||||
|
while ( !(k = ((n = inext(k)).arg1())).isnil() )
|
||||||
|
if ( ! (v = func.call(k, n.arg(2))).isnil() )
|
||||||
return v;
|
return v;
|
||||||
return v;
|
return NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================= test hooks =================
|
|
||||||
|
|
||||||
/** Value used in testing to provide the capacity of the array part */
|
|
||||||
int arrayCapacity() {
|
|
||||||
return array.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Value used in testing to provide the capacity of the hash part */
|
|
||||||
int hashCapacity() {
|
|
||||||
return hashKeys.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Value used in testing to provide the total count of elements */
|
|
||||||
int keyCount() {
|
|
||||||
int n = 0;
|
|
||||||
for ( int i=0; i<array.length; i++ )
|
|
||||||
if ( array[i] != null )
|
|
||||||
++n;
|
|
||||||
return n + hashEntries;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Value used in testing to enumerate the keys */
|
|
||||||
public LuaValue[] keys() {
|
|
||||||
LuaValue[] vals = new LuaValue[keyCount()];
|
|
||||||
int n = 0;
|
|
||||||
for ( int i=0; i<array.length; i++ )
|
|
||||||
if ( array[i] != null )
|
|
||||||
vals[n++] = LuaInteger.valueOf(i+1);
|
|
||||||
for ( int i=0; i<hashKeys.length; i++ )
|
|
||||||
if ( hashKeys[i] != null )
|
|
||||||
vals[n++] = hashKeys[i];
|
|
||||||
return vals;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ======================= hashset =================
|
// ======================= hashset =================
|
||||||
|
|
||||||
@@ -592,4 +556,21 @@ public class LuaTable extends LuaValue {
|
|||||||
array[j] = a;
|
array[j] = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @deprecate - count via iteration instead */
|
||||||
|
public int keyCount() {
|
||||||
|
return keys().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @deprecate - use next() instead */
|
||||||
|
public LuaValue[] keys() {
|
||||||
|
Vector l = new Vector();
|
||||||
|
LuaValue k = LuaValue.NIL;
|
||||||
|
while ( true ) {
|
||||||
|
Varargs n = next(k);
|
||||||
|
if ( (k = n.arg1()).isnil() )
|
||||||
|
break;
|
||||||
|
l.add( k );
|
||||||
|
}
|
||||||
|
return (LuaValue[]) l.toArray(new LuaValue[l.size()]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ public class LuaValue extends Varargs {
|
|||||||
public static LuaValue argerror(int iarg,String msg) { throw new LuaError("bad argument #"+iarg+": "+msg); }
|
public static LuaValue argerror(int iarg,String msg) { throw new LuaError("bad argument #"+iarg+": "+msg); }
|
||||||
protected LuaValue typerror(String expected) { throw new LuaError(expected+" expected, got "+typename()); }
|
protected LuaValue typerror(String expected) { throw new LuaError(expected+" expected, got "+typename()); }
|
||||||
protected LuaValue unimplemented(String fun) { throw new LuaError("'"+fun+"' not implemented for "+typename()); }
|
protected LuaValue unimplemented(String fun) { throw new LuaError("'"+fun+"' not implemented for "+typename()); }
|
||||||
|
protected LuaValue illegal(String op,String typename) { throw new LuaError("illegal operation '"+op+"' for "+typename); }
|
||||||
protected LuaValue callerror() { throw new LuaError("attempt to call "+typename()); }
|
protected LuaValue callerror() { throw new LuaError("attempt to call "+typename()); }
|
||||||
protected LuaValue lenerror() { throw new LuaError("attempt to get length of "+typename()); }
|
protected LuaValue lenerror() { throw new LuaError("attempt to get length of "+typename()); }
|
||||||
protected LuaValue aritherror() { throw new LuaError("attempt to perform arithmetic on "+typename()); }
|
protected LuaValue aritherror() { throw new LuaError("attempt to perform arithmetic on "+typename()); }
|
||||||
@@ -316,6 +317,7 @@ public class LuaValue extends Varargs {
|
|||||||
|
|
||||||
// lua number/string conversion
|
// lua number/string conversion
|
||||||
public LuaString strvalue() { typerror("strValue"); return null; }
|
public LuaString strvalue() { typerror("strValue"); return null; }
|
||||||
|
public LuaValue strongkey() { return this; }
|
||||||
public LuaValue strongvalue() { return this; }
|
public LuaValue strongvalue() { return this; }
|
||||||
|
|
||||||
// conversion from java values
|
// conversion from java values
|
||||||
|
|||||||
@@ -23,170 +23,269 @@ package org.luaj.vm2;
|
|||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
import org.luaj.vm2.lib.TwoArgFunction;
|
||||||
|
|
||||||
public class WeakTable extends LuaTable {
|
public class WeakTable extends LuaTable {
|
||||||
|
private LuaTable backing;
|
||||||
|
private boolean weakkeys,weakvalues;
|
||||||
|
|
||||||
private final boolean weakKeys,weakValues;
|
public WeakTable(boolean weakkeys, boolean weakvalues) {
|
||||||
|
this(weakkeys, weakvalues, 0, 0);
|
||||||
WeakTable( boolean weakKeys, boolean weakValues ) {
|
}
|
||||||
this.weakKeys = weakKeys;
|
protected WeakTable(boolean weakkeys, boolean weakvalues, int narray, int nhash) {
|
||||||
this.weakValues = weakValues;
|
this.backing = new LuaTable(narray, nhash);
|
||||||
|
this.weakkeys = weakkeys;
|
||||||
|
this.weakvalues = weakvalues;
|
||||||
|
}
|
||||||
|
protected WeakTable(boolean weakkeys, boolean weakvalues, LuaTable source) {
|
||||||
|
this(weakkeys, weakvalues, source.getArrayLength(), source.getHashLength());
|
||||||
|
Varargs n;
|
||||||
|
LuaValue k = NIL;
|
||||||
|
while ( !(k = ((n = source.next(k)).arg1())).isnil() )
|
||||||
|
rawset(k, n.arg(2));
|
||||||
|
m_metatable = source.m_metatable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class WeakValue extends LuaValue {
|
public void presize( int narray ) {
|
||||||
private final WeakReference ref;
|
backing.presize(narray);
|
||||||
public WeakValue(LuaValue val) {
|
}
|
||||||
ref = new WeakReference(val);
|
|
||||||
|
public void presize(int narray, int nhash) {
|
||||||
|
backing.presize(narray, nhash);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getArrayLength() {
|
||||||
|
return backing.getArrayLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getHashLength() {
|
||||||
|
return backing.getHashLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected WeakTable changemode(boolean weakkeys, boolean weakvalues) {
|
||||||
|
this.weakkeys = weakkeys;
|
||||||
|
this.weakvalues = weakvalues;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaValue weaken( 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);
|
||||||
|
default:
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rawset( int key, LuaValue value ) {
|
||||||
|
if ( weakvalues )
|
||||||
|
value = weaken( value );
|
||||||
|
backing.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** caller must ensure key is not nil */
|
||||||
|
public void rawset( LuaValue key, LuaValue value ) {
|
||||||
|
if ( weakvalues )
|
||||||
|
value = weaken( value );
|
||||||
|
if ( weakkeys ) {
|
||||||
|
switch ( key.type() ) {
|
||||||
|
case LuaValue.TFUNCTION:
|
||||||
|
case LuaValue.TTHREAD:
|
||||||
|
case LuaValue.TTABLE:
|
||||||
|
case LuaValue.TUSERDATA:
|
||||||
|
key = value = new WeakEntry(this, key, value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
backing.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public LuaValue rawget( int key ) {
|
||||||
|
return rawget(valueOf(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LuaValue rawget( LuaValue key ) {
|
||||||
|
LuaValue v = backing.rawget(key);
|
||||||
|
if ( v.isnil() )
|
||||||
|
return NIL;
|
||||||
|
v = v.strongvalue();
|
||||||
|
if ( v.isnil() )
|
||||||
|
backing.rawset(key, NIL);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int maxn() {
|
||||||
|
return backing.maxn();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next element after a particular key in the table
|
||||||
|
* @return key,value or nil
|
||||||
|
*/
|
||||||
|
public Varargs next( LuaValue key ) {
|
||||||
|
while ( true ) {
|
||||||
|
Varargs n = backing.next(key);
|
||||||
|
LuaValue k = n.arg1();
|
||||||
|
if ( k.isnil() )
|
||||||
|
return NIL;
|
||||||
|
LuaValue ks = k.strongkey();
|
||||||
|
LuaValue vs = n.arg(2).strongvalue();
|
||||||
|
if ( ks.isnil() || vs.isnil() ) {
|
||||||
|
backing.rawset(ks, NIL);
|
||||||
|
} else {
|
||||||
|
return varargsOf(ks,vs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next element after a particular key in the
|
||||||
|
* contiguous array part of a table
|
||||||
|
* @return key,value or nil
|
||||||
|
*/
|
||||||
|
public Varargs inext(LuaValue key) {
|
||||||
|
int k = key.optint(0)+1;
|
||||||
|
LuaValue v = this.rawget(k);
|
||||||
|
return v.isnil()? NIL: varargsOf(valueOf(k),v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------- sort support -----------------------------
|
||||||
|
public void sort(final LuaValue comparator) {
|
||||||
|
backing.sort( new TwoArgFunction() {
|
||||||
|
public LuaValue call(LuaValue arg1, LuaValue arg2) {
|
||||||
|
return comparator.call( arg1.strongvalue(), arg2.strongvalue() );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
static class WeakValue extends LuaValue {
|
||||||
|
final WeakReference ref;
|
||||||
|
|
||||||
|
protected WeakValue(LuaValue value) {
|
||||||
|
ref = new WeakReference(value);
|
||||||
|
}
|
||||||
|
|
||||||
public int type() {
|
public int type() {
|
||||||
return strongvalue().type();
|
illegal("type","weak value");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String typename() {
|
public String typename() {
|
||||||
return "weakvalue";
|
illegal("typename","weak value");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "weak<"+ref.get()+">";
|
||||||
|
}
|
||||||
|
|
||||||
|
public LuaValue strongkey() {
|
||||||
|
Object o = ref.get();
|
||||||
|
return o!=null? (LuaValue)o: NIL;
|
||||||
|
}
|
||||||
|
|
||||||
public LuaValue strongvalue() {
|
public LuaValue strongvalue() {
|
||||||
Object o = ref.get();
|
Object o = ref.get();
|
||||||
return o!=null? (LuaValue)o: NIL;
|
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) {
|
public boolean eq_b(LuaValue rhs) {
|
||||||
return strongkey().eq_b(rhs);
|
Object o = ref.get();
|
||||||
}
|
return o!=null && rhs.eq_b((LuaValue)o);
|
||||||
public int hashCode() {
|
|
||||||
return strongkey().hashCode();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final class WeakUserdata extends WeakValue {
|
||||||
|
private final WeakReference ob;
|
||||||
|
private final WeakReference mt;
|
||||||
|
|
||||||
private boolean shouldWeaken( LuaValue value ) {
|
private WeakUserdata(LuaValue value) {
|
||||||
switch ( value.type() ) {
|
super(value);
|
||||||
case LuaValue.TFUNCTION:
|
ob = new WeakReference(value.touserdata());
|
||||||
case LuaValue.TTHREAD:
|
LuaValue udmt = value.getmetatable();
|
||||||
case LuaValue.TTABLE:
|
mt = udmt!=null? new WeakReference(udmt): null;
|
||||||
case LuaValue.TUSERDATA:
|
}
|
||||||
|
|
||||||
|
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 true;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private LuaValue toWeak( LuaValue value ) {
|
public Object touserdata() {
|
||||||
switch ( value.type() ) {
|
return ob.get();
|
||||||
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 LuaValue rawget(int key) {
|
static final class WeakEntry extends LuaValue {
|
||||||
LuaValue v = super.rawget(key);
|
final WeakTable table;
|
||||||
if ( v.isnil() )
|
final LuaValue weakkey;
|
||||||
return NIL;
|
final int keyhash;
|
||||||
v = v.strongvalue();
|
|
||||||
if ( v.isnil() ) {
|
|
||||||
// TODO: mark table for culling?
|
|
||||||
super.rawset(key, NIL);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LuaValue rawget(LuaValue key) {
|
private WeakEntry(WeakTable table, LuaValue key, LuaValue weakvalue) {
|
||||||
LuaValue v = super.rawget(key);
|
this.table = table;
|
||||||
if ( v.isnil() )
|
this.weakkey = table.weaken(key);
|
||||||
return NIL;
|
this.keyhash = key.hashCode();
|
||||||
v = v.strongvalue();
|
|
||||||
if ( v.isnil() ) {
|
|
||||||
// TODO: mark table for culling?
|
|
||||||
super.rawset(key, NIL);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void rawset(int key, LuaValue val) {
|
// store an association from table to value in the key's metatable
|
||||||
if ( val.isnil() || !weakValues || !shouldWeaken(val) ) {
|
LuaValue mt = key.getmetatable();
|
||||||
super.rawset(key, val);
|
if ( mt == null )
|
||||||
} else {
|
key.setmetatable(mt=new LuaTable(0,1));
|
||||||
super.rawset(key, toWeak(val));
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected LuaTable changemode(boolean k, boolean v) {
|
|
||||||
if ( k!=this.weakKeys || v!=weakValues )
|
|
||||||
return recreateas(k,v);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import junit.framework.TestSuite;
|
|||||||
|
|
||||||
import org.luaj.vm2.WeakTableTest.WeakKeyTableTest;
|
import org.luaj.vm2.WeakTableTest.WeakKeyTableTest;
|
||||||
import org.luaj.vm2.WeakTableTest.WeakKeyValueTableTest;
|
import org.luaj.vm2.WeakTableTest.WeakKeyValueTableTest;
|
||||||
|
import org.luaj.vm2.WeakTableTest.WeakValueTableTest;
|
||||||
import org.luaj.vm2.compiler.CompilerUnitTests;
|
import org.luaj.vm2.compiler.CompilerUnitTests;
|
||||||
import org.luaj.vm2.compiler.DumpLoadEndianIntTest;
|
import org.luaj.vm2.compiler.DumpLoadEndianIntTest;
|
||||||
import org.luaj.vm2.compiler.RegressionTests;
|
import org.luaj.vm2.compiler.RegressionTests;
|
||||||
@@ -52,7 +53,7 @@ public class AllTests {
|
|||||||
table.addTestSuite(TableTest.class);
|
table.addTestSuite(TableTest.class);
|
||||||
table.addTestSuite(TableArrayTest.class);
|
table.addTestSuite(TableArrayTest.class);
|
||||||
table.addTestSuite(TableHashTest.class);
|
table.addTestSuite(TableHashTest.class);
|
||||||
table.addTestSuite(WeakTableTest.class);
|
table.addTestSuite(WeakValueTableTest.class);
|
||||||
table.addTestSuite(WeakKeyTableTest.class);
|
table.addTestSuite(WeakKeyTableTest.class);
|
||||||
table.addTestSuite(WeakKeyValueTableTest.class);
|
table.addTestSuite(WeakKeyValueTableTest.class);
|
||||||
suite.addTest(table);
|
suite.addTest(table);
|
||||||
|
|||||||
@@ -57,10 +57,10 @@ public class TableArrayTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure capacities make sense
|
// Ensure capacities make sense
|
||||||
assertEquals( 0, t.hashCapacity() );
|
assertEquals( 0, t.getHashLength() );
|
||||||
|
|
||||||
assertTrue( t.arrayCapacity() >= 32 );
|
assertTrue( t.getArrayLength() >= 32 );
|
||||||
assertTrue( t.arrayCapacity() <= 64 );
|
assertTrue( t.getArrayLength() <= 64 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,8 +79,8 @@ public class TableArrayTest extends TestCase {
|
|||||||
assertEquals(LuaInteger.valueOf(i), t.get(i));
|
assertEquals(LuaInteger.valueOf(i), t.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue( t.arrayCapacity() >= 0 && t.arrayCapacity() <= 2 );
|
assertTrue( t.getArrayLength() >= 0 && t.getArrayLength() <= 2 );
|
||||||
assertTrue( t.hashCapacity() >= 4 );
|
assertTrue( t.getHashLength() >= 4 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testOutOfOrderIntegerKeyInsertion() {
|
public void testOutOfOrderIntegerKeyInsertion() {
|
||||||
@@ -96,11 +96,11 @@ public class TableArrayTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure capacities make sense
|
// Ensure capacities make sense
|
||||||
assertTrue( t.arrayCapacity() >= 0 );
|
assertTrue( t.getArrayLength() >= 0 );
|
||||||
assertTrue( t.arrayCapacity() <= 6 );
|
assertTrue( t.getArrayLength() <= 6 );
|
||||||
|
|
||||||
assertTrue( t.hashCapacity() >= 16 );
|
assertTrue( t.getHashLength() >= 16 );
|
||||||
assertTrue( t.hashCapacity() <= 64 );
|
assertTrue( t.getHashLength() <= 64 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,10 +113,10 @@ public class TableArrayTest extends TestCase {
|
|||||||
t.set( str, LuaInteger.valueOf( i ) );
|
t.set( str, LuaInteger.valueOf( i ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue( t.arrayCapacity() >= 9 ); // 1, 2, ..., 9
|
assertTrue( t.getArrayLength() >= 9 ); // 1, 2, ..., 9
|
||||||
assertTrue( t.arrayCapacity() <= 18 );
|
assertTrue( t.getArrayLength() <= 18 );
|
||||||
assertTrue( t.hashCapacity() >= 11 ); // 0, "0", "1", ..., "9"
|
assertTrue( t.getHashLength() >= 11 ); // 0, "0", "1", ..., "9"
|
||||||
assertTrue( t.hashCapacity() <= 33 );
|
assertTrue( t.getHashLength() <= 33 );
|
||||||
|
|
||||||
LuaValue[] keys = t.keys();
|
LuaValue[] keys = t.keys();
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public class TableHashTest extends TestCase {
|
|||||||
public void testSetRemove() {
|
public void testSetRemove() {
|
||||||
LuaTable t = new_Table();
|
LuaTable t = new_Table();
|
||||||
|
|
||||||
assertEquals( 0, t.hashCapacity() );
|
assertEquals( 0, t.getHashLength() );
|
||||||
assertEquals( 0, t.length() );
|
assertEquals( 0, t.length() );
|
||||||
assertEquals( 0, t.keyCount() );
|
assertEquals( 0, t.keyCount() );
|
||||||
|
|
||||||
@@ -52,13 +52,13 @@ public class TableHashTest extends TestCase {
|
|||||||
"cd", "ef", "g", "hi", "jk", "lm", "no", "pq", "rs", };
|
"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 };
|
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 ) {
|
for ( int i = 0; i < keys.length; ++i ) {
|
||||||
assertEquals( capacities[i], t.hashCapacity() );
|
assertEquals( capacities[i], t.getHashLength() );
|
||||||
String si = "Test Value! "+i;
|
String si = "Test Value! "+i;
|
||||||
t.set( keys[i], si );
|
t.set( keys[i], si );
|
||||||
assertEquals( 0, t.length() );
|
assertEquals( 0, t.length() );
|
||||||
assertEquals( i+1, t.keyCount() );
|
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 ) {
|
for ( int i = 0; i < keys.length; ++i ) {
|
||||||
LuaValue vi = LuaString.valueOf( "Test Value! "+i );
|
LuaValue vi = LuaString.valueOf( "Test Value! "+i );
|
||||||
assertEquals( vi, t.get( keys[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 ) );
|
t.set( keys[i], LuaString.valueOf( "Replacement Value! "+i ) );
|
||||||
assertEquals( 0, t.length() );
|
assertEquals( 0, t.length() );
|
||||||
assertEquals( keys.length, t.keyCount() );
|
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 ) {
|
for ( int i = 0; i < keys.length; ++i ) {
|
||||||
LuaValue vi = LuaString.valueOf( "Replacement Value! "+i );
|
LuaValue vi = LuaString.valueOf( "Replacement Value! "+i );
|
||||||
@@ -85,9 +85,9 @@ public class TableHashTest extends TestCase {
|
|||||||
assertEquals( 0, t.length() );
|
assertEquals( 0, t.length() );
|
||||||
assertEquals( keys.length-i-1, t.keyCount() );
|
assertEquals( keys.length-i-1, t.keyCount() );
|
||||||
if ( i<keys.length-1 )
|
if ( i<keys.length-1 )
|
||||||
assertEquals( capacities[keys.length], t.hashCapacity() );
|
assertEquals( capacities[keys.length], t.getHashLength() );
|
||||||
else
|
else
|
||||||
assertTrue( 0<=t.hashCapacity() );
|
assertTrue( 0<=t.getHashLength() );
|
||||||
}
|
}
|
||||||
for ( int i = 0; i < keys.length; ++i ) {
|
for ( int i = 0; i < keys.length; ++i ) {
|
||||||
assertEquals( LuaValue.NIL, t.get( keys[i] ) );
|
assertEquals( LuaValue.NIL, t.get( keys[i] ) );
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package org.luaj.vm2;
|
package org.luaj.vm2;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
@@ -35,6 +36,23 @@ public class TableTest extends TestCase {
|
|||||||
return new LuaTable(n,m);
|
return new LuaTable(n,m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int keyCount(LuaTable t) {
|
||||||
|
return keys(t).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LuaValue[] keys(LuaTable t) {
|
||||||
|
ArrayList<LuaValue> l = new ArrayList<LuaValue>();
|
||||||
|
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() {
|
public void testInOrderIntegerKeyInsertion() {
|
||||||
LuaTable t = new_Table();
|
LuaTable t = new_Table();
|
||||||
|
|
||||||
@@ -48,10 +66,10 @@ public class TableTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure capacities make sense
|
// Ensure capacities make sense
|
||||||
assertEquals( 0, t.hashCapacity() );
|
assertEquals( 0, t.getHashLength() );
|
||||||
|
|
||||||
assertTrue( t.arrayCapacity() >= 32 );
|
assertTrue( t.getArrayLength() >= 32 );
|
||||||
assertTrue( t.arrayCapacity() <= 64 );
|
assertTrue( t.getArrayLength() <= 64 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,8 +88,8 @@ public class TableTest extends TestCase {
|
|||||||
assertEquals(LuaInteger.valueOf(i), t.get(i));
|
assertEquals(LuaInteger.valueOf(i), t.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue( t.arrayCapacity() >= 0 && t.arrayCapacity() <= 2 );
|
assertTrue( t.getArrayLength() >= 0 && t.getArrayLength() <= 2 );
|
||||||
assertTrue( t.hashCapacity() >= 4 );
|
assertTrue( t.getHashLength() >= 4 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testOutOfOrderIntegerKeyInsertion() {
|
public void testOutOfOrderIntegerKeyInsertion() {
|
||||||
@@ -87,11 +105,11 @@ public class TableTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure capacities make sense
|
// Ensure capacities make sense
|
||||||
assertTrue( t.arrayCapacity() >= 0 );
|
assertTrue( t.getArrayLength() >= 0 );
|
||||||
assertTrue( t.arrayCapacity() <= 6 );
|
assertTrue( t.getArrayLength() <= 6 );
|
||||||
|
|
||||||
assertTrue( t.hashCapacity() >= 16 );
|
assertTrue( t.getHashLength() >= 16 );
|
||||||
assertTrue( t.hashCapacity() <= 64 );
|
assertTrue( t.getHashLength() <= 64 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,12 +122,12 @@ public class TableTest extends TestCase {
|
|||||||
t.set( str, LuaInteger.valueOf( i ) );
|
t.set( str, LuaInteger.valueOf( i ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue( t.arrayCapacity() >= 9 ); // 1, 2, ..., 9
|
assertTrue( t.getArrayLength() >= 9 ); // 1, 2, ..., 9
|
||||||
assertTrue( t.arrayCapacity() <= 18 );
|
assertTrue( t.getArrayLength() <= 18 );
|
||||||
assertTrue( t.hashCapacity() >= 11 ); // 0, "0", "1", ..., "9"
|
assertTrue( t.getHashLength() >= 11 ); // 0, "0", "1", ..., "9"
|
||||||
assertTrue( t.hashCapacity() <= 33 );
|
assertTrue( t.getHashLength() <= 33 );
|
||||||
|
|
||||||
LuaValue[] keys = t.keys();
|
LuaValue[] keys = keys(t);
|
||||||
|
|
||||||
int intKeys = 0;
|
int intKeys = 0;
|
||||||
int stringKeys = 0;
|
int stringKeys = 0;
|
||||||
@@ -145,7 +163,7 @@ public class TableTest extends TestCase {
|
|||||||
|
|
||||||
t.set( "test", LuaValue.valueOf("foo") );
|
t.set( "test", LuaValue.valueOf("foo") );
|
||||||
t.set( "explode", LuaValue.valueOf("explode") );
|
t.set( "explode", LuaValue.valueOf("explode") );
|
||||||
assertEquals( 2, t.keyCount() );
|
assertEquals( 2, keyCount(t) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRemove0() {
|
public void testRemove0() {
|
||||||
@@ -173,11 +191,11 @@ public class TableTest extends TestCase {
|
|||||||
t.set( 42, LuaValue.NIL );
|
t.set( 42, LuaValue.NIL );
|
||||||
t.set( new_Table(), LuaValue.NIL );
|
t.set( new_Table(), LuaValue.NIL );
|
||||||
t.set( "test", LuaValue.NIL );
|
t.set( "test", LuaValue.NIL );
|
||||||
assertEquals( 0, t.keyCount() );
|
assertEquals( 0, keyCount(t) );
|
||||||
|
|
||||||
t.set( 10, LuaInteger.valueOf( 5 ) );
|
t.set( 10, LuaInteger.valueOf( 5 ) );
|
||||||
t.set( 10, LuaValue.NIL );
|
t.set( 10, LuaValue.NIL );
|
||||||
assertEquals( 0, t.keyCount() );
|
assertEquals( 0, keyCount(t) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRemove2() {
|
public void testRemove2() {
|
||||||
@@ -185,23 +203,23 @@ public class TableTest extends TestCase {
|
|||||||
|
|
||||||
t.set( "test", LuaValue.valueOf("foo") );
|
t.set( "test", LuaValue.valueOf("foo") );
|
||||||
t.set( "string", LuaInteger.valueOf( 10 ) );
|
t.set( "string", LuaInteger.valueOf( 10 ) );
|
||||||
assertEquals( 2, t.keyCount() );
|
assertEquals( 2, keyCount(t) );
|
||||||
|
|
||||||
t.set( "string", LuaValue.NIL );
|
t.set( "string", LuaValue.NIL );
|
||||||
t.set( "three", LuaValue.valueOf( 3.14 ) );
|
t.set( "three", LuaValue.valueOf( 3.14 ) );
|
||||||
assertEquals( 2, t.keyCount() );
|
assertEquals( 2, keyCount(t) );
|
||||||
|
|
||||||
t.set( "test", LuaValue.NIL );
|
t.set( "test", LuaValue.NIL );
|
||||||
assertEquals( 1, t.keyCount() );
|
assertEquals( 1, keyCount(t) );
|
||||||
|
|
||||||
t.set( 10, LuaInteger.valueOf( 5 ) );
|
t.set( 10, LuaInteger.valueOf( 5 ) );
|
||||||
assertEquals( 2, t.keyCount() );
|
assertEquals( 2, keyCount(t) );
|
||||||
|
|
||||||
t.set( 10, LuaValue.NIL );
|
t.set( 10, LuaValue.NIL );
|
||||||
assertEquals( 1, t.keyCount() );
|
assertEquals( 1, keyCount(t) );
|
||||||
|
|
||||||
t.set( "three", LuaValue.NIL );
|
t.set( "three", LuaValue.NIL );
|
||||||
assertEquals( 0, t.keyCount() );
|
assertEquals( 0, keyCount(t) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testInOrderLuaLength() {
|
public void testInOrderLuaLength() {
|
||||||
|
|||||||
@@ -22,63 +22,8 @@
|
|||||||
package org.luaj.vm2;
|
package org.luaj.vm2;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
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 static class MyData {
|
||||||
public final int value;
|
public final int value;
|
||||||
@@ -91,98 +36,161 @@ public class WeakTableTest extends TableTest {
|
|||||||
public boolean equals( Object o ) {
|
public boolean equals( Object o ) {
|
||||||
return (o instanceof MyData) && ((MyData)o).value == value;
|
return (o instanceof MyData) && ((MyData)o).value == value;
|
||||||
}
|
}
|
||||||
public String toSting() {
|
public String toString() {
|
||||||
return "mydata-"+value;
|
return "mydata-"+value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testWeakKeysTable() {
|
static void collectGarbage() {
|
||||||
LuaTable t = new WeakTable(true, false);
|
Runtime rt = Runtime.getRuntime();
|
||||||
|
rt.gc();
|
||||||
LuaValue key = LuaValue.userdataOf(new MyData(111));
|
try {
|
||||||
LuaValue val = LuaValue.userdataOf(new MyData(222));
|
Thread.sleep(20);
|
||||||
|
rt.gc();
|
||||||
// set up the table
|
Thread.sleep(20);
|
||||||
t.set( key, val );
|
} catch ( Exception e ) {
|
||||||
assertEquals( val, t.get(key) );
|
e.printStackTrace();
|
||||||
System.gc();
|
}
|
||||||
assertEquals( val, t.get(key) );
|
rt.gc();
|
||||||
|
|
||||||
// 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() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testWeakKeysValuesTable() {
|
public static class WeakValueTableTest extends WeakTableTest {
|
||||||
LuaTable t = new WeakTable(true, true);
|
protected LuaTable new_Table() { return new WeakTable(false, true); }
|
||||||
|
protected LuaTable new_Table(int n,int m) { return new WeakTable(false, true); }
|
||||||
|
|
||||||
LuaValue key = LuaValue.userdataOf(new MyData(111));
|
public void testWeakValuesTable() {
|
||||||
LuaValue val = LuaValue.userdataOf(new MyData(222));
|
LuaTable t = new_Table();
|
||||||
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
|
Object obj = new Object();
|
||||||
t.set( key, val );
|
LuaTable tableValue = new LuaTable();
|
||||||
t.set( key2, val2 );
|
LuaString stringValue = LuaString.valueOf("this is a test");
|
||||||
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
|
t.set("table", tableValue);
|
||||||
WeakReference origkey = new WeakReference(key);
|
t.set("userdata", LuaValue.userdataOf(obj, null));
|
||||||
WeakReference origval = new WeakReference(val);
|
t.set("string", stringValue);
|
||||||
WeakReference origkey2 = new WeakReference(key2);
|
t.set("string2", LuaString.valueOf("another string"));
|
||||||
WeakReference origval2 = new WeakReference(val2);
|
assertTrue("table must have at least 4 elements", t.getHashLength() > 4);
|
||||||
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
|
// check that table can be used to get elements
|
||||||
System.gc();
|
assertEquals(tableValue, t.get("table"));
|
||||||
assertEquals( null, origkey.get() );
|
assertEquals(stringValue, t.get("string"));
|
||||||
assertEquals( null, origval.get() );
|
assertEquals(obj, t.get("userdata").checkuserdata());
|
||||||
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
|
// nothing should be collected, since we have strong references here
|
||||||
val2 = null;
|
collectGarbage();
|
||||||
key3 = null;
|
|
||||||
System.gc();
|
// check that elements are still there
|
||||||
assertEquals( null, origval2.get() );
|
assertEquals(tableValue, t.get("table"));
|
||||||
assertEquals( null, origkey3.get() );
|
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); }
|
||||||
|
|
||||||
|
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); }
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
collectGarbage();
|
||||||
|
assertEquals( null, origval2.get() );
|
||||||
|
assertEquals( null, origkey3.get() );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user