Advertisement
Alakazard12

OpenComptuers kernel.lua

Oct 14th, 2014
343
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 24.86 KB | None | 0 0
  1. local hookInterval = 100
  2. local deadline = math.huge
  3. local hitDeadline = false
  4. local function checkDeadline()
  5.   if computer.realTime() > deadline then
  6.     debug.sethook(coroutine.running(), checkDeadline, "", 1)
  7.     if not hitDeadline then
  8.       deadline = deadline + 0.5
  9.     end
  10.     hitDeadline = true
  11.     error("too long without yielding", 0)
  12.   end
  13. end
  14.  
  15. -------------------------------------------------------------------------------
  16.  
  17. local function checkArg(n, have, ...)
  18.   have = type(have)
  19.   local function check(want, ...)
  20.     if not want then
  21.       return false
  22.     else
  23.       return have == want or check(...)
  24.     end
  25.   end
  26.   if not check(...) then
  27.     local msg = string.format("bad argument #%d (%s expected, got %s)",
  28.                               n, table.concat({...}, " or "), have)
  29.     error(msg, 3)
  30.   end
  31. end
  32.  
  33. -------------------------------------------------------------------------------
  34.  
  35. local function smatch(f, s, ...)
  36.   if #s > system.maxPatternInputLength() then
  37.     return nil, "input too long, see maxPatternInputLength in the config"
  38.   end
  39.   return f(s, ...)
  40. end
  41.  
  42. -- Replacing methods in actual string table to also take care of OO
  43. -- calls (i.e. ("asd"):match(...)).
  44. local string_find, string_gmatch, string_gsub, string_match =
  45.       string.find, string.gmatch, string.gsub, string.match
  46. local function installPatternSandbox()
  47.   string.find = function(...)
  48.     return smatch(string_find, ...)
  49.   end
  50.   string.gmatch = function(...)
  51.     return smatch(string_gmatch, ...)
  52.   end
  53.   string.gsub = function(...)
  54.     return smatch(string_gsub, ...)
  55.   end
  56.   string.match = function(...)
  57.     return smatch(string_match, ...)
  58.   end
  59. end
  60. installPatternSandbox()
  61.  
  62. -------------------------------------------------------------------------------
  63.  
  64. local loadHookMetatable
  65. loadHookMetatable = {
  66.   [persistKey and persistKey() or "LuaJ"] = function()
  67.     return function()
  68.       -- Has to be re-applied after loading saved state because global
  69.       -- metatables (such as the string one) aren't persisted automatically.
  70.       installPatternSandbox()
  71.       return setmetatable({}, loadHookMetatable)
  72.     end
  73.   end
  74. }
  75. local loadHook = setmetatable({}, loadHookMetatable)
  76.  
  77. -------------------------------------------------------------------------------
  78.  
  79. local function spcall(...)
  80.   local result = table.pack(pcall(...))
  81.   if not result[1] then
  82.     error(tostring(result[2]), 0)
  83.   else
  84.     return table.unpack(result, 2, result.n)
  85.   end
  86. end
  87.  
  88. local function sgc(self)
  89.   local oldDeadline, oldHitDeadline = deadline, hitDeadline
  90.   local mt = debug.getmetatable(self)
  91.   mt = rawget(mt, "mt")
  92.   local gc = rawget(mt, "__gc")
  93.   if type(gc) ~= "function" then
  94.     return
  95.   end
  96.   local co = coroutine.create(gc)
  97.   debug.sethook(co, checkDeadline, "", hookInterval)
  98.   deadline, hitDeadline = math.min(oldDeadline, computer.realTime() + 0.5), true
  99.   local result, reason = coroutine.resume(co, self)
  100.   debug.sethook(co)
  101.   deadline, hitDeadline = oldDeadline, oldHitDeadline
  102.   if not result then
  103.     error(reason, 0)
  104.   end
  105. end
  106.  
  107. --[[ This is the global environment we make available to userland programs. ]]
  108. -- You'll notice that we do a lot of wrapping of native functions and adding
  109. -- parameter checks in those wrappers. This is to avoid errors from the host
  110. -- side that would push error objects - which are userdata and cannot be
  111. -- persisted.
  112. local sandbox, libprocess
  113. sandbox = {
  114.   assert = assert,
  115.   dofile = nil, -- in boot/*_base.lua
  116.   error = error,
  117.   _G = nil, -- see below
  118.   getmetatable = function(t)
  119.     if type(t) == "string" then -- don't allow messing with the string mt
  120.       return nil
  121.     end
  122.     local result = getmetatable(t)
  123.     -- check if we have a wrapped __gc using mt
  124.     if type(result) == "table" and rawget(result, "__gc") == sgc then
  125.       result = rawget(result, "mt")
  126.     end
  127.     return result
  128.   end,
  129.   ipairs = ipairs,
  130.   load = function(ld, source, mode, env)
  131.     if not system.allowBytecode() then
  132.       mode = "t"
  133.     end
  134.     return load(ld, source, mode, env or sandbox)
  135.   end,
  136.   loadfile = nil, -- in boot/*_base.lua
  137.   next = next,
  138.   pairs = pairs,
  139.   pcall = function(...)
  140.     local result = table.pack(pcall(...))
  141.     checkDeadline()
  142.     return table.unpack(result, 1, result.n)
  143.   end,
  144.   print = nil, -- in boot/*_base.lua
  145.   rawequal = rawequal,
  146.   rawget = rawget,
  147.   rawlen = rawlen,
  148.   rawset = rawset,
  149.   select = select,
  150.   setmetatable = function(t, mt)
  151.     if type(mt) ~= "table" then
  152.       return setmetatable(t, mt)
  153.     end
  154.     if type(rawget(mt, "__gc")) == "function" then
  155.       -- For all user __gc functions we enforce a much tighter deadline.
  156.       -- This is because these functions may be called from the main
  157.       -- thread under certain circumstanced (such as when saving the world),
  158.       -- which can lead to noticeable lag if the __gc function behaves badly.
  159.       local sbmt = {} -- sandboxed metatable. only for __gc stuff, so it's
  160.                       -- kinda ok to have a shallow copy instead... meh.
  161.       for k, v in pairs(mt) do
  162.         sbmt[k] = v
  163.       end
  164.       sbmt.mt = mt
  165.       sbmt.__gc = sgc
  166.       mt = sbmt
  167.     end
  168.     return setmetatable(t, mt)
  169.   end,
  170.   tonumber = tonumber,
  171.   tostring = tostring,
  172.   type = type,
  173.   _VERSION = "Lua 5.2",
  174.   xpcall = function(f, msgh, ...)
  175.     local handled = false
  176.     local result = table.pack(xpcall(f, function(...)
  177.       if handled then
  178.         return ...
  179.       else
  180.         handled = true
  181.         return msgh(...)
  182.       end
  183.     end, ...))
  184.     checkDeadline()
  185.     return table.unpack(result, 1, result.n)
  186.   end,
  187.  
  188.   coroutine = {
  189.     create = coroutine.create,
  190.     resume = function(co, ...) -- custom resume part for bubbling sysyields
  191.       checkArg(1, co, "thread")
  192.       local args = table.pack(...)
  193.       while true do -- for consecutive sysyields
  194.         debug.sethook(co, checkDeadline, "", hookInterval)
  195.         local result = table.pack(
  196.           coroutine.resume(co, table.unpack(args, 1, args.n)))
  197.         debug.sethook(co) -- avoid gc issues
  198.         checkDeadline()
  199.         if result[1] then -- success: (true, sysval?, ...?)
  200.           if coroutine.status(co) == "dead" then -- return: (true, ...)
  201.             return true, table.unpack(result, 2, result.n)
  202.           elseif result[2] ~= nil then -- yield: (true, sysval)
  203.             args = table.pack(coroutine.yield(result[2]))
  204.           else -- yield: (true, nil, ...)
  205.             return true, table.unpack(result, 3, result.n)
  206.           end
  207.         else -- error: result = (false, string)
  208.           return false, result[2]
  209.         end
  210.       end
  211.     end,
  212.     running = coroutine.running,
  213.     status = coroutine.status,
  214.     wrap = function(f) -- for bubbling coroutine.resume
  215.       local co = coroutine.create(f)
  216.       return function(...)
  217.         local result = table.pack(sandbox.coroutine.resume(co, ...))
  218.         if result[1] then
  219.           return table.unpack(result, 2, result.n)
  220.         else
  221.           error(result[2], 0)
  222.         end
  223.       end
  224.     end,
  225.     yield = function(...) -- custom yield part for bubbling sysyields
  226.       return coroutine.yield(nil, ...)
  227.     end
  228.   },
  229.  
  230.   string = {
  231.     byte = string.byte,
  232.     char = string.char,
  233.     dump = string.dump,
  234.     find = string.find,
  235.     format = string.format,
  236.     gmatch = string.gmatch,
  237.     gsub = string.gsub,
  238.     len = string.len,
  239.     lower = string.lower,
  240.     match = string.match,
  241.     rep = string.rep,
  242.     reverse = string.reverse,
  243.     sub = string.sub,
  244.     upper = string.upper
  245.   },
  246.  
  247.   table = {
  248.     concat = table.concat,
  249.     insert = table.insert,
  250.     pack = table.pack,
  251.     remove = table.remove,
  252.     sort = table.sort,
  253.     unpack = table.unpack
  254.   },
  255.  
  256.   math = {
  257.     abs = math.abs,
  258.     acos = math.acos,
  259.     asin = math.asin,
  260.     atan = math.atan,
  261.     atan2 = math.atan2,
  262.     ceil = math.ceil,
  263.     cos = math.cos,
  264.     cosh = math.cosh,
  265.     deg = math.deg,
  266.     exp = math.exp,
  267.     floor = math.floor,
  268.     fmod = math.fmod,
  269.     frexp = math.frexp,
  270.     huge = math.huge,
  271.     ldexp = math.ldexp,
  272.     log = math.log,
  273.     max = math.max,
  274.     min = math.min,
  275.     modf = math.modf,
  276.     pi = math.pi,
  277.     pow = math.pow,
  278.     rad = math.rad,
  279.     random = function(...)
  280.       return spcall(math.random, ...)
  281.     end,
  282.     randomseed = function(seed)
  283.       spcall(math.randomseed, seed)
  284.     end,
  285.     sin = math.sin,
  286.     sinh = math.sinh,
  287.     sqrt = math.sqrt,
  288.     tan = math.tan,
  289.     tanh = math.tanh
  290.   },
  291.  
  292.   bit32 = {
  293.     arshift = bit32.arshift,
  294.     band = bit32.band,
  295.     bnot = bit32.bnot,
  296.     bor = bit32.bor,
  297.     btest = bit32.btest,
  298.     bxor = bit32.bxor,
  299.     extract = bit32.extract,
  300.     replace = bit32.replace,
  301.     lrotate = bit32.lrotate,
  302.     lshift = bit32.lshift,
  303.     rrotate = bit32.rrotate,
  304.     rshift = bit32.rshift
  305.   },
  306.  
  307.   io = nil, -- in lib/io.lua
  308.  
  309.   os = {
  310.     clock = os.clock,
  311.     date = function(format, time)
  312.       return spcall(os.date, format, time)
  313.     end,
  314.     difftime = function(t2, t1)
  315.       return t2 - t1
  316.     end,
  317.     execute = nil, -- in boot/*_os.lua
  318.     exit = nil, -- in boot/*_os.lua
  319.     remove = nil, -- in boot/*_os.lua
  320.     rename = nil, -- in boot/*_os.lua
  321.     time = function(table)
  322.       checkArg(1, table, "table", "nil")
  323.       return os.time(table)
  324.     end,
  325.     tmpname = nil, -- in boot/*_os.lua
  326.   },
  327.  
  328.   debug = {
  329.     traceback = debug.traceback
  330.   },
  331.  
  332.   checkArg = checkArg
  333. }
  334. sandbox._G = sandbox
  335.  
  336. -------------------------------------------------------------------------------
  337. -- Start of non-standard stuff.
  338.  
  339. -- JNLua derps when the metatable of userdata is changed, so we have to
  340. -- wrap and isolate it, to make sure it can't be touched by user code.
  341. -- These functions provide the logic for wrapping and unwrapping (when
  342. -- pushed to user code and when pushed back to the host, respectively).
  343. local wrapUserdata, wrapSingleUserdata, unwrapUserdata, wrappedUserdataMeta
  344.  
  345. wrappedUserdataMeta = {
  346.   -- Weak keys, clean up once a proxy is no longer referenced anywhere.
  347.   __mode="k",
  348.   -- We need custom persist logic here to avoid ERIS trying to save the
  349.   -- userdata referenced in this table directly. It will be repopulated
  350.   -- in the load methods of the persisted userdata wrappers (see below).
  351.   [persistKey and persistKey() or "LuaJ"] = function()
  352.     return function()
  353.       -- When using special persistence we have to manually reassign the
  354.       -- metatable of the persisted value.
  355.       return setmetatable({}, wrappedUserdataMeta)
  356.     end
  357.   end
  358. }
  359. local wrappedUserdata = setmetatable({}, wrappedUserdataMeta)
  360.  
  361. local function processResult(result)
  362.   wrapUserdata(result) -- needed for metamethods.
  363.   if not result[1] then -- error that should be re-thrown.
  364.     error(result[2], 0)
  365.   else -- success or already processed error.
  366.     return table.unpack(result, 2, result.n)
  367.   end
  368. end
  369.  
  370. local function invoke(target, direct, ...)
  371.   local result
  372.   if direct then
  373.     local args = table.pack(...) -- for unwrapping
  374.     unwrapUserdata(args)
  375.     result = table.pack(target.invoke(table.unpack(args, 1, args.n)))
  376.     if result.n == 0 then -- limit for direct calls reached
  377.       result = nil
  378.     end
  379.     -- no need to wrap here, will be wrapped in processResult
  380.   end
  381.   if not result then
  382.     local args = table.pack(...) -- for access in closure
  383.     result = select(1, coroutine.yield(function()
  384.       unwrapUserdata(args)
  385.       local result = table.pack(target.invoke(table.unpack(args, 1, args.n)))
  386.       wrapUserdata(result)
  387.       return result
  388.     end))
  389.   end
  390.   return processResult(result)
  391. end
  392.  
  393. local function udinvoke(f, data, ...)
  394.   local args = table.pack(...)
  395.   unwrapUserdata(args)
  396.   local result = table.pack(f(data, table.unpack(args)))
  397.   return processResult(result)
  398. end
  399.  
  400. -- Metatable for additional functionality on userdata.
  401. local userdataWrapper = {
  402.   __index = function(self, ...)
  403.     return udinvoke(userdata.apply, wrappedUserdata[self], ...)
  404.   end,
  405.   __newindex = function(self, ...)
  406.     return udinvoke(userdata.unapply, wrappedUserdata[self], ...)
  407.   end,
  408.   __call = function(self, ...)
  409.     return udinvoke(userdata.call, wrappedUserdata[self], ...)
  410.   end,
  411.   __gc = function(self)
  412.     local data = wrappedUserdata[self]
  413.     wrappedUserdata[self] = nil
  414.     userdata.dispose(data)
  415.   end,
  416.   -- This is the persistence protocol for userdata. Userdata is considered
  417.   -- to be 'owned' by Lua, and is saved to an NBT tag. We also get the name
  418.   -- of the actual class when saving, so we can create a new instance via
  419.   -- reflection when loading again (and then immediately wrap it again).
  420.   -- Collect wrapped callback methods.
  421.   [persistKey and persistKey() or "LuaJ"] = function(self)
  422.     local className, nbt = userdata.save(wrappedUserdata[self])
  423.     -- The returned closure is what actually gets persisted, including the
  424.     -- upvalues, that being the classname and a byte array representing the
  425.     -- nbt data of the userdata value.
  426.     return function()
  427.       return wrapSingleUserdata(userdata.load(className, nbt))
  428.     end
  429.   end,
  430.   -- Do not allow changing the metatable to avoid the gc callback being
  431.   -- unset, leading to potential resource leakage on the host side.
  432.   __metatable = "userdata",
  433.   __tostring = function(self)
  434.     local data = wrappedUserdata[self]
  435.     return tostring(select(2, pcall(data.toString, data)))
  436.   end
  437. }
  438.  
  439. local userdataCallback = {
  440.   __call = function(self, ...)
  441.     local methods = spcall(userdata.methods, wrappedUserdata[self.proxy])
  442.     for name, direct in pairs(methods) do
  443.       if name == self.name then
  444.         return invoke(userdata, direct, self.proxy, name, ...)
  445.       end
  446.     end
  447.     error("no such method", 1)
  448.   end,
  449.   __tostring = function(self)
  450.     return userdata.doc(wrappedUserdata[self.proxy], self.name) or "function"
  451.   end
  452. }
  453.  
  454. function wrapSingleUserdata(data)
  455.   -- Reuse proxies for lower memory consumption and more logical behavior
  456.   -- without the need of metamethods like __eq, as well as proper reference
  457.   -- behavior after saving and loading again.
  458.   for k, v in pairs(wrappedUserdata) do
  459.     -- We need a custom 'equals' check for userdata because metamethods on
  460.     -- userdata introduced by JNLua tend to crash the game for some reason.
  461.     if v == data then
  462.       return k
  463.     end
  464.   end
  465.   local proxy = {type = "userdata"}
  466.   local methods = spcall(userdata.methods, data)
  467.   for method in pairs(methods) do
  468.     proxy[method] = setmetatable({name=method, proxy=proxy}, userdataCallback)
  469.   end
  470.   wrappedUserdata[proxy] = data
  471.   return setmetatable(proxy, userdataWrapper)
  472. end
  473.  
  474. function wrapUserdata(values)
  475.   local processed = {}
  476.   local function wrapRecursively(value)
  477.     if type(value) == "table" then
  478.       if not processed[value] then
  479.         processed[value] = true
  480.         for k, v in pairs(value) do
  481.           value[k] = wrapRecursively(v)
  482.         end
  483.       end
  484.     elseif type(value) == "userdata" then
  485.       return wrapSingleUserdata(value)
  486.     end
  487.     return value
  488.   end
  489.   wrapRecursively(values)
  490. end
  491.  
  492. function unwrapUserdata(values)
  493.   local processed = {}
  494.   local function unwrapRecursively(value)
  495.     if wrappedUserdata[value] then
  496.       return wrappedUserdata[value]
  497.     end
  498.     if type(value) == "table" then
  499.       if not processed[value] then
  500.         processed[value] = true
  501.         for k, v in pairs(value) do
  502.           value[k] = unwrapRecursively(v)
  503.         end
  504.       end
  505.     end
  506.     return value
  507.   end
  508.   unwrapRecursively(values)
  509. end
  510.  
  511. -------------------------------------------------------------------------------
  512.  
  513. local libcomponent
  514.  
  515. -- Caching proxy objects for lower memory use.
  516. local proxyCache = setmetatable({}, {__mode="v"})
  517.  
  518. -- Short-term caching of callback directness for improved performance.
  519. local directCache = setmetatable({}, {__mode="k"})
  520. local function isDirect(address, method)
  521.   local cacheKey = address..":"..method
  522.   local cachedValue = directCache[cacheKey]
  523.   if cachedValue ~= nil then
  524.     return cachedValue
  525.   end
  526.   local methods, reason = spcall(component.methods, address)
  527.   if not methods then
  528.     return false
  529.   end
  530.   for name, info in pairs(methods) do
  531.     if name == method then
  532.       directCache[cacheKey] = info.direct
  533.       return info.direct
  534.     end
  535.   end
  536.   error("no such method", 1)
  537. end
  538.  
  539. local componentProxy = {
  540.   __index = function(self, key)
  541.     if self.fields[key] and self.fields[key].getter then
  542.       return libcomponent.invoke(self.address, key)
  543.     else
  544.       rawget(self, key)
  545.     end
  546.   end,
  547.   __newindex = function(self, key, value)
  548.     if self.fields[key] and self.fields[key].setter then
  549.       return libcomponent.invoke(self.address, key, value)
  550.     elseif self.fields[key] and self.fields[key].getter then
  551.       error("field is read-only")
  552.     else
  553.       rawset(self, key, value)
  554.     end
  555.   end,
  556.   __pairs = function(self)
  557.     local keyProxy, keyField, value
  558.     return function()
  559.       if not keyField then
  560.         repeat
  561.           keyProxy, value = next(self, keyProxy)
  562.         until not keyProxy or keyProxy ~= "fields"
  563.       end
  564.       if not keyProxy then
  565.         keyField, value = next(self.fields, keyField)
  566.       end
  567.       return keyProxy or keyField, value
  568.     end
  569.   end
  570. }
  571.  
  572. local componentCallback = {
  573.   __call = function(self, ...)
  574.     return libcomponent.invoke(self.address, self.name, ...)
  575.   end,
  576.   __tostring = function(self)
  577.     return libcomponent.doc(self.address, self.name) or "function"
  578.   end
  579. }
  580.  
  581. libcomponent = {
  582.   doc = function(address, method)
  583.     checkArg(1, address, "string")
  584.     checkArg(2, method, "string")
  585.     local result, reason = spcall(component.doc, address, method)
  586.     if not result and reason then
  587.       error(reason, 2)
  588.     end
  589.     return result
  590.   end,
  591.   invoke = function(address, method, ...)
  592.     checkArg(1, address, "string")
  593.     checkArg(2, method, "string")
  594.     return invoke(component, isDirect(address, method), address, method, ...)
  595.   end,
  596.   list = function(filter, exact)
  597.     checkArg(1, filter, "string", "nil")
  598.     local list = spcall(component.list, filter, not not exact)
  599.     local key = nil
  600.     return setmetatable(list, {__call=function()
  601.       key = next(list, key)
  602.       if key then
  603.         return key, list[key]
  604.       end
  605.     end})
  606.   end,
  607.   methods = function(address)
  608.     local result, reason = spcall(component.methods, address)
  609.     -- Transform to pre 1.4 format to avoid breaking scripts.
  610.     if type(result) == "table" then
  611.       for k, v in pairs(result) do
  612.         if not v.getter and not v.setter then
  613.           result[k] = v.direct
  614.         else
  615.           result[k] = nil
  616.         end
  617.       end
  618.       return result
  619.     end
  620.     return result, reason
  621.   end,
  622.   fields = function(address)
  623.     local result, reason = spcall(component.methods, address)
  624.     if type(result) == "table" then
  625.       for k, v in pairs(result) do
  626.         if not v.getter and not v.setter then
  627.           result[k] = nil
  628.         end
  629.       end
  630.       return result
  631.     end
  632.     return result, reason
  633.   end,
  634.   proxy = function(address)
  635.     local type, reason = spcall(component.type, address)
  636.     if not type then
  637.       return nil, reason
  638.     end
  639.     local slot, reason = spcall(component.slot, address)
  640.     if not slot then
  641.       return nil, reason
  642.     end
  643.     if proxyCache[address] then
  644.       return proxyCache[address]
  645.     end
  646.     local proxy = {address = address, type = type, slot = slot, fields = {}}
  647.     local methods, reason = spcall(component.methods, address)
  648.     if not methods then
  649.       return nil, reason
  650.     end
  651.     for method, info in pairs(methods) do
  652.       if not info.getter and not info.setter then
  653.         proxy[method] = setmetatable({address=address,name=method}, componentCallback)
  654.       else
  655.         proxy.fields[method] = info
  656.       end
  657.     end
  658.     setmetatable(proxy, componentProxy)
  659.     proxyCache[address] = proxy
  660.     return proxy
  661.   end,
  662.   type = function(address)
  663.     return spcall(component.type, address)
  664.   end,
  665.   slot = function(address)
  666.     return spcall(component.slot, address)
  667.   end
  668. }
  669. sandbox.component = libcomponent
  670.  
  671. local libcomputer = {
  672.   isRobot = computer.isRobot,
  673.   address = computer.address,
  674.   tmpAddress = computer.tmpAddress,
  675.   freeMemory = computer.freeMemory,
  676.   totalMemory = computer.totalMemory,
  677.   uptime = computer.uptime,
  678.   energy = computer.energy,
  679.   maxEnergy = computer.maxEnergy,
  680.  
  681.   getBootAddress = computer.getBootAddress,
  682.   setBootAddress = function(...)
  683.     return spcall(computer.setBootAddress, ...)
  684.   end,
  685.  
  686.   users = computer.users,
  687.   addUser = function(...)
  688.     return spcall(computer.addUser, ...)
  689.   end,
  690.   removeUser = function(...)
  691.     return spcall(computer.removeUser, ...)
  692.   end,
  693.  
  694.   shutdown = function(reboot)
  695.     coroutine.yield(reboot ~= nil and reboot ~= false)
  696.   end,
  697.   pushSignal = function(...)
  698.     return spcall(computer.pushSignal, ...)
  699.   end,
  700.   pullSignal = function(timeout)
  701.     local deadline = computer.uptime() +
  702.       (type(timeout) == "number" and timeout or math.huge)
  703.     repeat
  704.       local signal = table.pack(coroutine.yield(deadline - computer.uptime()))
  705.       if signal.n > 0 then
  706.         return table.unpack(signal, 1, signal.n)
  707.       end
  708.     until computer.uptime() >= deadline
  709.   end,
  710.  
  711.   beep = function(...)
  712.     libcomponent.invoke(computer.address(), "beep", ...)
  713.   end
  714. }
  715. sandbox.computer = libcomputer
  716.  
  717. local libunicode = {
  718.   char = function(...)
  719.     return spcall(unicode.char, ...)
  720.   end,
  721.   len = function(s)
  722.     return spcall(unicode.len, s)
  723.   end,
  724.   lower = function(s)
  725.     return spcall(unicode.lower, s)
  726.   end,
  727.   reverse = function(s)
  728.     return spcall(unicode.reverse, s)
  729.   end,
  730.   sub = function(s, i, j)
  731.     if j then
  732.       return spcall(unicode.sub, s, i, j)
  733.     end
  734.     return spcall(unicode.sub, s, i)
  735.   end,
  736.   upper = function(s)
  737.     return spcall(unicode.upper, s)
  738.   end,
  739.   isWide = function(s)
  740.     return spcall(unicode.isWide, s)
  741.   end,
  742.   charWidth = function(s)
  743.     return spcall(unicode.charWidth, s)
  744.   end,
  745.   wlen = function(s)
  746.     return spcall(unicode.wlen, s)
  747.   end,
  748.   wtrunc = function(s, n)
  749.     return spcall(unicode.wtrunc, s, n)
  750.   end
  751. }
  752. sandbox.unicode = libunicode
  753.  
  754. -------------------------------------------------------------------------------
  755.  
  756. local function bootstrap()
  757.   function boot_invoke(address, method, ...)
  758.     local result = table.pack(pcall(libcomponent.invoke, address, method, ...))
  759.     if not result[1] then
  760.       return nil, result[2]
  761.     else
  762.       return table.unpack(result, 2, result.n)
  763.     end
  764.   end
  765.   do
  766.     local screen = libcomponent.list("screen")()
  767.     local gpu = libcomponent.list("gpu")()
  768.     if gpu and screen then
  769.       boot_invoke(gpu, "bind", screen)
  770.     end
  771.   end
  772.   local function tryLoadFrom(address)
  773.     local handle, reason = boot_invoke(address, "open", "/init.lua")
  774.     if not handle then
  775.       return nil, reason
  776.     end
  777.     local buffer = ""
  778.     repeat
  779.       local data, reason = boot_invoke(address, "read", handle, math.huge)
  780.       if not data and reason then
  781.         return nil, reason
  782.       end
  783.       buffer = buffer .. (data or "")
  784.     until not data
  785.     boot_invoke(address, "close", handle)
  786.     return load(buffer, "=init", "t", sandbox)
  787.   end
  788.   local init, reason
  789.   if computer.getBootAddress() then
  790.     init, reason = tryLoadFrom(computer.getBootAddress())
  791.   end
  792.   if not init then
  793.     computer.setBootAddress()
  794.     for address in libcomponent.list("filesystem") do
  795.       init, reason = tryLoadFrom(address)
  796.       if init then
  797.         computer.setBootAddress(address)
  798.         break
  799.       end
  800.     end
  801.   end
  802.   if not init then
  803.     error("no bootable medium found" .. (reason and (": " .. tostring(reason)) or ""), 0)
  804.   end
  805.  
  806.   return coroutine.create(init), {n=0}
  807. end
  808.  
  809. -------------------------------------------------------------------------------
  810.  
  811. local function main()
  812.   -- Yield once to get a memory baseline.
  813.   coroutine.yield()
  814.  
  815.   -- After memory footprint to avoid init.lua bumping the baseline.
  816.   local co, args = bootstrap()
  817.   local forceGC = 10
  818.  
  819.   while true do
  820.     deadline = computer.realTime() + system.timeout()
  821.     hitDeadline = false
  822.  
  823.     -- NOTE: since this is run in an executor thread and we enforce timeouts
  824.     -- in user-defined garbage collector callbacks this should be safe.
  825.     if persistKey then -- otherwise we're in LuaJ
  826.       forceGC = forceGC - 1
  827.       if forceGC < 1 then
  828.         collectgarbage("collect")
  829.         forceGC = 10
  830.       end
  831.     end
  832.  
  833.     debug.sethook(co, checkDeadline, "", hookInterval)
  834.     local result = table.pack(coroutine.resume(co, table.unpack(args, 1, args.n)))
  835.     if not result[1] then
  836.       error(tostring(result[2]), 0)
  837.     elseif coroutine.status(co) == "dead" then
  838.       error("computer stopped unexpectedly", 0)
  839.     else
  840.       args = table.pack(coroutine.yield(result[2])) -- system yielded value
  841.       wrapUserdata(args)
  842.     end
  843.   end
  844. end
  845.  
  846. -- JNLua converts the coroutine to a string immediately, so we can't get the
  847. -- traceback later. Because of that we have to do the error handling here.
  848. return pcall(main)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement