From f83ad9e4ae328798e4a574831b90aacda593c07b Mon Sep 17 00:00:00 2001 From: Ian Farmer Date: Fri, 17 Oct 2008 22:02:13 +0000 Subject: [PATCH] Fix two problems with weak tables. (1) User data values would be dropped even if other references to the wrapped Java instance existed. (2) Dropped elements were never actually cleaned out of the table. --- src/core/org/luaj/vm/LTable.java | 8 ++--- src/core/org/luaj/vm/LUserData.java | 4 +++ src/core/org/luaj/vm/LValue.java | 5 +++ src/core/org/luaj/vm/LWeakTable.java | 48 ++++++++++++++++++++++++++-- version.properties | 2 +- 5 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/core/org/luaj/vm/LTable.java b/src/core/org/luaj/vm/LTable.java index 630b3d93..adedcb0f 100644 --- a/src/core/org/luaj/vm/LTable.java +++ b/src/core/org/luaj/vm/LTable.java @@ -51,7 +51,7 @@ public class LTable extends LValue { protected Object[] array; protected LValue[] hashKeys; - private Object[] hashValues; + protected Object[] hashValues; private int hashEntries; private LTable m_metatable; @@ -625,7 +625,7 @@ public class LTable extends LValue { } } - private void hashClearSlot( int i ) { + protected void hashClearSlot( int i ) { if ( hashKeys[ i ] != null ) { int j = i; @@ -651,14 +651,14 @@ public class LTable extends LValue { } } - private boolean checkLoadFactor() { + protected boolean checkLoadFactor() { // Using a load factor of 2/3 because that is easy to compute without // overflow or division. final int hashCapacity = hashKeys.length; return ( hashCapacity >> 1 ) >= ( hashCapacity - hashEntries ); } - private void rehash() { + protected void rehash() { final int oldCapacity = hashKeys.length; final int newCapacity = ( oldCapacity > 0 ) ? 2 * oldCapacity : MIN_HASH_CAPACITY; diff --git a/src/core/org/luaj/vm/LUserData.java b/src/core/org/luaj/vm/LUserData.java index adfad40c..21ac2afc 100644 --- a/src/core/org/luaj/vm/LUserData.java +++ b/src/core/org/luaj/vm/LUserData.java @@ -66,4 +66,8 @@ public class LUserData extends LValue { return lhs.equals( this ); return super.luaBinCmpUnknown( opcode, lhs ); } + + public boolean isUserData() { + return true; + } } diff --git a/src/core/org/luaj/vm/LValue.java b/src/core/org/luaj/vm/LValue.java index bcd7496a..a773e26c 100644 --- a/src/core/org/luaj/vm/LValue.java +++ b/src/core/org/luaj/vm/LValue.java @@ -346,4 +346,9 @@ public class LValue { public boolean isFunction() { return false; } + + /** Returns true if this is an LUserData */ + public boolean isUserData() { + return false; + } } diff --git a/src/core/org/luaj/vm/LWeakTable.java b/src/core/org/luaj/vm/LWeakTable.java index c9a1588d..71da6dbc 100644 --- a/src/core/org/luaj/vm/LWeakTable.java +++ b/src/core/org/luaj/vm/LWeakTable.java @@ -48,15 +48,30 @@ public class LWeakTable extends LTable { } protected LValue normalizeGet(Object val) { - if ( val != null ) + if ( val instanceof WeakReference ) val = ((WeakReference)val).get(); + else if ( val != null ) { + LUserData ud = (LUserData) val; + Object o = ((WeakReference) ud.m_instance).get(); + if ( o != null ) + val = new LUserData(o, ud.m_metatable); + else + val = LNil.NIL; + } return val==null? LNil.NIL: (LValue) val; } protected Object normalizePut(LValue val) { - return val==LNil.NIL? null: new WeakReference(val); + if ( val.isNil() ) { + return null; + } else if ( val.isUserData() ) { + LUserData ud = (LUserData) val; + return new LUserData(new WeakReference(ud.m_instance), ud.m_metatable); + } else { + return new WeakReference(val); + } } - + public boolean next(LuaState vm, LValue key, boolean indexedonly) { while ( super.next(vm, key, indexedonly) ) { if ( ! vm.isnil(-1) ) @@ -66,5 +81,32 @@ public class LWeakTable extends LTable { } return false; } + + protected void rehash() { + final LValue[] keys = this.hashKeys; + final Object[] values = this.hashValues; + final int n = hashKeys.length; + + for ( int i = 0; i < n; ++i ) { + if ( keys[i] != null && normalizeGet(values[i]).isNil() ) { + // key has dropped out, clear the slot + // It's necessary to call hashClearSlot instead of just nulling + // out the slot because the table must be left in a consistent + // state if an OutOfMemoryError occurs later in the rehash + // process. + hashClearSlot(i); + // If number of hash entries gets to zero, hashClearSlot will + // set hashKeys back to an empty array. Check for that so we + // don't have an out-of-bounds index operation. + if ( hashKeys != keys ) + break; + } + } + + // check load factor again since rehash might not be required if enough + // entries dropped out. + if ( checkLoadFactor() ) + super.rehash(); + } } diff --git a/version.properties b/version.properties index 1c31e41d..11ccf95f 100644 --- a/version.properties +++ b/version.properties @@ -1 +1 @@ -version: 0.54 +version: 0.55