Refactor table implementation.

This commit is contained in:
Ian Farmer
2013-07-05 06:24:46 +00:00
parent 49fc8ec7ec
commit f3aeb69d30
11 changed files with 1387 additions and 432 deletions

View File

@@ -71,9 +71,10 @@ It also includes miscellaneous improvements over luaj 2.0.x:
<li>Better coroutine-related garbage collection.
<li>Better debug reporting when using closures.
<li>Line numbers in parse syntax tree.
<li>More compatible table behavior.
</ul>
<h3>Luaj 2.0.x</h3>
Support for luaj 5.1.x features, plus:
Support for lua 5.1.x features, plus:
<ul>
<li>Support for compiling lua source code into Java source code.
<li>Support for compiling lua bytecode directly into Java bytecode.
@@ -82,7 +83,7 @@ Support for luaj 5.1.x features, plus:
<li>Implementation of weak keys and values, and all metatags.
</ul>
<h3>Luaj 1.0.x</h3>
Support for most luaj 5.1.x features.
Support for most lua 5.1.x features.
<h2>Performance</h2>
Good performance is a major goal of luaj.
@@ -854,6 +855,10 @@ Files are no longer hosted at LuaForge.
<li>Fix bug in luajava overload resolution.</li>
<li>Fix luastring bug where parsing did not check for overflow.</li>
<li>Fix luastring bug where circular dependency randomly caused NullPointerException.</li>
<li>Major refactor of table implementation.</li>
<li>Improved behavior of next() (fixes issue #7).</li>
<li>Existing tables can now be made weak (fixes issue #16).</li>
<li>More compatible allocation of table entries in array vs. hash (fixes issue #8).</li>
</ul></td></tr>
</table></td></tr></table>

View File

@@ -126,6 +126,10 @@ public class LuaInteger extends LuaNumber {
return v;
}
public static int hashCode(int x) {
return x;
}
// unary operators
public LuaValue neg() { return valueOf(-(long)v); }

File diff suppressed because it is too large Load Diff

View File

@@ -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
@@ -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.
*/

View File

@@ -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);
}

View File

@@ -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];
}
}

View File

@@ -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,71 +35,268 @@ 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 {
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 boolean useWeakKeys() {
return weakkeys;
}
public void presize( int narray ) {
super.presize(narray);
public boolean useWeakValues() {
return weakvalues;
}
/**
* 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);
public LuaValue toLuaValue() {
return backing;
}
protected int getArrayLength() {
return super.getArrayLength();
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 );
}
protected int getHashLength() {
return super.getHashLength();
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;
}
protected LuaTable changemode(boolean weakkeys, boolean weakvalues) {
this.weakkeys = weakkeys;
this.weakvalues = weakvalues;
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 );
}
}
/**
@@ -106,7 +304,7 @@ public class WeakTable extends LuaTable {
* @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:
@@ -119,107 +317,27 @@ public class WeakTable extends LuaTable {
}
}
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();
}
if ( ref instanceof WeakValue ) {
return ((WeakValue) ref).strongvalue();
}
}
// ----------------- 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() );
}
} );
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);
@@ -241,17 +359,13 @@ public class WeakTable extends LuaTable {
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.
@@ -272,73 +386,28 @@ public class WeakTable extends LuaTable {
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;
}
}
/** Internal class to implement weak table entries.
* @see WeakTable
*/
static final class WeakEntry extends LuaValue {
final LuaValue weakkey;
LuaValue weakvalue;
final int keyhash;
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");
if ( o != null ) {
LuaValue ud = LuaValue.userdataOf(o,mt);
ref = new WeakReference(ud);
return ud;
} else {
return null;
}
public String toString() {
return "weak<"+weakkey.strongvalue()+","+strongvalue()+">";
}
}
public int hashCode() {
return keyhash;
public LuaValue wrap(LuaValue value) {
return weakvalues ? weaken( value ) : value;
}
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;
}
}

View File

@@ -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 );
}
}

View File

@@ -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);
}
}
}

View File

@@ -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));