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:
@@ -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 ) );
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,24 +227,18 @@ 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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
put(key, val);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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" );
|
||||
}
|
||||
|
||||
5
src/test/res/metatables.lua
Normal file
5
src/test/res/metatables.lua
Normal 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))
|
||||
BIN
src/test/res/metatables.luac
Normal file
BIN
src/test/res/metatables.luac
Normal file
Binary file not shown.
Reference in New Issue
Block a user