0 ) {
+ index = hashSlot( key );
+ for ( Slot slot = hash[ index ]; slot != null; slot = slot.rest() ) {
+ StrongSlot foundSlot;
+ if ( ( foundSlot = slot.find( key ) ) != null ) {
+ hash[index] = hash[index].set( foundSlot, value );
+ return;
+ }
+ }
}
- int slot = hashFindSlot( key );
- if ( hashFillSlot( slot, value ) )
- return;
- hashKeys[slot] = key;
- hashValues[slot] = value;
- if ( checkLoadFactor() )
- rehash();
+ if ( checkLoadFactor() ) {
+ if ( key.isinttype() && key.toint() > 0 ) {
+ // a rehash might make room in the array portion for this key.
+ rehash( key.toint() );
+ if ( arrayset(key.toint(), value) )
+ return;
+ } else {
+ rehash( -1 );
+ }
+ index = hashSlot( key );
+ }
+ Slot entry = ( m_metatable != null )
+ ? m_metatable.entry( key, value )
+ : defaultEntry( key, value );
+ hash[ index ] = ( hash[index] != null ) ? hash[index].add( entry ) : entry;
+ ++hashEntries;
+ }
+ }
+
+ public static int hashpow2( int hashCode, int mask ) {
+ return hashCode & mask;
+ }
+
+ public static int hashmod( int hashCode, int mask ) {
+ return ( hashCode & 0x7FFFFFFF ) % mask;
+ }
+
+ /**
+ * Find the hashtable slot index to use.
+ * @param key the key to look for
+ * @param hashMask N-1 where N is the number of hash slots (must be power of 2)
+ * @return the slot index
+ */
+ public static int hashSlot( LuaValue key, int hashMask ) {
+ switch ( key.type() ) {
+ case TNUMBER:
+ case TTABLE:
+ case TTHREAD:
+ case TLIGHTUSERDATA:
+ case TUSERDATA:
+ return hashmod( key.hashCode(), hashMask );
+ default:
+ return hashpow2( key.hashCode(), hashMask );
}
}
@@ -474,93 +514,259 @@ public class LuaTable extends LuaValue {
* @param key key to look for
* @return slot to use
*/
- public int hashFindSlot(LuaValue key) {
- int i = ( key.hashCode() & 0x7FFFFFFF ) % hashKeys.length;
-
- // This loop is guaranteed to terminate as long as we never allow the
- // table to get 100% full.
- LuaValue k;
- while ( ( k = hashKeys[i] ) != null && !k.raweq(key) ) {
- i = ( i + 1 ) % hashKeys.length;
- }
- return i;
+ private int hashSlot(LuaValue key) {
+ return hashSlot( key, hash.length - 1 );
}
- private boolean hashFillSlot( int slot, LuaValue value ) {
- hashValues[ slot ] = value;
- if ( hashKeys[ slot ] != null ) {
- return true;
- } else {
- ++hashEntries;
- return false;
- }
- }
-
private void hashRemove( LuaValue key ) {
- if ( hashKeys.length > 0 ) {
- int slot = hashFindSlot( key );
- hashClearSlot( slot );
- }
- }
-
- /**
- * Clear a particular slot in the table
- * @param i slot to clear.
- */
- protected void hashClearSlot( int i ) {
- if ( hashKeys[ i ] != null ) {
-
- int j = i;
- int n = hashKeys.length;
- while ( hashKeys[ j = ( ( j + 1 ) % n ) ] != null ) {
- final int k = ( ( hashKeys[ j ].hashCode() )& 0x7FFFFFFF ) % n;
- if ( ( j > i && ( k <= i || k > j ) ) ||
- ( j < i && ( k <= i && k > j ) ) ) {
- hashKeys[ i ] = hashKeys[ j ];
- hashValues[ i ] = hashValues[ j ];
- i = j;
+ if ( hash.length > 0 ) {
+ int index = hashSlot(key);
+ for ( Slot slot = hash[index]; slot != null; slot = slot.rest() ) {
+ StrongSlot foundSlot;
+ if ( ( foundSlot = slot.find( key ) ) != null ) {
+ hash[index] = hash[index].remove( foundSlot );
+ --hashEntries;
+ return;
}
}
-
- --hashEntries;
- hashKeys[ i ] = null;
- hashValues[ i ] = null;
-
- if ( hashEntries == 0 ) {
- hashKeys = NOVALS;
- hashValues = NOVALS;
- }
}
}
private boolean checkLoadFactor() {
- // Using a load factor of (n+1) >= 7/8 because that is easy to compute without
- // overflow or division.
- final int hashCapacity = hashKeys.length;
- return hashEntries >= (hashCapacity - (hashCapacity>>3));
+ return hashEntries >= hash.length;
}
- private void rehash() {
- final int oldCapacity = hashKeys.length;
- final int newCapacity = oldCapacity+(oldCapacity>>2)+MIN_HASH_CAPACITY;
-
- final LuaValue[] oldKeys = hashKeys;
- final LuaValue[] oldValues = hashValues;
-
- hashKeys = new LuaValue[ newCapacity ];
- hashValues = new LuaValue[ newCapacity ];
-
- for ( int i = 0; i < oldCapacity; ++i ) {
- final LuaValue k = oldKeys[i];
- if ( k != null ) {
- final LuaValue v = oldValues[i];
- final int slot = hashFindSlot( k );
- hashKeys[slot] = k;
- hashValues[slot] = v;
+ private int countHashKeys() {
+ int keys = 0;
+ for ( int i = 0; i < hash.length; ++i ) {
+ for ( Slot slot = hash[i]; slot != null; slot = slot.rest() ) {
+ if ( slot.first() != null )
+ keys++;
}
}
+ return keys;
}
-
+
+ private void dropWeakArrayValues() {
+ for ( int i = 0; i < array.length; ++i ) {
+ m_metatable.arrayget(array, i);
+ }
+ }
+
+ private int countIntKeys(int[] nums) {
+ int total = 0;
+ int i = 1;
+
+ // Count integer keys in array part
+ for ( int bit = 0; bit < 31; ++bit ) {
+ if ( i > array.length )
+ break;
+ int j = Math.min(array.length, 1 << bit);
+ int c = 0;
+ while ( i <= j ) {
+ if ( array[ i++ - 1 ] != null )
+ c++;
+ }
+ nums[bit] = c;
+ total += c;
+ }
+
+ // Count integer keys in hash part
+ for ( i = 0; i < hash.length; ++i ) {
+ for ( Slot s = hash[i]; s != null; s = s.rest() ) {
+ int k;
+ if ( ( k = s.arraykey(Integer.MAX_VALUE) ) > 0 ) {
+ nums[log2(k)]++;
+ total++;
+ }
+ }
+ }
+
+ return total;
+ }
+
+ // Compute ceil(log2(x))
+ static int log2(int x) {
+ int lg = 0;
+ x -= 1;
+ if ( x < 0 )
+ // 2^(-(2^31)) is approximately 0
+ return Integer.MIN_VALUE;
+ if ( ( x & 0xFFFF0000 ) != 0 ) {
+ lg = 16;
+ x >>>= 16;
+ }
+ if ( ( x & 0xFF00 ) != 0 ) {
+ lg += 8;
+ x >>>= 8;
+ }
+ if ( ( x & 0xF0 ) != 0 ) {
+ lg += 4;
+ x >>>= 4;
+ }
+ switch (x) {
+ case 0x0: return 0;
+ case 0x1: lg += 1; break;
+ case 0x2: lg += 2; break;
+ case 0x3: lg += 2; break;
+ case 0x4: lg += 3; break;
+ case 0x5: lg += 3; break;
+ case 0x6: lg += 3; break;
+ case 0x7: lg += 3; break;
+ case 0x8: lg += 4; break;
+ case 0x9: lg += 4; break;
+ case 0xA: lg += 4; break;
+ case 0xB: lg += 4; break;
+ case 0xC: lg += 4; break;
+ case 0xD: lg += 4; break;
+ case 0xE: lg += 4; break;
+ case 0xF: lg += 4; break;
+ }
+ return lg;
+ }
+
+ /*
+ * newKey > 0 is next key to insert
+ * newKey == 0 means number of keys not changing (__mode changed)
+ * newKey < 0 next key will go in hash part
+ */
+ private void rehash(int newKey) {
+ if ( m_metatable != null && ( m_metatable.useWeakKeys() || m_metatable.useWeakValues() )) {
+ // If this table has weak entries, hashEntries is just an upper bound.
+ hashEntries = countHashKeys();
+ if ( m_metatable.useWeakValues() ) {
+ dropWeakArrayValues();
+ }
+ }
+ int[] nums = new int[32];
+ int total = countIntKeys(nums);
+ if ( newKey > 0 ) {
+ total++;
+ nums[log2(newKey)]++;
+ }
+
+ // Choose N such that N <= sum(nums[0..log(N)]) < 2N
+ int keys = nums[0];
+ int newArraySize = 0;
+ for ( int log = 1; log < 32; ++log ) {
+ keys += nums[log];
+ if (total * 2 < 1 << log) {
+ // Not enough integer keys.
+ break;
+ } else if (keys >= (1 << (log - 1))) {
+ newArraySize = 1 << log;
+ }
+ }
+
+ final LuaValue[] oldArray = array;
+ final Slot[] oldHash = hash;
+ final LuaValue[] newArray;
+ final Slot[] newHash;
+
+ // Copy existing array entries and compute number of moving entries.
+ int movingToArray = 0;
+ if ( newKey > 0 && newKey <= newArraySize ) {
+ movingToArray--;
+ }
+ if (newArraySize != oldArray.length) {
+ newArray = new LuaValue[newArraySize];
+ if (newArraySize > oldArray.length) {
+ for (int i = log2(oldArray.length + 1), j = log2(newArraySize) + 1; i < j; ++i) {
+ movingToArray += nums[i];
+ }
+ } else if (oldArray.length > newArraySize) {
+ for (int i = log2(newArraySize + 1), j = log2(oldArray.length) + 1; i < j; ++i) {
+ movingToArray -= nums[i];
+ }
+ }
+ System.arraycopy(oldArray, 0, newArray, 0, Math.min(oldArray.length, newArraySize));
+ } else {
+ newArray = array;
+ }
+
+ final int newHashSize = hashEntries - movingToArray
+ + ((newKey < 0 || newKey > newArraySize) ? 1 : 0); // Make room for the new entry
+ final int oldCapacity = oldHash.length;
+ final int newCapacity;
+ final int newHashMask;
+
+ if (newHashSize > 0) {
+ // round up to next power of 2.
+ newCapacity = ( newHashSize < MIN_HASH_CAPACITY )
+ ? MIN_HASH_CAPACITY
+ : 1 << log2(newHashSize);
+ newHashMask = newCapacity - 1;
+ newHash = new Slot[ newCapacity ];
+ } else {
+ newCapacity = 0;
+ newHashMask = 0;
+ newHash = NOBUCKETS;
+ }
+
+ // Move hash buckets
+ for ( int i = 0; i < oldCapacity; ++i ) {
+ for ( Slot slot = oldHash[i]; slot != null; slot = slot.rest() ) {
+ int k;
+ if ( ( k = slot.arraykey( newArraySize ) ) > 0 ) {
+ StrongSlot entry = slot.first();
+ if (entry != null)
+ newArray[ k - 1 ] = entry.value();
+ } else {
+ int j = slot.keyindex( newHashMask );
+ newHash[j] = slot.relink( newHash[j] );
+ }
+ }
+ }
+
+ // Move array values into hash portion
+ for ( int i = newArraySize; i < oldArray.length; ) {
+ LuaValue v;
+ if ( ( v = oldArray[ i++ ] ) != null ) {
+ int slot = hashmod( LuaInteger.hashCode( i ), newHashMask );
+ Slot newEntry;
+ if ( m_metatable != null ) {
+ newEntry = m_metatable.entry( valueOf(i), v );
+ if ( newEntry == null )
+ continue;
+ } else {
+ newEntry = defaultEntry( valueOf(i), v );
+ }
+ newHash[ slot ] = ( newHash[slot] != null )
+ ? newHash[slot].add( newEntry ) : newEntry;
+ }
+ }
+
+ hash = newHash;
+ array = newArray;
+ hashEntries -= movingToArray;
+ }
+
+ public Slot entry( LuaValue key, LuaValue value ) {
+ return defaultEntry( key, value );
+ }
+
+ protected static boolean isLargeKey(LuaValue key) {
+ switch (key.type()) {
+ case TSTRING:
+ return key.rawlen() > LuaString.RECENT_STRINGS_MAX_LENGTH;
+ case TNUMBER:
+ case TBOOLEAN:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ protected static Entry defaultEntry(LuaValue key, LuaValue value) {
+ if ( key.isinttype() ) {
+ return new IntKeyEntry( key.toint(), value );
+ } else if (value.type() == TNUMBER) {
+ return new NumberValueEntry( key, value.todouble() );
+ } else {
+ return new NormalEntry( key, value );
+ }
+ }
+
// ----------------- sort support -----------------------------
//
// implemented heap sort from wikipedia
@@ -571,6 +777,9 @@ public class LuaTable extends LuaValue {
* @param comparator {@link LuaValue} to be called to compare elements.
*/
public void sort(LuaValue comparator) {
+ if (m_metatable != null && m_metatable.useWeakValues()) {
+ dropWeakArrayValues();
+ }
int n = array.length;
while ( n > 0 && array[n-1] == null )
--n;
@@ -605,8 +814,14 @@ public class LuaTable extends LuaValue {
}
private boolean compare(int i, int j, LuaValue cmpfunc) {
- LuaValue a = array[i];
- LuaValue b = array[j];
+ LuaValue a, b;
+ if (m_metatable == null) {
+ a = array[i];
+ b = array[j];
+ } else {
+ a = m_metatable.arrayget(array, i);
+ b = m_metatable.arrayget(array, j);
+ }
if ( a == null || b == null )
return false;
if ( ! cmpfunc.isnil() ) {
@@ -659,7 +874,7 @@ public class LuaTable extends LuaValue {
if ( this == val ) return true;
if ( m_metatable == null || !val.istable() ) return false;
LuaValue valmt = val.getmetatable();
- return valmt!=null && LuaValue.eqmtcall(this, m_metatable, val, valmt);
+ return valmt!=null && LuaValue.eqmtcall(this, m_metatable.toLuaValue(), val, valmt);
}
/** Unpack all the elements of this table */
@@ -688,4 +903,458 @@ public class LuaTable extends LuaValue {
return varargsOf(v);
}
}
+
+ /**
+ * Represents a slot in the hash table.
+ */
+ interface Slot {
+
+ /** Return hash{pow2,mod}( first().key().hashCode(), sizeMask ) */
+ int keyindex( int hashMask );
+
+ /** Return first Entry, if still present, or null. */
+ StrongSlot first();
+
+ /** Compare given key with first()'s key; return first() if equal. */
+ StrongSlot find( LuaValue key );
+
+ /**
+ * Compare given key with first()'s key; return true if equal. May
+ * return true for keys no longer present in the table.
+ */
+ boolean keyeq( LuaValue key );
+
+ /** Return rest of elements */
+ Slot rest();
+
+ /**
+ * Return first entry's key, iff it is an integer between 1 and max,
+ * inclusive, or zero otherwise.
+ */
+ int arraykey( int max );
+
+ /**
+ * Set the value of this Slot's first Entry, if possible, or return a
+ * new Slot whose first entry has the given value.
+ */
+ Slot set( StrongSlot target, LuaValue value );
+
+ /**
+ * Link the given new entry to this slot.
+ */
+ Slot add( Slot newEntry );
+
+ /**
+ * Return a Slot with the given value set to nil; must not return null
+ * for next() to behave correctly.
+ */
+ Slot remove( StrongSlot target );
+
+ /**
+ * Return a Slot with the same first key and value (if still present)
+ * and rest() equal to rest.
+ */
+ Slot relink( Slot rest );
+ }
+
+ /**
+ * Subclass of Slot guaranteed to have a strongly-referenced key and value,
+ * to support weak tables.
+ */
+ interface StrongSlot extends Slot {
+ /** Return first entry's key */
+ LuaValue key();
+
+ /** Return first entry's value */
+ LuaValue value();
+
+ /** Return varargsOf(key(), value()) or equivalent */
+ Varargs toVarargs();
+ }
+
+ private static class LinkSlot implements StrongSlot {
+ private Entry entry;
+ private Slot next;
+
+ LinkSlot( Entry entry, Slot next ) {
+ this.entry = entry;
+ this.next = next;
+ }
+
+ public LuaValue key() {
+ return entry.key();
+ }
+
+ public int keyindex( int hashMask ) {
+ return entry.keyindex( hashMask );
+ }
+
+ public LuaValue value() {
+ return entry.value();
+ }
+
+ public Varargs toVarargs() {
+ return entry.toVarargs();
+ }
+
+ public StrongSlot first() {
+ return entry;
+ }
+
+ public StrongSlot find(LuaValue key) {
+ return entry.keyeq(key) ? this : null;
+ }
+
+ public boolean keyeq(LuaValue key) {
+ return entry.keyeq(key);
+ }
+
+ public Slot rest() {
+ return next;
+ }
+
+ public int arraykey( int max ) {
+ return entry.arraykey( max );
+ }
+
+ public Slot set(StrongSlot target, LuaValue value) {
+ if ( target == this ) {
+ entry = entry.set( value );
+ return this;
+ } else {
+ return setnext(next.set( target, value ));
+ }
+ }
+
+ public Slot add( Slot entry ) {
+ return setnext(next.add( entry ));
+ }
+
+ public Slot remove( StrongSlot target ) {
+ if ( this == target ) {
+ return new DeadSlot( key(), next );
+ } else {
+ this.next = next.remove( target );
+ }
+ return this;
+ }
+
+ public Slot relink(Slot rest) {
+ // This method is (only) called during rehash, so it must not change this.next.
+ return ( rest != null ) ? new LinkSlot(entry, rest) : (Slot)entry;
+ }
+
+ // this method ensures that this.next is never set to null.
+ private Slot setnext(Slot next) {
+ if ( next != null ) {
+ this.next = next;
+ return this;
+ } else {
+ return entry;
+ }
+ }
+
+ public String toString() {
+ return entry + "; " + next;
+ }
+ }
+
+ /**
+ * Base class for regular entries.
+ *
+ *
+ * If the key may be an integer, the {@link #arraykey(int)} method must be
+ * overridden to handle that case.
+ */
+ static abstract class Entry extends Varargs implements StrongSlot {
+ public abstract LuaValue key();
+ public abstract LuaValue value();
+ abstract Entry set(LuaValue value);
+
+ public int arraykey( int max ) {
+ return 0;
+ }
+
+ public LuaValue arg(int i) {
+ switch (i) {
+ case 1: return key();
+ case 2: return value();
+ }
+ return NIL;
+ }
+
+ public int narg() {
+ return 2;
+ }
+
+ /**
+ * Subclasses should redefine as "return this;" whenever possible.
+ */
+ public Varargs toVarargs() {
+ return varargsOf(key(), value());
+ }
+
+ public LuaValue arg1() {
+ return key();
+ }
+
+ public Varargs subargs(int start) {
+ switch (start) {
+ case 1: return this;
+ case 2: return value();
+ }
+ return NONE;
+ }
+
+ public StrongSlot first() {
+ return this;
+ }
+
+ public Slot rest() {
+ return null;
+ }
+
+ public StrongSlot find(LuaValue key) {
+ return keyeq(key) ? this : null;
+ }
+
+ public Slot set(StrongSlot target, LuaValue value) {
+ return set( value );
+ }
+
+ public Slot add( Slot entry ) {
+ return new LinkSlot( this, entry );
+ }
+
+ public Slot remove(StrongSlot target) {
+ return new DeadSlot( key(), null );
+ }
+
+ public Slot relink( Slot rest ) {
+ return ( rest != null ) ? new LinkSlot( this, rest ) : (Slot)this;
+ }
+ }
+
+ static class NormalEntry extends Entry {
+ private final LuaValue key;
+ private LuaValue value;
+
+ NormalEntry( LuaValue key, LuaValue value ) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public LuaValue key() {
+ return key;
+ }
+
+ public LuaValue value() {
+ return value;
+ }
+
+ public Entry set(LuaValue value) {
+ this.value = value;
+ return this;
+ }
+
+ public Varargs toVarargs() {
+ return this;
+ }
+
+ public int keyindex( int hashMask ) {
+ return hashSlot( key, hashMask );
+ }
+
+ public boolean keyeq(LuaValue key) {
+ return key.raweq(this.key);
+ }
+ }
+
+ private static class IntKeyEntry extends Entry {
+ private final int key;
+ private LuaValue value;
+
+ IntKeyEntry(int key, LuaValue value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public LuaValue key() {
+ return valueOf( key );
+ }
+
+ public int arraykey(int max) {
+ return ( key >= 1 && key <= max ) ? key : 0;
+ }
+
+ public LuaValue value() {
+ return value;
+ }
+
+ public Entry set(LuaValue value) {
+ this.value = value;
+ return this;
+ }
+
+ public int keyindex( int mask ) {
+ return hashmod( LuaInteger.hashCode( key ), mask );
+ }
+
+ public boolean keyeq(LuaValue key) {
+ return key.raweq( this.key );
+ }
+ }
+
+ /**
+ * Entry class used with numeric values, but only when the key is not an integer.
+ */
+ private static class NumberValueEntry extends Entry {
+ private double value;
+ private final LuaValue key;
+
+ NumberValueEntry(LuaValue key, double value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public LuaValue key() {
+ return key;
+ }
+
+ public LuaValue value() {
+ return valueOf(value);
+ }
+
+ public Entry set(LuaValue value) {
+ LuaValue n = value.tonumber();
+ if ( !n.isnil() ) {
+ this.value = n.todouble();
+ return this;
+ } else {
+ return new NormalEntry( this.key, value );
+ }
+ }
+
+ public int keyindex( int mask ) {
+ return hashSlot( key, mask );
+ }
+
+ public boolean keyeq(LuaValue key) {
+ return key.raweq(this.key);
+ }
+ }
+
+ /**
+ * A Slot whose value has been set to nil. The key is kept in a weak reference so that
+ * it can be found by next().
+ */
+ private static class DeadSlot implements Slot {
+
+ private final Object key;
+ private Slot next;
+
+ private DeadSlot( LuaValue key, Slot next ) {
+ this.key = isLargeKey(key) ? new WeakReference( key ) : (Object)key;
+ this.next = next;
+ }
+
+ private LuaValue key() {
+ return (LuaValue) (key instanceof WeakReference ? ((WeakReference) key).get() : key);
+ }
+
+ public int keyindex(int hashMask) {
+ // Not needed: this entry will be dropped during rehash.
+ return 0;
+ }
+
+ public StrongSlot first() {
+ return null;
+ }
+
+ public StrongSlot find(LuaValue key) {
+ return null;
+ }
+
+ public boolean keyeq(LuaValue key) {
+ LuaValue k = key();
+ return k != null && key.raweq(k);
+ }
+
+ public Slot rest() {
+ return next;
+ }
+
+ public int arraykey(int max) {
+ return -1;
+ }
+
+ public Slot set(StrongSlot target, LuaValue value) {
+ Slot next = ( this.next != null ) ? this.next.set( target, value ) : null;
+ if ( key() != null ) {
+ // if key hasn't been garbage collected, it is still potentially a valid argument
+ // to next(), so we can't drop this entry yet.
+ this.next = next;
+ return this;
+ } else {
+ return next;
+ }
+ }
+
+ public Slot add(Slot newEntry) {
+ return ( next != null ) ? next.add(newEntry) : newEntry;
+ }
+
+ public Slot remove(StrongSlot target) {
+ if ( key() != null ) {
+ next = next.remove(target);
+ return this;
+ } else {
+ return next;
+ }
+ }
+
+ public Slot relink(Slot rest) {
+ return rest;
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append("');
+ if (next != null) {
+ buf.append("; ");
+ buf.append(next.toString());
+ }
+ return buf.toString();
+ }
+ };
+
+ private static final Slot[] NOBUCKETS = {};
+
+ // Metatable operations
+
+ public boolean useWeakKeys() {
+ return false;
+ }
+
+ public boolean useWeakValues() {
+ return false;
+ }
+
+ public LuaValue toLuaValue() {
+ return this;
+ }
+
+ public LuaValue wrap(LuaValue value) {
+ return value;
+ }
+
+ public LuaValue arrayget(LuaValue[] array, int index) {
+ return array[index];
+ }
}
diff --git a/src/core/org/luaj/vm2/LuaUserdata.java b/src/core/org/luaj/vm2/LuaUserdata.java
index 300980a1..b6f58e09 100644
--- a/src/core/org/luaj/vm2/LuaUserdata.java
+++ b/src/core/org/luaj/vm2/LuaUserdata.java
@@ -35,7 +35,7 @@ public class LuaUserdata extends LuaValue {
m_instance = obj;
m_metatable = metatable;
}
-
+
public String tojstring() {
return String.valueOf(m_instance);
}
@@ -120,7 +120,7 @@ public class LuaUserdata extends LuaValue {
}
// __eq metatag processing
- public boolean eqmt( LuaValue val ) {
+ public boolean eqmt( LuaValue val ) {
return m_metatable!=null && val.isuserdata()? LuaValue.eqmtcall(this, m_metatable, val, val.getmetatable()): false;
}
}
diff --git a/src/core/org/luaj/vm2/LuaValue.java b/src/core/org/luaj/vm2/LuaValue.java
index 4a0b4fb9..13164df5 100644
--- a/src/core/org/luaj/vm2/LuaValue.java
+++ b/src/core/org/luaj/vm2/LuaValue.java
@@ -1384,7 +1384,7 @@ public class LuaValue extends Varargs {
* @see LuaFunction#s_metatable
* @see LuaThread#s_metatable
*/
- public LuaValue getmetatable() { return null; };
+ public LuaValue getmetatable() { return null; }
/**
* Set the metatable for this {@link LuaValue}
@@ -3131,24 +3131,12 @@ public class LuaValue extends Varargs {
*/
public LuaString strvalue() { typerror("strValue"); return null; }
- /** Return the key part of this value if it is a weak table entry, or {@link NIL} if it was weak and is no longer referenced.
- * @return {@link LuaValue} key, or {@link NIL} if it was weak and is no longer referenced.
- * @see WeakTable
- */
- public LuaValue strongkey() { return strongvalue(); }
-
- /** Return this value as a strong reference, or {@link NIL} if it was weak and is no longer referenced.
- * @return {@link LuaValue} referred to, or {@link NIL} if it was weak and is no longer referenced.
+ /** Return this value as a strong reference, or null if it was weak and is no longer referenced.
+ * @return {@link LuaValue} referred to, or null if it was weak and is no longer referenced.
* @see WeakTable
*/
public LuaValue strongvalue() { return this; }
- /** Test if this is a weak reference and its value no longer is referenced.
- * @return true if this is a weak reference whose value no longer is referenced
- * @see WeakTable
- */
- public boolean isweaknil() { return false; }
-
/** Convert java boolean to a {@link LuaValue}.
*
* @param b boolean value to convert
@@ -3215,7 +3203,7 @@ public class LuaValue extends Varargs {
* @return new {@link LuaTable} instance with no values and no metatable, but preallocated for array and hashed elements.
*/
public static LuaTable tableOf(int narray, int nhash) { return new LuaTable(narray, nhash); }
-
+
/** Construct a {@link LuaTable} initialized with supplied array values.
* @param unnamedValues array of {@link LuaValue} containing the values to use in initialization
* @return new {@link LuaTable} instance with sequential elements coming from the array.
@@ -3229,7 +3217,7 @@ public class LuaValue extends Varargs {
* @return new {@link LuaTable} instance with sequential elements coming from the array and varargs.
*/
public static LuaTable listOf(LuaValue[] unnamedValues,Varargs lastarg) { return new LuaTable(null,unnamedValues,lastarg); }
-
+
/** Construct a {@link LuaTable} initialized with supplied named values.
* @param namedValues array of {@link LuaValue} containing the keys and values to use in initialization
* in order {@code {key-a, value-a, key-b, value-b, ...} }
@@ -3365,6 +3353,26 @@ public class LuaValue extends Varargs {
return h;
}
+ /** Construct a Metatable instance from the given LuaValue */
+ protected static Metatable metatableOf(LuaValue mt) {
+ if ( mt != null && mt.istable() ) {
+ LuaValue mode = mt.rawget(MODE);
+ if ( mode.isstring() ) {
+ String m = mode.tojstring();
+ boolean weakkeys = m.indexOf('k') >= 0;
+ boolean weakvalues = m.indexOf('v') >= 0;
+ if ( weakkeys || weakvalues ) {
+ return new WeakTable(weakkeys, weakvalues, mt);
+ }
+ }
+ return (LuaTable)mt;
+ } else if ( mt != null ) {
+ return new NonTableMetatable( mt );
+ } else {
+ return null;
+ }
+ }
+
/** Throw {@link LuaError} indicating index was attempted on illegal type
* @throws LuaError when called.
*/
diff --git a/src/core/org/luaj/vm2/Metatable.java b/src/core/org/luaj/vm2/Metatable.java
new file mode 100644
index 00000000..49c639b6
--- /dev/null
+++ b/src/core/org/luaj/vm2/Metatable.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Luaj.org. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ ******************************************************************************/
+package org.luaj.vm2;
+
+import org.luaj.vm2.LuaTable.Slot;
+
+/**
+ * Provides operations that depend on the __mode key of the metatable.
+ */
+interface Metatable {
+
+ /** Return whether or not this table's keys are weak. */
+ public boolean useWeakKeys();
+
+ /** Return whether or not this table's values are weak. */
+ public boolean useWeakValues();
+
+ /** Return this metatable as a LuaValue. */
+ public LuaValue toLuaValue();
+
+ /** Return an instance of Slot appropriate for the given key and value. */
+ public Slot entry( LuaValue key, LuaValue value );
+
+ /** Returns the given value wrapped in a weak reference if appropriate. */
+ public LuaValue wrap( LuaValue value );
+
+ /**
+ * Returns the value at the given index in the array, or null if it is a weak reference that
+ * has been dropped.
+ */
+ public LuaValue arrayget(LuaValue[] array, int index);
+}
diff --git a/src/core/org/luaj/vm2/NonTableMetatable.java b/src/core/org/luaj/vm2/NonTableMetatable.java
new file mode 100644
index 00000000..4cf112a8
--- /dev/null
+++ b/src/core/org/luaj/vm2/NonTableMetatable.java
@@ -0,0 +1,36 @@
+package org.luaj.vm2;
+
+import org.luaj.vm2.LuaTable.Slot;
+
+class NonTableMetatable implements Metatable {
+
+ private final LuaValue value;
+
+ public NonTableMetatable(LuaValue value) {
+ this.value = value;
+ }
+
+ public boolean useWeakKeys() {
+ return false;
+ }
+
+ public boolean useWeakValues() {
+ return false;
+ }
+
+ public LuaValue toLuaValue() {
+ return value;
+ }
+
+ public Slot entry(LuaValue key, LuaValue value) {
+ return LuaTable.defaultEntry(key, value);
+ }
+
+ public LuaValue wrap(LuaValue value) {
+ return value;
+ }
+
+ public LuaValue arrayget(LuaValue[] array, int index) {
+ return array[index];
+ }
+}
diff --git a/src/core/org/luaj/vm2/WeakTable.java b/src/core/org/luaj/vm2/WeakTable.java
index 73cb5b64..20606b37 100644
--- a/src/core/org/luaj/vm2/WeakTable.java
+++ b/src/core/org/luaj/vm2/WeakTable.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2009-2011 Luaj.org. All rights reserved.
+ * Copyright (c) 2009-2011, 2013 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,7 +23,8 @@ package org.luaj.vm2;
import java.lang.ref.WeakReference;
-import org.luaj.vm2.lib.TwoArgFunction;
+import org.luaj.vm2.LuaTable.Slot;
+import org.luaj.vm2.LuaTable.StrongSlot;
/**
* Subclass of {@link LuaTable} that provides weak key and weak value semantics.
@@ -34,79 +35,276 @@ import org.luaj.vm2.lib.TwoArgFunction;
* However, calling the constructors directly when weak tables are required from
* Java will reduce overhead.
*/
-public class WeakTable extends LuaTable {
- private boolean weakkeys,weakvalues;
-
- /**
+public class WeakTable implements Metatable {
+
+ private boolean weakkeys, weakvalues;
+ private LuaValue backing;
+
+ public static LuaTable make(boolean weakkeys, boolean weakvalues) {
+ LuaString mode;
+ if ( weakkeys && weakvalues ) {
+ mode = LuaString.valueOf("kv");
+ } else if ( weakkeys ) {
+ mode = LuaString.valueOf("k");
+ } else if ( weakvalues ) {
+ mode = LuaString.valueOf("v");
+ } else {
+ return LuaTable.tableOf();
+ }
+ LuaTable table = LuaTable.tableOf();
+ LuaTable mt = LuaTable.tableOf(new LuaValue[] { LuaValue.MODE, mode });
+ table.setmetatable(mt);
+ return table;
+ }
+
+ /**
* Construct a table with weak keys, weak values, or both
* @param weakkeys true to let the table have weak keys
* @param weakvalues true to let the table have weak values
*/
- public WeakTable(boolean weakkeys, boolean weakvalues) {
- this(weakkeys, weakvalues, 0, 0);
- }
-
- /**
- * Construct a table with weak keys, weak values, or both, and an initial capacity
- * @param weakkeys true to let the table have weak keys
- * @param weakvalues true to let the table have weak values
- * @param narray capacity of array part
- * @param nhash capacity of hash part
- */
- protected WeakTable(boolean weakkeys, boolean weakvalues, int narray, int nhash) {
- super(narray, nhash);
+ public WeakTable(boolean weakkeys, boolean weakvalues, LuaValue backing) {
this.weakkeys = weakkeys;
this.weakvalues = weakvalues;
+ this.backing = backing;
}
- /**
- * Construct a table with weak keys, weak values, or both, and a source of initial data
- * @param weakkeys true to let the table have weak keys
- * @param weakvalues true to let the table have weak values
- * @param source {@link LuaTable} containing the initial elements
- */
- 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;
- }
-
- public void presize( int narray ) {
- super.presize(narray);
+ public boolean useWeakKeys() {
+ return weakkeys;
}
- /**
- * Presize capacity of both array and hash parts.
- * @param narray capacity of array part
- * @param nhash capacity of hash part
- */
- public void presize(int narray, int nhash) {
- super.presize(narray, nhash);
- }
-
- protected int getArrayLength() {
- return super.getArrayLength();
+ public boolean useWeakValues() {
+ return weakvalues;
}
- protected int getHashLength() {
- return super.getHashLength();
+ public LuaValue toLuaValue() {
+ return backing;
}
-
- protected LuaTable changemode(boolean weakkeys, boolean weakvalues) {
- this.weakkeys = weakkeys;
- this.weakvalues = weakvalues;
- return this;
+
+ public Slot entry(LuaValue key, LuaValue value) {
+ value = value.strongvalue();
+ if ( value == null )
+ return null;
+ if ( weakkeys && !( key.isnumber() || key.isstring() || key.isboolean() )) {
+ if ( weakvalues && !( value.isnumber() || value.isstring() || value.isboolean() )) {
+ return new WeakKeyAndValueSlot( key, value, null );
+ } else {
+ return new WeakKeySlot( key, value, null );
+ }
+ }
+ if ( weakvalues && ! (value.isnumber() || value.isstring() || value.isboolean() )) {
+ return new WeakValueSlot( key, value, null );
+ }
+ return LuaTable.defaultEntry( key, value );
}
-
+
+ public static abstract class WeakSlot implements Slot {
+
+ protected Object key;
+ protected Object value;
+ protected Slot next;
+
+ protected WeakSlot(Object key, Object value, Slot next) {
+ this.key = key;
+ this.value = value;
+ this.next = next;
+ }
+
+ public abstract int keyindex( int hashMask );
+
+ public abstract Slot set(LuaValue value);
+
+ public StrongSlot first() {
+ LuaValue key = strongkey();
+ LuaValue value = strongvalue();
+ if ( key != null && value != null ) {
+ return new LuaTable.NormalEntry(key, value);
+ } else {
+ this.key = null;
+ this.value = null;
+ return null;
+ }
+ }
+
+ public StrongSlot find(LuaValue key) {
+ StrongSlot first = first();
+ return ( first != null ) ? first.find( key ) : null;
+ }
+
+ public boolean keyeq(LuaValue key) {
+ StrongSlot first = first();
+ return ( first != null ) && first.keyeq( key );
+ }
+
+ public Slot rest() {
+ return next;
+ }
+
+ public int arraykey(int max) {
+ // Integer keys can never be weak.
+ return 0;
+ }
+
+ public Slot set(StrongSlot target, LuaValue value) {
+ LuaValue key = strongkey();
+ if ( key != null && target.find( key ) != null ) {
+ return set( value );
+ } else if ( key != null ) {
+ // Our key is still good.
+ next = next.set( target, value );
+ return this;
+ } else {
+ // our key was dropped, remove ourselves from the chain.
+ return next.set( target, value );
+ }
+ }
+
+ public Slot add( Slot entry ) {
+ next = ( next != null ) ? next.add( entry ) : entry;
+ if ( strongkey() != null && strongvalue() != null ) {
+ return this;
+ } else {
+ return next;
+ }
+ }
+
+ public Slot remove( StrongSlot target ) {
+ LuaValue key = strongkey();
+ if ( key == null ) {
+ return next.remove( target );
+ } else if ( target.keyeq( key ) ) {
+ this.value = null;
+ return this;
+ } else {
+ next = next.remove( target );
+ return this;
+ }
+ }
+
+ public Slot relink( Slot rest ) {
+ if ( strongkey() != null && strongvalue() != null ) {
+ if ( rest == null && this.next == null ) {
+ return this;
+ } else {
+ return copy( rest );
+ }
+ } else {
+ return rest;
+ }
+ }
+
+ public LuaValue strongkey() {
+ return (LuaValue) key;
+ }
+
+ public LuaValue strongvalue() {
+ return (LuaValue) value;
+ }
+
+ protected abstract WeakSlot copy( Slot next );
+ }
+
+ static class WeakKeySlot extends WeakSlot {
+
+ private final int keyhash;
+
+ protected WeakKeySlot( LuaValue key, LuaValue value, Slot next ) {
+ super(weaken(key), value, next);
+ keyhash = key.hashCode();
+ }
+
+ protected WeakKeySlot( WeakKeySlot copyFrom, Slot next ) {
+ super( copyFrom.key, copyFrom.value, next );
+ this.keyhash = copyFrom.keyhash;
+ }
+
+ public int keyindex( int mask ) {
+ return LuaTable.hashmod( keyhash, mask );
+ }
+
+ public Slot set(LuaValue value) {
+ this.value = value;
+ return this;
+ }
+
+ public LuaValue strongkey() {
+ return strengthen( key );
+ }
+
+ protected WeakSlot copy( Slot rest ) {
+ return new WeakKeySlot( this, rest );
+ }
+ }
+
+ static class WeakValueSlot extends WeakSlot {
+
+ protected WeakValueSlot( LuaValue key, LuaValue value, Slot next ) {
+ super( key, weaken(value), next);
+ }
+
+ protected WeakValueSlot( WeakValueSlot copyFrom, Slot next ) {
+ super( copyFrom.key, copyFrom.value, next );
+ }
+
+ public int keyindex( int mask ) {
+ return LuaTable.hashSlot( strongkey(), mask );
+ }
+
+ public Slot set(LuaValue value) {
+ this.value = weaken(value);
+ return this;
+ }
+
+ public LuaValue strongvalue() {
+ return strengthen( value );
+ }
+
+ protected WeakSlot copy(Slot next) {
+ return new WeakValueSlot( this, next );
+ }
+ }
+
+ static class WeakKeyAndValueSlot extends WeakSlot {
+
+ private final int keyhash;
+
+ protected WeakKeyAndValueSlot( LuaValue key, LuaValue value, Slot next ) {
+ super( weaken(key), weaken(value), next );
+ keyhash = key.hashCode();
+ }
+
+ protected WeakKeyAndValueSlot(WeakKeyAndValueSlot copyFrom, Slot next) {
+ super( copyFrom.key, copyFrom.value, next );
+ keyhash = copyFrom.keyhash;
+ }
+
+ public int keyindex( int hashMask ) {
+ return LuaTable.hashmod( keyhash, hashMask );
+ }
+
+ public Slot set(LuaValue value) {
+ this.value = weaken(value);
+ return this;
+ }
+
+ public LuaValue strongkey() {
+ return strengthen( key );
+ }
+
+ public LuaValue strongvalue() {
+ return strengthen( value );
+ }
+
+ protected WeakSlot copy( Slot next ) {
+ return new WeakKeyAndValueSlot( this, next );
+ }
+ }
+
/**
* Self-sent message to convert a value to its weak counterpart
* @param value value to convert
* @return {@link LuaValue} that is a strong or weak reference, depending on type of {@code value}
*/
- LuaValue weaken( LuaValue value ) {
+ protected static LuaValue weaken( LuaValue value ) {
switch ( value.type() ) {
case LuaValue.TFUNCTION:
case LuaValue.TTHREAD:
@@ -118,108 +316,28 @@ public class WeakTable extends LuaTable {
return value;
}
}
-
- public void rawset( int key, LuaValue value ) {
- if ( weakvalues )
- value = weaken( value );
- super.rawset(key, value);
- }
-
- 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;
- }
- }
- super.rawset(key, value);
- }
-
-
- public LuaValue rawget( int key ) {
- return super.rawget(key).strongvalue();
- }
-
- public LuaValue rawget( LuaValue key ) {
- return super.rawget(key).strongvalue();
- }
-
- /** Get the hash value for a key
- * key the key to look up
- * */
- protected LuaValue hashget(LuaValue key) {
- if ( hashEntries > 0 ) {
- int i = hashFindSlot(key);
- if ( hashEntries == 0 )
- return NIL;
- LuaValue v = hashValues[i];
- return v!=null? v: NIL;
- }
- return NIL;
- }
-
-
- // override to remove values for weak keys as we search
- public int hashFindSlot(LuaValue key) {
- int i = ( key.hashCode() & 0x7FFFFFFF ) % hashKeys.length;
- LuaValue k;
- while ( ( k = hashKeys[i] ) != null ) {
- if ( k.isweaknil() ) {
- hashClearSlot(i);
- if ( hashEntries == 0 )
- return 0;
- }
- else {
- if ( k.raweq(key.strongkey()) )
- return i;
- i = ( i + 1 ) % hashKeys.length;
- }
- }
- return i;
- }
/**
- * Get the next element after a particular key in the table
- * @return key,value or nil
+ * Unwrap a LuaValue from a WeakReference and/or WeakUserdata.
+ * @param ref reference to convert
+ * @return LuaValue or null
+ * @see #weaken(LuaValue)
*/
- public Varargs next( LuaValue key ) {
- while ( true ) {
- Varargs n = super.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() ) {
- super.rawset(k, NIL);
- } else {
- return varargsOf(ks,vs);
- }
+ protected static LuaValue strengthen(Object ref) {
+ if ( ref instanceof WeakReference ) {
+ ref = ((WeakReference) ref).get();
}
- }
-
- // ----------------- sort support -----------------------------
- public void sort(final LuaValue comparator) {
- super.sort( new TwoArgFunction() {
- public LuaValue call(LuaValue arg1, LuaValue arg2) {
- return comparator.call( arg1.strongvalue(), arg2.strongvalue() );
- }
- } );
+ if ( ref instanceof WeakValue ) {
+ return ((WeakValue) ref).strongvalue();
+ }
+ return (LuaValue) ref;
}
/** Internal class to implement weak values.
* @see WeakTable
*/
static class WeakValue extends LuaValue {
- final WeakReference ref;
+ WeakReference ref;
protected WeakValue(LuaValue value) {
ref = new WeakReference(value);
@@ -234,26 +352,22 @@ public class WeakTable extends LuaTable {
illegal("typename","weak value");
return null;
}
-
+
public String toString() {
return "weak<"+ref.get()+">";
}
-
+
public LuaValue strongvalue() {
Object o = ref.get();
- return o!=null? (LuaValue)o: NIL;
+ return (LuaValue)o;
}
-
+
public boolean raweq(LuaValue rhs) {
Object o = ref.get();
return o!=null && rhs.raweq((LuaValue)o);
}
-
- public boolean isweaknil() {
- return ref.get() == null;
- }
}
-
+
/** Internal class to implement weak userdata values.
* @see WeakTable
*/
@@ -266,79 +380,34 @@ public class WeakTable extends LuaTable {
ob = new WeakReference(value.touserdata());
mt = value.getmetatable();
}
-
+
public LuaValue strongvalue() {
Object u = ref.get();
if ( u != null )
return (LuaValue) u;
Object o = ob.get();
- return o!=null? userdataOf(o,mt): NIL;
- }
-
- public boolean raweq(LuaValue rhs) {
- if ( ! rhs.isuserdata() )
- return false;
- LuaValue v = (LuaValue) ref.get();
- if ( v != null && v.raweq(rhs) )
- return true;
- return rhs.touserdata() == ob.get();
- }
-
- public boolean isweaknil() {
- return ob.get() == null || ref.get() == null;
+ if ( o != null ) {
+ LuaValue ud = LuaValue.userdataOf(o,mt);
+ ref = new WeakReference(ud);
+ return ud;
+ } else {
+ return null;
+ }
}
}
- /** Internal class to implement weak table entries.
- * @see WeakTable
- */
- static final class WeakEntry extends LuaValue {
- final LuaValue weakkey;
- LuaValue weakvalue;
- final int keyhash;
+ public LuaValue wrap(LuaValue value) {
+ return weakvalues ? weaken( value ) : value;
+ }
- private WeakEntry(WeakTable table, LuaValue key, LuaValue weakvalue) {
- this.weakkey = table.weaken(key);
- this.keyhash = key.hashCode();
- this.weakvalue = weakvalue;
- }
-
- public LuaValue strongkey() {
- return weakkey.strongvalue();
- }
-
- // when looking up the value, look in the keys metatable
- public LuaValue strongvalue() {
- LuaValue key = weakkey.strongvalue();
- if ( key.isnil() )
- return weakvalue = NIL;
- return weakvalue.strongvalue();
- }
-
- public int type() {
- return TNONE;
- }
-
- public String typename() {
- illegal("typename","weak entry");
- return null;
- }
-
- public String toString() {
- return "weak<"+weakkey.strongvalue()+","+strongvalue()+">";
- }
-
- public int hashCode() {
- return keyhash;
- }
-
- public boolean raweq(LuaValue rhs) {
- //return rhs.raweq(weakkey.strongvalue());
- return weakkey.raweq(rhs);
- }
-
- public boolean isweaknil() {
- return weakkey.isweaknil() || weakvalue.isweaknil();
+ public LuaValue arrayget(LuaValue[] array, int index) {
+ LuaValue value = array[index];
+ if (value != null) {
+ value = strengthen(value);
+ if (value == null) {
+ array[index] = null;
+ }
}
+ return value;
}
}
diff --git a/test/junit/org/luaj/vm2/TableHashTest.java b/test/junit/org/luaj/vm2/TableHashTest.java
index 1f643896..b36b84cc 100644
--- a/test/junit/org/luaj/vm2/TableHashTest.java
+++ b/test/junit/org/luaj/vm2/TableHashTest.java
@@ -50,7 +50,7 @@ public class TableHashTest extends TestCase {
String[] keys = { "abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "wxy", "z01",
"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, 2, 4, 4, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 32, 32, 32 };
for ( int i = 0; i < keys.length; ++i ) {
assertEquals( capacities[i], t.getHashLength() );
String si = "Test Value! "+i;
@@ -242,4 +242,79 @@ public class TableHashTest extends TestCase {
assertEquals( LuaValue.valueOf("bbb"), t.next(LuaValue.valueOf("aa")).arg(2) );
assertEquals( LuaValue.NIL, t.next(LuaValue.valueOf("bb")) );
}
+
+ public void testLoopWithRemoval() {
+ final LuaTable t = new_Table();
+
+ t.set( LuaValue.valueOf(1), LuaValue.valueOf("1") );
+ t.set( LuaValue.valueOf(3), LuaValue.valueOf("3") );
+ t.set( LuaValue.valueOf(8), LuaValue.valueOf("4") );
+ t.set( LuaValue.valueOf(17), LuaValue.valueOf("5") );
+ t.set( LuaValue.valueOf(26), LuaValue.valueOf("6") );
+ t.set( LuaValue.valueOf(35), LuaValue.valueOf("7") );
+ t.set( LuaValue.valueOf(42), LuaValue.valueOf("8") );
+ t.set( LuaValue.valueOf(60), LuaValue.valueOf("10") );
+ t.set( LuaValue.valueOf(63), LuaValue.valueOf("11") );
+
+ Varargs entry = t.next(LuaValue.NIL);
+ while ( !entry.isnil(1) ) {
+ LuaValue k = entry.arg1();
+ LuaValue v = entry.arg(2);
+ if ( ( k.toint() & 1 ) == 0 ) {
+ t.set( k, LuaValue.NIL );
+ }
+ entry = t.next(k);
+ }
+
+ int numEntries = 0;
+ entry = t.next(LuaValue.NIL);
+ while ( !entry.isnil(1) ) {
+ LuaValue k = entry.arg1();
+ // Only odd keys should remain
+ assertTrue( ( k.toint() & 1 ) == 1 );
+ numEntries++;
+ entry = t.next(k);
+ }
+ assertEquals( 5, numEntries );
+ }
+
+ public void testLoopWithRemovalAndSet() {
+ final LuaTable t = new_Table();
+
+ t.set( LuaValue.valueOf(1), LuaValue.valueOf("1") );
+ t.set( LuaValue.valueOf(3), LuaValue.valueOf("3") );
+ t.set( LuaValue.valueOf(8), LuaValue.valueOf("4") );
+ t.set( LuaValue.valueOf(17), LuaValue.valueOf("5") );
+ t.set( LuaValue.valueOf(26), LuaValue.valueOf("6") );
+ t.set( LuaValue.valueOf(35), LuaValue.valueOf("7") );
+ t.set( LuaValue.valueOf(42), LuaValue.valueOf("8") );
+ t.set( LuaValue.valueOf(60), LuaValue.valueOf("10") );
+ t.set( LuaValue.valueOf(63), LuaValue.valueOf("11") );
+
+ Varargs entry = t.next(LuaValue.NIL);
+ Varargs entry2 = entry;
+ while ( !entry.isnil(1) ) {
+ LuaValue k = entry.arg1();
+ LuaValue v = entry.arg(2);
+ if ( ( k.toint() & 1 ) == 0 ) {
+ t.set( k, LuaValue.NIL );
+ } else {
+ t.set( k, v.tonumber() );
+ entry2 = t.next(entry2.arg1());
+ }
+ entry = t.next(k);
+ }
+
+ int numEntries = 0;
+ entry = t.next(LuaValue.NIL);
+ while ( !entry.isnil(1) ) {
+ LuaValue k = entry.arg1();
+ // Only odd keys should remain
+ assertTrue( ( k.toint() & 1 ) == 1 );
+ assertTrue( entry.arg(2).type() == LuaValue.TNUMBER );
+ numEntries++;
+ entry = t.next(k);
+ }
+ assertEquals( 5, numEntries );
+ }
}
diff --git a/test/junit/org/luaj/vm2/TableTest.java b/test/junit/org/luaj/vm2/TableTest.java
index 785981fa..5f33496b 100644
--- a/test/junit/org/luaj/vm2/TableTest.java
+++ b/test/junit/org/luaj/vm2/TableTest.java
@@ -37,7 +37,7 @@ public class TableTest extends TestCase {
}
private int keyCount(LuaTable t) {
- return keys(t).length;
+ return keys(t).length;
}
private LuaValue[] keys(LuaTable t) {
@@ -88,8 +88,9 @@ public class TableTest extends TestCase {
assertEquals(LuaInteger.valueOf(i), t.get(i));
}
- assertTrue( t.getArrayLength() >= 0 && t.getArrayLength() <= 2 );
- assertTrue( t.getHashLength() >= 4 );
+ assertTrue( t.getArrayLength() >= 3 );
+ assertTrue( t.getArrayLength() <= 12 );
+ assertTrue( t.getHashLength() <= 3 );
}
public void testOutOfOrderIntegerKeyInsertion() {
@@ -105,12 +106,8 @@ public class TableTest extends TestCase {
}
// Ensure capacities make sense
- assertTrue( t.getArrayLength() >= 0 );
- assertTrue( t.getArrayLength() <= 6 );
-
- assertTrue( t.getHashLength() >= 16 );
- assertTrue( t.getHashLength() <= 64 );
-
+ assertEquals( 32, t.getArrayLength() );
+ assertEquals( 0, t.getHashLength() );
}
public void testStringAndIntegerKeys() {
@@ -122,8 +119,8 @@ public class TableTest extends TestCase {
t.set( str, LuaInteger.valueOf( i ) );
}
- assertTrue( t.getArrayLength() >= 9 ); // 1, 2, ..., 9
- assertTrue( t.getArrayLength() <= 18 );
+ assertTrue( t.getArrayLength() >= 8 ); // 1, 2, ..., 9
+ assertTrue( t.getArrayLength() <= 16 );
assertTrue( t.getHashLength() >= 11 ); // 0, "0", "1", ..., "9"
assertTrue( t.getHashLength() <= 33 );
@@ -222,6 +219,41 @@ public class TableTest extends TestCase {
assertEquals( 0, keyCount(t) );
}
+ public void testShrinkNonPowerOfTwoArray() {
+ LuaTable t = new_Table(6, 2);
+
+ t.set(1, "one");
+ t.set(2, "two");
+ t.set(3, "three");
+ t.set(4, "four");
+ t.set(5, "five");
+ t.set(6, "six");
+
+ t.set("aa", "aaa");
+ t.set("bb", "bbb");
+
+ t.set(3, LuaValue.NIL);
+ t.set(4, LuaValue.NIL);
+ t.set(6, LuaValue.NIL);
+
+ t.set("cc", "ccc");
+ t.set("dd", "ddd");
+
+ assertEquals(4, t.getArrayLength());
+ assertTrue(t.getHashLength() < 10);
+ assertEquals(5, t.hashEntries);
+ assertEquals("one", t.get(1).tojstring());
+ assertEquals("two", t.get(2).tojstring());
+ assertEquals(LuaValue.NIL, t.get(3));
+ assertEquals(LuaValue.NIL, t.get(4));
+ assertEquals("five", t.get(5).tojstring());
+ assertEquals(LuaValue.NIL, t.get(6));
+ assertEquals("aaa", t.get("aa").tojstring());
+ assertEquals("bbb", t.get("bb").tojstring());
+ assertEquals("ccc", t.get("cc").tojstring());
+ assertEquals("ddd", t.get("dd").tojstring());
+ }
+
public void testInOrderLuaLength() {
LuaTable t = new_Table();
@@ -350,5 +382,4 @@ public class TableTest extends TestCase {
compareLists(t,v);
}
}
-
}
diff --git a/test/junit/org/luaj/vm2/WeakTableTest.java b/test/junit/org/luaj/vm2/WeakTableTest.java
index d6f90f91..92eca050 100644
--- a/test/junit/org/luaj/vm2/WeakTableTest.java
+++ b/test/junit/org/luaj/vm2/WeakTableTest.java
@@ -55,8 +55,8 @@ abstract public class WeakTableTest extends TableTest {
}
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); }
+ protected LuaTable new_Table() { return WeakTable.make(false, true); }
+ protected LuaTable new_Table(int n,int m) { return WeakTable.make(false, true); }
public void testWeakValuesTable() {
LuaTable t = new_Table();
@@ -64,17 +64,21 @@ abstract public class WeakTableTest extends TableTest {
Object obj = new Object();
LuaTable tableValue = new LuaTable();
LuaString stringValue = LuaString.valueOf("this is a test");
+ LuaTable tableValue2 = new LuaTable();
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);
+ t.set("string2", LuaValue.valueOf("another string"));
+ t.set(1, tableValue2);
+ assertTrue("table must have at least 4 elements", t.getHashLength() >= 4);
+ assertTrue("array part must have 1 element", t.getArrayLength() >= 1);
// 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());
+ assertEquals(tableValue2, t.get(1));
// nothing should be collected, since we have strong references here
collectGarbage();
@@ -83,10 +87,12 @@ abstract public class WeakTableTest extends TableTest {
assertEquals(tableValue, t.get("table"));
assertEquals(stringValue, t.get("string"));
assertEquals(obj, t.get("userdata").checkuserdata());
+ assertEquals(tableValue2, t.get(1));
// drop our strong references
obj = null;
tableValue = null;
+ tableValue2 = null;
stringValue = null;
// Garbage collection should cause weak entries to be dropped.
@@ -95,16 +101,17 @@ abstract public class WeakTableTest extends TableTest {
// check that they are dropped
assertEquals(LuaValue.NIL, t.get("table"));
assertEquals(LuaValue.NIL, t.get("userdata"));
+ assertEquals(LuaValue.NIL, t.get(1));
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); }
+ protected LuaTable new_Table() { return WeakTable.make(true, false); }
+ protected LuaTable new_Table(int n,int m) { return WeakTable.make(true, false); }
public void testWeakKeysTable() {
- LuaTable t = new WeakTable(true, false);
+ LuaTable t = WeakTable.make(true, false);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
@@ -137,7 +144,7 @@ abstract public class WeakTableTest extends TableTest {
}
public void testNext() {
- LuaTable t = new WeakTable(true, true);
+ LuaTable t = WeakTable.make(true, true);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
@@ -167,11 +174,11 @@ abstract public class WeakTableTest extends TableTest {
}
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); }
-
+ protected LuaTable new_Table() { return WeakTable.make(true, true); }
+ protected LuaTable new_Table(int n,int m) { return WeakTable.make(true, true); }
+
public void testWeakKeysValuesTable() {
- LuaTable t = new WeakTable(true, true);
+ LuaTable t = WeakTable.make(true, true);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
@@ -224,7 +231,7 @@ abstract public class WeakTableTest extends TableTest {
}
public void testReplace() {
- LuaTable t = new WeakTable(true, true);
+ LuaTable t = WeakTable.make(true, true);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));