OpenOS 1.6.3 update (#2414)

Why a version change?
> This minor version change should not introduce breaking changes, but older code doing non standard things with process environments, `require` and the package library, shebang redirections, or direct calls to /bin/lua, may see some breakages. Also, `shell.resolve` has been fully reworked. This is a crucial and heavily used api. Thus, in case of any mistakes or bugs a version change will help pinpoint regressions. I've heavily tested the resolve code, but it is new code and deserves some bake time in the wild.

Changelog:
/bin/lua removes shebang line and calls load directly. Improves workflows that define custom environments, and loadfile no longer removes shebang lines

load: loaded code chunks now inherit the parent _ENV naturally, having a real-lua behavior

require: heavily optimized and made errors more natural and helpful, exposing more information about failure to require a library

/lib/pipes: commented out pipes.create which is a future feature to create event-boxed threads

process: more process crash text is dumped to the shell to help identify "out of memory" issues

shell.resolve: reworked and optimized! possibly breaking change: specifying an extension (ext) to `shell.resolve(name, ext)` will no longer return results to directories, but only files (if they exist). However, it was never the intent of this method to return results to directories when specifying an extension

various memory optimizations throughout. openos now allocates ~153k to boot to shell.
This commit is contained in:
payonel 2017-06-02 13:33:45 -07:00 committed by GitHub
parent f297eefd7d
commit 06a0861597
15 changed files with 119 additions and 190 deletions

View File

@ -5,13 +5,23 @@ if #args == 0 then
args = {"/opt/core/lua_shell.lua"}
end
local script, reason = loadfile(args[1], nil, setmetatable({},{__index=_ENV}))
local filename = args[1]
local buffer, script, reason
buffer = io.lines(filename, "*a")()
if buffer then
buffer = buffer:gsub("^#![^\n]+", "") -- remove shebang if any
script, reason = load(buffer, "="..filename)
else
reason = string.format("could not open %s for reading", filename)
end
if not script then
io.stderr:write(tostring(reason) .. "\n")
os.exit(false)
end
local result, reason = pcall(script, table.unpack(args, 2))
if not result then
buffer, reason = pcall(script, table.unpack(args, 2))
if not buffer then
io.stderr:write(reason, "\n")
os.exit(false)
end

View File

@ -1,4 +1,4 @@
function loadfile(filename, mode, env)
function loadfile(filename, ...)
if filename:sub(1,1) ~= "/" then
filename = (os.getenv("PWD") or "/") .. "/" .. filename
end
@ -16,11 +16,9 @@ function loadfile(filename, mode, env)
end
break
end
table.insert(buffer, data)
buffer[#buffer + 1] = data
end
buffer[1] = (buffer[1] or ""):gsub("^#![^\n]+", "") -- remove shebang if any
buffer = table.concat(buffer)
return load(buffer, "=" .. filename, mode, env)
return load(table.concat(buffer), "=" .. filename, ...)
end
function dofile(filename)
@ -35,12 +33,10 @@ function print(...)
local args = table.pack(...)
local stdout = io.stdout
stdout:setvbuf("line")
local pre = ""
for i = 1, args.n do
local arg = tostring(args[i])
if i > 1 then
arg = "\t" .. arg
end
stdout:write(arg)
stdout:write(pre, tostring(args[i]))
pre = "\t"
end
stdout:write("\n")
stdout:setvbuf("no")

View File

@ -27,10 +27,18 @@ local kernel_load = _G.load
local intercept_load
intercept_load = function(source, label, mode, env)
if env then
env.load = kernel_load([[
local source, label, mode, env = ...
return load(source, label, mode, env or fenv)
]], "=load", "t", {fenv=env, load=intercept_load})
local loader = setmetatable(
{
env = env,
load = intercept_load,
},
{__call = function(tbl, _source, _label, _mode, _env)
return tbl.load(_source, _label, _mode, _env or tbl.env)
end})
if env.load and (type(env.load) ~= "table" or env.load.load ~= intercept_load) then
loader.load = env.load
end
env.load = loader
end
return kernel_load(source, label, mode, env or process.info().env)
end

View File

@ -2,11 +2,7 @@ local computer = require("computer")
local event = require("event")
local fs = require("filesystem")
local shell = require("shell")
local process = require("process")
local function env()
return process.info().data.vars
end
local info = require("process").info
os.execute = function(command)
if not command then
@ -20,7 +16,7 @@ function os.exit(code)
end
function os.getenv(varname)
local env = env()
local env = info().data.vars
if not varname then
return env
elseif varname == '#' then
@ -34,7 +30,7 @@ function os.setenv(varname, value)
if value ~= nil then
value = tostring(value)
end
env()[varname] = value
info().data.vars[varname] = value
return value
end

View File

@ -1,11 +1,6 @@
local buffer = require("buffer")
local tty = require("tty")
local io_open = io.open
function io.open(path, mode)
return io_open(require("shell").resolve(path), mode)
end
local stdinStream = {handle="stdin"}
local stdoutStream = {handle="stdout"}
local stderrStream = {handle="stderr"}
@ -15,13 +10,13 @@ local function badFileDescriptor()
return nil, "bad file descriptor"
end
function stdinStream:close()
function stdinStream.close()
return nil, "cannot close standard file"
end
stdoutStream.close = stdinStream.close
stderrStream.close = stdinStream.close
function stdinStream:read()
function stdinStream.read()
return tty.read(stdinHistory)
end

View File

@ -1,5 +1,3 @@
#!/bin/lua
local component = require("component")
local computer = require("computer")
local text = require("text")

View File

@ -36,7 +36,8 @@ end
function io.open(path, mode)
-- These requires are not on top because this is a bootstrapped file.
local stream, result = require("filesystem").open(path, mode)
local resolved_path = require("shell").resolve(path)
local stream, result = require("filesystem").open(resolved_path, mode)
if stream then
return require("buffer").new(mode, stream)
else

View File

@ -1,5 +1,3 @@
local component = require("component")
local keyboard = {pressedChars = {}, pressedCodes = {}}
-- these key definitions are only a subset of all the defined keys
@ -32,16 +30,6 @@ keyboard.keys = {
numpadenter = 0x9C,
}
-- Create inverse mapping for name lookup.
setmetatable(keyboard.keys,
{
__index = function(tbl, k)
getmetatable(keyboard.keys).__index = nil -- to be safe
loadfile(package.searchpath("tools/keyboard_full", package.path), "t", setmetatable({keyboard=keyboard},{__index=_G}))()
return tbl[k]
end
})
-------------------------------------------------------------------------------
local function getKeyboardAddress(address)
@ -94,4 +82,13 @@ end
-------------------------------------------------------------------------------
setmetatable(keyboard.keys,
{
__index = function(tbl, key)
setmetatable(tbl, nil)
dofile("/opt/core/full_keyboard.lua")
return tbl[key] -- some keyboard keys are handled by __index by design
end
})
return keyboard

View File

@ -16,11 +16,6 @@ local loaded = {
}
package.loaded = loaded
local preload = {}
package.preload = preload
package.searchers = {}
function package.searchpath(name, path, sep, rep)
checkArg(1, name, "string")
checkArg(2, path, "string")
@ -47,67 +42,28 @@ function package.searchpath(name, path, sep, rep)
return nil, table.concat(errorFiles, "\n")
end
local function preloadSearcher(module)
if preload[module] ~= nil then
return preload[module]
else
return "\tno field package.preload['" .. module .. "']"
end
end
local function pathSearcher(module)
local filepath, reason = package.searchpath(module, package.path)
if filepath then
local loader, reason = loadfile(filepath, "bt", _G)
if loader then
return loader, filepath
else
return reason
end
else
return reason
end
end
table.insert(package.searchers, preloadSearcher)
table.insert(package.searchers, pathSearcher)
function require(module)
checkArg(1, module, "string")
if loaded[module] ~= nil then
return loaded[module]
elseif not loading[module] then
loading[module] = true
local loader, value, errorMsg = nil, nil, {"module '" .. module .. "' not found:"}
for i = 1, #package.searchers do
-- the pcall is mostly for out of memory errors
local ok, f, extra = pcall(package.searchers[i], module)
if not ok then
table.insert(errorMsg, "\t" .. (f or "nil"))
elseif f and type(f) ~= "string" then
loader = f
value = extra
break
elseif f then
table.insert(errorMsg, f)
end
local library, status, step
step, library, status = "not found", package.searchpath(module, package.path)
if library then
step, library, status = "loadfile failed", loadfile(library)
end
if loader then
local success, result = pcall(loader, module, value)
if library then
loading[module] = true
step, library, status = "load failed", pcall(library, module)
loading[module] = false
if not success then
error(result, 2)
end
if result then
loaded[module] = result
elseif not loaded[module] then
loaded[module] = true
end
return loaded[module]
else
loading[module] = false
error(table.concat(errorMsg, "\n"), 2)
end
assert(library, string.format("module '%s' %s:\n%s", module, step, status))
loaded[module] = status
return status
else
error("already loading: " .. module .. "\n" .. debug.traceback(), 2)
end

View File

@ -371,11 +371,14 @@ function plib.popen(prog, mode, env)
return pfd
end
function plib.create(fp, name)
checkArg(1, fp, "function")
checkArg(2, name, "string", "nil")
local pco = plib.internal.create(fp, nil, name)
return pco.stack[1]
end
-- function plib.create(fp, name)
-- checkArg(1, fp, "function")
-- checkArg(2, name, "string", "nil")
-- local pco = plib.internal.create(fp, function()end, name)
-- -- process.info(pco.stack[1]).data.event = nil
-- return pco.stack[1]
-- end
return plib

View File

@ -32,10 +32,10 @@ function process.load(path, env, init, name)
local p = process.findProcess()
env = env or p.env
local code
if type(path) == 'string' then
if type(path) == "string" then
code = function(...)
local fs, shell = require("filesystem"), require("shell")
local program, reason = shell.resolve(path, 'lua')
local program, reason = shell.resolve(path, "lua")
if not program then
if fs.isDirectory(shell.resolve(path)) then
io.stderr:write(path .. ": is a directory\n")
@ -48,7 +48,7 @@ function process.load(path, env, init, name)
os.setenv("_", program)
local f = fs.open(program)
if f then
local shebang = f:read(1024):match("^#!([^\n]+)")
local shebang = (f:read(1024) or ""):match("^#!([^\n]+)")
f:close()
if shebang then
local result = table.pack(shell.execute(shebang:gsub("%s",""), env, program, ...))
@ -59,7 +59,7 @@ function process.load(path, env, init, name)
local command
command, reason = loadfile(program, "bt", env)
if not command then
io.stderr:write(program..(reason or ""):gsub("^[^:]*", "").."\n")
io.stderr:write(program, " ", reason or "", "\n")
return 128
end
return command(...)
@ -80,14 +80,14 @@ function process.load(path, env, init, name)
end,
function(msg)
-- msg can be a custom error object
if type(msg) == 'table' then
if type(msg) == "table" then
if msg.reason ~= "terminated" then
io.stderr:write(msg.reason.."\n")
end
return msg.code or 0
end
local stack = debug.traceback():gsub('^([^\n]*\n)[^\n]*\n[^\n]*\n','%1')
io.stderr:write(string.format('%s:\n%s', msg or '', stack))
local stack = debug.traceback():gsub("^([^\n]*\n)[^\n]*\n[^\n]*\n","%1")
io.stderr:write(string.format("%s:\n%s", msg or "", stack))
return 128 -- syserr
end, ...)
}
@ -107,13 +107,11 @@ function process.load(path, env, init, name)
{
handles = {},
io = {},
coroutine_handler = {}
},
parent = p,
instances = setmetatable({}, {__mode="v"}),
}
setmetatable(new_proc.data.io, {__index=p.data.io})
setmetatable(new_proc.data.coroutine_handler, {__index=p.data.coroutine_handler})
setmetatable(new_proc.data, {__index=p.data})
process.list[thread] = new_proc

