diff --git a/src/core/org/luaj/compiler/DumpState.java b/src/core/org/luaj/compiler/DumpState.java index 7acad0c0..7337f8c8 100644 --- a/src/core/org/luaj/compiler/DumpState.java +++ b/src/core/org/luaj/compiler/DumpState.java @@ -119,7 +119,7 @@ public class DumpState { dumpInt(n); for (i = 0; i < n; i++) { final LValue o = f.k[i]; - if (o == LNil.NIL) { + if (o.isNil()) { writer.write(Lua.LUA_TNIL); // do nothing more } else if (o instanceof LBoolean) { diff --git a/src/core/org/luaj/lib/BaseLib.java b/src/core/org/luaj/lib/BaseLib.java index 9fb8dc83..bc6600c0 100644 --- a/src/core/org/luaj/lib/BaseLib.java +++ b/src/core/org/luaj/lib/BaseLib.java @@ -55,6 +55,7 @@ public class BaseLib extends LFunction { "tostring", "unpack", "next", + "_inext", // not public }; private static final int INSTALL = 0; @@ -81,10 +82,16 @@ public class BaseLib extends LFunction { private static final int TOSTRING = 21; private static final int UNPACK = 22; private static final int NEXT = 23; + private static final int INEXT = 24; + + private static LFunction next; + private static LFunction inext; public static void install(LTable globals) { - for ( int i=1; i 0 && key.isInteger() ) { final int index = key.toJavaInt() - 1; if ( index >= 0 && index < m_vector.length ) { - final LValue v = m_vector[index]; - return v != LNil.NIL; + return ! m_vector[index].isNil(); } } if ( m_hashKeys == null ) @@ -243,7 +244,7 @@ public class LTable extends LValue { public void luaGetTable(LuaState vm, LValue table, LValue key) { LValue v = get(key); - if ( v == LNil.NIL && m_metatable != null ) { + if ( v.isNil() && m_metatable != null ) { super.luaGetTable( vm, table, key ); } else { vm.pushlvalue(v); @@ -264,8 +265,8 @@ public class LTable extends LValue { */ public int luaLength() { for ( int i = Math.max( 0, m_arrayEntries-1 ); i < m_vector.length; ++i ) { - if ( m_vector[i] != LNil.NIL && - ( i+1 == m_vector.length || m_vector[i+1] == LNil.NIL ) ) { + if ( ! m_vector[i].isNil() && + ( i+1 == m_vector.length || m_vector[i+1].isNil() ) ) { return i+1; } } @@ -278,15 +279,24 @@ public class LTable extends LValue { } /** Valid for tables */ - public void luaSetMetatable(LValue metatable) { - if ( m_metatable != null && m_metatable.containsKey(TM_METATABLE) ) + public LValue luaSetMetatable(LValue metatable) { + if ( m_metatable != null && m_metatable.containsKey(TM_METATABLE) ) throw new LuaErrorException("cannot change a protected metatable"); - if ( metatable == null || metatable == LNil.NIL ) + if ( metatable == null || metatable.isNil() ) this.m_metatable = null; - else if ( metatable.luaGetType() == Lua.LUA_TTABLE ) - this.m_metatable = (LTable) metatable; + else if ( metatable.luaGetType() == Lua.LUA_TTABLE ) { + LTable t = (LTable) metatable; + LValue m = t.get(TM_MODE); + if ( "v".equals(m.toJavaString()) ) { + LTable n = new LWeakTable(this); + n.m_metatable = t; + return n; + } + this.m_metatable = t; + } else throw new LuaErrorException("nil or table expected, got "+metatable.luaGetTypeName()); + return null; } public String toJavaString() { @@ -296,47 +306,6 @@ public class LTable extends LValue { public int luaGetType() { return Lua.LUA_TTABLE; } - - /** Valid for tables */ - public LValue luaPairs(boolean isPairs) { - return new LTableIterator(isPairs); - } - - /** Iterator for tables */ - private final class LTableIterator extends LFunction { - private int arrayIndex; - private int hashIndex; - private final boolean isPairs; - - private LTableIterator(boolean isPairs) { - this.arrayIndex = 0; - this.hashIndex = 0; - this.isPairs = isPairs; - } - - // perform a lua call - public boolean luaStackCall(LuaState vm) { - vm.resettop(); - int i; - while ( ( i = arrayIndex++ ) < m_vector.length ) { - if ( m_vector[i] != LNil.NIL ) { - vm.pushinteger( arrayIndex ); - vm.pushlvalue( m_vector[ i ] ); - return false; - } - } - if ( isPairs && (m_hashKeys != null) ) { - while ( ( i = hashIndex++ ) < m_hashKeys.length ) { - if ( m_hashKeys[i] != null ) { - vm.pushlvalue( m_hashKeys[i] ); - vm.pushlvalue( m_hashValues[i] ); - return false; - } - } - } - return false; - } - } /** * Helper method to get all the keys in this table in an array. Meant to be @@ -349,7 +318,7 @@ public class LTable extends LValue { int out = 0; for ( int i = 0; i < m_vector.length; ++i ) { - if ( m_vector[ i ] != LNil.NIL ) { + if ( ! m_vector[ i ].isNil() ) { keys[ out++ ] = LInteger.valueOf( i + 1 ); } } @@ -365,11 +334,11 @@ public class LTable extends LValue { } /** Remove the value in the table with the given integer key. */ - private void remove( int key ) { + protected void remove( int key ) { if ( key > 0 ) { final int index = key - 1; if ( index < m_vector.length ) { - if ( m_vector[ index ] != LNil.NIL ) { + if ( ! m_vector[ index ].isNil() ) { m_vector[ index ] = LNil.NIL; --m_arrayEntries; } @@ -383,7 +352,7 @@ public class LTable extends LValue { } } - private void remove( LValue key ) { + protected void remove( LValue key ) { if ( m_hashKeys != null ) { int slot = findSlot( key ); clearSlot( slot ); @@ -522,7 +491,7 @@ public class LTable extends LValue { } else { for ( int i = newCapacity; i < oldCapacity; ++i ) { LValue v = m_vector[i]; - if ( v != LNil.NIL ) { + if ( ! v.isNil() ) { if (checkLoadFactor()) rehash(); final int slot = findSlot( i+1 ); @@ -557,7 +526,7 @@ public class LTable extends LValue { final int index = Math.max(0,pos==0? m_arrayEntries: pos-1); if ( m_arrayEntries + 1 > m_vector.length ) resize( ( m_arrayEntries + 1 ) * 2 ); - if ( m_vector[index] != LNil.NIL ) { + if ( ! m_vector[index].isNil() ) { System.arraycopy(m_vector, index, m_vector, index+1, m_vector.length-1-index); } m_vector[index] = value; @@ -572,7 +541,7 @@ public class LTable extends LValue { public LValue luaRemovePos(int pos) { if ( pos > m_arrayEntries ) { LValue val = get( pos ); - if ( val != LNil.NIL ) + if ( ! val.isNil() ) put( pos, LNil.NIL ); return val; } else { @@ -592,7 +561,7 @@ public class LTable extends LValue { LValue result = LInteger.valueOf(0); for ( int i = m_vector.length - 1; i >= 0; i-- ) { - if ( m_vector[i] != LNil.NIL ) { + if ( ! m_vector[i].isNil() ) { result = LInteger.valueOf(i + 1); break; } @@ -648,7 +617,7 @@ public class LTable extends LValue { } private boolean compare(int i, int j, LuaState vm, LValue cmpfunc) { - if ( cmpfunc != LNil.NIL ) { + if ( ! cmpfunc.isNil() ) { vm.pushlvalue(cmpfunc); vm.pushlvalue(m_vector[i]); vm.pushlvalue(m_vector[j]); @@ -670,65 +639,77 @@ public class LTable extends LValue { /** * Leave key,value pair on top, or nil if at end of list. * @param vm the LuaState to leave the values on + * @param indexedonly TODO * @param index index to start search + * @return true if next exists, false if at end of list */ - public void next(LuaState vm, LValue key ) { + public boolean next(LuaState vm, LValue key, boolean indexedonly ) { - // find place to start looking - int start = nextKey2StartIndex( vm, key ); + int n = (m_vector != null? m_vector.length: 0); + int i = findindex(key, n, indexedonly); + if ( i == INVALID_KEY_TO_NEXT ) + vm.error( "invalid key to 'next'" ); - // look in vector part - int n = m_vector.length; - if ( start < n ) { - for ( int i=start; i 0 && key.isInteger() ) { - final int index = key.toJavaInt() - 1; - if ( index >= 0 && index < n ) { - if ( m_vector[index] == LNil.NIL ) - vm.error( "invalid key to 'next'" ); - return index + 1; - } - } - - if ( m_hashKeys == null ) - vm.error( "invalid key to 'next'" ); - - int slot = findSlot( key ); - if ( m_hashKeys[slot] == null ) - vm.error( "invalid key to 'next'" ); - - return n + slot + 1; + return false; } + private int findindex (LValue key, int n, boolean indexedonly) { + + // first iteration + if ( key.isNil() ) + return -1; + + // is `key' inside array part? + if ( key.isInteger() ) { + int i = key.toJavaInt(); + if ( (0 < i) && (i <= n) ) { + if ( m_vector[i-1] == LNil.NIL ) + return INVALID_KEY_TO_NEXT; + return i-1; + } + } + + // vector only? + if ( indexedonly ) + return n; + + if ( m_hashKeys == null ) + return INVALID_KEY_TO_NEXT; + + // find slot + int slot = findSlot(key); + if ( m_hashKeys[slot] == null ) + return INVALID_KEY_TO_NEXT; + + return n + slot; + } + /** * Executes the given f over all elements of table. For each element, f is @@ -738,35 +719,26 @@ public class LTable extends LValue { * * @param vm * @param function - * @param isforeachi is a table.foreachi() call, not a table.foreach() call + * @param indexedonly is a table.foreachi() call, not a table.foreach() call * @return */ - public LValue foreach(LuaState vm, LFunction function, boolean isforeachi) { - for ( int i = 0; i < m_vector.length; ++i ) { - if ( m_vector[ i ] != LNil.NIL ) { - if ( foreachitem( vm, function, LInteger.valueOf(i+1), m_vector[i] ) ) - return vm.topointer(1); - } - } - - if ( (! isforeachi) && m_hashKeys != null ) { - for ( int i = 0; i < m_hashKeys.length; ++i ) { - if ( m_hashKeys[ i ] != null ) { - if ( foreachitem( vm, function, m_hashKeys[i], m_hashValues[i] ) ) - return vm.topointer(1); - } - } - } - - return LNil.NIL; - } + public LValue foreach(LuaState vm, LFunction function, boolean indexedonly) { - private static boolean foreachitem(LuaState vm, LFunction f, LValue key, LValue value) { - vm.resettop(); - vm.pushlvalue( f ); - vm.pushlvalue( key ); - vm.pushlvalue( value ); - vm.call(2, 1); - return ! vm.isnil(1); + LValue key = LNil.NIL; + while ( true ) { + // push function onto stack + vm.resettop(); + vm.pushlvalue(function); + + // get next value + if ( ! next(vm,key,indexedonly) ) + return LNil.NIL; + key = vm.topointer(2); + + // call function + vm.call(2, 1); + if ( ! vm.isnil(-1) ) + return vm.poplvalue(); + } } } diff --git a/src/core/org/luaj/vm/LValue.java b/src/core/org/luaj/vm/LValue.java index 60d44f1f..b40354af 100644 --- a/src/core/org/luaj/vm/LValue.java +++ b/src/core/org/luaj/vm/LValue.java @@ -34,6 +34,9 @@ public class LValue { /** Metatable tag for intercepting table sets */ public static final LString TM_METATABLE = new LString("__metatable"); + /** Metatable tag for setting table mode */ + public static final LString TM_MODE = new LString("__mode"); + protected void conversionError(String target) { throw new LuaErrorException( "bad conversion: "+luaGetTypeName()+" to "+target ); } @@ -59,6 +62,11 @@ public class LValue { return false; } + /** Return true if this value is LNil.NIL, false otherwise */ + public boolean isNil() { + return false; + } + // perform a lua call, return true if the call is to a lua function, false // if it ran to completion. public boolean luaStackCall(LuaState vm) { @@ -124,7 +132,7 @@ public class LValue { LTable mt = luaGetMetatable(); if ( mt != null ) { LValue event = mt.get( TM_NEWINDEX ); - if ( event != null && event != LNil.NIL ) { + if ( event != null && ! event.isNil() ) { event.luaSetTable( vm, table, key, val ); return; } @@ -141,7 +149,7 @@ public class LValue { LTable mt = luaGetMetatable(); if ( mt != null ) { LValue event = mt.get( TM_INDEX ); - if ( event != null && event != LNil.NIL ) { + if ( event != null && ! event.isNil() ) { event.luaGetTable( vm, table, key ); return; } @@ -184,13 +192,14 @@ public class LValue { return null; } - /** Valid for tables */ - public void luaSetMetatable(LValue metatable) { + /** Valid for tables + * @return TODO*/ + public LValue luaSetMetatable(LValue metatable) { throw new LuaErrorException( "bad argument #1 to 'setmetatable' (table expected, got "+metatable.luaGetTypeName()+")"); } /** Valid for all types: return the int value identifying the type of this value */ - public abstract int luaGetType(); + abstract public int luaGetType(); /** Valid for all types: return the type of this value as an LString */ @@ -309,4 +318,8 @@ public class LValue { return LNil.NIL; } + /** Dereference a potentially weak reference, and return the value */ + public LValue toStrongReference() { + return this; + } } diff --git a/src/core/org/luaj/vm/LWeakTable.java b/src/core/org/luaj/vm/LWeakTable.java new file mode 100644 index 00000000..fd6f1f18 --- /dev/null +++ b/src/core/org/luaj/vm/LWeakTable.java @@ -0,0 +1,110 @@ +package org.luaj.vm; + +import java.lang.ref.WeakReference; + +public class LWeakTable extends LTable { + + private static class LWeakValue extends LValue { + private WeakReference ref; + private LWeakValue(LValue v) { + ref = new WeakReference(v); + } + public LValue toStrongReference() { + return (LValue) ref.get(); + } + public LValue value() { + LValue v = (LValue) ref.get(); + return (v!=null? v: LNil.NIL); + } + public String toJavaString() { + return value().toJavaString(); + } + public int luaGetType() { + return value().luaGetType(); + } + public boolean isNil() { + return value().isNil(); + } + } + + private static void makestrong(LuaState vm) { + LValue v = vm.poplvalue(); + v = v.toStrongReference(); + vm.pushlvalue(v!=null? v: LNil.NIL); + } + + /** Construct a new LTable with weak-reference keys */ + public LWeakTable() { + } + + /** Copy constructor */ + public LWeakTable(LTable copy) { + this.m_arrayEntries = copy.m_arrayEntries; + this.m_hashEntries = copy.m_hashEntries; + if ( copy.m_vector != null ) { + int n = copy.m_vector.length; + this.m_vector = new LValue[n]; + for ( int i=0; i