Initial implementation of module, package.seeall
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
LString modname = vm.tolstring(2);
|
||||||
|
int n = vm.gettop();
|
||||||
|
LValue value = LOADED.get(modname);
|
||||||
|
LTable module;
|
||||||
|
if ( value.luaGetType() != Lua.LUA_TTABLE ) { /* not found? */
|
||||||
|
|
||||||
public static void module( LuaState vm ) {
|
/* try global variable (and create one if it does not exist) */
|
||||||
vm.error( "module not implemented" );
|
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" );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,4 +72,5 @@ public final class LNil extends LValue {
|
|||||||
public Short toJavaBoxedShort() {
|
public Short toJavaBoxedShort() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 ) );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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
63
src/test/res/module.lua
Normal 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
BIN
src/test/res/module.luac
Normal file
Binary file not shown.
Reference in New Issue
Block a user