Load everything through bytecodes

This commit is contained in:
Marcel Krüger 2020-07-04 06:27:30 +02:00
parent 3d7380f76a
commit aa00de1c9d
6 changed files with 287 additions and 211 deletions

View File

@ -0,0 +1,28 @@
local concat = table.concat
local format = string.format
local ioopen = io.open
local assert = assert
local ipairs = ipairs
local _ENV = {}
local first, later =
'local __hidden_local__package_preload__=package.preload',
'\n__hidden_local__package_preload__[%q]=function(...)%s\nend'
first = first .. later
local list = {}
return function(t)
local length = #t
local tmpl = first
for i, mod in ipairs(t) do
local name, f = mod[1], assert(ioopen(mod[2], 'r'))
local data = f:read'a'
f:close()
list[i] = format(tmpl, name, data)
tmpl = later
end
return concat(list, nil, 1, length)
end

View File

@ -0,0 +1,84 @@
-- Two callbacks are defined in other files: pre_dump in lateinit and find_fmt_file in init
local read_tfm = font.read_tfm
local font_define = font.define
local callback_register = callback.register
if status.ini_version then
callback_register('define_font', function(name, size)
local f = read_tfm(name, size)
if not f then return end
local id = font_define(f)
lua.prepared_code[#lua.prepared_code+1] = string.format("assert(%i == font.define(font.read_tfm(%q, %i)))", id, name, size)
return id
end)
else
callback_register('define_font', function(name, size)
local f = read_tfm(name, size)
if not f then return end
return font.define(f)
end)
end
callback_register('find_log_file', function(name) return name end)
do
local function normal_find_data_file(name)
return kpse.find_file(name, 'tex', true)
end
if status.ini_version then
function unhook_expl()
callback_register('find_data_file', normal_find_data_file)
end
callback_register('find_data_file', function(name)
if name == 'ltexpl.ltx' then
name = 'luametalatex-ltexpl-hook'
end
return normal_find_data_file(name)
end)
else
callback_register('find_data_file', normal_find_data_file)
end
end
-- callback_register('read_data_file', function(name) error[[TODO]]return kpse.find_file(name, 'tex', true) end)
callback_register('open_data_file', function(name)
local f = io.open(name)
return setmetatable({
reader = function()
local line = f:read()
return line
end,
close = function()error[[1]] return f:close() end,
}, {
__gc = function()f:close()end,
})
end)
callback_register('handle_error_hook', function()
repeat
texio.write_nl'? '
local line = io.read()
if not line then
tex.fatalerror'End of line encountered on terminal'
end
if line == "" then return 3 end
local first = line:sub(1,1):upper()
if first == 'H' then
texio.write(tex.gethelptext() or "Sorry, I don't know how to help in this situation.\n\z
Maybe you should try asking a human?")
elseif first == 'I' then
line = line:sub(2)
tex.runtoks(function()
tex.sprint(token.scan_token(), line)
end)
return 3
elseif first == 'Q' then texio.write'OK, entering \\batchmode...\n' return 0
elseif first == 'R' then texio.write'OK, entering \\nonstopmode...\n' return 1
elseif first == 'S' then texio.write'OK, entering \\scrollmode...\n' return 2
elseif first == 'X' then return -1
else
texio.write'Type <return> to proceed, S to scroll future error messages,\
\z R to run without stopping, Q to run quietly,\
\z I to insert something,\
\z H for help, X to quit.'
end
until false
return 3
end)

View File

@ -1,55 +1,4 @@
local functions = lua.getfunctionstable()
-- I am not sure why this is necessary, but otherwise LuaMetaTeX resets
-- the functions table every time the getter is called
function lua.get_functions_table() return functions end
local set_lua = token.set_lua
-- There are two approaches to manage luafunctions ids without triggering
-- issues with ltluatex assigned numbers: Originally we assigned numbers
-- starting with 1, then switched to luatexbase ASAP and synchronised both
-- numbering schemes. But there is a useful quirk in ltluatex's luafunction
-- allocator: It only uses numbers upto 65535, so we can just use bigger
-- numbers. (This might have negative repercussins on performance because it
-- probably doesn't store the function in the array part of the Lua table.
-- Let's reconsider if this ever becomes a problem.
-- local new_luafunction = luatexbase.new_luafunction
local predefined_luafunctions = status.ini_version and 65536 -- 1<<16 -- We start with 1<<16 + 1 (1<<16=65536 is reserved for luametalatex-local)
local function new_luafunction(name)
if predefined_luafunctions then
predefined_luafunctions = predefined_luafunctions + 1
return predefined_luafunctions
else
error[[Here only preexisting luafunctions should be set]]
end
end
local undefined_cmd = token.command_id'undefined_cs'
local lua_call_cmd = token.command_id'lua_call'
local lua_value_cmd = token.command_id'lua_value'
local lua_expandable_call_cmd = token.command_id'lua_expandable_call'
function token.luacmd(name, func, ...)
local idx
local tok = token.create(name)
local cmd = tok.command
if cmd == lua_value_cmd then
idx = tok.mode
elseif cmd == lua_call_cmd then
idx = tok.mode
elseif cmd == lua_expandable_call_cmd then
idx = tok.mode
elseif ... == 'force' then
idx = new_luafunction(name)
set_lua(name, idx, select(2, ...))
elseif cmd == undefined_cmd then
idx = new_luafunction(name)
set_lua(name, idx, ...)
else
error(tok.cmdname)
end
if functions[idx] then
error[[Already defined]]
end
functions[idx] = func
return idx
end
local properties = node.direct.get_properties_table()
node.direct.properties = properties
function node.direct.get_properties_table()
@ -154,6 +103,7 @@ token.luacmd("write", function(_, immediate) -- \write
end
end, "protected")
local functions = lua.get_functions_table()
token.luacmd("immediate", function() -- \immediate
local next_tok = token.scan_token()
if next_tok.command ~= lua_call_cmd then
@ -166,22 +116,6 @@ end, "protected")
-- local name = token.scan_string()
-- print('Missing \\pdf' .. name)
-- end
if status.ini_version then
function fixupluafunctions()
return predefined_luafunctions
end
else
function fixupluafunctions(i)
predefined_luafunctions = i
end
local prepared_code = lua.bytecode[1]
prepared_code()
lua.bytecode[1] = nil
-- function fixupluafunctions()
-- new_luafunction = luatexbase.new_luafunction
-- fixupluafunctions = nil
-- end
end
require'luametalatex-baseregisters'
require'luametalatex-back-pdf'
require'luametalatex-node-luaotfload'

View File

@ -1,5 +1,6 @@
local read_tfm = require'luametalatex-font-tfm'
font.read_tfm = require'luametalatex-font-tfm'
local read_vf = require'luametalatex-font-vf'
font.read_vf = read_vf
local fontmap = require'luametalatex-pdf-font-map'.fontmap
local callback_find = callback.find

View File

@ -1,7 +1,8 @@
do
local ourpath = arg[0]:match('^%-%-lua=(.*[/\\])[^/\\]*%.lua$')
local ourpath = lua.startupfile:match('(.*[/\\])[^/\\]*%.lua$')
kpse = assert(package.loadlib(ourpath .. 'kpse.so', 'luaopen_kpse'))()
end
local interaction
do
local arg0, progname
for _, a in ipairs(arg) do
@ -9,6 +10,17 @@ do
progname = a:sub(12)
elseif a:sub(1,7) == "--arg0=" then
arg0 = a:sub(8)
elseif a:match'^%-%-?interaction=' then
local interaction_name = a:sub(a:find'='+1)
interaction = ({
batchmode=0,
nonstopmode=1,
scrollmode=2,
errorstopmode=3,
})[interaction_name]
if not interaction then
texio.write('term', string.format('Unknown interaction mode %q ignored.\n', interaction_name))
end
end
end
kpse.set_program_name(arg0, progname)
@ -25,149 +37,35 @@ package.searchers[2] = function(modname)
return mod, filename
end
-- kpse.set_maketex("kpse_fmt_format", true)
bit32 = require'luametalatex-bit32'
kpse.init_prog("LUATEX", 400, "nexthi", nil)
status.init_kpse = 1
-- kpse.init_prog("LUATEX", 400, "nexthi", nil)
require'luametalatex-init-config'
status.safer_option = 0
status.shell_escape = 0
local read_tfm = require'luametalatex-font-tfm'
local read_vf = require'luametalatex-font-vf'
font.read_tfm = read_tfm
font.read_vf = read_vf
local callback_register = callback.register
require'module'
pdf = {
getfontname = function(id) -- No font sharing
return id
end,
variable = {},
}
require'luametalatex-font-resolve' -- Replace font.define
local function base_define_font_cb(name, size)
local f = read_tfm(name, size)
if not f then return end
local id = font.define(f)
if status.ini_version then
lua.prepared_code[#lua.prepared_code+1] = string.format("assert(%i == font.define(font.read_tfm(%q, %i)))", id, name, size)
end
return id
end
callback_register('define_font', base_define_font_cb)
callback_register('find_log_file', function(name) return name end)
do
local function normal_find_data_file(name)
return kpse.find_file(name, 'tex', true)
end
if status.ini_version then
function unhook_expl()
callback_register('find_data_file', normal_find_data_file)
end
callback_register('find_data_file', function(name)
if name == 'ltexpl.ltx' then
name = 'luametalatex-ltexpl-hook'
end
return normal_find_data_file(name)
end)
else
callback_register('find_data_file', normal_find_data_file)
end
end
callback_register('read_data_file', function(name) error[[TODO]]return kpse.find_file(name, 'tex', true) end)
callback_register('open_data_file', function(name)
local f = io.open(name)
return setmetatable({
reader = function()
local line = f:read()
return line
end,
close = function()error[[1]] return f:close() end,
}, {
__gc = function()f:close()end,
})
end)
callback_register('find_format_file', function(name) return kpse.find_file(name, 'fmt', true) end)
callback_register('handle_error_hook', function()
repeat
texio.write_nl'? '
local line = io.read()
if not line then
tex.fatalerror'End of line encountered on terminal'
end
if line == "" then return 3 end
local first = line:sub(1,1):upper()
if first == 'H' then
texio.write(tex.gethelptext() or "Sorry, I don't know how to help in this situation.\n\z
Maybe you should try asking a human?")
elseif first == 'I' then
line = line:sub(2)
tex.runtoks(function()
tex.sprint(token.scan_token(), line)
end)
return 3
elseif first == 'Q' then texio.write'OK, entering \\batchmode...\n' return 0
elseif first == 'R' then texio.write'OK, entering \\nonstopmode...\n' return 1
elseif first == 'S' then texio.write'OK, entering \\scrollmode...\n' return 2
elseif first == 'X' then return -1
else
texio.write'Type <return> to proceed, S to scroll future error messages,\
\z R to run without stopping, Q to run quietly,\
\z I to insert something,\
\z H for help, X to quit.'
end
until false
return 3
end)
callback_register('pre_dump', function()
local prepared = lua.prepared_code
prepared[1] = string.format("fixupluafunctions(%i)", fixupluafunctions())
for i=0,0 do -- maybeFIXME: In practise only one language is preloaded in LuaTeX anyway
-- for i=0,tex.count[19] do -- Sometimes catches reserved language ids which are not used yet
-- for i=0,lang.new():id()-1 do -- lang.new():id() is always 0 in luametatex?!?
local l = lang.new(i)
local str = string.format("do \z
local l = lang.new(%i)\z
l:hyphenationmin(%i)\z
l:prehyphenchar(%i)\z
l:posthyphenchar(%i)\z
l:preexhyphenchar(%i)\z
l:postexhyphenchar(%i)",
i,
l:hyphenationmin(),
l:prehyphenchar(),
l:posthyphenchar(),
l:preexhyphenchar(),
l:postexhyphenchar())
local patterns = l:patterns()
local exceptions = l:hyphenation()
if patterns and exceptions then
str = string.format("%sl:patterns(%q)l:hyphenation(%q)end", str, patterns, exceptions)
elseif patterns then
str = string.format("%sl:patterns(%q)end", str, patterns)
elseif exceptions then
str = string.format("%sl:hyphenation(%q)end", str, exceptions)
else
str = str .. 'end'
end
prepared[#prepared+1] = str
end
for i=2,#prepared do
if type(prepared[i]) ~= 'string' then
prepared[i] = assert(prepared[i]())
end
end
lua.bytecode[1] = assert(load(table.concat(prepared, ' ')))
end)
function texconfig.init()
lua.bytecode[2]()
if not status.ini_version then
lua.bytecode[2] = nil
end
end
local build_bytecode
if status.ini_version then
lua.prepared_code = {false}
local code = package.searchers[2]('luametalatex-firstcode')
if type(code) == "string" then error(string.format("Initialization code not found %s", code)) end
lua.bytecode[2] = code
local build_bytecode_mod = require'luametalatex-build-bytecode'
local preloaded_modules = {}
local old_searcher = package.searchers[2]
package.searchers[2] = function(name)
local mod, file = old_searcher(name)
if not file then return mod end -- Only works because we always return file when successful
preloaded_modules[#preloaded_modules+1] = {name, file}
return mod, file
end
function build_bytecode(str)
return load(build_bytecode_mod(preloaded_modules) .. "\nrequire'luametalatex-lateinit'(function()" .. str .. '\nend)', 'preloaded', 't')
end
end
callback_register('find_format_file', function(name) return kpse.find_file(name, 'fmt', true) end)
function texconfig.init()
if interaction then
tex.setinteraction(interaction)
end
if build_bytecode then -- Effectivly if status.ini_version
require'luametalatex-lateinit'(build_bytecode)
else
local register = tex.count[262]+1
lua.bytecode[register]()
lua.bytecode[register] = nil
end
end

131
luametalatex-lateinit.lua Normal file
View File

@ -0,0 +1,131 @@
local initex = status.ini_version
if initex then
lua.prepared_code = {false}
end
bit32 = require'luametalatex-bit32' -- Why? And why so early?
-- kpse.init_prog("LUATEX", 400, "nexthi", nil) -- ?
status.init_kpse = 1 -- Why?
status.safer_option = 0 -- compat
status.shell_escape = 0 -- compat -- This is currently a lie.
-- require'module' -- ???
pdf = {
getfontname = function(id) -- No font sharing yet (TODO)
return id
end,
variable = {},
}
require'luametalatex-font-resolve' -- Replace font.define. Must be loaded before callbacks
require'luametalatex-callbacks'
local functions = lua.getfunctionstable()
-- I am not sure why this is necessary, but otherwise LuaMetaTeX resets
-- the functions table every time the getter is called
function lua.get_functions_table() return functions end
local set_lua = token.set_lua
-- There are two approaches to manage luafunctions ids without triggering
-- issues with ltluatex assigned numbers: Originally we assigned numbers
-- starting with 1, then switched to luatexbase ASAP and synchronised both
-- numbering schemes. But there is a useful quirk in ltluatex's luafunction
-- allocator: It only uses numbers upto 65535, so we can just use bigger
-- numbers. (This might have negative repercussins on performance because it
-- probably doesn't store the function in the array part of the Lua table.
-- Let's reconsider if this ever becomes a problem.
-- local new_luafunction = luatexbase.new_luafunction
local predefined_luafunctions = initex and 65536 -- 1<<16 -- We start with 1<<16 + 1 (1<<16=65536 is reserved for luametalatex-local)
local new_luafunction
function new_luafunction(name)
if predefined_luafunctions then
predefined_luafunctions = predefined_luafunctions + 1
return predefined_luafunctions
else
error[[Here only preexisting luafunctions should be set]]
end
end
local undefined_cmd = token.command_id'undefined_cs'
local lua_call_cmd = token.command_id'lua_call'
local lua_value_cmd = token.command_id'lua_value'
local lua_expandable_call_cmd = token.command_id'lua_expandable_call'
function token.luacmd(name, func, ...)
local idx
local tok = token.create(name)
local cmd = tok.command
if cmd == lua_value_cmd then
idx = tok.mode
elseif cmd == lua_call_cmd then
idx = tok.mode
elseif cmd == lua_expandable_call_cmd then
idx = tok.mode
elseif ... == 'force' then
idx = new_luafunction(name)
set_lua(name, idx, select(2, ...))
elseif cmd == undefined_cmd then
idx = new_luafunction(name)
set_lua(name, idx, ...)
else
error(tok.cmdname)
end
if functions[idx] then
error[[Already defined]]
end
functions[idx] = func
return idx
end
if initex then
local build_bytecode = nil -- To be filled
callback.register('pre_dump', function()
local prepared = lua.prepared_code
prepared[1] = string.format("fixupluafunctions(%i)", predefined_luafunctions)
for i=0,0 do -- maybeFIXME: In practise only one language is preloaded in LuaTeX anyway
-- for i=0,tex.count[19] do -- Sometimes catches reserved language ids which are not used yet
-- for i=0,lang.new():id()-1 do -- lang.new():id() is always 0 in luametatex?!?
local l = lang.new(i)
local str = string.format("do \z
local l = lang.new(%i)\z
l:hyphenationmin(%i)\z
l:prehyphenchar(%i)\z
l:posthyphenchar(%i)\z
l:preexhyphenchar(%i)\z
l:postexhyphenchar(%i)",
i,
l:hyphenationmin(),
l:prehyphenchar(),
l:posthyphenchar(),
l:preexhyphenchar(),
l:postexhyphenchar())
local patterns = l:patterns()
local exceptions = l:hyphenation()
if patterns and exceptions then
str = string.format("%sl:patterns(%q)l:hyphenation(%q)end", str, patterns, exceptions)
elseif patterns then
str = string.format("%sl:patterns(%q)end", str, patterns)
elseif exceptions then
str = string.format("%sl:hyphenation(%q)end", str, exceptions)
else
str = str .. 'end'
end
prepared[#prepared+1] = str
end
for i=2,#prepared do
if type(prepared[i]) ~= 'string' then
prepared[i] = assert(prepared[i]())
end
end
lua.bytecode[tex.count[262]+1] = build_bytecode(table.concat(prepared, '\n'))
end)
return function(f)
build_bytecode = f
return require'luametalatex-firstcode'
end
else
function fixupluafunctions(i)
predefined_luafunctions = i
fixupluafunctions = nil
end
return function(f)
f()
return require'luametalatex-firstcode'
end
end