View File

@ -246,12 +246,12 @@ function sh.internal.createThreads(commands, env, start_args)
local thread_env = type(program) == "string" and env or nil
local thread, reason = process.load(program, thread_env, function(...)
local cdata = process.info().data.command
local args, has_tokens, start_args = cdata.args, cdata.has_tokens, cdata.start_args
local args, has_tokens, c_start_args = cdata.args, cdata.has_tokens, cdata.start_args
if has_tokens then
args = sh.internal.buildCommandRedirects(args)
end
sh.internal.concatn(args, start_args)
sh.internal.concatn(args, c_start_args)
sh.internal.concatn(args, {...}, select('#', ...))
-- popen expects each process to first write an empty string

View File

@ -18,60 +18,13 @@ function shell.getShell()
if shells[shellName] then
return shells[shellName]
end
local sh, reason = loadfile(shellName, "t")
local sh, reason = loadfile(shellName)
if sh then
shells[shellName] = sh
end
return sh, reason
end
local function findFile(name, ext)
checkArg(1, name, "string")
local function findIn(dir)
if dir:sub(1, 1) ~= "/" then
dir = shell.resolve(dir)
end
dir = fs.concat(fs.concat(dir, name), "..")
local name = fs.name(name)
local list = fs.list(dir)
if list and name then
local files = {}
for file in list do
files[file] = true
end
if ext and name:sub(-(1 + ext:len())) == "." .. ext then
-- Name already contains extension, prioritize.
if files[name] then
return true, fs.concat(dir, name)
end
elseif files[name] then
-- Check exact name.
return true, fs.concat(dir, name)
elseif ext then
-- Check name with automatially added extension.
local name = name .. "." .. ext
if files[name] then
return true, fs.concat(dir, name)
end
end
end
return false
end
if name:sub(1, 1) == "/" then
local found, where = findIn("/")
if found then return where end
elseif name:sub(1, 2) == "./" then
local found, where = findIn(shell.getWorkingDirectory())
if found then return where end
else
for path in string.gmatch(shell.getPath(), "[^:]+") do
local found, where = findIn(path)
if found then return where end
end
end
return false
end
-------------------------------------------------------------------------------
function shell.prime()
@ -133,21 +86,37 @@ function shell.setPath(value)
end
function shell.resolve(path, ext)
if ext then
checkArg(1, path, "string")
local dir = path
if dir:find("/") ~= 1 then
dir = fs.concat(shell.getWorkingDirectory(), dir)
end
local name = fs.name(path)
dir = fs[name and "path" or "canonical"](dir)
local fullname = fs.concat(dir, name or "")
if not ext then
return fullname
elseif name then
checkArg(2, ext, "string")
local where = findFile(path, ext)
if where then
return where
else
return nil, "file not found"
end
else
if path:sub(1, 1) == "/" then
return fs.canonical(path)
else
return fs.concat(shell.getWorkingDirectory(), path)
-- search for name in PATH if no dir was given
-- no dir was given if path has no /
local search_in = path:find("/") and dir or shell.getPath()
for search_path in string.gmatch(search_in, "[^:]+") do
-- resolve search_path because they may be relative
local search_name = fs.concat(shell.resolve(search_path), name)
if not fs.exists(search_name) then
search_name = search_name .. "." .. ext
end
-- extensions are provided when the caller is looking for a file
if fs.exists(search_name) and not fs.isDirectory(search_name) then
return search_name
end
end
end
return nil, "file not found"
end
function shell.execute(command, env, ...)
@ -179,11 +148,11 @@ function shell.parse(...)
if param == "--" then
doneWithOptions = true -- stop processing options at `--`
elseif param:sub(1, 2) == "--" then
if param:match("%-%-(.-)=") ~= nil then
options[param:match("%-%-(.-)=")] = param:match("=(.*)")
else
options[param:sub(3)] = true
local key, value = param:match("%-%-(.-)=(.*)")
if not key then
key, value = param:sub(3), true
end
options[key] = value
elseif param:sub(1, 1) == "-" and param ~= "-" then
for j = 2, unicode.len(param) do
options[unicode.sub(param, j, j)] = true

View File

@ -1,7 +1,7 @@
-- called from /init.lua
local raw_loadfile = ...
_G._OSVERSION = "OpenOS 1.6.2"
_G._OSVERSION = "OpenOS 1.6.3"
local component = component
local computer = computer
@ -96,11 +96,11 @@ do
package.loaded.component = component
package.loaded.computer = computer
package.loaded.unicode = unicode
package.preload["buffer"] = loadfile("/lib/buffer.lua")
package.preload["filesystem"] = loadfile("/lib/filesystem.lua")
package.loaded.buffer = assert(loadfile("/lib/buffer.lua"))()
package.loaded.filesystem = assert(loadfile("/lib/filesystem.lua"))()
-- Inject the io modules
_G.io = loadfile("/lib/io.lua")()
_G.io = assert(loadfile("/lib/io.lua"))()
end
status("Initializing file system...")

View File

@ -1,3 +1,5 @@
local keyboard = require("keyboard")
keyboard.keys["1"] = 0x02
keyboard.keys["2"] = 0x03
keyboard.keys["3"] = 0x04
@ -132,7 +134,7 @@ setmetatable(keyboard.keys,
{
__index = function(tbl, k)
if type(k) ~= "number" then return end
for name,value in pairs(keyboard.keys) do
for name,value in pairs(tbl.keys) do
if value == k then
return name
end