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

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

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

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

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,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;
}
}