#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 #include #include LuaDefine(makeclass, "classname", "create a class if it doesn't already exist") { LuaArg classname; LuaRet classtab; LuaStack LS(L, classname, classtab); if (!LS.isstring(classname)) { luaL_error(L, "class name must be a string"); } if (!LS.validclassname(classname)) { luaL_error(L, "invalid class name: %s", LS.ckstring(classname).c_str()); }; LS.makeclass(classtab, classname); return LS.result(); } LuaDefine(getclass, "classname", "get the classtab with the specified name") { LuaArg classname; LuaRet classtab; LuaStack LS(L, classname, classtab); eng::string err = LS.getclass(classtab, classname); if (err != "") { luaL_error(L, "%s", err.c_str()); } return LS.result(); } LuaDefine(classname, "classtable", "get the class name from a class table") { LuaArg table; LuaRet result; LuaStack 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 void get_reg_name(const LuaFunctionReg *reg, std::string_view &classname, std::string_view &funcname) { std::string_view name(reg->get_name()); size_t upos = name.find('_'); if (upos == std::string_view::npos) { funcname = name; classname = ""; } else { funcname = name.substr(upos + 1); classname = name.substr(0, upos); } } static void get_info_table(LuaStack &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(LuaStack &LS0, LuaSlot info, const eng::string &fn, const eng::string &code) { LuaVar loadresult; LuaStack LS(LS0.state(), loadresult); if (code == "") { LS.rawset(info, "loadresult", "missing or empty source file"); } else { eng::string chunk = "=" + fn; luaL_loadbuffer(LS.state(), code.c_str(), code.size(), chunk.c_str()); lua_replace(LS.state(), loadresult.index()); 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; LuaStack SLS(lua_state_, sdb, sfn, sinfo, shash, sseq); LuaStack 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); MLS.result(); SLS.result(); } bool SourceDB::patch(StreamBuffer *sb, DebugCollector *dbc) { lua_State *L = lua_state_; LuaVar db, info; LuaStack 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); } } } LS.result(); return (nupdates > 0); } void SourceDB::set(const eng::string &fn, const eng::string &code, int sequence) { lua_State *L = lua_state_; LuaVar db, info; LuaStack 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); LS.result(); } eng::string SourceDB::get(const eng::string &fn) { lua_State *L = lua_state_; LuaVar db, info, code, sequence, loadresult; LuaStack LS(L, db, info, code, sequence, loadresult); LS.rawget(db, LuaRegistry, "sourcedb"); LS.rawget(info, db, fn); if (!LS.istable(info)) { LS.result(); 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); } eng::ostringstream oss; oss << seqno << ":" << ccode << ":" << cloadresult; LS.result(); return oss.str(); } void SourceDB::update(const util::LuaSourceVec &source) { lua_State *L = lua_state_; LuaVar sourcedb, info; LuaStack 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; std::cerr << "Compiling " << file << std::endl; 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); } LS.result(); } // Delete everything from the global environment except // the class tables. // static void source_clear_globals(lua_State *L) { LuaVar classname, classtab, key, globtab; LuaStack LS(L, classname, classtab, key, globtab); LS.getglobaltable(globtab); LS.rawset(globtab, "_G", LuaNil); LS.set(classname, LuaNil); while (LS.next(globtab, classname, classtab) != 0) { if (LS.rawequal(globtab, classtab)) { LS.rawset(globtab, classname, LuaNil); } else if (LS.istable(classtab)) { LS.cleartable(classtab, true); } else { LS.rawset(globtab, classname, LuaNil); } } LS.rawset(globtab, "_G", globtab); LS.result(); } // Load all the 'LuaDefine' C functions into the lua state. // static void source_load_cfunctions(lua_State *L) { LuaVar classobj; LuaStack 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; std::string_view funcname; get_reg_name(r, classname, funcname); if (classname.empty()) { LS.getglobaltable(classobj); LS.rawset(classobj, funcname, func); } else { LS.makeclass(classobj, classname); LS.rawset(classobj, funcname, func); } } } LS.result(); } // Run all the closures from the source database. // static eng::string source_load_lfunctions(lua_State *L) { LuaVar sourcedb, key, info, seq, closure, err; LuaStack LS(L, sourcedb, key, info, seq, closure, err); // Get the source database. LS.rawget(sourcedb, LuaRegistry, "sourcedb"); if (LS.type(sourcedb) != LUA_TTABLE) { LS.newtable(sourcedb); LS.rawset(LuaRegistry, "sourcedb", sourcedb); } // 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); } // Now call the closures in the proper order. eng::ostringstream errss; for (const auto &p : indices) { LS.rawget(info, sourcedb, p.second); LS.rawget(closure, info, "loadresult"); // If there's already an error in the sourcedb, collect it. if (!LS.isfunction(closure)) { errss << LS.ckstring(closure) << "\n"; continue; } // Call the closure. If there's an error, collect it. lua_pushvalue(L, closure.index()); if (traceback_pcall(L, 0, 0) != 0) { lua_replace(L, err.index()); errss << LS.ckstring(err); } } LS.result(); return errss.str(); } eng::string SourceDB::rebuild() { lua_State *L = lua_state_; LuaVar mathclass; LuaStack LS(L, mathclass); source_clear_globals(L); source_load_cfunctions(L); eng::string errs = source_load_lfunctions(L); // A few builtin constants. These are hardwired. LS.makeclass(mathclass, "math"); LS.rawset(mathclass, "pi", M_PI); LS.rawset(mathclass, "huge", HUGE_VAL); LS.result(); return errs; } void SourceDB::run_unittests() { lua_State *L = lua_state_; LuaVar unittests, name, func, err, globtab; LuaStack LS(L, unittests, name, func, err, globtab); LS.getglobaltable(globtab); LS.rawget(unittests, globtab, "unittests"); // Sort the unit test names. eng::set names; LS.set(name, LuaNil); while (LS.next(unittests, name, func) != 0) { if (LS.isfunction(func) && LS.isstring(name)) { names.insert(LS.ckstring(name)); } } // Run the functions in order bool any = false; for (const eng::string &name : names) { std::cerr << "Running unittests." << name << std::endl; LS.rawget(func, unittests, name); lua_pushvalue(L, func.index()); if (traceback_pcall(L, 0, 0) != 0) { lua_replace(L, err.index()); std::cerr << LS.ckstring(err); any = true; } } if (any) { exit(1); } LS.result(); } void SourceDB::init(lua_State *L) { lua_state_ = L; LuaVar globtab, persist, unpersist, classname, classtab, funcname, funcp, rawfunc, nullstring; LuaStack 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(); // 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); } } // This function should not touch the dlmalloc heap. void SourceDB::register_lua_builtins() { lua_State *L = LuaStack::newstate(nullptr); luaL_openlibs(L); LuaVar globals, lclassname, lfuncname, classtab, func; LuaStack 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 funcname; std::string_view classname; get_reg_name(reg, classname, funcname); 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) { std::cerr << "No such builtin function: " << classname << " " << funcname << std::endl; } 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)) { std::cerr << "Failed to declare builtin: " << LS.ckstring(lclassname) << "." << LS.ckstring(lfuncname) << std::endl; } } } } } lua_close(L); } eng::string SourceDB::function_docs(const LuaStack &LS0, LuaSlot fn) { lua_State *L = LS0.state(); LuaVar sourcedb, fname, finfo, code; LuaStack LS(L, sourcedb, fname, finfo, code); if (LS.iscfunction(fn)) { lua_CFunction cfn = lua_tocfunction(L, fn.index()); const LuaFunctionReg *reg = LuaFunctionReg::lookup(cfn); if (reg == nullptr) { return ""; } std::string_view classname; std::string_view funcname; get_reg_name(reg, classname, funcname); eng::ostringstream oss; util::StringVec docs = util::split_docstring(reg->get_docs()); for (const eng::string &line : docs) { oss << "-- " << line << std::endl; } oss << "function "; if (!classname.empty()) { oss << classname << "."; } oss << funcname << "(" << reg->get_args() << ")"; return oss.str(); } else if (LS.isfunction(fn)) { lua_Debug ar; lua_pushvalue(L, fn.index()); int status = lua_getinfo(L, ">S", &ar); if (status == 0) return ""; // 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, eng::string(ar.short_src)); 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 ""; } // Split the code into lines. util::StringVec lines = util::split_lines(LS.ckstring(code)); int linehi = ar.linedefined - 1; if ((linehi < 0) || (linehi >= int(lines.size()))) { return ""; } // Incorporate the function comment. int linelo = linehi; while ((linelo > 0) && (util::is_lua_comment(lines[linelo-1]))) linelo -= 1; // Output the docs. eng::ostringstream result; for (int i = linelo; i <= linehi; i++) { result << lines[i] << std::endl; } return result.str(); } else { return ""; } } // These should go away eventually. They're for debugging. LuaDefine(coroutine_setnextid, "thread,id", "set the next id of a thread (debugging only)") { LuaArg co, lid; LuaStack LS(L, co, lid); lua_State *CO = LS.ckthread(co); lua_Number id = LS.ckinteger(lid); lua_setnextid(CO, id); return LS.result(); } LuaDefine(coroutine_getnextid, "thread", "get the next id of a thread (debugging only)") { LuaArg co; LuaRet lid; LuaStack LS(L, co, lid); lua_State *CO = LS.ckthread(co); LS.set(lid, lua_getnextid(CO)); return LS.result(); } 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"); 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_format, "formatstr, v1,v2,v3...", "generate formatted output string"); 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_random, "[m [, n]]", "return random [0.0-1.0), or [1-m], or [m-n]."); LuaDefineBuiltin(math_randomseed, "x", "set x as the seed for random numbers"); 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"); 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(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(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, "", "");