Skip to content

Commit

Permalink
Merge pull request #2 from mousebyte/experimental-modules-support
Browse files Browse the repository at this point in the history
Experimental modules support
  • Loading branch information
mousebyte authored Jul 21, 2023
2 parents 3842a57 + 3ed981c commit 848e9a5
Show file tree
Hide file tree
Showing 14 changed files with 157 additions and 184 deletions.
12 changes: 1 addition & 11 deletions docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Functions supporting the creation and registration of Lua classes.
:project: LuaClassLib
:members:

.. doxygenfunction:: luaC_register
.. doxygenfunction:: luaC_classfromptr
:project: LuaClassLib

.. doxygenfunction:: luaC_unregister
Expand All @@ -23,9 +23,6 @@ Functions supporting the creation and registration of Lua classes.
.. doxygenfunction:: luaC_setinheritcb
:project: LuaClassLib

.. doxygenfunction:: luaC_packageadd
:project: LuaClassLib

.. doxygenfunction:: luaC_newclass
:project: LuaClassLib

Expand Down Expand Up @@ -183,13 +180,6 @@ Functions provided by LCL to Lua code.

.. lua:module:: lcl
.. lua:function:: register(class)
Adds the given Moonscript class and all of its named parents to the class registry.

:param class: The class to register.
:return: The registered class.

.. lua:function:: uvget(obj[, uv], idx)
Gets the value of ``t[idx]``, where ``t`` is the table stored
Expand Down
2 changes: 1 addition & 1 deletion docs/usage/udataclass.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ The class can now be constructed with a call to `luaC_construct`:
.. sourcecode:: LCL

lua_pushstring(L, "myfile.txt");
luaC_construct(L, 1, "File");
luaC_construct(L, 1, "lcltests.File");

Since we set `luaC_Class::user_ctor` to ``1``, our class can be constructed from Lua code by calling the class object,
assuming it is made accessible (e.g. by a call to `luaC_packageadd`).
148 changes: 96 additions & 52 deletions src/luaclasslib.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,6 @@ static int classlib_type(lua_State *L) {
return 1;
}

static int classlib_register(lua_State *L) {
luaL_argcheck(L, luaC_isclass(L, 1), 1, "expected class");
luaC_register(L, 1);
return 1;
}

