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 );
|
globals.put( "math", math );
|
||||||
|
|
||||||
LTable string = new LTable();
|
LTable string = LString.getMetatable();
|
||||||
for ( int i = 0; i < STRING_NAMES.length; ++i ) {
|
for ( int i = 0; i < STRING_NAMES.length; ++i ) {
|
||||||
string.put( STRING_NAMES[i], new LuaCompat( STRING_BASE + i ) );
|
string.put( STRING_NAMES[i], new LuaCompat( STRING_BASE + i ) );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package lua.value;
|
package lua.value;
|
||||||
|
|
||||||
import lua.Lua;
|
import lua.Lua;
|
||||||
import lua.StackState;
|
|
||||||
|
|
||||||
public class LString extends LValue {
|
public class LString extends LValue {
|
||||||
|
|
||||||
@@ -10,6 +9,8 @@ public class LString extends LValue {
|
|||||||
final String m_string;
|
final String m_string;
|
||||||
final int m_hash;
|
final int m_hash;
|
||||||
|
|
||||||
|
private static LTable s_stringMT;
|
||||||
|
|
||||||
public LString(String string) {
|
public LString(String string) {
|
||||||
this.m_string = string;
|
this.m_string = string;
|
||||||
this.m_hash = string.hashCode();
|
this.m_hash = string.hashCode();
|
||||||
@@ -91,5 +92,24 @@ public class LString extends LValue {
|
|||||||
public LString luaGetType() {
|
public LString luaGetType() {
|
||||||
return TYPE_NAME;
|
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");
|
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
|
* Zero-length array to use instead of null, so that we don't need to
|
||||||
* check for null everywhere.
|
* check for null everywhere.
|
||||||
@@ -229,28 +223,22 @@ public class LTable extends LValue {
|
|||||||
final int slot = findSlot( key );
|
final int slot = findSlot( key );
|
||||||
return m_hashKeys[ slot ] != null;
|
return m_hashKeys[ slot ] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void luaGetTable(VM vm, LValue table, LValue key) {
|
public void luaGetTable(VM vm, LValue table, LValue key) {
|
||||||
LValue v = get(key);
|
LValue v = get(key);
|
||||||
if ( v == LNil.NIL && m_metatable != null ) {
|
if ( v == LNil.NIL && m_metatable != null ) {
|
||||||
LValue event = m_metatable.get( TM_INDEX );
|
super.luaGetTable( vm, table, key );
|
||||||
if ( event != null && event != LNil.NIL ) {
|
} else {
|
||||||
event.luaGetTable( vm, table, key );
|
vm.push(v);
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
vm.push(v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void luaSetTable(VM vm, LValue table, LValue key, LValue val) {
|
public void luaSetTable(VM vm, LValue table, LValue key, LValue val) {
|
||||||
if ( !containsKey( key ) && m_metatable != null ) {
|
if ( !containsKey( key ) && m_metatable != null ) {
|
||||||
LValue event = m_metatable.get( TM_NEWINDEX );
|
super.luaSetTable( vm, table, key, val );
|
||||||
if ( event != null && event != LNil.NIL ) {
|
} else {
|
||||||
event.luaSetTable( vm, table, key, val );
|
put(key, val);
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
put(key, val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -269,13 +257,14 @@ public class LTable extends LValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Valid for tables */
|
/** Valid for tables */
|
||||||
public LValue luaGetMetatable() {
|
public LTable luaGetMetatable() {
|
||||||
return this.m_metatable;
|
return this.m_metatable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Valid for tables */
|
/** Valid for tables */
|
||||||
public void luaSetMetatable(LValue metatable) {
|
public void luaSetMetatable(LValue metatable) {
|
||||||
this.m_metatable = (LTable) metatable;
|
this.m_metatable = ( metatable != null && metatable != LNil.NIL ) ?
|
||||||
|
(LTable) metatable : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String luaAsString() {
|
public String luaAsString() {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ public class LUserData extends LValue {
|
|||||||
public static final LString TYPE_NAME = new LString("userdata");
|
public static final LString TYPE_NAME = new LString("userdata");
|
||||||
|
|
||||||
public final Object m_instance;
|
public final Object m_instance;
|
||||||
|
public LTable m_metatable;
|
||||||
|
|
||||||
public LUserData(Object obj) {
|
public LUserData(Object obj) {
|
||||||
m_instance = obj;
|
m_instance = obj;
|
||||||
@@ -25,4 +26,8 @@ public class LUserData extends LValue {
|
|||||||
public LString luaGetType() {
|
public LString luaGetType() {
|
||||||
return TYPE_NAME;
|
return TYPE_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LTable luaGetMetatable() {
|
||||||
|
return m_metatable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ import lua.VM;
|
|||||||
abstract
|
abstract
|
||||||
public class LValue {
|
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() {
|
protected static LValue luaUnsupportedOperation() {
|
||||||
throw new java.lang.RuntimeException( "not supported" );
|
throw new java.lang.RuntimeException( "not supported" );
|
||||||
}
|
}
|
||||||
@@ -79,22 +85,39 @@ public class LValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** set a value in a table
|
/** set a value in a table
|
||||||
|
* For non-tables, goes straight to the meta-table.
|
||||||
* @param vm the calling vm
|
* @param vm the calling vm
|
||||||
* @param table the table to operate on
|
* @param table the table to operate on
|
||||||
* @param the key to set
|
* @param the key to set
|
||||||
* @param the value to set
|
* @param the value to set
|
||||||
*/
|
*/
|
||||||
public void luaSetTable(VM vm, LValue table, LValue key, LValue val) {
|
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
|
/** Get a value from a table
|
||||||
* @param vm the calling vm
|
* @param vm the calling vm
|
||||||
* @param table the table from which to get the value
|
* @param table the table from which to get the value
|
||||||
* @param key the key to look up
|
* @param key the key to look up
|
||||||
*/
|
*/
|
||||||
public void luaGetTable(VM vm, LValue table, LValue key) {
|
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
|
/** Get the value as a String
|
||||||
@@ -135,9 +158,18 @@ public class LValue {
|
|||||||
return luaUnsupportedOperation();
|
return luaUnsupportedOperation();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Valid for tables */
|
/**
|
||||||
public LValue luaGetMetatable() {
|
* Valid for all types: get a metatable. Only tables and userdata can have a
|
||||||
return luaUnsupportedOperation();
|
* 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 */
|
/** Valid for tables */
|
||||||
@@ -147,4 +179,5 @@ public class LValue {
|
|||||||
|
|
||||||
/** Valid for all types: return the type of this value as an LString */
|
/** Valid for all types: return the type of this value as an LString */
|
||||||
public abstract LString luaGetType();
|
public abstract LString luaGetType();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ public class LuaJTest extends TestCase {
|
|||||||
runTest( "compare" );
|
runTest( "compare" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testMetatables() throws IOException, InterruptedException {
|
||||||
|
runTest( "metatables" );
|
||||||
|
}
|
||||||
|
|
||||||
public void testSelect() throws IOException, InterruptedException {
|
public void testSelect() throws IOException, InterruptedException {
|
||||||
runTest( "select" );
|
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