aboutsummaryrefslogtreecommitdiff
path: root/src/compat53/init.lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/compat53/init.lua')
-rw-r--r--src/compat53/init.lua325
1 files changed, 325 insertions, 0 deletions
diff --git a/src/compat53/init.lua b/src/compat53/init.lua
new file mode 100644
index 00000000..b5075713
--- /dev/null
+++ b/src/compat53/init.lua
@@ -0,0 +1,325 @@
1local lua_version = _VERSION:sub(-3)
2
3
4if lua_version < "5.3" then
5
6 local _G, pairs, require, select, type =
7 _G, pairs, require, select, type
8 local debug, io = debug, io
9 local unpack = lua_version == "5.1" and unpack or table.unpack
10
11 local M = require("compat53.module")
12
13 -- select the most powerful getmetatable function available
14 local gmt = type(debug) == "table" and debug.getmetatable or
15 getmetatable or function() return false end
16 -- metatable for file objects from Lua's standard io library
17 local file_meta = gmt(io.stdout)
18
19
20 -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag)
21 local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ"
22 local is_luajit52 = is_luajit and
23 #setmetatable({}, { __len = function() return 1 end }) == 1
24
25
26 if type(file_meta) == "table" and type(file_meta.__index) == "table" then
27 local file_mt = require("compat53.file_mt")
28 file_mt.update_file_meta(file_meta, is_luajit52)
29 end -- got a valid metatable for file objects
30
31
32 -- changes for Lua 5.1 only
33 if lua_version == "5.1" then
34
35 -- cache globals
36 local error, pcall, rawset, setmetatable, tostring, xpcall =
37 error, pcall, rawset, setmetatable, tostring, xpcall
38 local coroutine, package, string = coroutine, package, string
39 local coroutine_resume = coroutine.resume
40 local coroutine_running = coroutine.running
41 local coroutine_status = coroutine.status
42 local coroutine_yield = coroutine.yield
43 local io_type = io.type
44
45
46 -- make package.searchers available as an alias for package.loaders
47 local p_index = { searchers = package.loaders }
48 setmetatable(package, {
49 __index = p_index,
50 __newindex = function(p, k, v)
51 if k == "searchers" then
52 rawset(p, "loaders", v)
53 p_index.searchers = v
54 else
55 rawset(p, k, v)
56 end
57 end
58 })
59
60
61 if type(file_meta) == "table" and type(file_meta.__index) == "table" then
62 if not is_luajit then
63 local function helper(_, var_1, ...)
64 if var_1 == nil then
65 if (...) ~= nil then
66 error((...), 2)
67 end
68 end
69 return var_1, ...
70 end
71
72 local function lines_iterator(st)
73 return helper(st, st.f:read(unpack(st, 1, st.n)))
74 end
75
76 local file_write = file_meta.__index.write
77 file_meta.__index.write = function(self, ...)
78 local res, msg, errno = file_write(self, ...)
79 if res then
80 return self
81 else
82 return nil, msg, errno
83 end
84 end
85
86 file_meta.__index.lines = function(self, ...)
87 if io_type(self) == "closed file" then
88 error("attempt to use a closed file", 2)
89 end
90 local st = { f=self, n=select('#', ...), ... }
91 for i = 1, st.n do
92 local t = type(st[i])
93 if t == "string" then
94 local fmt = st[i]:match("^*?([aln])")
95 if not fmt then
96 error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
97 end
98 st[i] = "*"..fmt
99 elseif t ~= "number" then
100 error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
101 end
102 end
103 return lines_iterator, st
104 end
105 end -- not luajit
106 end -- file_meta valid
107
108
109 -- the (x)pcall implementations start a new coroutine internally
110 -- to allow yielding even in Lua 5.1. to allow for accurate
111 -- stack traces we keep track of the nested coroutine activations
112 -- in the weak tables below:
113 local weak_meta = { __mode = "kv" }
114 -- maps the internal pcall coroutines to the user coroutine that
115 -- *should* be running if pcall didn't use coroutines internally
116 local pcall_mainOf = setmetatable({}, weak_meta)
117 -- table that maps each running coroutine started by pcall to
118 -- the coroutine that resumed it (user coroutine *or* pcall
119 -- coroutine!)
120 local pcall_previous = setmetatable({}, weak_meta)
121 -- reverse of `pcall_mainOf`. maps a user coroutine to the
122 -- currently active pcall coroutine started within it
123 local pcall_callOf = setmetatable({}, weak_meta)
124 -- similar to `pcall_mainOf` but is used only while executing
125 -- the error handler of xpcall (thus no nesting is necessary!)
126 local xpcall_running = setmetatable({}, weak_meta)
127
128 -- handle debug functions
129 if type(debug) == "table" then
130 local debug_getinfo = debug.getinfo
131 local debug_traceback = debug.traceback
132
133 if not is_luajit then
134 local function calculate_trace_level(co, level)
135 if level ~= nil then
136 for out = 1, 1/0 do
137 local info = (co==nil) and debug_getinfo(out, "") or debug_getinfo(co, out, "")
138 if info == nil then
139 local max = out-1
140 if level <= max then
141 return level
142 end
143 return nil, level-max
144 end
145 end
146 end
147 return 1
148 end
149
150 local stack_pattern = "\nstack traceback:"
151 local stack_replace = ""
152 function debug.traceback(co, msg, level)
153 local lvl
154 local nilmsg
155 if type(co) ~= "thread" then
156 co, msg, level = coroutine_running(), co, msg
157 end
158 if msg == nil then
159 msg = ""
160 nilmsg = true
161 elseif type(msg) ~= "string" then
162 return msg
163 end
164 if co == nil then
165 msg = debug_traceback(msg, level or 1)
166 else
167 local xpco = xpcall_running[co]
168 if xpco ~= nil then
169 lvl, level = calculate_trace_level(xpco, level)
170 if lvl then
171 msg = debug_traceback(xpco, msg, lvl)
172 else
173 msg = msg..stack_pattern
174 end
175 lvl, level = calculate_trace_level(co, level)
176 if lvl then
177 local trace = debug_traceback(co, "", lvl)
178 msg = msg..trace:gsub(stack_pattern, stack_replace)
179 end
180 else
181 co = pcall_callOf[co] or co
182 lvl, level = calculate_trace_level(co, level)
183 if lvl then
184 msg = debug_traceback(co, msg, lvl)
185 else
186 msg = msg..stack_pattern
187 end
188 end
189 co = pcall_previous[co]
190 while co ~= nil do
191 lvl, level = calculate_trace_level(co, level)
192 if lvl then
193 local trace = debug_traceback(co, "", lvl)
194 msg = msg..trace:gsub(stack_pattern, stack_replace)
195 end
196 co = pcall_previous[co]
197 end
198 end
199 if nilmsg then
200 msg = msg:gsub("^\n", "")
201 end
202 msg = msg:gsub("\n\t%(tail call%): %?", "\000")
203 msg = msg:gsub("\n\t%.%.%.\n", "\001\n")
204 msg = msg:gsub("\n\t%.%.%.$", "\001")
205 msg = msg:gsub("(%z+)\001(%z+)", function(some, other)
206 return "\n\t(..."..#some+#other.."+ tail call(s)...)"
207 end)
208 msg = msg:gsub("\001(%z+)", function(zeros)
209 return "\n\t(..."..#zeros.."+ tail call(s)...)"
210 end)
211 msg = msg:gsub("(%z+)\001", function(zeros)
212 return "\n\t(..."..#zeros.."+ tail call(s)...)"
213 end)
214 msg = msg:gsub("%z+", function(zeros)
215 return "\n\t(..."..#zeros.." tail call(s)...)"
216 end)
217 msg = msg:gsub("\001", function()
218 return "\n\t..."
219 end)
220 return msg
221 end
222 end -- is not luajit
223 end -- debug table available
224
225
226 if not is_luajit52 then
227 local coroutine_running52 = M.coroutine.running
228 function M.coroutine.running()
229 local co, ismain = coroutine_running52()
230 if ismain then
231 return co, true
232 else
233 return pcall_mainOf[co] or co, false
234 end
235 end
236 end
237
238 if not is_luajit then
239 local function pcall_results(current, call, success, ...)
240 if coroutine_status(call) == "suspended" then
241 return pcall_results(current, call, coroutine_resume(call, coroutine_yield(...)))
242 end
243 if pcall_previous then
244 pcall_previous[call] = nil
245 local main = pcall_mainOf[call]
246 if main == current then current = nil end
247 pcall_callOf[main] = current
248 end
249 pcall_mainOf[call] = nil
250 return success, ...
251 end
252
253 local function pcall_exec(current, call, ...)
254 local main = pcall_mainOf[current] or current
255 pcall_mainOf[call] = main
256 if pcall_previous then
257 pcall_previous[call] = current
258 pcall_callOf[main] = call
259 end
260 return pcall_results(current, call, coroutine_resume(call, ...))
261 end
262
263 local coroutine_create52 = M.coroutine.create
264
265 local function pcall_coroutine(func)
266 if type(func) ~= "function" then
267 local callable = func
268 func = function (...) return callable(...) end
269 end
270 return coroutine_create52(func)
271 end
272
273 function M.pcall(func, ...)
274 local current = coroutine_running()
275 if not current then return pcall(func, ...) end
276 return pcall_exec(current, pcall_coroutine(func), ...)
277 end
278
279 local function xpcall_catch(current, call, msgh, success, ...)
280 if not success then
281 xpcall_running[current] = call
282 local ok, result = pcall(msgh, ...)
283 xpcall_running[current] = nil
284 if not ok then
285 return false, "error in error handling ("..tostring(result)..")"
286 end
287 return false, result
288 end
289 return true, ...
290 end
291
292 function M.xpcall(f, msgh, ...)
293 local current = coroutine_running()
294 if not current then
295 local args, n = { ... }, select('#', ...)
296 return xpcall(function() return f(unpack(args, 1, n)) end, msgh)
297 end
298 local call = pcall_coroutine(f)
299 return xpcall_catch(current, call, msgh, pcall_exec(current, call, ...))
300 end
301 end -- not luajit
302
303 end -- lua 5.1
304
305
306 -- handle exporting to global scope
307 local function extend_table(from, to)
308 if from ~= to then
309 for k,v in pairs(from) do
310 if type(v) == "table" and
311 type(to[k]) == "table" and
312 v ~= to[k] then
313 extend_table(v, to[k])
314 else
315 to[k] = v
316 end
317 end
318 end
319 end
320
321 extend_table(M, _G)
322
323end -- lua < 5.3
324
325-- vi: set expandtab softtabstop=3 shiftwidth=3 :