Merge lp:~widelands-dev/widelands/lua_eris into lp:widelands

Proposed by SirVer
Status: Merged
Merged at revision: 6829
Proposed branch: lp:~widelands-dev/widelands/lua_eris
Merge into: lp:widelands
Diff against target: 32789 lines (+24836/-5410)
143 files modified
CMakeLists.txt (+0/-3)
CREDITS (+3/-3)
doc/sphinx/source/implementation.rst (+11/-12)
regression_test.py (+1/-0)
scripting/format_help.lua (+2/-2)
scripting/infrastructure.lua (+1/-1)
scripting/lunit.lua (+129/-320)
src/CMakeLists.txt (+8/-6)
src/ai/defaultai.cc (+1/-1)
src/logic/bill_of_materials.h (+5/-4)
src/logic/building.h (+1/-1)
src/logic/cmd_luacoroutine.cc (+1/-1)
src/logic/editor_game_base.cc (+1/-1)
src/logic/game.cc (+1/-1)
src/logic/production_program.cc (+2/-2)
src/logic/production_program.h (+2/-2)
src/logic/productionsite.cc (+5/-6)
src/logic/productionsite.h (+6/-6)
src/logic/tribe.cc (+5/-10)
src/map_io/widelands_map_buildingdata_data_packet.cc (+3/-3)
src/map_io/widelands_map_scripting_data_packet.cc (+14/-4)
src/network/netclient.cc (+6/-7)
src/network/nethost.cc (+1/-1)
src/scripting/CMakeLists.txt (+74/-1)
src/scripting/c_utils.cc (+1/-3)
src/scripting/c_utils.h (+2/-2)
src/scripting/coroutine_impl.cc (+0/-144)
src/scripting/coroutine_impl.h (+0/-63)
src/scripting/eris.h (+29/-0)
src/scripting/eris/README.eris (+9/-9)
src/scripting/eris/eris.c (+2658/-0)
src/scripting/eris/eris.h (+148/-0)
src/scripting/eris/lapi.c (+1284/-0)
src/scripting/eris/lapi.h (+24/-0)
src/scripting/eris/lauxlib.c (+959/-0)
src/scripting/eris/lauxlib.h (+212/-0)
src/scripting/eris/lbaselib.c (+494/-0)
src/scripting/eris/lbitlib.c (+212/-0)
src/scripting/eris/lcode.c (+881/-0)
src/scripting/eris/lcode.h (+83/-0)
src/scripting/eris/lcorolib.c (+171/-0)
src/scripting/eris/lctype.c (+52/-0)
src/scripting/eris/lctype.h (+95/-0)
src/scripting/eris/ldblib.c (+398/-0)
src/scripting/eris/ldebug.c (+593/-0)
src/scripting/eris/ldebug.h (+34/-0)
src/scripting/eris/ldo.c (+681/-0)
src/scripting/eris/ldo.h (+46/-0)
src/scripting/eris/ldump.c (+173/-0)
src/scripting/eris/lfunc.c (+161/-0)
src/scripting/eris/lfunc.h (+33/-0)
src/scripting/eris/lgc.c (+1220/-0)
src/scripting/eris/lgc.h (+157/-0)
src/scripting/eris/linit.c (+68/-0)
src/scripting/eris/liolib.c (+682/-0)
src/scripting/eris/llex.c (+530/-0)
src/scripting/eris/llex.h (+78/-0)
src/scripting/eris/llimits.h (+309/-0)
src/scripting/eris/lmathlib.c (+279/-0)
src/scripting/eris/lmem.c (+99/-0)
src/scripting/eris/lmem.h (+57/-0)
src/scripting/eris/loadlib.c (+759/-0)
src/scripting/eris/lobject.c (+287/-0)
src/scripting/eris/lobject.h (+607/-0)
src/scripting/eris/lopcodes.c (+107/-0)
src/scripting/eris/lopcodes.h (+288/-0)
src/scripting/eris/loslib.c (+323/-0)
src/scripting/eris/lparser.c (+1638/-0)
src/scripting/eris/lparser.h (+119/-0)
src/scripting/eris/lstate.c (+323/-0)
src/scripting/eris/lstate.h (+228/-0)
src/scripting/eris/lstring.c (+185/-0)
src/scripting/eris/lstring.h (+46/-0)
src/scripting/eris/lstrlib.c (+1035/-0)
src/scripting/eris/ltable.c (+588/-0)
src/scripting/eris/ltable.h (+45/-0)
src/scripting/eris/ltablib.c (+283/-0)
src/scripting/eris/ltm.c (+77/-0)
src/scripting/eris/ltm.h (+57/-0)
src/scripting/eris/lua.c (+497/-0)
src/scripting/eris/lua.h (+444/-0)
src/scripting/eris/lua.hpp (+9/-0)
src/scripting/eris/luac.c (+432/-0)
src/scripting/eris/luaconf.h (+551/-0)
src/scripting/eris/lualib.h (+58/-0)
src/scripting/eris/lundump.c (+258/-0)
src/scripting/eris/lundump.h (+28/-0)
src/scripting/eris/lvm.c (+867/-0)
src/scripting/eris/lvm.h (+44/-0)
src/scripting/eris/lzio.c (+76/-0)
src/scripting/eris/lzio.h (+65/-0)
src/scripting/factory.h (+1/-2)
src/scripting/lua_bases.cc (+6/-5)
src/scripting/lua_bases.h (+1/-2)
src/scripting/lua_editor.cc (+6/-6)
src/scripting/lua_editor.h (+1/-2)
src/scripting/lua_game.cc (+6/-5)
src/scripting/lua_game.h (+1/-4)
src/scripting/lua_globals.cc (+3/-7)
src/scripting/lua_globals.h (+1/-3)
src/scripting/lua_map.cc (+596/-561)
src/scripting/lua_map.h (+38/-159)
src/scripting/lua_root.cc (+5/-5)
src/scripting/lua_root.h (+1/-4)
src/scripting/lua_ui.cc (+6/-6)
src/scripting/lua_ui.h (+1/-2)
src/scripting/luna.h (+3/-13)
src/scripting/luna_impl.cc (+33/-30)
src/scripting/luna_impl.h (+23/-13)
src/scripting/pdep.cc (+0/-112)
src/scripting/pdep/README (+0/-5)
src/scripting/pdep/lauxlib.h (+0/-174)
src/scripting/pdep/ldo.h (+0/-57)
src/scripting/pdep/lfunc.h (+0/-34)
src/scripting/pdep/lgc.h (+0/-110)
src/scripting/pdep/llimits.h (+0/-128)
src/scripting/pdep/lobject.h (+0/-381)
src/scripting/pdep/lopcodes.h (+0/-268)
src/scripting/pdep/lstate.h (+0/-169)
src/scripting/pdep/lstring.h (+0/-31)
src/scripting/pdep/ltm.h (+0/-54)
src/scripting/pdep/lua.h (+0/-388)
src/scripting/pdep/lzio.h (+0/-65)
src/scripting/pdep/pdep.h (+0/-44)
src/scripting/persistence.cc (+88/-41)
src/scripting/persistence.h (+5/-3)
src/scripting/pluto.cc (+0/-1430)
src/scripting/pluto.h (+0/-31)
src/scripting/scripting.cc (+322/-280)
src/scripting/scripting.h (+123/-63)
src/scripting/test/CMakeLists.txt (+12/-10)
src/scripting/test/test_luna.cc (+24/-23)
src/ui_basic/helpwindow.cc (+2/-4)
src/ui_fsmenu/fileview.cc (+2/-2)
src/ui_fsmenu/launchMPG.cc (+1/-1)
src/ui_fsmenu/launchSPG.cc (+1/-1)
test/maps/expedition.wmf/scripting/init.lua (+0/-1)
test/maps/lua_persistence.wmf/scripting/test_persistence.lua (+5/-11)
test/maps/lua_testsuite.wmf/scripting/common_init.lua (+0/-1)
test/maps/ship_transportation.wmf/scripting/init.lua (+0/-1)
test/save/compatibility_build15/test_game_loads.lua (+0/-13)
test/save/compatibility_build16/test_game_loads.lua (+0/-13)
test/save/compatibility_build17/test_game_loads.lua (+0/-13)
To merge this branch: bzr merge lp:~widelands-dev/widelands/lua_eris
Reviewer Review Type Date Requested Status
Jens Beyer Pending
Widelands Developers Pending
Review via email: mp+191901@code.launchpad.net

Commit message

This is meant for merging after b18. It gets rid of some subtle persistence bugs in Lua like bug 1237016. This brings the following changes:

