Refactor table implementation.
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
51
src/core/org/luaj/vm2/Metatable.java
Normal file
51
src/core/org/luaj/vm2/Metatable.java
Normal 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);
|
||||
}
|
||||
36
src/core/org/luaj/vm2/NonTableMetatable.java
Normal file
36
src/core/org/luaj/vm2/NonTableMetatable.java
Normal 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];
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user