Allow metatable operations on values of all types, and use the metatable

on strings as the string package, so that string functions can be called
with self (:) syntax. Autoload test case now inexplicably fails more,
but it was already broken.
This commit is contained in:
Ian Farmer
2007-09-08 20:54:40 +00:00
parent e30646bcf8
commit aa44eedf4b
8 changed files with 86 additions and 30 deletions

View File

@@ -39,7 +39,7 @@ public class LuaCompat extends LFunction {
globals.put( "math", math );
LTable string = new LTable();
LTable string = LString.getMetatable();
for ( int i = 0; i < STRING_NAMES.length; ++i ) {
string.put( STRING_NAMES[i], new LuaCompat( STRING_BASE + i ) );
}

View File

@@ -1,7 +1,6 @@
package lua.value;
import lua.Lua;
import lua.StackState;
public class LString extends LValue {
@@ -10,6 +9,8 @@ public class LString extends LValue {
final String m_string;
final int m_hash;
private static LTable s_stringMT;
public LString(String string) {
this.m_string = string;
this.m_hash = string.hashCode();
@@ -92,4 +93,23 @@ public class LString extends LValue {
return TYPE_NAME;
}
public LTable luaGetMetatable() {
synchronized ( LString.class ) {
return s_stringMT;
}
}
/**
* Get the metatable for all string values. Creates the table if it does not
* exist yet, and sets its __index entry to point to itself.
*
* @return metatable that will be used for all strings
*/
public static synchronized LTable getMetatable() {
if ( s_stringMT == null ) {
s_stringMT = new LTable();
s_stringMT.put( TM_INDEX, s_stringMT );
}
return s_stringMT;
}
}

View File

@@ -23,12 +23,6 @@ public class LTable extends LValue {
public static final LString TYPE_NAME = new LString("table");
/** Metatable tag for intercepting table gets */
private static final LString TM_INDEX = new LString("__index");
/** Metatable tag for intercepting table sets */
private static final LString TM_NEWINDEX = new LString("__newindex");
/**
* Zero-length array to use instead of null, so that we don't need to
* check for null everywhere.
@@ -233,25 +227,19 @@ public class LTable extends LValue {
public void luaGetTable(VM vm, LValue table, LValue key) {
LValue v = get(key);
if ( v == LNil.NIL && m_metatable != null ) {
LValue event = m_metatable.get( TM_INDEX );
if ( event != null && event != LNil.NIL ) {
event.luaGetTable( vm, table, key );
return;
}
}
super.luaGetTable( vm, table, key );
} else {
vm.push(v);
}
}
public void luaSetTable(VM vm, LValue table, LValue key, LValue val) {
if ( !containsKey( key ) && m_metatable != null ) {
LValue event = m_metatable.get( TM_NEWINDEX );
if ( event != null && event != LNil.NIL ) {
event.luaSetTable( vm, table, key, val );
return;
}
}
super.luaSetTable( vm, table, key, val );
} else {
put(key, val);
}
}
/**
* Return the "length" of this table. This will not return the same result
@@ -269,13 +257,14 @@ public class LTable extends LValue {
}
/** Valid for tables */
public LValue luaGetMetatable() {
public LTable luaGetMetatable() {
return this.m_metatable;
}
/** Valid for tables */
public void luaSetMetatable(LValue metatable) {
this.m_metatable = (LTable) metatable;
this.m_metatable = ( metatable != null && metatable != LNil.NIL ) ?
(LTable) metatable : null;
}
public String luaAsString() {

View File

@@ -4,6 +4,7 @@ public class LUserData extends LValue {
public static final LString TYPE_NAME = new LString("userdata");
public final Object m_instance;
public LTable m_metatable;
public LUserData(Object obj) {
m_instance = obj;
@@ -25,4 +26,8 @@ public class LUserData extends LValue {
public LString luaGetType() {
return TYPE_NAME;
}
public LTable luaGetMetatable() {
return m_metatable;
}
}

View File

@@ -6,6 +6,12 @@ import lua.VM;
abstract
public class LValue {
/** Metatable tag for intercepting table gets */
public static final LString TM_INDEX = new LString("__index");
/** Metatable tag for intercepting table sets */
public static final LString TM_NEWINDEX = new LString("__newindex");
protected static LValue luaUnsupportedOperation() {
throw new java.lang.RuntimeException( "not supported" );
}
@@ -79,13 +85,22 @@ public class LValue {
}
/** set a value in a table
* For non-tables, goes straight to the meta-table.
* @param vm the calling vm
* @param table the table to operate on
* @param the key to set
* @param the value to set
*/
public void luaSetTable(VM vm, LValue table, LValue key, LValue val) {
luaUnsupportedOperation();
LTable mt = luaGetMetatable();
if ( mt != null ) {
LValue event = mt.get( TM_NEWINDEX );
if ( event != null && event != LNil.NIL ) {
event.luaSetTable( vm, table, key, val );
return;
}
}
vm.push( LNil.NIL );
}
/** Get a value from a table
@@ -94,7 +109,15 @@ public class LValue {
* @param key the key to look up
*/
public void luaGetTable(VM vm, LValue table, LValue key) {
luaUnsupportedOperation();
LTable mt = luaGetMetatable();
if ( mt != null ) {
LValue event = mt.get( TM_INDEX );
if ( event != null && event != LNil.NIL ) {
event.luaGetTable( vm, table, key );
return;
}
}
vm.push(LNil.NIL);
}
/** Get the value as a String
@@ -135,9 +158,18 @@ public class LValue {
return luaUnsupportedOperation();
}
/** Valid for tables */
public LValue luaGetMetatable() {
return luaUnsupportedOperation();
/**
* Valid for all types: get a metatable. Only tables and userdata can have a
* different metatable per instance, though, other types are restricted to
* one metatable per type.
*
* Since metatables on non-tables can only be set through Java and not Lua,
* this function should be overridden for each value type as necessary.
*
* @return null if there is no meta-table
*/
public LTable luaGetMetatable() {
return null;
}
/** Valid for tables */
@@ -147,4 +179,5 @@ public class LValue {
/** Valid for all types: return the type of this value as an LString */
public abstract LString luaGetType();
}

View File

@@ -64,6 +64,10 @@ public class LuaJTest extends TestCase {
runTest( "compare" );
}
public void testMetatables() throws IOException, InterruptedException {
runTest( "metatables" );
}
public void testSelect() throws IOException, InterruptedException {
runTest( "select" );
}

View File

@@ -0,0 +1,5 @@
-- The purpose of this test case is to demonstrate that
-- basic metatable operations on non-table types work.
-- i.e. that s.sub(s,...) could be used in place of string.sub(s,...)
local s = "hello"
print(s:sub(2,4))

Binary file not shown.