int luaC_isobject(lua_State *L, int index) {
int ret = 0;
if (lua_istable(L, index) || lua_isuserdata(L, index)) {
Expand Down Expand Up @@ -123,7 +117,53 @@ void *luaC_checkuclass(lua_State *L, int arg, const char *name) {
}

int luaC_pushclass(lua_State *L, const char *name) {
return luaC_getregfield(L, name);
// check the registry first
if (luaC_getregfield(L, name) == LUA_TTABLE) return LUA_TTABLE;
else lua_pop(L, 1);

// otherwise, try to `require` the module
lua_pushfstring(L, "return require('%s')", name);
luaL_loadstring(L, lua_tostring(L, -1));
lua_remove(L, -2);

if (lua_pcall(L, 0, 1, 0) != LUA_OK) {
lua_pop(L, 1);
const char *pos = strrchr(name, '.');

if (!pos || strlen(pos + 1) == 0) {
lua_pushnil(L);
return LUA_TNIL;
}

// try to `require` module table and get class as field
lua_pushstring(L, "return require('");
lua_pushlstring(L, name, pos - name);
lua_pushstring(L, "')");
lua_concat(L, 3);
luaL_loadstring(L, lua_tostring(L, -1));
lua_remove(L, -2);

if (lua_pcall(L, 0, 1, 0) != LUA_OK) {
lua_pop(L, 1);
lua_pushnil(L);
return LUA_TNIL;
} else if (lua_istable(L, -1)) {
lua_getfield(L, -1, pos + 1);
lua_remove(L, -2);
}
}

if (!luaC_isclass(L, -1)) {
lua_pop(L, 1);
lua_pushnil(L);
return LUA_TNIL;
}

// add class to registry for quick access
lua_pushvalue(L, -1);
luaC_setregfield(L, name);

return LUA_TTABLE;
}

luaC_Class *luaC_uclass(lua_State *L, int index) {
Expand Down Expand Up @@ -396,11 +436,16 @@ static int default_class_inherited(lua_State *L) {
return 0;
}

int register_c_class(lua_State *L, int idx) {
luaC_Class *c = lua_touserdata(L, idx);
if (!c || luaC_getregfield(L, c->name) != LUA_TNIL) return 0;
int luaC_classfromptr(lua_State *L) {
int uclass = lua_gettop(L);
luaC_Class *c = lua_touserdata(L, uclass);
if (!c || !c->name) return 0;
lua_pushvalue(L, uclass);
if (luaC_getreg(L) != LUA_TNIL) {
lua_pop(L, 1);
return 1;
}
lua_pop(L, 1);
int uclass = lua_absindex(L, idx);

lua_newtable(L); // base table
luaL_setfuncs(L, c->methods, 0); // load in methods
Expand Down Expand Up @@ -461,15 +506,16 @@ int register_c_class(lua_State *L, int idx) {
if (c->parent == NULL) { // no parent
lua_pushvalue(L, base); // push base
lua_setfield(L, class_mt, "__index"); // set meta __index to base
} else if (luaC_getregfield(L, c->parent) == LUA_TTABLE) { // get parent
lua_pushvalue(L, base); // push base
} else if (luaC_pushclass(L, c->parent) == LUA_TTABLE) { // get parent
lua_pushvalue(L, base); // push base
lua_pushcclosure(L, default_class_index, 1); // wrap it in a closure
lua_setfield(L, class_mt, "__index"); // set meta __index
lua_getfield(L, -1, "__base"); // get parent __base
lua_setmetatable(L, base); // set base metatable to parent base
lua_setfield(L, class, "__parent"); // set class __parent to parent
} else { // else parent not registered
lua_pop(L, 3); // clean up and return
lua_pop(L, 4); // clean up and return
lua_remove(L, uclass);
return 0;
}

Expand All @@ -485,36 +531,44 @@ int register_c_class(lua_State *L, int idx) {

lua_pushvalue(L, class);
lua_pushvalue(L, uclass);
luaC_setreg(L); // register uclass
luaC_setreg(L); // reg[class] = uclass
lua_pushvalue(L, uclass);
lua_pushvalue(L, class);
luaC_setregfield(L, c->name); // register class
lua_remove(L, base); // remove base from stack
lua_remove(L, uclass); // remove uclass from stack
return 1;
}
luaC_setreg(L); // reg[uclass] = class

int luaC_register(lua_State *L, int index) {
if (lua_isuserdata(L, index)) return register_c_class(L, index);
if (!luaC_isclass(L, index)) return 0;
int top = lua_gettop(L);
lua_pushvalue(L, index); // push class
do {
if (luaC_getname(L, -1)) { // get name
lua_pushvalue(L, -2); // push class
luaC_setreg(L); // register class
} else lua_pop(L, 1); // anonymous class, skip
} while (luaC_getparent(L, -1));
lua_settop(L, top);
lua_remove(L, base); // remove base from stack
lua_remove(L, uclass); // remove uclass from stack
return 1;
}

void luaC_unregister(lua_State *L, const char *name) {
if (luaC_getregfield(L, name) == LUA_TTABLE) {
if (luaC_pushclass(L, name) == LUA_TTABLE) {
lua_pushvalue(L, -1);
if (luaC_getreg(L) == LUA_TUSERDATA) {
lua_pushnil(L);
luaC_setreg(L); // reg[uclass] = nil
}
lua_pushnil(L);
luaC_setreg(L); // reg[class] = nil
lua_pushnil(L);
luaC_setreg(L); // remove C class if present
luaC_setregfield(L, name); // reg[module?.name] = nil
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
lua_pushnil(L);
luaC_setregfield(L, name); // remove class table
} else lua_pop(L, 1);
lua_setfield(L, -2, name); // package.loaded[module?.name] = nil

// check for module table
const char *pos = strrchr(name, '.');
if (pos && strlen(pos + 1) > 0) {
lua_pushlstring(L, name, pos - name);
if (lua_gettable(L, -2) == LUA_TTABLE) { // get module table
lua_pushnil(L);
// package.loaded.module[name] = nil
lua_setfield(L, -2, pos + 1);
}
lua_pop(L, 1); // pop module table
}
}
lua_pop(L, 1); // pop nil or package.loaded
}

void luaC_setinheritcb(lua_State *L, int idx, lua_CFunction cb) {
Expand All @@ -526,15 +580,6 @@ void luaC_setinheritcb(lua_State *L, int idx, lua_CFunction cb) {
}
}

void luaC_packageadd(lua_State *L, const char *name, const char *module) {
int top = lua_gettop(L);
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
if (module) luaL_getsubtable(L, -1, module); // get module table
luaC_pushclass(L, name);
lua_setfield(L, -2, name);
lua_settop(L, top);
}

int luaC_newclass(
lua_State *L,
const char *name,
Expand All @@ -547,17 +592,16 @@ int luaC_newclass(
cls->alloc = NULL;
cls->gc = NULL;
cls->methods = methods;
return luaC_register(L, -1);
return luaC_classfromptr(L);
}

int luaopen_lcl(lua_State *L) {
static const luaL_Reg classlib_funcs[] = {
{"uvget", classlib_uvget },
{"uvset", classlib_uvset },
{"rawget", classlib_rawget },
{"rawset", classlib_rawset },
{"register", classlib_register},
{NULL, NULL }
{"uvget", classlib_uvget },
{"uvset", classlib_uvset },
{"rawget", classlib_rawget},
{"rawset", classlib_rawset},
{NULL, NULL }
};
luaL_newlib(L, classlib_funcs);
return 1;
Expand Down
25 changes: 6 additions & 19 deletions src/luaclasslib.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ void *luaC_checkuclass(lua_State *L, int arg, const char *name);
* @brief Pushes onto the stack the class registered under the given *name*.
*
* @param L The Lua state.
* @param name The class name.
* @param name The fully qualified (with module prefix) class name.
*
* @return The type of the pushed value.
*/
Expand Down Expand Up @@ -446,16 +446,14 @@ int luaC_getparentfield(lua_State *L, int index, int depth, const char *name);
void luaC_super(lua_State *L, const char *name, int nargs, int nresults);

/**
* @brief Adds the class represented by the value at the given stack index to
* the class registry. If the value is a class table, all of its parents will be
* registered as well.
* @brief Obtains the Lua class table associated with the `luaC_Class` at the
* top of the stack. If the class table does not exist, it will be created.
*
* @param L The Lua state.
* @param index The stack index of the class.
*
* @return 1 if the class was successfully registered, and 0 otherwise.
*/
int luaC_register(lua_State *L, int index);
int luaC_classfromptr(lua_State *L);

/**
* @brief Removes the class with the given name from the class registry.
Expand All @@ -476,26 +474,15 @@ void luaC_unregister(lua_State *L, const char *name);
*/
void luaC_setinheritcb(lua_State *L, int index, lua_CFunction cb);

/**
* @brief Adds a class to `package.loaded` under the module table with the
* specified name. If `module` is `NULL`, adds the class directly to
* `package.loaded`. The class must already be registered in the LuaClassLib
* registry.
*
* @param L The Lua state.
* @param name The class name.
* @param module The module to add the class under.
*/
void luaC_packageadd(lua_State *L, const char *name, const char *module);

/**
* @brief Helper method for creating and registering a simple luaC_Class as a
* full userdata. Useful for when you're using stock classes and don't want to
* define your luaC_Class with static linkage.
*
* @param L The Lua state.
* @param name The class name.
* @param parent The parent class name. Must be in the registry.
* @param module The module to add the class to. Can be null.
* @param parent The parent class name. Must be in the registry. Can be null.
* @param methods The class methods.
*
* @return 1 if the class was successfully created and registered, and 0
Expand Down
2 changes: 1 addition & 1 deletion tests/assets/DerivedFromUdata.moon
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import File from require "LCL"
import File from require "lcltests"

class DerivedFromUdata extends File
new: (n, filename)=>
Expand Down
2 changes: 1 addition & 1 deletion tests/assets/DerivedFromUdata2.moon
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import DerivedFromUdata from require "LCL"
DerivedFromUdata = require "DerivedFromUdata"

class DerivedFromUdata2 extends DerivedFromUdata
new: (n, filename)=>
Expand Down
6 changes: 0 additions & 6 deletions tests/assets/RegistersItself.moon

This file was deleted.

Loading

0 comments on commit 848e9a5

Please sign in to comment.