Initial implementation of module, package.seeall

This commit is contained in:
James Roseborough
2007-11-29 01:11:18 +00:00
parent 62cda2bc49
commit b493230edb
8 changed files with 210 additions and 24 deletions

View File

@@ -153,7 +153,7 @@ public class BaseLib extends LFunction {
} }
case GETMETATABLE: { case GETMETATABLE: {
checkargexists(vm,2,Lua.LUA_TVALUE); checkargexists(vm,2,Lua.LUA_TVALUE);
if ( 0 == vm.getmetatable(2) ) { if ( ! vm.getmetatable(2) ) {
vm.resettop(); vm.resettop();
vm.pushnil(); vm.pushnil();
} else { } else {
@@ -248,14 +248,16 @@ public class BaseLib extends LFunction {
vm.pushlvalue(t); vm.pushlvalue(t);
} break; } break;
case GETFENV: { case GETFENV: {
if ( vm.gettop() <= 1 ) { if ( vm.isfunction(2) ) {
vm.pushlvalue(vm._G);
} else {
if ( ! vm.isfunction(2) ) {
int i = (vm.isnil(2)? 1: vm.tointeger(2));
vm.pushlvalue( vm.getStackFrame(i).closure );
}
vm.getfenv(-1); vm.getfenv(-1);
} else {
int i = (vm.isnil(2)? 1: vm.tointeger(2));
if ( i <= 0 )
vm.pushlvalue(vm._G);
else if ( i-1 <= vm.cc )
vm.pushlvalue( vm.getStackFrame(i-1).closure.env );
else
vm.pushnil();
} }
vm.insert(1); vm.insert(1);
vm.settop(1); vm.settop(1);

View File

@@ -24,12 +24,14 @@ package org.luaj.lib;
import java.io.InputStream; import java.io.InputStream;
import java.io.PrintStream; import java.io.PrintStream;
import org.luaj.vm.CallInfo;
import org.luaj.vm.LBoolean; import org.luaj.vm.LBoolean;
import org.luaj.vm.LFunction; import org.luaj.vm.LFunction;
import org.luaj.vm.LNil; import org.luaj.vm.LNil;
import org.luaj.vm.LString; import org.luaj.vm.LString;
import org.luaj.vm.LTable; import org.luaj.vm.LTable;
import org.luaj.vm.LValue; import org.luaj.vm.LValue;
import org.luaj.vm.Lua;
import org.luaj.vm.LuaState; import org.luaj.vm.LuaState;
@@ -39,6 +41,13 @@ public class PackageLib extends LFunction {
public static PrintStream STDOUT = System.out; public static PrintStream STDOUT = System.out;
public static LTable LOADED = new LTable(); public static LTable LOADED = new LTable();
private static final LString _M = new LString("_M");
private static final LString _NAME = new LString("_NAME");
private static final LString _PACKAGE = new LString("_PACKAGE");
private static final LString _DOT = new LString(".");
private static final LString _EMPTY = new LString("");
private static final LString __INDEX = new LString("__index");
private static final String[] NAMES = { private static final String[] NAMES = {
"package", "package",
"module", "module",
@@ -53,6 +62,7 @@ public class PackageLib extends LFunction {
private static final int LOADLIB = 3; private static final int LOADLIB = 3;
private static final int SEEALL = 4; private static final int SEEALL = 4;
public static void install( LTable globals ) { public static void install( LTable globals ) {
for ( int i=1; i<LOADLIB; i++ ) for ( int i=1; i<LOADLIB; i++ )
globals.put( NAMES[i], new PackageLib(i) ); globals.put( NAMES[i], new PackageLib(i) );
@@ -87,9 +97,17 @@ public class PackageLib extends LFunction {
case LOADLIB: case LOADLIB:
loadlib(vm); loadlib(vm);
break; break;
case SEEALL: case SEEALL: {
seeall(vm); if ( ! vm.istable(2) )
vm.error( "table expected, got "+vm.typename(2) );
LTable t = vm.totable(2);
LTable m = t.luaGetMetatable();
if ( m == null )
t.luaSetMetatable(m = new LTable());
m.put(__INDEX, vm._G);
vm.resettop();
break; break;
}
default: default:
luaUnsupportedOperation(); luaUnsupportedOperation();
} }
@@ -98,9 +116,98 @@ public class PackageLib extends LFunction {
// ======================== Module, Package loading ============================= // ======================== Module, Package loading =============================
/**
* module (name [, ···])
*
* Creates a module. If there is a table in package.loaded[name], this table
* is the module. Otherwise, if there is a global table t with the given
* name, this table is the module. Otherwise creates a new table t and sets
* it as the value of the global name and the value of package.loaded[name].
* This function also initializes t._NAME with the given name, t._M with the
* module (t itself), and t._PACKAGE with the package name (the full module
* name minus last component; see below). Finally, module sets t as the new
* environment of the current function and the new value of
* package.loaded[name], so that require returns t.
*
* If name is a compound name (that is, one with components separated by
* dots), module creates (or reuses, if they already exist) tables for each
* component. For instance, if name is a.b.c, then module stores the module
* table in field c of field b of global a.
*
* This function may receive optional options after the module name, where
* each option is a function to be applied over the module.
*/
public static void module(LuaState vm) { public static void module(LuaState vm) {
vm.error( "module not implemented" ); LString modname = vm.tolstring(2);
int n = vm.gettop();
LValue value = LOADED.get(modname);
LTable module;
if ( value.luaGetType() != Lua.LUA_TTABLE ) { /* not found? */
/* try global variable (and create one if it does not exist) */
module = findtable( vm._G, modname );
if ( module == null )
vm.error( "name conflict for module '"+modname+"'", 2 );
LOADED.luaSetTable(vm, LOADED, modname, module);
} else {
module = (LTable) value;
}
/* check whether table already has a _NAME field */
module.luaGetTable(vm, module, _NAME);
if ( vm.isnil(-1) ) {
modinit( vm, module, modname );
}
// set the environment of the current function
CallInfo ci = vm.getStackFrame(0);
ci.closure.env = module;
// apply the functions
for ( int i=3; i<=n; i++ ) {
vm.pushvalue( i ); /* get option (a function) */
vm.pushlvalue( module ); /* module */
vm.call( 1, 0 );
}
// returns no results
vm.resettop();
}
/**
*
* @param table the table at which to start the search
* @param fname the name to look up or create, such as "abc.def.ghi"
* @return the table for that name, possible a new one, or null if a non-table has that name already.
*/
private static LTable findtable(LTable table, LString fname) {
int b, e=(-1);
do {
e = fname.indexOf(_DOT, b=e+1 );
if ( e < 0 )
e = fname.m_length;
LString key = fname.substring(b, e);
LValue val = table.get(key);
if ( val == LNil.NIL ) { /* no such field? */
LTable field = new LTable(); /* new table for field */
table.put(key, field);
table = field;
} else if ( val.luaGetType() != Lua.LUA_TTABLE ) { /* field has a non-table value? */
return null;
} else {
table = (LTable) val;
}
} while ( e < fname.m_length );
return table;
}
private static void modinit(LuaState vm, LTable module, LString modname) {
/* module._M = module */
module.luaSetTable(vm, module, _M, module);
int e = modname.lastIndexOf(_DOT);
module.luaSetTable(vm, module, _NAME, modname );
module.luaSetTable(vm, module, _PACKAGE, (e<0? _EMPTY: modname.substring(0,e+1)) );
} }
/** /**
@@ -154,8 +261,4 @@ public class PackageLib extends LFunction {
public static void loadlib( LuaState vm ) { public static void loadlib( LuaState vm ) {
vm.error( "loadlib not implemented" ); vm.error( "loadlib not implemented" );
} }
public static void seeall( LuaState vm ) {
vm.error( "seeall not implemented" );
}
} }

View File

@@ -72,4 +72,5 @@ public final class LNil extends LValue {
public Short toJavaBoxedShort() { public Short toJavaBoxedShort() {
return null; return null;
} }
} }

View File

@@ -222,6 +222,17 @@ public class LString extends LValue {
return -1; return -1;
} }
public int lastIndexOf( LString s ) {
final int slen = s.length();
final int limit = m_offset + m_length - slen;
for ( int i = limit; i >= m_offset; --i ) {
if ( equals( m_bytes, i, s.m_bytes, s.m_offset, slen ) ) {
return i;
}
}
return -1;
}
public static LString valueOf( double d ) { public static LString valueOf( double d ) {
return new LString( String.valueOf( d ) ); return new LString( String.valueOf( d ) );
} }

View File

@@ -1355,15 +1355,17 @@ public class LuaState extends Lua {
* <p> * <p>
* Pushes onto the stack the metatable of the value at the given acceptable * Pushes onto the stack the metatable of the value at the given acceptable
* index. If the index is not valid, or if the value does not have a * index. If the index is not valid, or if the value does not have a
* metatable, the function returns&nbsp;0 and pushes nothing on the stack. * metatable, the function returns false and pushes nothing on the stack.
*
* @return true if the metatable was pushed onto the stack, false otherwise
*/ */
public int getmetatable(int index) { public boolean getmetatable(int index) {
LTable mt = topointer(index).luaGetMetatable(); LTable mt = topointer(index).luaGetMetatable();
if ( mt != null ) { if ( mt != null ) {
pushlvalue( mt ); pushlvalue( mt );
return 1; return true;
} }
return 0; return false;
} }
/** /**

View File

@@ -78,6 +78,10 @@ public class LuaJTest extends TestCase {
runTest( "metatables" ); runTest( "metatables" );
} }
public void testModule() throws IOException, InterruptedException {
runTest( "module" );
}
public void testNext() throws IOException, InterruptedException { public void testNext() throws IOException, InterruptedException {
runTest( "next" ); runTest( "next" );
} }

63
src/test/res/module.lua Normal file
View File

@@ -0,0 +1,63 @@
-- unit tests for module() function
local ids = {}
local function id(obj)
if not obj or type(obj) == 'number' or type(obj) == 'string' then
return obj
end
local v = ids[obj]
if v then
return v
end
table.insert(ids,obj)
ids[obj] = type(obj)..'.'..tostring(#ids)
return ids[obj]
end
-- module tests
local pr = print
local pkg = package
local g = _G
local md = module
local rq = require
local sa = package.seeall
local gfe = getfenv
local gmt = getmetatable
local function envs()
return id(gfe(0)), id(gfe(1)), id(gfe(2)),
id(gmt(gfe(0))), id(gmt(gfe(1))), id(gmt(gfe(2)))
end
local function trymodule(name)
pr( '_G['..name..']', id(g[name]) )
pr( 'pkg.loaded['..name..']', id(pkg.loaded[name]) )
pr( 'envs', envs() )
md(name)
pr( 'envs', envs() )
pr( 'status,result', status, result )
pr( '_G['..name..']', id(g[name]) )
local t = pkg.loaded[name]
pr( 't=pkg.loaded['..name..']', id(t) )
pr( 't._M, t._NAME, t._PACKAGE', id(t._M), id(t._NAME), id(t._PACKAGE) )
pr( 'rq('..name..')', id( rq(name) ) )
pr( 'print', id(print) )
pr( 'seeall(t)', sa(t) )
pr( 'print, _G['..name..']', id(print), id(g[name]) )
end
trymodule('abc.def.ghi')
trymodule('abc.def.ghi')
trymodule('abc.def')
trymodule('abc.def.lmn')
trymodule('abc.def')
trymodule('abc')
package.loaded['opq.rst'] = {}
trymodule('opq.rst')
trymodule('opq.rst')
uvw = { xyz="x1y1z1" }
--print( "uvw", id(uvw) )
--print( "uvw.xyz", id(uvw.xyz) )
--print( "uvw.abc", id(uvw.abc) )
print( pcall( trymodule, 'uvw.xyz' ) )
print( pcall( trymodule, 'uvw' ) )
print( pcall( trymodule, 'uvw.abc' ) )
print( "uvw", id(uvw) )
print( "uvw.xyz", id(uvw.xyz) )
print( "uvw.abc", id(uvw.abc) )

BIN
src/test/res/module.luac Normal file

Binary file not shown.