#include #include #include #include #include #include #include #include "util.hpp" #include "luastack.hpp" #include "table.hpp" #include "source.hpp" //////////////////////////////////////////////////////////// // // The source database is a lua table that maps filenames // to file info. A file info is a lua table containing: // // name: filename as a string // fingerprint: file modification time as human-readable string // code: the entire contents of the source file as a string // error: a syntax error message, or nil // hash: 128-bit hash of the code // closure: a lua closure, the compiled file // sequence: the position of the file in control.lst // // Operations on a source DB: // // source.peek() // - peek at the source database, for inspection purposes. // source.read() // - return a new source, reusing info from old. // //////////////////////////////////////////////////////////// LuaDefineHidden(source_updatefile) { LuaArg source, fn; LuaRet info; LuaVar fingerprint, null; LuaStack LS(L, source, fn, info, fingerprint, null); // Get the existing info table from the source DB. if (LS.istable(source)) { LS.rawget(info, source, fn); if (!LS.istable(info)) { LS.newtable(info); } } else { LS.newtable(info); } // If the file modification is wrong, update // these fields: code, fingerprint, closure, error // Otherwise, update nothing. std::string cfn = LS.tostring(fn); LS.getfield(fingerprint, info, "fingerprint"); std::string old_fingerprint; if (LS.isstring(fingerprint)) { old_fingerprint = LS.tostring(fingerprint); } std::cerr << "Probing " << cfn << std::endl; std::string new_fingerprint = util::get_file_fingerprint("syslua/" + cfn); LS.set(null, LuaNil); if ((old_fingerprint == "") || (old_fingerprint != new_fingerprint)) { std::cerr << "Rereading " << cfn << std::endl; std::string ccode = util::get_file_contents("syslua/" + cfn); LS.setfield(info, "name", fn); LS.setfield(info, "fingerprint", new_fingerprint); LS.setfield(info, "code", ccode); if ((new_fingerprint == "")||(ccode == "")) { LS.setfield(info, "loadresult", "cannot read source file"); } else { std::string chunk = "=" + cfn; luaL_loadbuffer(L, ccode.c_str(), ccode.size(), chunk.c_str()); lua_setfield(L, info.index(), "loadresult"); } } return LS.result(); } LuaDefineHidden(source_update) { LuaVar sourcedb, newdb, info, fn, seq; LuaStack LS(L, newdb, sourcedb, info, fn, seq); // Get the (old) source database. LS.getfield(sourcedb, LuaRegistry, "sourcedb"); if (!LS.istable(sourcedb)) { LS.newtable(sourcedb); } // Read the list of filenames. std::string ctrl = "syslua/control.lst"; util::stringvec filenames = util::trim_and_uncomment(util::read_lines(ctrl)); if (filenames.empty()) { luaL_error(L, "cannot read source database control.lst"); } // Process the files one by one. LS.newtable(newdb); for (int i = 0; i < int(filenames.size()); i++) { LS.set(fn, filenames[i]); // Call source_updatefile to get the updated info for one file. LS.call(info, source_updatefile, sourcedb, fn); // Insert the sequence number and put finalized info into the new database. LS.set(seq, i + 1); LS.setfield(info, "sequence", seq); LS.rawset(newdb, fn, info); } // Store the new source db. LS.setfield(LuaRegistry, "sourcedb", newdb); return LS.result(); } // Get a class from the class database. // // CLASSNAME // if classname is already present, and is a table, return it. // if classname is already present, and not a table, error. // if classname is not present, create and initialize it. // LuaDefineGlobalFunction(source_class) { LuaArg classname; LuaRet classtab; LuaVar classdb, action; LuaStack LS(L, classname, classtab, classdb, action); LS.checktype(classname, LUA_TSTRING); // Get a pointer to the classdb. LS.getfield(classdb, LuaRegistry, "classdb"); if (!LS.istable(classdb)) { LS.newtable(classdb); LS.setfield(LuaRegistry, "classdb", classdb); } // Get the classtab from the classdb, sanity check it. LS.rawget(classtab, classdb, classname); if (LS.istable(classtab)) { return LS.result(); } else if (!LS.isnil(classtab)) { luaL_error(L, "%s is not a class", LS.tostring(classname).c_str()); } // Create a new classtab and store it in the classdb. LS.newtable(classtab); LS.rawset(classdb, classname, classtab); LS.setfield(classtab, "__index", classtab); LS.setfield(classtab, "__class", classname); LS.newtable(action); LS.setfield(classtab, "action", action); return LS.result(); } // Reset a class database: // // Clear out all classes. Instead of replacing the class tables, // it simply deletes all keys. That way, if somebody has a pointer // to a class, the pointer is not invalidated. // LuaDefineHidden(source_reset_classes) { LuaVar classdb, classname, classtab, action, key; LuaStack LS(L, classname, classtab, classdb, action, key); // Get a pointer to the classdb. LS.getfield(classdb, LuaRegistry, "classdb"); if (!LS.istable(classdb)) { LS.newtable(classdb); LS.setfield(LuaRegistry, "classdb", classdb); } // Iterate over the classdb, clearing it. LS.set(classname, LuaNil); while (LS.next(classdb, classname, classtab) != 0) { if (LS.istable(classtab)) { LS.set(key, "action"); LS.rawget(action, classtab, key); if (LS.istable(action)) { LS.call(table_clear, action); } else { LS.newtable(action); } LS.call(table_clear, classtab); LS.setfield(classtab, "__index", classtab); LS.setfield(classtab, "__class", classname); LS.setfield(classtab, "action", action); } } return LS.result(); } LuaDefineHidden(source_load_builtins) { luaopen_base(L); luaopen_table(L); luaopen_string(L); luaopen_math(L); luaopen_bit(L); // luaopen_package(L); // Omitted because we use our own package system. // luaopen_io(L); // Not safe for the sandbox. // luaopen_os(L); // Not safe for the sandbox. // luaopen_debug(L); // Not safe for the sandbox. // luaopen_jit(L); // Don't know what it's for. return 0; } LuaDefineHidden(source_load_cfunctions) { auto regs = LuaFunctionReg::all(); for (const LuaFunctionReg *r : regs) { const std::string &name = r->get_name(); size_t upos = name.find('_'); std::string classname; std::string funcname; if (upos == std::string::npos) { continue; } else { funcname = name.substr(upos + 1); classname = name.substr(0, upos); } lua_CFunction func = r->get_func(); int mode = r->get_mode(); if (mode == 3) { // Class Method lua_pushlstring(L, classname.c_str(), classname.size()); source_class(L); lua_pushcfunction(L, func); lua_setfield(L, -2, funcname.c_str()); } if ((mode == 1) || (mode == 2)) { // Global function or global method int top = lua_gettop(L); lua_getglobal(L, classname.c_str()); if (!lua_istable(L, -1)) { lua_pop(L, 1); lua_newtable(L); lua_pushvalue(L, -1); lua_setglobal(L, classname.c_str()); } lua_pushcfunction(L, func); lua_setfield(L, -2, funcname.c_str()); lua_settop(L, top); } if (mode == 1) { // Global function lua_pushcfunction(L, func); lua_setglobal(L, funcname.c_str()); } } return 0; } // Fetches the source database and runs all the loadresults. // // Returns a single string, which is a bunch of concatenated error // messages. // LuaDefineHidden(source_load_lfunctions) { LuaRet errors; LuaVar sourcedb, key, info, seq, closure, err; LuaStack LS(L, sourcedb, errors, key, info, seq, closure, err); // Get the source database. LS.getfield(sourcedb, LuaRegistry, "sourcedb"); if (LS.type(sourcedb) != LUA_TTABLE) { LS.newtable(sourcedb); LS.setfield(LuaRegistry, "sourcedb", sourcedb); } // Sort the keys by sequence number. std::map indices; LS.set(key, LuaNil); while (LS.next(sourcedb, key, info) != 0) { LS.getfield(seq, info, "sequence"); indices[LS.tointeger(seq)] = LS.tostring(key); } // Now call the closures in the proper order. std::stringstream errss; for (const auto &p : indices) { LS.rawget(info, sourcedb, p.second); LS.getfield(closure, info, "loadresult"); // If there's already an error in the sourcedb, collect it. if (!LS.isfunction(closure)) { errss << LS.tostring(closure); continue; } // Call the closure. If there's an error, collect it. lua_pushvalue(L, closure.index()); if (lua_pcall(L, 0, 0, 0) != 0) { lua_replace(L, err.index()); errss << LS.tostring(err); } } LS.set(errors, errss.str()); return LS.result(); } LuaDefineGlobalMethod(source_rebuild) { LuaVar errs; LuaStack LS(L, errs); source_reset_classes(L); source_load_builtins(L); source_load_cfunctions(L); source_load_lfunctions(L); lua_replace(L, errs.index()); std::string errstr = LS.tostring(errs); std::cerr << errstr; return LS.result(); }