#define _USE_MATH_DEFINES #include #include "wrap-string.hpp" #include "wrap-vector.hpp" #include "wrap-map.hpp" #include "wrap-set.hpp" #include "wrap-sstream.hpp" #include "util.hpp" #include "luastack.hpp" #include "traceback.hpp" #include "table.hpp" #include "source.hpp" #include "luasnap.hpp" #include #include LuaDefine(makeclass, "classname", "create a class if it doesn't already exist") { LuaArg classname; LuaRet classtab; LuaDefStack LS(L, classname, classtab); if (!sv::is_lua_classname(LS.ckstring(classname))) { luaL_error(L, "invalid class name: %s", LS.ckstring(classname).c_str()); }; LS.makeclass(classtab, classname); return LS.result(); } LuaDefine(getclass, "x", "|Get the class table of X." "|" "|The object passed in can be:" "|" "| * A valid class table." "| * A valid, existing class name." "| * A tangible that has a class." "| * A normal table with a class metatable." "|" "|On success, returns the class table." "|On failure, throws an error." ) { LuaArg input; LuaRet classtab; LuaDefStack LS(L, input, classtab); eng::string err = LS.getclass(classtab, input); if (!err.empty()) { luaL_error(L, "%s", err.c_str()); } return LS.result(); } LuaDefine(classname, "x", "|Get the class name of X." "|" "|The object passed in can be:" "|" "| * A valid class table." "| * A valid, existing class name." "| * A tangible that has a class." "| * A normal table with a class metatable." "|" "|If the object is none of these, returns empty string." "|This function does not throw errors." ) { LuaArg table; LuaRet result; LuaDefStack LS(L, table, result); eng::string rstr = LS.classname(table); if (rstr == "") { LS.set(result, LuaNil); } else { LS.set(result, rstr); } return LS.result(); } static std::string_view get_reg_classname(std::string_view name) { size_t upos = name.find('_'); if (upos == std::string_view::npos) { return ""; } else { return name.substr(0, upos); } } static std::string_view get_reg_funcname(std::string_view name) { size_t upos = name.find('_'); if (upos == std::string_view::npos) { return name; } else { return name.substr(upos + 1); } } static eng::string get_reg_fullname(std::string_view name) { size_t upos = name.find('_'); if (upos == std::string_view::npos) { return eng::string(name); } else { return eng::string(name.substr(0, upos)) + "." + eng::string(name.substr(upos + 1)); } } static eng::string get_reg_prototype(const LuaFunctionReg *reg) { eng::ostringstream oss; oss << "function " << get_reg_fullname(reg->get_name()); oss << "(" << reg->get_args() << ")"; return oss.str(); } static bool lines_contain_substring(const util::StringVec &lines, int lo, int hi, const eng::string &substring) { for (int i = lo; i < hi; i++) { if (sv::contains_substring_utf8(lines[i], substring)) { return true; } } return false; } static void get_info_table(LuaCoreStack &LS, LuaSlot db, LuaSlot info, const eng::string &fn) { LS.rawget(info, db, fn); if (!LS.istable(info)) { LS.set(info, LuaNewTable); LS.rawset(db, fn, info); } LS.rawset(info, "name", fn); } static void calculate_loadresult(LuaCoreStack &LS0, LuaSlot info, const eng::string &fn, const eng::string &code) { LuaVar loadresult; LuaExtStack LS(LS0.state(), loadresult); if (code == "") { LS.rawset(info, "loadresult", "missing or empty source file"); } else { LS.load(loadresult, code, fn); LS.rawset(info, "loadresult", loadresult); } } void SourceDB::diff(const SourceDB &auth, StreamBuffer *sb) { LuaVar sdb, sfn, sinfo, shash, sseq; LuaVar mdb, mfn, minfo, mhash, mseq, mcode; LuaExtStack SLS(lua_state_, sdb, sfn, sinfo, shash, sseq); LuaExtStack MLS(auth.lua_state_, mdb, mfn, minfo, mhash, mseq, mcode); sb->write_int32(0); int wc_after = sb->total_writes(); int nupdates = 0; // Fetch the two source databases. SLS.rawget(sdb, LuaRegistry, "sourcedb"); MLS.rawget(mdb, LuaRegistry, "sourcedb"); // Loop over the master database. MLS.set(mfn, LuaNil); while (MLS.next(mdb, mfn, minfo) != 0) { eng::string fn = MLS.ckstring(mfn); assert(MLS.istable(minfo)); MLS.rawget(mseq, minfo, "sequence"); MLS.rawget(mhash, minfo, "hash"); bool diffhash = true; bool diffseq = true; SLS.set(sfn, fn); SLS.rawget(sinfo, sdb, sfn); if (SLS.istable(sinfo)) { SLS.rawget(shash, sinfo, "hash"); SLS.rawget(sseq, sinfo, "sequence"); diffhash = MLS.ckstring(mhash) != SLS.ckstring(shash); diffseq = MLS.ckinteger(mseq) != SLS.ckinteger(sseq); } if (diffhash || diffseq) { sb->write_string(MLS.ckstring(mfn)); sb->write_int32(MLS.ckinteger(mseq)); if (diffhash) { MLS.rawget(mcode, minfo, "code"); sb->write_string(MLS.ckstring(mcode)); } else { sb->write_string("\001"); } nupdates += 1; } } // Loop over synch database. SLS.set(sfn, LuaNil); while (SLS.next(sdb, sfn, sinfo) != 0) { eng::string fn = SLS.ckstring(sfn); assert(SLS.istable(sinfo)); MLS.set(mfn, fn); MLS.rawget(minfo, mdb, mfn); if (!MLS.istable(minfo)) { sb->write_string(SLS.ckstring(sfn)); sb->write_int32(-1); sb->write_string(""); nupdates += 1; } } sb->overwrite_int32(wc_after, nupdates); } bool SourceDB::patch(StreamBuffer *sb, DebugCollector *dbc) { lua_State *L = lua_state_; LuaVar db, info; LuaExtStack LS(L, db, info); LS.rawget(db, LuaRegistry, "sourcedb"); int nupdates = sb->read_int32(); for (int i = 0; i < nupdates; i++) { eng::string fn = sb->read_string(); int sequence = sb->read_int32(); eng::string code = sb->read_string(); DebugLine(dbc) << "Source file " << fn << " updated"; if (sequence < 0) { LS.rawset(db, fn, LuaNil); } else { get_info_table(LS, db, info, fn); LS.rawset(info, "sequence", sequence); if (code != "\001") { LS.rawset(info, "code", code); LS.rawset(info, "hash", util::hash_to_hex(util::hash_string(code))); calculate_loadresult(LS, info, fn, code); } } } return (nupdates > 0); } void SourceDB::set(const eng::string &fn, const eng::string &code, int sequence) { lua_State *L = lua_state_; LuaVar db, info; LuaExtStack LS(L, db, info); LS.rawget(db, LuaRegistry, "sourcedb"); get_info_table(LS, db, info, fn); LS.rawset(info, "sequence", sequence); LS.rawset(info, "code", code); LS.rawset(info, "fingerprint", ""); LS.rawset(info, "hash", util::hash_to_hex(util::hash_string(code))); calculate_loadresult(LS, info, fn, code); } eng::string SourceDB::get(const eng::string &fn) { lua_State *L = lua_state_; LuaVar db, info, code, sequence, loadresult; LuaExtStack LS(L, db, info, code, sequence, loadresult); LS.rawget(db, LuaRegistry, "sourcedb"); LS.rawget(info, db, fn); if (!LS.istable(info)) { return ""; } LS.rawget(code, info, "code"); LS.rawget(sequence, info, "sequence"); LS.rawget(loadresult, info, "loadresult"); eng::string ccode = LS.ckstring(code); int seqno = LS.ckint(sequence); eng::string cloadresult; if (LS.isfunction(loadresult)) { cloadresult = ""; } else { cloadresult = LS.ckstring(loadresult); } return util::ss(seqno, ":", ccode, ":", cloadresult); } void SourceDB::update(const util::LuaSourceVec &source) { lua_State *L = lua_state_; LuaVar sourcedb, info; LuaExtStack LS(L, sourcedb, info); // Get and clear the source database. LS.rawget(sourcedb, LuaRegistry, "sourcedb"); if (!LS.istable(sourcedb)) { LS.newtable(sourcedb); LS.rawset(LuaRegistry, "sourcedb", sourcedb); } LS.cleartable(sourcedb, true); for (int i = 0; i < int(source.size()); i++) { const eng::string &file = source[i].first; const eng::string &code = source[i].second; LS.newtable(info); LS.rawset(info, "name", file); LS.rawset(info, "code", code); LS.rawset(info, "hash", util::hash_to_hex(util::hash_string(code))); LS.rawset(info, "sequence", i + 1); calculate_loadresult(LS, info, file, code); LS.rawset(sourcedb, file, info); } } // Delete everything from the global environment. // Clear all the classes in the registry classes table. // static void source_clear_globals(lua_State *L) { LuaVar classname, classtab, key, globtab, classes; LuaExtStack LS(L, classname, classtab, key, globtab, classes); LS.getglobaltable(globtab); LS.cleartable(globtab, true); LS.rawset(globtab, "_G", globtab); LS.rawget(classes, LuaRegistry, "classes"); assert(LS.istable(classes)); LS.set(classname, LuaNil); while (LS.next(classes, classname, classtab) != 0) { assert(LS.istable(classtab)); LS.cleartable(classtab, true); } } // Load all the 'LuaDefine' C functions into the lua state. // static void source_load_cfunctions(lua_State *L) { LuaVar classobj; LuaExtStack LS(L, classobj); for (auto r = LuaFunctionReg::All; r != nullptr; r=r->next()) { lua_CFunction func = r->get_func(); if ((func != nullptr) && (!r->get_sandbox())) { std::string_view classname = get_reg_classname(r->get_name()); std::string_view funcname = get_reg_funcname(r->get_name()); if (classname.empty()) { LS.getglobaltable(classobj); LS.rawset(classobj, funcname, func); } else { LS.makeclass(classobj, classname); LS.rawset(classobj, funcname, func); } } } } // Load all the 'LuaConstant' constants into the lua state. // static void source_load_cconstants(lua_State *L) { LuaVar classobj, value; LuaExtStack LS(L, classobj, value); for (auto r = LuaConstantReg::All; r != nullptr; r=r->next()) { if (r->get_tokenvalue().empty()) { LS.set(value, r->get_numbervalue()); } else { LS.set(value, r->get_tokenvalue()); } std::string_view classname = get_reg_classname(r->get_name()); std::string_view funcname = get_reg_funcname(r->get_name()); if (classname.empty()) { LS.getglobaltable(classobj); LS.rawset(classobj, funcname, value); } else { LS.makeclass(classobj, classname); LS.rawset(classobj, funcname, value); } } } eng::string SourceDB::get_source(const eng::string &fn) { LuaVar sourcedb, fname, finfo, code; LuaExtStack LS(lua_state_, sourcedb, fname, finfo, code); // Get the source database. LS.rawget(sourcedb, LuaRegistry, "sourcedb"); if (!LS.istable(sourcedb)) return ""; // Get the finfo table from the source db. LS.set(fname, fn); LS.rawget(finfo, sourcedb, fname); if (!LS.istable(finfo)) return ""; // Get the code from the finfo table. LS.rawget(code, finfo, "code"); if (!LS.isstring(code)) return ""; return LS.ckstring(code); } eng::vector SourceDB::modules() { eng::vector result; LuaVar sourcedb, key, info, seq; LuaExtStack LS(lua_state_, sourcedb, key, info, seq); result.push_back("CORE"); // Get the source database. LS.rawget(sourcedb, LuaRegistry, "sourcedb"); if (!LS.istable(sourcedb)) { return result; } // Sort the keys by sequence number. eng::map indices; LS.set(key, LuaNil); while (LS.next(sourcedb, key, info) != 0) { LS.rawget(seq, info, "sequence"); indices[LS.ckinteger(seq)] = LS.ckstring(key); } for (const auto &pair : indices) { result.push_back(pair.second); } return result; } void SourceDB::rebuild_core() { source_clear_globals(lua_state_); source_load_cfunctions(lua_state_); source_load_cconstants(lua_state_); } eng::string SourceDB::rebuild_module(const eng::string &mod) { if (mod == "CORE") { rebuild_core(); return ""; } else { LuaVar sourcedb, info, closure; LuaExtStack LS(lua_state_, sourcedb, info, closure); LS.rawget(sourcedb, LuaRegistry, "sourcedb"); if (!LS.istable(sourcedb)) { return "SourceDB not initialized"; } LS.rawget(info, sourcedb, mod); if (!LS.istable(info)) { return util::ss("No such module: ", mod); } LS.rawget(closure, info, "loadresult"); if (!LS.isfunction(closure)) { return util::ss(mod, ":", LS.ckstring(closure)); } lua_pushvalue(lua_state_, closure.index()); return traceback_pcall(lua_state_, 0, 0); } } void SourceDB::init(lua_State *L) { register_lua_builtins(); lua_state_ = L; LuaVar globtab, persist, unpersist, classname, classtab, funcname, funcp, rawfunc, nullstring; LuaExtStack LS(L, globtab, persist, unpersist, classname, classtab, funcname, funcp, rawfunc, nullstring); LS.getglobaltable(globtab); LS.rawset(LuaRegistry, "sourcedb", LuaNewTable); // Set the metatable for strings. LS.makeclass(classtab, "string"); LS.set(nullstring, ""); LS.setmetatable(nullstring, classtab); // Rebuild the global environment. rebuild_core(); // We need to register all C functions with the eris permanents tables. LS.rawget(persist, LuaRegistry, "persist"); LS.rawget(unpersist, LuaRegistry, "unpersist"); LS.set(classname, LuaNil); while (LS.next(globtab, classname, classtab) != 0) { if (LS.isstring(classname) && LS.istable(classtab)) { LS.set(funcname, LuaNil); while (LS.next(classtab, funcname, funcp) != 0) { if (LS.isstring(funcname) && LS.iscfunction(funcp)) { eng::string full = "cfunc:"; full += LS.ckstring(classname); full += "."; full += LS.ckstring(funcname); lua_pushcfunction(L, lua_tocfunction(L, funcp.index())); lua_replace(L, rawfunc.index()); LS.rawset(persist, rawfunc, full); LS.rawset(unpersist, full, rawfunc); } } } } } void SourceDB::serialize_source(const util::LuaSourceVec &sv, StreamBuffer *sb) { sb->write_int32(sv.size()); for (const auto &pair : sv) { sb->write_string(pair.first); sb->write_string(pair.second); } } void SourceDB::deserialize_source(util::LuaSourceVec *sv, StreamBuffer *sb) { sv->clear(); int count = sb->read_int32(); if ((count < 0) || (count > 10000)) throw StreamCorruption(); for (int i = 0; i < count; i++) { eng::string fn = sb->read_string(); eng::string code = sb->read_string(); sv->emplace_back(fn, code); } } // Register lua builtins. // // To find the lua builtins, we use this approach: we create a new lua state // (the 'prototype', we call it), and we call luaL_openlibs on it. The // prototype should now contain all the builtins. // // Then, we iterate over our function registry, looking for LuaDefineBuiltin // records. These records don't have a function pointer yet. For each such // record, we look into the prototype, and look up the function in the global // environment. If it's not found, we print an error. If we find it, we extract // the C function pointer from it. Then, we remove the function from the // prototype, to indicate that we already processed that function. // // At the end, we scan the prototype to make sure all functions have been // removed. If not, we know our function registry is missing something and we // print out an error. // void SourceDB::register_lua_builtins() { static bool initialized = false; if (initialized) return; initialized = true; lua_State *L = LuaCoreStack::newstate(nullptr); luaL_openlibs(L); { LuaVar globals, lclassname, lfuncname, classtab, func; LuaExtStack LS(L, globals, lclassname, lfuncname, classtab, func); LS.getglobaltable(globals); // Iterate over the function registry, copying function pointers from // the prototype lua state into the registry, then remove the closure // from the prototype. for (auto reg = LuaFunctionReg::All; reg != nullptr; reg=reg->next()) { std::string_view classname = get_reg_classname(reg->get_name()); std::string_view funcname = get_reg_funcname(reg->get_name()); if (classname.empty()) { LS.getglobaltable(classtab); } else { LS.rawget(classtab, globals, classname); } lua_CFunction builtin = nullptr; if (LS.istable(classtab)) { LS.rawget(func, classtab, funcname); if (LS.iscfunction(func)) { builtin = lua_tocfunction(L, func.index()); LS.rawset(classtab, funcname, LuaNil); } } if (reg->get_func() == nullptr) { if (builtin == nullptr) { if (!reg->get_sandbox()) { util::dprint("No such builtin function: ", classname, " ", funcname); } } else { reg->set_func(builtin); } } } // Iterate over the prototype. All cfunctions should have been removed. LS.set(lclassname, LuaNil); while (LS.next(globals, lclassname, classtab)) { if (LS.isstring(lclassname)) { if (LS.istable(classtab)) { LS.set(lfuncname, LuaNil); while (LS.next(classtab, lfuncname, func)) { if (LS.iscfunction(func)) { util::dprint("Failed to declare builtin: ", LS.ckstring(lclassname), ".", LS.ckstring(lfuncname)); } } } } } } lua_close(L); } bool SourceDB::search_docs(const eng::string &substring, std::ostream &ostream) { eng::map results; // Search the built-in functions. for (const LuaFunctionReg *reg = LuaFunctionReg::All; reg != nullptr; reg=reg->next()) { eng::string proto = get_reg_prototype(reg); if (reg->get_sandbox()) continue; if (sv::has_prefix(reg->get_name(), "unittests_")) continue; if (sv::contains_substring_utf8(proto, substring) || sv::contains_substring_utf8(reg->get_docs(), substring)) { std::ostringstream oss; oss << proto; util::StringVec docs = util::split_docstring(reg->get_docs()); for (const eng::string &line : docs) { if (sv::contains_substring_utf8(line, substring)) { oss << " -- " << sv::trim(line); break; } } oss << std::endl; results[get_reg_fullname(reg->get_name())] = oss.str(); } } // Search the lua source code. for (const eng::string &module : modules()) { if (module == "CORE") continue; eng::string code = get_source(module); if (code.empty()) continue; util::StringVec lines = util::split_lines(code); int comment_lines = 0; for (int i = 0; i < int(lines.size()); i++) { std::string_view fullname = sv::lua_function_proto_name(lines[i]); if (sv::has_prefix(fullname, "unittests.")) continue; if (!fullname.empty()) { if (lines_contain_substring(lines, i - comment_lines, i+1, substring)) { eng::ostringstream oss; oss << lines[i]; for (int j = i - comment_lines; j < i; j++) { if (sv::contains_substring_utf8(lines[j], substring)) { oss << " " << sv::trim(lines[j]); break; } } oss << std::endl; results[eng::string(fullname)] = oss.str(); } comment_lines = 0; } else if (sv::is_lua_comment(lines[i])) { comment_lines++; } else { comment_lines = 0; } } } for (const auto &pair : results) { ostream << pair.second; } return !results.empty(); } bool SourceDB::function_docs(const LuaCoreStack &LS, LuaSlot fn, std::ostream &ostream) { lua_State *L = LS.state(); if (LS.iscfunction(fn)) { lua_CFunction cfn = lua_tocfunction(L, fn.index()); const LuaFunctionReg *reg = LuaFunctionReg::lookup(cfn); if (reg == nullptr) return false; ostream << get_reg_prototype(reg) << std::endl; util::StringVec docs = util::split_docstring(reg->get_docs()); ostream << "--" << std::endl; for (const eng::string &line : docs) { ostream << "-- " << line << std::endl; } ostream << "--" << std::endl; return true; } else if (LS.isfunction(fn)) { lua_Debug ar; lua_pushvalue(L, fn.index()); int status = lua_getinfo(L, ">S", &ar); if (status == 0) return false; // Get the source code. util::StringVec lines = util::split_lines(get_source(eng::string(ar.short_src))); if (lines.empty()) return false; // Find the line of code containing the function prototype. // Lua numbers source lines from 1, but we number lines from 0, // so we have to subtract one. int linehi = ar.linedefined - 1; if ((linehi < 0) || (linehi >= int(lines.size()))) { return false; } // Incorporate the function comment. int linelo = linehi; while ((linelo > 0) && (sv::is_lua_comment(lines[linelo-1]))) linelo -= 1; // Output the docs. ostream << lines[linehi] << std::endl; ostream << "--" << std::endl; for (int i = linelo; i < linehi; i++) { ostream << lines[i] << std::endl; } ostream << "--" << std::endl; return true; } else { return false; } } LuaDefine(unittests_sourcedb, "", "some unit tests") { LuaSnap msnap; LuaSnap ssnap; SourceDB mdb; SourceDB sdb; mdb.init(msnap.state()); sdb.init(ssnap.state()); StreamBuffer sb; // Create a code database using 'set'. mdb.set("foo", "function foo() print('foo') end", 1); mdb.set("bar", "function bar() print('bar') end", 2); mdb.set("zoo", "funcjdshja mxooso yowza!", 3); // Verify that get works. LuaAssertStrEq(L, mdb.get("foo"), "1:function foo() print('foo') end:"); LuaAssertStrEq(L, mdb.get("bar"), "2:function bar() print('bar') end:"); LuaAssertStrEq(L, mdb.get("zoo"), "3:funcjdshja mxooso yowza!:zoo:1: syntax error near 'mxooso'"); LuaAssertStrEq(L, mdb.get("baz"), ""); // Difference transmit. sdb.diff(mdb, &sb); // There should still be nothing in the sdb. LuaAssertStrEq(L, sdb.get("foo"), ""); LuaAssertStrEq(L, sdb.get("bar"), ""); LuaAssertStrEq(L, sdb.get("zoo"), ""); LuaAssertStrEq(L, sdb.get("baz"), ""); // Apply the diffs. sdb.patch(&sb, nullptr); // Everything should now be copied to sdb. LuaAssertStrEq(L, sdb.get("foo"), "1:function foo() print('foo') end:"); LuaAssertStrEq(L, sdb.get("bar"), "2:function bar() print('bar') end:"); LuaAssertStrEq(L, sdb.get("zoo"), "3:funcjdshja mxooso yowza!:zoo:1: syntax error near 'mxooso'"); LuaAssertStrEq(L, sdb.get("baz"), ""); // Make a single change to the code. mdb.set("bar", "function bar1() print('bar1') end", 2); // Diff and patch sdb.diff(mdb, &sb); sdb.patch(&sb, nullptr); // Verify that it's been updated. LuaAssertStrEq(L, sdb.get("foo"), "1:function foo() print('foo') end:"); LuaAssertStrEq(L, sdb.get("bar"), "2:function bar1() print('bar1') end:"); LuaAssertStrEq(L, sdb.get("zoo"), "3:funcjdshja mxooso yowza!:zoo:1: syntax error near 'mxooso'"); LuaAssertStrEq(L, sdb.get("baz"), ""); // Make a single change to just a sequence number. mdb.set("foo", "function foo() print('foo') end", 6); // Diff sdb.diff(mdb, &sb); // The only thing we've updated is a single sequence number. Make // sure the number of bytes transmitted is reasonable. // 4 bytes for the count of changes // 4 bytes for "foo" // 4 bytes for the sequence number // 2 bytes for unmodified code // This verifies that the hash comparisons are working. LuaAssert(L, sb.fill() == 14); // Patch. sdb.patch(&sb, nullptr); // Verify that it's been updated. LuaAssertStrEq(L, sdb.get("foo"), "6:function foo() print('foo') end:"); LuaAssertStrEq(L, sdb.get("bar"), "2:function bar1() print('bar1') end:"); LuaAssertStrEq(L, sdb.get("zoo"), "3:funcjdshja mxooso yowza!:zoo:1: syntax error near 'mxooso'"); LuaAssertStrEq(L, sdb.get("baz"), ""); return 0; } LuaDefineBuiltin(table_concat, "vector1, vector2", "concatenate two vectors"); LuaDefineBuiltin(table_insert, "vector, pos, value", "insert an element into a vector"); LuaDefineBuiltin(table_remove, "vector, pos", "remove an element from a vector"); LuaDefineBuiltin(table_sort, "vector [,comparefn]", "sort a vector"); LuaDefineBuiltin(table_pack, "v1, v2, v3...", "turn a sequence of arguments into a vector"); LuaDefineBuiltin(table_unpack, "vector", "turn a vector into a sequence of return values"); LuaSandboxBuiltin(table_maxn, "", ""); LuaDefineBuiltin(string_byte, "str [,index]", "get a single byte from a string"); LuaDefineBuiltin(string_char, "byte, byte,...", "convert sequence of bytes to a string"); LuaDefineBuiltin(string_find, "str, pattern [,index]", "return start and end of pattern in a string"); LuaDefineBuiltin(string_len, "str", "return the length of the string, in bytes"); LuaDefineBuiltin(string_rep, "str, count", "repeat the string some number of times"); LuaDefineBuiltin(string_reverse, "str", "reverse the bytes of the string"); LuaDefineBuiltin(string_lower, "str", "convert string to lowercase"); LuaDefineBuiltin(string_upper, "str", "convert string to uppercase"); LuaDefineBuiltin(string_gmatch, "str, pattern", "iterate over pattern-matched substrings"); LuaDefineBuiltin(string_gsub, "str, pattern, replace", "global replace pattern in string"); LuaDefineBuiltin(string_match, "str, pattern", "return start and end of pattern in string"); LuaDefineBuiltin(string_sub, "str, pos1, pos2", "return substring of str from pos1 to pos2"); LuaSandboxBuiltin(string_dump, "func", "convert a function to a string"); LuaDefineBuiltin(bit32_arshift, "n, shift", "shift 32-bit number to the right, keeping high bit unchanged"); LuaDefineBuiltin(bit32_band, "n, n, n...", "return the bitwise and of all 32-bit numbers"); LuaDefineBuiltin(bit32_bnot, "n", "return the bitwise negation of n, ie, (-1 - n) % 2^32"); LuaDefineBuiltin(bit32_bor, "n, n, n...", "return the bitwise or of all 32-bit arguments"); LuaDefineBuiltin(bit32_bxor, "n, n, n...", "return the bitwise exclusive or of all 32-bit arguments"); LuaDefineBuiltin(bit32_btest, "n, n, n...", "compute bitwise and of all 32-bit arguments, return true if nonzero"); LuaDefineBuiltin(bit32_extract, "n, field, width", "return value from extracted bitfield of 32-bit number"); LuaDefineBuiltin(bit32_lrotate, "n, shift", "rotate 32-bit number to the left"); LuaDefineBuiltin(bit32_lshift, "n, shift", "shift 32-bit number to the left, padding with zeros"); LuaDefineBuiltin(bit32_replace, "n, v, field, width", "change value of extracted bitfield in 32-bit number"); LuaDefineBuiltin(bit32_rrotate, "n, shift", "rotate 32-bit number to the right"); LuaDefineBuiltin(bit32_rshift, "n, shift", "shift 32-bit number to the right, padding with zeros"); LuaDefineBuiltin(math_abs, "x", "return absolute value of x"); LuaDefineBuiltin(math_acos, "x", "return arc-cosine of x in radians"); LuaDefineBuiltin(math_asin, "x", "return arc-sine of x in radians"); LuaDefineBuiltin(math_atan, "x", "return the arc-tangent of x in radians"); LuaDefineBuiltin(math_atan2, "y, x", "return arc-tangent of y/x, using signs to compute quadrant"); LuaDefineBuiltin(math_ceil, "x", "return the smallest integer larger than or equal to x"); LuaDefineBuiltin(math_cos, "x", "return the cosine of x in radians"); LuaDefineBuiltin(math_cosh, "x", "return the hyperbolic cosine of x in radians"); LuaDefineBuiltin(math_deg, "rad", "convert radians to degrees"); LuaDefineBuiltin(math_exp, "x", "returns the value e^x"); LuaDefineBuiltin(math_floor, "x", "returns the smallest integer less than or equal to x"); LuaDefineBuiltin(math_fmod, "x, y", "return the remainder of x/y that rounds the quotient towards zero"); LuaDefineBuiltin(math_frexp, "x", "given x, returns mantissa and exponent"); LuaDefineBuiltin(math_ldexp, "x", "return unnormalized mantissa and exponent"); LuaDefineBuiltin(math_log, "x [, base]", "return the log of x in base, default base is e"); LuaDefineBuiltin(math_max, "x, x, x...", "return the largest argument"); LuaDefineBuiltin(math_min, "x, x, x...", "return the smallest argument"); LuaDefineBuiltin(math_modf, "x", "returns the integral and fractional part of x"); LuaDefineBuiltin(math_pow, "x, y", "returns x ^ y, equivalent to the operator"); LuaDefineBuiltin(math_rad, "deg", "convert degrees to radians"); LuaDefineBuiltin(math_sin, "x", "return the sine of x in radians"); LuaDefineBuiltin(math_sinh, "x", "return the hyperbolic sine of x in radians"); LuaDefineBuiltin(math_sqrt, "x", "return the square root of x"); LuaDefineBuiltin(math_tan, "x", "return the tangent of x in radians"); LuaDefineBuiltin(math_tanh, "x", "return the hyperbolic tangent of x in radians"); LuaSandboxBuiltin(math_log10, "", ""); LuaNumberConstant(math_pi, M_PI, ""); LuaNumberConstant(math_huge, HUGE_VAL, ""); LuaNumberConstant(math_nan, NAN, ""); LuaNumberConstant(math_maxint, LuaCoreStack::MAXINT, ""); LuaTokenConstant(math_auto, "auto", "A value used to request that a value should be automatically computed"); // math.random and math.randomseed are in world-accessor.cpp, because // generating random numbers must manipulate global state which is // stored in the world model. LuaDefineBuiltin(assert, "flag [,message]", "assert that flag is true, if not, raise error"); LuaDefineBuiltin(error, "message", "raise an error"); LuaDefineBuiltin(getmetatable, "table", "get the metatable of a table"); LuaDefineBuiltin(ipairs, "vector", "meant to be used in loops, iterates over a vector"); LuaDefineBuiltin(next, "table, k", "returns the key after k and the corresponding value"); LuaDefineBuiltin(pairs, "table", "meant to be used in loops, iterates over a table"); LuaDefineBuiltin(rawpairs, "table", "meant to be used in loops, iterates over a table"); LuaDefineBuiltin(pcall, "function, arg1, arg2, ...", "call the function, catching any errors"); LuaDefineBuiltin(rawequal, "val1, val2", "return true if the two values are the same object"); LuaDefineBuiltin(rawlen, "obj", "return the length of a vector or string"); LuaDefineBuiltin(rawget, "table, key", "get a table entry, ignoring metamethods"); LuaDefineBuiltin(rawset, "table, key, value", "set a table entry, ignoring metamethods"); LuaDefineBuiltin(select, "n, arg1, arg2, ...", "return the nth argument"); LuaDefineBuiltin(setmetatable, "table, meta", "set the metatable of the specified table"); LuaDefineBuiltin(tonumber, "str", "convert a string to a number"); LuaDefineBuiltin(type, "obj", "return the type of obj as a string"); // print is redefined in world.cpp (because it prints into the world model) // tostring is redefined in pprint.cpp LuaSandboxBuiltin(collectgarbage, "", ""); LuaSandboxBuiltin(dofile, "", ""); LuaSandboxBuiltin(xpcall, "", ""); LuaSandboxBuiltin(loadfile, "", ""); LuaSandboxBuiltin(load, "", ""); LuaSandboxBuiltin(require, "", ""); LuaSandboxBuiltin(module, "", ""); LuaSandboxBuiltin(loadstring, "", ""); LuaSandboxBuiltin(unpack, "", ""); LuaSandboxBuiltin(debug_debug, "", ""); LuaSandboxBuiltin(debug_getuservalue, "", ""); LuaSandboxBuiltin(debug_gethook, "", ""); LuaSandboxBuiltin(debug_getinfo, "", ""); LuaSandboxBuiltin(debug_getlocal, "", ""); LuaSandboxBuiltin(debug_getregistry, "", ""); LuaSandboxBuiltin(debug_getmetatable, "", ""); LuaSandboxBuiltin(debug_getupvalue, "", ""); LuaSandboxBuiltin(debug_upvaluejoin, "", ""); LuaSandboxBuiltin(debug_upvalueid, "", ""); LuaSandboxBuiltin(debug_setuservalue, "", ""); LuaSandboxBuiltin(debug_sethook, "", ""); LuaSandboxBuiltin(debug_setlocal, "", ""); LuaSandboxBuiltin(debug_setmetatable, "", ""); LuaSandboxBuiltin(debug_setupvalue, "", ""); LuaSandboxBuiltin(debug_traceback, "", ""); LuaSandboxBuiltin(eris_persist, "", ""); LuaSandboxBuiltin(eris_unpersist, "", ""); LuaSandboxBuiltin(eris_settings, "", ""); LuaSandboxBuiltin(package_loadlib, "", ""); LuaSandboxBuiltin(package_searchpath, "", ""); LuaSandboxBuiltin(package_seeall, "", ""); LuaSandboxBuiltin(coroutine_create, "", ""); LuaSandboxBuiltin(coroutine_resume, "", ""); LuaSandboxBuiltin(coroutine_running, "", ""); LuaSandboxBuiltin(coroutine_status, "", ""); LuaSandboxBuiltin(coroutine_wrap, "", ""); LuaSandboxBuiltin(coroutine_yield, "", ""); LuaSandboxBuiltin(io_close, "", ""); LuaSandboxBuiltin(io_flush, "", ""); LuaSandboxBuiltin(io_input, "", ""); LuaSandboxBuiltin(io_lines, "", ""); LuaSandboxBuiltin(io_open, "", ""); LuaSandboxBuiltin(io_output, "", ""); LuaSandboxBuiltin(io_popen, "", ""); LuaSandboxBuiltin(io_read, "", ""); LuaSandboxBuiltin(io_tmpfile, "", ""); LuaSandboxBuiltin(io_type, "", ""); LuaSandboxBuiltin(io_write, "", ""); LuaSandboxBuiltin(os_clock, "", ""); LuaSandboxBuiltin(os_date, "", ""); LuaSandboxBuiltin(os_difftime, "", ""); LuaSandboxBuiltin(os_execute, "", ""); LuaSandboxBuiltin(os_exit, "", ""); LuaSandboxBuiltin(os_getenv, "", ""); LuaSandboxBuiltin(os_remove, "", ""); LuaSandboxBuiltin(os_rename, "", ""); LuaSandboxBuiltin(os_setlocale, "", ""); LuaSandboxBuiltin(os_time, "", ""); LuaSandboxBuiltin(os_tmpname, "", "");