- kill our hacked and modified version of Pluto and instead include a pristine, unchanged version of Eris and use this for persistence (https://github.com/fnuecke/eris).
- This also means that we no longer depend on Lua 5.1 to be on the system as Eris brings Lua 5.2 with it. That also means that players with the same Widelands version use the exact same persistence and Lua logic - which is a good thing, though I am not aware that we had trouble with inconsistencies between Lua's minor versions before.
- Makes the necessary changes so that widelands runs with Lua 5.2. This brought a bunch of code simplifications with it - But **killed safegame compatibility for all safegames**.
- Some code cleanups and removal of bad c++ style like virtual inheritance.

Description of the change

This is meant for merging after b18. It gets rid of some subtle persistence bugs in Lua like bug 1237016. This brings the following changes:

- kill our hacked and modified version of Pluto and instead include a pristine, unchanged version of Eris and use this for persistence (https://github.com/fnuecke/eris).
- This also means that we no longer depend on Lua 5.1 to be on the system as Eris brings Lua 5.2 with it. That also means that players with the same Widelands version use the exact same persistence and Lua logic - which is a good thing, though I am not aware that we had trouble with inconsistencies between Lua's minor versions before.
- Makes the necessary changes so that widelands runs with Lua 5.2. This brought a bunch of code simplifications with it - But **killed safegame compatibility for all safegames**.
- Some code cleanups and removal of bad c++ style like virtual inheritance.

Todos:
- our Cmake setup is not equipped to compile .c files it seems, so I am not sure if the CFLAGS are set correctly (or at all). Works on my system though :P. Also, lua sets some cflags depending on which system it compiles, but they should not be needed. Jens, could you look into this?
- As this breaks safegame compatiblity anyways, we can use this to get rid of cruft - compatibilty wares, backwards compatible loading branches for old safegames and so on.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2013-12-29 16:10:29 +0000
3+++ CMakeLists.txt 2014-02-22 14:58:17 +0000
4@@ -477,9 +477,6 @@
5 message(FATAL_ERROR "Required package SDL_gfx not found, please install and re-run cmake")
6 endif (NOT SDLGFX_FOUND)
7
8-find_package(Lua51 REQUIRED)
9-include_directories(${LUA_INCLUDE_DIR})
10-
11 IF (WIN32)
12 SET(GUI_TYPE WIN32)
13 ENDIF (WIN32)
14
15=== modified file 'CREDITS'
16--- CREDITS 2012-06-20 09:59:07 +0000
17+++ CREDITS 2014-02-22 14:58:17 +0000
18@@ -75,9 +75,9 @@
19 * http://www.lua.org
20
21
22- Pluto
23- * by Ben Sunshine-Hill, Rob Hoelz
24- * http://luaforge.net/projects/pluto/
25+ Eris
26+ * by Florian Nücke
27+ * https://github.com/fnuecke/eris
28
29
30 Lodepng (for testing, not in Widelands itself)
31
32=== modified file 'doc/sphinx/source/implementation.rst'
33--- doc/sphinx/source/implementation.rst 2012-06-20 09:07:10 +0000
34+++ doc/sphinx/source/implementation.rst 2014-02-22 14:58:17 +0000
35@@ -104,20 +104,18 @@
36 -----------
37
38 When a savegame is created, the current environment of Lua is persisted. For
39-this, I used a library called Pluto_ which is in the public domain. It was
40-heavily modified to work with widelands, the ``FileRead`` and ``FileWrite``
41-classes and with the Luna class concepts.
42+this, I used a library called Eris_.
43
44-.. _Pluto: http://luaforge.net/projects/pluto/
45+.. _Eris: https://github.com/fnuecke
46
47 The global environment is persisted into the file map/globals.dump. Everything
48 is persisted except of the Lua build-in functions and everything in the ``wl``
49-table. Those are c-functions that can not be written out to disk portably.
50-Everything else can be saved, that is also everything in the auxiliary scripts
51-are saved to disk, so save games only depend on the API defined inside the
52-``wl`` module.
53+table and some of our own global functions. Those are c-functions that can not
54+be written out to disk portably. Everything else can be saved, that is also
55+everything in the auxiliary scripts are saved to disk, so save games only
56+depend on the API defined inside the Widelands.
57
58-Coroutines are persisted together with their ``Cmd_LuaCoroutine`` package.
59+Coroutines are persisted in their ``Cmd_LuaCoroutine`` package.
60
61 Luna classes have to implement two functions to be properly persistable:
62
63@@ -130,9 +128,10 @@
64 ``__unpersist(lua_State *)``
65 On loading, an instance of the user object is created via the default
66 constructor. This function is then called with the table that was created
67- by ``__persist()`` on the top of the stack. The object is then expected to
68- recreate it's former state with this table. There are equivalent
69- unpersisting macros defined to help with this task (e.g. UNPERS_UINT32).
70+ by ``__persist()`` as an upvalue (because it is inside a closure). The
71+ object is then expected to recreate it's former state with this table.
72+ There are equivalent unpersisting macros defined to help with this task
73+ (e.g. UNPERS_UINT32).
74
75 Widelands reassigns some serial number upon saving and restores them upon
76 loading. Some Luna classes need this information (for example
77
78=== modified file 'regression_test.py'
79--- regression_test.py 2014-01-15 21:12:05 +0000
80+++ regression_test.py 2014-02-22 14:58:17 +0000
81@@ -36,6 +36,7 @@
82 if os.path.exists(self.run_dir):
83 if not self.keep_output_around:
84 shutil.rmtree(self.run_dir)
85+ os.makedirs(self.run_dir)
86 else:
87 os.makedirs(self.run_dir)
88 self.widelands_returncode = 0
89
90=== modified file 'scripting/format_help.lua'
91--- scripting/format_help.lua 2012-03-06 22:14:31 +0000
92+++ scripting/format_help.lua 2014-02-22 14:58:17 +0000
93@@ -19,7 +19,7 @@
94 end
95
96 string = "image=" .. images[1]
97- for k,v in ipairs({unpack(images,2)}) do
98+ for k,v in ipairs({table.unpack(images,2)}) do
99 string = string .. ";pics/arrow-right.png;" .. v
100 end
101
102@@ -81,4 +81,4 @@
103 tab_picture = "pics/medium.png",
104 }
105 }
106-end
107\ No newline at end of file
108+end
109
110=== modified file 'scripting/infrastructure.lua'
111--- scripting/infrastructure.lua 2013-10-05 07:50:17 +0000
112+++ scripting/infrastructure.lua 2014-02-22 14:58:17 +0000
113@@ -44,7 +44,7 @@
114 moves[#moves+1] = m:sub(1,-2)
115 if(m:sub(-1) == '|') then
116 moves[#moves+1] = true -- Force the road
117- r = p:place_road(start, unpack(moves))
118+ r = p:place_road(start, table.unpack(moves))
119 start = r.end_flag
120 if create_carriers then
121 r:set_workers("carrier", 1)
122
123=== modified file 'scripting/lunit.lua'
124--- scripting/lunit.lua 2013-10-10 20:39:19 +0000
125+++ scripting/lunit.lua 2014-02-22 14:58:17 +0000
126@@ -28,60 +28,18 @@
127 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
128 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
129
130+
131+ This file was heavily modified for the internal use of Widelands.
132+
133 --]]--------------------------------------------------------------------------
134
135-
136-
137-
138------------------------
139--- Intialize package --
140------------------------
141-
142-local P = { }
143-lunit = P
144-
145--- Import
146-local type = type
147-local print = print
148-local ipairs = ipairs
149-local pairs = pairs
150-local string = string
151-local table = table
152-local pcall = pcall
153-local xpcall = xpcall
154-local debug = debug
155-local assert = assert
156-local error = error
157-local setmetatable = setmetatable
158-local rawset = rawset
159-local getfenv = getfenv
160-local setfenv = setfenv
161-local tostring = tostring
162-
163-
164--- Start package scope
165-setfenv(1, P)
166-
167-
168-
169-
170---------------------------------
171--- Private data and functions --
172---------------------------------
173-
174-local run_testcase
175-local do_assert, check_msg
176-local stats = { }
177-local testcases = { }
178-local stats_inc, tc_mt
179-
180-
181-
182+lunit = {}
183+lunit.testcases = {}
184+lunit.stats = {}
185
186 --------------------------
187 -- Type check functions --
188 --------------------------
189-
190 function is_nil(x)
191 return type(x) == "nil"
192 end
193@@ -114,229 +72,198 @@
194 return type(x) == "userdata"
195 end
196
197-
198-
199-
200 ----------------------
201 -- Assert functions --
202 ----------------------
203-
204 function assert_fail(msg)
205- stats_inc("assertions")
206- check_msg("assert_fail", msg)
207- do_assert(false, "failure", msg)
208+ lunit_stats_inc("assertions")
209+ lunit_check_msg("assert_fail", msg)
210+ lunit_do_assert(false, "failure", msg)
211 end
212
213-
214 function assert_true(actual, msg)
215- stats_inc("assertions")
216- check_msg("assert_true", msg)
217- do_assert(is_boolean(actual), "true expected but was a "..type(actual), msg)
218- do_assert(actual == true, "true expected but was false", msg)
219+ lunit_stats_inc("assertions")
220+ lunit_check_msg("assert_true", msg)
221+ lunit_do_assert(is_boolean(actual), "true expected but was a "..type(actual), msg)
222+ lunit_do_assert(actual == true, "true expected but was false", msg)
223 return actual
224 end
225
226-
227 function assert_false(actual, msg)
228- stats_inc("assertions")
229- check_msg("assert_false", msg)
230- do_assert(is_boolean(actual), "false expected but was a "..type(actual), msg)
231- do_assert(actual == false, "false expected but was true", msg)
232+ lunit_stats_inc("assertions")
233+ lunit_check_msg("assert_false", msg)
234+ lunit_do_assert(is_boolean(actual), "false expected but was a "..type(actual), msg)
235+ lunit_do_assert(actual == false, "false expected but was true", msg)
236 return actual
237 end
238
239-
240 function assert_equal(expected, actual, msg)
241- stats_inc("assertions")
242- check_msg("assert_equal", msg)
243- do_assert(expected == actual, "expected '"..tostring(expected).."' but was '"..tostring(actual).."'", msg)
244+ lunit_stats_inc("assertions")
245+ lunit_check_msg("assert_equal", msg)
246+ lunit_do_assert(expected == actual, "expected '"..tostring(expected).."' but was '"..tostring(actual).."'", msg)
247 return actual
248 end
249
250-
251 function assert_not_equal(unexpected, actual, msg)
252- stats_inc("assertions")
253- check_msg("assert_not_equal", msg)
254- do_assert(unexpected ~= actual, "'"..tostring(expected).."' not expected but was one", msg)
255+ lunit_stats_inc("assertions")
256+ lunit_check_msg("assert_not_equal", msg)
257+ lunit_do_assert(unexpected ~= actual, "'"..tostring(expected).."' not expected but was one", msg)
258 return actual
259 end
260
261-
262 function assert_match(pattern, actual, msg)
263- stats_inc("assertions")
264- check_msg("assert_match", msg)
265- do_assert(is_string(pattern), "assert_match expects the pattern as a string")
266- do_assert(is_string(actual), "expected a string to match pattern '"..pattern.."' but was a '"..type(actual).."'", msg)
267- do_assert(not not string.find(actual, pattern), "expected '"..actual.."' to match pattern '"..pattern.."' but doesn't", msg)
268+ lunit_stats_inc("assertions")
269+ lunit_check_msg("assert_match", msg)
270+ lunit_do_assert(is_string(pattern), "assert_match expects the pattern as a string")
271+ lunit_do_assert(is_string(actual), "expected a string to match pattern '"..pattern.."' but was a '"..type(actual).."'", msg)
272+ lunit_do_assert(not not string.find(actual, pattern), "expected '"..actual.."' to match pattern '"..pattern.."' but doesn't", msg)
273 return actual
274 end
275
276-
277 function assert_not_match(pattern, actual, msg)
278- stats_inc("assertions")
279- check_msg("assert_not_match", msg)
280- do_assert(is_string(actual), "expected a string to not match pattern '"..pattern.."' but was a '"..type(actual).."'", msg)
281- do_assert(string.find(actual, pattern) == nil, "expected '"..actual.."' to not match pattern '"..pattern.."' but it does", msg)
282+ lunit_stats_inc("assertions")
283+ lunit_check_msg("assert_not_match", msg)
284+ lunit_do_assert(is_string(actual), "expected a string to not match pattern '"..pattern.."' but was a '"..type(actual).."'", msg)
285+ lunit_do_assert(string.find(actual, pattern) == nil, "expected '"..actual.."' to not match pattern '"..pattern.."' but it does", msg)
286 return actual
287 end
288
289-
290 function assert_nil(actual, msg)
291- stats_inc("assertions")
292- check_msg("assert_nil", msg)
293- do_assert(is_nil(actual), "nil expected but was a "..type(actual), msg)
294+ lunit_stats_inc("assertions")
295+ lunit_check_msg("assert_nil", msg)
296+ lunit_do_assert(is_nil(actual), "nil expected but was a "..type(actual), msg)
297 return actual
298 end
299
300-
301 function assert_not_nil(actual, msg)
302- stats_inc("assertions")
303- check_msg("assert_not_nil", msg)
304- do_assert(not is_nil(actual), "nil not expected but was one", msg)
305+ lunit_stats_inc("assertions")
306+ lunit_check_msg("assert_not_nil", msg)
307+ lunit_do_assert(not is_nil(actual), "nil not expected but was one", msg)
308 return actual
309 end
310
311-
312 function assert_boolean(actual, msg)
313- stats_inc("assertions")
314- check_msg("assert_boolean", msg)
315- do_assert(is_boolean(actual), "boolean expected but was a "..type(actual), msg)
316+ lunit_stats_inc("assertions")
317+ lunit_check_msg("assert_boolean", msg)
318+ lunit_do_assert(is_boolean(actual), "boolean expected but was a "..type(actual), msg)
319 return actual
320 end
321
322-
323 function assert_not_boolean(actual, msg)
324- stats_inc("assertions")
325- check_msg("assert_not_boolean", msg)
326- do_assert(not is_boolean(actual), "boolean not expected but was one", msg)
327+ lunit_stats_inc("assertions")
328+ lunit_check_msg("assert_not_boolean", msg)
329+ lunit_do_assert(not is_boolean(actual), "boolean not expected but was one", msg)
330 return actual
331 end
332
333-
334 function assert_number(actual, msg)
335- stats_inc("assertions")
336- check_msg("assert_number", msg)
337- do_assert(is_number(actual), "number expected but was a "..type(actual), msg)
338+ lunit_stats_inc("assertions")
339+ lunit_check_msg("assert_number", msg)
340+ lunit_do_assert(is_number(actual), "number expected but was a "..type(actual), msg)
341 return actual
342 end
343
344-
345 function assert_not_number(actual, msg)
346- stats_inc("assertions")
347- check_msg("assert_not_number", msg)
348- do_assert(not is_number(actual), "number not expected but was one", msg)
349+ lunit_stats_inc("assertions")
350+ lunit_check_msg("assert_not_number", msg)
351+ lunit_do_assert(not is_number(actual), "number not expected but was one", msg)
352 return actual
353 end
354
355-
356 function assert_string(actual, msg)
357- stats_inc("assertions")
358- check_msg("assert_string", msg)
359- do_assert(is_string(actual), "string expected but was a "..type(actual), msg)
360+ lunit_stats_inc("assertions")
361+ lunit_check_msg("assert_string", msg)
362+ lunit_do_assert(is_string(actual), "string expected but was a "..type(actual), msg)
363 return actual
364 end
365
366-
367 function assert_not_string(actual, msg)
368- stats_inc("assertions")
369- check_msg("assert_not_string", msg)
370- do_assert(not is_string(actual), "string not expected but was one", msg)
371+ lunit_stats_inc("assertions")
372+ lunit_check_msg("assert_not_string", msg)
373+ lunit_do_assert(not is_string(actual), "string not expected but was one", msg)
374 return actual
375 end
376
377-
378 function assert_table(actual, msg)
379- stats_inc("assertions")
380- check_msg("assert_table", msg)
381- do_assert(is_table(actual), "table expected but was a "..type(actual), msg)
382+ lunit_stats_inc("assertions")
383+ lunit_check_msg("assert_table", msg)
384+ lunit_do_assert(is_table(actual), "table expected but was a "..type(actual), msg)
385 return actual
386 end
387
388-
389 function assert_not_table(actual, msg)
390- stats_inc("assertions")
391- check_msg("assert_not_table", msg)
392- do_assert(not is_table(actual), "table not expected but was one", msg)
393+ lunit_stats_inc("assertions")
394+ lunit_check_msg("assert_not_table", msg)
395+ lunit_do_assert(not is_table(actual), "table not expected but was one", msg)
396 return actual
397 end
398
399-
400 function assert_function(actual, msg)
401- stats_inc("assertions")
402- check_msg("assert_function", msg)
403- do_assert(is_function(actual), "function expected but was a "..type(actual), msg)
404+ lunit_stats_inc("assertions")
405+ lunit_check_msg("assert_function", msg)
406+ lunit_do_assert(is_function(actual), "function expected but was a "..type(actual), msg)
407 return actual
408 end
409
410-
411 function assert_not_function(actual, msg)
412- stats_inc("assertions")
413- check_msg("assert_not_function", msg)
414- do_assert(not is_function(actual), "function not expected but was one", msg)
415+ lunit_stats_inc("assertions")
416+ lunit_check_msg("assert_not_function", msg)
417+ lunit_do_assert(not is_function(actual), "function not expected but was one", msg)
418 return actual
419 end
420
421-
422 function assert_thread(actual, msg)
423- stats_inc("assertions")
424- check_msg("assert_thread", msg)
425- do_assert(is_thread(actual), "thread expected but was a "..type(actual), msg)
426+ lunit_stats_inc("assertions")
427+ lunit_check_msg("assert_thread", msg)
428+ lunit_do_assert(is_thread(actual), "thread expected but was a "..type(actual), msg)
429 return actual
430 end
431
432-
433 function assert_not_thread(actual, msg)
434- stats_inc("assertions")
435- check_msg("assert_not_thread", msg)
436- do_assert(not is_thread(actual), "thread not expected but was one", msg)
437+ lunit_stats_inc("assertions")
438+ lunit_check_msg("assert_not_thread", msg)
439+ lunit_do_assert(not is_thread(actual), "thread not expected but was one", msg)
440 return actual
441 end
442
443-
444 function assert_userdata(actual, msg)
445- stats_inc("assertions")
446- check_msg("assert_userdata", msg)
447- do_assert(is_userdata(actual), "userdata expected but was a "..type(actual), msg)
448+ lunit_stats_inc("assertions")
449+ lunit_check_msg("assert_userdata", msg)
450+ lunit_do_assert(is_userdata(actual), "userdata expected but was a "..type(actual), msg)
451 return actual
452 end
453
454-
455 function assert_not_userdata(actual, msg)
456- stats_inc("assertions")
457- check_msg("assert_not_userdata", msg)
458- do_assert(not is_userdata(actual), "userdata not expected but was one", msg)
459+ lunit_stats_inc("assertions")
460+ lunit_check_msg("assert_not_userdata", msg)
461+ lunit_do_assert(not is_userdata(actual), "userdata not expected but was one", msg)
462 return actual
463 end
464
465-
466 function assert_error(msg, func)
467- stats_inc("assertions")
468+ lunit_stats_inc("assertions")
469 if is_nil(func) then func, msg = msg, nil end
470- check_msg("assert_error", msg)
471- do_assert(is_function(func), "assert_error expects a function as the last argument but it was a "..type(func))
472+ lunit_check_msg("assert_error", msg)
473+ lunit_do_assert(is_function(func), "assert_error expects a function as the last argument but it was a "..type(func))
474 local ok, errmsg = pcall(func)
475- do_assert(ok == false, "error expected but no error occurred", msg)
476+ lunit_do_assert(ok == false, "error expected but no error occurred", msg)
477 end
478
479-
480 function assert_pass(msg, func)
481- stats_inc("assertions")
482+ lunit_stats_inc("assertions")
483 if is_nil(func) then func, msg = msg, nil end
484- check_msg("assert_pass", msg)
485- do_assert(is_function(func), "assert_pass expects a function as the last argument but it was a "..type(func))
486+ lunit_check_msg("assert_pass", msg)
487+ lunit_do_assert(is_function(func), "assert_pass expects a function as the last argument but it was a "..type(func))
488 local ok, errmsg = pcall(func)
489- if not ok then do_assert(ok == true, "no error expected but error was: "..errmsg, msg) end
490+ if not ok then lunit_do_assert(ok == true, "no error expected but error was: "..errmsg, msg) end
491 end
492
493
494-
495-
496 -----------------------------------------------------------
497 -- Assert implementation that assumes it was called from --
498 -- lunit code which was called directly from user code. --
499 -----------------------------------------------------------
500-
501-function do_assert(assertion, base_msg, user_msg)
502+function lunit_do_assert(assertion, base_msg, user_msg)
503 assert(is_boolean(assertion))
504 assert(is_string(base_msg))
505 assert(is_string(user_msg) or is_nil(user_msg))
506@@ -352,8 +279,7 @@
507 -------------------------------------------
508 -- Checks the msg argument in assert_xxx --
509 -------------------------------------------
510-
511-function check_msg(name, msg)
512+function lunit_check_msg(name, msg)
513 assert(is_string(name))
514 if not (is_nil(msg) or is_string(msg)) then
515 error("lunit."..name.."() expects the optional message as a string but it was a "..type(msg).."!" ,3)
516@@ -361,26 +287,23 @@
517 end
518
519
520-
521-
522 -------------------------------------
523 -- Creates a new TestCase 'Object' --
524 -------------------------------------
525-
526-function TestCase(name)
527- do_assert(is_string(name), "lunit.TestCase() needs a string as an argument")
528+function lunit.TestCase(name)
529+ lunit_do_assert(is_string(name), "lunit.TestCase() needs a string as an argument")
530 local tc = {
531 __lunit_name = name;
532 __lunit_setup = nil;
533 __lunit_tests = { };
534 __lunit_teardown = nil;
535 }
536- setmetatable(tc, tc_mt)
537- table.insert(testcases, tc)
538+ setmetatable(tc, lunit.tc_mt)
539+ table.insert(lunit.testcases, tc)
540 return tc
541 end
542
543-tc_mt = {
544+lunit.tc_mt = {
545 __newindex = function(tc, key, value)
546 rawset(tc, key, value)
547 if is_string(key) and is_function(value) then
548@@ -396,84 +319,69 @@
549 end
550 }
551
552-
553-
554 -----------------------------------------
555 -- Wrap Functions in a TestCase object --
556 -----------------------------------------
557-
558 function wrap(name, ...)
559 if is_function(name) then
560 table.insert(arg, 1, name)
561 name = "Anonymous Testcase"
562 end
563
564- local tc = TestCase(name)
565+ local tc = lunit.TestCase(name)
566 for index, test in ipairs(arg) do
567 tc["Test #"..tostring(index)] = test
568 end
569 return tc
570 end
571
572-
573-
574-
575-
576-
577 ----------------------------------
578 -- Runs the complete Test Suite --
579 ----------------------------------
580-
581-function run()
582-
583+function lunit.run()
584 ---------------------------
585 -- Initialize statistics --
586 ---------------------------
587-
588- stats.testcases = 0 -- Total number of Test Cases
589- stats.tests = 0 -- Total number of all Tests in all Test Cases
590- stats.run = 0 -- Number of Tests run
591- stats.notrun = 0 -- Number of Tests not run
592- stats.failed = 0 -- Number of Tests failed
593- stats.passed = 0 -- Number of Test passed
594- stats.assertions = 0 -- Number of all assertions made in all Test in all Test Cases
595+ lunit.stats.ntestcases = 0 -- Total number of Test Cases
596+ lunit.stats.tests = 0 -- Total number of all Tests in all Test Cases
597+ lunit.stats.run = 0 -- Number of Tests run
598+ lunit.stats.notrun = 0 -- Number of Tests not run
599+ lunit.stats.failed = 0 -- Number of Tests failed
600+ lunit.stats.passed = 0 -- Number of Test passed
601+ lunit.stats.assertions = 0 -- Number of all assertions made in all Test in all Test Cases
602
603 --------------------------------
604 -- Count Test Cases and Tests --
605 --------------------------------
606-
607- stats.testcases = table.getn(testcases)
608-
609- for _, tc in ipairs(testcases) do
610- stats_inc("tests" , table.getn(tc.__lunit_tests))
611+ lunit.stats.ntestcases = #lunit.testcases
612+
613+ for _, tc in ipairs(lunit.testcases) do
614+ lunit_stats_inc("tests" , #tc.__lunit_tests)
615 end
616
617 ------------------
618 -- Print Header --
619 ------------------
620-
621 print()
622- print("#### Test Suite with "..stats.tests.." Tests in "..stats.testcases.." Test Cases loaded.")
623+ print("#### Test Suite with "..lunit.stats.tests.." Tests in "..lunit.stats.ntestcases.." Test Cases loaded.")
624
625 ------------------------
626 -- Run all Test Cases --
627 ------------------------
628-
629- for _, tc in ipairs(testcases) do
630- run_testcase(tc)
631+ for _, tc in ipairs(lunit.testcases) do
632+ lunit_run_testcase(tc)
633 end
634
635 ------------------
636 -- Print Footer --
637 ------------------
638-
639 print()
640 print("#### Test Suite finished.")
641
642- local msg_assertions = stats.assertions.." Assertions checked. "
643- local msg_passed = stats.passed == stats.tests and "All Tests passed" or stats.passed.." Tests passed"
644- local msg_failed = stats.failed > 0 and ", "..stats.failed.." failed" or ""
645- local msg_run = stats.notrun > 0 and ", "..stats.notrun.." not run" or ""
646+ local msg_assertions = lunit.stats.assertions.." Assertions checked. "
647+ local msg_passed = lunit.stats.passed == lunit.stats.tests and "All Tests passed" or lunit.stats.passed.." Tests passed"
648+ local msg_failed = lunit.stats.failed > 0 and ", "..lunit.stats.failed.." failed" or ""
649+ local msg_run = lunit.stats.notrun > 0 and ", "..lunit.stats.notrun.." not run" or ""
650
651 print()
652 print(msg_assertions..msg_passed..msg_failed..msg_run.."!")
653@@ -481,23 +389,17 @@
654 -----------------
655 -- Return code --
656 -----------------
657-
658- if stats.passed == stats.tests then
659+ if lunit.stats.passed == lunit.stats.tests then
660 return 0
661 else
662 return 1
663 end
664 end
665
666-
667-
668-
669 -----------------------------
670 -- Runs a single Test Case --
671 -----------------------------
672-
673-function run_testcase(tc)
674-
675+function lunit_run_testcase(tc)
676 assert(is_table(tc))
677 assert(is_table(tc.__lunit_tests))
678 assert(is_string(tc.__lunit_name))
679@@ -507,7 +409,6 @@
680 --------------------------------------------
681 -- Protected call to a Test Case function --
682 --------------------------------------------
683-
684 local function call(errprefix, func)
685 assert(is_string(errprefix))
686 assert(is_function(func))
687@@ -522,7 +423,6 @@
688 ------------------------------------
689 -- Calls setup() on the Test Case --
690 ------------------------------------
691-
692 local function setup()
693 if tc.__lunit_setup then
694 return call("ERROR: setup() failed", tc.__lunit_setup)
695@@ -534,15 +434,14 @@
696 ------------------------------------------
697 -- Calls a single Test on the Test Case --
698 ------------------------------------------
699-
700 local function run(testname)
701 assert(is_string(testname))
702 assert(is_function(tc[testname]))
703 local ok = call("FAIL: "..testname, tc[testname])
704 if not ok then
705- stats_inc("failed")
706+ lunit_stats_inc("failed")
707 else
708- stats_inc("passed")
709+ lunit_stats_inc("passed")
710 end
711 return ok
712 end
713@@ -550,7 +449,6 @@
714 ---------------------------------------
715 -- Calls teardown() on the Test Case --
716 ---------------------------------------
717-
718 local function teardown()
719 if tc.__lunit_teardown then
720 call("WARNING: teardown() failed", tc.__lunit_teardown)
721@@ -560,117 +458,28 @@
722 ---------------------------------
723 -- Run all Tests on a TestCase --
724 ---------------------------------
725-
726 print()
727- print("#### Running '"..tc.__lunit_name.."' ("..table.getn(tc.__lunit_tests).." Tests)...")
728+ print("#### Running '"..tc.__lunit_name.."' ("..#tc.__lunit_tests.." Tests)...")
729
730 for _, testname in ipairs(tc.__lunit_tests) do
731 if setup() then
732 run(testname)
733- stats_inc("run")
734+ lunit_stats_inc("run")
735 teardown()
736 else
737 print("WARN: Skipping '"..testname.."'...")
738- stats_inc("notrun")
739- end
740- end
741-
742-end
743-
744-
745-
746-
747----------------------
748--- Import function --
749----------------------
750-
751-function import(name)
752-
753- do_assert(is_string(name), "lunit.import() expects a single string as argument")
754-
755- local user_env = getfenv(2)
756-
757- --------------------------------------------------
758- -- Installs a specific function in the user env --
759- --------------------------------------------------
760-
761- local function install(funcname)
762- user_env[funcname] = P[funcname]
763- end
764-
765-
766- ----------------------------------------------------------
767- -- Install functions matching a pattern in the user env --
768- ----------------------------------------------------------
769-
770- local function install_pattern(pattern)
771- for funcname, _ in pairs(P) do
772- if string.find(funcname, pattern) then
773- install(funcname)
774- end
775- end
776- end
777-
778- ------------------------------------------------------------
779- -- Installs assert() and all assert_xxx() in the user env --
780- ------------------------------------------------------------
781-
782- local function install_asserts()
783- install_pattern("^assert.*")
784- end
785-
786- -------------------------------------------
787- -- Installs all is_xxx() in the user env --
788- -------------------------------------------
789-
790- local function install_tests()
791- install_pattern("^is_.+")
792- end
793-
794- if name == "asserts" or name == "assertions" then
795- install_asserts()
796- elseif name == "tests" or name == "checks" then
797- install_tests()
798- elseif name == "all" then
799- install_asserts()
800- install_tests()
801- install("TestCase")
802- elseif string.find(name, "^assert.*") and P[name] then
803- install(name)
804- elseif string.find(name, "^is_.+") and P[name] then
805- install(name)
806- elseif name == "TestCase" then
807- install("TestCase")
808- else
809- error("luniit.import(): invalid function '"..name.."' to import", 2)
810- end
811-end
812-
813-
814-
815-
816---------------------------------------------------
817--- Installs a private environment on the caller --
818---------------------------------------------------
819-
820-function setprivfenv()
821- local new_env = { }
822- local new_env_mt = { __index = getfenv(2) }
823- setmetatable(new_env, new_env_mt)
824- setfenv(2, new_env)
825-end
826-
827-
828-
829+ lunit_stats_inc("notrun")
830+ end
831+ end
832+end
833
834 --------------------------------------------------
835 -- Increments a counter in the statistics table --
836 --------------------------------------------------
837-
838-function stats_inc(varname, value)
839- assert(is_table(stats))
840+function lunit_stats_inc(varname, value)
841+ assert(is_table(lunit.stats))
842 assert(is_string(varname))
843 assert(is_nil(value) or is_number(value))
844- if not stats[varname] then return end
845- stats[varname] = stats[varname] + (value or 1)
846+ if not lunit.stats[varname] then return end
847+ lunit.stats[varname] = lunit.stats[varname] + (value or 1)
848 end
849
850=== modified file 'src/CMakeLists.txt'
851--- src/CMakeLists.txt 2012-11-30 18:16:47 +0000
852+++ src/CMakeLists.txt 2014-02-22 14:58:17 +0000
853@@ -36,10 +36,10 @@
854 list (REMOVE_ITEM WL_SRCS_CC ${sourcefile})
855 list (REMOVE_ITEM WL_SRCS_H ${sourcefile})
856 endif (${sourcefile} MATCHES ".*/test/.*")
857- # exclude test sources from Codecheck, as well as the scripting/pdep sources from lua
858- if (NOT ${sourcefile} MATCHES ".*/scripting/pdep/.*" AND NOT ${sourcefile} MATCHES ".*/test/.*")
859+ # exclude test sources from Codecheck, as well as the scripting/eris sources from lua
860+ if (NOT ${sourcefile} MATCHES ".*/scripting/eris/.*" AND NOT ${sourcefile} MATCHES ".*/test/.*")
861 list (APPEND WL_CODECHECK_SRCS ${sourcefile})
862- endif (NOT ${sourcefile} MATCHES ".*/scripting/pdep/.*" AND NOT ${sourcefile} MATCHES ".*/test/.*")
863+ endif (NOT ${sourcefile} MATCHES ".*/scripting/eris/.*" AND NOT ${sourcefile} MATCHES ".*/test/.*")
864 endforeach (sourcefile ${WL_SRCS_CC} ${WL_SRCS_H})
865 foreach (sourcefile ${WL_SRCS_CPP})
866 list (APPEND WL_CODECHECK_SRCS ${sourcefile})
867@@ -91,29 +91,31 @@
868 # Tests need to link with SDL library without main
869 set(TEST_EXTRA_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/build_info.cc)
870 set(TEST_EXTRA_LIBS ${SDL_LIBRARY} )
871- list(REMOVE_ITEM TEST_EXTRA_LIBS ${SDLMAIN_LIBRARY})
872+ list(REMOVE_ITEM TEST_EXTRA_LIBS ${SDLMAIN_LIBRARY})
873
874 add_subdirectory(economy)
875 add_subdirectory(io)
876- add_subdirectory(scripting)
877 endif (WL_UNIT_TESTS)
878
879+add_subdirectory(scripting)
880+
881 if (NOT MINIZIP_FOUND)
882 add_subdirectory(minizip)
883 target_link_libraries(widelands_all wl_minizip)
884 endif (NOT MINIZIP_FOUND)
885
886+target_link_libraries(widelands_all eris)
887 target_link_libraries(widelands_all ${SDLIMAGE_LIBRARY})
888 target_link_libraries(widelands_all ${SDLMIXER_LIBRARY})
889 target_link_libraries(widelands_all ${SDLNET_LIBRARY})
890 target_link_libraries(widelands_all ${SDLTTF_LIBRARY})
891 target_link_libraries(widelands_all ${SDLGFX_LIBRARY})
892-target_link_libraries(widelands_all ${LUA_LIBRARY})
893 target_link_libraries(widelands_all ${PNG_LIBRARY})
894 target_link_libraries(widelands_all ${ZLIB_LIBRARY})
895 target_link_libraries(widelands_all ${OPENGL_gl_LIBRARY})
896 target_link_libraries(widelands_all ${GLEW_LIBRARY})
897 target_link_libraries(widelands_all ${Boost_SIGNALS_LIBRARY})
898+
899 if (MINIZIP_FOUND)
900 target_link_libraries(widelands_all ${MINIZIP_LIBRARY})
901 endif (MINIZIP_FOUND)
902
903=== modified file 'src/ai/defaultai.cc'
904--- src/ai/defaultai.cc 2013-07-26 20:19:36 +0000
905+++ src/ai/defaultai.cc 2014-02-22 14:58:17 +0000
906@@ -279,7 +279,7 @@
907 bld.get_ismine() ? BuildingObserver::MINE :
908 BuildingObserver::PRODUCTIONSITE;
909
910- container_iterate_const(Ware_Types, prod.inputs(), j)
911+ container_iterate_const(BillOfMaterials, prod.inputs(), j)
912 bo.inputs.push_back(j.current->first.value());
913
914 container_iterate_const
915
916=== renamed file 'src/logic/ware_types.h' => 'src/logic/bill_of_materials.h'
917--- src/logic/ware_types.h 2013-07-26 20:19:36 +0000
918+++ src/logic/bill_of_materials.h 2014-02-22 14:58:17 +0000
919@@ -25,12 +25,13 @@
920 #include "logic/widelands.h"
921
922 namespace Widelands {
923-typedef std::vector<std::pair<Ware_Index, uint32_t> > Ware_Types;
924+typedef std::pair<Ware_Index, uint32_t> WareAmount;
925+typedef std::vector<WareAmount> BillOfMaterials;
926
927 // range structure for iterating ware range with index
928 struct ware_range
929 {
930- ware_range(const Ware_Types & range) :
931+ ware_range(const BillOfMaterials & range) :
932 i(0), current(range.begin()), end(range.end()) {}
933 ware_range & operator++ () {
934 ++i; ++current; return *this;
935@@ -39,9 +40,9 @@
936 operator bool() const {return not empty();}
937
938 uint8_t i;
939- Ware_Types::const_iterator current;
940+ BillOfMaterials::const_iterator current;
941 private:
942- Ware_Types::const_iterator const end;
943+ BillOfMaterials::const_iterator const end;
944 };
945
946 }
947
948=== modified file 'src/logic/building.h'
949--- src/logic/building.h 2013-10-08 20:13:35 +0000
950+++ src/logic/building.h 2014-02-22 14:58:17 +0000
951@@ -28,10 +28,10 @@
952
953 #include "ai/ai_hints.h"
954 #include "io/filewrite.h"
955+#include "logic/bill_of_materials.h"
956 #include "logic/buildcost.h"
957 #include "logic/immovable.h"
958 #include "logic/soldier_counts.h"
959-#include "logic/ware_types.h"
960 #include "logic/wareworker.h"
961 #include "logic/widelands.h"
962 #include "workarea_info.h"
963
964=== modified file 'src/logic/cmd_luacoroutine.cc'
965--- src/logic/cmd_luacoroutine.cc 2013-07-26 20:19:36 +0000
966+++ src/logic/cmd_luacoroutine.cc 2014-02-22 14:58:17 +0000
967@@ -55,7 +55,7 @@
968 }
969 }
970
971-#define CMD_LUACOROUTINE_VERSION 1
972+#define CMD_LUACOROUTINE_VERSION 2
973 void Cmd_LuaCoroutine::Read
974 (FileRead & fr, Editor_Game_Base & egbase, Map_Map_Object_Loader & mol)
975 {
976
977=== modified file 'src/logic/editor_game_base.cc'
978--- src/logic/editor_game_base.cc 2013-08-06 10:40:54 +0000
979+++ src/logic/editor_game_base.cc 2014-02-22 14:58:17 +0000
980@@ -69,7 +69,7 @@
981 m_lasttrackserial (0)
982 {
983 if (not m_lua) // TODO SirVer: this is sooo ugly, I can't say
984- m_lua = create_LuaEditorInterface(this);
985+ m_lua = new LuaEditorInterface(this);
986
987 g_sound_handler.m_egbase = this;
988
989
990=== modified file 'src/logic/game.cc'
991--- src/logic/game.cc 2013-10-16 19:29:35 +0000
992+++ src/logic/game.cc 2014-02-22 14:58:17 +0000
993@@ -128,7 +128,7 @@
994
995
996 Game::Game() :
997- Editor_Game_Base(create_LuaGameInterface(this)),
998+ Editor_Game_Base(new LuaGameInterface(this)),
999 m_syncwrapper (*this, m_synchash),
1000 m_ctrl (0),
1001 m_writereplay (true),
1002
1003=== modified file 'src/logic/production_program.cc'
1004--- src/logic/production_program.cc 2013-08-14 10:39:59 +0000
1005+++ src/logic/production_program.cc 2014-02-22 14:58:17 +0000
1006@@ -66,7 +66,7 @@
1007 (char * & parameters,
1008 Ware_Type_Group & group,
1009 const Tribe_Descr & tribe,
1010- const Ware_Types & inputs)
1011+ const BillOfMaterials & inputs)
1012 {
1013 std::set<Ware_Index>::iterator last_insert_pos = group.first.end();
1014 uint8_t count = 1;
1015@@ -80,7 +80,7 @@
1016 char const terminator = *parameters;
1017 *parameters = '\0';
1018 Ware_Index const ware_index = tribe.safe_ware_index(ware);
1019- for (wl_const_range<Ware_Types> i(inputs);; ++i)
1020+ for (wl_const_range<BillOfMaterials> i(inputs);; ++i)
1021 if (i.empty())
1022 throw game_data_error
1023 (_
1024
1025=== modified file 'src/logic/production_program.h'
1026--- src/logic/production_program.h 2013-09-22 18:01:36 +0000
1027+++ src/logic/production_program.h 2014-02-22 14:58:17 +0000
1028@@ -31,9 +31,9 @@
1029 #include "container_iterate.h"
1030 #include "io/filewrite.h"
1031 #include "log.h"
1032+#include "logic/bill_of_materials.h"
1033 #include "logic/program_result.h"
1034 #include "logic/tattribute.h"
1035-#include "logic/ware_types.h"
1036 #include "logic/widelands.h"
1037
1038 struct Profile;
1039@@ -80,7 +80,7 @@
1040 (char * & parameters,
1041 Ware_Type_Group & group,
1042 const Tribe_Descr & tribe,
1043- const Ware_Types & inputs);
1044+ const BillOfMaterials & inputs);
1045
1046 /// Returns from the program.
1047 ///
1048
1049=== modified file 'src/logic/productionsite.cc'
1050--- src/logic/productionsite.cc 2013-09-23 18:47:02 +0000
1051+++ src/logic/productionsite.cc 2014-02-22 14:58:17 +0000
1052@@ -84,7 +84,7 @@
1053 while (Section::Value const * const val = s->get_next_val())
1054 try {
1055 if (Ware_Index const idx = tribe().ware_index(val->get_name())) {
1056- container_iterate_const(Ware_Types, inputs(), i)
1057+ container_iterate_const(BillOfMaterials, inputs(), i)
1058 if (i.current->first == idx)
1059 throw wexception("duplicated");
1060 int32_t const value = val->get_int();
1061@@ -104,7 +104,7 @@
1062 while (Section::Value const * const v = working_positions_s->get_next_val())
1063 try {
1064 if (Ware_Index const woi = tribe().worker_index(v->get_name())) {
1065- container_iterate_const(Ware_Types, working_positions(), i)
1066+ container_iterate_const(BillOfMaterials, working_positions(), i)
1067 if (i.current->first == woi)
1068 throw wexception("duplicated");
1069 m_working_positions.push_back(std::pair<Ware_Index, uint32_t>(woi, v->get_positive()));
1070@@ -365,8 +365,7 @@
1071 {
1072 Building::init(egbase);
1073
1074-
1075- const Ware_Types & inputs = descr().inputs();
1076+ const BillOfMaterials & inputs = descr().inputs();
1077 m_input_queues.resize(inputs.size());
1078 for (ware_range i(inputs); i; ++i)
1079 m_input_queues[i.i] =
1080@@ -377,7 +376,7 @@
1081
1082 // Request missing workers.
1083 Working_Position * wp = m_working_positions;
1084- container_iterate_const(Ware_Types, descr().working_positions(), i) {
1085+ container_iterate_const(BillOfMaterials, descr().working_positions(), i) {
1086 Ware_Index const worker_index = i.current->first;
1087 for (uint32_t j = i.current->second; j; --j, ++wp)
1088 if (Worker * const worker = wp->worker)
1089@@ -490,7 +489,7 @@
1090 molog("%s leaving\n", w.descname().c_str());
1091 Working_Position * wp = m_working_positions;
1092
1093- container_iterate_const(Ware_Types, descr().working_positions(), i) {
1094+ container_iterate_const(BillOfMaterials, descr().working_positions(), i) {
1095 Ware_Index const worker_index = i.current->first;
1096 for (uint32_t j = i.current->second; j; --j, ++wp) {
1097 Worker * const worker = wp->worker;
1098
1099=== modified file 'src/logic/productionsite.h'
1100--- src/logic/productionsite.h 2013-09-23 18:47:02 +0000
1101+++ src/logic/productionsite.h 2014-02-22 14:58:17 +0000
1102@@ -26,10 +26,10 @@
1103 #include <string>
1104 #include <vector>
1105
1106+#include "logic/bill_of_materials.h"
1107 #include "logic/building.h"
1108 #include "logic/production_program.h"
1109 #include "logic/program_result.h"
1110-#include "logic/ware_types.h"
1111
1112 namespace Widelands {
1113
1114@@ -64,11 +64,11 @@
1115
1116 uint32_t nr_working_positions() const {
1117 uint32_t result = 0;
1118- container_iterate_const(Ware_Types, working_positions(), i)
1119+ container_iterate_const(BillOfMaterials, working_positions(), i)
1120 result += i.current->second;
1121 return result;
1122 }
1123- const Ware_Types & working_positions() const {
1124+ const BillOfMaterials & working_positions() const {
1125 return m_working_positions;
1126 }
1127 bool is_output_ware_type (const Ware_Index& i) const {
1128@@ -77,7 +77,7 @@
1129 bool is_output_worker_type(const Ware_Index& i) const {
1130 return m_output_worker_types.count(i);
1131 }
1132- const Ware_Types & inputs() const {return m_inputs;}
1133+ const BillOfMaterials & inputs() const {return m_inputs;}
1134 typedef std::set<Ware_Index> Output;
1135 const Output & output_ware_types () const {return m_output_ware_types;}
1136 const Output & output_worker_types() const {return m_output_worker_types;}
1137@@ -89,8 +89,8 @@
1138 const std::vector<std::string> & compatibility_working_positions(const std::string & workername) const;
1139
1140 private:
1141- Ware_Types m_working_positions;
1142- Ware_Types m_inputs;
1143+ BillOfMaterials m_working_positions;
1144+ BillOfMaterials m_inputs;
1145 Output m_output_ware_types;
1146 Output m_output_worker_types;
1147 Programs m_programs;
1148
1149=== modified file 'src/logic/tribe.cc'
1150--- src/logic/tribe.cc 2013-10-08 20:13:35 +0000
1151+++ src/logic/tribe.cc 2014-02-22 14:58:17 +0000
1152@@ -298,7 +298,7 @@
1153 }
1154
1155 // Read initializations -- all scripts are initializations currently
1156- ScriptContainer & scripts =
1157+ const ScriptContainer& scripts =
1158 egbase.lua().get_scripts_for("tribe_" + tribename);
1159 container_iterate_const(ScriptContainer, scripts, s) {
1160 std::unique_ptr<LuaTable> t =
1161@@ -376,7 +376,7 @@
1162 buf += name;
1163 buf += "/conf";
1164
1165- LuaInterface * lua = create_LuaInterface();
1166+ LuaInterface lua;
1167 FileRead f;
1168 if (f.TryOpen(*g_fs, buf.c_str())) {
1169 if (info)
1170@@ -389,30 +389,25 @@
1171 std::string path = "tribes/" + name + "/scripting";
1172 if (g_fs->IsDirectory(path)) {
1173 std::unique_ptr<FileSystem> sub_fs(g_fs->MakeSubFileSystem(path));
1174- lua->register_scripts(*sub_fs, "tribe_" + name, "");
1175+ lua.register_scripts(*sub_fs, "tribe_" + name, "");
1176 }
1177
1178- ScriptContainer & scripts = lua->get_scripts_for("tribe_" + name);
1179+ const ScriptContainer& scripts = lua.get_scripts_for("tribe_" + name);
1180 container_iterate_const(ScriptContainer, scripts, s) {
1181 std::unique_ptr<LuaTable> t =
1182- lua->run_script("tribe_" + name, s->first);
1183+ lua.run_script("tribe_" + name, s->first);
1184
1185 info->initializations.push_back
1186 (TribeBasicInfo::Initialization
1187 (s->first, t->get_string("name")));
1188 }
1189 } catch (const _wexception & e) {
1190- delete lua;
1191 throw game_data_error
1192 ("reading basic info for tribe \"%s\": %s",
1193 name.c_str(), e.what());
1194 }
1195-
1196- delete lua;
1197 return true;
1198 }
1199-
1200- delete lua;
1201 return false;
1202 }
1203
1204
1205=== modified file 'src/map_io/widelands_map_buildingdata_data_packet.cc'
1206--- src/map_io/widelands_map_buildingdata_data_packet.cc 2013-10-08 20:13:35 +0000
1207+++ src/map_io/widelands_map_buildingdata_data_packet.cc 2014-02-22 14:58:17 +0000
1208@@ -876,7 +876,7 @@
1209 ProductionSite::Working_Position & wp_begin =
1210 *productionsite.m_working_positions;
1211 const ProductionSite_Descr & pr_descr = productionsite.descr();
1212- const Ware_Types & working_positions = pr_descr.working_positions();
1213+ const BillOfMaterials & working_positions = pr_descr.working_positions();
1214
1215 uint16_t nr_worker_requests = fr.Unsigned16();
1216 for (uint16_t i = nr_worker_requests; i; --i) {
1217@@ -892,7 +892,7 @@
1218 // Find a working position that matches this request.
1219 ProductionSite::Working_Position * wp = &wp_begin;
1220 for
1221- (wl_const_range<Ware_Types>
1222+ (wl_const_range<BillOfMaterials>
1223 j(working_positions);;
1224 ++j)
1225 {
1226@@ -973,7 +973,7 @@
1227 const Worker_Descr & worker_descr = worker->descr();
1228 ProductionSite::Working_Position * wp = &wp_begin;
1229 for
1230- (wl_const_range<Ware_Types> j(working_positions);;
1231+ (wl_const_range<BillOfMaterials> j(working_positions);;
1232 ++j)
1233 {
1234 if (j.empty())
1235
1236=== modified file 'src/map_io/widelands_map_scripting_data_packet.cc'
1237--- src/map_io/widelands_map_scripting_data_packet.cc 2013-10-08 20:13:35 +0000
1238+++ src/map_io/widelands_map_scripting_data_packet.cc 2014-02-22 14:58:17 +0000
1239@@ -30,6 +30,9 @@
1240
1241 namespace Widelands {
1242
1243+namespace {
1244+const int SCRIPTING_DATA_PACKET_VERSION=1;
1245+} // namespace
1246 /*
1247 * ========================================================================
1248 * PUBLIC IMPLEMENTATION
1249@@ -51,6 +54,12 @@
1250 upcast(Game, g, &egbase);
1251 Widelands::FileRead fr;
1252 if (g and fr.TryOpen(fs, "scripting/globals.dump")) {
1253+ const uint32_t sentinel = fr.Unsigned32();
1254+ const uint32_t packet_version = fr.Unsigned32();
1255+ if (sentinel != 0xDEADBEEF && packet_version != SCRIPTING_DATA_PACKET_VERSION) {
1256+ throw game_data_error(
1257+ "This savegame is from an older version of Widelands and can not be loaded any more.");
1258+ }
1259 upcast(LuaGameInterface, lgi, &g->lua());
1260 lgi->read_global_env(fr, mol, fr.Unsigned32());
1261 }
1262@@ -60,11 +69,11 @@
1263 void Map_Scripting_Data_Packet::Write
1264 (FileSystem & fs, Editor_Game_Base & egbase, Map_Map_Object_Saver & mos)
1265 {
1266- ScriptContainer & p = egbase.lua().get_scripts_for("map");
1267+ const ScriptContainer& p = egbase.lua().get_scripts_for("map");
1268
1269 fs.EnsureDirectoryExists("scripting");
1270
1271- for (ScriptContainer::iterator i = p.begin(); i != p.end(); ++i) {
1272+ for (ScriptContainer::const_iterator i = p.begin(); i != p.end(); ++i) {
1273 std::string fname = "scripting/";
1274 fname += i->first;
1275 fname += ".lua";
1276@@ -74,9 +83,10 @@
1277
1278 // Dump the global environment if this is a game and not in the editor
1279 if (upcast(Game, g, &egbase)) {
1280-
1281 Widelands::FileWrite fw;
1282- Widelands::FileWrite::Pos pos = fw.GetPos();
1283+ fw.Unsigned32(0xDEADBEEF); // Sentinel, because there was no packet version.
1284+ fw.Unsigned32(SCRIPTING_DATA_PACKET_VERSION);
1285+ const Widelands::FileWrite::Pos pos = fw.GetPos();
1286 fw.Unsigned32(0); // N bytes written, follows below
1287
1288 upcast(LuaGameInterface, lgi, &g->lua());
1289
1290=== modified file 'src/network/netclient.cc'
1291--- src/network/netclient.cc 2013-09-20 17:47:29 +0000
1292+++ src/network/netclient.cc 2014-02-22 14:58:17 +0000
1293@@ -119,15 +119,14 @@
1294 file = 0;
1295
1296 // Temporarily register win condition scripts to get the default
1297- LuaInterface * lua = create_LuaInterface();
1298- lua->register_scripts(*g_fs, "win_conditions", "scripting/win_conditions");
1299- ScriptContainer sc = lua->get_scripts_for("win_conditions");
1300+ LuaInterface lua;
1301+ lua.register_scripts(*g_fs, "win_conditions", "scripting/win_conditions");
1302+ ScriptContainer sc = lua.get_scripts_for("win_conditions");
1303 std::vector<std::string> win_conditions;
1304 container_iterate_const(ScriptContainer, sc, wc)
1305 win_conditions.push_back(wc->first);
1306 assert(win_conditions.size());
1307 d->settings.win_condition = win_conditions[0];
1308- delete lua;
1309 }
1310
1311 NetClient::~NetClient ()
1312@@ -906,16 +905,16 @@
1313 info.name = packet.String();
1314
1315 // Get initializations (we have to do this locally, for translations)
1316- LuaInterface * lua = create_LuaInterface();
1317+ LuaInterface lua;
1318 std::string path = "tribes/" + info.name;
1319 if (g_fs->IsDirectory(path)) {
1320 std::unique_ptr<FileSystem> sub_fs(g_fs->MakeSubFileSystem(path));
1321- lua->register_scripts(*sub_fs, "tribe_" + info.name, "scripting");
1322+ lua.register_scripts(*sub_fs, "tribe_" + info.name, "scripting");
1323 }
1324
1325 for (uint8_t j = packet.Unsigned8(); j; --j) {
1326 std::string const name = packet.String();
1327- std::unique_ptr<LuaTable> t = lua->run_script
1328+ std::unique_ptr<LuaTable> t = lua.run_script
1329 ("tribe_" + info.name, name);
1330 info.initializations.push_back
1331 (TribeBasicInfo::Initialization(name, t->get_string("name")));
1332
1333=== modified file 'src/network/nethost.cc'
1334--- src/network/nethost.cc 2013-10-08 20:13:35 +0000
1335+++ src/network/nethost.cc 2014-02-22 14:58:17 +0000
1336@@ -293,7 +293,7 @@
1337 if (m_win_conditions.size() < 1) {
1338 // Register win condition scripts
1339 if (!m_lua)
1340- m_lua = create_LuaInterface();
1341+ m_lua = new LuaInterface();
1342 m_lua->register_scripts(*g_fs, "win_conditions", "scripting/win_conditions");
1343
1344 ScriptContainer sc = m_lua->get_scripts_for("win_conditions");
1345
1346=== modified file 'src/scripting/CMakeLists.txt'
1347--- src/scripting/CMakeLists.txt 2010-11-01 03:43:56 +0000
1348+++ src/scripting/CMakeLists.txt 2014-02-22 14:58:17 +0000
1349@@ -1,4 +1,77 @@
1350+file(GLOB_RECURSE ERIS_SRCS_C *.c)
1351+file(GLOB_RECURSE ERIS_SRCS_H *.h)
1352+
1353+SET(ERIS_SOURCES
1354+ eris/eris.c
1355+ eris/lapi.c
1356+ eris/lauxlib.c
1357+ eris/lbaselib.c
1358+ eris/lbitlib.c
1359+ eris/lcode.c
1360+ eris/lcorolib.c
1361+ eris/lctype.c
1362+ eris/ldblib.c
1363+ eris/ldebug.c
1364+ eris/ldo.c
1365+ eris/ldump.c
1366+ eris/lfunc.c
1367+ eris/lgc.c
1368+ eris/linit.c
1369+ eris/liolib.c
1370+ eris/llex.c
1371+ eris/lmathlib.c
1372+ eris/lmem.c
1373+ eris/loadlib.c
1374+ eris/lobject.c
1375+ eris/lopcodes.c
1376+ eris/loslib.c
1377+ eris/lparser.c
1378+ eris/lstate.c
1379+ eris/lstring.c
1380+ eris/lstrlib.c
1381+ eris/ltable.c
1382+ eris/ltablib.c
1383+ eris/ltm.c
1384+ eris/lua.c
1385+ eris/luac.c
1386+ eris/lundump.c
1387+ eris/lvm.c
1388+ eris/lzio.c
1389+)
1390+
1391+SET(ERIS_HEADERS
1392+ eris/eris.h
1393+ eris/lapi.h
1394+ eris/lauxlib.h
1395+ eris/lcode.h
1396+ eris/lctype.h
1397+ eris/ldebug.h
1398+ eris/ldo.h
1399+ eris/lfunc.h
1400+ eris/lgc.h
1401+ eris/llex.h
1402+ eris/llimits.h
1403+ eris/lmem.h
1404+ eris/lobject.h
1405+ eris/lopcodes.h
1406+ eris/lparser.h
1407+ eris/lstate.h
1408+ eris/lstring.h
1409+ eris/ltable.h
1410+ eris/ltm.h
1411+ eris/lua.h
1412+ eris/luaconf.h
1413+ eris/lualib.h
1414+ eris/lundump.h
1415+ eris/lvm.h
1416+ eris/lzio.h
1417+)
1418+
1419+add_library(eris STATIC
1420+ ${ERIS_SOURCES}
1421+ ${ERIS_HEADERS}
1422+)
1423+
1424 if (WL_UNIT_TESTS)
1425 add_subdirectory(test)
1426 endif (WL_UNIT_TESTS)
1427-
1428
1429=== modified file 'src/scripting/c_utils.cc'
1430--- src/scripting/c_utils.cc 2013-07-26 20:19:36 +0000
1431+++ src/scripting/c_utils.cc 2014-02-22 14:58:17 +0000
1432@@ -107,9 +107,8 @@
1433 vsnprintf(buffer, sizeof(buffer), fmt, va);
1434 va_end(va);
1435
1436-
1437 // Also create a traceback
1438- lua_getfield(L, LUA_GLOBALSINDEX, "debug");
1439+ lua_getglobal(L, "debug");
1440 if (!lua_istable(L, -1)) {
1441 lua_pop(L, 1);
1442 return 1;
1443@@ -127,4 +126,3 @@
1444
1445 return 0;
1446 }
1447-
1448
1449=== modified file 'src/scripting/c_utils.h'
1450--- src/scripting/c_utils.h 2013-07-26 20:19:36 +0000
1451+++ src/scripting/c_utils.h 2014-02-22 14:58:17 +0000
1452@@ -20,12 +20,12 @@
1453 #ifndef C_UTILS_H
1454 #define C_UTILS_H
1455
1456-#include <lua.hpp>
1457
1458-#include "scripting/factory.h"
1459 #include "logic/game.h"
1460 #include "map_io/widelands_map_map_object_loader.h"
1461 #include "map_io/widelands_map_map_object_saver.h"
1462+#include "scripting/eris/lua.hpp"
1463+#include "scripting/factory.h"
1464
1465 Factory & get_factory(lua_State *);
1466 Widelands::Game & get_game(lua_State *);
1467
1468=== removed file 'src/scripting/coroutine_impl.cc'
1469--- src/scripting/coroutine_impl.cc 2013-07-26 20:19:36 +0000
1470+++ src/scripting/coroutine_impl.cc 1970-01-01 00:00:00 +0000
1471@@ -1,144 +0,0 @@
1472-/*
1473- * Copyright (C) 2006-2010 by the Widelands Development Team
1474- *
1475- * This program is free software; you can redistribute it and/or
1476- * modify it under the terms of the GNU General Public License
1477- * as published by the Free Software Foundation; either version 2
1478- * of the License, or (at your option) any later version.
1479- *
1480- * This program is distributed in the hope that it will be useful,
1481- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1482- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1483- * GNU General Public License for more details.
1484- *
1485- * You should have received a copy of the GNU General Public License
1486- * along with this program; if not, write to the Free Software
1487- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1488- *
1489- */
1490-
1491-#include "scripting/coroutine_impl.h"
1492-
1493-#include <csetjmp>
1494-
1495-#include <boost/lexical_cast.hpp>
1496-
1497-#include "log.h"
1498-#include "logic/player.h"
1499-#include "scripting/c_utils.h"
1500-#include "scripting/lua_game.h"
1501-#include "scripting/lua_map.h"
1502-#include "scripting/persistence.h"
1503-
1504-LuaCoroutine_Impl::LuaCoroutine_Impl(lua_State * ms)
1505- : m_L(ms), m_idx(LUA_REFNIL), m_nargs(0)
1506-{
1507- m_reference();
1508-}
1509-
1510-LuaCoroutine_Impl::~LuaCoroutine_Impl()
1511-{
1512- m_unreference();
1513-}
1514-
1515-int LuaCoroutine_Impl::resume(uint32_t * sleeptime)
1516-{
1517- int rv = lua_resume(m_L, m_nargs);
1518- m_nargs = 0;
1519- int n = lua_gettop(m_L);
1520-
1521- uint32_t sleep_for = 0;
1522- if (n == 1) {
1523- sleep_for = luaL_checkint32(m_L, -1);
1524- lua_pop(m_L, 1);
1525- }
1526-
1527- if (sleeptime)
1528- *sleeptime = sleep_for;
1529-
1530- if (rv != 0 && rv != YIELDED) {
1531- // lua_error() never returns. prints error and exit program imediately
1532- //return lua_error(m_L);
1533- const char * err = lua_tostring(m_L, -1);
1534- throw LuaError(err);
1535- }
1536-
1537- return rv;
1538-}
1539-
1540-/*
1541- * Push an argument that will be passed to the coroutine the next time it is
1542- * resumed
1543- */
1544-void LuaCoroutine_Impl::push_arg(const Widelands::Player * plr) {
1545- to_lua<LuaGame::L_Player>(m_L, new LuaGame::L_Player(plr->player_number()));
1546- m_nargs++;
1547-}
1548-void LuaCoroutine_Impl::push_arg(const Widelands::Coords & coords) {
1549- to_lua<LuaMap::L_Field>(m_L, new LuaMap::L_Field(coords));
1550- ++m_nargs;
1551-}
1552-
1553-#define COROUTINE_DATA_PACKET_VERSION 1
1554-uint32_t LuaCoroutine_Impl::write
1555- (lua_State * parent, Widelands::FileWrite & fw,
1556- Widelands::Map_Map_Object_Saver & mos)
1557-{
1558- fw.Unsigned8(COROUTINE_DATA_PACKET_VERSION);
1559-
1560- // The current numbers of arguments on the stack
1561- fw.Unsigned32(m_nargs);
1562-
1563- // Empty table + object to persist on the stack Stack
1564- lua_newtable(parent);
1565- lua_pushthread(m_L);
1566- lua_xmove (m_L, parent, 1);
1567-
1568- return persist_object(parent, fw, mos);
1569-}
1570-
1571-void LuaCoroutine_Impl::read
1572- (lua_State * parent, Widelands::FileRead & fr,
1573- Widelands::Map_Map_Object_Loader & mol, uint32_t size)
1574-{
1575- uint8_t version = fr.Unsigned8();
1576-
1577- if (version != COROUTINE_DATA_PACKET_VERSION)
1578- throw wexception("Unknown data packet version: %i\n", version);
1579-
1580- // The current numbers of arguments on the stack
1581- m_nargs = fr.Unsigned32();
1582-
1583- // Empty table + object to persist on the stack Stack
1584- unpersist_object(parent, fr, mol, size);
1585-
1586- m_L = luaL_checkthread(parent, -1);
1587- lua_pop(parent, 1);
1588-
1589- // Cache a lua reference to this object so that it does not
1590- // get garbage collected
1591- lua_pushthread(m_L);
1592- m_idx = luaL_ref(m_L, LUA_REGISTRYINDEX);
1593-
1594-}
1595-
1596-/*
1597- * ========================================================================
1598- * PRIVATE FUNCTIONS
1599- * ========================================================================
1600- */
1601-/**
1602- * Cache a lua reference to this object so that it does not get garbage
1603- * collected
1604- */
1605-void LuaCoroutine_Impl::m_reference() {
1606- if (m_L) {
1607- lua_pushthread(m_L);
1608- m_idx = luaL_ref(m_L, LUA_REGISTRYINDEX);
1609- }
1610-}
1611-void LuaCoroutine_Impl::m_unreference() {
1612- luaL_unref(m_L, LUA_REGISTRYINDEX, m_idx);
1613-}
1614-
1615-
1616
1617=== removed file 'src/scripting/coroutine_impl.h'
1618--- src/scripting/coroutine_impl.h 2013-09-22 18:01:36 +0000
1619+++ src/scripting/coroutine_impl.h 1970-01-01 00:00:00 +0000
1620@@ -1,63 +0,0 @@
1621-/*
1622- * Copyright (C) 2006-2010 by the Widelands Development Team
1623- *
1624- * This program is free software; you can redistribute it and/or
1625- * modify it under the terms of the GNU General Public License
1626- * as published by the Free Software Foundation; either version 2
1627- * of the License, or (at your option) any later version.
1628- *
1629- * This program is distributed in the hope that it will be useful,
1630- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1631- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1632- * GNU General Public License for more details.
1633- *
1634- * You should have received a copy of the GNU General Public License
1635- * along with this program; if not, write to the Free Software
1636- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1637- *
1638- */
1639-
1640-#ifndef COROUTINE_IMPL_H
1641-#define COROUTINE_IMPL_H
1642-
1643-#include "scripting/scripting.h"
1644-
1645-namespace Widelands {
1646- class Player;
1647-}
1648-
1649-/*
1650-============================================
1651- Lua Coroutine
1652-============================================
1653-*/
1654-class LuaCoroutine_Impl : public LuaCoroutine {
1655-public:
1656- LuaCoroutine_Impl(lua_State * L);
1657- virtual ~LuaCoroutine_Impl();
1658-
1659- virtual int get_status() {return lua_status(m_L);}
1660- virtual int resume(uint32_t * sleeptime = 0);
1661-
1662- virtual uint32_t write
1663- (lua_State *, Widelands::FileWrite &,
1664- Widelands::Map_Map_Object_Saver &);
1665- virtual void read
1666- (lua_State *, Widelands::FileRead &,
1667- Widelands::Map_Map_Object_Loader &, uint32_t);
1668-
1669- virtual void push_arg(const Widelands::Player *);
1670- virtual void push_arg(const Widelands::Coords &);
1671-
1672-private:
1673- void m_reference();
1674- void m_unreference();
1675-
1676-private:
1677- lua_State * m_L;
1678- uint32_t m_idx;
1679- uint32_t m_nargs;
1680-};
1681-
1682-
1683-#endif /* end of include guard: COROUTINE_IMPL_H */
1684
1685=== added directory 'src/scripting/eris'
1686=== added file 'src/scripting/eris.h'
1687--- src/scripting/eris.h 1970-01-01 00:00:00 +0000
1688+++ src/scripting/eris.h 2014-02-22 14:58:17 +0000
1689@@ -0,0 +1,29 @@
1690+/*
1691+ * Copyright (C) 2006-2013 by the Widelands Development Team
1692+ *
1693+ * This program is free software; you can redistribute it and/or
1694+ * modify it under the terms of the GNU General Public License
1695+ * as published by the Free Software Foundation; either version 2
1696+ * of the License, or (at your option) any later version.
1697+ *
1698+ * This program is distributed in the hope that it will be useful,
1699+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1700+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1701+ * GNU General Public License for more details.
1702+ *
1703+ * You should have received a copy of the GNU General Public License
1704+ * along with this program; if not, write to the Free Software
1705+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
1706+ *
1707+ */
1708+
1709+#ifndef WL_ERIS_H
1710+#define WL_ERIS_H
1711+
1712+// We need eris in a cpp context. Include it as such.
1713+extern "C" {
1714+#include "scripting/eris/eris.h"
1715+}
1716+
1717+
1718+#endif /* end of include guard: WL_ERIS_H */
1719
1720=== renamed file 'src/scripting/README.pluto' => 'src/scripting/eris/README.eris'
1721--- src/scripting/README.pluto 2010-01-30 14:54:27 +0000
1722+++ src/scripting/eris/README.eris 2014-02-22 14:58:17 +0000
1723@@ -1,11 +1,11 @@
1724-Our scripting support contains the stripped down essentials of pluto[1]. You
1725-find the complete source and README of pluto on the github repo from one of
1726-it's authors Rob Hoelz at [2]. Our version of pluto has been heavily modified
1727-to create system agnostic files and to work with the widelands file streaming.
1728-The Widelands Team wishes to expresses total and complete gratitude to the
1729-authors of Pluto for making it public domain.
1730-
1731-[1] http://luaforge.net/projects/pluto/
1732-[2] http://github.com/hoelzro/pluto
1733+This directory contains a verbatim copy of Eris by Florian Nücke.
1734+
1735+URL: https://github.com/fnuecke/eris
1736+VERSION: 2e39ecc7dcb73120dde775929227fa661fbc6bc0
1737+
1738+We use this for heavy persistence and it also brings with it the Lua version
1739+that we use in Widelands. The Widelands Team wishes to expresses total and
1740+complete gratitude to the authors of Eris for making it available under the MIT
1741+License.
1742
1743 -- SirVer, in behalf of the Widelands Team
1744
1745=== added file 'src/scripting/eris/eris.c'
1746--- src/scripting/eris/eris.c 1970-01-01 00:00:00 +0000
1747+++ src/scripting/eris/eris.c 2014-02-22 14:58:17 +0000
1748@@ -0,0 +1,2658 @@
1749+/*
1750+Eris - Heavy-duty persistence for Lua 5.2.2 - Based on Pluto
1751+Copyright (c) 2013 by Florian Nuecke.
1752+
1753+Permission is hereby granted, free of charge, to any person obtaining a copy
1754+of this software and associated documentation files (the "Software"), to deal
1755+in the Software without restriction, including without limitation the rights
1756+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1757+copies of the Software, and to permit persons to whom the Software is
1758+furnished to do so, subject to the following conditions:
1759+
1760+The above copyright notice and this permission notice shall be included in
1761+all copies or substantial portions of the Software.
1762+
1763+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1764+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1765+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1766+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1767+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1768+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1769+THE SOFTWARE.
1770+*/
1771+
1772+/* Standard library headers. */
1773+#include <assert.h>
1774+#include <math.h>
1775+#include <stdint.h>
1776+#include <stdio.h>
1777+#include <string.h>
1778+
1779+/* Not using stdbool because Visual Studio lives in the past... */
1780+typedef int bool;
1781+#define false 0
1782+#define true 1
1783+
1784+/* Mark us as part of the Lua core to get access to what we need. */
1785+#define LUA_CORE
1786+
1787+/* Public Lua headers. */
1788+#include "lua.h"
1789+#include "lauxlib.h"
1790+#include "lualib.h"
1791+
1792+/* Internal Lua headers. */
1793+#include "ldebug.h"
1794+#include "ldo.h"
1795+#include "lfunc.h"
1796+#include "lobject.h"
1797+#include "lstate.h"
1798+#include "lstring.h"
1799+#include "lzio.h"
1800+
1801+/* Eris header. */
1802+#include "eris.h"
1803+
1804+/*
1805+** {===========================================================================
1806+** Default settings.
1807+** ============================================================================
1808+*/
1809+
1810+/* Note that these are the default settings. They can be changed either from C
1811+ * by calling eris_g|set_setting() or from Lua using eris.settings(). */
1812+
1813+/* The metatable key we use to allow customized persistence for tables and
1814+ * userdata. */
1815+static const char *const kPersistKey = "__persist";
1816+
1817+/* Whether to pass the IO object (reader/writer) to the special function
1818+ * defined in the metafield or not. This is disabled per default because it
1819+ * mey allow Lua scripts to do more than they should. Enable this as needed. */
1820+static const bool kPassIOToPersist = false;
1821+
1822+/* Whether to persist debug information such as line numbers and upvalue and
1823+ * local variable names. */
1824+static const bool kWriteDebugInformation = true;
1825+
1826+/* Generate a human readable "path" that is shown together with error messages
1827+ * to indicate where in the object the error occurred. For example:
1828+ * eris.persist({false, bad = setmetatable({}, {__persist = false})})
1829+ * Will produce: main:1: attempt to persist forbidden table (root.bad)
1830+ * This can be used for debugging, but is disabled per default due to the
1831+ * processing and memory overhead this introduces. */
1832+static const bool kGeneratePath = false;
1833+
1834+/* The maximum object complexity. This is the number of allowed recursions when
1835+ * persisting or unpersisting an object, for example for nested tables. This is
1836+ * used to avoid segfaults when writing or reading user data. */
1837+static const lua_Unsigned kMaxComplexity = 10000;
1838+
1839+/*
1840+** ============================================================================
1841+** Lua internals interfacing.
1842+** ============================================================================
1843+*/
1844+
1845+/* Lua internals we use. We define these as macros to make it easier to swap
1846+ * them out, should the need ever arise. For example, the later Pluto versions
1847+ * copied these function to own files (presumably to allow building it as an
1848+ * extra shared library). These should be all functions we use that are not
1849+ * declared in lua.h or lauxlib.h. If there are some still directly in the code
1850+ * they were missed and should be replaced with a macro added here instead. */
1851+/* I'm quite sure we won't ever want to do this, because Eris needs a slightly
1852+ * patched Lua version to be able to persist some of the library functions,
1853+ * anyway: it needs to put the continuation C functions in the perms table. */
1854+/* ldebug.h */
1855+#define eris_ci_func ci_func
1856+/* ldo.h */
1857+#define eris_incr_top incr_top
1858+#define eris_savestack savestack
1859+#define eris_restorestack restorestack
1860+#define eris_reallocstack luaD_reallocstack
1861+/* lfunc.h */
1862+#define eris_newproto luaF_newproto
1863+#define eris_newLclosure luaF_newLclosure
1864+#define eris_newupval luaF_newupval
1865+#define eris_findupval luaF_findupval
1866+/* lgc.h */
1867+#define eris_barrierproto luaC_barrierproto
1868+/* lmem.h */
1869+#define eris_reallocvector luaM_reallocvector
1870+/* lobject.h */
1871+#define eris_ttypenv ttypenv
1872+#define eris_setnilvalue setnilvalue
1873+#define eris_setclLvalue setclLvalue
1874+#define eris_setobj setobj
1875+#define eris_setsvalue2n setsvalue2n
1876+/* lstate.h */
1877+#define eris_isLua isLua
1878+#define eris_gch gch
1879+#define eris_gco2uv gco2uv
1880+#define eris_obj2gco obj2gco
1881+#define eris_extendCI luaE_extendCI
1882+/* lstring. h */
1883+#define eris_newlstr luaS_newlstr
1884+/* lzio.h */
1885+#define eris_initbuffer luaZ_initbuffer
1886+#define eris_buffer luaZ_buffer
1887+#define eris_sizebuffer luaZ_sizebuffer
1888+#define eris_bufflen luaZ_bufflen
1889+#define eris_init luaZ_init
1890+#define eris_read luaZ_read
1891+
1892+/* These are required for cross-platform support, since the size of TValue may
1893+ * differ, so the byte offset used by savestack/restorestack in Lua it is not a
1894+ * valid measure. */
1895+#define eris_savestackidx(L, p) ((p) - (L)->stack)
1896+#define eris_restorestackidx(L, n) ((L)->stack + (n))
1897+
1898+/* Enabled if we have a patched version of Lua (for accessing internals). */
1899+#if 1
1900+
1901+/* Functions in Lua libraries used to access C functions we need to add to the
1902+ * permanents table to fully support yielded coroutines. */
1903+extern void eris_permbaselib(lua_State *L, bool forUnpersist);
1904+extern void eris_permcorolib(lua_State *L, bool forUnpersist);
1905+extern void eris_permloadlib(lua_State *L, bool forUnpersist);
1906+extern void eris_permiolib(lua_State *L, bool forUnpersist);
1907+extern void eris_permstrlib(lua_State *L, bool forUnpersist);
1908+
1909+/* Utility macro for populating the perms table with internal C functions. */
1910+#define populateperms(L, forUnpersist) {\
1911+ eris_permbaselib(L, forUnpersist);\
1912+ eris_permcorolib(L, forUnpersist);\
1913+ eris_permloadlib(L, forUnpersist);\
1914+ eris_permiolib(L, forUnpersist);\
1915+ eris_permstrlib(L, forUnpersist);\
1916+}
1917+
1918+#else
1919+
1920+/* Does nothing if we don't have a patched version of Lua. */
1921+#define populateperms(L, forUnpersist) ((void)0)
1922+
1923+#endif
1924+
1925+/*
1926+** ============================================================================
1927+** Constants, settings, types and forward declarations.
1928+** ============================================================================
1929+*/
1930+
1931+/* The "type" we write when we persist a value via a replacement from the
1932+ * permanents table. This is just an arbitrary number, but it must we lower
1933+ * than the reference offset (below) and outside the range Lua uses for its
1934+ * types (> LUA_TOTALTAGS). */
1935+#define ERIS_PERMANENT (LUA_TOTALTAGS + 1)
1936+
1937+/* This is essentially the first reference we'll use. We do this to save one
1938+ * field in our persisted data: if the value is smaller than this, the object
1939+ * itself follows, otherwise we have a reference to an already unpersisted
1940+ * object. Note that in the reftable the actual entries are still stored
1941+ * starting at the first array index to have a sequence (when unpersisting). */
1942+#define ERIS_REFERENCE_OFFSET (ERIS_PERMANENT + 1)
1943+
1944+/* Avoids having to write the NULL all the time, plus makes it easier adding
1945+ * a custom error message should you ever decide you want one. */
1946+#define eris_checkstack(L, n) luaL_checkstack(L, n, NULL)
1947+
1948+/* Used for internal consistency checks, for debugging. These are true asserts
1949+ * in the sense that they should never fire, even for bad inputs. */
1950+#if 0
1951+#define eris_assert(c) assert(c)
1952+#define eris_ifassert(e) e
1953+#else
1954+#define eris_assert(c) ((void)0)
1955+#define eris_ifassert(e) ((void)0)
1956+#endif
1957+
1958+/* State information when persisting an object. */
1959+typedef struct PersistInfo {
1960+ lua_Writer writer;
1961+ void *ud;
1962+ const char *metafield;
1963+ bool writeDebugInfo;
1964+} PersistInfo;
1965+
1966+/* State information when unpersisting an object. */
1967+typedef struct UnpersistInfo {
1968+ ZIO zio;
1969+ size_t sizeof_int;
1970+ size_t sizeof_size_t;
1971+} UnpersistInfo;
1972+
1973+/* Info shared in persist and unpersist. */
1974+typedef struct Info {
1975+ lua_State *L;
1976+ lua_Unsigned level;
1977+ int refcount; /* int because rawseti/rawgeti takes an int. */
1978+ lua_Unsigned maxComplexity;
1979+ bool generatePath;
1980+ bool passIOToPersist;
1981+ /* Which one it really is will always be clear from the context. */
1982+ union {
1983+ PersistInfo pi;
1984+ UnpersistInfo upi;
1985+ } u;
1986+} Info;
1987+
1988+/* Type names, used for error messages. */
1989+static const char *const kTypenames[] = {
1990+ "nil", "boolean", "lightuserdata", "number", "string",
1991+ "table", "function", "userdata", "thread", "proto", "upval",
1992+ "deadkey", "permanent"
1993+};
1994+
1995+/* Setting names as used in eris.settings / eris_g|set_setting. Also, the
1996+ * addresses of these static variables are used as keys in the registry of Lua
1997+ * states to save the current values of the settings (as light userdata). */
1998+static const char *const kSettingMetafield = "spkey";
1999+static const char *const kSettingPassIOToPersist = "spio";
2000+static const char *const kSettingGeneratePath = "path";
2001+static const char *const kSettingWriteDebugInfo = "debug";
2002+static const char *const kSettingMaxComplexity = "maxrec";
2003+
2004+/* Header we prefix to persisted data for a quick check when unpersisting. */
2005+static char const kHeader[] = { 'E', 'R', 'I', 'S' };
2006+#define HEADER_LENGTH sizeof(kHeader)
2007+
2008+/* Floating point number used to check compatibility of loaded data. */
2009+static const lua_Number kHeaderNumber = (lua_Number)-1.234567890;
2010+
2011+/* Stack indices of some internal values/tables, to avoid magic numbers. */
2012+#define PERMIDX 1
2013+#define REFTIDX 2
2014+#define BUFFIDX 3
2015+#define PATHIDX 4
2016+
2017+/* }======================================================================== */
2018+
2019+/*
2020+** {===========================================================================
2021+** Utility functions.
2022+** ============================================================================
2023+*/
2024+
2025+/* Pushes an object into the reference table when unpersisting. This creates an
2026+ * entry pointing from the id the object is referenced by to the object. */
2027+static int
2028+registerobject(Info *info) { /* perms reftbl ... obj */
2029+ const int reference = ++(info->refcount);
2030+ eris_checkstack(info->L, 1);
2031+ lua_pushvalue(info->L, -1); /* perms reftbl ... obj obj */
2032+ lua_rawseti(info->L, REFTIDX, reference); /* perms reftbl ... obj */
2033+ return reference;
2034+}
2035+
2036+/** ======================================================================== */
2037+
2038+/* Pushes a TString* onto the stack if it holds a value, nil if it is NULL. */
2039+static void
2040+pushtstring(lua_State* L, TString *ts) { /* ... */
2041+ if (ts) {
2042+ eris_setsvalue2n(L, L->top, ts);
2043+ eris_incr_top(L); /* ... str */
2044+ }
2045+ else {
2046+ lua_pushnil(L); /* ... nil */
2047+ }
2048+}
2049+
2050+/* Creates a copy of the string on top of the stack and sets it as the value
2051+ * of the specified TString**. */
2052+static void
2053+copytstring(lua_State* L, TString **ts) {
2054+ size_t length;
2055+ const char *value = lua_tolstring(L, -1, &length);
2056+ *ts = eris_newlstr(L, value, length);
2057+}
2058+
2059+/** ======================================================================== */
2060+
2061+/* Pushes the specified segment to the current path, if we're generating one.
2062+ * This supports formatting strings using Lua's formatting capabilities. */
2063+static void
2064+pushpath(Info *info, const char* fmt, ...) { /* perms reftbl var path ... */
2065+ if (!info->generatePath) {
2066+ return;
2067+ }
2068+ else {
2069+ va_list argp;
2070+ eris_checkstack(info->L, 1);
2071+ va_start(argp, fmt);
2072+ lua_pushvfstring(info->L, fmt, argp); /* perms reftbl var path ... str */
2073+ va_end(argp);
2074+ lua_rawseti(info->L, PATHIDX, luaL_len(info->L, PATHIDX) + 1);
2075+ } /* perms reftbl var path ... */
2076+}
2077+
2078+/* Pops the last added segment from the current path if we're generating one. */
2079+static void
2080+poppath(Info *info) { /* perms reftbl var path ... */
2081+ if (!info->generatePath) {
2082+ return;
2083+ }
2084+ eris_checkstack(info->L, 1);
2085+ lua_pushnil(info->L); /* perms reftbl var path ... nil */
2086+ lua_rawseti(info->L, PATHIDX, luaL_len(info->L, PATHIDX));
2087+} /* perms reftbl var path ... */
2088+
2089+/* Concatenates all current path segments into one string, pushes it and
2090+ * returns it. This is relatively inefficient, but it's for errors only and
2091+ * keeps the stack small, so it's better this way. */
2092+static const char*
2093+path(Info *info) { /* perms reftbl var path ... */
2094+ if (!info->generatePath) {
2095+ return "";
2096+ }
2097+ eris_checkstack(info->L, 3);
2098+ lua_pushstring(info->L, ""); /* perms reftbl var path ... str */
2099+ lua_pushnil(info->L); /* perms reftbl var path ... str nil */
2100+ while (lua_next(info->L, PATHIDX)) { /* perms reftbl var path ... str k v */
2101+ lua_insert(info->L, -2); /* perms reftbl var path ... str v k */
2102+ lua_insert(info->L, -3); /* perms reftbl var path ... k str v */
2103+ lua_concat(info->L, 2); /* perms reftbl var path ... k str */
2104+ lua_insert(info->L, -2); /* perms reftbl var path ... str k */
2105+ } /* perms reftbl var path ... str */
2106+ return lua_tostring(info->L, -1);
2107+}
2108+
2109+/* Generates an error message with the appended path, if available. */
2110+static int
2111+eris_error(Info *info, const char *fmt, ...) { /* ... */
2112+ va_list argp;
2113+ eris_checkstack(info->L, 5);
2114+
2115+ luaL_where(info->L, 1); /* ... where */
2116+ va_start(argp, fmt);
2117+ lua_pushvfstring(info->L, fmt, argp); /* ... where str */
2118+ va_end(argp);
2119+ if (info->generatePath) {
2120+ lua_pushstring(info->L, " ("); /* ... where str " (" */
2121+ path(info); /* ... where str " (" path */
2122+ lua_pushstring(info->L, ")"); /* ... where str " (" path ")" */
2123+ lua_concat(info->L, 5); /* ... msg */
2124+ }
2125+ else {
2126+ lua_concat(info->L, 2); /* ... msg */
2127+ }
2128+ return lua_error(info->L);
2129+}
2130+
2131+/** ======================================================================== */
2132+
2133+/* Tries to get a setting from the registry. */
2134+static bool
2135+get_setting(lua_State *L, void *key) { /* ... */
2136+ eris_checkstack(L, 1);
2137+ lua_pushlightuserdata(L, key); /* ... key */
2138+ lua_gettable(L, LUA_REGISTRYINDEX); /* ... value/nil */
2139+ if (lua_isnil(L, -1)) { /* ... nil */
2140+ lua_pop(L, 1); /* ... */
2141+ return false;
2142+ } /* ... value */
2143+ return true;
2144+}
2145+
2146+/* Stores a setting in the registry (or removes it if the value is nil). */
2147+static void
2148+set_setting(lua_State *L, void *key) { /* ... value */
2149+ eris_checkstack(L, 2);
2150+ lua_pushlightuserdata(L, key); /* ... value key */
2151+ lua_insert(L, -2); /* ... key value */
2152+ lua_settable(L, LUA_REGISTRYINDEX); /* ... */
2153+}
2154+
2155+/* Used as a callback for luaL_opt to check boolean setting values. */
2156+static void
2157+checkboolean(lua_State *L, int narg) { /* ... bool? ... */
2158+ if (!lua_isboolean(L, narg)) { /* ... :( ... */
2159+ luaL_argerror(L, narg, lua_pushfstring(L,
2160+ "boolean expected, got %s", lua_typename(L, lua_type(L, narg))));
2161+ } /* ... bool ... */
2162+}
2163+
2164+/* }======================================================================== */
2165+
2166+/*
2167+** {===========================================================================
2168+** Persist and unpersist.
2169+** ============================================================================
2170+*/
2171+
2172+/* I have macros and I'm not afraid to use them! These are highly situational
2173+ * and assume an Info* named 'info' is available. */
2174+
2175+/* Writes a raw memory block with the specified size. */
2176+#define WRITE_RAW(value, size) {\
2177+ if (info->u.pi.writer(info->L, (value), (size), info->u.pi.ud)) \
2178+ eris_error(info, "could not write data"); }
2179+
2180+/* Writes a single value with the specified type. */
2181+#define WRITE_VALUE(value, type) write_##type(info, value)
2182+
2183+/* Writes a typed array with the specified length. */
2184+#define WRITE(value, length, type) { \
2185+ int i; for (i = 0; i < length; ++i) WRITE_VALUE((value)[i], type); }
2186+
2187+/** ======================================================================== */
2188+
2189+/* Reads a raw block of memory with the specified size. */
2190+#define READ_RAW(value, size) {\
2191+ if (eris_read(&info->u.upi.zio, (value), (size))) \
2192+ eris_error(info, "could not read data"); }
2193+
2194+/* Reads a single value with the specified type. */
2195+#define READ_VALUE(type) read_##type(info)
2196+
2197+/* Reads a typed array with the specified length. */
2198+#define READ(value, length, type) { \
2199+ int i; for (i = 0; i < length; ++i) (value)[i] = READ_VALUE(type); }
2200+
2201+/** ======================================================================== */
2202+
2203+static void
2204+write_uint8_t(Info *info, uint8_t value) {
2205+ WRITE_RAW(&value, sizeof(uint8_t));
2206+}
2207+
2208+static void
2209+write_uint16_t(Info *info, uint16_t value) {
2210+ write_uint8_t(info, value);
2211+ write_uint8_t(info, value >> 8);
2212+}
2213+
2214+static void
2215+write_uint32_t(Info *info, uint32_t value) {
2216+ write_uint8_t(info, value);
2217+ write_uint8_t(info, value >> 8);
2218+ write_uint8_t(info, value >> 16);
2219+ write_uint8_t(info, value >> 24);
2220+}
2221+
2222+static void
2223+write_uint64_t(Info *info, uint64_t value) {
2224+ write_uint8_t(info, value);
2225+ write_uint8_t(info, value >> 8);
2226+ write_uint8_t(info, value >> 16);
2227+ write_uint8_t(info, value >> 24);
2228+ write_uint8_t(info, value >> 32);
2229+ write_uint8_t(info, value >> 40);
2230+ write_uint8_t(info, value >> 48);
2231+ write_uint8_t(info, value >> 56);
2232+}
2233+
2234+static void
2235+write_int16_t(Info *info, int16_t value) {
2236+ write_uint16_t(info, (uint16_t)value);
2237+}
2238+
2239+static void
2240+write_int32_t(Info *info, int32_t value) {
2241+ write_uint32_t(info, (uint32_t)value);
2242+}
2243+
2244+static void
2245+write_int64_t(Info *info, int64_t value) {
2246+ write_uint64_t(info, (uint64_t)value);
2247+}
2248+
2249+static void
2250+write_float32(Info *info, float value) {
2251+ uint32_t rep;
2252+ memcpy(&rep, &value, sizeof(float));
2253+ write_uint32_t(info, rep);
2254+}
2255+
2256+static void
2257+write_float64(Info *info, double value) {
2258+ uint64_t rep;
2259+ memcpy(&rep, &value, sizeof(double));
2260+ write_uint64_t(info, rep);
2261+}
2262+
2263+/* Note regarding the following: any decent compiler should be able
2264+ * to reduce these to just the write call, since sizeof is constant. */
2265+
2266+static void
2267+write_int(Info *info, int value) {
2268+ if (sizeof(int) == sizeof(int16_t)) {
2269+ write_int16_t(info, value);
2270+ }
2271+ else if (sizeof(int) == sizeof(int32_t)) {
2272+ write_int32_t(info, value);
2273+ }
2274+ else if (sizeof(int) == sizeof(int64_t)) {
2275+ write_int64_t(info, value);
2276+ }
2277+ else {
2278+ eris_error(info, "unsupported int type");
2279+ }
2280+}
2281+
2282+static void
2283+write_size_t(Info *info, size_t value) {
2284+ if (sizeof(size_t) == sizeof(uint16_t)) {
2285+ write_uint16_t(info, value);
2286+ }
2287+ else if (sizeof(size_t) == sizeof(uint32_t)) {
2288+ write_uint32_t(info, value);
2289+ }
2290+ else if (sizeof(size_t) == sizeof(uint64_t)) {
2291+ write_uint64_t(info, value);
2292+ }
2293+ else {
2294+ eris_error(info, "unsupported size_t type");
2295+ }
2296+}
2297+
2298+static void
2299+write_lua_Number(Info *info, lua_Number value) {
2300+ if (sizeof(lua_Number) == sizeof(uint32_t)) {
2301+ write_float32(info, value);
2302+ }
2303+ else if (sizeof(lua_Number) == sizeof(uint64_t)) {
2304+ write_float64(info, value);
2305+ }
2306+ else {
2307+ eris_error(info, "unsupported lua_Number type");
2308+ }
2309+}
2310+
2311+/* Note that Lua only ever uses 32 bits of the Instruction type, so we can
2312+ * assert that there will be no truncation, even if the underlying type has
2313+ * more bits (might be the case on some 64 bit systems). */
2314+
2315+static void
2316+write_Instruction(Info *info, Instruction value) {
2317+ if (sizeof(Instruction) == sizeof(uint32_t)) {
2318+ write_uint32_t(info, value);
2319+ }
2320+ else {
2321+ uint32_t pvalue = (uint32_t)value;
2322+ /* Lua only uses 32 bits for its instructions. */
2323+ eris_assert((Instruction)pvalue == value);
2324+ write_uint32_t(info, pvalue);
2325+ }
2326+}
2327+
2328+/** ======================================================================== */
2329+
2330+static uint8_t
2331+read_uint8_t(Info *info) {
2332+ uint8_t value;
2333+ READ_RAW(&value, sizeof(uint8_t));
2334+ return value;
2335+}
2336+
2337+static uint16_t
2338+read_uint16_t(Info *info) {
2339+ return (uint16_t)read_uint8_t(info) |
2340+ ((uint16_t)read_uint8_t(info) << 8);
2341+}
2342+
2343+static uint32_t
2344+read_uint32_t(Info *info) {
2345+ return (uint32_t)read_uint8_t(info) |
2346+ ((uint32_t)read_uint8_t(info) << 8) |
2347+ ((uint32_t)read_uint8_t(info) << 16) |
2348+ ((uint32_t)read_uint8_t(info) << 24);
2349+}
2350+
2351+static uint64_t
2352+read_uint64_t(Info *info) {
2353+ return (uint64_t)read_uint8_t(info) |
2354+ ((uint64_t)read_uint8_t(info) << 8) |
2355+ ((uint64_t)read_uint8_t(info) << 16) |
2356+ ((uint64_t)read_uint8_t(info) << 24) |
2357+ ((uint64_t)read_uint8_t(info) << 32) |
2358+ ((uint64_t)read_uint8_t(info) << 40) |
2359+ ((uint64_t)read_uint8_t(info) << 48) |
2360+ ((uint64_t)read_uint8_t(info) << 56);
2361+}
2362+
2363+static int16_t
2364+read_int16_t(Info *info) {
2365+ return (int16_t)read_uint16_t(info);
2366+}
2367+
2368+static int32_t
2369+read_int32_t(Info *info) {
2370+ return (int32_t)read_uint32_t(info);
2371+}
2372+
2373+static int64_t
2374+read_int64_t(Info *info) {
2375+ return (int64_t)read_uint64_t(info);
2376+}
2377+
2378+static float
2379+read_float32(Info *info) {
2380+ float value;
2381+ uint32_t rep = read_uint32_t(info);
2382+ memcpy(&value, &rep, sizeof(float));
2383+ return value;
2384+}
2385+
2386+static double
2387+read_float64(Info *info) {
2388+ double value;
2389+ uint64_t rep = read_uint64_t(info);
2390+ memcpy(&value, &rep, sizeof(double));
2391+ return value;
2392+}
2393+
2394+/* Note regarding the following: unlike with writing the sizeof check will be
2395+ * impossible to optimize away, since it depends on the input; however, the
2396+ * truncation check may be optimized away in the case where the read data size
2397+ * equals the native one, so reading data written on the same machine should be
2398+ * reasonably quick. Doing a (rather rudimentary) benchmark this did not have
2399+ * any measurable impact on performance. */
2400+
2401+static int
2402+read_int(Info *info) {
2403+ int value;
2404+ if (info->u.upi.sizeof_int == sizeof(int16_t)) {
2405+ int16_t pvalue = read_int16_t(info);
2406+ value = (int)pvalue;
2407+ if ((int32_t)value != pvalue) {
2408+ eris_error(info, "int value would get truncated");
2409+ }
2410+ }
2411+ else if (info->u.upi.sizeof_int == sizeof(int32_t)) {
2412+ int32_t pvalue = read_int32_t(info);
2413+ value = (int)pvalue;
2414+ if ((int32_t)value != pvalue) {
2415+ eris_error(info, "int value would get truncated");
2416+ }
2417+ }
2418+ else if (info->u.upi.sizeof_int == sizeof(int64_t)) {
2419+ int64_t pvalue = read_int64_t(info);
2420+ value = (int)pvalue;
2421+ if ((int64_t)value != pvalue) {
2422+ eris_error(info, "int value would get truncated");
2423+ }
2424+ }
2425+ else {
2426+ eris_error(info, "unsupported int type");
2427+ value = 0; /* not reached */
2428+ }
2429+ return value;
2430+}
2431+
2432+static size_t
2433+read_size_t(Info *info) {
2434+ size_t value;
2435+ if (info->u.upi.sizeof_size_t == sizeof(uint16_t)) {
2436+ uint16_t pvalue = read_uint16_t(info);
2437+ value = (size_t)pvalue;
2438+ if ((uint32_t)value != pvalue) {
2439+ eris_error(info, "size_t value would get truncated");
2440+ }
2441+ }
2442+ else if (info->u.upi.sizeof_size_t == sizeof(uint32_t)) {
2443+ uint32_t pvalue = read_uint32_t(info);
2444+ value = (size_t)pvalue;
2445+ if ((uint32_t)value != pvalue) {
2446+ eris_error(info, "size_t value would get truncated");
2447+ }
2448+ }
2449+ else if (info->u.upi.sizeof_size_t == sizeof(uint64_t)) {
2450+ uint64_t pvalue = read_uint64_t(info);
2451+ value = (size_t)pvalue;
2452+ if ((uint64_t)value != pvalue) {
2453+ eris_error(info, "size_t value would get truncated");
2454+ }
2455+ }
2456+ else {
2457+ eris_error(info, "unsupported size_t type");
2458+ value = 0; /* not reached */
2459+ }
2460+ return value;
2461+}
2462+
2463+static lua_Number
2464+read_lua_Number(Info *info) {
2465+ if (sizeof(lua_Number) == sizeof(uint32_t)) {
2466+ return read_float32(info);
2467+ }
2468+ else if (sizeof(lua_Number) == sizeof(uint64_t)) {
2469+ return read_float64(info);
2470+ }
2471+ else {
2472+ eris_error(info, "unsupported lua_Number type");
2473+ return 0; /* not reached */
2474+ }
2475+}
2476+
2477+static Instruction
2478+read_Instruction(Info *info) {
2479+ return (Instruction)read_uint32_t(info);
2480+}
2481+
2482+/** ======================================================================== */
2483+
2484+/* Forward declarations for recursively called top-level functions. */
2485+static void persist_keyed(Info*, int type);
2486+static void persist(Info*);
2487+static void unpersist(Info*);
2488+
2489+/*
2490+** ============================================================================
2491+** Simple types.
2492+** ============================================================================
2493+*/
2494+
2495+static void
2496+p_boolean(Info *info) { /* ... bool */
2497+ WRITE_VALUE(lua_toboolean(info->L, -1), uint8_t);
2498+}
2499+
2500+static void
2501+u_boolean(Info *info) { /* ... */
2502+ eris_checkstack(info->L, 1);
2503+ lua_pushboolean(info->L, READ_VALUE(uint8_t)); /* ... bool */
2504+
2505+ eris_assert(lua_type(info->L, -1) == LUA_TBOOLEAN);
2506+}
2507+
2508+/** ======================================================================== */
2509+
2510+static void
2511+p_pointer(Info *info) { /* ... ludata */
2512+ WRITE_VALUE((size_t)lua_touserdata(info->L, -1), size_t);
2513+}
2514+
2515+static void
2516+u_pointer(Info *info) { /* ... */
2517+ eris_checkstack(info->L, 1);
2518+ lua_pushlightuserdata(info->L, (void*)READ_VALUE(size_t)); /* ... ludata */
2519+
2520+ eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
2521+}
2522+
2523+/** ======================================================================== */
2524+
2525+static void
2526+p_number(Info *info) { /* ... num */
2527+ WRITE_VALUE(lua_tonumber(info->L, -1), lua_Number);
2528+}
2529+
2530+static void
2531+u_number(Info *info) { /* ... */
2532+ eris_checkstack(info->L, 1);
2533+ lua_pushnumber(info->L, READ_VALUE(lua_Number)); /* ... num */
2534+
2535+ eris_assert(lua_type(info->L, -1) == LUA_TNUMBER);
2536+}
2537+
2538+/** ======================================================================== */
2539+
2540+static void
2541+p_string(Info *info) { /* ... str */
2542+ size_t length;
2543+ const char *value = lua_tolstring(info->L, -1, &length);
2544+ WRITE_VALUE(length, size_t);
2545+ WRITE_RAW(value, length);
2546+}
2547+
2548+static void
2549+u_string(Info *info) { /* ... */
2550+ eris_checkstack(info->L, 2);
2551+ {
2552+ /* TODO Can we avoid this copy somehow? (Without it getting too nasty) */
2553+ const size_t length = READ_VALUE(size_t);
2554+ char *value = lua_newuserdata(info->L, length * sizeof(char)); /* ... tmp */
2555+ READ_RAW(value, length);
2556+ lua_pushlstring(info->L, value, length); /* ... tmp str */
2557+ lua_replace(info->L, -2); /* ... str */
2558+ }
2559+ registerobject(info);
2560+
2561+ eris_assert(lua_type(info->L, -1) == LUA_TSTRING);
2562+}
2563+
2564+/*
2565+** ============================================================================
2566+** Tables and userdata.
2567+** ============================================================================
2568+*/
2569+
2570+static void
2571+p_metatable(Info *info) { /* ... obj */
2572+ eris_checkstack(info->L, 1);
2573+ pushpath(info, "@metatable");
2574+ if (!lua_getmetatable(info->L, -1)) { /* ... obj mt? */
2575+ lua_pushnil(info->L); /* ... obj nil */
2576+ } /* ... obj mt/nil */
2577+ persist(info); /* ... obj mt/nil */
2578+ lua_pop(info->L, 1); /* ... obj */
2579+ poppath(info);
2580+}
2581+
2582+static void
2583+u_metatable(Info *info) { /* ... tbl */
2584+ eris_checkstack(info->L, 1);
2585+ pushpath(info, "@metatable");
2586+ unpersist(info); /* ... tbl mt/nil? */
2587+ if (lua_istable(info->L, -1)) { /* ... tbl mt */
2588+ lua_setmetatable(info->L, -2); /* ... tbl */
2589+ }
2590+ else if (lua_isnil(info->L, -1)) { /* ... tbl nil */
2591+ lua_pop(info->L, 1); /* ... tbl */
2592+ }
2593+ else { /* tbl :( */
2594+ eris_error(info, "bad metatable, not nil or table");
2595+ }
2596+ poppath(info);
2597+}
2598+
2599+/** ======================================================================== */
2600+
2601+static void
2602+p_literaltable(Info *info) { /* ... tbl */
2603+ eris_checkstack(info->L, 3);
2604+
2605+ /* Persist all key / value pairs. */
2606+ lua_pushnil(info->L); /* ... tbl nil */
2607+ while (lua_next(info->L, -2)) { /* ... tbl k v */
2608+ lua_pushvalue(info->L, -2); /* ... tbl k v k */
2609+
2610+ if (info->generatePath) {
2611+ if (lua_type(info->L, -1) == LUA_TSTRING) {
2612+ const char *key = lua_tostring(info->L, -1);
2613+ pushpath(info, ".%s", key);
2614+ }
2615+ else {
2616+ const char *key = luaL_tolstring(info->L, -1, NULL);
2617+ pushpath(info, "[%s]", key);
2618+ lua_pop(info->L, 1);
2619+ }
2620+ }
2621+
2622+ persist(info); /* ... tbl k v k */
2623+ lua_pop(info->L, 1); /* ... tbl k v */
2624+ persist(info); /* ... tbl k v */
2625+ lua_pop(info->L, 1); /* ... tbl k */
2626+
2627+ poppath(info);
2628+ } /* ... tbl */
2629+
2630+ /* Terminate list. */
2631+ lua_pushnil(info->L); /* ... tbl nil */
2632+ persist(info); /* ... tbl nil */
2633+ lua_pop(info->L, 1); /* ... tbl */
2634+
2635+ p_metatable(info);
2636+}
2637+
2638+static void
2639+u_literaltable(Info *info) { /* ... */
2640+ eris_checkstack(info->L, 3);
2641+
2642+ lua_newtable(info->L); /* ... tbl */
2643+
2644+ /* Preregister table for handling of cycles (keys, values or metatable). */
2645+ registerobject(info);
2646+
2647+ /* Unpersist all key / value pairs. */
2648+ for (;;) {
2649+ pushpath(info, "@key");
2650+ unpersist(info); /* ... tbl key/nil */
2651+ poppath(info);
2652+ if (lua_isnil(info->L, -1)) { /* ... tbl nil */
2653+ lua_pop(info->L, 1); /* ... tbl */
2654+ break;
2655+ } /* ... tbl key */
2656+
2657+ if (info->generatePath) {
2658+ if (lua_type(info->L, -1) == LUA_TSTRING) {
2659+ const char *key = lua_tostring(info->L, -1);
2660+ pushpath(info, ".%s", key);
2661+ }
2662+ else {
2663+ const char *key = luaL_tolstring(info->L, -1, NULL);
2664+ pushpath(info, "[%s]", key);
2665+ lua_pop(info->L, 1);
2666+ }
2667+ }
2668+
2669+ unpersist(info); /* ... tbl key value? */
2670+ if (!lua_isnil(info->L, -1)) { /* ... tbl key value */
2671+ lua_rawset(info->L, -3); /* ... tbl */
2672+ }
2673+ else {
2674+ eris_error(info, "bad table value, got a nil value");
2675+ }
2676+
2677+ poppath(info);
2678+ }
2679+
2680+ u_metatable(info); /* ... tbl */
2681+}
2682+
2683+/** ======================================================================== */
2684+
2685+static void
2686+p_literaluserdata(Info *info) { /* ... udata */
2687+ const size_t size = lua_rawlen(info->L, -1);
2688+ const void *value = lua_touserdata(info->L, -1);
2689+ WRITE_VALUE(size, size_t);
2690+ WRITE_RAW(value, size);
2691+ p_metatable(info); /* ... udata */
2692+}
2693+
2694+static void
2695+u_literaluserdata(Info *info) { /* ... */
2696+ eris_checkstack(info->L, 1);
2697+ {
2698+ size_t size = READ_VALUE(size_t);
2699+ void *value = lua_newuserdata(info->L, size); /* ... udata */
2700+ READ_RAW(value, size); /* ... udata */
2701+ }
2702+ registerobject(info);
2703+ u_metatable(info);
2704+}
2705+
2706+/** ======================================================================== */
2707+
2708+typedef void (*Callback) (Info*);
2709+
2710+static void
2711+p_special(Info *info, Callback literal) { /* ... obj */
2712+ int allow = (lua_type(info->L, -1) == LUA_TTABLE);
2713+ eris_checkstack(info->L, 4);
2714+
2715+ /* Check whether we should persist literally, or via the metafunction. */
2716+ if (lua_getmetatable(info->L, -1)) { /* ... obj mt */
2717+ lua_pushstring(info->L, info->u.pi.metafield); /* ... obj mt pkey */
2718+ lua_rawget(info->L, -2); /* ... obj mt persist? */
2719+ switch (lua_type(info->L, -1)) {
2720+ /* No entry, act according to default. */
2721+ case LUA_TNIL: /* ... obj mt nil */
2722+ lua_pop(info->L, 2); /* ... obj */
2723+ break;
2724+
2725+ /* Boolean value, tells us whether allowed or not. */
2726+ case LUA_TBOOLEAN: /* ... obj mt bool */
2727+ allow = lua_toboolean(info->L, -1);
2728+ lua_pop(info->L, 2); /* ... obj */
2729+ break;
2730+
2731+ /* Function value, call it and don't persist literally. */
2732+ case LUA_TFUNCTION: /* ... obj mt func */
2733+ lua_replace(info->L, -2); /* ... obj func */
2734+ lua_pushvalue(info->L, -2); /* ... obj func obj */
2735+
2736+ if (info->passIOToPersist) {
2737+ lua_pushlightuserdata(info->L, info->u.pi.writer);
2738+ /* ... obj func obj writer */
2739+ lua_pushlightuserdata(info->L, info->u.pi.ud);
2740+ /* ... obj func obj writer ud */
2741+ lua_call(info->L, 3, 1); /* ... obj func? */
2742+ }
2743+ else {
2744+ lua_call(info->L, 1, 1); /* ... obj func? */
2745+ }
2746+ if (!lua_isfunction(info->L, -1)) { /* ... obj :( */
2747+ eris_error(info, "%s did not return a function",
2748+ info->u.pi.metafield);
2749+ } /* ... obj func */
2750+
2751+ /* Special persistence, call this function when unpersisting. */
2752+ WRITE_VALUE(true, uint8_t);
2753+ persist(info); /* ... obj func */
2754+ lua_pop(info->L, 1); /* ... obj */
2755+ return;
2756+ default: /* ... obj mt :( */
2757+ eris_error(info, "%d not nil, boolean, or function",
2758+ info->u.pi.metafield);
2759+ return; /* not reached */
2760+ }
2761+ }
2762+
2763+ if (allow) {
2764+ /* Not special but literally persisted object. */
2765+ WRITE_VALUE(false, uint8_t);
2766+ literal(info); /* ... obj */
2767+ }
2768+ else if (lua_type(info->L, -1) == LUA_TTABLE) {
2769+ eris_error(info, "attempt to persist forbidden table");
2770+ }
2771+ else {
2772+ eris_error(info, "attempt to literally persist userdata");
2773+ }
2774+}
2775+
2776+static void
2777+u_special(Info *info, int type, Callback literal) { /* ... */
2778+ eris_checkstack(info->L, 2);
2779+ if (READ_VALUE(uint8_t)) {
2780+ int reference;
2781+ /* Reserve entry in the reftable before unpersisting the function to keep
2782+ * the reference order intact. We can set this to nil at first, because
2783+ * there's no way the special function would access this. */
2784+ lua_pushnil(info->L); /* ... nil */
2785+ reference = registerobject(info);
2786+ lua_pop(info->L, 1); /* ... */
2787+ /* Increment reference counter by one to compensate for the increment when
2788+ * persisting a special object. */
2789+ unpersist(info); /* ... spfunc? */
2790+ if (!lua_isfunction(info->L, -1)) { /* ... :( */
2791+ eris_error(info, "invalid restore function");
2792+ } /* ... spfunc */
2793+
2794+ if (info->passIOToPersist) {
2795+ lua_pushlightuserdata(info->L, &info->u.upi.zio); /* ... spfunc zio */
2796+ lua_call(info->L, 1, 1); /* ... obj? */
2797+ } else {
2798+ lua_call(info->L, 0, 1); /* ... obj? */
2799+ }
2800+
2801+ if (lua_type(info->L, -1) != type) { /* ... :( */
2802+ eris_error(info, "bad unpersist function (%s expected, returned %s)",
2803+ kTypenames[type], kTypenames[lua_type(info->L, -1)]);
2804+ } /* ... obj */
2805+
2806+ /* Update the reftable entry. */
2807+ lua_pushvalue(info->L, -1); /* ... obj obj */
2808+ lua_rawseti(info->L, 2, reference); /* ... obj */
2809+ }
2810+ else {
2811+ literal(info); /* ... obj */
2812+ }
2813+}
2814+
2815+/** ======================================================================== */
2816+
2817+static void
2818+p_table(Info *info) { /* ... tbl */
2819+ p_special(info, p_literaltable); /* ... tbl */
2820+}
2821+
2822+static void
2823+u_table(Info *info) { /* ... */
2824+ u_special(info, LUA_TTABLE, u_literaltable); /* ... tbl */
2825+
2826+ eris_assert(lua_type(info->L, -1) == LUA_TTABLE);
2827+}
2828+
2829+/** ======================================================================== */
2830+
2831+static void
2832+p_userdata(Info *info) { /* perms reftbl ... udata */
2833+ p_special(info, p_literaluserdata);
2834+}
2835+
2836+static void
2837+u_userdata(Info *info) { /* ... */
2838+ u_special(info, LUA_TUSERDATA, u_literaluserdata); /* ... udata */
2839+
2840+ eris_assert(lua_type(info->L, -1) == LUA_TUSERDATA);
2841+}
2842+
2843+/*
2844+** ============================================================================
2845+** Closures and threads.
2846+** ============================================================================
2847+*/
2848+
2849+/* We track the actual upvalues themselves by pushing their "id" (meaning a
2850+ * pointer to them) as lightuserdata to the reftable. This is safe because
2851+ * lightuserdata will not normally end up in there, because simple value types
2852+ * are always persisted directly (because that'll be just as large, memory-
2853+ * wise as when pointing to the first instance). Same for protos. */
2854+
2855+static void
2856+p_proto(Info *info) { /* ... proto */
2857+ int i;
2858+ const Proto *p = lua_touserdata(info->L, -1);
2859+ eris_checkstack(info->L, 3);
2860+
2861+ /* Write general information. */
2862+ WRITE_VALUE(p->linedefined, int);
2863+ WRITE_VALUE(p->lastlinedefined, int);
2864+ WRITE_VALUE(p->numparams, uint8_t);
2865+ WRITE_VALUE(p->is_vararg, uint8_t);
2866+ WRITE_VALUE(p->maxstacksize, uint8_t);
2867+
2868+ /* Write byte code. */
2869+ WRITE_VALUE(p->sizecode, int);
2870+ WRITE(p->code, p->sizecode, Instruction);
2871+
2872+ /* Write constants. */
2873+ WRITE_VALUE(p->sizek, int);
2874+ pushpath(info, ".constants");
2875+ for (i = 0; i < p->sizek; ++i) {
2876+ pushpath(info, "[%d]", i);
2877+ eris_setobj(info->L, info->L->top++, &p->k[i]); /* ... lcl proto obj */
2878+ persist(info); /* ... lcl proto obj */
2879+ lua_pop(info->L, 1); /* ... lcl proto */
2880+ poppath(info);
2881+ }
2882+ poppath(info);
2883+
2884+ /* Write child protos. */
2885+ WRITE_VALUE(p->sizep, int);
2886+ pushpath(info, ".protos");
2887+ for (i = 0; i < p->sizep; ++i) {
2888+ pushpath(info, "[%d]", i);
2889+ lua_pushlightuserdata(info->L, p->p[i]); /* ... lcl proto proto */
2890+ lua_pushvalue(info->L, -1); /* ... lcl proto proto proto */
2891+ persist_keyed(info, LUA_TPROTO); /* ... lcl proto proto */
2892+ lua_pop(info->L, 1); /* ... lcl proto */
2893+ poppath(info);
2894+ }
2895+ poppath(info);
2896+
2897+ /* Write upvalues. */
2898+ WRITE_VALUE(p->sizeupvalues, int);
2899+ for (i = 0; i < p->sizeupvalues; ++i) {
2900+ WRITE_VALUE(p->upvalues[i].instack, uint8_t);
2901+ WRITE_VALUE(p->upvalues[i].idx, uint8_t);
2902+ }
2903+
2904+ /* If we don't have to persist debug information skip the rest. */
2905+ WRITE_VALUE(info->u.pi.writeDebugInfo, uint8_t);
2906+ if (!info->u.pi.writeDebugInfo) {
2907+ return;
2908+ }
2909+
2910+ /* Write function source code. */
2911+ pushtstring(info->L, p->source); /* ... lcl proto source */
2912+ persist(info); /* ... lcl proto source */
2913+ lua_pop(info->L, 1); /* ... lcl proto */
2914+
2915+ /* Write line information. */
2916+ WRITE_VALUE(p->sizelineinfo, int);
2917+ WRITE(p->lineinfo, p->sizelineinfo, int);
2918+
2919+ /* Write locals info. */
2920+ WRITE_VALUE(p->sizelocvars, int);
2921+ pushpath(info, ".locvars");
2922+ for (i = 0; i < p->sizelocvars; ++i) {
2923+ pushpath(info, "[%d]", i);
2924+ WRITE_VALUE(p->locvars[i].startpc, int);
2925+ WRITE_VALUE(p->locvars[i].endpc, int);
2926+ pushtstring(info->L, p->locvars[i].varname); /* ... lcl proto varname */
2927+ persist(info); /* ... lcl proto varname */
2928+ lua_pop(info->L, 1); /* ... lcl proto */
2929+ poppath(info);
2930+ }
2931+ poppath(info);
2932+
2933+ /* Write upvalue names. */
2934+ pushpath(info, ".upvalnames");
2935+ for (i = 0; i < p->sizeupvalues; ++i) {
2936+ pushpath(info, "[%d]", i);
2937+ pushtstring(info->L, p->upvalues[i].name); /* ... lcl proto name */
2938+ persist(info); /* ... lcl proto name */
2939+ lua_pop(info->L, 1); /* ... lcl proto */
2940+ poppath(info);
2941+ }
2942+ poppath(info);
2943+}
2944+
2945+static void
2946+u_proto(Info *info) { /* ... proto */
2947+ int i, n;
2948+ Proto *p = lua_touserdata(info->L, -1);
2949+ eris_assert(p);
2950+
2951+ eris_checkstack(info->L, 2);
2952+
2953+ /* Preregister proto for handling of cycles (probably impossible, but
2954+ * maybe via the constants of the proto... not worth taking the risk). */
2955+ registerobject(info);
2956+
2957+ /* Read general information. */
2958+ p->linedefined = READ_VALUE(int);
2959+ p->lastlinedefined = READ_VALUE(int);
2960+ p->numparams = READ_VALUE(uint8_t);
2961+ p->is_vararg = READ_VALUE(uint8_t);
2962+ p->maxstacksize = READ_VALUE(uint8_t);
2963+
2964+ /* Read byte code. */
2965+ p->sizecode = READ_VALUE(int);
2966+ eris_reallocvector(info->L, p->code, 0, p->sizecode, Instruction);
2967+ READ(p->code, p->sizecode, Instruction);
2968+
2969+ /* Read constants. */
2970+ p->sizek = READ_VALUE(int);
2971+ eris_reallocvector(info->L, p->k, 0, p->sizek, TValue);
2972+ /* Set all values to nil to avoid confusing the GC. */
2973+ for (i = 0, n = p->sizek; i < n; ++i) {
2974+ eris_setnilvalue(&p->k[i]);
2975+ }
2976+ pushpath(info, ".constants");
2977+ for (i = 0, n = p->sizek; i < n; ++i) {
2978+ pushpath(info, "[%d]", i);
2979+ unpersist(info); /* ... proto obj */
2980+ eris_setobj(info->L, &p->k[i], info->L->top - 1);
2981+ lua_pop(info->L, 1); /* ... proto */
2982+ poppath(info);
2983+ }
2984+ poppath(info);
2985+
2986+ /* Read child protos. */
2987+ p->sizep = READ_VALUE(int);
2988+ eris_reallocvector(info->L, p->p, 0, p->sizep, Proto*);
2989+ /* Null all entries to avoid confusing the GC. */
2990+ memset(p->p, 0, p->sizep * sizeof(Proto*));
2991+ pushpath(info, ".protos");
2992+ for (i = 0, n = p->sizep; i < n; ++i) {
2993+ Proto *cp;
2994+ pushpath(info, "[%d]", i);
2995+ p->p[i] = eris_newproto(info->L);
2996+ lua_pushlightuserdata(info->L, p->p[i]); /* ... proto nproto */
2997+ unpersist(info); /* ... proto nproto nproto/oproto */
2998+ cp = lua_touserdata(info->L, -1);
2999+ if (cp != p->p[i]) { /* ... proto nproto oproto */
3000+ /* Just overwrite it, GC will clean this up. */
3001+ p->p[i] = cp;
3002+ }
3003+ lua_pop(info->L, 2); /* ... proto */
3004+ poppath(info);
3005+ }
3006+ poppath(info);
3007+
3008+ /* Read upvalues. */
3009+ p->sizeupvalues = READ_VALUE(int);
3010+ eris_reallocvector(info->L, p->upvalues, 0, p->sizeupvalues, Upvaldesc);
3011+ for (i = 0, n = p->sizeupvalues; i < n; ++i) {
3012+ p->upvalues[i].name = NULL;
3013+ p->upvalues[i].instack = READ_VALUE(uint8_t);
3014+ p->upvalues[i].idx = READ_VALUE(uint8_t);
3015+ }
3016+
3017+ /* Read debug information if any is present. */
3018+ if (!READ_VALUE(uint8_t)) {
3019+ return;
3020+ }
3021+
3022+ /* Read function source code. */
3023+ unpersist(info); /* ... proto str */
3024+ copytstring(info->L, &p->source);
3025+ lua_pop(info->L, 1); /* ... proto */
3026+
3027+ /* Read line information. */
3028+ p->sizelineinfo = READ_VALUE(int);
3029+ eris_reallocvector(info->L, p->lineinfo, 0, p->sizelineinfo, int);
3030+ READ(p->lineinfo, p->sizelineinfo, int);
3031+
3032+ /* Read locals info. */
3033+ p->sizelocvars = READ_VALUE(int);
3034+ eris_reallocvector(info->L, p->locvars, 0, p->sizelocvars, LocVar);
3035+ /* Null the variable names to avoid confusing the GC. */
3036+ for (i = 0, n = p->sizelocvars; i < n; ++i) {
3037+ p->locvars[i].varname = NULL;
3038+ }
3039+ pushpath(info, ".locvars");
3040+ for (i = 0, n = p->sizelocvars; i < n; ++i) {
3041+ pushpath(info, "[%d]", i);
3042+ p->locvars[i].startpc = READ_VALUE(int);
3043+ p->locvars[i].endpc = READ_VALUE(int);
3044+ unpersist(info); /* ... proto str */
3045+ copytstring(info->L, &p->locvars[i].varname);
3046+ lua_pop(info->L, 1); /* ... proto */
3047+ poppath(info);
3048+ }
3049+ poppath(info);
3050+
3051+ /* Read upvalue names. */
3052+ pushpath(info, ".upvalnames");
3053+ for (i = 0, n = p->sizeupvalues; i < n; ++i) {
3054+ pushpath(info, "[%d]", i);
3055+ unpersist(info); /* ... proto str */
3056+ copytstring(info->L, &p->upvalues[i].name);
3057+ lua_pop(info->L, 1); /* ... proto */
3058+ poppath(info);
3059+ }
3060+ poppath(info);
3061+ lua_pushvalue(info->L, -1); /* ... proto proto */
3062+
3063+ eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
3064+}
3065+
3066+/** ======================================================================== */
3067+
3068+static void
3069+p_upval(Info *info) { /* ... obj */
3070+ persist(info); /* ... obj */
3071+}
3072+
3073+static void
3074+u_upval(Info *info) { /* ... */
3075+ eris_checkstack(info->L, 2);
3076+
3077+ /* Create the table we use to store the pointer to the actual upval (1), the
3078+ * value of the upval (2) and any pointers to the pointer to the upval (3+).*/
3079+ lua_createtable(info->L, 3, 0); /* ... tbl */
3080+ registerobject(info);
3081+ unpersist(info); /* ... tbl obj */
3082+ lua_rawseti(info->L, -2, 1); /* ... tbl */
3083+
3084+ eris_assert(lua_type(info->L, -1) == LUA_TTABLE);
3085+}
3086+
3087+/** ======================================================================== */
3088+
3089+/* For Lua closures we write the upvalue ID, which is usually the memory
3090+ * address at which it is stored. This is used to tell which upvalues are
3091+ * identical when unpersisting. */
3092+/* In either case we store the upvale *values*, i.e. the actual objects they
3093+ * point to. As in Pluto, we will restore any upvalues of Lua closures as
3094+ * closed as first, i.e. the upvalue will store the TValue itself. When
3095+ * loading a thread containing the upvalue (meaning it's the actual owner of
3096+ * the upvalue) we open it, i.e. we point it to the thread's upvalue list.
3097+ * For C closures, upvalues are always closed. */
3098+static void
3099+p_closure(Info *info) { /* perms reftbl ... func */
3100+ int nup;
3101+ eris_checkstack(info->L, 2);
3102+ switch (ttype(info->L->top - 1)) {
3103+ case LUA_TLCF: /* light C function */
3104+ /* We cannot persist these, they have to be handled via the permtable. */
3105+ eris_error(info, "attempt to persist a light C function (%p)",
3106+ lua_tocfunction(info->L, -1));
3107+ return; /* not reached */
3108+ case LUA_TCCL: /* C closure */ { /* perms reftbl ... ccl */
3109+ CClosure *cl = clCvalue(info->L->top - 1);
3110+ /* Mark it as a C closure. */
3111+ WRITE_VALUE(true, uint8_t);
3112+ /* Write the upvalue count first, since we have to know it when creating
3113+ * a new closure when unpersisting. */
3114+ WRITE_VALUE(cl->nupvalues, uint8_t);
3115+
3116+ /* We can only persist these if the underlying C function is in the
3117+ * permtable. So we try to persist it first as a light C function. If it
3118+ * isn't in the permtable that'll cause an error (in the case above). */
3119+ lua_pushcfunction(info->L, lua_tocfunction(info->L, -1));
3120+ /* perms reftbl ... ccl cfunc */
3121+ persist(info); /* perms reftbl ... ccl cfunc */
3122+ lua_pop(info->L, 1); /* perms reftbl ... ccl */
3123+
3124+ /* Persist the upvalues. Since for C closures all upvalues are always
3125+ * closed we can just write the actual values. */
3126+ pushpath(info, ".upvalues");
3127+ for (nup = 1; nup <= cl->nupvalues; ++nup) {
3128+ pushpath(info, "[%d]", nup);
3129+ lua_getupvalue(info->L, -1, nup); /* perms reftbl ... ccl obj */
3130+ persist(info); /* perms reftbl ... ccl obj */
3131+ lua_pop(info->L, 1); /* perms reftbl ... ccl */
3132+ poppath(info);
3133+ }
3134+ poppath(info);
3135+ break;
3136+ }
3137+ case LUA_TLCL: /* Lua function */ { /* perms reftbl ... lcl */
3138+ LClosure *cl = clLvalue(info->L->top - 1);
3139+ /* Mark it as a Lua closure. */
3140+ WRITE_VALUE(false, uint8_t);
3141+ /* Write the upvalue count first, since we have to know it when creating
3142+ * a new closure when unpersisting. */
3143+ WRITE_VALUE(cl->nupvalues, uint8_t);
3144+
3145+ /* Persist the function's prototype. Pass the proto as a parameter to
3146+ * p_proto so that it can access it and register it in the ref table. */
3147+ pushpath(info, ".proto");
3148+ lua_pushlightuserdata(info->L, cl->p); /* perms reftbl ... lcl proto */
3149+ lua_pushvalue(info->L, -1); /* perms reftbl ... lcl proto proto */
3150+ persist_keyed(info, LUA_TPROTO); /* perms reftbl ... lcl proto */
3151+ lua_pop(info->L, 1); /* perms reftbl ... lcl */
3152+ poppath(info);
3153+
3154+ /* Persist the upvalues. We pretend to write these as their own type,
3155+ * to get proper identity preservation. We also pass them as a parameter
3156+ * to p_upval so it can register the upvalue in the reference table. */
3157+ pushpath(info, ".upvalues");
3158+ for (nup = 1; nup <= cl->nupvalues; ++nup) {
3159+ const char *name = lua_getupvalue(info->L, -1, nup);
3160+ /* perms reftbl ... lcl obj */
3161+ pushpath(info, ".%s", name);
3162+ lua_pushlightuserdata(info->L, lua_upvalueid(info->L, -2, nup));
3163+ /* perms reftbl ... lcl obj id */
3164+ persist_keyed(info, LUA_TUPVAL); /* perms reftbl ... lcl obj */
3165+ lua_pop(info->L, 1); /* perms reftble ... lcl */
3166+ poppath(info);
3167+ }
3168+ poppath(info);
3169+ break;
3170+ }
3171+ default:
3172+ eris_error(info, "attempt to persist unknown function type");
3173+ return; /* not reached */
3174+ }
3175+}
3176+
3177+static void
3178+u_closure(Info *info) { /* ... */
3179+ int nup;
3180+ bool isCClosure = READ_VALUE(uint8_t);
3181+ lu_byte nups = READ_VALUE(uint8_t);
3182+ if (isCClosure) {
3183+ lua_CFunction f;
3184+
3185+ /* Reserve reference for the closure to avoid light C function or its
3186+ * perm table key going first. */
3187+ const int reference = ++(info->refcount);
3188+
3189+ /* nups is guaranteed to be >= 1, otherwise it'd be a light C function. */
3190+ eris_checkstack(info->L, nups < 2 ? 2 : nups);
3191+
3192+ /* Read the C function from the permanents table. */
3193+ unpersist(info); /* ... cfunc */
3194+ if (!lua_iscfunction(info->L, -1)) {
3195+ eris_error(info, "bad C closure (C function expected, got %s)",
3196+ kTypenames[lua_type(info->L, -1)]);
3197+ }
3198+ f = lua_tocfunction(info->L, -1);
3199+ if (!f) {
3200+ eris_error(info, "bad C closure (C function expected, got null)");
3201+ }
3202+ lua_pop(info->L, 1); /* ... */
3203+
3204+ /* Now this is a little roundabout, but we want to create the closure
3205+ * before unpersisting the actual upvalues to avoid cycles. So we have to
3206+ * create it with all nil first, then fill the upvalues in afterwards. */
3207+ for (nup = 1; nup <= nups; ++nup) {
3208+ lua_pushnil(info->L); /* ... nil[1] ... nil[nup] */
3209+ }
3210+ lua_pushcclosure(info->L, f, nups); /* ... ccl */
3211+
3212+ /* Create the entry in the reftable. */
3213+ lua_pushvalue(info->L, -1); /* perms reftbl ... ccl ccl */
3214+ lua_rawseti(info->L, REFTIDX, reference); /* perms reftbl ... ccl */
3215+
3216+ /* Unpersist actual upvalues. */
3217+ pushpath(info, ".upvalues");
3218+ for (nup = 1; nup <= nups; ++nup) {
3219+ pushpath(info, "[%d]", nup);
3220+ unpersist(info); /* ... ccl obj */
3221+ lua_setupvalue(info->L, -2, nup); /* ... ccl */
3222+ poppath(info);
3223+ }
3224+ poppath(info);
3225+ }
3226+ else {
3227+ Closure *cl;
3228+ Proto *p;
3229+
3230+ eris_checkstack(info->L, 4);
3231+
3232+ /* Create closure and anchor it on the stack (avoid collection via GC). */
3233+ cl = eris_newLclosure(info->L, nups);
3234+ eris_setclLvalue(info->L, info->L->top, cl); /* ... lcl */
3235+ eris_incr_top(info->L);
3236+
3237+ /* Preregister closure for handling of cycles (upvalues). */
3238+ registerobject(info);
3239+
3240+ /* Read prototype. In general, we create protos (and upvalues) before
3241+ * trying to read them and pass a pointer to the instance along to the
3242+ * unpersist function. This way the instance is safely hooked up to an
3243+ * object, so we don't have to worry about it getting GCed. */
3244+ pushpath(info, ".proto");
3245+ cl->l.p = eris_newproto(info->L);
3246+ /* Push the proto into which to unpersist as a parameter to u_proto. */
3247+ lua_pushlightuserdata(info->L, cl->l.p); /* ... lcl nproto */
3248+ unpersist(info); /* ... lcl nproto nproto/oproto */
3249+ eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
3250+ /* The proto we have now may differ, if we already unpersisted it before.
3251+ * In that case we now have a reference to the originally unpersisted
3252+ * proto so we'll use that. */
3253+ p = lua_touserdata(info->L, -1);
3254+ if (p != cl->l.p) { /* ... lcl nproto oproto */
3255+ /* Just overwrite the old one, GC will clean this up. */
3256+ cl->l.p = p;
3257+ }
3258+ lua_pop(info->L, 2); /* ... lcl */
3259+ eris_assert(cl->l.p->sizeupvalues == nups);
3260+ poppath(info);
3261+
3262+ /* Unpersist all upvalues. */
3263+ pushpath(info, ".upvalues");
3264+ for (nup = 1; nup <= nups; ++nup) {
3265+ UpVal **uv = &cl->l.upvals[nup - 1];
3266+ /* Get the actual name of the upvalue, if possible. */
3267+ if (p->upvalues[nup - 1].name) {
3268+ pushpath(info, "[%s]", getstr(p->upvalues[nup - 1].name));
3269+ }
3270+ else {
3271+ pushpath(info, "[%d]", nup);
3272+ }
3273+ unpersist(info); /* ... lcl tbl */
3274+ eris_assert(lua_type(info->L, -1) == LUA_TTABLE);
3275+ lua_rawgeti(info->L, -1, 2); /* ... lcl tbl upval/nil */
3276+ if (lua_isnil(info->L, -1)) { /* ... lcl tbl nil */
3277+ lua_pop(info->L, 1); /* ... lcl tbl */
3278+ *uv = eris_newupval(info->L);
3279+ lua_pushlightuserdata(info->L, *uv); /* ... lcl tbl upval */
3280+ lua_rawseti(info->L, -2, 2); /* ... lcl tbl */
3281+ }
3282+ else { /* ... lcl tbl upval */
3283+ eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
3284+ *uv = (UpVal*)lua_touserdata(info->L, -1);
3285+ lua_pop(info->L, 1); /* ... lcl tbl */
3286+ }
3287+
3288+ /* Set the upvalue's actual value and add our reference to the upvalue to
3289+ * the list, for pointer patching if we have to open the upvalue in
3290+ * u_thread. Either is only necessary if the upvalue is still closed. */
3291+ if ((*uv)->v == &(*uv)->u.value) {
3292+ /* Always update the value of the upvalue's value for closed upvalues,
3293+ * even if we re-used one - if we had a cycle, it might have been
3294+ * incorrectly initialized to nil before. */
3295+ lua_rawgeti(info->L, -1, 1); /* ... lcl tbl obj */
3296+ eris_setobj(info->L, &(*uv)->u.value, info->L->top - 1);
3297+ lua_pop(info->L, 1); /* ... lcl tbl */
3298+
3299+ lua_pushlightuserdata(info->L, uv); /* ... lcl tbl upvalp */
3300+ if (luaL_len(info->L, -2) >= 2) {
3301+ /* Got a valid sequence, insert at the end. */
3302+ lua_rawseti(info->L, -2, luaL_len(info->L, -2) + 1); /* ... lcl tbl */
3303+ }
3304+ else { /* ... lcl tbl upvalp */
3305+ int i;
3306+ /* Find where to insert. This can happen if we have cycles, in which
3307+ * case the table is not fully initialized at this point, i.e. the
3308+ * value is not in it, yet (we work around that by always setting it,
3309+ * as seen above). */
3310+ for (i = 3;; ++i) {
3311+ lua_rawgeti(info->L, -2, i); /* ... lcl tbl upvalp upvalp/nil */
3312+ if (lua_isnil(info->L, -1)) { /* ... lcl tbl upvalp nil */
3313+ lua_pop(info->L, 1); /* ... lcl tbl upvalp */
3314+ lua_rawseti(info->L, -2, i); /* ... lcl tbl */
3315+ break;
3316+ }
3317+ else {
3318+ lua_pop(info->L, 1); /* ... lcl tbl upvalp */
3319+ }
3320+ } /* ... lcl tbl */
3321+ }
3322+ }
3323+
3324+ lua_pop(info->L, 1); /* ... lcl */
3325+ poppath(info);
3326+ }
3327+ poppath(info);
3328+
3329+ eris_barrierproto(info->L, p, cl);
3330+ p->cache = cl; /* save it in cache for reuse, see lvm.c:416 */
3331+ }
3332+
3333+ eris_assert(lua_type(info->L, -1) == LUA_TFUNCTION);
3334+}
3335+
3336+/** ======================================================================== */
3337+
3338+static void
3339+p_thread(Info *info) { /* ... thread */
3340+ lua_State* thread = lua_tothread(info->L, -1);
3341+ size_t level;
3342+ StkId o;
3343+ CallInfo *ci;
3344+ UpVal *uv;
3345+
3346+ eris_checkstack(info->L, 2);
3347+
3348+ /* We cannot persist any running threads, because by definition we *are* that
3349+ * running thread. And we use the stack. So yeah, really not a good idea. */
3350+ if (thread == info->L) {
3351+ eris_error(info, "cannot persist currently running thread");
3352+ return; /* not reached */
3353+ }
3354+
3355+ /* Persist the stack. Save the total size and used space first. */
3356+ WRITE_VALUE(thread->stacksize, int);
3357+ WRITE_VALUE(thread->top - thread->stack, size_t);
3358+
3359+ /* The Lua stack looks like this:
3360+ * stack ... top ... stack_last
3361+ * Where stack <= top <= stack_last, and "top" actually being the first free
3362+ * element, i.e. there's nothing stored there. So we stop one below that. */
3363+ pushpath(info, ".stack");
3364+ lua_pushnil(info->L); /* ... thread nil */
3365+ level = 0;
3366+ for (o = thread->stack; o < thread->top; ++o) {
3367+ pushpath(info, "[%d]", level++);
3368+ eris_setobj(info->L, info->L->top - 1, o); /* ... thread obj */
3369+ persist(info); /* ... thread obj */
3370+ poppath(info);
3371+ }
3372+ lua_pop(info->L, 1); /* ... thread */
3373+ poppath(info);
3374+
3375+ /* If the thread isn't running this must be the default value, which is 1. */
3376+ eris_assert(thread->nny == 1);
3377+
3378+ /* Error jump info should only be set while thread is running. */
3379+ eris_assert(thread->errorJmp == NULL);
3380+
3381+ /* thread->oldpc always seems to be uninitialized, at least gdb always shows
3382+ * it as 0xbaadf00d when I set a breakpoint here. */
3383+
3384+ /* Write general information. */
3385+ WRITE_VALUE(thread->status, uint8_t);
3386+ WRITE_VALUE(eris_savestackidx(thread,
3387+ eris_restorestack(thread, thread->errfunc)), size_t);
3388+ /* These are only used while a thread is being executed or can be deduced:
3389+ WRITE_VALUE(thread->nCcalls, uint16_t);
3390+ WRITE_VALUE(thread->allowhook, uint8_t); */
3391+
3392+ /* Hooks are not supported, bloody can of worms, those.
3393+ WRITE_VALUE(thread->hookmask, uint8_t);
3394+ WRITE_VALUE(thread->basehookcount, int);
3395+ WRITE_VALUE(thread->hookcount, int); */
3396+
3397+ if (thread->hook) {
3398+ /* TODO Warn that hooks are not persisted? */
3399+ }
3400+
3401+ /* Write call information (stack frames). In 5.2 CallInfo is stored in a
3402+ * linked list that originates in thead.base_ci. Upon initialization the
3403+ * thread.ci is set to thread.base_ci. During thread calls this is extended
3404+ * and always represents the tail of the callstack, though not necessarily of
3405+ * the linked list (which can be longer if the callstack was deeper earlier,
3406+ * but shrunk due to returns). */
3407+ pushpath(info, ".callinfo");
3408+ level = 0;
3409+ eris_assert(&thread->base_ci != thread->ci->next);
3410+ for (ci = &thread->base_ci; ci != thread->ci->next; ci = ci->next) {
3411+ pushpath(info, "[%d]", level++);
3412+ WRITE_VALUE(eris_savestackidx(thread, ci->func), size_t);
3413+ WRITE_VALUE(eris_savestackidx(thread, ci->top), size_t);
3414+ WRITE_VALUE(ci->nresults, int16_t);
3415+ WRITE_VALUE(ci->callstatus, uint8_t);
3416+ /* CallInfo.extra is used in two contexts: if L->status == LUA_YIELD and
3417+ * CallInfo is the one stored as L->ci, in which case ci->extra refers to
3418+ * the original value of L->ci->func, and when we have a yieldable pcall
3419+ * (ci->callstatus & CIST_YPCALL) where ci->extra holds the stack level
3420+ * of the function being called (see lua_pcallk). We save the ci->extra
3421+ * for L->ci after the loop, because we won't know which one it is when
3422+ * unpersisting. */
3423+ if (ci->callstatus & CIST_YPCALL) {
3424+ WRITE_VALUE(eris_savestackidx(thread,
3425+ eris_restorestack(thread, ci->extra)), size_t);
3426+ }
3427+
3428+ eris_assert(eris_isLua(ci) || (ci->callstatus & CIST_TAIL) == 0);
3429+ if (ci->callstatus & CIST_HOOKYIELD) {
3430+ eris_error(info, "cannot persist yielded hooks");
3431+ }
3432+
3433+ if (eris_isLua(ci)) {
3434+ const LClosure *lcl = eris_ci_func(ci);
3435+ WRITE_VALUE(eris_savestackidx(thread, ci->u.l.base), size_t);
3436+ WRITE_VALUE(ci->u.l.savedpc - lcl->p->code, size_t);
3437+ }
3438+ else {
3439+ WRITE_VALUE(ci->u.c.status, uint8_t);
3440+
3441+ /* These are only used while a thread is being executed:
3442+ WRITE_VALUE(ci->u.c.old_errfunc, ptrdiff_t);
3443+ WRITE_VALUE(ci->u.c.old_allowhook, uint8_t); */
3444+
3445+ /* TODO Is this really right? Hooks may be a problem? */
3446+ if (ci->callstatus & (CIST_YPCALL | CIST_YIELDED)) {
3447+ WRITE_VALUE(ci->u.c.ctx, int);
3448+ eris_assert(ci->u.c.k);
3449+ lua_pushcfunction(info->L, ci->u.c.k); /* ... thread func */
3450+ persist(info); /* ... thread func/nil */
3451+ lua_pop(info->L, 1); /* ... thread */
3452+ }
3453+ }
3454+
3455+ /* Write whether there's more to come. */
3456+ WRITE_VALUE(ci->next == thread->ci->next, uint8_t);
3457+
3458+ poppath(info);
3459+ }
3460+ /** See comment on ci->extra in loop. */
3461+ if (thread->status == LUA_YIELD) {
3462+ WRITE_VALUE(eris_savestackidx(thread,
3463+ eris_restorestack(thread, thread->ci->extra)), size_t);
3464+ }
3465+ poppath(info);
3466+
3467+ pushpath(info, ".openupval");
3468+ lua_pushnil(info->L); /* ... thread nil */
3469+ level = 0;
3470+ for (uv = eris_gco2uv(thread->openupval);
3471+ uv != NULL;
3472+ uv = eris_gco2uv(eris_gch(eris_obj2gco(uv))->next))
3473+ {
3474+ pushpath(info, "[%d]", level);
3475+ WRITE_VALUE(eris_savestackidx(thread, uv->v) + 1, size_t);
3476+ eris_setobj(info->L, info->L->top - 1, uv->v); /* ... thread obj */
3477+ lua_pushlightuserdata(info->L, uv); /* ... thread obj id */
3478+ persist_keyed(info, LUA_TUPVAL); /* ... thread obj */
3479+ poppath(info);
3480+ }
3481+ WRITE_VALUE(0, size_t);
3482+ lua_pop(info->L, 1); /* ... thread */
3483+ poppath(info);
3484+}
3485+
3486+/* Used in u_thread to validate read stack positions. */
3487+#define validate(stackpos, inclmax) \
3488+ if ((stackpos) < thread->stack || stackpos > (inclmax)) { \
3489+ (stackpos) = thread->stack; \
3490+ eris_error(info, "stack index out of bounds"); }
3491+
3492+/* I had so hoped to get by without any 'hacks', but I surrender. We mark the
3493+ * thread as incomplete to avoid the GC messing with it while we're building
3494+ * it. Otherwise it may try to shrink its stack. We do this by setting its
3495+ * stack field to null for every call that may trigger a GC run, since that
3496+ * field is what's used to determine whether threads should be shrunk. See
3497+ * lgc.c:699. Some of the locks could probably be joined (since nothing
3498+ * inbetween requires the stack field to be valid), but I prefer to keep the
3499+ * "invalid" blocks as small as possible to make it clearer. Also, locking and
3500+ * unlocking are really just variable assignments, so they're really cheap. */
3501+#define LOCK(L) (L->stack = NULL)
3502+#define UNLOCK(L) (L->stack = stack)
3503+
3504+static void
3505+u_thread(Info *info) { /* ... */
3506+ lua_State* thread;
3507+ size_t level;
3508+ StkId stack, o;
3509+
3510+ eris_checkstack(info->L, 3);
3511+
3512+ thread = lua_newthread(info->L); /* ... thread */
3513+ registerobject(info);
3514+
3515+ /* Unpersist the stack. Read size first and adjust accordingly. */
3516+ eris_reallocstack(thread, READ_VALUE(int));
3517+ stack = thread->stack; /* After the realloc in case the address changes. */
3518+ thread->top = thread->stack + READ_VALUE(size_t);
3519+ validate(thread->top, thread->stack_last);
3520+
3521+ /* Read the elements one by one. */
3522+ LOCK(thread);
3523+ pushpath(info, ".stack");
3524+ UNLOCK(thread);
3525+ level = 0;
3526+ for (o = stack; o < thread->top; ++o) {
3527+ LOCK(thread);
3528+ pushpath(info, "[%d]", level++);
3529+ unpersist(info); /* ... thread obj */
3530+ UNLOCK(thread);
3531+ eris_setobj(thread, o, info->L->top - 1);
3532+ lua_pop(info->L, 1); /* ... thread */
3533+ LOCK(thread);
3534+ poppath(info);
3535+ UNLOCK(thread);
3536+ }
3537+ LOCK(thread);
3538+ poppath(info);
3539+ UNLOCK(thread);
3540+
3541+ /* As in p_thread, just to make sure. */
3542+ eris_assert(thread->nny == 1);
3543+ eris_assert(thread->errorJmp == NULL);
3544+ eris_assert(thread->hook == NULL);
3545+
3546+ /* See comment in persist. */
3547+ thread->oldpc = NULL;
3548+
3549+ /* Read general information. */
3550+ thread->status = READ_VALUE(uint8_t);
3551+ thread->errfunc = eris_savestack(thread,
3552+ eris_restorestackidx(thread, READ_VALUE(size_t)));
3553+ if (thread->errfunc) {
3554+ o = eris_restorestack(thread, thread->errfunc);
3555+ validate(o, thread->top);
3556+ if (eris_ttypenv(o) != LUA_TFUNCTION) {
3557+ eris_error(info, "invalid errfunc");
3558+ }
3559+ }
3560+ /* These are only used while a thread is being executed or can be deduced:
3561+ thread->nCcalls = READ_VALUE(uint16_t);
3562+ thread->allowhook = READ_VALUE(uint8_t); */
3563+ eris_assert(thread->allowhook == 1);
3564+
3565+ /* Not supported.
3566+ thread->hookmask = READ_VALUE(uint8_t);
3567+ thread->basehookcount = READ_VALUE(int);
3568+ thread->hookcount = READ_VALUE(int); */
3569+
3570+ /* Read call information (stack frames). */
3571+ LOCK(thread);
3572+ pushpath(info, ".callinfo");
3573+ UNLOCK(thread);
3574+ thread->ci = &thread->base_ci;
3575+ level = 0;
3576+ for (;;) {
3577+ LOCK(thread);
3578+ pushpath(info, "[%d]", level++);
3579+ UNLOCK(thread);
3580+ thread->ci->func = eris_restorestackidx(thread, READ_VALUE(size_t));
3581+ validate(thread->ci->func, thread->top - 1);
3582+ thread->ci->top = eris_restorestackidx(thread, READ_VALUE(size_t));
3583+ validate(thread->ci->top, thread->stack_last);
3584+ thread->ci->nresults = READ_VALUE(int16_t);
3585+ thread->ci->callstatus = READ_VALUE(uint8_t);
3586+ /** See comment in p_thread. */
3587+ if (thread->ci->callstatus & CIST_YPCALL) {
3588+ thread->ci->extra = eris_savestack(thread,
3589+ eris_restorestackidx(thread, READ_VALUE(size_t)));
3590+ o = eris_restorestack(thread, thread->ci->extra);
3591+ validate(o, thread->top);
3592+ if (eris_ttypenv(o) != LUA_TFUNCTION) {
3593+ eris_error(info, "invalid callinfo");
3594+ }
3595+ }
3596+
3597+ if (eris_isLua(thread->ci)) {
3598+ LClosure *lcl = eris_ci_func(thread->ci);
3599+ thread->ci->u.l.base = eris_restorestackidx(thread, READ_VALUE(size_t));
3600+ validate(thread->ci->u.l.base, thread->top);
3601+ thread->ci->u.l.savedpc = lcl->p->code + READ_VALUE(size_t);
3602+ if (thread->ci->u.l.savedpc < lcl->p->code ||
3603+ thread->ci->u.l.savedpc > lcl->p->code + lcl->p->sizecode)
3604+ {
3605+ thread->ci->u.l.savedpc = lcl->p->code; /* Just to be safe. */
3606+ eris_error(info, "saved program counter out of bounds");
3607+ }
3608+ }
3609+ else {
3610+ thread->ci->u.c.status = READ_VALUE(uint8_t);
3611+
3612+ /* These are only used while a thread is being executed:
3613+ thread->ci->u.c.old_errfunc = READ_VALUE(ptrdiff_t);
3614+ thread->ci->u.c.old_allowhook = READ_VALUE(uint8_t); */
3615+ thread->ci->u.c.old_errfunc = 0;
3616+ thread->ci->u.c.old_allowhook = 0;
3617+
3618+ /* TODO Is this really right? */
3619+ if (thread->ci->callstatus & (CIST_YPCALL | CIST_YIELDED)) {
3620+ thread->ci->u.c.ctx = READ_VALUE(int);
3621+ LOCK(thread);
3622+ unpersist(info); /* ... thread func? */
3623+ UNLOCK(thread);
3624+ if (lua_iscfunction(info->L, -1)) { /* ... thread func */
3625+ thread->ci->u.c.k = lua_tocfunction(info->L, -1);
3626+ }
3627+ else {
3628+ eris_error(info, "bad C continuation function");
3629+ return; /* not reached */
3630+ }
3631+ lua_pop(info->L, 1); /* ... thread */
3632+ }
3633+ else {
3634+ thread->ci->u.c.ctx = 0;
3635+ thread->ci->u.c.k = NULL;
3636+ }
3637+ }
3638+ LOCK(thread);
3639+ poppath(info);
3640+ UNLOCK(thread);
3641+
3642+ /* Read in value for check for next iteration. */
3643+ if (READ_VALUE(uint8_t)) {
3644+ break;
3645+ }
3646+ else {
3647+ thread->ci = eris_extendCI(thread);
3648+ }
3649+ }
3650+ if (thread->status == LUA_YIELD) {
3651+ thread->ci->extra = eris_savestack(thread,
3652+ eris_restorestackidx(thread, READ_VALUE(size_t)));
3653+ o = eris_restorestack(thread, thread->ci->extra);
3654+ validate(o, thread->top);
3655+ if (eris_ttypenv(o) != LUA_TFUNCTION) {
3656+ eris_error(info, "invalid callinfo");
3657+ }
3658+ }
3659+ LOCK(thread);
3660+ poppath(info);
3661+ UNLOCK(thread);
3662+
3663+ /* Get from context: only zero for dead threads, otherwise one. */
3664+ thread->nCcalls = thread->status != LUA_OK || lua_gettop(thread) != 0;
3665+
3666+ /* Proceed to open upvalues. These upvalues will already exist due to the
3667+ * functions using them having been unpersisted (they'll usually be in the
3668+ * stack of the thread). For this reason we store all previous references to
3669+ * the upvalue in a table that is returned when we try to unpersist an
3670+ * upvalue, so that we can adjust these pointers in here. */
3671+ LOCK(thread);
3672+ pushpath(info, ".openupval");
3673+ UNLOCK(thread);
3674+ level = 0;
3675+ for (;;) {
3676+ UpVal *nuv;
3677+ StkId stk;
3678+ /* Get the position of the upvalue on the stack. As a special value we pass
3679+ * zero to indicate there are no more upvalues. */
3680+ const size_t offset = READ_VALUE(size_t);
3681+ if (offset == 0) {
3682+ break;
3683+ }
3684+ LOCK(thread);
3685+ pushpath(info, "[%d]", level);
3686+ UNLOCK(thread);
3687+ stk = eris_restorestackidx(thread, offset - 1);
3688+ validate(stk, thread->top - 1);
3689+ LOCK(thread);
3690+ unpersist(info); /* ... thread tbl */
3691+ UNLOCK(thread);
3692+ eris_assert(lua_type(info->L, -1) == LUA_TTABLE);
3693+
3694+ /* Create the open upvalue either way. */
3695+ LOCK(thread);
3696+ nuv = eris_findupval(thread, stk);
3697+ UNLOCK(thread);
3698+
3699+ /* Then check if we need to patch some pointers. */
3700+ lua_rawgeti(info->L, -1, 2); /* ... thread tbl upval/nil */
3701+ if (!lua_isnil(info->L, -1)) { /* ... thread tbl upval */
3702+ int i, n;
3703+ eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
3704+ /* Already exists, replace it. To do this we have to patch all the
3705+ * pointers to the already existing one, which we added to the table in
3706+ * u_closure, starting at index 3. */
3707+ lua_pop(info->L, 1); /* ... thread tbl */
3708+ for (i = 3, n = luaL_len(info->L, -1); i <= n; ++i) {
3709+ lua_rawgeti(info->L, -1, i); /* ... thread tbl upvalp */
3710+ (*(UpVal**)lua_touserdata(info->L, -1)) = nuv;
3711+ lua_pop(info->L, 1); /* ... thread tbl */
3712+ }
3713+ }
3714+ else { /* ... thread tbl nil */
3715+ eris_assert(lua_isnil(info->L, -1));
3716+ lua_pop(info->L, 1); /* ... thread tbl */
3717+ }
3718+
3719+ /* Store open upvalue in table for future references. */
3720+ LOCK(thread);
3721+ lua_pushlightuserdata(info->L, nuv); /* ... thread tbl upval */
3722+ lua_rawseti(info->L, -2, 2); /* ... thread tbl */
3723+ lua_pop(info->L, 1); /* ... thread */
3724+ poppath(info);
3725+ UNLOCK(thread);
3726+ }
3727+ poppath(info);
3728+
3729+ eris_assert(lua_type(info->L, -1) == LUA_TTHREAD);
3730+}
3731+
3732+#undef UNLOCK
3733+#undef LOCK
3734+
3735+#undef validate
3736+
3737+/*
3738+** ============================================================================
3739+** Top-level delegator.
3740+** ============================================================================
3741+*/
3742+
3743+static void
3744+persist_typed(Info *info, int type) { /* perms reftbl ... obj */
3745+ eris_ifassert(const int top = lua_gettop(info->L));
3746+ if (info->level >= info->maxComplexity) {
3747+ eris_error(info, "object too complex");
3748+ }
3749+ ++info->level;
3750+
3751+ WRITE_VALUE(type, int);
3752+ switch(type) {
3753+ case LUA_TBOOLEAN:
3754+ p_boolean(info);
3755+ break;
3756+ case LUA_TLIGHTUSERDATA:
3757+ p_pointer(info);
3758+ break;
3759+ case LUA_TNUMBER:
3760+ p_number(info);
3761+ break;
3762+ case LUA_TSTRING:
3763+ p_string(info);
3764+ break;
3765+ case LUA_TTABLE:
3766+ p_table(info);
3767+ break;
3768+ case LUA_TFUNCTION:
3769+ p_closure(info);
3770+ break;
3771+ case LUA_TUSERDATA:
3772+ p_userdata(info);
3773+ break;
3774+ case LUA_TTHREAD:
3775+ p_thread(info);
3776+ break;
3777+ case LUA_TPROTO:
3778+ p_proto(info);
3779+ break;
3780+ case LUA_TUPVAL:
3781+ p_upval(info);
3782+ break;
3783+ default:
3784+ eris_error(info, "trying to persist unknown type");
3785+ } /* perms reftbl ... obj */
3786+
3787+ --info->level;
3788+ eris_assert(top == lua_gettop(info->L));
3789+}
3790+
3791+/* Second-level delegating persist function, used for cases when persisting
3792+ * data that's stored in the reftable with a key that is not the data itself,
3793+ * namely upvalues and protos. */
3794+static void
3795+persist_keyed(Info *info, int type) { /* perms reftbl ... obj refkey */
3796+ eris_checkstack(info->L, 2);
3797+
3798+ /* Keep a copy of the key for pushing it to the reftable, if necessary. */
3799+ lua_pushvalue(info->L, -1); /* perms reftbl ... obj refkey refkey */
3800+
3801+ /* If the object has already been written, write a reference to it. */
3802+ lua_rawget(info->L, REFTIDX); /* perms reftbl ... obj refkey ref? */
3803+ if (!lua_isnil(info->L, -1)) { /* perms reftbl ... obj refkey ref */
3804+ const int reference = lua_tointeger(info->L, -1);
3805+ WRITE_VALUE(reference + ERIS_REFERENCE_OFFSET, int);
3806+ lua_pop(info->L, 2); /* perms reftbl ... obj */
3807+ return;
3808+ } /* perms reftbl ... obj refkey nil */
3809+ lua_pop(info->L, 1); /* perms reftbl ... obj refkey */
3810+
3811+ /* Copy the refkey for the perms check below. */
3812+ lua_pushvalue(info->L, -1); /* perms reftbl ... obj refkey refkey */
3813+
3814+ /* Put the value in the reference table. This creates an entry pointing from
3815+ * the object (or its key) to the id the object is referenced by. */
3816+ lua_pushinteger(info->L, ++(info->refcount));
3817+ /* perms reftbl ... obj refkey refkey ref */
3818+ lua_rawset(info->L, REFTIDX); /* perms reftbl ... obj refkey */
3819+
3820+ /* At this point, we'll give the permanents table a chance to play. */
3821+ lua_gettable(info->L, PERMIDX); /* perms reftbl ... obj permkey? */
3822+ if (!lua_isnil(info->L, -1)) { /* perms reftbl ... obj permkey */
3823+ type = lua_type(info->L, -2);
3824+ /* Prepend permanent "type" so that we know it's a permtable key. This will
3825+ * trigger u_permanent when unpersisting. Also write the original type, so
3826+ * that we can verify what we get in the permtable when unpersisting is of
3827+ * the same kind we had when persisting. */
3828+ WRITE_VALUE(ERIS_PERMANENT, int);
3829+ WRITE_VALUE(type, uint8_t);
3830+ persist(info); /* perms reftbl ... obj permkey */
3831+ lua_pop(info->L, 1); /* perms reftbl ... obj */
3832+ }
3833+ else { /* perms reftbl ... obj nil */
3834+ /* No entry in the permtable for this object, persist it directly. */
3835+ lua_pop(info->L, 1); /* perms reftbl ... obj */
3836+ persist_typed(info, type); /* perms reftbl ... obj */
3837+ } /* perms reftbl ... obj */
3838+}
3839+
3840+/* Top-level delegating persist function. */
3841+static void
3842+persist(Info *info) { /* perms reftbl ... obj */
3843+ /* Grab the object's type. */
3844+ const int type = lua_type(info->L, -1);
3845+
3846+ /* If the object is nil, only write its type. */
3847+ if (type == LUA_TNIL) {
3848+ WRITE_VALUE(type, int);
3849+ }
3850+ /* Write simple values directly, because writing a "reference" would take up
3851+ * just as much space and we can save ourselves work this way. */
3852+ else if (type == LUA_TBOOLEAN ||
3853+ type == LUA_TLIGHTUSERDATA ||
3854+ type == LUA_TNUMBER)
3855+ {
3856+ persist_typed(info, type); /* perms reftbl ... obj */
3857+ }
3858+ /* For all non-simple values we keep a record in the reftable, so that we
3859+ * keep references alive across persisting and unpersisting an object. This
3860+ * has the nice side-effect of saving some space. */
3861+ else {
3862+ eris_checkstack(info->L, 1);
3863+ lua_pushvalue(info->L, -1); /* perms reftbl ... obj obj */
3864+ persist_keyed(info, type); /* perms reftbl ... obj */
3865+ }
3866+}
3867+
3868+/** ======================================================================== */
3869+
3870+static void
3871+u_permanent(Info *info) { /* perms reftbl ... */
3872+ const int type = READ_VALUE(uint8_t);
3873+ /* Reserve reference to avoid the key going first. */
3874+ const int reference = ++(info->refcount);
3875+ eris_checkstack(info->L, 1);
3876+ unpersist(info); /* perms reftbl ... permkey */
3877+ lua_gettable(info->L, PERMIDX); /* perms reftbl ... obj? */
3878+ if (lua_isnil(info->L, -1)) { /* perms reftbl ... nil */
3879+ /* Since we may need permanent values to rebuild other structures, namely
3880+ * closures and threads, we cannot allow perms to fail unpersisting. */
3881+ eris_error(info, "bad permanent value (no value)");
3882+ }
3883+ else if (lua_type(info->L, -1) != type) { /* perms reftbl ... :( */
3884+ /* For the same reason that we cannot allow nil we must also require the
3885+ * unpersisted value to be of the correct type. */
3886+ eris_error(info, "bad permanent value (%s expected, got %s)",
3887+ kTypenames[type], kTypenames[lua_type(info->L, -1)]);
3888+ } /* perms reftbl ... obj */
3889+ /* Create the entry in the reftable. */
3890+ lua_pushvalue(info->L, -1); /* perms reftbl ... obj obj */
3891+ lua_rawseti(info->L, REFTIDX, reference); /* perms reftbl ... obj */
3892+}
3893+
3894+static void
3895+unpersist(Info *info) { /* perms reftbl ... */
3896+ eris_ifassert(const int top = lua_gettop(info->L));
3897+ if (info->level >= info->maxComplexity) {
3898+ eris_error(info, "object too complex");
3899+ }
3900+ ++info->level;
3901+
3902+ eris_checkstack(info->L, 1);
3903+ {
3904+ const int typeOrReference = READ_VALUE(int);
3905+ if (typeOrReference > ERIS_REFERENCE_OFFSET) {
3906+ const int reference = typeOrReference - ERIS_REFERENCE_OFFSET;
3907+ lua_rawgeti(info->L, REFTIDX, reference); /* perms reftbl ud ... obj? */
3908+ if (lua_isnil(info->L, -1)) { /* perms reftbl ud ... :( */
3909+ eris_error(info, "invalid reference #%d", reference);
3910+ } /* perms reftbl ud ... obj */
3911+ }
3912+ else {
3913+ const int type = typeOrReference;
3914+ switch (type) {
3915+ case LUA_TNIL:
3916+ lua_pushnil(info->L);
3917+ break;
3918+ case LUA_TBOOLEAN:
3919+ u_boolean(info);
3920+ break;
3921+ case LUA_TLIGHTUSERDATA:
3922+ u_pointer(info);
3923+ break;
3924+ case LUA_TNUMBER:
3925+ u_number(info);
3926+ break;
3927+ case LUA_TSTRING:
3928+ u_string(info);
3929+ break;
3930+ case LUA_TTABLE:
3931+ u_table(info);
3932+ break;
3933+ case LUA_TFUNCTION:
3934+ u_closure(info);
3935+ break;
3936+ case LUA_TUSERDATA:
3937+ u_userdata(info);
3938+ break;
3939+ case LUA_TTHREAD:
3940+ u_thread(info);
3941+ break;
3942+ case LUA_TPROTO:
3943+ u_proto(info);
3944+ break;
3945+ case LUA_TUPVAL:
3946+ u_upval(info);
3947+ break;
3948+ case ERIS_PERMANENT:
3949+ u_permanent(info);
3950+ break;
3951+ default:
3952+ eris_error(info, "trying to unpersist unknown type %d", type);
3953+ } /* perms reftbl ... obj? */
3954+ }
3955+ }
3956+
3957+ --info->level;
3958+ eris_assert(top + 1 == lua_gettop(info->L));
3959+}
3960+
3961+/* }======================================================================== */
3962+
3963+/*
3964+** {===========================================================================
3965+** Writer and reader implementation for library calls.
3966+** ============================================================================
3967+*/
3968+
3969+/* Implementation note: we use the MBuffer struct, but we don't use the built-
3970+ * in reallocation functions since we'll keep our working copy on the stack, to
3971+ * allow for proper collection if we have to throw an error. This is very much
3972+ * what the auxlib does with its buffer functionality. Which we don't use since
3973+ * we cannot guarantee stack balance inbetween calls to luaL_add*. */
3974+
3975+static int
3976+writer(lua_State *L, const void *p, size_t sz, void *ud) {
3977+ /* perms reftbl buff path? ... */
3978+ const char *value = (const char*)p;
3979+ Mbuffer *buff = (Mbuffer*)ud;
3980+ const size_t size = eris_bufflen(buff);
3981+ const size_t capacity = eris_sizebuffer(buff);
3982+ if (capacity - size < sz) {
3983+ size_t newcapacity = capacity * 2; /* overflow checked below */
3984+ if (newcapacity - size < sz) {
3985+ newcapacity = capacity + sz; /* overflow checked below */
3986+ }
3987+ if (newcapacity <= capacity) {
3988+ /* Overflow in capacity, buffer size limit reached. */
3989+ return 1;
3990+ } else {
3991+ char *newbuff;
3992+ eris_checkstack(L, 1);
3993+ newbuff = (char*)lua_newuserdata(L, newcapacity * sizeof(char));
3994+ /* perms reftbl buff path? ... nbuff */
3995+ memcpy(newbuff, eris_buffer(buff), eris_bufflen(buff));
3996+ lua_replace(L, BUFFIDX); /* perms reftbl nbuff path? ... */
3997+ eris_buffer(buff) = newbuff;
3998+ eris_sizebuffer(buff) = newcapacity;
3999+ }
4000+ }
4001+ memcpy(&eris_buffer(buff)[eris_bufflen(buff)], value, sz);
4002+ eris_bufflen(buff) += sz;
4003+ return 0;
4004+}
4005+
4006+/** ======================================================================== */
4007+
4008+/* Readonly, interface compatible with MBuffer macros. */
4009+typedef struct RBuffer {
4010+ const char *buffer;
4011+ size_t n;
4012+ size_t buffsize;
4013+} RBuffer;
4014+
4015+static const char*
4016+reader(lua_State *L, void *ud, size_t *sz) {
4017+ RBuffer *buff = (RBuffer*)ud;
4018+ (void) L; /* unused */
4019+ if (eris_bufflen(buff) == 0) {
4020+ return NULL;
4021+ }
4022+ *sz = eris_bufflen(buff);
4023+ eris_bufflen(buff) = 0;
4024+ return eris_buffer(buff);
4025+}
4026+
4027+/* }======================================================================== */
4028+
4029+/*
4030+** {===========================================================================
4031+** Library functions.
4032+** ============================================================================
4033+*/
4034+
4035+static void
4036+p_header(Info *info) {
4037+ WRITE_RAW(kHeader, HEADER_LENGTH);
4038+ WRITE_VALUE(sizeof(lua_Number), uint8_t);
4039+ WRITE_VALUE(kHeaderNumber, lua_Number);
4040+ WRITE_VALUE(sizeof(int), uint8_t);
4041+ WRITE_VALUE(sizeof(size_t), uint8_t);
4042+}
4043+
4044+static void
4045+u_header(Info *info) {
4046+ char header[HEADER_LENGTH];
4047+ uint8_t number_size;
4048+ READ_RAW(header, HEADER_LENGTH);
4049+ if (strncmp(kHeader, header, HEADER_LENGTH)) {
4050+ luaL_error(info->L, "invalid data");
4051+ }
4052+ number_size = READ_VALUE(uint8_t);
4053+ if (number_size == 0) {
4054+ /* Old 64-bit versions of eris wrote '\0' and then three random bytes. */
4055+ /* We skip them here for backwards compatibility. */
4056+ char throw_away[3];
4057+ READ_RAW(throw_away, 3);
4058+
4059+ number_size = READ_VALUE(uint8_t);
4060+ }
4061+ if (number_size != sizeof(lua_Number)) {
4062+ luaL_error(info->L, "incompatible floating point type");
4063+ }
4064+ /* In this case we really do want floating point equality. */
4065+ if (READ_VALUE(lua_Number) != kHeaderNumber) {
4066+ luaL_error(info->L, "incompatible floating point representation");
4067+ }
4068+ info->u.upi.sizeof_int = READ_VALUE(uint8_t);
4069+ info->u.upi.sizeof_size_t = READ_VALUE(uint8_t);
4070+}
4071+
4072+static void
4073+unchecked_persist(lua_State *L, lua_Writer writer, void *ud) {
4074+ Info info; /* perms buff rootobj */
4075+ info.L = L;
4076+ info.level = 0;
4077+ info.refcount = 0;
4078+ info.maxComplexity = kMaxComplexity;
4079+ info.passIOToPersist = kPassIOToPersist;
4080+ info.generatePath = kGeneratePath;
4081+ info.u.pi.writer = writer;
4082+ info.u.pi.ud = ud;
4083+ info.u.pi.metafield = kPersistKey;
4084+ info.u.pi.writeDebugInfo = kWriteDebugInformation;
4085+
4086+ eris_checkstack(L, 3);
4087+
4088+ if (get_setting(L, (void*)&kSettingMaxComplexity)) {
4089+ /* perms buff rootobj value */
4090+ info.maxComplexity = lua_tounsigned(L, -1);
4091+ lua_pop(L, 1); /* perms buff rootobj */
4092+ }
4093+ if (get_setting(L, (void*)&kSettingGeneratePath)) {
4094+ /* perms buff rootobj value */
4095+ info.generatePath = lua_toboolean(L, -1);
4096+ lua_pop(L, 1); /* perms buff rootobj */
4097+ }
4098+ if (get_setting(L, (void*)&kSettingPassIOToPersist)) {
4099+ /* perms buff rootobj value */
4100+ info.passIOToPersist = lua_toboolean(L, -1);
4101+ lua_pop(L, 1); /* perms buff rootobj */
4102+ }
4103+ if (get_setting(L, (void*)&kSettingMetafield)) {/* perms buff rootobj value */
4104+ info.u.pi.metafield = lua_tostring(L, -1);
4105+ lua_pop(L, 1); /* perms buff rootobj */
4106+ }
4107+ if (get_setting(L, (void*)&kSettingWriteDebugInfo)) {
4108+ /* perms buff rootobj value */
4109+ info.u.pi.writeDebugInfo = lua_toboolean(L, -1);
4110+ lua_pop(L, 1); /* perms buff rootobj */
4111+ }
4112+
4113+ lua_newtable(L); /* perms buff rootobj reftbl */
4114+ lua_insert(L, REFTIDX); /* perms reftbl buff rootobj */
4115+ if (info.generatePath) {
4116+ lua_newtable(L); /* perms reftbl buff rootobj path */
4117+ lua_insert(L, PATHIDX); /* perms reftbl buff path rootobj */
4118+ pushpath(&info, "root");
4119+ }
4120+
4121+ /* Populate perms table with Lua internals. */
4122+ lua_pushvalue(L, PERMIDX); /* perms reftbl buff path? rootobj perms */
4123+ populateperms(L, false);
4124+ lua_pop(L, 1); /* perms reftbl buff path? rootobj */
4125+
4126+ p_header(&info);
4127+ persist(&info); /* perms reftbl buff path? rootobj */
4128+
4129+ if (info.generatePath) { /* perms reftbl buff path rootobj */
4130+ lua_remove(L, PATHIDX); /* perms reftbl buff rootobj */
4131+ } /* perms reftbl buff rootobj */
4132+ lua_remove(L, REFTIDX); /* perms buff rootobj */
4133+}
4134+
4135+static void
4136+unchecked_unpersist(lua_State *L, lua_Reader reader, void *ud) {/* perms str? */
4137+ Info info;
4138+ info.L = L;
4139+ info.level = 0;
4140+ info.refcount = 0;
4141+ info.maxComplexity = kMaxComplexity;
4142+ info.generatePath = kGeneratePath;
4143+ info.passIOToPersist = kPassIOToPersist;
4144+ eris_init(L, &info.u.upi.zio, reader, ud);
4145+
4146+ eris_checkstack(L, 3);
4147+
4148+ if (get_setting(L, (void*)&kSettingMaxComplexity)) {
4149+ /* perms buff rootobj value */
4150+ info.maxComplexity = lua_tounsigned(L, -1);
4151+ lua_pop(L, 1); /* perms buff rootobj */
4152+ }
4153+ if (get_setting(L, (void*)&kSettingGeneratePath)) {
4154+ /* perms buff? rootobj value */
4155+ info.generatePath = lua_toboolean(L, -1);
4156+ lua_pop(L, 1); /* perms buff? rootobj */
4157+ }
4158+ if (get_setting(L, (void*)&kSettingPassIOToPersist)) { /* perms str? value */
4159+ info.passIOToPersist = lua_toboolean(L, -1);
4160+ lua_pop(L, 1); /* perms str? */
4161+ }
4162+
4163+ lua_newtable(L); /* perms str? reftbl */
4164+ lua_insert(L, REFTIDX); /* perms reftbl str? */
4165+ if (info.generatePath) {
4166+ /* Make sure the path is always at index 4, so that it's the same for
4167+ * persist and unpersist. */
4168+ lua_pushnil(L); /* perms reftbl str? nil */
4169+ lua_insert(L, BUFFIDX); /* perms reftbl nil str? */
4170+ lua_newtable(L); /* perms reftbl nil str? path */
4171+ lua_insert(L, PATHIDX); /* perms reftbl nil path str? */
4172+ pushpath(&info, "root");
4173+ }
4174+
4175+ /* Populate perms table with Lua internals. */
4176+ lua_pushvalue(L, PERMIDX); /* perms reftbl nil? path? str? perms */
4177+ populateperms(L, true);
4178+ lua_pop(L, 1); /* perms reftbl nil? path? str? */
4179+
4180+ u_header(&info);
4181+ unpersist(&info); /* perms reftbl nil? path? str? rootobj */
4182+ if (info.generatePath) { /* perms reftbl nil path str? rootobj */
4183+ lua_remove(L, PATHIDX); /* perms reftbl nil str? rootobj */
4184+ lua_remove(L, BUFFIDX); /* perms reftbl str? rootobj */
4185+ } /* perms reftbl str? rootobj */
4186+ lua_remove(L, REFTIDX); /* perms str? rootobj */
4187+}
4188+
4189+/** ======================================================================== */
4190+
4191+static int
4192+l_persist(lua_State *L) { /* perms? rootobj? ...? */
4193+ Mbuffer buff;
4194+
4195+ /* See if we have anything at all. */
4196+ luaL_checkany(L, 1);
4197+
4198+ /* If we only have one object we assume it is the root object and that there
4199+ * is no perms table, so we create an empty one for internal use. */
4200+ if (lua_gettop(L) == 1) { /* rootobj */
4201+ eris_checkstack(L, 1);
4202+ lua_newtable(L); /* rootobj perms */
4203+ lua_insert(L, PERMIDX); /* perms rootobj */
4204+ }
4205+ else {
4206+ luaL_checktype(L, 1, LUA_TTABLE); /* perms rootobj? ...? */
4207+ luaL_checkany(L, 2); /* perms rootobj ...? */
4208+ lua_settop(L, 2); /* perms rootobj */
4209+ }
4210+ eris_checkstack(L, 1);
4211+ lua_pushnil(L); /* perms rootobj buff */
4212+ lua_insert(L, 2); /* perms buff rootobj */
4213+
4214+ eris_initbuffer(L, &buff);
4215+ eris_bufflen(&buff) = 0; /* Not initialized by initbuffer... */
4216+
4217+ unchecked_persist(L, writer, &buff); /* perms buff rootobj */
4218+
4219+ /* Copy the buffer as the result string before removing it, to avoid the data
4220+ * being garbage collected. */
4221+ lua_pushlstring(L, eris_buffer(&buff), eris_bufflen(&buff));
4222+ /* perms buff rootobj str */
4223+
4224+ return 1;
4225+}
4226+
4227+static int
4228+l_unpersist(lua_State *L) { /* perms? str? ...? */
4229+ RBuffer buff;
4230+
4231+ /* See if we have anything at all. */
4232+ luaL_checkany(L, 1);
4233+
4234+ /* If we only have one object we assume it is the root object and that there
4235+ * is no perms table, so we create an empty one for internal use. */
4236+ if (lua_gettop(L) == 1) { /* str? */
4237+ eris_checkstack(L, 1);
4238+ lua_newtable(L); /* str? perms */
4239+ lua_insert(L, PERMIDX); /* perms str? */
4240+ }
4241+ else {
4242+ luaL_checktype(L, 1, LUA_TTABLE); /* perms str? ...? */
4243+ }
4244+ eris_buffer(&buff) = luaL_checklstring(L, 2, &eris_bufflen(&buff));
4245+ eris_sizebuffer(&buff) = eris_bufflen(&buff); /* perms str ...? */
4246+ lua_settop(L, 2); /* perms str */
4247+
4248+ unchecked_unpersist(L, reader, &buff); /* perms str rootobj */
4249+
4250+ return 1;
4251+}
4252+
4253+#define IS(s) strncmp(s, name, length < sizeof(s) ? length : sizeof(s)) == 0
4254+
4255+static int
4256+l_settings(lua_State *L) { /* name value? ...? */
4257+ size_t length;
4258+ const char *name = luaL_checklstring(L, 1, &length);
4259+ if (lua_isnone(L, 2)) { /* name ...? */
4260+ lua_settop(L, 1); /* name */
4261+ /* Get the current setting value and return it. */
4262+ if (IS(kSettingMetafield)) {
4263+ if (!get_setting(L, (void*)&kSettingMetafield)) {
4264+ lua_pushstring(L, kPersistKey);
4265+ }
4266+ }
4267+ else if (IS(kSettingPassIOToPersist)) {
4268+ if (!get_setting(L, (void*)&kSettingPassIOToPersist)) {
4269+ lua_pushboolean(L, kPassIOToPersist);
4270+ }
4271+ }
4272+ else if (IS(kSettingWriteDebugInfo)) {
4273+ if (!get_setting(L, (void*)&kSettingWriteDebugInfo)) {
4274+ lua_pushboolean(L, kWriteDebugInformation);
4275+ }
4276+ }
4277+ else if (IS(kSettingGeneratePath)) {
4278+ if (!get_setting(L, (void*)&kSettingGeneratePath)) {
4279+ lua_pushboolean(L, kGeneratePath);
4280+ }
4281+ }
4282+ else if (IS(kSettingMaxComplexity)) {
4283+ if (!get_setting(L, (void*)&kSettingMaxComplexity)) {
4284+ lua_pushunsigned(L, kMaxComplexity);
4285+ }
4286+ }
4287+ else {
4288+ return luaL_argerror(L, 1, "no such setting");
4289+ } /* name value */
4290+ return 1;
4291+ }
4292+ else { /* name value ...? */
4293+ lua_settop(L, 2); /* name value */
4294+ /* Set a new value for the setting. */
4295+ if (IS(kSettingMetafield)) {
4296+ luaL_optstring(L, 2, NULL);
4297+ set_setting(L, (void*)&kSettingMetafield);
4298+ }
4299+ else if (IS(kSettingPassIOToPersist)) {
4300+ luaL_opt(L, checkboolean, 2, false);
4301+ set_setting(L, (void*)&kSettingPassIOToPersist);
4302+ }
4303+ else if (IS(kSettingWriteDebugInfo)) {
4304+ luaL_opt(L, checkboolean, 2, false);
4305+ set_setting(L, (void*)&kSettingWriteDebugInfo);
4306+ }
4307+ else if (IS(kSettingGeneratePath)) {
4308+ luaL_opt(L, checkboolean, 2, false);
4309+ set_setting(L, (void*)&kSettingGeneratePath);
4310+ }
4311+ else if (IS(kSettingMaxComplexity)) {
4312+ luaL_optunsigned(L, 2, 0);
4313+ set_setting(L, (void*)&kSettingMaxComplexity);
4314+ }
4315+ else {
4316+ return luaL_argerror(L, 1, "no such setting");
4317+ } /* name */
4318+ return 0;
4319+ }
4320+}
4321+
4322+#undef IS
4323+
4324+/** ======================================================================== */
4325+
4326+static luaL_Reg erislib[] = {
4327+ { "persist", l_persist },
4328+ { "unpersist", l_unpersist },
4329+ { "settings", l_settings },
4330+ { NULL, NULL }
4331+};
4332+
4333+LUA_API int luaopen_eris(lua_State *L) {
4334+ luaL_newlib(L, erislib);
4335+ return 1;
4336+}
4337+
4338+/* }======================================================================== */
4339+
4340+/*
4341+** {===========================================================================
4342+** Public API functions.
4343+** ============================================================================
4344+*/
4345+
4346+LUA_API void
4347+eris_dump(lua_State *L, lua_Writer writer, void *ud) { /* perms? rootobj? */
4348+ if (lua_gettop(L) > 2) {
4349+ luaL_error(L, "too many arguments");
4350+ }
4351+ luaL_checktype(L, 1, LUA_TTABLE); /* perms rootobj? */
4352+ luaL_checkany(L, 2); /* perms rootobj */
4353+ lua_pushnil(L); /* perms rootobj nil */
4354+ lua_insert(L, -2); /* perms nil rootobj */
4355+ unchecked_persist(L, writer, ud); /* perms nil rootobj */
4356+ lua_remove(L, -2); /* perms rootobj */
4357+}
4358+
4359+LUA_API void
4360+eris_undump(lua_State *L, lua_Reader reader, void *ud) { /* perms? */
4361+ if (lua_gettop(L) > 1) {
4362+ luaL_error(L, "too many arguments");
4363+ }
4364+ luaL_checktype(L, 1, LUA_TTABLE); /* perms */
4365+ unchecked_unpersist(L, reader, ud); /* perms rootobj */
4366+}
4367+
4368+/** ======================================================================== */
4369+
4370+LUA_API void
4371+eris_persist(lua_State *L, int perms, int value) { /* ...? */
4372+ eris_checkstack(L, 3);
4373+ lua_pushcfunction(L, l_persist); /* ... l_persist */
4374+ lua_pushvalue(L, perms); /* ... l_persist perms */
4375+ lua_pushvalue(L, value); /* ... l_persist perms rootobj */
4376+ lua_call(L, 2, 1); /* ... str */
4377+}
4378+
4379+LUA_API void
4380+eris_unpersist(lua_State *L, int perms, int value) { /* ... */
4381+ eris_checkstack(L, 3);
4382+ lua_pushcfunction(L, l_unpersist); /* ... l_unpersist */
4383+ lua_pushvalue(L, perms); /* ... l_unpersist perms */
4384+ lua_pushvalue(L, value); /* ... l_unpersist perms str */
4385+ lua_call(L, 2, 1); /* ... rootobj */
4386+}
4387+
4388+LUA_API void
4389+eris_get_setting(lua_State *L, const char *name) { /* ... */
4390+ eris_checkstack(L, 2);
4391+ lua_pushcfunction(L, l_settings); /* ... l_settings */
4392+ lua_pushstring(L, name); /* ... l_settings name */
4393+ lua_call(L, 1, 1); /* ... value */
4394+}
4395+
4396+LUA_API void
4397+eris_set_setting(lua_State *L, const char *name, int value) { /* ... */
4398+ eris_checkstack(L, 3);
4399+ lua_pushcfunction(L, l_settings); /* ... l_settings */
4400+ lua_pushstring(L, name); /* ... l_settings name */
4401+ lua_pushvalue(L, value); /* ... l_settings name value */
4402+ lua_call(L, 2, 0); /* ... */
4403+}
4404+
4405+/* }======================================================================== */
4406+
4407
4408=== added file 'src/scripting/eris/eris.h'
4409--- src/scripting/eris/eris.h 1970-01-01 00:00:00 +0000
4410+++ src/scripting/eris/eris.h 2014-02-22 14:58:17 +0000
4411@@ -0,0 +1,148 @@
4412+/*
4413+Eris - Heavy-duty persistence for Lua 5.2.2 - Based on Pluto
4414+Copyright (c) 2013 by Florian Nuecke.
4415+
4416+Permission is hereby granted, free of charge, to any person obtaining a copy
4417+of this software and associated documentation files (the "Software"), to deal
4418+in the Software without restriction, including without limitation the rights
4419+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4420+copies of the Software, and to permit persons to whom the Software is
4421+furnished to do so, subject to the following conditions:
4422+
4423+The above copyright notice and this permission notice shall be included in
4424+all copies or substantial portions of the Software.
4425+
4426+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4427+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4428+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4429+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4430+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4431+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
4432+THE SOFTWARE.
4433+*/
4434+
4435+/* lua.h must be included before this file */
4436+
4437+#ifndef ERIS_H
4438+#define ERIS_H
4439+
4440+/*
4441+** ==================================================================
4442+** API
4443+** ==================================================================
4444+*/
4445+
4446+/**
4447+ * This provides an interface to Eris' persist functionality for writing in
4448+ * an arbitrary way, using a writer.
4449+ *
4450+ * When called, the stack in 'L' must look like this:
4451+ * 1: perms:table
4452+ * 2: value:any
4453+ *
4454+ * 'writer' is the writer function used to store all data, 'ud' is passed to
4455+ * the writer function whenever it is called.
4456+ *
4457+ * [-0, +0, e]
4458+ */
4459+LUA_API void eris_dump(lua_State* L, lua_Writer writer, void* ud);
4460+
4461+/**
4462+ * This provides an interface to Eris' unpersist functionality for reading
4463+ * in an arbitrary way, using a reader.
4464+ *
4465+ * When called, the stack in 'L' must look like this:
4466+ * 1: perms:table
4467+ *
4468+ * 'reader' is the reader function used to read all data, 'ud' is passed to
4469+ * the reader function whenever it is called.
4470+ *
4471+ * The result of the operation will be pushed onto the stack.
4472+ *
4473+ * [-0, +1, e]
4474+ */
4475+LUA_API void eris_undump(lua_State* L, lua_Reader reader, void* ud);
4476+
4477+/**
4478+ * This is a stack-based alternative to eris_dump.
4479+ *
4480+ * It expects the perms table at the specified index 'perms' and the value to
4481+ * persist at the specified index 'value'. It will push the resulting string
4482+ * onto the stack on success.
4483+ *
4484+ * [-0, +1, e]
4485+ */
4486+LUA_API void eris_persist(lua_State* L, int perms, int value);
4487+
4488+/**
4489+ * This is a stack-based alternative to eris_undump.
4490+ *
4491+ * It expects the perms table at the specified index 'perms' and the string
4492+ * containing persisted data at the specified index 'value'. It will push the
4493+ * resulting value onto the stack on success.
4494+ *
4495+ * [-0, +1, e]
4496+ */
4497+LUA_API void eris_unpersist(lua_State* L, int perms, int value);
4498+
4499+/**
4500+ * Pushes the current value of a setting onto the stack.
4501+ *
4502+ * The name is the name of the setting to get the value for:
4503+ * - 'debug' whether to write debug information when persisting function
4504+ * prototypes (line numbers, local variable names, upvalue names).
4505+ * - 'maxrec' the maximum complexity of objects we support (the nesting level
4506+ * of tables, for example). This can be useful to avoid segmentation
4507+ * faults due to too deep recursion when working with user-provided
4508+ * data.
4509+ * - 'path' whether to generate a "path" used to indicate where in an object
4510+ * an error occurred. This adds considerable overhead and should
4511+ * only be used to debug errors as they appear.
4512+ * - 'spio' whether to pass IO objects along as light userdata to special
4513+ * persistence functions. When persisting this will pass along the
4514+ * lua_Writer and its void* in addition to the original object, when
4515+ * unpersisting it will pass along a ZIO*.
4516+ * - 'spkey' the name of the field in the metatable of tables and userdata
4517+ * used to control persistence (on/off or special persistence).
4518+ *
4519+ * If an unknown name is specified this will throw an error.
4520+ *
4521+ * [-0, +1, e]
4522+ */
4523+LUA_API void eris_get_setting(lua_State *L, const char *name);
4524+
4525+/**
4526+ * Changes the value of a setting to a value on the stack.
4527+ *
4528+ * For the available settings see eris_set_setting(). This will get the new
4529+ * value from the specified stack index 'value'. If the type does not match
4530+ * this will throw an error. Specify a nil value to reset the setting to its
4531+ * default value.
4532+ *
4533+ * [-0, +0, e]
4534+ */
4535+LUA_API void eris_set_setting(lua_State *L, const char *name, int value);
4536+
4537+/*
4538+** ==================================================================
4539+** Library installer
4540+** ==================================================================
4541+*/
4542+
4543+/**
4544+ * This pushes a table with the two functions 'persist' and 'unpersist':
4545+ * persist([perms,] value)
4546+ * Where 'perms' is a table with "permanent" objects and 'value' is the
4547+ * value that should be persisted. Returns the string with persisted data.
4548+ * If only one value is given, the perms table is assumed to be empty.
4549+ *
4550+ * unpersist([perms,] value)
4551+ * Where 'perms' is a table with the inverse mapping as used when
4552+ * persisting the data via persist() and 'value' is the string with the
4553+ * persisted data returned by persist(). Returns the unpersisted value.
4554+ * If only one value is given, the perms table is assumed to be empty.
4555+ */
4556+LUA_API int luaopen_eris(lua_State* L);
4557+
4558+#endif
4559+
4560
4561=== added file 'src/scripting/eris/lapi.c'
4562--- src/scripting/eris/lapi.c 1970-01-01 00:00:00 +0000
4563+++ src/scripting/eris/lapi.c 2014-02-22 14:58:17 +0000
4564@@ -0,0 +1,1284 @@
4565+/*
4566+** $Id: lapi.c,v 2.171.1.1 2013/04/12 18:48:47 roberto Exp $
4567+** Lua API
4568+** See Copyright Notice in lua.h
4569+*/
4570+
4571+
4572+#include <stdarg.h>
4573+#include <string.h>
4574+
4575+#define lapi_c
4576+#define LUA_CORE
4577+
4578+#include "lua.h"
4579+
4580+#include "lapi.h"
4581+#include "ldebug.h"
4582+#include "ldo.h"
4583+#include "lfunc.h"
4584+#include "lgc.h"
4585+#include "lmem.h"
4586+#include "lobject.h"
4587+#include "lstate.h"
4588+#include "lstring.h"
4589+#include "ltable.h"
4590+#include "ltm.h"
4591+#include "lundump.h"
4592+#include "lvm.h"
4593+
4594+
4595+
4596+const char lua_ident[] =
4597+ "$LuaVersion: " LUA_COPYRIGHT " $"
4598+ "$LuaAuthors: " LUA_AUTHORS " $";
4599+
4600+
4601+/* value at a non-valid index */
4602+#define NONVALIDVALUE cast(TValue *, luaO_nilobject)
4603+
4604+/* corresponding test */
4605+#define isvalid(o) ((o) != luaO_nilobject)
4606+
4607+/* test for pseudo index */
4608+#define ispseudo(i) ((i) <= LUA_REGISTRYINDEX)
4609+
4610+/* test for valid but not pseudo index */
4611+#define isstackindex(i, o) (isvalid(o) && !ispseudo(i))
4612+
4613+#define api_checkvalidindex(L, o) api_check(L, isvalid(o), "invalid index")
4614+
4615+#define api_checkstackindex(L, i, o) \
4616+ api_check(L, isstackindex(i, o), "index not in the stack")
4617+
4618+
4619+static TValue *index2addr (lua_State *L, int idx) {
4620+ CallInfo *ci = L->ci;
4621+ if (idx > 0) {
4622+ TValue *o = ci->func + idx;
4623+ api_check(L, idx <= ci->top - (ci->func + 1), "unacceptable index");
4624+ if (o >= L->top) return NONVALIDVALUE;
4625+ else return o;
4626+ }
4627+ else if (!ispseudo(idx)) { /* negative index */
4628+ api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index");
4629+ return L->top + idx;
4630+ }
4631+ else if (idx == LUA_REGISTRYINDEX)
4632+ return &G(L)->l_registry;
4633+ else { /* upvalues */
4634+ idx = LUA_REGISTRYINDEX - idx;
4635+ api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");
4636+ if (ttislcf(ci->func)) /* light C function? */
4637+ return NONVALIDVALUE; /* it has no upvalues */
4638+ else {
4639+ CClosure *func = clCvalue(ci->func);
4640+ return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : NONVALIDVALUE;
4641+ }
4642+ }
4643+}
4644+
4645+
4646+/*
4647+** to be called by 'lua_checkstack' in protected mode, to grow stack
4648+** capturing memory errors
4649+*/
4650+static void growstack (lua_State *L, void *ud) {
4651+ int size = *(int *)ud;
4652+ luaD_growstack(L, size);
4653+}
4654+
4655+
4656+LUA_API int lua_checkstack (lua_State *L, int size) {
4657+ int res;
4658+ CallInfo *ci = L->ci;
4659+ lua_lock(L);
4660+ if (L->stack_last - L->top > size) /* stack large enough? */
4661+ res = 1; /* yes; check is OK */
4662+ else { /* no; need to grow stack */
4663+ int inuse = cast_int(L->top - L->stack) + EXTRA_STACK;
4664+ if (inuse > LUAI_MAXSTACK - size) /* can grow without overflow? */
4665+ res = 0; /* no */
4666+ else /* try to grow stack */
4667+ res = (luaD_rawrunprotected(L, &growstack, &size) == LUA_OK);
4668+ }
4669+ if (res && ci->top < L->top + size)
4670+ ci->top = L->top + size; /* adjust frame top */
4671+ lua_unlock(L);
4672+ return res;
4673+}
4674+
4675+
4676+LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) {
4677+ int i;
4678+ if (from == to) return;
4679+ lua_lock(to);
4680+ api_checknelems(from, n);
4681+ api_check(from, G(from) == G(to), "moving among independent states");
4682+ api_check(from, to->ci->top - to->top >= n, "not enough elements to move");
4683+ from->top -= n;
4684+ for (i = 0; i < n; i++) {
4685+ setobj2s(to, to->top++, from->top + i);
4686+ }
4687+ lua_unlock(to);
4688+}
4689+
4690+
4691+LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) {
4692+ lua_CFunction old;
4693+ lua_lock(L);
4694+ old = G(L)->panic;
4695+ G(L)->panic = panicf;
4696+ lua_unlock(L);
4697+ return old;
4698+}
4699+
4700+
4701+LUA_API const lua_Number *lua_version (lua_State *L) {
4702+ static const lua_Number version = LUA_VERSION_NUM;
4703+ if (L == NULL) return &version;
4704+ else return G(L)->version;
4705+}
4706+
4707+
4708+
4709+/*
4710+** basic stack manipulation
4711+*/
4712+
4713+
4714+/*
4715+** convert an acceptable stack index into an absolute index
4716+*/
4717+LUA_API int lua_absindex (lua_State *L, int idx) {
4718+ return (idx > 0 || ispseudo(idx))
4719+ ? idx
4720+ : cast_int(L->top - L->ci->func + idx);
4721+}
4722+
4723+
4724+LUA_API int lua_gettop (lua_State *L) {
4725+ return cast_int(L->top - (L->ci->func + 1));
4726+}
4727+
4728+
4729+LUA_API void lua_settop (lua_State *L, int idx) {
4730+ StkId func = L->ci->func;
4731+ lua_lock(L);
4732+ if (idx >= 0) {
4733+ api_check(L, idx <= L->stack_last - (func + 1), "new top too large");
4734+ while (L->top < (func + 1) + idx)
4735+ setnilvalue(L->top++);
4736+ L->top = (func + 1) + idx;
4737+ }
4738+ else {
4739+ api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
4740+ L->top += idx+1; /* `subtract' index (index is negative) */
4741+ }
4742+ lua_unlock(L);
4743+}
4744+
4745+
4746+LUA_API void lua_remove (lua_State *L, int idx) {
4747+ StkId p;
4748+ lua_lock(L);
4749+ p = index2addr(L, idx);
4750+ api_checkstackindex(L, idx, p);
4751+ while (++p < L->top) setobjs2s(L, p-1, p);
4752+ L->top--;
4753+ lua_unlock(L);
4754+}
4755+
4756+
4757+LUA_API void lua_insert (lua_State *L, int idx) {
4758+ StkId p;
4759+ StkId q;
4760+ lua_lock(L);
4761+ p = index2addr(L, idx);
4762+ api_checkstackindex(L, idx, p);
4763+ for (q = L->top; q > p; q--) /* use L->top as a temporary */
4764+ setobjs2s(L, q, q - 1);
4765+ setobjs2s(L, p, L->top);
4766+ lua_unlock(L);
4767+}
4768+
4769+
4770+static void moveto (lua_State *L, TValue *fr, int idx) {
4771+ TValue *to = index2addr(L, idx);
4772+ api_checkvalidindex(L, to);
4773+ setobj(L, to, fr);
4774+ if (idx < LUA_REGISTRYINDEX) /* function upvalue? */
4775+ luaC_barrier(L, clCvalue(L->ci->func), fr);
4776+ /* LUA_REGISTRYINDEX does not need gc barrier
4777+ (collector revisits it before finishing collection) */
4778+}
4779+
4780+
4781+LUA_API void lua_replace (lua_State *L, int idx) {
4782+ lua_lock(L);
4783+ api_checknelems(L, 1);
4784+ moveto(L, L->top - 1, idx);
4785+ L->top--;
4786+ lua_unlock(L);
4787+}
4788+
4789+
4790+LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) {
4791+ TValue *fr;
4792+ lua_lock(L);
4793+ fr = index2addr(L, fromidx);
4794+ moveto(L, fr, toidx);
4795+ lua_unlock(L);
4796+}
4797+
4798+
4799+LUA_API void lua_pushvalue (lua_State *L, int idx) {
4800+ lua_lock(L);
4801+ setobj2s(L, L->top, index2addr(L, idx));
4802+ api_incr_top(L);
4803+ lua_unlock(L);
4804+}
4805+
4806+
4807+
4808+/*
4809+** access functions (stack -> C)
4810+*/
4811+
4812+
4813+LUA_API int lua_type (lua_State *L, int idx) {
4814+ StkId o = index2addr(L, idx);
4815+ return (isvalid(o) ? ttypenv(o) : LUA_TNONE);
4816+}
4817+
4818+
4819+LUA_API const char *lua_typename (lua_State *L, int t) {
4820+ UNUSED(L);
4821+ return ttypename(t);
4822+}
4823+
4824+
4825+LUA_API int lua_iscfunction (lua_State *L, int idx) {
4826+ StkId o = index2addr(L, idx);
4827+ return (ttislcf(o) || (ttisCclosure(o)));
4828+}
4829+
4830+
4831+LUA_API int lua_isnumber (lua_State *L, int idx) {
4832+ TValue n;
4833+ const TValue *o = index2addr(L, idx);
4834+ return tonumber(o, &n);
4835+}
4836+
4837+
4838+LUA_API int lua_isstring (lua_State *L, int idx) {
4839+ int t = lua_type(L, idx);
4840+ return (t == LUA_TSTRING || t == LUA_TNUMBER);
4841+}
4842+
4843+
4844+LUA_API int lua_isuserdata (lua_State *L, int idx) {
4845+ const TValue *o = index2addr(L, idx);
4846+ return (ttisuserdata(o) || ttislightuserdata(o));
4847+}
4848+
4849+
4850+LUA_API int lua_rawequal (lua_State *L, int index1, int index2) {
4851+ StkId o1 = index2addr(L, index1);
4852+ StkId o2 = index2addr(L, index2);
4853+ return (isvalid(o1) && isvalid(o2)) ? luaV_rawequalobj(o1, o2) : 0;
4854+}
4855+
4856+
4857+LUA_API void lua_arith (lua_State *L, int op) {
4858+ StkId o1; /* 1st operand */
4859+ StkId o2; /* 2nd operand */
4860+ lua_lock(L);
4861+ if (op != LUA_OPUNM) /* all other operations expect two operands */
4862+ api_checknelems(L, 2);
4863+ else { /* for unary minus, add fake 2nd operand */
4864+ api_checknelems(L, 1);
4865+ setobjs2s(L, L->top, L->top - 1);
4866+ L->top++;
4867+ }
4868+ o1 = L->top - 2;
4869+ o2 = L->top - 1;
4870+ if (ttisnumber(o1) && ttisnumber(o2)) {
4871+ setnvalue(o1, luaO_arith(op, nvalue(o1), nvalue(o2)));
4872+ }
4873+ else
4874+ luaV_arith(L, o1, o1, o2, cast(TMS, op - LUA_OPADD + TM_ADD));
4875+ L->top--;
4876+ lua_unlock(L);
4877+}
4878+
4879+
4880+LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) {
4881+ StkId o1, o2;
4882+ int i = 0;
4883+ lua_lock(L); /* may call tag method */
4884+ o1 = index2addr(L, index1);
4885+ o2 = index2addr(L, index2);
4886+ if (isvalid(o1) && isvalid(o2)) {
4887+ switch (op) {
4888+ case LUA_OPEQ: i = equalobj(L, o1, o2); break;
4889+ case LUA_OPLT: i = luaV_lessthan(L, o1, o2); break;
4890+ case LUA_OPLE: i = luaV_lessequal(L, o1, o2); break;
4891+ default: api_check(L, 0, "invalid option");
4892+ }
4893+ }
4894+ lua_unlock(L);
4895+ return i;
4896+}
4897+
4898+
4899+LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *isnum) {
4900+ TValue n;
4901+ const TValue *o = index2addr(L, idx);
4902+ if (tonumber(o, &n)) {
4903+ if (isnum) *isnum = 1;
4904+ return nvalue(o);
4905+ }
4906+ else {
4907+ if (isnum) *isnum = 0;
4908+ return 0;
4909+ }
4910+}
4911+
4912+
4913+LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *isnum) {
4914+ TValue n;
4915+ const TValue *o = index2addr(L, idx);
4916+ if (tonumber(o, &n)) {
4917+ lua_Integer res;
4918+ lua_Number num = nvalue(o);
4919+ lua_number2integer(res, num);
4920+ if (isnum) *isnum = 1;
4921+ return res;
4922+ }
4923+ else {
4924+ if (isnum) *isnum = 0;
4925+ return 0;
4926+ }
4927+}
4928+
4929+
4930+LUA_API lua_Unsigned lua_tounsignedx (lua_State *L, int idx, int *isnum) {
4931+ TValue n;
4932+ const TValue *o = index2addr(L, idx);
4933+ if (tonumber(o, &n)) {
4934+ lua_Unsigned res;
4935+ lua_Number num = nvalue(o);
4936+ lua_number2unsigned(res, num);
4937+ if (isnum) *isnum = 1;
4938+ return res;
4939+ }
4940+ else {
4941+ if (isnum) *isnum = 0;
4942+ return 0;
4943+ }
4944+}
4945+
4946+
4947+LUA_API int lua_toboolean (lua_State *L, int idx) {
4948+ const TValue *o = index2addr(L, idx);
4949+ return !l_isfalse(o);
4950+}
4951+
4952+
4953+LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {
4954+ StkId o = index2addr(L, idx);
4955+ if (!ttisstring(o)) {
4956+ lua_lock(L); /* `luaV_tostring' may create a new string */
4957+ if (!luaV_tostring(L, o)) { /* conversion failed? */
4958+ if (len != NULL) *len = 0;
4959+ lua_unlock(L);
4960+ return NULL;
4961+ }
4962+ luaC_checkGC(L);
4963+ o = index2addr(L, idx); /* previous call may reallocate the stack */
4964+ lua_unlock(L);
4965+ }
4966+ if (len != NULL) *len = tsvalue(o)->len;
4967+ return svalue(o);
4968+}
4969+
4970+
4971+LUA_API size_t lua_rawlen (lua_State *L, int idx) {
4972+ StkId o = index2addr(L, idx);
4973+ switch (ttypenv(o)) {
4974+ case LUA_TSTRING: return tsvalue(o)->len;
4975+ case LUA_TUSERDATA: return uvalue(o)->len;
4976+ case LUA_TTABLE: return luaH_getn(hvalue(o));
4977+ default: return 0;
4978+ }
4979+}
4980+
4981+
4982+LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) {
4983+ StkId o = index2addr(L, idx);
4984+ if (ttislcf(o)) return fvalue(o);
4985+ else if (ttisCclosure(o))
4986+ return clCvalue(o)->f;
4987+ else return NULL; /* not a C function */
4988+}
4989+
4990+
4991+LUA_API void *lua_touserdata (lua_State *L, int idx) {
4992+ StkId o = index2addr(L, idx);
4993+ switch (ttypenv(o)) {
4994+ case LUA_TUSERDATA: return (rawuvalue(o) + 1);
4995+ case LUA_TLIGHTUSERDATA: return pvalue(o);
4996+ default: return NULL;
4997+ }
4998+}
4999+
5000+
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to status/vote changes: