Tue, 16 Jan 2018 17:02:29 +0000
Further lua5.3 compatibility fixes
5 | 1 | --[[ |
2 | compat_env - see README for details. | |
3 | (c) 2012 David Manura. Licensed under Lua 5.1/5.2 terms (MIT license). | |
4 | --]] | |
5 | ||
6 | local M = {_TYPE='module', _NAME='compat_env', _VERSION='0.2.2.20120406'} | |
7 | ||
8 | local function check_chunk_type(s, mode) | |
9 | local nmode = mode or 'bt' | |
10 | local is_binary = s and #s > 0 and s:byte(1) == 27 | |
11 | if is_binary and not nmode:match'b' then | |
12 | return nil, ("attempt to load a binary chunk (mode is '%s')"):format(mode) | |
13 | elseif not is_binary and not nmode:match't' then | |
14 | return nil, ("attempt to load a text chunk (mode is '%s')"):format(mode) | |
15 | end | |
16 | return true | |
17 | end | |
18 | ||
19 | local IS_52_LOAD = pcall(load, '') | |
20 | if IS_52_LOAD then | |
21 | M.load = _G.load | |
22 | M.loadfile = _G.loadfile | |
23 | else | |
24 | -- 5.2 style `load` implemented in 5.1 | |
25 | function M.load(ld, source, mode, env) | |
26 | local f | |
27 | if type(ld) == 'string' then | |
28 | local s = ld | |
29 | local ok, err = check_chunk_type(s, mode) | |
30 | if not ok then return ok, err end | |
31 | local err; f, err = loadstring(s, source) | |
32 | if not f then return f, err end | |
33 | elseif type(ld) == 'function' then | |
34 | local ld2 = ld | |
35 | if (mode or 'bt') ~= 'bt' then | |
36 | local first = ld() | |
37 | local ok, err = check_chunk_type(first, mode) | |
38 | if not ok then return ok, err end | |
39 | ld2 = function() | |
40 | if first then | |
41 | local chunk=first; first=nil; return chunk | |
42 | else return ld() end | |
43 | end | |
44 | end | |
45 | local err; f, err = load(ld2, source); if not f then return f, err end | |
46 | else | |
47 | error(("bad argument #1 to 'load' (function expected, got %s)") | |
48 | :format(type(ld)), 2) | |
49 | end | |
50 | if env then setfenv(f, env) end | |
51 | return f | |
52 | end | |
53 | ||
54 | -- 5.2 style `loadfile` implemented in 5.1 | |
55 | function M.loadfile(filename, mode, env) | |
56 | if (mode or 'bt') ~= 'bt' then | |
57 | local ioerr | |
58 | local fh, err = io.open(filename, 'rb'); if not fh then return fh,err end | |
59 | local function ld() | |
60 | local chunk; chunk,ioerr = fh:read(4096); return chunk | |
61 | end | |
62 | local f, err = M.load(ld, filename and '@'..filename, mode, env) | |
63 | fh:close() | |
64 | if not f then return f, err end | |
65 | if ioerr then return nil, ioerr end | |
66 | return f | |
67 | else | |
68 | local f, err = loadfile(filename); if not f then return f, err end | |
69 | if env then setfenv(f, env) end | |
70 | return f | |
71 | end | |
72 | end | |
73 | end | |
74 | ||
75 | if _G.setfenv then -- Lua 5.1 | |
76 | M.setfenv = _G.setfenv | |
77 | M.getfenv = _G.getfenv | |
78 | else -- >= Lua 5.2 | |
79 | -- helper function for `getfenv`/`setfenv` | |
80 | local function envlookup(f) | |
81 | local name, val | |
82 | local up = 0 | |
83 | local unknown | |
84 | repeat | |
85 | up=up+1; name, val = debug.getupvalue(f, up) | |
86 | if name == '' then unknown = true end | |
87 | until name == '_ENV' or name == nil | |
88 | if name ~= '_ENV' then | |
89 | up = nil | |
90 | if unknown then | |
91 | error("upvalues not readable in Lua 5.2 when debug info missing", 3) | |
92 | end | |
93 | end | |
94 | return (name == '_ENV') and up, val, unknown | |
95 | end | |
96 | ||
97 | -- helper function for `getfenv`/`setfenv` | |
98 | local function envhelper(f, name) | |
99 | if type(f) == 'number' then | |
100 | if f < 0 then | |
101 | error(("bad argument #1 to '%s' (level must be non-negative)") | |
102 | :format(name), 3) | |
103 | elseif f < 1 then | |
104 | error("thread environments unsupported in Lua 5.2", 3) --[*] | |
105 | end | |
106 | f = debug.getinfo(f+2, 'f').func | |
107 | elseif type(f) ~= 'function' then | |
108 | error(("bad argument #1 to '%s' (number expected, got %s)") | |
109 | :format(type(name, f)), 2) | |
110 | end | |
111 | return f | |
112 | end | |
113 | -- [*] might simulate with table keyed by coroutine.running() | |
114 | ||
115 | -- 5.1 style `setfenv` implemented in 5.2 | |
116 | function M.setfenv(f, t) | |
117 | local f = envhelper(f, 'setfenv') | |
118 | local up, val, unknown = envlookup(f) | |
119 | if up then | |
120 | debug.upvaluejoin(f, up, function() return up end, 1) --unique upval[*] | |
121 | debug.setupvalue(f, up, t) | |
122 | else | |
123 | local what = debug.getinfo(f, 'S').what | |
124 | if what ~= 'Lua' and what ~= 'main' then -- not Lua func | |
125 | error("'setfenv' cannot change environment of given object", 2) | |
126 | end -- else ignore no _ENV upvalue (warning: incompatible with 5.1) | |
127 | end | |
128 | return f -- invariant: original f ~= 0 | |
129 | end | |
130 | -- [*] http://lua-users.org/lists/lua-l/2010-06/msg00313.html | |
131 | ||
132 | -- 5.1 style `getfenv` implemented in 5.2 | |
133 | function M.getfenv(f) | |
134 | if f == 0 or f == nil then return _G end -- simulated behavior | |
135 | local f = envhelper(f, 'setfenv') | |
136 | local up, val = envlookup(f) | |
137 | if not up then return _G end -- simulated behavior [**] | |
138 | return val | |
139 | end | |
140 | -- [**] possible reasons: no _ENV upvalue, C function | |
141 | end | |
142 | ||
143 | ||
144 | return M |