forked from rjpcomputing/luaforwindows
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSerializer.lua
More file actions
executable file
·291 lines (264 loc) · 7.66 KB
/
Serializer.lua
File metadata and controls
executable file
·291 lines (264 loc) · 7.66 KB
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
--------------------------------------------------------------------------------
---------------------- ## ##### ##### ###### -----------------------
---------------------- ## ## ## ## ## ## ## -----------------------
---------------------- ## ## ## ## ## ###### -----------------------
---------------------- ## ## ## ## ## ## -----------------------
---------------------- ###### ##### ##### ## -----------------------
---------------------- -----------------------
----------------------- Lua Object-Oriented Programming ------------------------
--------------------------------------------------------------------------------
-- Project: LOOP Class Library --
-- Release: 2.3 beta --
-- Title : Serializer that Serialize Values to Lua Code --
-- Author : Renato Maia <[email protected]> --
--------------------------------------------------------------------------------
local _G = _G
local getmetatable = getmetatable
local setmetatable = setmetatable
local getfenv = getfenv
local setfenv = setfenv
local package = package
local assert = assert
local select = select
local pairs = pairs
local pcall = pcall
local ipairs = ipairs
local loadstring = loadstring
local rawget = rawget
local rawset = rawset
local require = require
local tostring = tostring
local tonumber = tonumber
local error = error
local type = type
local debug = debug
local string = require "string"
local table = require "loop.table"
local oo = require "loop.base"
module("loop.serial.Serializer", oo.class)
__mode = "k"
namespace = "serial"
------------------------------------------------------------------------------
_M.globals = _G
_M.require = require
_M.getmetatable = getmetatable
_M.setmetatable = setmetatable
_M.getfenv = getfenv
_M.setfenv = setfenv
_M.getupvalue = debug and debug.getupvalue
_M.setupvalue = debug and debug.setupvalue
_M.package = package and package.loaded
------------------------------------------------------------------------------
Environment = oo.class{ __index = _G }
function addmembers(self, pack)
if type(pack) == "table" then
for field, member in pairs(pack) do
local kind = type(member)
if
self[member] == nil and
kind ~= "boolean" and kind ~= "number" and kind ~= "string" and
type(field) == "string" and field:match("^[%a_]+[%w_]*$")
then
self[member] = self[pack].."."..field
end
end
end
end
function __init(self, object)
self = oo.rawnew(self, object)
self.environment = self.environment or Environment()
self.environment[self.namespace] = self
if self.globals then
self[self.globals] = self.namespace..".globals"
for field, member in pairs(self.globals) do
if
self[member] == nil and
type(field) == "string" and field:match("^[%a_]+[%w_]*$") and
type(member) == "function" and not pcall(string.dump, member)
then
self[member] = self.namespace..".globals."..field
end
end
end
if self.package then
for name, pack in pairs(self.package) do
if self[pack] == nil then
self[pack] = self.namespace..'.require("'..name..'")'
self:addmembers(pack)
end
end
end
return self
end
------------------------------------------------------------------------------
local Incomplete = oo.class()
function Incomplete:__load(contents, metatable)
table.copy(contents, self)
return setmetatable(self, metatable)
end
function value(self, id, type, ...)
local value = self[id]
if not value then
if type == "function" then
value = assert(loadstring((...)))
elseif type == "userdata" then
value = assert(self[...], "unknown userdata")()
elseif type == "table" then
local meta
value, meta = ...
if meta and self.setmetatable then
self.setmetatable(value, meta)
end
else
value = Incomplete()
end
self[id] = value
elseif type == "table" and oo.classof(value) == Incomplete then
value:__load(...)
end
return value
end
function setup(self, value, ...)
local type = type(value)
if type == "function" then
if self.setfenv then self.setfenv(value, ... or self.globals) end
local setupvalue = self.setupvalue
if setupvalue then
local up = 1
while setupvalue(value, up, select(up+1, ...) or nil) do
up = up + 1
end
end
else
local loader = getmetatable(value)
if loader then loader = loader.__load end
if loader then loader(value, ...) end
end
return value
end
function load(self, data)
local errmsg
data, errmsg = loadstring(data)
if data then setfenv(data, self.environment) end
return data, errmsg
end
------------------------------------------------------------------------------
function serialstring(self, string)
self:write(string.format("%q", string))
end
function serialtable(self, table, id)
self[table] = self.namespace..":value("..id..")"
-- serialize contents
self:write(self.namespace,":value(",id,",'table',{")
for key, val in pairs(table) do
self:write("[")
self:serialize(key)
self:write("]=")
self:serialize(val)
self:write(",")
end
self:write("}")
-- serialize metatable
if self.getmetatable then
local meta = self.getmetatable(table)
if meta then
self:write(",")
self:serialize(meta)
end
end
self:write(")")
end
function serialfunction(self, func, id)
self[func] = self.namespace..":value("..id..")"
self:write(self.namespace,":setup(")
-- serialize bytecodes
self:write(self.namespace,":value(",id,",'function','")
local bytecodes = string.dump(func)
for i = 1, #bytecodes do
self:write("\\",string.byte(bytecodes, i))
end
self:write("')")
-- serialize environment
local env
if self.getfenv then
env = self.getfenv(func)
if env == self.globals then env = nil end
end
self:write(",")
self:serialize(env)
-- serialize upvalues
if self.getupvalue then
local name, value
local up = 1
repeat
name, value = self.getupvalue(func, up)
if name then
self:write(",")
self:serialize(value)
end
up = up + 1
until not name
end
self:write(")")
end
function serialcustom(self, id, name, ...)
local state = select("#", ...) > 0
if state then
self:write(self.namespace,":setup(")
end
self:write(self.namespace,":value(",id,",'userdata','",name,"')")
if state then
self:write(",")
self:serialize(...)
self:write(")")
end
end
function serialuserdata(self, userdata, id)
local serializer = getmetatable(userdata)
if serializer then
serializer = serializer.__serialize
if serializer then
self[userdata] = self.namespace..":value("..id..")"
return self:serialcustom(id, serializer(userdata))
end
end
error("unable to serialize a userdata without custom serialization")
end
local function getidfor(value)
local meta = getmetatable(value)
local backup
if meta then
backup = rawget(meta, "__tostring")
if backup ~= nil then rawset(meta, "__tostring", nil) end
end
local id = string.match(tostring(value), "%l+: (%w+)")
if meta then
if backup ~= nil then rawset(meta, "__tostring", backup) end
end
return tonumber(id, 16) or id
end
function serialize(self, ...)
for i=1, select("#", ...) do
if i ~= 1 then self:write(",") end
local value = select(i, ...)
local type = type(value)
if type == "nil" or type == "boolean" or type == "number" then
self:write(tostring(value))
elseif type == "string" then
self:serialstring(value)
else
local id = self[value]
if id then
self:write(id)
elseif self[type] then
self[type](self, value, getidfor(value))
else
error("unable to serialize a "..type)
end
end
end
end
_M["table"] = serialtable
_M["function"] = serialfunction
_M["userdata"] = serialuserdata
_M["thread"] = serialthread