forked from annocx/ylua
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathenv.lua
166 lines (146 loc) · 5.72 KB
/
env.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
local parser = require("parser")
local runtime = require("runtime")
local function deepcopy(obj, seen)
-- Handle non-tables and previously-seen tables.
if type(obj) ~= 'table' then return obj end
if seen and seen[obj] then return seen[obj] end
-- New table; mark it as seen and copy recursively.
local s = seen or {}
local res = {}
s[obj] = res
for k, v in pairs(obj) do res[deepcopy(k, s)] = deepcopy(v, s) end
return setmetatable(res, getmetatable(obj))
end
local function errorfmt(index, funcname, msg)
return string.format("bad argument #%u to '%s' (%s)", index, funcname, msg)
end
local function getvarinfo(stacklevel, index)
local pc = runtime.currentpc[stacklevel]
local proto = runtime.protos[stacklevel]
local protos_index = 0
local i = 0
local varinfo
-- iterate over the the localvar table, skipping variables out of scope to get the variable name at a proper index
while protos_index < index and i <= #proto.localvar do
varinfo = proto.localvar[i]
if varinfo.start_pc < pc and varinfo.end_pc >= pc then
protos_index = protos_index + 1
end
i = i + 1
end
-- if we didn't exhaust the localvar list in the loop before
if protos_index == index then
return varinfo
end
end
local env = {
[0] = { _ENV }
}
local natives = {}
local debuglib = {}
natives.debug = debug
env[0][1].debug = debuglib
function debuglib.getlocal(f, index)
assert(type(f) ~= "thread", errorfmt(1, "getlocal", "YLua: thread argument not yet supported"))
assert(type(f) == "number" or type(f) == "function", errorfmt(1, "getlocal", "number expected, got " .. type(f)))
assert(type(index) == "number", errorfmt(2, "getlocal", "number expected, got " .. type(index)))
if type(f) == "function" then
assert(runtime.closures[f], errorfmt(1, "getlocal", "YLua: C functions not supported"))
local proto = runtime.closures[f].proto
if index > 0 and proto.localvar[index - 1] and proto.localvar[index - 1].start_pc == 0 then
return proto.localvar[index - 1].varname.val
else
return nil
end
else -- type(f) == "number"
assert(f ~= 0, errorfmt(1, "getlocal", "YLua: introspection of stack at level 0 not supported"))
assert(f > 0 and f <= runtime.current_stacklevel, errorfmt(1, "getlocal", "level out of range"))
local stacklevel = runtime.current_stacklevel - f + 1
local proto = runtime.protos[stacklevel]
local varinfo
if index > 0 then
local varinfo = getvarinfo(stacklevel, index)
if varinfo then
return varinfo.varname.val, runtime.registers[stacklevel][index - 1]
else
return nil -- return nil when no variable found
end
elseif index < 0 then
error(errorfmt(2, "getlocal", "YLua: negative stack level not yet supported"))
else -- if index is 0, return nil
return nil
end
end
end
function debug.setlocal(level, index, value)
assert(type(level) ~= "thread", errorfmt(1, "setlocal", "YLua: thread argument not yet supported"))
assert(type(level) == "number", errorfmt(1, "setlocal", "number expected, got " .. type(level)))
assert(type(index) == "number", errorfmt(2, "setlocal", "number expected, got " .. type(index)))
assert(level ~= 0, errorfmt(1, "setlocal", "YLua: modifying stack at level 0 not supported"))
assert(level > 0 and level <= runtime.current_stacklevel, errorfmt(1, "setlocal", "level out of range"))
local stacklevel = runtime.current_stacklevel - level + 1
local proto = runtime.protos[stacklevel]
if index > 0 then
local varinfo = getvarinfo(stacklevel, index)
if varinfo then
runtime.registers[stacklevel][index - 1] = value
return varinfo.varname.val
else
return nil -- return nil when no variable found
end
elseif index < 0 then
error(errorfmt(2, "getlocal", "YLua: negative stack level not yet supported"))
else -- if index is 0, return nil
return nil
end
end
--[[ natives.load = load
env[0][1].load = function(chunk, chunkname, mode, environment)
local chunkstr
local chunknamestr
environment = { [0] = { environment } } or env
if type(chunk) == "string" then
chunkstr = chunk
chunknamestr = chunknamestr or "chunk"
elseif type(chunk) == "function" then
local function read(f)
local chunkpiece = f()
local chunkt = {}
while chunkpiece ~= "" and chunkpiece ~= nil do
assert(type(chunkpiece) == "string", "reader function must return a string")
chunkt[#chunkt + 1] = chunkpiece
chunkpiece = f()
end
return table.concat(chunkt)
end
local isok
isok, chunkstr = pcall(read, chunk)
if not isok then
return nil, chunkstr
end
chunknamestr = chunknamestr or "=(load)"
end
mode = mode or "bt"
if string.find(mode, "bt") or string.find(mode, "tb") then
mode = string.sub(chunkstr, 1, 1) == "\27" and "b" or "t"
end
if mode == "t" then
local errstr
chunkstr, errstr = natives.load(chunkstr, chunkname, mode, environment)
if not errstr then
chunkstr = string.dump(chunkstr)
else
return nil, errstr
end
end
local isok, func = pcall(parser.parse_bytecode, chunkstr)
if isok then
return function(...)
func.args = table.pack(...)
return runtime.exec_bytecode(func, environment)
end
else
return nil, func
end
end ]]
return env