-
Notifications
You must be signed in to change notification settings - Fork 236
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Native Plugins #527
Native Plugins #527
Conversation
To test it out, just build Just a proof of concept, but I think it works. We can provide a header file for anyone that wants to use a native plugin, which would initialise local function pointers to the entire lua library (and all the common macros). This way we can have our (statically linked) cake, and eat it too! And it's only about 100 lines of C! |
src/api/system.c
Outdated
size_t sname, modlen, pathlen; | ||
char buffer[512] = "lua_open_"; | ||
const char* modname = luaL_checklstring(L, -2, &modlen); | ||
const char* path = luaL_checklstring(L, -1, &pathlen); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If what we point to is not a string, what is modlen and pathlen set to?
src/api/system.c
Outdated
const char* modname = luaL_checklstring(L, -2, &modlen); | ||
const char* path = luaL_checklstring(L, -1, &pathlen); | ||
void* library = SDL_LoadObject(path); | ||
if (modlen == 0 || !library) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the case modname could not be found and modlen is not changed it will retain whatever value is in memory and pass this check despite not being a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it is not a string Lua will raise and error and the C code with longjmp to the Lua code managing the error and the C flow will not continue. I guess your comment is because you are not familiar with Lua C API and its error handling.
src/api/system.c
Outdated
void* library = SDL_LoadObject(path); | ||
if (modlen == 0 || !library) | ||
return luaL_error(L, "Unable to load %s: %s", modname, SDL_GetError()); | ||
for (sname = modlen - 1; sname > 0 && modname[sname] != '.'; --sname); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
its unclear that the for loop does not have a body
perhaps add an empty body or switch to a while loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, I did think this was a normal idiom in C for counting things, but I have no issues changing the ;
to {}
. Does anyone else have any comments on this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh it is but it comes at the cost of readability
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, I find it just as readable; again, I think it's a common idiom used frequently in C (at least C that I've read). But if someone else wants to the chime in here, and also concurs, I have no issue changing it.
src/api/system.c
Outdated
#if _WIN32 | ||
#define PATH_SEPARATOR '\\' | ||
#define DYNAMIC_SUFFIX "dll" | ||
#else | ||
#if __APPLE__ | ||
#define DYNAMIC_SUFFIX "lib" | ||
#else | ||
#define DYNAMIC_SUFFIX "so" | ||
#endif | ||
#define PATH_SEPARATOR '/' | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The preprocessors _WIN32 and __APPLE__ should usually be defined but in the case they are value that has a false truthiness we might fall through to the Unix Logic.
#if _WIN32 | |
#define PATH_SEPARATOR '\\' | |
#define DYNAMIC_SUFFIX "dll" | |
#else | |
#if __APPLE__ | |
#define DYNAMIC_SUFFIX "lib" | |
#else | |
#define DYNAMIC_SUFFIX "so" | |
#endif | |
#define PATH_SEPARATOR '/' | |
#endif | |
#if defined(_WIN32) | |
#define PATH_SEPARATOR '\\' | |
#define DYNAMIC_SUFFIX "dll" | |
#elif defined(__APPLE__) | |
#define PATH_SEPARATOR '/' | |
#define DYNAMIC_SUFFIX "lib" | |
#elif defined(__linux__) | |
#define PATH_SEPARATOR '/' | |
#define DYNAMIC_SUFFIX "so" | |
#endif | |
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, we may set an preprocessor #error directive in the unknown OS branch. Otherwise we may default to unix or check for other unix-like Os like BSD systems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy to make both these changes, but this does not follow the conventions elsewhere in lite (not bothering with defined, not bothering to explicitly check linux). If we change it here, we should change it everywhere else that's applicable.
Personally, I don't think it's necessary to have defined
; though I don't mind adding it (is there ever a realistic case where these would have different outcomes)?
Regarding defaulting to linux, I think that's a reasonable step, as there are far more *nix flavours than just linux, and they're all relatively similar in terms of this kind of thing.
src/api/system.c
Outdated
const char* cpath = luaL_checklstring(L, -1, &cpath_len); | ||
char lib[8192]; | ||
for (size_t i = 0; i <= cpath_len; ++i) { | ||
if (i == cpath_len || cpath[i] == ';') { | ||
if (cpath_q) { | ||
size_t offset = cpath_q - cpath_idx; | ||
strncpy(lib, &cpath[cpath_idx], offset); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we be sure that cpath fits in lib?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, an additional check should be made here.
OK! Big changes.
The only thing that's missing is me making a table for the lua uility and aux libraries; but other than that, it's pretty much good to go, I think (obviously, I'll move around the scripts, sample plugin and stuff). Thoughts? |
OK! I think that's everything. I've now committed what I feel can be the final-ish version of the plugin API header, finalised the entrypoint. Plugin would now look like this:
All in all, only 66 extra SLOC of C; not including the plugin header which isn't actually part of the application. |
One last thing that may be worthy of doing before finalizing this: Several people (I think @liquidev , and a few others), in discord bring up the issue of a non-standard entrypoint perhaps should be a different name than the standard lua entrypoint. As there's a different function signature, if you go in with an standard-named entrypoint with only a single parameter, and we call it from lite with a second parameter, I'm pretty sure that's UB, and things will likely get weird from there. So I'm actually going to split the entrypoint in two, so that we can use legacy entrypoints if they're named as such (as use the normal behaviour), and the extended entrypoint if it's called |
Hey ya'll, I've got an itch to make some native plugins once this gets in, so if no one has any objections, I'd like to set the date for acceptance of this as Sept 24; so if no one has any other concerns by then, I'll merge. |
data/core/start.lua
Outdated
@@ -19,3 +19,18 @@ package.path = DATADIR .. '/?.lua;' .. package.path | |||
package.path = DATADIR .. '/?/init.lua;' .. package.path | |||
package.path = USERDIR .. '/?.lua;' .. package.path | |||
package.path = USERDIR .. '/?/init.lua;' .. package.path | |||
|
|||
local dynamic_suffix = MACOS and 'lib' or (WINDOWS and 'dll' or 'so') | |||
package.cpath = DATADIR .. '/?.' .. dynamic_suffix .. ';' .. package.cpath |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the initial .. package.cpath
should be dopped here, I think using the system default should be avoided. This is also true for package.path
but that is not as big of a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re: This and the next comment. So, in theory, if you were to require a native plugin that didn't interface with lua, really; it would still be valid. Like if requiring a plugin just ran a program. Or, if you require a plugin with the exact same system lib as your statically linked library, I think it would probably work; which may make easier for people using luarocks to have system modules.
I dunno if that's a compelling reason to not do this though. I do see the reason why we should disallow that. Does anyone have any other thoughts? I think that @Mehgugs is probably correct, though. It probably should be disallowed, if no one has any objections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's still possible to load a shared object by path from lua using package.loadlib
if people are so determined, this would just prevent the automatic behaviour. The all in one loader (the fourth searcher) is just something native plugins wont need to work, and as above its functionality can still be reproduced with loadlib if people really do need that external module loaded.
data/core/start.lua
Outdated
local dynamic_suffix = MACOS and 'lib' or (WINDOWS and 'dll' or 'so') | ||
package.cpath = DATADIR .. '/?.' .. dynamic_suffix .. ';' .. package.cpath | ||
package.cpath = USERDIR .. '/?.' .. dynamic_suffix .. ';' .. package.cpath | ||
package.searchers[3] = function(modname) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
package.searchers[4]
should be removed too unless it's explicitly needed to ensure all native module requires go through native plugin machinery.
data/core/start.lua
Outdated
package.cpath = USERDIR .. '/?.' .. dynamic_suffix .. ';' .. package.cpath | ||
package.searchers[3] = function(modname) | ||
local s,e = 0 | ||
repeat |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could package.searchpath
be used here instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It absolutely could be. Good call; I'll refactor.
src/api/system.c
Outdated
if (name == 0 || !library) | ||
return luaL_error(L, "Unable to load %s: %s", name, SDL_GetError()); | ||
lua_pushlightuserdata(L, library); | ||
lua_setfield(L, LUA_REGISTRYINDEX, name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be better to use a table in the registry to keep plugins together, rather than putting them in the registry proper since its a user defined name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't want to bother, but yeah; it's probably a better call for future-proofing. I'll refactor.
OK, that should incorporate @Mehgugs 's changes. |
…ndant C code that's already encapsulated within PLATFORM.
Would like a comment on 713ef78, if anyone has one. I removed my setglobal calls to |
Of course I am fine with this change. The only thing is, I think platform should be called "macOS" as this is the real official name. |
Hrm. The only issue with this is that we are pulling the stuff directly from SDL. Do you want to do a translation at that level before it goes into Platform? |
SDL's names do seem a bit arbitrary: #elif __MACOS__
return "MacOS Classic";
#elif __MACOSX__
return "Mac OS X"; I think almost everyone would be ambivalent about translating, but this would probably be a breaking change and maybe not a super necessary one?
|
It would be breaking, yeah. That's not necessarily the end of the world if we can verify that almost no one is using |
If it is from SDL2 we can stick with their names. I think they set them up before Apple changed the name of Mac OS X. |
OK, that should be the last change. |
I'm going to merge this if no one has objections tomorrow, mmmk? |
Last chance! Or it's going in. |
I have to have a closer look at that but I guess it is useful for plugin development. Thank you for this contribution. |
Added in a quick interface to allow C interop.
The idea behind this is described in #525 ; this is just a sample implementation, with a sample plugin (forthcoming).