Fri, 17 Jul 2020 12:25:24 -0500
fix weird nodejs length/index
| 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 |