compat_env.lua

changeset 5
7667b101cb1e
equal deleted inserted replaced
4:4cb0d1dbc65b 5:7667b101cb1e
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

mercurial