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
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2013-12-29 16:10:29 +0000
+++ CMakeLists.txt 2014-02-22 14:58:17 +0000
@@ -477,9 +477,6 @@
477 message(FATAL_ERROR "Required package SDL_gfx not found, please install and re-run cmake")477 message(FATAL_ERROR "Required package SDL_gfx not found, please install and re-run cmake")
478endif (NOT SDLGFX_FOUND)478endif (NOT SDLGFX_FOUND)
479479
480find_package(Lua51 REQUIRED)
481include_directories(${LUA_INCLUDE_DIR})
482
483IF (WIN32)480IF (WIN32)
484 SET(GUI_TYPE WIN32)481 SET(GUI_TYPE WIN32)
485ENDIF (WIN32)482ENDIF (WIN32)
486483
=== modified file 'CREDITS'
--- CREDITS 2012-06-20 09:59:07 +0000
+++ CREDITS 2014-02-22 14:58:17 +0000
@@ -75,9 +75,9 @@
75 * http://www.lua.org75 * http://www.lua.org
7676
7777
78 Pluto78 Eris
79 * by Ben Sunshine-Hill, Rob Hoelz79 * by Florian Nücke
80 * http://luaforge.net/projects/pluto/80 * https://github.com/fnuecke/eris
8181
8282
83 Lodepng (for testing, not in Widelands itself)83 Lodepng (for testing, not in Widelands itself)
8484
=== modified file 'doc/sphinx/source/implementation.rst'
--- doc/sphinx/source/implementation.rst 2012-06-20 09:07:10 +0000
+++ doc/sphinx/source/implementation.rst 2014-02-22 14:58:17 +0000
@@ -104,20 +104,18 @@
104-----------104-----------
105105
106When a savegame is created, the current environment of Lua is persisted. For106When a savegame is created, the current environment of Lua is persisted. For
107this, I used a library called Pluto_ which is in the public domain. It was107this, I used a library called Eris_.
108heavily modified to work with widelands, the ``FileRead`` and ``FileWrite``
109classes and with the Luna class concepts.
110108
111.. _Pluto: http://luaforge.net/projects/pluto/109.. _Eris: https://github.com/fnuecke
112 110
113The global environment is persisted into the file map/globals.dump. Everything111The global environment is persisted into the file map/globals.dump. Everything
114is persisted except of the Lua build-in functions and everything in the ``wl``112is persisted except of the Lua build-in functions and everything in the ``wl``
115table. Those are c-functions that can not be written out to disk portably.113table and some of our own global functions. Those are c-functions that can not
116Everything else can be saved, that is also everything in the auxiliary scripts114be written out to disk portably. Everything else can be saved, that is also
117are saved to disk, so save games only depend on the API defined inside the115everything in the auxiliary scripts are saved to disk, so save games only
118``wl`` module.116depend on the API defined inside the Widelands.
119117
120Coroutines are persisted together with their ``Cmd_LuaCoroutine`` package. 118Coroutines are persisted in their ``Cmd_LuaCoroutine`` package.
121119
122Luna classes have to implement two functions to be properly persistable:120Luna classes have to implement two functions to be properly persistable:
123121
@@ -130,9 +128,10 @@
130``__unpersist(lua_State *)``128``__unpersist(lua_State *)``
131 On loading, an instance of the user object is created via the default129 On loading, an instance of the user object is created via the default
132 constructor. This function is then called with the table that was created130 constructor. This function is then called with the table that was created
133 by ``__persist()`` on the top of the stack. The object is then expected to131 by ``__persist()`` as an upvalue (because it is inside a closure). The
134 recreate it's former state with this table. There are equivalent132 object is then expected to recreate it's former state with this table.
135 unpersisting macros defined to help with this task (e.g. UNPERS_UINT32).133 There are equivalent unpersisting macros defined to help with this task
134 (e.g. UNPERS_UINT32).
136135
137Widelands reassigns some serial number upon saving and restores them upon136Widelands reassigns some serial number upon saving and restores them upon
138loading. Some Luna classes need this information (for example137loading. Some Luna classes need this information (for example
139138
=== modified file 'regression_test.py'
--- regression_test.py 2014-01-15 21:12:05 +0000
+++ regression_test.py 2014-02-22 14:58:17 +0000
@@ -36,6 +36,7 @@
36 if os.path.exists(self.run_dir):36 if os.path.exists(self.run_dir):
37 if not self.keep_output_around:37 if not self.keep_output_around:
38 shutil.rmtree(self.run_dir)38 shutil.rmtree(self.run_dir)
39 os.makedirs(self.run_dir)
39 else:40 else:
40 os.makedirs(self.run_dir)41 os.makedirs(self.run_dir)
41 self.widelands_returncode = 042 self.widelands_returncode = 0
4243
=== modified file 'scripting/format_help.lua'
--- scripting/format_help.lua 2012-03-06 22:14:31 +0000
+++ scripting/format_help.lua 2014-02-22 14:58:17 +0000
@@ -19,7 +19,7 @@
19 end19 end
2020
21 string = "image=" .. images[1]21 string = "image=" .. images[1]
22 for k,v in ipairs({unpack(images,2)}) do22 for k,v in ipairs({table.unpack(images,2)}) do
23 string = string .. ";pics/arrow-right.png;" .. v23 string = string .. ";pics/arrow-right.png;" .. v
24 end24 end
2525
@@ -81,4 +81,4 @@
81 tab_picture = "pics/medium.png",81 tab_picture = "pics/medium.png",
82 }82 }
83 }83 }
84end
85\ No newline at end of file84\ No newline at end of file
85end
8686
=== modified file 'scripting/infrastructure.lua'
--- scripting/infrastructure.lua 2013-10-05 07:50:17 +0000
+++ scripting/infrastructure.lua 2014-02-22 14:58:17 +0000
@@ -44,7 +44,7 @@
44 moves[#moves+1] = m:sub(1,-2)44 moves[#moves+1] = m:sub(1,-2)
45 if(m:sub(-1) == '|') then45 if(m:sub(-1) == '|') then
46 moves[#moves+1] = true -- Force the road46 moves[#moves+1] = true -- Force the road
47 r = p:place_road(start, unpack(moves))47 r = p:place_road(start, table.unpack(moves))
48 start = r.end_flag48 start = r.end_flag
49 if create_carriers then49 if create_carriers then
50 r:set_workers("carrier", 1)50 r:set_workers("carrier", 1)
5151
=== modified file 'scripting/lunit.lua'
--- scripting/lunit.lua 2013-10-10 20:39:19 +0000
+++ scripting/lunit.lua 2014-02-22 14:58:17 +0000
@@ -28,60 +28,18 @@
28 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE28 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
29 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.29 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3030
31
32 This file was heavily modified for the internal use of Widelands.
33
31--]]--------------------------------------------------------------------------34--]]--------------------------------------------------------------------------
3235
3336lunit = {}
3437lunit.testcases = {}
3538lunit.stats = {}
36-----------------------
37-- Intialize package --
38-----------------------
39
40local P = { }
41lunit = P
42
43-- Import
44local type = type
45local print = print
46local ipairs = ipairs
47local pairs = pairs
48local string = string
49local table = table
50local pcall = pcall
51local xpcall = xpcall
52local debug = debug
53local assert = assert
54local error = error
55local setmetatable = setmetatable
56local rawset = rawset
57local getfenv = getfenv
58local setfenv = setfenv
59local tostring = tostring
60
61
62-- Start package scope
63setfenv(1, P)
64
65
66
67
68--------------------------------
69-- Private data and functions --
70--------------------------------
71
72local run_testcase
73local do_assert, check_msg
74local stats = { }
75local testcases = { }
76local stats_inc, tc_mt
77
78
79
8039
81--------------------------40--------------------------
82-- Type check functions --41-- Type check functions --
83--------------------------42--------------------------
84
85function is_nil(x)43function is_nil(x)
86 return type(x) == "nil"44 return type(x) == "nil"
87end45end
@@ -114,229 +72,198 @@
114 return type(x) == "userdata"72 return type(x) == "userdata"
115end73end
11674
117
118
119
120----------------------75----------------------
121-- Assert functions --76-- Assert functions --
122----------------------77----------------------
123
124function assert_fail(msg)78function assert_fail(msg)
125 stats_inc("assertions")79 lunit_stats_inc("assertions")
126 check_msg("assert_fail", msg)80 lunit_check_msg("assert_fail", msg)
127 do_assert(false, "failure", msg)81 lunit_do_assert(false, "failure", msg)
128end82end
12983
130
131function assert_true(actual, msg)84function assert_true(actual, msg)
132 stats_inc("assertions")85 lunit_stats_inc("assertions")
133 check_msg("assert_true", msg)86 lunit_check_msg("assert_true", msg)
134 do_assert(is_boolean(actual), "true expected but was a "..type(actual), msg)87 lunit_do_assert(is_boolean(actual), "true expected but was a "..type(actual), msg)
135 do_assert(actual == true, "true expected but was false", msg)88 lunit_do_assert(actual == true, "true expected but was false", msg)
136 return actual89 return actual
137end90end
13891
139
140function assert_false(actual, msg)92function assert_false(actual, msg)
141 stats_inc("assertions")93 lunit_stats_inc("assertions")
142 check_msg("assert_false", msg)94 lunit_check_msg("assert_false", msg)
143 do_assert(is_boolean(actual), "false expected but was a "..type(actual), msg)95 lunit_do_assert(is_boolean(actual), "false expected but was a "..type(actual), msg)
144 do_assert(actual == false, "false expected but was true", msg)96 lunit_do_assert(actual == false, "false expected but was true", msg)
145 return actual97 return actual
146end98end
14799
148
149function assert_equal(expected, actual, msg)100function assert_equal(expected, actual, msg)
150 stats_inc("assertions")101 lunit_stats_inc("assertions")
151 check_msg("assert_equal", msg)102 lunit_check_msg("assert_equal", msg)
152 do_assert(expected == actual, "expected '"..tostring(expected).."' but was '"..tostring(actual).."'", msg)103 lunit_do_assert(expected == actual, "expected '"..tostring(expected).."' but was '"..tostring(actual).."'", msg)
153 return actual104 return actual
154end105end
155106
156
157function assert_not_equal(unexpected, actual, msg)107function assert_not_equal(unexpected, actual, msg)
158 stats_inc("assertions")108 lunit_stats_inc("assertions")
159 check_msg("assert_not_equal", msg)109 lunit_check_msg("assert_not_equal", msg)
160 do_assert(unexpected ~= actual, "'"..tostring(expected).."' not expected but was one", msg)110 lunit_do_assert(unexpected ~= actual, "'"..tostring(expected).."' not expected but was one", msg)
161 return actual111 return actual
162end112end
163113
164
165function assert_match(pattern, actual, msg)114function assert_match(pattern, actual, msg)
166 stats_inc("assertions")115 lunit_stats_inc("assertions")
167 check_msg("assert_match", msg)116 lunit_check_msg("assert_match", msg)
168 do_assert(is_string(pattern), "assert_match expects the pattern as a string")117 lunit_do_assert(is_string(pattern), "assert_match expects the pattern as a string")
169 do_assert(is_string(actual), "expected a string to match pattern '"..pattern.."' but was a '"..type(actual).."'", msg)118 lunit_do_assert(is_string(actual), "expected a string to match pattern '"..pattern.."' but was a '"..type(actual).."'", msg)
170 do_assert(not not string.find(actual, pattern), "expected '"..actual.."' to match pattern '"..pattern.."' but doesn't", msg)119 lunit_do_assert(not not string.find(actual, pattern), "expected '"..actual.."' to match pattern '"..pattern.."' but doesn't", msg)
171 return actual120 return actual
172end121end
173122
174
175function assert_not_match(pattern, actual, msg)123function assert_not_match(pattern, actual, msg)
176 stats_inc("assertions")124 lunit_stats_inc("assertions")
177 check_msg("assert_not_match", msg)125 lunit_check_msg("assert_not_match", msg)
178 do_assert(is_string(actual), "expected a string to not match pattern '"..pattern.."' but was a '"..type(actual).."'", msg)126 lunit_do_assert(is_string(actual), "expected a string to not match pattern '"..pattern.."' but was a '"..type(actual).."'", msg)
179 do_assert(string.find(actual, pattern) == nil, "expected '"..actual.."' to not match pattern '"..pattern.."' but it does", msg)127 lunit_do_assert(string.find(actual, pattern) == nil, "expected '"..actual.."' to not match pattern '"..pattern.."' but it does", msg)
180 return actual128 return actual
181end129end
182130
183
184function assert_nil(actual, msg)131function assert_nil(actual, msg)
185 stats_inc("assertions")132 lunit_stats_inc("assertions")
186 check_msg("assert_nil", msg)133 lunit_check_msg("assert_nil", msg)
187 do_assert(is_nil(actual), "nil expected but was a "..type(actual), msg)134 lunit_do_assert(is_nil(actual), "nil expected but was a "..type(actual), msg)
188 return actual135 return actual
189end136end
190137
191
192function assert_not_nil(actual, msg)138function assert_not_nil(actual, msg)
193 stats_inc("assertions")139 lunit_stats_inc("assertions")
194 check_msg("assert_not_nil", msg)140 lunit_check_msg("assert_not_nil", msg)
195 do_assert(not is_nil(actual), "nil not expected but was one", msg)141 lunit_do_assert(not is_nil(actual), "nil not expected but was one", msg)
196 return actual142 return actual
197end143end
198144
199
200function assert_boolean(actual, msg)145function assert_boolean(actual, msg)
201 stats_inc("assertions")146 lunit_stats_inc("assertions")
202 check_msg("assert_boolean", msg)147 lunit_check_msg("assert_boolean", msg)
203 do_assert(is_boolean(actual), "boolean expected but was a "..type(actual), msg)148 lunit_do_assert(is_boolean(actual), "boolean expected but was a "..type(actual), msg)
204 return actual149 return actual
205end150end
206151
207
208function assert_not_boolean(actual, msg)152function assert_not_boolean(actual, msg)
209 stats_inc("assertions")153 lunit_stats_inc("assertions")
210 check_msg("assert_not_boolean", msg)154 lunit_check_msg("assert_not_boolean", msg)
211 do_assert(not is_boolean(actual), "boolean not expected but was one", msg)155 lunit_do_assert(not is_boolean(actual), "boolean not expected but was one", msg)
212 return actual156 return actual
213end157end
214158
215
216function assert_number(actual, msg)159function assert_number(actual, msg)
217 stats_inc("assertions")160 lunit_stats_inc("assertions")
218 check_msg("assert_number", msg)161 lunit_check_msg("assert_number", msg)
219 do_assert(is_number(actual), "number expected but was a "..type(actual), msg)162 lunit_do_assert(is_number(actual), "number expected but was a "..type(actual), msg)
220 return actual163 return actual
221end164end
222165
223
224function assert_not_number(actual, msg)166function assert_not_number(actual, msg)
225 stats_inc("assertions")167 lunit_stats_inc("assertions")
226 check_msg("assert_not_number", msg)168 lunit_check_msg("assert_not_number", msg)
227 do_assert(not is_number(actual), "number not expected but was one", msg)169 lunit_do_assert(not is_number(actual), "number not expected but was one", msg)
228 return actual170 return actual
229end171end
230172
231
232function assert_string(actual, msg)173function assert_string(actual, msg)
233 stats_inc("assertions")174 lunit_stats_inc("assertions")
234 check_msg("assert_string", msg)175 lunit_check_msg("assert_string", msg)
235 do_assert(is_string(actual), "string expected but was a "..type(actual), msg)176 lunit_do_assert(is_string(actual), "string expected but was a "..type(actual), msg)
236 return actual177 return actual
237end178end
238179
239
240function assert_not_string(actual, msg)180function assert_not_string(actual, msg)
241 stats_inc("assertions")181 lunit_stats_inc("assertions")
242 check_msg("assert_not_string", msg)182 lunit_check_msg("assert_not_string", msg)
243 do_assert(not is_string(actual), "string not expected but was one", msg)183 lunit_do_assert(not is_string(actual), "string not expected but was one", msg)
244 return actual184 return actual
245end185end
246186
247
248function assert_table(actual, msg)187function assert_table(actual, msg)
249 stats_inc("assertions")188 lunit_stats_inc("assertions")
250 check_msg("assert_table", msg)189 lunit_check_msg("assert_table", msg)
251 do_assert(is_table(actual), "table expected but was a "..type(actual), msg)190 lunit_do_assert(is_table(actual), "table expected but was a "..type(actual), msg)
252 return actual191 return actual
253end192end
254193
255
256function assert_not_table(actual, msg)194function assert_not_table(actual, msg)
257 stats_inc("assertions")195 lunit_stats_inc("assertions")
258 check_msg("assert_not_table", msg)196 lunit_check_msg("assert_not_table", msg)
259 do_assert(not is_table(actual), "table not expected but was one", msg)197 lunit_do_assert(not is_table(actual), "table not expected but was one", msg)
260 return actual198 return actual
261end199end
262200
263
264function assert_function(actual, msg)201function assert_function(actual, msg)
265 stats_inc("assertions")202 lunit_stats_inc("assertions")
266 check_msg("assert_function", msg)203 lunit_check_msg("assert_function", msg)
267 do_assert(is_function(actual), "function expected but was a "..type(actual), msg)204 lunit_do_assert(is_function(actual), "function expected but was a "..type(actual), msg)
268 return actual205 return actual
269end206end
270207
271
272function assert_not_function(actual, msg)208function assert_not_function(actual, msg)
273 stats_inc("assertions")209 lunit_stats_inc("assertions")
274 check_msg("assert_not_function", msg)210 lunit_check_msg("assert_not_function", msg)
275 do_assert(not is_function(actual), "function not expected but was one", msg)211 lunit_do_assert(not is_function(actual), "function not expected but was one", msg)
276 return actual212 return actual
277end213end
278214
279
280function assert_thread(actual, msg)215function assert_thread(actual, msg)
281 stats_inc("assertions")216 lunit_stats_inc("assertions")
282 check_msg("assert_thread", msg)217 lunit_check_msg("assert_thread", msg)
283 do_assert(is_thread(actual), "thread expected but was a "..type(actual), msg)218 lunit_do_assert(is_thread(actual), "thread expected but was a "..type(actual), msg)
284 return actual219 return actual
285end220end
286221
287
288function assert_not_thread(actual, msg)222function assert_not_thread(actual, msg)
289 stats_inc("assertions")223 lunit_stats_inc("assertions")
290 check_msg("assert_not_thread", msg)224 lunit_check_msg("assert_not_thread", msg)
291 do_assert(not is_thread(actual), "thread not expected but was one", msg)225 lunit_do_assert(not is_thread(actual), "thread not expected but was one", msg)
292 return actual226 return actual
293end227end
294228
295
296function assert_userdata(actual, msg)229function assert_userdata(actual, msg)
297 stats_inc("assertions")230 lunit_stats_inc("assertions")
298 check_msg("assert_userdata", msg)231 lunit_check_msg("assert_userdata", msg)
299 do_assert(is_userdata(actual), "userdata expected but was a "..type(actual), msg)232 lunit_do_assert(is_userdata(actual), "userdata expected but was a "..type(actual), msg)
300 return actual233 return actual
301end234end
302235
303
304function assert_not_userdata(actual, msg)236function assert_not_userdata(actual, msg)
305 stats_inc("assertions")237 lunit_stats_inc("assertions")
306 check_msg("assert_not_userdata", msg)238 lunit_check_msg("assert_not_userdata", msg)
307 do_assert(not is_userdata(actual), "userdata not expected but was one", msg)239 lunit_do_assert(not is_userdata(actual), "userdata not expected but was one", msg)
308 return actual240 return actual
309end241end
310242
311
312function assert_error(msg, func)243function assert_error(msg, func)
313 stats_inc("assertions")244 lunit_stats_inc("assertions")
314 if is_nil(func) then func, msg = msg, nil end245 if is_nil(func) then func, msg = msg, nil end
315 check_msg("assert_error", msg)246 lunit_check_msg("assert_error", msg)
316 do_assert(is_function(func), "assert_error expects a function as the last argument but it was a "..type(func))247 lunit_do_assert(is_function(func), "assert_error expects a function as the last argument but it was a "..type(func))
317 local ok, errmsg = pcall(func)248 local ok, errmsg = pcall(func)
318 do_assert(ok == false, "error expected but no error occurred", msg)249 lunit_do_assert(ok == false, "error expected but no error occurred", msg)
319end250end
320251
321
322function assert_pass(msg, func)252function assert_pass(msg, func)
323 stats_inc("assertions")253 lunit_stats_inc("assertions")
324 if is_nil(func) then func, msg = msg, nil end254 if is_nil(func) then func, msg = msg, nil end
325 check_msg("assert_pass", msg)255 lunit_check_msg("assert_pass", msg)
326 do_assert(is_function(func), "assert_pass expects a function as the last argument but it was a "..type(func))256 lunit_do_assert(is_function(func), "assert_pass expects a function as the last argument but it was a "..type(func))
327 local ok, errmsg = pcall(func)257 local ok, errmsg = pcall(func)
328 if not ok then do_assert(ok == true, "no error expected but error was: "..errmsg, msg) end258 if not ok then lunit_do_assert(ok == true, "no error expected but error was: "..errmsg, msg) end
329end259end
330260
331261
332
333
334-----------------------------------------------------------262-----------------------------------------------------------
335-- Assert implementation that assumes it was called from --263-- Assert implementation that assumes it was called from --
336-- lunit code which was called directly from user code. --264-- lunit code which was called directly from user code. --
337-----------------------------------------------------------265-----------------------------------------------------------
338266function lunit_do_assert(assertion, base_msg, user_msg)
339function do_assert(assertion, base_msg, user_msg)
340 assert(is_boolean(assertion))267 assert(is_boolean(assertion))
341 assert(is_string(base_msg))268 assert(is_string(base_msg))
342 assert(is_string(user_msg) or is_nil(user_msg))269 assert(is_string(user_msg) or is_nil(user_msg))
@@ -352,8 +279,7 @@
352-------------------------------------------279-------------------------------------------
353-- Checks the msg argument in assert_xxx --280-- Checks the msg argument in assert_xxx --
354-------------------------------------------281-------------------------------------------
355282function lunit_check_msg(name, msg)
356function check_msg(name, msg)
357 assert(is_string(name))283 assert(is_string(name))
358 if not (is_nil(msg) or is_string(msg)) then284 if not (is_nil(msg) or is_string(msg)) then
359 error("lunit."..name.."() expects the optional message as a string but it was a "..type(msg).."!" ,3)285 error("lunit."..name.."() expects the optional message as a string but it was a "..type(msg).."!" ,3)
@@ -361,26 +287,23 @@
361end287end
362288
363289
364
365
366-------------------------------------290-------------------------------------
367-- Creates a new TestCase 'Object' --291-- Creates a new TestCase 'Object' --
368-------------------------------------292-------------------------------------
369293function lunit.TestCase(name)
370function TestCase(name)294 lunit_do_assert(is_string(name), "lunit.TestCase() needs a string as an argument")
371 do_assert(is_string(name), "lunit.TestCase() needs a string as an argument")
372 local tc = {295 local tc = {
373 __lunit_name = name;296 __lunit_name = name;
374 __lunit_setup = nil;297 __lunit_setup = nil;
375 __lunit_tests = { };298 __lunit_tests = { };
376 __lunit_teardown = nil;299 __lunit_teardown = nil;
377 }300 }
378 setmetatable(tc, tc_mt)301 setmetatable(tc, lunit.tc_mt)
379 table.insert(testcases, tc)302 table.insert(lunit.testcases, tc)
380 return tc303 return tc
381end304end
382305
383tc_mt = {306lunit.tc_mt = {
384 __newindex = function(tc, key, value)307 __newindex = function(tc, key, value)
385 rawset(tc, key, value)308 rawset(tc, key, value)
386 if is_string(key) and is_function(value) then309 if is_string(key) and is_function(value) then
@@ -396,84 +319,69 @@
396 end319 end
397}320}
398321
399
400
401-----------------------------------------322-----------------------------------------
402-- Wrap Functions in a TestCase object --323-- Wrap Functions in a TestCase object --
403-----------------------------------------324-----------------------------------------
404
405function wrap(name, ...)325function wrap(name, ...)
406 if is_function(name) then326 if is_function(name) then
407 table.insert(arg, 1, name)327 table.insert(arg, 1, name)
408 name = "Anonymous Testcase"328 name = "Anonymous Testcase"
409 end329 end
410330
411 local tc = TestCase(name)331 local tc = lunit.TestCase(name)
412 for index, test in ipairs(arg) do332 for index, test in ipairs(arg) do
413 tc["Test #"..tostring(index)] = test333 tc["Test #"..tostring(index)] = test
414 end334 end
415 return tc335 return tc
416end336end
417337
418
419
420
421
422
423----------------------------------338----------------------------------
424-- Runs the complete Test Suite --339-- Runs the complete Test Suite --
425----------------------------------340----------------------------------
426341function lunit.run()
427function run()
428
429 ---------------------------342 ---------------------------
430 -- Initialize statistics --343 -- Initialize statistics --
431 ---------------------------344 ---------------------------
432345 lunit.stats.ntestcases = 0 -- Total number of Test Cases
433 stats.testcases = 0 -- Total number of Test Cases346 lunit.stats.tests = 0 -- Total number of all Tests in all Test Cases
434 stats.tests = 0 -- Total number of all Tests in all Test Cases347 lunit.stats.run = 0 -- Number of Tests run
435 stats.run = 0 -- Number of Tests run348 lunit.stats.notrun = 0 -- Number of Tests not run
436 stats.notrun = 0 -- Number of Tests not run349 lunit.stats.failed = 0 -- Number of Tests failed
437 stats.failed = 0 -- Number of Tests failed350 lunit.stats.passed = 0 -- Number of Test passed
438 stats.passed = 0 -- Number of Test passed351 lunit.stats.assertions = 0 -- Number of all assertions made in all Test in all Test Cases
439 stats.assertions = 0 -- Number of all assertions made in all Test in all Test Cases
440352
441 --------------------------------353 --------------------------------
442 -- Count Test Cases and Tests --354 -- Count Test Cases and Tests --
443 --------------------------------355 --------------------------------
444356 lunit.stats.ntestcases = #lunit.testcases
445 stats.testcases = table.getn(testcases)357
446358 for _, tc in ipairs(lunit.testcases) do
447 for _, tc in ipairs(testcases) do359 lunit_stats_inc("tests" , #tc.__lunit_tests)
448 stats_inc("tests" , table.getn(tc.__lunit_tests))
449 end360 end
450361
451 ------------------362 ------------------
452 -- Print Header --363 -- Print Header --
453 ------------------364 ------------------
454
455 print()365 print()
456 print("#### Test Suite with "..stats.tests.." Tests in "..stats.testcases.." Test Cases loaded.")366 print("#### Test Suite with "..lunit.stats.tests.." Tests in "..lunit.stats.ntestcases.." Test Cases loaded.")
457367
458 ------------------------368 ------------------------
459 -- Run all Test Cases --369 -- Run all Test Cases --
460 ------------------------370 ------------------------
461371 for _, tc in ipairs(lunit.testcases) do
462 for _, tc in ipairs(testcases) do372 lunit_run_testcase(tc)
463 run_testcase(tc)
464 end373 end
465374
466 ------------------375 ------------------
467 -- Print Footer --376 -- Print Footer --
468 ------------------377 ------------------
469
470 print()378 print()
471 print("#### Test Suite finished.")379 print("#### Test Suite finished.")
472380
473 local msg_assertions = stats.assertions.." Assertions checked. "381 local msg_assertions = lunit.stats.assertions.." Assertions checked. "
474 local msg_passed = stats.passed == stats.tests and "All Tests passed" or stats.passed.." Tests passed"382 local msg_passed = lunit.stats.passed == lunit.stats.tests and "All Tests passed" or lunit.stats.passed.." Tests passed"
475 local msg_failed = stats.failed > 0 and ", "..stats.failed.." failed" or ""383 local msg_failed = lunit.stats.failed > 0 and ", "..lunit.stats.failed.." failed" or ""
476 local msg_run = stats.notrun > 0 and ", "..stats.notrun.." not run" or ""384 local msg_run = lunit.stats.notrun > 0 and ", "..lunit.stats.notrun.." not run" or ""
477385
478 print()386 print()
479 print(msg_assertions..msg_passed..msg_failed..msg_run.."!")387 print(msg_assertions..msg_passed..msg_failed..msg_run.."!")
@@ -481,23 +389,17 @@
481 -----------------389 -----------------
482 -- Return code --390 -- Return code --
483 -----------------391 -----------------
484392 if lunit.stats.passed == lunit.stats.tests then
485 if stats.passed == stats.tests then
486 return 0393 return 0
487 else394 else
488 return 1395 return 1
489 end396 end
490end397end
491398
492
493
494
495-----------------------------399-----------------------------
496-- Runs a single Test Case --400-- Runs a single Test Case --
497-----------------------------401-----------------------------
498402function lunit_run_testcase(tc)
499function run_testcase(tc)
500
501 assert(is_table(tc))403 assert(is_table(tc))
502 assert(is_table(tc.__lunit_tests))404 assert(is_table(tc.__lunit_tests))
503 assert(is_string(tc.__lunit_name))405 assert(is_string(tc.__lunit_name))
@@ -507,7 +409,6 @@
507 --------------------------------------------409 --------------------------------------------
508 -- Protected call to a Test Case function --410 -- Protected call to a Test Case function --
509 --------------------------------------------411 --------------------------------------------
510
511 local function call(errprefix, func)412 local function call(errprefix, func)
512 assert(is_string(errprefix))413 assert(is_string(errprefix))
513 assert(is_function(func))414 assert(is_function(func))
@@ -522,7 +423,6 @@
522 ------------------------------------423 ------------------------------------
523 -- Calls setup() on the Test Case --424 -- Calls setup() on the Test Case --
524 ------------------------------------425 ------------------------------------
525
526 local function setup()426 local function setup()
527 if tc.__lunit_setup then427 if tc.__lunit_setup then
528 return call("ERROR: setup() failed", tc.__lunit_setup)428 return call("ERROR: setup() failed", tc.__lunit_setup)
@@ -534,15 +434,14 @@
534 ------------------------------------------434 ------------------------------------------
535 -- Calls a single Test on the Test Case --435 -- Calls a single Test on the Test Case --
536 ------------------------------------------436 ------------------------------------------
537
538 local function run(testname)437 local function run(testname)
539 assert(is_string(testname))438 assert(is_string(testname))
540 assert(is_function(tc[testname]))439 assert(is_function(tc[testname]))
541 local ok = call("FAIL: "..testname, tc[testname])440 local ok = call("FAIL: "..testname, tc[testname])
542 if not ok then441 if not ok then
543 stats_inc("failed")442 lunit_stats_inc("failed")
544 else443 else
545 stats_inc("passed")444 lunit_stats_inc("passed")
546 end445 end
547 return ok446 return ok
548 end447 end
@@ -550,7 +449,6 @@
550 ---------------------------------------449 ---------------------------------------
551 -- Calls teardown() on the Test Case --450 -- Calls teardown() on the Test Case --
552 ---------------------------------------451 ---------------------------------------
553
554 local function teardown()452 local function teardown()
555 if tc.__lunit_teardown then453 if tc.__lunit_teardown then
556 call("WARNING: teardown() failed", tc.__lunit_teardown)454 call("WARNING: teardown() failed", tc.__lunit_teardown)
@@ -560,117 +458,28 @@
560 ---------------------------------458 ---------------------------------
561 -- Run all Tests on a TestCase --459 -- Run all Tests on a TestCase --
562 ---------------------------------460 ---------------------------------
563
564 print()461 print()
565 print("#### Running '"..tc.__lunit_name.."' ("..table.getn(tc.__lunit_tests).." Tests)...")462 print("#### Running '"..tc.__lunit_name.."' ("..#tc.__lunit_tests.." Tests)...")
566463
567 for _, testname in ipairs(tc.__lunit_tests) do464 for _, testname in ipairs(tc.__lunit_tests) do
568 if setup() then465 if setup() then
569 run(testname)466 run(testname)
570 stats_inc("run")467 lunit_stats_inc("run")
571 teardown()468 teardown()
572 else469 else
573 print("WARN: Skipping '"..testname.."'...")470 print("WARN: Skipping '"..testname.."'...")
574 stats_inc("notrun")471 lunit_stats_inc("notrun")
575 end472 end
576 end473 end
577474end
578end
579
580
581
582
583---------------------
584-- Import function --
585---------------------
586
587function import(name)
588
589 do_assert(is_string(name), "lunit.import() expects a single string as argument")
590
591 local user_env = getfenv(2)
592
593 --------------------------------------------------
594 -- Installs a specific function in the user env --
595 --------------------------------------------------
596
597 local function install(funcname)
598 user_env[funcname] = P[funcname]
599 end
600
601
602 ----------------------------------------------------------
603 -- Install functions matching a pattern in the user env --
604 ----------------------------------------------------------
605
606 local function install_pattern(pattern)
607 for funcname, _ in pairs(P) do
608 if string.find(funcname, pattern) then
609 install(funcname)
610 end
611 end
612 end
613
614 ------------------------------------------------------------
615 -- Installs assert() and all assert_xxx() in the user env --
616 ------------------------------------------------------------
617
618 local function install_asserts()
619 install_pattern("^assert.*")
620 end
621
622 -------------------------------------------
623 -- Installs all is_xxx() in the user env --
624 -------------------------------------------
625
626 local function install_tests()
627 install_pattern("^is_.+")
628 end
629
630 if name == "asserts" or name == "assertions" then
631 install_asserts()
632 elseif name == "tests" or name == "checks" then
633 install_tests()
634 elseif name == "all" then
635 install_asserts()
636 install_tests()
637 install("TestCase")
638 elseif string.find(name, "^assert.*") and P[name] then
639 install(name)
640 elseif string.find(name, "^is_.+") and P[name] then
641 install(name)
642 elseif name == "TestCase" then
643 install("TestCase")
644 else
645 error("luniit.import(): invalid function '"..name.."' to import", 2)
646 end
647end
648
649
650
651
652--------------------------------------------------
653-- Installs a private environment on the caller --
654--------------------------------------------------
655
656function setprivfenv()
657 local new_env = { }
658 local new_env_mt = { __index = getfenv(2) }
659 setmetatable(new_env, new_env_mt)
660 setfenv(2, new_env)
661end
662
663
664
665475
666--------------------------------------------------476--------------------------------------------------
667-- Increments a counter in the statistics table --477-- Increments a counter in the statistics table --
668--------------------------------------------------478--------------------------------------------------
669479function lunit_stats_inc(varname, value)
670function stats_inc(varname, value)480 assert(is_table(lunit.stats))
671 assert(is_table(stats))
672 assert(is_string(varname))481 assert(is_string(varname))
673 assert(is_nil(value) or is_number(value))482 assert(is_nil(value) or is_number(value))
674 if not stats[varname] then return end483 if not lunit.stats[varname] then return end
675 stats[varname] = stats[varname] + (value or 1)484 lunit.stats[varname] = lunit.stats[varname] + (value or 1)
676end485end
677486
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2012-11-30 18:16:47 +0000
+++ src/CMakeLists.txt 2014-02-22 14:58:17 +0000
@@ -36,10 +36,10 @@
36 list (REMOVE_ITEM WL_SRCS_CC ${sourcefile})36 list (REMOVE_ITEM WL_SRCS_CC ${sourcefile})
37 list (REMOVE_ITEM WL_SRCS_H ${sourcefile})37 list (REMOVE_ITEM WL_SRCS_H ${sourcefile})
38 endif (${sourcefile} MATCHES ".*/test/.*")38 endif (${sourcefile} MATCHES ".*/test/.*")
39 # exclude test sources from Codecheck, as well as the scripting/pdep sources from lua39 # exclude test sources from Codecheck, as well as the scripting/eris sources from lua
40 if (NOT ${sourcefile} MATCHES ".*/scripting/pdep/.*" AND NOT ${sourcefile} MATCHES ".*/test/.*")40 if (NOT ${sourcefile} MATCHES ".*/scripting/eris/.*" AND NOT ${sourcefile} MATCHES ".*/test/.*")
41 list (APPEND WL_CODECHECK_SRCS ${sourcefile})41 list (APPEND WL_CODECHECK_SRCS ${sourcefile})
42 endif (NOT ${sourcefile} MATCHES ".*/scripting/pdep/.*" AND NOT ${sourcefile} MATCHES ".*/test/.*")42 endif (NOT ${sourcefile} MATCHES ".*/scripting/eris/.*" AND NOT ${sourcefile} MATCHES ".*/test/.*")
43endforeach (sourcefile ${WL_SRCS_CC} ${WL_SRCS_H})43endforeach (sourcefile ${WL_SRCS_CC} ${WL_SRCS_H})
44foreach (sourcefile ${WL_SRCS_CPP})44foreach (sourcefile ${WL_SRCS_CPP})
45 list (APPEND WL_CODECHECK_SRCS ${sourcefile})45 list (APPEND WL_CODECHECK_SRCS ${sourcefile})
@@ -91,29 +91,31 @@
91 # Tests need to link with SDL library without main91 # Tests need to link with SDL library without main
92 set(TEST_EXTRA_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/build_info.cc)92 set(TEST_EXTRA_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/build_info.cc)
93 set(TEST_EXTRA_LIBS ${SDL_LIBRARY} )93 set(TEST_EXTRA_LIBS ${SDL_LIBRARY} )
94 list(REMOVE_ITEM TEST_EXTRA_LIBS ${SDLMAIN_LIBRARY}) 94 list(REMOVE_ITEM TEST_EXTRA_LIBS ${SDLMAIN_LIBRARY})
9595
96 add_subdirectory(economy)96 add_subdirectory(economy)
97 add_subdirectory(io)97 add_subdirectory(io)
98 add_subdirectory(scripting)
99endif (WL_UNIT_TESTS)98endif (WL_UNIT_TESTS)
10099
100add_subdirectory(scripting)
101
101if (NOT MINIZIP_FOUND)102if (NOT MINIZIP_FOUND)
102 add_subdirectory(minizip)103 add_subdirectory(minizip)
103 target_link_libraries(widelands_all wl_minizip)104 target_link_libraries(widelands_all wl_minizip)
104endif (NOT MINIZIP_FOUND)105endif (NOT MINIZIP_FOUND)
105106
107target_link_libraries(widelands_all eris)
106target_link_libraries(widelands_all ${SDLIMAGE_LIBRARY})108target_link_libraries(widelands_all ${SDLIMAGE_LIBRARY})
107target_link_libraries(widelands_all ${SDLMIXER_LIBRARY})109target_link_libraries(widelands_all ${SDLMIXER_LIBRARY})
108target_link_libraries(widelands_all ${SDLNET_LIBRARY})110target_link_libraries(widelands_all ${SDLNET_LIBRARY})
109target_link_libraries(widelands_all ${SDLTTF_LIBRARY})111target_link_libraries(widelands_all ${SDLTTF_LIBRARY})
110target_link_libraries(widelands_all ${SDLGFX_LIBRARY})112target_link_libraries(widelands_all ${SDLGFX_LIBRARY})
111target_link_libraries(widelands_all ${LUA_LIBRARY})
112target_link_libraries(widelands_all ${PNG_LIBRARY})113target_link_libraries(widelands_all ${PNG_LIBRARY})
113target_link_libraries(widelands_all ${ZLIB_LIBRARY})114target_link_libraries(widelands_all ${ZLIB_LIBRARY})
114target_link_libraries(widelands_all ${OPENGL_gl_LIBRARY})115target_link_libraries(widelands_all ${OPENGL_gl_LIBRARY})
115target_link_libraries(widelands_all ${GLEW_LIBRARY})116target_link_libraries(widelands_all ${GLEW_LIBRARY})
116target_link_libraries(widelands_all ${Boost_SIGNALS_LIBRARY})117target_link_libraries(widelands_all ${Boost_SIGNALS_LIBRARY})
118
117if (MINIZIP_FOUND)119if (MINIZIP_FOUND)
118 target_link_libraries(widelands_all ${MINIZIP_LIBRARY})120 target_link_libraries(widelands_all ${MINIZIP_LIBRARY})
119endif (MINIZIP_FOUND)121endif (MINIZIP_FOUND)
120122
=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc 2013-07-26 20:19:36 +0000
+++ src/ai/defaultai.cc 2014-02-22 14:58:17 +0000
@@ -279,7 +279,7 @@
279 bld.get_ismine() ? BuildingObserver::MINE :279 bld.get_ismine() ? BuildingObserver::MINE :
280 BuildingObserver::PRODUCTIONSITE;280 BuildingObserver::PRODUCTIONSITE;
281281
282 container_iterate_const(Ware_Types, prod.inputs(), j)282 container_iterate_const(BillOfMaterials, prod.inputs(), j)
283 bo.inputs.push_back(j.current->first.value());283 bo.inputs.push_back(j.current->first.value());
284284
285 container_iterate_const285 container_iterate_const
286286
=== renamed file 'src/logic/ware_types.h' => 'src/logic/bill_of_materials.h'
--- src/logic/ware_types.h 2013-07-26 20:19:36 +0000
+++ src/logic/bill_of_materials.h 2014-02-22 14:58:17 +0000
@@ -25,12 +25,13 @@
25#include "logic/widelands.h"25#include "logic/widelands.h"
2626
27namespace Widelands {27namespace Widelands {
28typedef std::vector<std::pair<Ware_Index, uint32_t> > Ware_Types;28typedef std::pair<Ware_Index, uint32_t> WareAmount;
29typedef std::vector<WareAmount> BillOfMaterials;
2930
30// range structure for iterating ware range with index31// range structure for iterating ware range with index
31struct ware_range32struct ware_range
32{33{
33 ware_range(const Ware_Types & range) :34 ware_range(const BillOfMaterials & range) :
34 i(0), current(range.begin()), end(range.end()) {}35 i(0), current(range.begin()), end(range.end()) {}
35 ware_range & operator++ () {36 ware_range & operator++ () {
36 ++i; ++current; return *this;37 ++i; ++current; return *this;
@@ -39,9 +40,9 @@
39 operator bool() const {return not empty();}40 operator bool() const {return not empty();}
4041
41 uint8_t i;42 uint8_t i;
42 Ware_Types::const_iterator current;43 BillOfMaterials::const_iterator current;
43private:44private:
44 Ware_Types::const_iterator const end;45 BillOfMaterials::const_iterator const end;
45};46};
4647
47}48}
4849
=== modified file 'src/logic/building.h'
--- src/logic/building.h 2013-10-08 20:13:35 +0000
+++ src/logic/building.h 2014-02-22 14:58:17 +0000
@@ -28,10 +28,10 @@
2828
29#include "ai/ai_hints.h"29#include "ai/ai_hints.h"
30#include "io/filewrite.h"30#include "io/filewrite.h"
31#include "logic/bill_of_materials.h"
31#include "logic/buildcost.h"32#include "logic/buildcost.h"
32#include "logic/immovable.h"33#include "logic/immovable.h"
33#include "logic/soldier_counts.h"34#include "logic/soldier_counts.h"
34#include "logic/ware_types.h"
35#include "logic/wareworker.h"35#include "logic/wareworker.h"
36#include "logic/widelands.h"36#include "logic/widelands.h"
37#include "workarea_info.h"37#include "workarea_info.h"
3838
=== modified file 'src/logic/cmd_luacoroutine.cc'
--- src/logic/cmd_luacoroutine.cc 2013-07-26 20:19:36 +0000
+++ src/logic/cmd_luacoroutine.cc 2014-02-22 14:58:17 +0000
@@ -55,7 +55,7 @@
55 }55 }
56}56}
5757
58#define CMD_LUACOROUTINE_VERSION 158#define CMD_LUACOROUTINE_VERSION 2
59void Cmd_LuaCoroutine::Read59void Cmd_LuaCoroutine::Read
60 (FileRead & fr, Editor_Game_Base & egbase, Map_Map_Object_Loader & mol)60 (FileRead & fr, Editor_Game_Base & egbase, Map_Map_Object_Loader & mol)
61{61{
6262
=== modified file 'src/logic/editor_game_base.cc'
--- src/logic/editor_game_base.cc 2013-08-06 10:40:54 +0000
+++ src/logic/editor_game_base.cc 2014-02-22 14:58:17 +0000
@@ -69,7 +69,7 @@
69m_lasttrackserial (0)69m_lasttrackserial (0)
70{70{
71 if (not m_lua) // TODO SirVer: this is sooo ugly, I can't say71 if (not m_lua) // TODO SirVer: this is sooo ugly, I can't say
72 m_lua = create_LuaEditorInterface(this);72 m_lua = new LuaEditorInterface(this);
7373
74 g_sound_handler.m_egbase = this;74 g_sound_handler.m_egbase = this;
7575
7676
=== modified file 'src/logic/game.cc'
--- src/logic/game.cc 2013-10-16 19:29:35 +0000
+++ src/logic/game.cc 2014-02-22 14:58:17 +0000
@@ -128,7 +128,7 @@
128128
129129
130Game::Game() :130Game::Game() :
131 Editor_Game_Base(create_LuaGameInterface(this)),131 Editor_Game_Base(new LuaGameInterface(this)),
132 m_syncwrapper (*this, m_synchash),132 m_syncwrapper (*this, m_synchash),
133 m_ctrl (0),133 m_ctrl (0),
134 m_writereplay (true),134 m_writereplay (true),
135135
=== modified file 'src/logic/production_program.cc'
--- src/logic/production_program.cc 2013-08-14 10:39:59 +0000
+++ src/logic/production_program.cc 2014-02-22 14:58:17 +0000
@@ -66,7 +66,7 @@
66 (char * & parameters,66 (char * & parameters,
67 Ware_Type_Group & group,67 Ware_Type_Group & group,
68 const Tribe_Descr & tribe,68 const Tribe_Descr & tribe,
69 const Ware_Types & inputs)69 const BillOfMaterials & inputs)
70{70{
71 std::set<Ware_Index>::iterator last_insert_pos = group.first.end();71 std::set<Ware_Index>::iterator last_insert_pos = group.first.end();
72 uint8_t count = 1;72 uint8_t count = 1;
@@ -80,7 +80,7 @@
80 char const terminator = *parameters;80 char const terminator = *parameters;
81 *parameters = '\0';81 *parameters = '\0';
82 Ware_Index const ware_index = tribe.safe_ware_index(ware);82 Ware_Index const ware_index = tribe.safe_ware_index(ware);
83 for (wl_const_range<Ware_Types> i(inputs);; ++i)83 for (wl_const_range<BillOfMaterials> i(inputs);; ++i)
84 if (i.empty())84 if (i.empty())
85 throw game_data_error85 throw game_data_error
86 (_86 (_
8787
=== modified file 'src/logic/production_program.h'
--- src/logic/production_program.h 2013-09-22 18:01:36 +0000
+++ src/logic/production_program.h 2014-02-22 14:58:17 +0000
@@ -31,9 +31,9 @@
31#include "container_iterate.h"31#include "container_iterate.h"
32#include "io/filewrite.h"32#include "io/filewrite.h"
33#include "log.h"33#include "log.h"
34#include "logic/bill_of_materials.h"
34#include "logic/program_result.h"35#include "logic/program_result.h"
35#include "logic/tattribute.h"36#include "logic/tattribute.h"
36#include "logic/ware_types.h"
37#include "logic/widelands.h"37#include "logic/widelands.h"
3838
39struct Profile;39struct Profile;
@@ -80,7 +80,7 @@
80 (char * & parameters,80 (char * & parameters,
81 Ware_Type_Group & group,81 Ware_Type_Group & group,
82 const Tribe_Descr & tribe,82 const Tribe_Descr & tribe,
83 const Ware_Types & inputs);83 const BillOfMaterials & inputs);
8484
85 /// Returns from the program.85 /// Returns from the program.
86 ///86 ///
8787
=== modified file 'src/logic/productionsite.cc'
--- src/logic/productionsite.cc 2013-09-23 18:47:02 +0000
+++ src/logic/productionsite.cc 2014-02-22 14:58:17 +0000
@@ -84,7 +84,7 @@
84 while (Section::Value const * const val = s->get_next_val())84 while (Section::Value const * const val = s->get_next_val())
85 try {85 try {
86 if (Ware_Index const idx = tribe().ware_index(val->get_name())) {86 if (Ware_Index const idx = tribe().ware_index(val->get_name())) {
87 container_iterate_const(Ware_Types, inputs(), i)87 container_iterate_const(BillOfMaterials, inputs(), i)
88 if (i.current->first == idx)88 if (i.current->first == idx)
89 throw wexception("duplicated");89 throw wexception("duplicated");
90 int32_t const value = val->get_int();90 int32_t const value = val->get_int();
@@ -104,7 +104,7 @@
104 while (Section::Value const * const v = working_positions_s->get_next_val())104 while (Section::Value const * const v = working_positions_s->get_next_val())
105 try {105 try {
106 if (Ware_Index const woi = tribe().worker_index(v->get_name())) {106 if (Ware_Index const woi = tribe().worker_index(v->get_name())) {
107 container_iterate_const(Ware_Types, working_positions(), i)107 container_iterate_const(BillOfMaterials, working_positions(), i)
108 if (i.current->first == woi)108 if (i.current->first == woi)
109 throw wexception("duplicated");109 throw wexception("duplicated");
110 m_working_positions.push_back(std::pair<Ware_Index, uint32_t>(woi, v->get_positive()));110 m_working_positions.push_back(std::pair<Ware_Index, uint32_t>(woi, v->get_positive()));
@@ -365,8 +365,7 @@
365{365{
366 Building::init(egbase);366 Building::init(egbase);
367367
368368 const BillOfMaterials & inputs = descr().inputs();
369 const Ware_Types & inputs = descr().inputs();
370 m_input_queues.resize(inputs.size());369 m_input_queues.resize(inputs.size());
371 for (ware_range i(inputs); i; ++i)370 for (ware_range i(inputs); i; ++i)
372 m_input_queues[i.i] =371 m_input_queues[i.i] =
@@ -377,7 +376,7 @@
377376
378 // Request missing workers.377 // Request missing workers.
379 Working_Position * wp = m_working_positions;378 Working_Position * wp = m_working_positions;
380 container_iterate_const(Ware_Types, descr().working_positions(), i) {379 container_iterate_const(BillOfMaterials, descr().working_positions(), i) {
381 Ware_Index const worker_index = i.current->first;380 Ware_Index const worker_index = i.current->first;
382 for (uint32_t j = i.current->second; j; --j, ++wp)381 for (uint32_t j = i.current->second; j; --j, ++wp)
383 if (Worker * const worker = wp->worker)382 if (Worker * const worker = wp->worker)
@@ -490,7 +489,7 @@
490 molog("%s leaving\n", w.descname().c_str());489 molog("%s leaving\n", w.descname().c_str());
491 Working_Position * wp = m_working_positions;490 Working_Position * wp = m_working_positions;
492491
493 container_iterate_const(Ware_Types, descr().working_positions(), i) {492 container_iterate_const(BillOfMaterials, descr().working_positions(), i) {
494 Ware_Index const worker_index = i.current->first;493 Ware_Index const worker_index = i.current->first;
495 for (uint32_t j = i.current->second; j; --j, ++wp) {494 for (uint32_t j = i.current->second; j; --j, ++wp) {
496 Worker * const worker = wp->worker;495 Worker * const worker = wp->worker;
497496
=== modified file 'src/logic/productionsite.h'
--- src/logic/productionsite.h 2013-09-23 18:47:02 +0000
+++ src/logic/productionsite.h 2014-02-22 14:58:17 +0000
@@ -26,10 +26,10 @@
26#include <string>26#include <string>
27#include <vector>27#include <vector>
2828
29#include "logic/bill_of_materials.h"
29#include "logic/building.h"30#include "logic/building.h"
30#include "logic/production_program.h"31#include "logic/production_program.h"
31#include "logic/program_result.h"32#include "logic/program_result.h"
32#include "logic/ware_types.h"
3333
34namespace Widelands {34namespace Widelands {
3535
@@ -64,11 +64,11 @@
6464
65 uint32_t nr_working_positions() const {65 uint32_t nr_working_positions() const {
66 uint32_t result = 0;66 uint32_t result = 0;
67 container_iterate_const(Ware_Types, working_positions(), i)67 container_iterate_const(BillOfMaterials, working_positions(), i)
68 result += i.current->second;68 result += i.current->second;
69 return result;69 return result;
70 }70 }
71 const Ware_Types & working_positions() const {71 const BillOfMaterials & working_positions() const {
72 return m_working_positions;72 return m_working_positions;
73 }73 }
74 bool is_output_ware_type (const Ware_Index& i) const {74 bool is_output_ware_type (const Ware_Index& i) const {
@@ -77,7 +77,7 @@
77 bool is_output_worker_type(const Ware_Index& i) const {77 bool is_output_worker_type(const Ware_Index& i) const {
78 return m_output_worker_types.count(i);78 return m_output_worker_types.count(i);
79 }79 }
80 const Ware_Types & inputs() const {return m_inputs;}80 const BillOfMaterials & inputs() const {return m_inputs;}
81 typedef std::set<Ware_Index> Output;81 typedef std::set<Ware_Index> Output;
82 const Output & output_ware_types () const {return m_output_ware_types;}82 const Output & output_ware_types () const {return m_output_ware_types;}
83 const Output & output_worker_types() const {return m_output_worker_types;}83 const Output & output_worker_types() const {return m_output_worker_types;}
@@ -89,8 +89,8 @@
89 const std::vector<std::string> & compatibility_working_positions(const std::string & workername) const;89 const std::vector<std::string> & compatibility_working_positions(const std::string & workername) const;
9090
91private:91private:
92 Ware_Types m_working_positions;92 BillOfMaterials m_working_positions;
93 Ware_Types m_inputs;93 BillOfMaterials m_inputs;
94 Output m_output_ware_types;94 Output m_output_ware_types;
95 Output m_output_worker_types;95 Output m_output_worker_types;
96 Programs m_programs;96 Programs m_programs;
9797
=== modified file 'src/logic/tribe.cc'
--- src/logic/tribe.cc 2013-10-08 20:13:35 +0000
+++ src/logic/tribe.cc 2014-02-22 14:58:17 +0000
@@ -298,7 +298,7 @@
298 }298 }
299299
300 // Read initializations -- all scripts are initializations currently300 // Read initializations -- all scripts are initializations currently
301 ScriptContainer & scripts =301 const ScriptContainer& scripts =
302 egbase.lua().get_scripts_for("tribe_" + tribename);302 egbase.lua().get_scripts_for("tribe_" + tribename);
303 container_iterate_const(ScriptContainer, scripts, s) {303 container_iterate_const(ScriptContainer, scripts, s) {
304 std::unique_ptr<LuaTable> t =304 std::unique_ptr<LuaTable> t =
@@ -376,7 +376,7 @@
376 buf += name;376 buf += name;
377 buf += "/conf";377 buf += "/conf";
378378
379 LuaInterface * lua = create_LuaInterface();379 LuaInterface lua;
380 FileRead f;380 FileRead f;
381 if (f.TryOpen(*g_fs, buf.c_str())) {381 if (f.TryOpen(*g_fs, buf.c_str())) {
382 if (info)382 if (info)
@@ -389,30 +389,25 @@
389 std::string path = "tribes/" + name + "/scripting";389 std::string path = "tribes/" + name + "/scripting";
390 if (g_fs->IsDirectory(path)) {390 if (g_fs->IsDirectory(path)) {
391 std::unique_ptr<FileSystem> sub_fs(g_fs->MakeSubFileSystem(path));391 std::unique_ptr<FileSystem> sub_fs(g_fs->MakeSubFileSystem(path));
392 lua->register_scripts(*sub_fs, "tribe_" + name, "");392 lua.register_scripts(*sub_fs, "tribe_" + name, "");
393 }393 }
394394
395 ScriptContainer & scripts = lua->get_scripts_for("tribe_" + name);395 const ScriptContainer& scripts = lua.get_scripts_for("tribe_" + name);
396 container_iterate_const(ScriptContainer, scripts, s) {396 container_iterate_const(ScriptContainer, scripts, s) {
397 std::unique_ptr<LuaTable> t =397 std::unique_ptr<LuaTable> t =
398 lua->run_script("tribe_" + name, s->first);398 lua.run_script("tribe_" + name, s->first);
399399
400 info->initializations.push_back400 info->initializations.push_back
401 (TribeBasicInfo::Initialization401 (TribeBasicInfo::Initialization
402 (s->first, t->get_string("name")));402 (s->first, t->get_string("name")));
403 }403 }
404 } catch (const _wexception & e) {404 } catch (const _wexception & e) {
405 delete lua;
406 throw game_data_error405 throw game_data_error
407 ("reading basic info for tribe \"%s\": %s",406 ("reading basic info for tribe \"%s\": %s",
408 name.c_str(), e.what());407 name.c_str(), e.what());
409 }408 }
410
411 delete lua;
412 return true;409 return true;
413 }410 }
414
415 delete lua;
416 return false;411 return false;
417}412}
418413
419414
=== modified file 'src/map_io/widelands_map_buildingdata_data_packet.cc'
--- src/map_io/widelands_map_buildingdata_data_packet.cc 2013-10-08 20:13:35 +0000
+++ src/map_io/widelands_map_buildingdata_data_packet.cc 2014-02-22 14:58:17 +0000
@@ -876,7 +876,7 @@
876 ProductionSite::Working_Position & wp_begin =876 ProductionSite::Working_Position & wp_begin =
877 *productionsite.m_working_positions;877 *productionsite.m_working_positions;
878 const ProductionSite_Descr & pr_descr = productionsite.descr();878 const ProductionSite_Descr & pr_descr = productionsite.descr();
879 const Ware_Types & working_positions = pr_descr.working_positions();879 const BillOfMaterials & working_positions = pr_descr.working_positions();
880880
881 uint16_t nr_worker_requests = fr.Unsigned16();881 uint16_t nr_worker_requests = fr.Unsigned16();
882 for (uint16_t i = nr_worker_requests; i; --i) {882 for (uint16_t i = nr_worker_requests; i; --i) {
@@ -892,7 +892,7 @@
892 // Find a working position that matches this request.892 // Find a working position that matches this request.
893 ProductionSite::Working_Position * wp = &wp_begin;893 ProductionSite::Working_Position * wp = &wp_begin;
894 for894 for
895 (wl_const_range<Ware_Types>895 (wl_const_range<BillOfMaterials>
896 j(working_positions);;896 j(working_positions);;
897 ++j)897 ++j)
898 {898 {
@@ -973,7 +973,7 @@
973 const Worker_Descr & worker_descr = worker->descr();973 const Worker_Descr & worker_descr = worker->descr();
974 ProductionSite::Working_Position * wp = &wp_begin;974 ProductionSite::Working_Position * wp = &wp_begin;
975 for975 for
976 (wl_const_range<Ware_Types> j(working_positions);;976 (wl_const_range<BillOfMaterials> j(working_positions);;
977 ++j)977 ++j)
978 {978 {
979 if (j.empty())979 if (j.empty())
980980
=== modified file 'src/map_io/widelands_map_scripting_data_packet.cc'
--- src/map_io/widelands_map_scripting_data_packet.cc 2013-10-08 20:13:35 +0000
+++ src/map_io/widelands_map_scripting_data_packet.cc 2014-02-22 14:58:17 +0000
@@ -30,6 +30,9 @@
3030
31namespace Widelands {31namespace Widelands {
3232
33namespace {
34const int SCRIPTING_DATA_PACKET_VERSION=1;
35} // namespace
33/*36/*
34 * ========================================================================37 * ========================================================================
35 * PUBLIC IMPLEMENTATION38 * PUBLIC IMPLEMENTATION
@@ -51,6 +54,12 @@
51 upcast(Game, g, &egbase);54 upcast(Game, g, &egbase);
52 Widelands::FileRead fr;55 Widelands::FileRead fr;
53 if (g and fr.TryOpen(fs, "scripting/globals.dump")) {56 if (g and fr.TryOpen(fs, "scripting/globals.dump")) {
57 const uint32_t sentinel = fr.Unsigned32();
58 const uint32_t packet_version = fr.Unsigned32();
59 if (sentinel != 0xDEADBEEF && packet_version != SCRIPTING_DATA_PACKET_VERSION) {
60 throw game_data_error(
61 "This savegame is from an older version of Widelands and can not be loaded any more.");
62 }
54 upcast(LuaGameInterface, lgi, &g->lua());63 upcast(LuaGameInterface, lgi, &g->lua());
55 lgi->read_global_env(fr, mol, fr.Unsigned32());64 lgi->read_global_env(fr, mol, fr.Unsigned32());
56 }65 }
@@ -60,11 +69,11 @@
60void Map_Scripting_Data_Packet::Write69void Map_Scripting_Data_Packet::Write
61 (FileSystem & fs, Editor_Game_Base & egbase, Map_Map_Object_Saver & mos)70 (FileSystem & fs, Editor_Game_Base & egbase, Map_Map_Object_Saver & mos)
62{71{
63 ScriptContainer & p = egbase.lua().get_scripts_for("map");72 const ScriptContainer& p = egbase.lua().get_scripts_for("map");
6473
65 fs.EnsureDirectoryExists("scripting");74 fs.EnsureDirectoryExists("scripting");
6675
67 for (ScriptContainer::iterator i = p.begin(); i != p.end(); ++i) {76 for (ScriptContainer::const_iterator i = p.begin(); i != p.end(); ++i) {
68 std::string fname = "scripting/";77 std::string fname = "scripting/";
69 fname += i->first;78 fname += i->first;
70 fname += ".lua";79 fname += ".lua";
@@ -74,9 +83,10 @@
7483
75 // Dump the global environment if this is a game and not in the editor84 // Dump the global environment if this is a game and not in the editor
76 if (upcast(Game, g, &egbase)) {85 if (upcast(Game, g, &egbase)) {
77
78 Widelands::FileWrite fw;86 Widelands::FileWrite fw;
79 Widelands::FileWrite::Pos pos = fw.GetPos();87 fw.Unsigned32(0xDEADBEEF); // Sentinel, because there was no packet version.
88 fw.Unsigned32(SCRIPTING_DATA_PACKET_VERSION);
89 const Widelands::FileWrite::Pos pos = fw.GetPos();
80 fw.Unsigned32(0); // N bytes written, follows below90 fw.Unsigned32(0); // N bytes written, follows below
8191
82 upcast(LuaGameInterface, lgi, &g->lua());92 upcast(LuaGameInterface, lgi, &g->lua());
8393
=== modified file 'src/network/netclient.cc'
--- src/network/netclient.cc 2013-09-20 17:47:29 +0000
+++ src/network/netclient.cc 2014-02-22 14:58:17 +0000
@@ -119,15 +119,14 @@
119 file = 0;119 file = 0;
120120
121 // Temporarily register win condition scripts to get the default121 // Temporarily register win condition scripts to get the default
122 LuaInterface * lua = create_LuaInterface();122 LuaInterface lua;
123 lua->register_scripts(*g_fs, "win_conditions", "scripting/win_conditions");123 lua.register_scripts(*g_fs, "win_conditions", "scripting/win_conditions");
124 ScriptContainer sc = lua->get_scripts_for("win_conditions");124 ScriptContainer sc = lua.get_scripts_for("win_conditions");
125 std::vector<std::string> win_conditions;125 std::vector<std::string> win_conditions;
126 container_iterate_const(ScriptContainer, sc, wc)126 container_iterate_const(ScriptContainer, sc, wc)
127 win_conditions.push_back(wc->first);127 win_conditions.push_back(wc->first);
128 assert(win_conditions.size());128 assert(win_conditions.size());
129 d->settings.win_condition = win_conditions[0];129 d->settings.win_condition = win_conditions[0];
130 delete lua;
131}130}
132131
133NetClient::~NetClient ()132NetClient::~NetClient ()
@@ -906,16 +905,16 @@
906 info.name = packet.String();905 info.name = packet.String();
907906
908 // Get initializations (we have to do this locally, for translations)907 // Get initializations (we have to do this locally, for translations)
909 LuaInterface * lua = create_LuaInterface();908 LuaInterface lua;
910 std::string path = "tribes/" + info.name;909 std::string path = "tribes/" + info.name;
911 if (g_fs->IsDirectory(path)) {910 if (g_fs->IsDirectory(path)) {
912 std::unique_ptr<FileSystem> sub_fs(g_fs->MakeSubFileSystem(path));911 std::unique_ptr<FileSystem> sub_fs(g_fs->MakeSubFileSystem(path));
913 lua->register_scripts(*sub_fs, "tribe_" + info.name, "scripting");912 lua.register_scripts(*sub_fs, "tribe_" + info.name, "scripting");
914 }913 }
915914
916 for (uint8_t j = packet.Unsigned8(); j; --j) {915 for (uint8_t j = packet.Unsigned8(); j; --j) {
917 std::string const name = packet.String();916 std::string const name = packet.String();
918 std::unique_ptr<LuaTable> t = lua->run_script917 std::unique_ptr<LuaTable> t = lua.run_script
919 ("tribe_" + info.name, name);918 ("tribe_" + info.name, name);
920 info.initializations.push_back919 info.initializations.push_back
921 (TribeBasicInfo::Initialization(name, t->get_string("name")));920 (TribeBasicInfo::Initialization(name, t->get_string("name")));
922921
=== modified file 'src/network/nethost.cc'
--- src/network/nethost.cc 2013-10-08 20:13:35 +0000
+++ src/network/nethost.cc 2014-02-22 14:58:17 +0000
@@ -293,7 +293,7 @@
293 if (m_win_conditions.size() < 1) {293 if (m_win_conditions.size() < 1) {
294 // Register win condition scripts294 // Register win condition scripts
295 if (!m_lua)295 if (!m_lua)
296 m_lua = create_LuaInterface();296 m_lua = new LuaInterface();
297 m_lua->register_scripts(*g_fs, "win_conditions", "scripting/win_conditions");297 m_lua->register_scripts(*g_fs, "win_conditions", "scripting/win_conditions");
298298
299 ScriptContainer sc = m_lua->get_scripts_for("win_conditions");299 ScriptContainer sc = m_lua->get_scripts_for("win_conditions");
300300
=== modified file 'src/scripting/CMakeLists.txt'
--- src/scripting/CMakeLists.txt 2010-11-01 03:43:56 +0000
+++ src/scripting/CMakeLists.txt 2014-02-22 14:58:17 +0000
@@ -1,4 +1,77 @@
1file(GLOB_RECURSE ERIS_SRCS_C *.c)
2file(GLOB_RECURSE ERIS_SRCS_H *.h)
3
4SET(ERIS_SOURCES
5 eris/eris.c
6 eris/lapi.c
7 eris/lauxlib.c
8 eris/lbaselib.c
9 eris/lbitlib.c
10 eris/lcode.c
11 eris/lcorolib.c
12 eris/lctype.c
13 eris/ldblib.c
14 eris/ldebug.c
15 eris/ldo.c
16 eris/ldump.c
17 eris/lfunc.c
18 eris/lgc.c
19 eris/linit.c
20 eris/liolib.c
21 eris/llex.c
22 eris/lmathlib.c
23 eris/lmem.c
24 eris/loadlib.c
25 eris/lobject.c
26 eris/lopcodes.c
27 eris/loslib.c
28 eris/lparser.c
29 eris/lstate.c
30 eris/lstring.c
31 eris/lstrlib.c
32 eris/ltable.c
33 eris/ltablib.c
34 eris/ltm.c
35 eris/lua.c
36 eris/luac.c
37 eris/lundump.c
38 eris/lvm.c
39 eris/lzio.c
40)
41
42SET(ERIS_HEADERS
43 eris/eris.h
44 eris/lapi.h
45 eris/lauxlib.h
46 eris/lcode.h
47 eris/lctype.h
48 eris/ldebug.h
49 eris/ldo.h
50 eris/lfunc.h
51 eris/lgc.h
52 eris/llex.h
53 eris/llimits.h
54 eris/lmem.h
55 eris/lobject.h
56 eris/lopcodes.h
57 eris/lparser.h
58 eris/lstate.h
59 eris/lstring.h
60 eris/ltable.h
61 eris/ltm.h
62 eris/lua.h
63 eris/luaconf.h
64 eris/lualib.h
65 eris/lundump.h
66 eris/lvm.h
67 eris/lzio.h
68)
69
70add_library(eris STATIC
71 ${ERIS_SOURCES}
72 ${ERIS_HEADERS}
73)
74
1if (WL_UNIT_TESTS)75if (WL_UNIT_TESTS)
2 add_subdirectory(test)76 add_subdirectory(test)
3endif (WL_UNIT_TESTS)77endif (WL_UNIT_TESTS)
4
578
=== modified file 'src/scripting/c_utils.cc'
--- src/scripting/c_utils.cc 2013-07-26 20:19:36 +0000
+++ src/scripting/c_utils.cc 2014-02-22 14:58:17 +0000
@@ -107,9 +107,8 @@
107 vsnprintf(buffer, sizeof(buffer), fmt, va);107 vsnprintf(buffer, sizeof(buffer), fmt, va);
108 va_end(va);108 va_end(va);
109109
110
111 // Also create a traceback110 // Also create a traceback
112 lua_getfield(L, LUA_GLOBALSINDEX, "debug");111 lua_getglobal(L, "debug");
113 if (!lua_istable(L, -1)) {112 if (!lua_istable(L, -1)) {
114 lua_pop(L, 1);113 lua_pop(L, 1);
115 return 1;114 return 1;
@@ -127,4 +126,3 @@
127126
128 return 0;127 return 0;
129}128}
130
131129
=== modified file 'src/scripting/c_utils.h'
--- src/scripting/c_utils.h 2013-07-26 20:19:36 +0000
+++ src/scripting/c_utils.h 2014-02-22 14:58:17 +0000
@@ -20,12 +20,12 @@
20#ifndef C_UTILS_H20#ifndef C_UTILS_H
21#define C_UTILS_H21#define C_UTILS_H
2222
23#include <lua.hpp>
2423
25#include "scripting/factory.h"
26#include "logic/game.h"24#include "logic/game.h"
27#include "map_io/widelands_map_map_object_loader.h"25#include "map_io/widelands_map_map_object_loader.h"
28#include "map_io/widelands_map_map_object_saver.h"26#include "map_io/widelands_map_map_object_saver.h"
27#include "scripting/eris/lua.hpp"
28#include "scripting/factory.h"
2929
30Factory & get_factory(lua_State *);30Factory & get_factory(lua_State *);
31Widelands::Game & get_game(lua_State *);31Widelands::Game & get_game(lua_State *);
3232
=== removed file 'src/scripting/coroutine_impl.cc'
--- src/scripting/coroutine_impl.cc 2013-07-26 20:19:36 +0000
+++ src/scripting/coroutine_impl.cc 1970-01-01 00:00:00 +0000
@@ -1,144 +0,0 @@
1/*
2 * Copyright (C) 2006-2010 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20#include "scripting/coroutine_impl.h"
21
22#include <csetjmp>
23
24#include <boost/lexical_cast.hpp>
25
26#include "log.h"
27#include "logic/player.h"
28#include "scripting/c_utils.h"
29#include "scripting/lua_game.h"
30#include "scripting/lua_map.h"
31#include "scripting/persistence.h"
32
33LuaCoroutine_Impl::LuaCoroutine_Impl(lua_State * ms)
34 : m_L(ms), m_idx(LUA_REFNIL), m_nargs(0)
35{
36 m_reference();
37}
38
39LuaCoroutine_Impl::~LuaCoroutine_Impl()
40{
41 m_unreference();
42}
43
44int LuaCoroutine_Impl::resume(uint32_t * sleeptime)
45{
46 int rv = lua_resume(m_L, m_nargs);
47 m_nargs = 0;
48 int n = lua_gettop(m_L);
49
50 uint32_t sleep_for = 0;
51 if (n == 1) {
52 sleep_for = luaL_checkint32(m_L, -1);
53 lua_pop(m_L, 1);
54 }
55
56 if (sleeptime)
57 *sleeptime = sleep_for;
58
59 if (rv != 0 && rv != YIELDED) {
60 // lua_error() never returns. prints error and exit program imediately
61 //return lua_error(m_L);
62 const char * err = lua_tostring(m_L, -1);
63 throw LuaError(err);
64 }
65
66 return rv;
67}
68
69/*
70 * Push an argument that will be passed to the coroutine the next time it is
71 * resumed
72 */
73void LuaCoroutine_Impl::push_arg(const Widelands::Player * plr) {
74 to_lua<LuaGame::L_Player>(m_L, new LuaGame::L_Player(plr->player_number()));
75 m_nargs++;
76}
77void LuaCoroutine_Impl::push_arg(const Widelands::Coords & coords) {
78 to_lua<LuaMap::L_Field>(m_L, new LuaMap::L_Field(coords));
79 ++m_nargs;
80}
81
82#define COROUTINE_DATA_PACKET_VERSION 1
83uint32_t LuaCoroutine_Impl::write
84 (lua_State * parent, Widelands::FileWrite & fw,
85 Widelands::Map_Map_Object_Saver & mos)
86{
87 fw.Unsigned8(COROUTINE_DATA_PACKET_VERSION);
88
89 // The current numbers of arguments on the stack
90 fw.Unsigned32(m_nargs);
91
92 // Empty table + object to persist on the stack Stack
93 lua_newtable(parent);
94 lua_pushthread(m_L);
95 lua_xmove (m_L, parent, 1);
96
97 return persist_object(parent, fw, mos);
98}
99
100void LuaCoroutine_Impl::read
101 (lua_State * parent, Widelands::FileRead & fr,
102 Widelands::Map_Map_Object_Loader & mol, uint32_t size)
103{
104 uint8_t version = fr.Unsigned8();
105
106 if (version != COROUTINE_DATA_PACKET_VERSION)
107 throw wexception("Unknown data packet version: %i\n", version);
108
109 // The current numbers of arguments on the stack
110 m_nargs = fr.Unsigned32();
111
112 // Empty table + object to persist on the stack Stack
113 unpersist_object(parent, fr, mol, size);
114
115 m_L = luaL_checkthread(parent, -1);
116 lua_pop(parent, 1);
117
118 // Cache a lua reference to this object so that it does not
119 // get garbage collected
120 lua_pushthread(m_L);
121 m_idx = luaL_ref(m_L, LUA_REGISTRYINDEX);
122
123}
124
125/*
126 * ========================================================================
127 * PRIVATE FUNCTIONS
128 * ========================================================================
129 */
130/**
131 * Cache a lua reference to this object so that it does not get garbage
132 * collected
133 */
134void LuaCoroutine_Impl::m_reference() {
135 if (m_L) {
136 lua_pushthread(m_L);
137 m_idx = luaL_ref(m_L, LUA_REGISTRYINDEX);
138 }
139}
140void LuaCoroutine_Impl::m_unreference() {
141 luaL_unref(m_L, LUA_REGISTRYINDEX, m_idx);
142}
143
144
1450
=== removed file 'src/scripting/coroutine_impl.h'
--- src/scripting/coroutine_impl.h 2013-09-22 18:01:36 +0000
+++ src/scripting/coroutine_impl.h 1970-01-01 00:00:00 +0000
@@ -1,63 +0,0 @@
1/*
2 * Copyright (C) 2006-2010 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20#ifndef COROUTINE_IMPL_H
21#define COROUTINE_IMPL_H
22
23#include "scripting/scripting.h"
24
25namespace Widelands {
26 class Player;
27}
28
29/*
30============================================
31 Lua Coroutine
32============================================
33*/
34class LuaCoroutine_Impl : public LuaCoroutine {
35public:
36 LuaCoroutine_Impl(lua_State * L);
37 virtual ~LuaCoroutine_Impl();
38
39 virtual int get_status() {return lua_status(m_L);}
40 virtual int resume(uint32_t * sleeptime = 0);
41
42 virtual uint32_t write
43 (lua_State *, Widelands::FileWrite &,
44 Widelands::Map_Map_Object_Saver &);
45 virtual void read
46 (lua_State *, Widelands::FileRead &,
47 Widelands::Map_Map_Object_Loader &, uint32_t);
48
49 virtual void push_arg(const Widelands::Player *);
50 virtual void push_arg(const Widelands::Coords &);
51
52private:
53 void m_reference();
54 void m_unreference();
55
56private:
57 lua_State * m_L;
58 uint32_t m_idx;
59 uint32_t m_nargs;
60};
61
62
63#endif /* end of include guard: COROUTINE_IMPL_H */
640
=== added directory 'src/scripting/eris'
=== added file 'src/scripting/eris.h'
--- src/scripting/eris.h 1970-01-01 00:00:00 +0000
+++ src/scripting/eris.h 2014-02-22 14:58:17 +0000
@@ -0,0 +1,29 @@
1/*
2 * Copyright (C) 2006-2013 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 *
18 */
19
20#ifndef WL_ERIS_H
21#define WL_ERIS_H
22
23// We need eris in a cpp context. Include it as such.
24extern "C" {
25#include "scripting/eris/eris.h"
26}
27
28
29#endif /* end of include guard: WL_ERIS_H */
030
=== renamed file 'src/scripting/README.pluto' => 'src/scripting/eris/README.eris'
--- src/scripting/README.pluto 2010-01-30 14:54:27 +0000
+++ src/scripting/eris/README.eris 2014-02-22 14:58:17 +0000
@@ -1,11 +1,11 @@
1Our scripting support contains the stripped down essentials of pluto[1]. You1This directory contains a verbatim copy of Eris by Florian Nücke.
2find the complete source and README of pluto on the github repo from one of2
3it's authors Rob Hoelz at [2]. Our version of pluto has been heavily modified3URL: https://github.com/fnuecke/eris
4to create system agnostic files and to work with the widelands file streaming.4VERSION: 2e39ecc7dcb73120dde775929227fa661fbc6bc0
5The Widelands Team wishes to expresses total and complete gratitude to the5
6authors of Pluto for making it public domain. 6We use this for heavy persistence and it also brings with it the Lua version
77that we use in Widelands. The Widelands Team wishes to expresses total and
8[1] http://luaforge.net/projects/pluto/8complete gratitude to the authors of Eris for making it available under the MIT
9[2] http://github.com/hoelzro/pluto9License.
1010
11 -- SirVer, in behalf of the Widelands Team11 -- SirVer, in behalf of the Widelands Team
1212
=== added file 'src/scripting/eris/eris.c'
--- src/scripting/eris/eris.c 1970-01-01 00:00:00 +0000
+++ src/scripting/eris/eris.c 2014-02-22 14:58:17 +0000
@@ -0,0 +1,2658 @@
1/*
2Eris - Heavy-duty persistence for Lua 5.2.2 - Based on Pluto
3Copyright (c) 2013 by Florian Nuecke.
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21THE SOFTWARE.
22*/
23
24/* Standard library headers. */
25#include <assert.h>
26#include <math.h>
27#include <stdint.h>
28#include <stdio.h>
29#include <string.h>
30
31/* Not using stdbool because Visual Studio lives in the past... */
32typedef int bool;
33#define false 0
34#define true 1
35
36/* Mark us as part of the Lua core to get access to what we need. */
37#define LUA_CORE
38
39/* Public Lua headers. */
40#include "lua.h"
41#include "lauxlib.h"
42#include "lualib.h"
43
44/* Internal Lua headers. */
45#include "ldebug.h"
46#include "ldo.h"
47#include "lfunc.h"
48#include "lobject.h"
49#include "lstate.h"
50#include "lstring.h"
51#include "lzio.h"
52
53/* Eris header. */
54#include "eris.h"
55
56/*
57** {===========================================================================
58** Default settings.
59** ============================================================================
60*/
61
62/* Note that these are the default settings. They can be changed either from C
63 * by calling eris_g|set_setting() or from Lua using eris.settings(). */
64
65/* The metatable key we use to allow customized persistence for tables and
66 * userdata. */
67static const char *const kPersistKey = "__persist";
68
69/* Whether to pass the IO object (reader/writer) to the special function
70 * defined in the metafield or not. This is disabled per default because it
71 * mey allow Lua scripts to do more than they should. Enable this as needed. */
72static const bool kPassIOToPersist = false;
73
74/* Whether to persist debug information such as line numbers and upvalue and
75 * local variable names. */
76static const bool kWriteDebugInformation = true;
77
78/* Generate a human readable "path" that is shown together with error messages
79 * to indicate where in the object the error occurred. For example:
80 * eris.persist({false, bad = setmetatable({}, {__persist = false})})
81 * Will produce: main:1: attempt to persist forbidden table (root.bad)
82 * This can be used for debugging, but is disabled per default due to the
83 * processing and memory overhead this introduces. */
84static const bool kGeneratePath = false;
85
86/* The maximum object complexity. This is the number of allowed recursions when
87 * persisting or unpersisting an object, for example for nested tables. This is
88 * used to avoid segfaults when writing or reading user data. */
89static const lua_Unsigned kMaxComplexity = 10000;
90
91/*
92** ============================================================================
93** Lua internals interfacing.
94** ============================================================================
95*/
96
97/* Lua internals we use. We define these as macros to make it easier to swap
98 * them out, should the need ever arise. For example, the later Pluto versions
99 * copied these function to own files (presumably to allow building it as an
100 * extra shared library). These should be all functions we use that are not
101 * declared in lua.h or lauxlib.h. If there are some still directly in the code
102 * they were missed and should be replaced with a macro added here instead. */
103/* I'm quite sure we won't ever want to do this, because Eris needs a slightly
104 * patched Lua version to be able to persist some of the library functions,
105 * anyway: it needs to put the continuation C functions in the perms table. */
106/* ldebug.h */
107#define eris_ci_func ci_func
108/* ldo.h */
109#define eris_incr_top incr_top
110#define eris_savestack savestack
111#define eris_restorestack restorestack
112#define eris_reallocstack luaD_reallocstack
113/* lfunc.h */
114#define eris_newproto luaF_newproto
115#define eris_newLclosure luaF_newLclosure
116#define eris_newupval luaF_newupval
117#define eris_findupval luaF_findupval
118/* lgc.h */
119#define eris_barrierproto luaC_barrierproto
120/* lmem.h */
121#define eris_reallocvector luaM_reallocvector
122/* lobject.h */
123#define eris_ttypenv ttypenv
124#define eris_setnilvalue setnilvalue
125#define eris_setclLvalue setclLvalue
126#define eris_setobj setobj
127#define eris_setsvalue2n setsvalue2n
128/* lstate.h */
129#define eris_isLua isLua
130#define eris_gch gch
131#define eris_gco2uv gco2uv
132#define eris_obj2gco obj2gco
133#define eris_extendCI luaE_extendCI
134/* lstring. h */
135#define eris_newlstr luaS_newlstr
136/* lzio.h */
137#define eris_initbuffer luaZ_initbuffer
138#define eris_buffer luaZ_buffer
139#define eris_sizebuffer luaZ_sizebuffer
140#define eris_bufflen luaZ_bufflen
141#define eris_init luaZ_init
142#define eris_read luaZ_read
143
144/* These are required for cross-platform support, since the size of TValue may
145 * differ, so the byte offset used by savestack/restorestack in Lua it is not a
146 * valid measure. */
147#define eris_savestackidx(L, p) ((p) - (L)->stack)
148#define eris_restorestackidx(L, n) ((L)->stack + (n))
149
150/* Enabled if we have a patched version of Lua (for accessing internals). */
151#if 1
152
153/* Functions in Lua libraries used to access C functions we need to add to the
154 * permanents table to fully support yielded coroutines. */
155extern void eris_permbaselib(lua_State *L, bool forUnpersist);
156extern void eris_permcorolib(lua_State *L, bool forUnpersist);
157extern void eris_permloadlib(lua_State *L, bool forUnpersist);
158extern void eris_permiolib(lua_State *L, bool forUnpersist);
159extern void eris_permstrlib(lua_State *L, bool forUnpersist);
160
161/* Utility macro for populating the perms table with internal C functions. */
162#define populateperms(L, forUnpersist) {\
163 eris_permbaselib(L, forUnpersist);\
164 eris_permcorolib(L, forUnpersist);\
165 eris_permloadlib(L, forUnpersist);\
166 eris_permiolib(L, forUnpersist);\
167 eris_permstrlib(L, forUnpersist);\
168}
169
170#else
171
172/* Does nothing if we don't have a patched version of Lua. */
173#define populateperms(L, forUnpersist) ((void)0)
174
175#endif
176
177/*
178** ============================================================================
179** Constants, settings, types and forward declarations.
180** ============================================================================
181*/
182
183/* The "type" we write when we persist a value via a replacement from the
184 * permanents table. This is just an arbitrary number, but it must we lower
185 * than the reference offset (below) and outside the range Lua uses for its
186 * types (> LUA_TOTALTAGS). */
187#define ERIS_PERMANENT (LUA_TOTALTAGS + 1)
188
189/* This is essentially the first reference we'll use. We do this to save one
190 * field in our persisted data: if the value is smaller than this, the object
191 * itself follows, otherwise we have a reference to an already unpersisted
192 * object. Note that in the reftable the actual entries are still stored
193 * starting at the first array index to have a sequence (when unpersisting). */
194#define ERIS_REFERENCE_OFFSET (ERIS_PERMANENT + 1)
195
196/* Avoids having to write the NULL all the time, plus makes it easier adding
197 * a custom error message should you ever decide you want one. */
198#define eris_checkstack(L, n) luaL_checkstack(L, n, NULL)
199
200/* Used for internal consistency checks, for debugging. These are true asserts
201 * in the sense that they should never fire, even for bad inputs. */
202#if 0
203#define eris_assert(c) assert(c)
204#define eris_ifassert(e) e
205#else
206#define eris_assert(c) ((void)0)
207#define eris_ifassert(e) ((void)0)
208#endif
209
210/* State information when persisting an object. */
211typedef struct PersistInfo {
212 lua_Writer writer;
213 void *ud;
214 const char *metafield;
215 bool writeDebugInfo;
216} PersistInfo;
217
218/* State information when unpersisting an object. */
219typedef struct UnpersistInfo {
220 ZIO zio;
221 size_t sizeof_int;
222 size_t sizeof_size_t;
223} UnpersistInfo;
224
225/* Info shared in persist and unpersist. */
226typedef struct Info {
227 lua_State *L;
228 lua_Unsigned level;
229 int refcount; /* int because rawseti/rawgeti takes an int. */
230 lua_Unsigned maxComplexity;
231 bool generatePath;
232 bool passIOToPersist;
233 /* Which one it really is will always be clear from the context. */
234 union {
235 PersistInfo pi;
236 UnpersistInfo upi;
237 } u;
238} Info;
239
240/* Type names, used for error messages. */
241static const char *const kTypenames[] = {
242 "nil", "boolean", "lightuserdata", "number", "string",
243 "table", "function", "userdata", "thread", "proto", "upval",
244 "deadkey", "permanent"
245};
246
247/* Setting names as used in eris.settings / eris_g|set_setting. Also, the
248 * addresses of these static variables are used as keys in the registry of Lua
249 * states to save the current values of the settings (as light userdata). */
250static const char *const kSettingMetafield = "spkey";
251static const char *const kSettingPassIOToPersist = "spio";
252static const char *const kSettingGeneratePath = "path";
253static const char *const kSettingWriteDebugInfo = "debug";
254static const char *const kSettingMaxComplexity = "maxrec";
255
256/* Header we prefix to persisted data for a quick check when unpersisting. */
257static char const kHeader[] = { 'E', 'R', 'I', 'S' };
258#define HEADER_LENGTH sizeof(kHeader)
259
260/* Floating point number used to check compatibility of loaded data. */
261static const lua_Number kHeaderNumber = (lua_Number)-1.234567890;
262
263/* Stack indices of some internal values/tables, to avoid magic numbers. */
264#define PERMIDX 1
265#define REFTIDX 2
266#define BUFFIDX 3
267#define PATHIDX 4
268
269/* }======================================================================== */
270
271/*
272** {===========================================================================
273** Utility functions.
274** ============================================================================
275*/
276
277/* Pushes an object into the reference table when unpersisting. This creates an
278 * entry pointing from the id the object is referenced by to the object. */
279static int
280registerobject(Info *info) { /* perms reftbl ... obj */
281 const int reference = ++(info->refcount);
282 eris_checkstack(info->L, 1);
283 lua_pushvalue(info->L, -1); /* perms reftbl ... obj obj */
284 lua_rawseti(info->L, REFTIDX, reference); /* perms reftbl ... obj */
285 return reference;
286}
287
288/** ======================================================================== */
289
290/* Pushes a TString* onto the stack if it holds a value, nil if it is NULL. */
291static void
292pushtstring(lua_State* L, TString *ts) { /* ... */
293 if (ts) {
294 eris_setsvalue2n(L, L->top, ts);
295 eris_incr_top(L); /* ... str */
296 }
297 else {
298 lua_pushnil(L); /* ... nil */
299 }
300}
301
302/* Creates a copy of the string on top of the stack and sets it as the value
303 * of the specified TString**. */
304static void
305copytstring(lua_State* L, TString **ts) {
306 size_t length;
307 const char *value = lua_tolstring(L, -1, &length);
308 *ts = eris_newlstr(L, value, length);
309}
310
311/** ======================================================================== */
312
313/* Pushes the specified segment to the current path, if we're generating one.
314 * This supports formatting strings using Lua's formatting capabilities. */
315static void
316pushpath(Info *info, const char* fmt, ...) { /* perms reftbl var path ... */
317 if (!info->generatePath) {
318 return;
319 }
320 else {
321 va_list argp;
322 eris_checkstack(info->L, 1);
323 va_start(argp, fmt);
324 lua_pushvfstring(info->L, fmt, argp); /* perms reftbl var path ... str */
325 va_end(argp);
326 lua_rawseti(info->L, PATHIDX, luaL_len(info->L, PATHIDX) + 1);
327 } /* perms reftbl var path ... */
328}
329
330/* Pops the last added segment from the current path if we're generating one. */
331static void
332poppath(Info *info) { /* perms reftbl var path ... */
333 if (!info->generatePath) {
334 return;
335 }
336 eris_checkstack(info->L, 1);
337 lua_pushnil(info->L); /* perms reftbl var path ... nil */
338 lua_rawseti(info->L, PATHIDX, luaL_len(info->L, PATHIDX));
339} /* perms reftbl var path ... */
340
341/* Concatenates all current path segments into one string, pushes it and
342 * returns it. This is relatively inefficient, but it's for errors only and
343 * keeps the stack small, so it's better this way. */
344static const char*
345path(Info *info) { /* perms reftbl var path ... */
346 if (!info->generatePath) {
347 return "";
348 }
349 eris_checkstack(info->L, 3);
350 lua_pushstring(info->L, ""); /* perms reftbl var path ... str */
351 lua_pushnil(info->L); /* perms reftbl var path ... str nil */
352 while (lua_next(info->L, PATHIDX)) { /* perms reftbl var path ... str k v */
353 lua_insert(info->L, -2); /* perms reftbl var path ... str v k */
354 lua_insert(info->L, -3); /* perms reftbl var path ... k str v */
355 lua_concat(info->L, 2); /* perms reftbl var path ... k str */
356 lua_insert(info->L, -2); /* perms reftbl var path ... str k */
357 } /* perms reftbl var path ... str */
358 return lua_tostring(info->L, -1);
359}
360
361/* Generates an error message with the appended path, if available. */
362static int
363eris_error(Info *info, const char *fmt, ...) { /* ... */
364 va_list argp;
365 eris_checkstack(info->L, 5);
366
367 luaL_where(info->L, 1); /* ... where */
368 va_start(argp, fmt);
369 lua_pushvfstring(info->L, fmt, argp); /* ... where str */
370 va_end(argp);
371 if (info->generatePath) {
372 lua_pushstring(info->L, " ("); /* ... where str " (" */
373 path(info); /* ... where str " (" path */
374 lua_pushstring(info->L, ")"); /* ... where str " (" path ")" */
375 lua_concat(info->L, 5); /* ... msg */
376 }
377 else {
378 lua_concat(info->L, 2); /* ... msg */
379 }
380 return lua_error(info->L);
381}
382
383/** ======================================================================== */
384
385/* Tries to get a setting from the registry. */
386static bool
387get_setting(lua_State *L, void *key) { /* ... */
388 eris_checkstack(L, 1);
389 lua_pushlightuserdata(L, key); /* ... key */
390 lua_gettable(L, LUA_REGISTRYINDEX); /* ... value/nil */
391 if (lua_isnil(L, -1)) { /* ... nil */
392 lua_pop(L, 1); /* ... */
393 return false;
394 } /* ... value */
395 return true;
396}
397
398/* Stores a setting in the registry (or removes it if the value is nil). */
399static void
400set_setting(lua_State *L, void *key) { /* ... value */
401 eris_checkstack(L, 2);
402 lua_pushlightuserdata(L, key); /* ... value key */
403 lua_insert(L, -2); /* ... key value */
404 lua_settable(L, LUA_REGISTRYINDEX); /* ... */
405}
406
407/* Used as a callback for luaL_opt to check boolean setting values. */
408static void
409checkboolean(lua_State *L, int narg) { /* ... bool? ... */
410 if (!lua_isboolean(L, narg)) { /* ... :( ... */
411 luaL_argerror(L, narg, lua_pushfstring(L,
412 "boolean expected, got %s", lua_typename(L, lua_type(L, narg))));
413 } /* ... bool ... */
414}
415
416/* }======================================================================== */
417
418/*
419** {===========================================================================
420** Persist and unpersist.
421** ============================================================================
422*/
423
424/* I have macros and I'm not afraid to use them! These are highly situational
425 * and assume an Info* named 'info' is available. */
426
427/* Writes a raw memory block with the specified size. */
428#define WRITE_RAW(value, size) {\
429 if (info->u.pi.writer(info->L, (value), (size), info->u.pi.ud)) \
430 eris_error(info, "could not write data"); }
431
432/* Writes a single value with the specified type. */
433#define WRITE_VALUE(value, type) write_##type(info, value)
434
435/* Writes a typed array with the specified length. */
436#define WRITE(value, length, type) { \
437 int i; for (i = 0; i < length; ++i) WRITE_VALUE((value)[i], type); }
438
439/** ======================================================================== */
440
441/* Reads a raw block of memory with the specified size. */
442#define READ_RAW(value, size) {\
443 if (eris_read(&info->u.upi.zio, (value), (size))) \
444 eris_error(info, "could not read data"); }
445
446/* Reads a single value with the specified type. */
447#define READ_VALUE(type) read_##type(info)
448
449/* Reads a typed array with the specified length. */
450#define READ(value, length, type) { \
451 int i; for (i = 0; i < length; ++i) (value)[i] = READ_VALUE(type); }
452
453/** ======================================================================== */
454
455static void
456write_uint8_t(Info *info, uint8_t value) {
457 WRITE_RAW(&value, sizeof(uint8_t));
458}
459
460static void
461write_uint16_t(Info *info, uint16_t value) {
462 write_uint8_t(info, value);
463 write_uint8_t(info, value >> 8);
464}
465
466static void
467write_uint32_t(Info *info, uint32_t value) {
468 write_uint8_t(info, value);
469 write_uint8_t(info, value >> 8);
470 write_uint8_t(info, value >> 16);
471 write_uint8_t(info, value >> 24);
472}
473
474static void
475write_uint64_t(Info *info, uint64_t value) {
476 write_uint8_t(info, value);
477 write_uint8_t(info, value >> 8);
478 write_uint8_t(info, value >> 16);
479 write_uint8_t(info, value >> 24);
480 write_uint8_t(info, value >> 32);
481 write_uint8_t(info, value >> 40);
482 write_uint8_t(info, value >> 48);
483 write_uint8_t(info, value >> 56);
484}
485
486static void
487write_int16_t(Info *info, int16_t value) {
488 write_uint16_t(info, (uint16_t)value);
489}
490
491static void
492write_int32_t(Info *info, int32_t value) {
493 write_uint32_t(info, (uint32_t)value);
494}
495
496static void
497write_int64_t(Info *info, int64_t value) {
498 write_uint64_t(info, (uint64_t)value);
499}
500
501static void
502write_float32(Info *info, float value) {
503 uint32_t rep;
504 memcpy(&rep, &value, sizeof(float));
505 write_uint32_t(info, rep);
506}
507
508static void
509write_float64(Info *info, double value) {
510 uint64_t rep;
511 memcpy(&rep, &value, sizeof(double));
512 write_uint64_t(info, rep);
513}
514
515/* Note regarding the following: any decent compiler should be able
516 * to reduce these to just the write call, since sizeof is constant. */
517
518static void
519write_int(Info *info, int value) {
520 if (sizeof(int) == sizeof(int16_t)) {
521 write_int16_t(info, value);
522 }
523 else if (sizeof(int) == sizeof(int32_t)) {
524 write_int32_t(info, value);
525 }
526 else if (sizeof(int) == sizeof(int64_t)) {
527 write_int64_t(info, value);
528 }
529 else {
530 eris_error(info, "unsupported int type");
531 }
532}
533
534static void
535write_size_t(Info *info, size_t value) {
536 if (sizeof(size_t) == sizeof(uint16_t)) {
537 write_uint16_t(info, value);
538 }
539 else if (sizeof(size_t) == sizeof(uint32_t)) {
540 write_uint32_t(info, value);
541 }
542 else if (sizeof(size_t) == sizeof(uint64_t)) {
543 write_uint64_t(info, value);
544 }
545 else {
546 eris_error(info, "unsupported size_t type");
547 }
548}
549
550static void
551write_lua_Number(Info *info, lua_Number value) {
552 if (sizeof(lua_Number) == sizeof(uint32_t)) {
553 write_float32(info, value);
554 }
555 else if (sizeof(lua_Number) == sizeof(uint64_t)) {
556 write_float64(info, value);
557 }
558 else {
559 eris_error(info, "unsupported lua_Number type");
560 }
561}
562
563/* Note that Lua only ever uses 32 bits of the Instruction type, so we can
564 * assert that there will be no truncation, even if the underlying type has
565 * more bits (might be the case on some 64 bit systems). */
566
567static void
568write_Instruction(Info *info, Instruction value) {
569 if (sizeof(Instruction) == sizeof(uint32_t)) {
570 write_uint32_t(info, value);
571 }
572 else {
573 uint32_t pvalue = (uint32_t)value;
574 /* Lua only uses 32 bits for its instructions. */
575 eris_assert((Instruction)pvalue == value);
576 write_uint32_t(info, pvalue);
577 }
578}
579
580/** ======================================================================== */
581
582static uint8_t
583read_uint8_t(Info *info) {
584 uint8_t value;
585 READ_RAW(&value, sizeof(uint8_t));
586 return value;
587}
588
589static uint16_t
590read_uint16_t(Info *info) {
591 return (uint16_t)read_uint8_t(info) |
592 ((uint16_t)read_uint8_t(info) << 8);
593}
594
595static uint32_t
596read_uint32_t(Info *info) {
597 return (uint32_t)read_uint8_t(info) |
598 ((uint32_t)read_uint8_t(info) << 8) |
599 ((uint32_t)read_uint8_t(info) << 16) |
600 ((uint32_t)read_uint8_t(info) << 24);
601}
602
603static uint64_t
604read_uint64_t(Info *info) {
605 return (uint64_t)read_uint8_t(info) |
606 ((uint64_t)read_uint8_t(info) << 8) |
607 ((uint64_t)read_uint8_t(info) << 16) |
608 ((uint64_t)read_uint8_t(info) << 24) |
609 ((uint64_t)read_uint8_t(info) << 32) |
610 ((uint64_t)read_uint8_t(info) << 40) |
611 ((uint64_t)read_uint8_t(info) << 48) |
612 ((uint64_t)read_uint8_t(info) << 56);
613}
614
615static int16_t
616read_int16_t(Info *info) {
617 return (int16_t)read_uint16_t(info);
618}
619
620static int32_t
621read_int32_t(Info *info) {
622 return (int32_t)read_uint32_t(info);
623}
624
625static int64_t
626read_int64_t(Info *info) {
627 return (int64_t)read_uint64_t(info);
628}
629
630static float
631read_float32(Info *info) {
632 float value;
633 uint32_t rep = read_uint32_t(info);
634 memcpy(&value, &rep, sizeof(float));
635 return value;
636}
637
638static double
639read_float64(Info *info) {
640 double value;
641 uint64_t rep = read_uint64_t(info);
642 memcpy(&value, &rep, sizeof(double));
643 return value;
644}
645
646/* Note regarding the following: unlike with writing the sizeof check will be
647 * impossible to optimize away, since it depends on the input; however, the
648 * truncation check may be optimized away in the case where the read data size
649 * equals the native one, so reading data written on the same machine should be
650 * reasonably quick. Doing a (rather rudimentary) benchmark this did not have
651 * any measurable impact on performance. */
652
653static int
654read_int(Info *info) {
655 int value;
656 if (info->u.upi.sizeof_int == sizeof(int16_t)) {
657 int16_t pvalue = read_int16_t(info);
658 value = (int)pvalue;
659 if ((int32_t)value != pvalue) {
660 eris_error(info, "int value would get truncated");
661 }
662 }
663 else if (info->u.upi.sizeof_int == sizeof(int32_t)) {
664 int32_t pvalue = read_int32_t(info);
665 value = (int)pvalue;
666 if ((int32_t)value != pvalue) {
667 eris_error(info, "int value would get truncated");
668 }
669 }
670 else if (info->u.upi.sizeof_int == sizeof(int64_t)) {
671 int64_t pvalue = read_int64_t(info);
672 value = (int)pvalue;
673 if ((int64_t)value != pvalue) {
674 eris_error(info, "int value would get truncated");
675 }
676 }
677 else {
678 eris_error(info, "unsupported int type");
679 value = 0; /* not reached */
680 }
681 return value;
682}
683
684static size_t
685read_size_t(Info *info) {
686 size_t value;
687 if (info->u.upi.sizeof_size_t == sizeof(uint16_t)) {
688 uint16_t pvalue = read_uint16_t(info);
689 value = (size_t)pvalue;
690 if ((uint32_t)value != pvalue) {
691 eris_error(info, "size_t value would get truncated");
692 }
693 }
694 else if (info->u.upi.sizeof_size_t == sizeof(uint32_t)) {
695 uint32_t pvalue = read_uint32_t(info);
696 value = (size_t)pvalue;
697 if ((uint32_t)value != pvalue) {
698 eris_error(info, "size_t value would get truncated");
699 }
700 }
701 else if (info->u.upi.sizeof_size_t == sizeof(uint64_t)) {
702 uint64_t pvalue = read_uint64_t(info);
703 value = (size_t)pvalue;
704 if ((uint64_t)value != pvalue) {
705 eris_error(info, "size_t value would get truncated");
706 }
707 }
708 else {
709 eris_error(info, "unsupported size_t type");
710 value = 0; /* not reached */
711 }
712 return value;
713}
714
715static lua_Number
716read_lua_Number(Info *info) {
717 if (sizeof(lua_Number) == sizeof(uint32_t)) {
718 return read_float32(info);
719 }
720 else if (sizeof(lua_Number) == sizeof(uint64_t)) {
721 return read_float64(info);
722 }
723 else {
724 eris_error(info, "unsupported lua_Number type");
725 return 0; /* not reached */
726 }
727}
728
729static Instruction
730read_Instruction(Info *info) {
731 return (Instruction)read_uint32_t(info);
732}
733
734/** ======================================================================== */
735
736/* Forward declarations for recursively called top-level functions. */
737static void persist_keyed(Info*, int type);
738static void persist(Info*);
739static void unpersist(Info*);
740
741/*
742** ============================================================================
743** Simple types.
744** ============================================================================
745*/
746
747static void
748p_boolean(Info *info) { /* ... bool */
749 WRITE_VALUE(lua_toboolean(info->L, -1), uint8_t);
750}
751
752static void
753u_boolean(Info *info) { /* ... */
754 eris_checkstack(info->L, 1);
755 lua_pushboolean(info->L, READ_VALUE(uint8_t)); /* ... bool */
756
757 eris_assert(lua_type(info->L, -1) == LUA_TBOOLEAN);
758}
759
760/** ======================================================================== */
761
762static void
763p_pointer(Info *info) { /* ... ludata */
764 WRITE_VALUE((size_t)lua_touserdata(info->L, -1), size_t);
765}
766
767static void
768u_pointer(Info *info) { /* ... */
769 eris_checkstack(info->L, 1);
770 lua_pushlightuserdata(info->L, (void*)READ_VALUE(size_t)); /* ... ludata */
771
772 eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
773}
774
775/** ======================================================================== */
776
777static void
778p_number(Info *info) { /* ... num */
779 WRITE_VALUE(lua_tonumber(info->L, -1), lua_Number);
780}
781
782static void
783u_number(Info *info) { /* ... */
784 eris_checkstack(info->L, 1);
785 lua_pushnumber(info->L, READ_VALUE(lua_Number)); /* ... num */
786
787 eris_assert(lua_type(info->L, -1) == LUA_TNUMBER);
788}
789
790/** ======================================================================== */
791
792static void
793p_string(Info *info) { /* ... str */
794 size_t length;
795 const char *value = lua_tolstring(info->L, -1, &length);
796 WRITE_VALUE(length, size_t);
797 WRITE_RAW(value, length);
798}
799
800static void
801u_string(Info *info) { /* ... */
802 eris_checkstack(info->L, 2);
803 {
804 /* TODO Can we avoid this copy somehow? (Without it getting too nasty) */
805 const size_t length = READ_VALUE(size_t);
806 char *value = lua_newuserdata(info->L, length * sizeof(char)); /* ... tmp */
807 READ_RAW(value, length);
808 lua_pushlstring(info->L, value, length); /* ... tmp str */
809 lua_replace(info->L, -2); /* ... str */
810 }
811 registerobject(info);
812
813 eris_assert(lua_type(info->L, -1) == LUA_TSTRING);
814}
815
816/*
817** ============================================================================
818** Tables and userdata.
819** ============================================================================
820*/
821
822static void
823p_metatable(Info *info) { /* ... obj */
824 eris_checkstack(info->L, 1);
825 pushpath(info, "@metatable");
826 if (!lua_getmetatable(info->L, -1)) { /* ... obj mt? */
827 lua_pushnil(info->L); /* ... obj nil */
828 } /* ... obj mt/nil */
829 persist(info); /* ... obj mt/nil */
830 lua_pop(info->L, 1); /* ... obj */
831 poppath(info);
832}
833
834static void
835u_metatable(Info *info) { /* ... tbl */
836 eris_checkstack(info->L, 1);
837 pushpath(info, "@metatable");
838 unpersist(info); /* ... tbl mt/nil? */
839 if (lua_istable(info->L, -1)) { /* ... tbl mt */
840 lua_setmetatable(info->L, -2); /* ... tbl */
841 }
842 else if (lua_isnil(info->L, -1)) { /* ... tbl nil */
843 lua_pop(info->L, 1); /* ... tbl */
844 }
845 else { /* tbl :( */
846 eris_error(info, "bad metatable, not nil or table");
847 }
848 poppath(info);
849}
850
851/** ======================================================================== */
852
853static void
854p_literaltable(Info *info) { /* ... tbl */
855 eris_checkstack(info->L, 3);
856
857 /* Persist all key / value pairs. */
858 lua_pushnil(info->L); /* ... tbl nil */
859 while (lua_next(info->L, -2)) { /* ... tbl k v */
860 lua_pushvalue(info->L, -2); /* ... tbl k v k */
861
862 if (info->generatePath) {
863 if (lua_type(info->L, -1) == LUA_TSTRING) {
864 const char *key = lua_tostring(info->L, -1);
865 pushpath(info, ".%s", key);
866 }
867 else {
868 const char *key = luaL_tolstring(info->L, -1, NULL);
869 pushpath(info, "[%s]", key);
870 lua_pop(info->L, 1);
871 }
872 }
873
874 persist(info); /* ... tbl k v k */
875 lua_pop(info->L, 1); /* ... tbl k v */
876 persist(info); /* ... tbl k v */
877 lua_pop(info->L, 1); /* ... tbl k */
878
879 poppath(info);
880 } /* ... tbl */
881
882 /* Terminate list. */
883 lua_pushnil(info->L); /* ... tbl nil */
884 persist(info); /* ... tbl nil */
885 lua_pop(info->L, 1); /* ... tbl */
886
887 p_metatable(info);
888}
889
890static void
891u_literaltable(Info *info) { /* ... */
892 eris_checkstack(info->L, 3);
893
894 lua_newtable(info->L); /* ... tbl */
895
896 /* Preregister table for handling of cycles (keys, values or metatable). */
897 registerobject(info);
898
899 /* Unpersist all key / value pairs. */
900 for (;;) {
901 pushpath(info, "@key");
902 unpersist(info); /* ... tbl key/nil */
903 poppath(info);
904 if (lua_isnil(info->L, -1)) { /* ... tbl nil */
905 lua_pop(info->L, 1); /* ... tbl */
906 break;
907 } /* ... tbl key */
908
909 if (info->generatePath) {
910 if (lua_type(info->L, -1) == LUA_TSTRING) {
911 const char *key = lua_tostring(info->L, -1);
912 pushpath(info, ".%s", key);
913 }
914 else {
915 const char *key = luaL_tolstring(info->L, -1, NULL);
916 pushpath(info, "[%s]", key);
917 lua_pop(info->L, 1);
918 }
919 }
920
921 unpersist(info); /* ... tbl key value? */
922 if (!lua_isnil(info->L, -1)) { /* ... tbl key value */
923 lua_rawset(info->L, -3); /* ... tbl */
924 }
925 else {
926 eris_error(info, "bad table value, got a nil value");
927 }
928
929 poppath(info);
930 }
931
932 u_metatable(info); /* ... tbl */
933}
934
935/** ======================================================================== */
936
937static void
938p_literaluserdata(Info *info) { /* ... udata */
939 const size_t size = lua_rawlen(info->L, -1);
940 const void *value = lua_touserdata(info->L, -1);
941 WRITE_VALUE(size, size_t);
942 WRITE_RAW(value, size);
943 p_metatable(info); /* ... udata */
944}
945
946static void
947u_literaluserdata(Info *info) { /* ... */
948 eris_checkstack(info->L, 1);
949 {
950 size_t size = READ_VALUE(size_t);
951 void *value = lua_newuserdata(info->L, size); /* ... udata */
952 READ_RAW(value, size); /* ... udata */
953 }
954 registerobject(info);
955 u_metatable(info);
956}
957
958/** ======================================================================== */
959
960typedef void (*Callback) (Info*);
961
962static void
963p_special(Info *info, Callback literal) { /* ... obj */
964 int allow = (lua_type(info->L, -1) == LUA_TTABLE);
965 eris_checkstack(info->L, 4);
966
967 /* Check whether we should persist literally, or via the metafunction. */
968 if (lua_getmetatable(info->L, -1)) { /* ... obj mt */
969 lua_pushstring(info->L, info->u.pi.metafield); /* ... obj mt pkey */
970 lua_rawget(info->L, -2); /* ... obj mt persist? */
971 switch (lua_type(info->L, -1)) {
972 /* No entry, act according to default. */
973 case LUA_TNIL: /* ... obj mt nil */
974 lua_pop(info->L, 2); /* ... obj */
975 break;
976
977 /* Boolean value, tells us whether allowed or not. */
978 case LUA_TBOOLEAN: /* ... obj mt bool */
979 allow = lua_toboolean(info->L, -1);
980 lua_pop(info->L, 2); /* ... obj */
981 break;
982
983 /* Function value, call it and don't persist literally. */
984 case LUA_TFUNCTION: /* ... obj mt func */
985 lua_replace(info->L, -2); /* ... obj func */
986 lua_pushvalue(info->L, -2); /* ... obj func obj */
987
988 if (info->passIOToPersist) {
989 lua_pushlightuserdata(info->L, info->u.pi.writer);
990 /* ... obj func obj writer */
991 lua_pushlightuserdata(info->L, info->u.pi.ud);
992 /* ... obj func obj writer ud */
993 lua_call(info->L, 3, 1); /* ... obj func? */
994 }
995 else {
996 lua_call(info->L, 1, 1); /* ... obj func? */
997 }
998 if (!lua_isfunction(info->L, -1)) { /* ... obj :( */
999 eris_error(info, "%s did not return a function",
1000 info->u.pi.metafield);
1001 } /* ... obj func */
1002
1003 /* Special persistence, call this function when unpersisting. */
1004 WRITE_VALUE(true, uint8_t);
1005 persist(info); /* ... obj func */
1006 lua_pop(info->L, 1); /* ... obj */
1007 return;
1008 default: /* ... obj mt :( */
1009 eris_error(info, "%d not nil, boolean, or function",
1010 info->u.pi.metafield);
1011 return; /* not reached */
1012 }
1013 }
1014
1015 if (allow) {
1016 /* Not special but literally persisted object. */
1017 WRITE_VALUE(false, uint8_t);
1018 literal(info); /* ... obj */
1019 }
1020 else if (lua_type(info->L, -1) == LUA_TTABLE) {
1021 eris_error(info, "attempt to persist forbidden table");
1022 }
1023 else {
1024 eris_error(info, "attempt to literally persist userdata");
1025 }
1026}
1027
1028static void
1029u_special(Info *info, int type, Callback literal) { /* ... */
1030 eris_checkstack(info->L, 2);
1031 if (READ_VALUE(uint8_t)) {
1032 int reference;
1033 /* Reserve entry in the reftable before unpersisting the function to keep
1034 * the reference order intact. We can set this to nil at first, because
1035 * there's no way the special function would access this. */
1036 lua_pushnil(info->L); /* ... nil */
1037 reference = registerobject(info);
1038 lua_pop(info->L, 1); /* ... */
1039 /* Increment reference counter by one to compensate for the increment when
1040 * persisting a special object. */
1041 unpersist(info); /* ... spfunc? */
1042 if (!lua_isfunction(info->L, -1)) { /* ... :( */
1043 eris_error(info, "invalid restore function");
1044 } /* ... spfunc */
1045
1046 if (info->passIOToPersist) {
1047 lua_pushlightuserdata(info->L, &info->u.upi.zio); /* ... spfunc zio */
1048 lua_call(info->L, 1, 1); /* ... obj? */
1049 } else {
1050 lua_call(info->L, 0, 1); /* ... obj? */
1051 }
1052
1053 if (lua_type(info->L, -1) != type) { /* ... :( */
1054 eris_error(info, "bad unpersist function (%s expected, returned %s)",
1055 kTypenames[type], kTypenames[lua_type(info->L, -1)]);
1056 } /* ... obj */
1057
1058 /* Update the reftable entry. */
1059 lua_pushvalue(info->L, -1); /* ... obj obj */
1060 lua_rawseti(info->L, 2, reference); /* ... obj */
1061 }
1062 else {
1063 literal(info); /* ... obj */
1064 }
1065}
1066
1067/** ======================================================================== */
1068
1069static void
1070p_table(Info *info) { /* ... tbl */
1071 p_special(info, p_literaltable); /* ... tbl */
1072}
1073
1074static void
1075u_table(Info *info) { /* ... */
1076 u_special(info, LUA_TTABLE, u_literaltable); /* ... tbl */
1077
1078 eris_assert(lua_type(info->L, -1) == LUA_TTABLE);
1079}
1080
1081/** ======================================================================== */
1082
1083static void
1084p_userdata(Info *info) { /* perms reftbl ... udata */
1085 p_special(info, p_literaluserdata);
1086}
1087
1088static void
1089u_userdata(Info *info) { /* ... */
1090 u_special(info, LUA_TUSERDATA, u_literaluserdata); /* ... udata */
1091
1092 eris_assert(lua_type(info->L, -1) == LUA_TUSERDATA);
1093}
1094
1095/*
1096** ============================================================================
1097** Closures and threads.
1098** ============================================================================
1099*/
1100
1101/* We track the actual upvalues themselves by pushing their "id" (meaning a
1102 * pointer to them) as lightuserdata to the reftable. This is safe because
1103 * lightuserdata will not normally end up in there, because simple value types
1104 * are always persisted directly (because that'll be just as large, memory-
1105 * wise as when pointing to the first instance). Same for protos. */
1106
1107static void
1108p_proto(Info *info) { /* ... proto */
1109 int i;
1110 const Proto *p = lua_touserdata(info->L, -1);
1111 eris_checkstack(info->L, 3);
1112
1113 /* Write general information. */
1114 WRITE_VALUE(p->linedefined, int);
1115 WRITE_VALUE(p->lastlinedefined, int);
1116 WRITE_VALUE(p->numparams, uint8_t);
1117 WRITE_VALUE(p->is_vararg, uint8_t);
1118 WRITE_VALUE(p->maxstacksize, uint8_t);
1119
1120 /* Write byte code. */
1121 WRITE_VALUE(p->sizecode, int);
1122 WRITE(p->code, p->sizecode, Instruction);
1123
1124 /* Write constants. */
1125 WRITE_VALUE(p->sizek, int);
1126 pushpath(info, ".constants");
1127 for (i = 0; i < p->sizek; ++i) {
1128 pushpath(info, "[%d]", i);
1129 eris_setobj(info->L, info->L->top++, &p->k[i]); /* ... lcl proto obj */
1130 persist(info); /* ... lcl proto obj */
1131 lua_pop(info->L, 1); /* ... lcl proto */
1132 poppath(info);
1133 }
1134 poppath(info);
1135
1136 /* Write child protos. */
1137 WRITE_VALUE(p->sizep, int);
1138 pushpath(info, ".protos");
1139 for (i = 0; i < p->sizep; ++i) {
1140 pushpath(info, "[%d]", i);
1141 lua_pushlightuserdata(info->L, p->p[i]); /* ... lcl proto proto */
1142 lua_pushvalue(info->L, -1); /* ... lcl proto proto proto */
1143 persist_keyed(info, LUA_TPROTO); /* ... lcl proto proto */
1144 lua_pop(info->L, 1); /* ... lcl proto */
1145 poppath(info);
1146 }
1147 poppath(info);
1148
1149 /* Write upvalues. */
1150 WRITE_VALUE(p->sizeupvalues, int);
1151 for (i = 0; i < p->sizeupvalues; ++i) {
1152 WRITE_VALUE(p->upvalues[i].instack, uint8_t);
1153 WRITE_VALUE(p->upvalues[i].idx, uint8_t);
1154 }
1155
1156 /* If we don't have to persist debug information skip the rest. */
1157 WRITE_VALUE(info->u.pi.writeDebugInfo, uint8_t);
1158 if (!info->u.pi.writeDebugInfo) {
1159 return;
1160 }
1161
1162 /* Write function source code. */
1163 pushtstring(info->L, p->source); /* ... lcl proto source */
1164 persist(info); /* ... lcl proto source */
1165 lua_pop(info->L, 1); /* ... lcl proto */
1166
1167 /* Write line information. */
1168 WRITE_VALUE(p->sizelineinfo, int);
1169 WRITE(p->lineinfo, p->sizelineinfo, int);
1170
1171 /* Write locals info. */
1172 WRITE_VALUE(p->sizelocvars, int);
1173 pushpath(info, ".locvars");
1174 for (i = 0; i < p->sizelocvars; ++i) {
1175 pushpath(info, "[%d]", i);
1176 WRITE_VALUE(p->locvars[i].startpc, int);
1177 WRITE_VALUE(p->locvars[i].endpc, int);
1178 pushtstring(info->L, p->locvars[i].varname); /* ... lcl proto varname */
1179 persist(info); /* ... lcl proto varname */
1180 lua_pop(info->L, 1); /* ... lcl proto */
1181 poppath(info);
1182 }
1183 poppath(info);
1184
1185 /* Write upvalue names. */
1186 pushpath(info, ".upvalnames");
1187 for (i = 0; i < p->sizeupvalues; ++i) {
1188 pushpath(info, "[%d]", i);
1189 pushtstring(info->L, p->upvalues[i].name); /* ... lcl proto name */
1190 persist(info); /* ... lcl proto name */
1191 lua_pop(info->L, 1); /* ... lcl proto */
1192 poppath(info);
1193 }
1194 poppath(info);
1195}
1196
1197static void
1198u_proto(Info *info) { /* ... proto */
1199 int i, n;
1200 Proto *p = lua_touserdata(info->L, -1);
1201 eris_assert(p);
1202
1203 eris_checkstack(info->L, 2);
1204
1205 /* Preregister proto for handling of cycles (probably impossible, but
1206 * maybe via the constants of the proto... not worth taking the risk). */
1207 registerobject(info);
1208
1209 /* Read general information. */
1210 p->linedefined = READ_VALUE(int);
1211 p->lastlinedefined = READ_VALUE(int);
1212 p->numparams = READ_VALUE(uint8_t);
1213 p->is_vararg = READ_VALUE(uint8_t);
1214 p->maxstacksize = READ_VALUE(uint8_t);
1215
1216 /* Read byte code. */
1217 p->sizecode = READ_VALUE(int);
1218 eris_reallocvector(info->L, p->code, 0, p->sizecode, Instruction);
1219 READ(p->code, p->sizecode, Instruction);
1220
1221 /* Read constants. */
1222 p->sizek = READ_VALUE(int);
1223 eris_reallocvector(info->L, p->k, 0, p->sizek, TValue);
1224 /* Set all values to nil to avoid confusing the GC. */
1225 for (i = 0, n = p->sizek; i < n; ++i) {
1226 eris_setnilvalue(&p->k[i]);
1227 }
1228 pushpath(info, ".constants");
1229 for (i = 0, n = p->sizek; i < n; ++i) {
1230 pushpath(info, "[%d]", i);
1231 unpersist(info); /* ... proto obj */
1232 eris_setobj(info->L, &p->k[i], info->L->top - 1);
1233 lua_pop(info->L, 1); /* ... proto */
1234 poppath(info);
1235 }
1236 poppath(info);
1237
1238 /* Read child protos. */
1239 p->sizep = READ_VALUE(int);
1240 eris_reallocvector(info->L, p->p, 0, p->sizep, Proto*);
1241 /* Null all entries to avoid confusing the GC. */
1242 memset(p->p, 0, p->sizep * sizeof(Proto*));
1243 pushpath(info, ".protos");
1244 for (i = 0, n = p->sizep; i < n; ++i) {
1245 Proto *cp;
1246 pushpath(info, "[%d]", i);
1247 p->p[i] = eris_newproto(info->L);
1248 lua_pushlightuserdata(info->L, p->p[i]); /* ... proto nproto */
1249 unpersist(info); /* ... proto nproto nproto/oproto */
1250 cp = lua_touserdata(info->L, -1);
1251 if (cp != p->p[i]) { /* ... proto nproto oproto */
1252 /* Just overwrite it, GC will clean this up. */
1253 p->p[i] = cp;
1254 }
1255 lua_pop(info->L, 2); /* ... proto */
1256 poppath(info);
1257 }
1258 poppath(info);
1259
1260 /* Read upvalues. */
1261 p->sizeupvalues = READ_VALUE(int);
1262 eris_reallocvector(info->L, p->upvalues, 0, p->sizeupvalues, Upvaldesc);
1263 for (i = 0, n = p->sizeupvalues; i < n; ++i) {
1264 p->upvalues[i].name = NULL;
1265 p->upvalues[i].instack = READ_VALUE(uint8_t);
1266 p->upvalues[i].idx = READ_VALUE(uint8_t);
1267 }
1268
1269 /* Read debug information if any is present. */
1270 if (!READ_VALUE(uint8_t)) {
1271 return;
1272 }
1273
1274 /* Read function source code. */
1275 unpersist(info); /* ... proto str */
1276 copytstring(info->L, &p->source);
1277 lua_pop(info->L, 1); /* ... proto */
1278
1279 /* Read line information. */
1280 p->sizelineinfo = READ_VALUE(int);
1281 eris_reallocvector(info->L, p->lineinfo, 0, p->sizelineinfo, int);
1282 READ(p->lineinfo, p->sizelineinfo, int);
1283
1284 /* Read locals info. */
1285 p->sizelocvars = READ_VALUE(int);
1286 eris_reallocvector(info->L, p->locvars, 0, p->sizelocvars, LocVar);
1287 /* Null the variable names to avoid confusing the GC. */
1288 for (i = 0, n = p->sizelocvars; i < n; ++i) {
1289 p->locvars[i].varname = NULL;
1290 }
1291 pushpath(info, ".locvars");
1292 for (i = 0, n = p->sizelocvars; i < n; ++i) {
1293 pushpath(info, "[%d]", i);
1294 p->locvars[i].startpc = READ_VALUE(int);
1295 p->locvars[i].endpc = READ_VALUE(int);
1296 unpersist(info); /* ... proto str */
1297 copytstring(info->L, &p->locvars[i].varname);
1298 lua_pop(info->L, 1); /* ... proto */
1299 poppath(info);
1300 }
1301 poppath(info);
1302
1303 /* Read upvalue names. */
1304 pushpath(info, ".upvalnames");
1305 for (i = 0, n = p->sizeupvalues; i < n; ++i) {
1306 pushpath(info, "[%d]", i);
1307 unpersist(info); /* ... proto str */
1308 copytstring(info->L, &p->upvalues[i].name);
1309 lua_pop(info->L, 1); /* ... proto */
1310 poppath(info);
1311 }
1312 poppath(info);
1313 lua_pushvalue(info->L, -1); /* ... proto proto */
1314
1315 eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
1316}
1317
1318/** ======================================================================== */
1319
1320static void
1321p_upval(Info *info) { /* ... obj */
1322 persist(info); /* ... obj */
1323}
1324
1325static void
1326u_upval(Info *info) { /* ... */
1327 eris_checkstack(info->L, 2);
1328
1329 /* Create the table we use to store the pointer to the actual upval (1), the
1330 * value of the upval (2) and any pointers to the pointer to the upval (3+).*/
1331 lua_createtable(info->L, 3, 0); /* ... tbl */
1332 registerobject(info);
1333 unpersist(info); /* ... tbl obj */
1334 lua_rawseti(info->L, -2, 1); /* ... tbl */
1335
1336 eris_assert(lua_type(info->L, -1) == LUA_TTABLE);
1337}
1338
1339/** ======================================================================== */
1340
1341/* For Lua closures we write the upvalue ID, which is usually the memory
1342 * address at which it is stored. This is used to tell which upvalues are
1343 * identical when unpersisting. */
1344/* In either case we store the upvale *values*, i.e. the actual objects they
1345 * point to. As in Pluto, we will restore any upvalues of Lua closures as
1346 * closed as first, i.e. the upvalue will store the TValue itself. When
1347 * loading a thread containing the upvalue (meaning it's the actual owner of
1348 * the upvalue) we open it, i.e. we point it to the thread's upvalue list.
1349 * For C closures, upvalues are always closed. */
1350static void
1351p_closure(Info *info) { /* perms reftbl ... func */
1352 int nup;
1353 eris_checkstack(info->L, 2);
1354 switch (ttype(info->L->top - 1)) {
1355 case LUA_TLCF: /* light C function */
1356 /* We cannot persist these, they have to be handled via the permtable. */
1357 eris_error(info, "attempt to persist a light C function (%p)",
1358 lua_tocfunction(info->L, -1));
1359 return; /* not reached */
1360 case LUA_TCCL: /* C closure */ { /* perms reftbl ... ccl */
1361 CClosure *cl = clCvalue(info->L->top - 1);
1362 /* Mark it as a C closure. */
1363 WRITE_VALUE(true, uint8_t);
1364 /* Write the upvalue count first, since we have to know it when creating
1365 * a new closure when unpersisting. */
1366 WRITE_VALUE(cl->nupvalues, uint8_t);
1367
1368 /* We can only persist these if the underlying C function is in the
1369 * permtable. So we try to persist it first as a light C function. If it
1370 * isn't in the permtable that'll cause an error (in the case above). */
1371 lua_pushcfunction(info->L, lua_tocfunction(info->L, -1));
1372 /* perms reftbl ... ccl cfunc */
1373 persist(info); /* perms reftbl ... ccl cfunc */
1374 lua_pop(info->L, 1); /* perms reftbl ... ccl */
1375
1376 /* Persist the upvalues. Since for C closures all upvalues are always
1377 * closed we can just write the actual values. */
1378 pushpath(info, ".upvalues");
1379 for (nup = 1; nup <= cl->nupvalues; ++nup) {
1380 pushpath(info, "[%d]", nup);
1381 lua_getupvalue(info->L, -1, nup); /* perms reftbl ... ccl obj */
1382 persist(info); /* perms reftbl ... ccl obj */
1383 lua_pop(info->L, 1); /* perms reftbl ... ccl */
1384 poppath(info);
1385 }
1386 poppath(info);
1387 break;
1388 }
1389 case LUA_TLCL: /* Lua function */ { /* perms reftbl ... lcl */
1390 LClosure *cl = clLvalue(info->L->top - 1);
1391 /* Mark it as a Lua closure. */
1392 WRITE_VALUE(false, uint8_t);
1393 /* Write the upvalue count first, since we have to know it when creating
1394 * a new closure when unpersisting. */
1395 WRITE_VALUE(cl->nupvalues, uint8_t);
1396
1397 /* Persist the function's prototype. Pass the proto as a parameter to
1398 * p_proto so that it can access it and register it in the ref table. */
1399 pushpath(info, ".proto");
1400 lua_pushlightuserdata(info->L, cl->p); /* perms reftbl ... lcl proto */
1401 lua_pushvalue(info->L, -1); /* perms reftbl ... lcl proto proto */
1402 persist_keyed(info, LUA_TPROTO); /* perms reftbl ... lcl proto */
1403 lua_pop(info->L, 1); /* perms reftbl ... lcl */
1404 poppath(info);
1405
1406 /* Persist the upvalues. We pretend to write these as their own type,
1407 * to get proper identity preservation. We also pass them as a parameter
1408 * to p_upval so it can register the upvalue in the reference table. */
1409 pushpath(info, ".upvalues");
1410 for (nup = 1; nup <= cl->nupvalues; ++nup) {
1411 const char *name = lua_getupvalue(info->L, -1, nup);
1412 /* perms reftbl ... lcl obj */
1413 pushpath(info, ".%s", name);
1414 lua_pushlightuserdata(info->L, lua_upvalueid(info->L, -2, nup));
1415 /* perms reftbl ... lcl obj id */
1416 persist_keyed(info, LUA_TUPVAL); /* perms reftbl ... lcl obj */
1417 lua_pop(info->L, 1); /* perms reftble ... lcl */
1418 poppath(info);
1419 }
1420 poppath(info);
1421 break;
1422 }
1423 default:
1424 eris_error(info, "attempt to persist unknown function type");
1425 return; /* not reached */
1426 }
1427}
1428
1429static void
1430u_closure(Info *info) { /* ... */
1431 int nup;
1432 bool isCClosure = READ_VALUE(uint8_t);
1433 lu_byte nups = READ_VALUE(uint8_t);
1434 if (isCClosure) {
1435 lua_CFunction f;
1436
1437 /* Reserve reference for the closure to avoid light C function or its
1438 * perm table key going first. */
1439 const int reference = ++(info->refcount);
1440
1441 /* nups is guaranteed to be >= 1, otherwise it'd be a light C function. */
1442 eris_checkstack(info->L, nups < 2 ? 2 : nups);
1443
1444 /* Read the C function from the permanents table. */
1445 unpersist(info); /* ... cfunc */
1446 if (!lua_iscfunction(info->L, -1)) {
1447 eris_error(info, "bad C closure (C function expected, got %s)",
1448 kTypenames[lua_type(info->L, -1)]);
1449 }
1450 f = lua_tocfunction(info->L, -1);
1451 if (!f) {
1452 eris_error(info, "bad C closure (C function expected, got null)");
1453 }
1454 lua_pop(info->L, 1); /* ... */
1455
1456 /* Now this is a little roundabout, but we want to create the closure
1457 * before unpersisting the actual upvalues to avoid cycles. So we have to
1458 * create it with all nil first, then fill the upvalues in afterwards. */
1459 for (nup = 1; nup <= nups; ++nup) {
1460 lua_pushnil(info->L); /* ... nil[1] ... nil[nup] */
1461 }
1462 lua_pushcclosure(info->L, f, nups); /* ... ccl */
1463
1464 /* Create the entry in the reftable. */
1465 lua_pushvalue(info->L, -1); /* perms reftbl ... ccl ccl */
1466 lua_rawseti(info->L, REFTIDX, reference); /* perms reftbl ... ccl */
1467
1468 /* Unpersist actual upvalues. */
1469 pushpath(info, ".upvalues");
1470 for (nup = 1; nup <= nups; ++nup) {
1471 pushpath(info, "[%d]", nup);
1472 unpersist(info); /* ... ccl obj */
1473 lua_setupvalue(info->L, -2, nup); /* ... ccl */
1474 poppath(info);
1475 }
1476 poppath(info);
1477 }
1478 else {
1479 Closure *cl;
1480 Proto *p;
1481
1482 eris_checkstack(info->L, 4);
1483
1484 /* Create closure and anchor it on the stack (avoid collection via GC). */
1485 cl = eris_newLclosure(info->L, nups);
1486 eris_setclLvalue(info->L, info->L->top, cl); /* ... lcl */
1487 eris_incr_top(info->L);
1488
1489 /* Preregister closure for handling of cycles (upvalues). */
1490 registerobject(info);
1491
1492 /* Read prototype. In general, we create protos (and upvalues) before
1493 * trying to read them and pass a pointer to the instance along to the
1494 * unpersist function. This way the instance is safely hooked up to an
1495 * object, so we don't have to worry about it getting GCed. */
1496 pushpath(info, ".proto");
1497 cl->l.p = eris_newproto(info->L);
1498 /* Push the proto into which to unpersist as a parameter to u_proto. */
1499 lua_pushlightuserdata(info->L, cl->l.p); /* ... lcl nproto */
1500 unpersist(info); /* ... lcl nproto nproto/oproto */
1501 eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
1502 /* The proto we have now may differ, if we already unpersisted it before.
1503 * In that case we now have a reference to the originally unpersisted
1504 * proto so we'll use that. */
1505 p = lua_touserdata(info->L, -1);
1506 if (p != cl->l.p) { /* ... lcl nproto oproto */
1507 /* Just overwrite the old one, GC will clean this up. */
1508 cl->l.p = p;
1509 }
1510 lua_pop(info->L, 2); /* ... lcl */
1511 eris_assert(cl->l.p->sizeupvalues == nups);
1512 poppath(info);
1513
1514 /* Unpersist all upvalues. */
1515 pushpath(info, ".upvalues");
1516 for (nup = 1; nup <= nups; ++nup) {
1517 UpVal **uv = &cl->l.upvals[nup - 1];
1518 /* Get the actual name of the upvalue, if possible. */
1519 if (p->upvalues[nup - 1].name) {
1520 pushpath(info, "[%s]", getstr(p->upvalues[nup - 1].name));
1521 }
1522 else {
1523 pushpath(info, "[%d]", nup);
1524 }
1525 unpersist(info); /* ... lcl tbl */
1526 eris_assert(lua_type(info->L, -1) == LUA_TTABLE);
1527 lua_rawgeti(info->L, -1, 2); /* ... lcl tbl upval/nil */
1528 if (lua_isnil(info->L, -1)) { /* ... lcl tbl nil */
1529 lua_pop(info->L, 1); /* ... lcl tbl */
1530 *uv = eris_newupval(info->L);
1531 lua_pushlightuserdata(info->L, *uv); /* ... lcl tbl upval */
1532 lua_rawseti(info->L, -2, 2); /* ... lcl tbl */
1533 }
1534 else { /* ... lcl tbl upval */
1535 eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
1536 *uv = (UpVal*)lua_touserdata(info->L, -1);
1537 lua_pop(info->L, 1); /* ... lcl tbl */
1538 }
1539
1540 /* Set the upvalue's actual value and add our reference to the upvalue to
1541 * the list, for pointer patching if we have to open the upvalue in
1542 * u_thread. Either is only necessary if the upvalue is still closed. */
1543 if ((*uv)->v == &(*uv)->u.value) {
1544 /* Always update the value of the upvalue's value for closed upvalues,
1545 * even if we re-used one - if we had a cycle, it might have been
1546 * incorrectly initialized to nil before. */
1547 lua_rawgeti(info->L, -1, 1); /* ... lcl tbl obj */
1548 eris_setobj(info->L, &(*uv)->u.value, info->L->top - 1);
1549 lua_pop(info->L, 1); /* ... lcl tbl */
1550
1551 lua_pushlightuserdata(info->L, uv); /* ... lcl tbl upvalp */
1552 if (luaL_len(info->L, -2) >= 2) {
1553 /* Got a valid sequence, insert at the end. */
1554 lua_rawseti(info->L, -2, luaL_len(info->L, -2) + 1); /* ... lcl tbl */
1555 }
1556 else { /* ... lcl tbl upvalp */
1557 int i;
1558 /* Find where to insert. This can happen if we have cycles, in which
1559 * case the table is not fully initialized at this point, i.e. the
1560 * value is not in it, yet (we work around that by always setting it,
1561 * as seen above). */
1562 for (i = 3;; ++i) {
1563 lua_rawgeti(info->L, -2, i); /* ... lcl tbl upvalp upvalp/nil */
1564 if (lua_isnil(info->L, -1)) { /* ... lcl tbl upvalp nil */
1565 lua_pop(info->L, 1); /* ... lcl tbl upvalp */
1566 lua_rawseti(info->L, -2, i); /* ... lcl tbl */
1567 break;
1568 }
1569 else {
1570 lua_pop(info->L, 1); /* ... lcl tbl upvalp */
1571 }
1572 } /* ... lcl tbl */
1573 }
1574 }
1575
1576 lua_pop(info->L, 1); /* ... lcl */
1577 poppath(info);
1578 }
1579 poppath(info);
1580
1581 eris_barrierproto(info->L, p, cl);
1582 p->cache = cl; /* save it in cache for reuse, see lvm.c:416 */
1583 }
1584
1585 eris_assert(lua_type(info->L, -1) == LUA_TFUNCTION);
1586}
1587
1588/** ======================================================================== */
1589
1590static void
1591p_thread(Info *info) { /* ... thread */
1592 lua_State* thread = lua_tothread(info->L, -1);
1593 size_t level;
1594 StkId o;
1595 CallInfo *ci;
1596 UpVal *uv;
1597
1598 eris_checkstack(info->L, 2);
1599
1600 /* We cannot persist any running threads, because by definition we *are* that
1601 * running thread. And we use the stack. So yeah, really not a good idea. */
1602 if (thread == info->L) {
1603 eris_error(info, "cannot persist currently running thread");
1604 return; /* not reached */
1605 }
1606
1607 /* Persist the stack. Save the total size and used space first. */
1608 WRITE_VALUE(thread->stacksize, int);
1609 WRITE_VALUE(thread->top - thread->stack, size_t);
1610
1611 /* The Lua stack looks like this:
1612 * stack ... top ... stack_last
1613 * Where stack <= top <= stack_last, and "top" actually being the first free
1614 * element, i.e. there's nothing stored there. So we stop one below that. */
1615 pushpath(info, ".stack");
1616 lua_pushnil(info->L); /* ... thread nil */
1617 level = 0;
1618 for (o = thread->stack; o < thread->top; ++o) {
1619 pushpath(info, "[%d]", level++);
1620 eris_setobj(info->L, info->L->top - 1, o); /* ... thread obj */
1621 persist(info); /* ... thread obj */
1622 poppath(info);
1623 }
1624 lua_pop(info->L, 1); /* ... thread */
1625 poppath(info);
1626
1627 /* If the thread isn't running this must be the default value, which is 1. */
1628 eris_assert(thread->nny == 1);
1629
1630 /* Error jump info should only be set while thread is running. */
1631 eris_assert(thread->errorJmp == NULL);
1632
1633 /* thread->oldpc always seems to be uninitialized, at least gdb always shows
1634 * it as 0xbaadf00d when I set a breakpoint here. */
1635
1636 /* Write general information. */
1637 WRITE_VALUE(thread->status, uint8_t);
1638 WRITE_VALUE(eris_savestackidx(thread,
1639 eris_restorestack(thread, thread->errfunc)), size_t);
1640 /* These are only used while a thread is being executed or can be deduced:
1641 WRITE_VALUE(thread->nCcalls, uint16_t);
1642 WRITE_VALUE(thread->allowhook, uint8_t); */
1643
1644 /* Hooks are not supported, bloody can of worms, those.
1645 WRITE_VALUE(thread->hookmask, uint8_t);
1646 WRITE_VALUE(thread->basehookcount, int);
1647 WRITE_VALUE(thread->hookcount, int); */
1648
1649 if (thread->hook) {
1650 /* TODO Warn that hooks are not persisted? */
1651 }
1652
1653 /* Write call information (stack frames). In 5.2 CallInfo is stored in a
1654 * linked list that originates in thead.base_ci. Upon initialization the
1655 * thread.ci is set to thread.base_ci. During thread calls this is extended
1656 * and always represents the tail of the callstack, though not necessarily of
1657 * the linked list (which can be longer if the callstack was deeper earlier,
1658 * but shrunk due to returns). */
1659 pushpath(info, ".callinfo");
1660 level = 0;
1661 eris_assert(&thread->base_ci != thread->ci->next);
1662 for (ci = &thread->base_ci; ci != thread->ci->next; ci = ci->next) {
1663 pushpath(info, "[%d]", level++);
1664 WRITE_VALUE(eris_savestackidx(thread, ci->func), size_t);
1665 WRITE_VALUE(eris_savestackidx(thread, ci->top), size_t);
1666 WRITE_VALUE(ci->nresults, int16_t);
1667 WRITE_VALUE(ci->callstatus, uint8_t);
1668 /* CallInfo.extra is used in two contexts: if L->status == LUA_YIELD and
1669 * CallInfo is the one stored as L->ci, in which case ci->extra refers to
1670 * the original value of L->ci->func, and when we have a yieldable pcall
1671 * (ci->callstatus & CIST_YPCALL) where ci->extra holds the stack level
1672 * of the function being called (see lua_pcallk). We save the ci->extra
1673 * for L->ci after the loop, because we won't know which one it is when
1674 * unpersisting. */
1675 if (ci->callstatus & CIST_YPCALL) {
1676 WRITE_VALUE(eris_savestackidx(thread,
1677 eris_restorestack(thread, ci->extra)), size_t);
1678 }
1679
1680 eris_assert(eris_isLua(ci) || (ci->callstatus & CIST_TAIL) == 0);
1681 if (ci->callstatus & CIST_HOOKYIELD) {
1682 eris_error(info, "cannot persist yielded hooks");
1683 }
1684
1685 if (eris_isLua(ci)) {
1686 const LClosure *lcl = eris_ci_func(ci);
1687 WRITE_VALUE(eris_savestackidx(thread, ci->u.l.base), size_t);
1688 WRITE_VALUE(ci->u.l.savedpc - lcl->p->code, size_t);
1689 }
1690 else {
1691 WRITE_VALUE(ci->u.c.status, uint8_t);
1692
1693 /* These are only used while a thread is being executed:
1694 WRITE_VALUE(ci->u.c.old_errfunc, ptrdiff_t);
1695 WRITE_VALUE(ci->u.c.old_allowhook, uint8_t); */
1696
1697 /* TODO Is this really right? Hooks may be a problem? */
1698 if (ci->callstatus & (CIST_YPCALL | CIST_YIELDED)) {
1699 WRITE_VALUE(ci->u.c.ctx, int);
1700 eris_assert(ci->u.c.k);
1701 lua_pushcfunction(info->L, ci->u.c.k); /* ... thread func */
1702 persist(info); /* ... thread func/nil */
1703 lua_pop(info->L, 1); /* ... thread */
1704 }
1705 }
1706
1707 /* Write whether there's more to come. */
1708 WRITE_VALUE(ci->next == thread->ci->next, uint8_t);
1709
1710 poppath(info);
1711 }
1712 /** See comment on ci->extra in loop. */
1713 if (thread->status == LUA_YIELD) {
1714 WRITE_VALUE(eris_savestackidx(thread,
1715 eris_restorestack(thread, thread->ci->extra)), size_t);
1716 }
1717 poppath(info);
1718
1719 pushpath(info, ".openupval");
1720 lua_pushnil(info->L); /* ... thread nil */
1721 level = 0;
1722 for (uv = eris_gco2uv(thread->openupval);
1723 uv != NULL;
1724 uv = eris_gco2uv(eris_gch(eris_obj2gco(uv))->next))
1725 {
1726 pushpath(info, "[%d]", level);
1727 WRITE_VALUE(eris_savestackidx(thread, uv->v) + 1, size_t);
1728 eris_setobj(info->L, info->L->top - 1, uv->v); /* ... thread obj */
1729 lua_pushlightuserdata(info->L, uv); /* ... thread obj id */
1730 persist_keyed(info, LUA_TUPVAL); /* ... thread obj */
1731 poppath(info);
1732 }
1733 WRITE_VALUE(0, size_t);
1734 lua_pop(info->L, 1); /* ... thread */
1735 poppath(info);
1736}
1737
1738/* Used in u_thread to validate read stack positions. */
1739#define validate(stackpos, inclmax) \
1740 if ((stackpos) < thread->stack || stackpos > (inclmax)) { \
1741 (stackpos) = thread->stack; \
1742 eris_error(info, "stack index out of bounds"); }
1743
1744/* I had so hoped to get by without any 'hacks', but I surrender. We mark the
1745 * thread as incomplete to avoid the GC messing with it while we're building
1746 * it. Otherwise it may try to shrink its stack. We do this by setting its
1747 * stack field to null for every call that may trigger a GC run, since that
1748 * field is what's used to determine whether threads should be shrunk. See
1749 * lgc.c:699. Some of the locks could probably be joined (since nothing
1750 * inbetween requires the stack field to be valid), but I prefer to keep the
1751 * "invalid" blocks as small as possible to make it clearer. Also, locking and
1752 * unlocking are really just variable assignments, so they're really cheap. */
1753#define LOCK(L) (L->stack = NULL)
1754#define UNLOCK(L) (L->stack = stack)
1755
1756static void
1757u_thread(Info *info) { /* ... */
1758 lua_State* thread;
1759 size_t level;
1760 StkId stack, o;
1761
1762 eris_checkstack(info->L, 3);
1763
1764 thread = lua_newthread(info->L); /* ... thread */
1765 registerobject(info);
1766
1767 /* Unpersist the stack. Read size first and adjust accordingly. */
1768 eris_reallocstack(thread, READ_VALUE(int));
1769 stack = thread->stack; /* After the realloc in case the address changes. */
1770 thread->top = thread->stack + READ_VALUE(size_t);
1771 validate(thread->top, thread->stack_last);
1772
1773 /* Read the elements one by one. */
1774 LOCK(thread);
1775 pushpath(info, ".stack");
1776 UNLOCK(thread);
1777 level = 0;
1778 for (o = stack; o < thread->top; ++o) {
1779 LOCK(thread);
1780 pushpath(info, "[%d]", level++);
1781 unpersist(info); /* ... thread obj */
1782 UNLOCK(thread);
1783 eris_setobj(thread, o, info->L->top - 1);
1784 lua_pop(info->L, 1); /* ... thread */
1785 LOCK(thread);
1786 poppath(info);
1787 UNLOCK(thread);
1788 }
1789 LOCK(thread);
1790 poppath(info);
1791 UNLOCK(thread);
1792
1793 /* As in p_thread, just to make sure. */
1794 eris_assert(thread->nny == 1);
1795 eris_assert(thread->errorJmp == NULL);
1796 eris_assert(thread->hook == NULL);
1797
1798 /* See comment in persist. */
1799 thread->oldpc = NULL;
1800
1801 /* Read general information. */
1802 thread->status = READ_VALUE(uint8_t);
1803 thread->errfunc = eris_savestack(thread,
1804 eris_restorestackidx(thread, READ_VALUE(size_t)));
1805 if (thread->errfunc) {
1806 o = eris_restorestack(thread, thread->errfunc);
1807 validate(o, thread->top);
1808 if (eris_ttypenv(o) != LUA_TFUNCTION) {
1809 eris_error(info, "invalid errfunc");
1810 }
1811 }
1812 /* These are only used while a thread is being executed or can be deduced:
1813 thread->nCcalls = READ_VALUE(uint16_t);
1814 thread->allowhook = READ_VALUE(uint8_t); */
1815 eris_assert(thread->allowhook == 1);
1816
1817 /* Not supported.
1818 thread->hookmask = READ_VALUE(uint8_t);
1819 thread->basehookcount = READ_VALUE(int);
1820 thread->hookcount = READ_VALUE(int); */
1821
1822 /* Read call information (stack frames). */
1823 LOCK(thread);
1824 pushpath(info, ".callinfo");
1825 UNLOCK(thread);
1826 thread->ci = &thread->base_ci;
1827 level = 0;
1828 for (;;) {
1829 LOCK(thread);
1830 pushpath(info, "[%d]", level++);
1831 UNLOCK(thread);
1832 thread->ci->func = eris_restorestackidx(thread, READ_VALUE(size_t));
1833 validate(thread->ci->func, thread->top - 1);
1834 thread->ci->top = eris_restorestackidx(thread, READ_VALUE(size_t));
1835 validate(thread->ci->top, thread->stack_last);
1836 thread->ci->nresults = READ_VALUE(int16_t);
1837 thread->ci->callstatus = READ_VALUE(uint8_t);
1838 /** See comment in p_thread. */
1839 if (thread->ci->callstatus & CIST_YPCALL) {
1840 thread->ci->extra = eris_savestack(thread,
1841 eris_restorestackidx(thread, READ_VALUE(size_t)));
1842 o = eris_restorestack(thread, thread->ci->extra);
1843 validate(o, thread->top);
1844 if (eris_ttypenv(o) != LUA_TFUNCTION) {
1845 eris_error(info, "invalid callinfo");
1846 }
1847 }
1848
1849 if (eris_isLua(thread->ci)) {
1850 LClosure *lcl = eris_ci_func(thread->ci);
1851 thread->ci->u.l.base = eris_restorestackidx(thread, READ_VALUE(size_t));
1852 validate(thread->ci->u.l.base, thread->top);
1853 thread->ci->u.l.savedpc = lcl->p->code + READ_VALUE(size_t);
1854 if (thread->ci->u.l.savedpc < lcl->p->code ||
1855 thread->ci->u.l.savedpc > lcl->p->code + lcl->p->sizecode)
1856 {
1857 thread->ci->u.l.savedpc = lcl->p->code; /* Just to be safe. */
1858 eris_error(info, "saved program counter out of bounds");
1859 }
1860 }
1861 else {
1862 thread->ci->u.c.status = READ_VALUE(uint8_t);
1863
1864 /* These are only used while a thread is being executed:
1865 thread->ci->u.c.old_errfunc = READ_VALUE(ptrdiff_t);
1866 thread->ci->u.c.old_allowhook = READ_VALUE(uint8_t); */
1867 thread->ci->u.c.old_errfunc = 0;
1868 thread->ci->u.c.old_allowhook = 0;
1869
1870 /* TODO Is this really right? */
1871 if (thread->ci->callstatus & (CIST_YPCALL | CIST_YIELDED)) {
1872 thread->ci->u.c.ctx = READ_VALUE(int);
1873 LOCK(thread);
1874 unpersist(info); /* ... thread func? */
1875 UNLOCK(thread);
1876 if (lua_iscfunction(info->L, -1)) { /* ... thread func */
1877 thread->ci->u.c.k = lua_tocfunction(info->L, -1);
1878 }
1879 else {
1880 eris_error(info, "bad C continuation function");
1881 return; /* not reached */
1882 }
1883 lua_pop(info->L, 1); /* ... thread */
1884 }
1885 else {
1886 thread->ci->u.c.ctx = 0;
1887 thread->ci->u.c.k = NULL;
1888 }
1889 }
1890 LOCK(thread);
1891 poppath(info);
1892 UNLOCK(thread);
1893
1894 /* Read in value for check for next iteration. */
1895 if (READ_VALUE(uint8_t)) {
1896 break;
1897 }
1898 else {
1899 thread->ci = eris_extendCI(thread);
1900 }
1901 }
1902 if (thread->status == LUA_YIELD) {
1903 thread->ci->extra = eris_savestack(thread,
1904 eris_restorestackidx(thread, READ_VALUE(size_t)));
1905 o = eris_restorestack(thread, thread->ci->extra);
1906 validate(o, thread->top);
1907 if (eris_ttypenv(o) != LUA_TFUNCTION) {
1908 eris_error(info, "invalid callinfo");
1909 }
1910 }
1911 LOCK(thread);
1912 poppath(info);
1913 UNLOCK(thread);
1914
1915 /* Get from context: only zero for dead threads, otherwise one. */
1916 thread->nCcalls = thread->status != LUA_OK || lua_gettop(thread) != 0;
1917
1918 /* Proceed to open upvalues. These upvalues will already exist due to the
1919 * functions using them having been unpersisted (they'll usually be in the
1920 * stack of the thread). For this reason we store all previous references to
1921 * the upvalue in a table that is returned when we try to unpersist an
1922 * upvalue, so that we can adjust these pointers in here. */
1923 LOCK(thread);
1924 pushpath(info, ".openupval");
1925 UNLOCK(thread);
1926 level = 0;
1927 for (;;) {
1928 UpVal *nuv;
1929 StkId stk;
1930 /* Get the position of the upvalue on the stack. As a special value we pass
1931 * zero to indicate there are no more upvalues. */
1932 const size_t offset = READ_VALUE(size_t);
1933 if (offset == 0) {
1934 break;
1935 }
1936 LOCK(thread);
1937 pushpath(info, "[%d]", level);
1938 UNLOCK(thread);
1939 stk = eris_restorestackidx(thread, offset - 1);
1940 validate(stk, thread->top - 1);
1941 LOCK(thread);
1942 unpersist(info); /* ... thread tbl */
1943 UNLOCK(thread);
1944 eris_assert(lua_type(info->L, -1) == LUA_TTABLE);
1945
1946 /* Create the open upvalue either way. */
1947 LOCK(thread);
1948 nuv = eris_findupval(thread, stk);
1949 UNLOCK(thread);
1950
1951 /* Then check if we need to patch some pointers. */
1952 lua_rawgeti(info->L, -1, 2); /* ... thread tbl upval/nil */
1953 if (!lua_isnil(info->L, -1)) { /* ... thread tbl upval */
1954 int i, n;
1955 eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
1956 /* Already exists, replace it. To do this we have to patch all the
1957 * pointers to the already existing one, which we added to the table in
1958 * u_closure, starting at index 3. */
1959 lua_pop(info->L, 1); /* ... thread tbl */
1960 for (i = 3, n = luaL_len(info->L, -1); i <= n; ++i) {
1961 lua_rawgeti(info->L, -1, i); /* ... thread tbl upvalp */
1962 (*(UpVal**)lua_touserdata(info->L, -1)) = nuv;
1963 lua_pop(info->L, 1); /* ... thread tbl */
1964 }
1965 }
1966 else { /* ... thread tbl nil */
1967 eris_assert(lua_isnil(info->L, -1));
1968 lua_pop(info->L, 1); /* ... thread tbl */
1969 }
1970
1971 /* Store open upvalue in table for future references. */
1972 LOCK(thread);
1973 lua_pushlightuserdata(info->L, nuv); /* ... thread tbl upval */
1974 lua_rawseti(info->L, -2, 2); /* ... thread tbl */
1975 lua_pop(info->L, 1); /* ... thread */
1976 poppath(info);
1977 UNLOCK(thread);
1978 }
1979 poppath(info);
1980
1981 eris_assert(lua_type(info->L, -1) == LUA_TTHREAD);
1982}
1983
1984#undef UNLOCK
1985#undef LOCK
1986
1987#undef validate
1988
1989/*
1990** ============================================================================
1991** Top-level delegator.
1992** ============================================================================
1993*/
1994
1995static void
1996persist_typed(Info *info, int type) { /* perms reftbl ... obj */
1997 eris_ifassert(const int top = lua_gettop(info->L));
1998 if (info->level >= info->maxComplexity) {
1999 eris_error(info, "object too complex");
2000 }
2001 ++info->level;
2002
2003 WRITE_VALUE(type, int);
2004 switch(type) {
2005 case LUA_TBOOLEAN:
2006 p_boolean(info);
2007 break;
2008 case LUA_TLIGHTUSERDATA:
2009 p_pointer(info);
2010 break;
2011 case LUA_TNUMBER:
2012 p_number(info);
2013 break;
2014 case LUA_TSTRING:
2015 p_string(info);
2016 break;
2017 case LUA_TTABLE:
2018 p_table(info);
2019 break;
2020 case LUA_TFUNCTION:
2021 p_closure(info);
2022 break;
2023 case LUA_TUSERDATA:
2024 p_userdata(info);
2025 break;
2026 case LUA_TTHREAD:
2027 p_thread(info);
2028 break;
2029 case LUA_TPROTO:
2030 p_proto(info);
2031 break;
2032 case LUA_TUPVAL:
2033 p_upval(info);
2034 break;
2035 default:
2036 eris_error(info, "trying to persist unknown type");
2037 } /* perms reftbl ... obj */
2038
2039 --info->level;
2040 eris_assert(top == lua_gettop(info->L));
2041}
2042
2043/* Second-level delegating persist function, used for cases when persisting
2044 * data that's stored in the reftable with a key that is not the data itself,
2045 * namely upvalues and protos. */
2046static void
2047persist_keyed(Info *info, int type) { /* perms reftbl ... obj refkey */
2048 eris_checkstack(info->L, 2);
2049
2050 /* Keep a copy of the key for pushing it to the reftable, if necessary. */
2051 lua_pushvalue(info->L, -1); /* perms reftbl ... obj refkey refkey */
2052
2053 /* If the object has already been written, write a reference to it. */
2054 lua_rawget(info->L, REFTIDX); /* perms reftbl ... obj refkey ref? */
2055 if (!lua_isnil(info->L, -1)) { /* perms reftbl ... obj refkey ref */
2056 const int reference = lua_tointeger(info->L, -1);
2057 WRITE_VALUE(reference + ERIS_REFERENCE_OFFSET, int);
2058 lua_pop(info->L, 2); /* perms reftbl ... obj */
2059 return;
2060 } /* perms reftbl ... obj refkey nil */
2061 lua_pop(info->L, 1); /* perms reftbl ... obj refkey */
2062
2063 /* Copy the refkey for the perms check below. */
2064 lua_pushvalue(info->L, -1); /* perms reftbl ... obj refkey refkey */
2065
2066 /* Put the value in the reference table. This creates an entry pointing from
2067 * the object (or its key) to the id the object is referenced by. */
2068 lua_pushinteger(info->L, ++(info->refcount));
2069 /* perms reftbl ... obj refkey refkey ref */
2070 lua_rawset(info->L, REFTIDX); /* perms reftbl ... obj refkey */
2071
2072 /* At this point, we'll give the permanents table a chance to play. */
2073 lua_gettable(info->L, PERMIDX); /* perms reftbl ... obj permkey? */
2074 if (!lua_isnil(info->L, -1)) { /* perms reftbl ... obj permkey */
2075 type = lua_type(info->L, -2);
2076 /* Prepend permanent "type" so that we know it's a permtable key. This will
2077 * trigger u_permanent when unpersisting. Also write the original type, so
2078 * that we can verify what we get in the permtable when unpersisting is of
2079 * the same kind we had when persisting. */
2080 WRITE_VALUE(ERIS_PERMANENT, int);
2081 WRITE_VALUE(type, uint8_t);
2082 persist(info); /* perms reftbl ... obj permkey */
2083 lua_pop(info->L, 1); /* perms reftbl ... obj */
2084 }
2085 else { /* perms reftbl ... obj nil */
2086 /* No entry in the permtable for this object, persist it directly. */
2087 lua_pop(info->L, 1); /* perms reftbl ... obj */
2088 persist_typed(info, type); /* perms reftbl ... obj */
2089 } /* perms reftbl ... obj */
2090}
2091
2092/* Top-level delegating persist function. */
2093static void
2094persist(Info *info) { /* perms reftbl ... obj */
2095 /* Grab the object's type. */
2096 const int type = lua_type(info->L, -1);
2097
2098 /* If the object is nil, only write its type. */
2099 if (type == LUA_TNIL) {
2100 WRITE_VALUE(type, int);
2101 }
2102 /* Write simple values directly, because writing a "reference" would take up
2103 * just as much space and we can save ourselves work this way. */
2104 else if (type == LUA_TBOOLEAN ||
2105 type == LUA_TLIGHTUSERDATA ||
2106 type == LUA_TNUMBER)
2107 {
2108 persist_typed(info, type); /* perms reftbl ... obj */
2109 }
2110 /* For all non-simple values we keep a record in the reftable, so that we
2111 * keep references alive across persisting and unpersisting an object. This
2112 * has the nice side-effect of saving some space. */
2113 else {
2114 eris_checkstack(info->L, 1);
2115 lua_pushvalue(info->L, -1); /* perms reftbl ... obj obj */
2116 persist_keyed(info, type); /* perms reftbl ... obj */
2117 }
2118}
2119
2120/** ======================================================================== */
2121
2122static void
2123u_permanent(Info *info) { /* perms reftbl ... */
2124 const int type = READ_VALUE(uint8_t);
2125 /* Reserve reference to avoid the key going first. */
2126 const int reference = ++(info->refcount);
2127 eris_checkstack(info->L, 1);
2128 unpersist(info); /* perms reftbl ... permkey */
2129 lua_gettable(info->L, PERMIDX); /* perms reftbl ... obj? */
2130 if (lua_isnil(info->L, -1)) { /* perms reftbl ... nil */
2131 /* Since we may need permanent values to rebuild other structures, namely
2132 * closures and threads, we cannot allow perms to fail unpersisting. */
2133 eris_error(info, "bad permanent value (no value)");
2134 }
2135 else if (lua_type(info->L, -1) != type) { /* perms reftbl ... :( */
2136 /* For the same reason that we cannot allow nil we must also require the
2137 * unpersisted value to be of the correct type. */
2138 eris_error(info, "bad permanent value (%s expected, got %s)",
2139 kTypenames[type], kTypenames[lua_type(info->L, -1)]);
2140 } /* perms reftbl ... obj */
2141 /* Create the entry in the reftable. */
2142 lua_pushvalue(info->L, -1); /* perms reftbl ... obj obj */
2143 lua_rawseti(info->L, REFTIDX, reference); /* perms reftbl ... obj */
2144}
2145
2146static void
2147unpersist(Info *info) { /* perms reftbl ... */
2148 eris_ifassert(const int top = lua_gettop(info->L));
2149 if (info->level >= info->maxComplexity) {
2150 eris_error(info, "object too complex");
2151 }
2152 ++info->level;
2153
2154 eris_checkstack(info->L, 1);
2155 {
2156 const int typeOrReference = READ_VALUE(int);
2157 if (typeOrReference > ERIS_REFERENCE_OFFSET) {
2158 const int reference = typeOrReference - ERIS_REFERENCE_OFFSET;
2159 lua_rawgeti(info->L, REFTIDX, reference); /* perms reftbl ud ... obj? */
2160 if (lua_isnil(info->L, -1)) { /* perms reftbl ud ... :( */
2161 eris_error(info, "invalid reference #%d", reference);
2162 } /* perms reftbl ud ... obj */
2163 }
2164 else {
2165 const int type = typeOrReference;
2166 switch (type) {
2167 case LUA_TNIL:
2168 lua_pushnil(info->L);
2169 break;
2170 case LUA_TBOOLEAN:
2171 u_boolean(info);
2172 break;
2173 case LUA_TLIGHTUSERDATA:
2174 u_pointer(info);
2175 break;
2176 case LUA_TNUMBER:
2177 u_number(info);
2178 break;
2179 case LUA_TSTRING:
2180 u_string(info);
2181 break;
2182 case LUA_TTABLE:
2183 u_table(info);
2184 break;
2185 case LUA_TFUNCTION:
2186 u_closure(info);
2187 break;
2188 case LUA_TUSERDATA:
2189 u_userdata(info);
2190 break;
2191 case LUA_TTHREAD:
2192 u_thread(info);
2193 break;
2194 case LUA_TPROTO:
2195 u_proto(info);
2196 break;
2197 case LUA_TUPVAL:
2198 u_upval(info);
2199 break;
2200 case ERIS_PERMANENT:
2201 u_permanent(info);
2202 break;
2203 default:
2204 eris_error(info, "trying to unpersist unknown type %d", type);
2205 } /* perms reftbl ... obj? */
2206 }
2207 }
2208
2209 --info->level;
2210 eris_assert(top + 1 == lua_gettop(info->L));
2211}
2212
2213/* }======================================================================== */
2214
2215/*
2216** {===========================================================================
2217** Writer and reader implementation for library calls.
2218** ============================================================================
2219*/
2220
2221/* Implementation note: we use the MBuffer struct, but we don't use the built-
2222 * in reallocation functions since we'll keep our working copy on the stack, to
2223 * allow for proper collection if we have to throw an error. This is very much
2224 * what the auxlib does with its buffer functionality. Which we don't use since
2225 * we cannot guarantee stack balance inbetween calls to luaL_add*. */
2226
2227static int
2228writer(lua_State *L, const void *p, size_t sz, void *ud) {
2229 /* perms reftbl buff path? ... */
2230 const char *value = (const char*)p;
2231 Mbuffer *buff = (Mbuffer*)ud;
2232 const size_t size = eris_bufflen(buff);
2233 const size_t capacity = eris_sizebuffer(buff);
2234 if (capacity - size < sz) {
2235 size_t newcapacity = capacity * 2; /* overflow checked below */
2236 if (newcapacity - size < sz) {
2237 newcapacity = capacity + sz; /* overflow checked below */
2238 }
2239 if (newcapacity <= capacity) {
2240 /* Overflow in capacity, buffer size limit reached. */
2241 return 1;
2242 } else {
2243 char *newbuff;
2244 eris_checkstack(L, 1);
2245 newbuff = (char*)lua_newuserdata(L, newcapacity * sizeof(char));
2246 /* perms reftbl buff path? ... nbuff */
2247 memcpy(newbuff, eris_buffer(buff), eris_bufflen(buff));
2248 lua_replace(L, BUFFIDX); /* perms reftbl nbuff path? ... */
2249 eris_buffer(buff) = newbuff;
2250 eris_sizebuffer(buff) = newcapacity;
2251 }
2252 }
2253 memcpy(&eris_buffer(buff)[eris_bufflen(buff)], value, sz);
2254 eris_bufflen(buff) += sz;
2255 return 0;
2256}
2257
2258/** ======================================================================== */
2259
2260/* Readonly, interface compatible with MBuffer macros. */
2261typedef struct RBuffer {
2262 const char *buffer;
2263 size_t n;
2264 size_t buffsize;
2265} RBuffer;
2266
2267static const char*
2268reader(lua_State *L, void *ud, size_t *sz) {
2269 RBuffer *buff = (RBuffer*)ud;
2270 (void) L; /* unused */
2271 if (eris_bufflen(buff) == 0) {
2272 return NULL;
2273 }
2274 *sz = eris_bufflen(buff);
2275 eris_bufflen(buff) = 0;
2276 return eris_buffer(buff);
2277}
2278
2279/* }======================================================================== */
2280
2281/*
2282** {===========================================================================
2283** Library functions.
2284** ============================================================================
2285*/
2286
2287static void
2288p_header(Info *info) {
2289 WRITE_RAW(kHeader, HEADER_LENGTH);
2290 WRITE_VALUE(sizeof(lua_Number), uint8_t);
2291 WRITE_VALUE(kHeaderNumber, lua_Number);
2292 WRITE_VALUE(sizeof(int), uint8_t);
2293 WRITE_VALUE(sizeof(size_t), uint8_t);
2294}
2295
2296static void
2297u_header(Info *info) {
2298 char header[HEADER_LENGTH];
2299 uint8_t number_size;
2300 READ_RAW(header, HEADER_LENGTH);
2301 if (strncmp(kHeader, header, HEADER_LENGTH)) {
2302 luaL_error(info->L, "invalid data");
2303 }
2304 number_size = READ_VALUE(uint8_t);
2305 if (number_size == 0) {
2306 /* Old 64-bit versions of eris wrote '\0' and then three random bytes. */
2307 /* We skip them here for backwards compatibility. */
2308 char throw_away[3];
2309 READ_RAW(throw_away, 3);
2310
2311 number_size = READ_VALUE(uint8_t);
2312 }
2313 if (number_size != sizeof(lua_Number)) {
2314 luaL_error(info->L, "incompatible floating point type");
2315 }
2316 /* In this case we really do want floating point equality. */
2317 if (READ_VALUE(lua_Number) != kHeaderNumber) {
2318 luaL_error(info->L, "incompatible floating point representation");
2319 }
2320 info->u.upi.sizeof_int = READ_VALUE(uint8_t);
2321 info->u.upi.sizeof_size_t = READ_VALUE(uint8_t);
2322}
2323
2324static void
2325unchecked_persist(lua_State *L, lua_Writer writer, void *ud) {
2326 Info info; /* perms buff rootobj */
2327 info.L = L;
2328 info.level = 0;
2329 info.refcount = 0;
2330 info.maxComplexity = kMaxComplexity;
2331 info.passIOToPersist = kPassIOToPersist;
2332 info.generatePath = kGeneratePath;
2333 info.u.pi.writer = writer;
2334 info.u.pi.ud = ud;
2335 info.u.pi.metafield = kPersistKey;
2336 info.u.pi.writeDebugInfo = kWriteDebugInformation;
2337
2338 eris_checkstack(L, 3);
2339
2340 if (get_setting(L, (void*)&kSettingMaxComplexity)) {
2341 /* perms buff rootobj value */
2342 info.maxComplexity = lua_tounsigned(L, -1);
2343 lua_pop(L, 1); /* perms buff rootobj */
2344 }
2345 if (get_setting(L, (void*)&kSettingGeneratePath)) {
2346 /* perms buff rootobj value */
2347 info.generatePath = lua_toboolean(L, -1);
2348 lua_pop(L, 1); /* perms buff rootobj */
2349 }
2350 if (get_setting(L, (void*)&kSettingPassIOToPersist)) {
2351 /* perms buff rootobj value */
2352 info.passIOToPersist = lua_toboolean(L, -1);
2353 lua_pop(L, 1); /* perms buff rootobj */
2354 }
2355 if (get_setting(L, (void*)&kSettingMetafield)) {/* perms buff rootobj value */
2356 info.u.pi.metafield = lua_tostring(L, -1);
2357 lua_pop(L, 1); /* perms buff rootobj */
2358 }
2359 if (get_setting(L, (void*)&kSettingWriteDebugInfo)) {
2360 /* perms buff rootobj value */
2361 info.u.pi.writeDebugInfo = lua_toboolean(L, -1);
2362 lua_pop(L, 1); /* perms buff rootobj */
2363 }
2364
2365 lua_newtable(L); /* perms buff rootobj reftbl */
2366 lua_insert(L, REFTIDX); /* perms reftbl buff rootobj */
2367 if (info.generatePath) {
2368 lua_newtable(L); /* perms reftbl buff rootobj path */
2369 lua_insert(L, PATHIDX); /* perms reftbl buff path rootobj */
2370 pushpath(&info, "root");
2371 }
2372
2373 /* Populate perms table with Lua internals. */
2374 lua_pushvalue(L, PERMIDX); /* perms reftbl buff path? rootobj perms */
2375 populateperms(L, false);
2376 lua_pop(L, 1); /* perms reftbl buff path? rootobj */
2377
2378 p_header(&info);
2379 persist(&info); /* perms reftbl buff path? rootobj */
2380
2381 if (info.generatePath) { /* perms reftbl buff path rootobj */
2382 lua_remove(L, PATHIDX); /* perms reftbl buff rootobj */
2383 } /* perms reftbl buff rootobj */
2384 lua_remove(L, REFTIDX); /* perms buff rootobj */
2385}
2386
2387static void
2388unchecked_unpersist(lua_State *L, lua_Reader reader, void *ud) {/* perms str? */
2389 Info info;
2390 info.L = L;
2391 info.level = 0;
2392 info.refcount = 0;
2393 info.maxComplexity = kMaxComplexity;
2394 info.generatePath = kGeneratePath;
2395 info.passIOToPersist = kPassIOToPersist;
2396 eris_init(L, &info.u.upi.zio, reader, ud);
2397
2398 eris_checkstack(L, 3);
2399
2400 if (get_setting(L, (void*)&kSettingMaxComplexity)) {
2401 /* perms buff rootobj value */
2402 info.maxComplexity = lua_tounsigned(L, -1);
2403 lua_pop(L, 1); /* perms buff rootobj */
2404 }
2405 if (get_setting(L, (void*)&kSettingGeneratePath)) {
2406 /* perms buff? rootobj value */
2407 info.generatePath = lua_toboolean(L, -1);
2408 lua_pop(L, 1); /* perms buff? rootobj */
2409 }
2410 if (get_setting(L, (void*)&kSettingPassIOToPersist)) { /* perms str? value */
2411 info.passIOToPersist = lua_toboolean(L, -1);
2412 lua_pop(L, 1); /* perms str? */
2413 }
2414
2415 lua_newtable(L); /* perms str? reftbl */
2416 lua_insert(L, REFTIDX); /* perms reftbl str? */
2417 if (info.generatePath) {
2418 /* Make sure the path is always at index 4, so that it's the same for
2419 * persist and unpersist. */
2420 lua_pushnil(L); /* perms reftbl str? nil */
2421 lua_insert(L, BUFFIDX); /* perms reftbl nil str? */
2422 lua_newtable(L); /* perms reftbl nil str? path */
2423 lua_insert(L, PATHIDX); /* perms reftbl nil path str? */
2424 pushpath(&info, "root");
2425 }
2426
2427 /* Populate perms table with Lua internals. */
2428 lua_pushvalue(L, PERMIDX); /* perms reftbl nil? path? str? perms */
2429 populateperms(L, true);
2430 lua_pop(L, 1); /* perms reftbl nil? path? str? */
2431
2432 u_header(&info);
2433 unpersist(&info); /* perms reftbl nil? path? str? rootobj */
2434 if (info.generatePath) { /* perms reftbl nil path str? rootobj */
2435 lua_remove(L, PATHIDX); /* perms reftbl nil str? rootobj */
2436 lua_remove(L, BUFFIDX); /* perms reftbl str? rootobj */
2437 } /* perms reftbl str? rootobj */
2438 lua_remove(L, REFTIDX); /* perms str? rootobj */
2439}
2440
2441/** ======================================================================== */
2442
2443static int
2444l_persist(lua_State *L) { /* perms? rootobj? ...? */
2445 Mbuffer buff;
2446
2447 /* See if we have anything at all. */
2448 luaL_checkany(L, 1);
2449
2450 /* If we only have one object we assume it is the root object and that there
2451 * is no perms table, so we create an empty one for internal use. */
2452 if (lua_gettop(L) == 1) { /* rootobj */
2453 eris_checkstack(L, 1);
2454 lua_newtable(L); /* rootobj perms */
2455 lua_insert(L, PERMIDX); /* perms rootobj */
2456 }
2457 else {
2458 luaL_checktype(L, 1, LUA_TTABLE); /* perms rootobj? ...? */
2459 luaL_checkany(L, 2); /* perms rootobj ...? */
2460 lua_settop(L, 2); /* perms rootobj */
2461 }
2462 eris_checkstack(L, 1);
2463 lua_pushnil(L); /* perms rootobj buff */
2464 lua_insert(L, 2); /* perms buff rootobj */
2465
2466 eris_initbuffer(L, &buff);
2467 eris_bufflen(&buff) = 0; /* Not initialized by initbuffer... */
2468
2469 unchecked_persist(L, writer, &buff); /* perms buff rootobj */
2470
2471 /* Copy the buffer as the result string before removing it, to avoid the data
2472 * being garbage collected. */
2473 lua_pushlstring(L, eris_buffer(&buff), eris_bufflen(&buff));
2474 /* perms buff rootobj str */
2475
2476 return 1;
2477}
2478
2479static int
2480l_unpersist(lua_State *L) { /* perms? str? ...? */
2481 RBuffer buff;
2482
2483 /* See if we have anything at all. */
2484 luaL_checkany(L, 1);
2485
2486 /* If we only have one object we assume it is the root object and that there
2487 * is no perms table, so we create an empty one for internal use. */
2488 if (lua_gettop(L) == 1) { /* str? */
2489 eris_checkstack(L, 1);
2490 lua_newtable(L); /* str? perms */
2491 lua_insert(L, PERMIDX); /* perms str? */
2492 }
2493 else {
2494 luaL_checktype(L, 1, LUA_TTABLE); /* perms str? ...? */
2495 }
2496 eris_buffer(&buff) = luaL_checklstring(L, 2, &eris_bufflen(&buff));
2497 eris_sizebuffer(&buff) = eris_bufflen(&buff); /* perms str ...? */
2498 lua_settop(L, 2); /* perms str */
2499
2500 unchecked_unpersist(L, reader, &buff); /* perms str rootobj */
2501
2502 return 1;
2503}
2504
2505#define IS(s) strncmp(s, name, length < sizeof(s) ? length : sizeof(s)) == 0
2506
2507static int
2508l_settings(lua_State *L) { /* name value? ...? */
2509 size_t length;
2510 const char *name = luaL_checklstring(L, 1, &length);
2511 if (lua_isnone(L, 2)) { /* name ...? */
2512 lua_settop(L, 1); /* name */
2513 /* Get the current setting value and return it. */
2514 if (IS(kSettingMetafield)) {
2515 if (!get_setting(L, (void*)&kSettingMetafield)) {
2516 lua_pushstring(L, kPersistKey);
2517 }
2518 }
2519 else if (IS(kSettingPassIOToPersist)) {
2520 if (!get_setting(L, (void*)&kSettingPassIOToPersist)) {
2521 lua_pushboolean(L, kPassIOToPersist);
2522 }
2523 }
2524 else if (IS(kSettingWriteDebugInfo)) {
2525 if (!get_setting(L, (void*)&kSettingWriteDebugInfo)) {
2526 lua_pushboolean(L, kWriteDebugInformation);
2527 }
2528 }
2529 else if (IS(kSettingGeneratePath)) {
2530 if (!get_setting(L, (void*)&kSettingGeneratePath)) {
2531 lua_pushboolean(L, kGeneratePath);
2532 }
2533 }
2534 else if (IS(kSettingMaxComplexity)) {
2535 if (!get_setting(L, (void*)&kSettingMaxComplexity)) {
2536 lua_pushunsigned(L, kMaxComplexity);
2537 }
2538 }
2539 else {
2540 return luaL_argerror(L, 1, "no such setting");
2541 } /* name value */
2542 return 1;
2543 }
2544 else { /* name value ...? */
2545 lua_settop(L, 2); /* name value */
2546 /* Set a new value for the setting. */
2547 if (IS(kSettingMetafield)) {
2548 luaL_optstring(L, 2, NULL);
2549 set_setting(L, (void*)&kSettingMetafield);
2550 }
2551 else if (IS(kSettingPassIOToPersist)) {
2552 luaL_opt(L, checkboolean, 2, false);
2553 set_setting(L, (void*)&kSettingPassIOToPersist);
2554 }
2555 else if (IS(kSettingWriteDebugInfo)) {
2556 luaL_opt(L, checkboolean, 2, false);
2557 set_setting(L, (void*)&kSettingWriteDebugInfo);
2558 }
2559 else if (IS(kSettingGeneratePath)) {
2560 luaL_opt(L, checkboolean, 2, false);
2561 set_setting(L, (void*)&kSettingGeneratePath);
2562 }
2563 else if (IS(kSettingMaxComplexity)) {
2564 luaL_optunsigned(L, 2, 0);
2565 set_setting(L, (void*)&kSettingMaxComplexity);
2566 }
2567 else {
2568 return luaL_argerror(L, 1, "no such setting");
2569 } /* name */
2570 return 0;
2571 }
2572}
2573
2574#undef IS
2575
2576/** ======================================================================== */
2577
2578static luaL_Reg erislib[] = {
2579 { "persist", l_persist },
2580 { "unpersist", l_unpersist },
2581 { "settings", l_settings },
2582 { NULL, NULL }
2583};
2584
2585LUA_API int luaopen_eris(lua_State *L) {
2586 luaL_newlib(L, erislib);
2587 return 1;
2588}
2589
2590/* }======================================================================== */
2591
2592/*
2593** {===========================================================================
2594** Public API functions.
2595** ============================================================================
2596*/
2597
2598LUA_API void
2599eris_dump(lua_State *L, lua_Writer writer, void *ud) { /* perms? rootobj? */
2600 if (lua_gettop(L) > 2) {
2601 luaL_error(L, "too many arguments");
2602 }
2603 luaL_checktype(L, 1, LUA_TTABLE); /* perms rootobj? */
2604 luaL_checkany(L, 2); /* perms rootobj */
2605 lua_pushnil(L); /* perms rootobj nil */
2606 lua_insert(L, -2); /* perms nil rootobj */
2607 unchecked_persist(L, writer, ud); /* perms nil rootobj */
2608 lua_remove(L, -2); /* perms rootobj */
2609}
2610
2611LUA_API void
2612eris_undump(lua_State *L, lua_Reader reader, void *ud) { /* perms? */
2613 if (lua_gettop(L) > 1) {
2614 luaL_error(L, "too many arguments");
2615 }
2616 luaL_checktype(L, 1, LUA_TTABLE); /* perms */
2617 unchecked_unpersist(L, reader, ud); /* perms rootobj */
2618}
2619
2620/** ======================================================================== */
2621
2622LUA_API void
2623eris_persist(lua_State *L, int perms, int value) { /* ...? */
2624 eris_checkstack(L, 3);
2625 lua_pushcfunction(L, l_persist); /* ... l_persist */
2626 lua_pushvalue(L, perms); /* ... l_persist perms */
2627 lua_pushvalue(L, value); /* ... l_persist perms rootobj */
2628 lua_call(L, 2, 1); /* ... str */
2629}
2630
2631LUA_API void
2632eris_unpersist(lua_State *L, int perms, int value) { /* ... */
2633 eris_checkstack(L, 3);
2634 lua_pushcfunction(L, l_unpersist); /* ... l_unpersist */
2635 lua_pushvalue(L, perms); /* ... l_unpersist perms */
2636 lua_pushvalue(L, value); /* ... l_unpersist perms str */
2637 lua_call(L, 2, 1); /* ... rootobj */
2638}
2639
2640LUA_API void
2641eris_get_setting(lua_State *L, const char *name) { /* ... */
2642 eris_checkstack(L, 2);
2643 lua_pushcfunction(L, l_settings); /* ... l_settings */
2644 lua_pushstring(L, name); /* ... l_settings name */
2645 lua_call(L, 1, 1); /* ... value */
2646}
2647
2648LUA_API void
2649eris_set_setting(lua_State *L, const char *name, int value) { /* ... */
2650 eris_checkstack(L, 3);
2651 lua_pushcfunction(L, l_settings); /* ... l_settings */
2652 lua_pushstring(L, name); /* ... l_settings name */
2653 lua_pushvalue(L, value); /* ... l_settings name value */
2654 lua_call(L, 2, 0); /* ... */
2655}
2656
2657/* }======================================================================== */
2658
02659
=== added file 'src/scripting/eris/eris.h'
--- src/scripting/eris/eris.h 1970-01-01 00:00:00 +0000
+++ src/scripting/eris/eris.h 2014-02-22 14:58:17 +0000
@@ -0,0 +1,148 @@
1/*
2Eris - Heavy-duty persistence for Lua 5.2.2 - Based on Pluto
3Copyright (c) 2013 by Florian Nuecke.
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21THE SOFTWARE.
22*/
23
24/* lua.h must be included before this file */
25
26#ifndef ERIS_H
27#define ERIS_H
28
29/*
30** ==================================================================
31** API
32** ==================================================================
33*/
34
35/**
36 * This provides an interface to Eris' persist functionality for writing in
37 * an arbitrary way, using a writer.
38 *
39 * When called, the stack in 'L' must look like this:
40 * 1: perms:table
41 * 2: value:any
42 *
43 * 'writer' is the writer function used to store all data, 'ud' is passed to
44 * the writer function whenever it is called.
45 *
46 * [-0, +0, e]
47 */
48LUA_API void eris_dump(lua_State* L, lua_Writer writer, void* ud);
49
50/**
51 * This provides an interface to Eris' unpersist functionality for reading
52 * in an arbitrary way, using a reader.
53 *
54 * When called, the stack in 'L' must look like this:
55 * 1: perms:table
56 *
57 * 'reader' is the reader function used to read all data, 'ud' is passed to
58 * the reader function whenever it is called.
59 *
60 * The result of the operation will be pushed onto the stack.
61 *
62 * [-0, +1, e]
63 */
64LUA_API void eris_undump(lua_State* L, lua_Reader reader, void* ud);
65
66/**
67 * This is a stack-based alternative to eris_dump.
68 *
69 * It expects the perms table at the specified index 'perms' and the value to
70 * persist at the specified index 'value'. It will push the resulting string
71 * onto the stack on success.
72 *
73 * [-0, +1, e]
74 */
75LUA_API void eris_persist(lua_State* L, int perms, int value);
76
77/**
78 * This is a stack-based alternative to eris_undump.
79 *
80 * It expects the perms table at the specified index 'perms' and the string
81 * containing persisted data at the specified index 'value'. It will push the
82 * resulting value onto the stack on success.
83 *
84 * [-0, +1, e]
85 */
86LUA_API void eris_unpersist(lua_State* L, int perms, int value);
87
88/**
89 * Pushes the current value of a setting onto the stack.
90 *
91 * The name is the name of the setting to get the value for:
92 * - 'debug' whether to write debug information when persisting function
93 * prototypes (line numbers, local variable names, upvalue names).
94 * - 'maxrec' the maximum complexity of objects we support (the nesting level
95 * of tables, for example). This can be useful to avoid segmentation
96 * faults due to too deep recursion when working with user-provided
97 * data.
98 * - 'path' whether to generate a "path" used to indicate where in an object
99 * an error occurred. This adds considerable overhead and should
100 * only be used to debug errors as they appear.
101 * - 'spio' whether to pass IO objects along as light userdata to special
102 * persistence functions. When persisting this will pass along the
103 * lua_Writer and its void* in addition to the original object, when
104 * unpersisting it will pass along a ZIO*.
105 * - 'spkey' the name of the field in the metatable of tables and userdata
106 * used to control persistence (on/off or special persistence).
107 *
108 * If an unknown name is specified this will throw an error.
109 *
110 * [-0, +1, e]
111 */
112LUA_API void eris_get_setting(lua_State *L, const char *name);
113
114/**
115 * Changes the value of a setting to a value on the stack.
116 *
117 * For the available settings see eris_set_setting(). This will get the new
118 * value from the specified stack index 'value'. If the type does not match
119 * this will throw an error. Specify a nil value to reset the setting to its
120 * default value.
121 *
122 * [-0, +0, e]
123 */
124LUA_API void eris_set_setting(lua_State *L, const char *name, int value);
125
126/*
127** ==================================================================
128** Library installer
129** ==================================================================
130*/
131
132/**
133 * This pushes a table with the two functions 'persist' and 'unpersist':
134 * persist([perms,] value)
135 * Where 'perms' is a table with "permanent" objects and 'value' is the
136 * value that should be persisted. Returns the string with persisted data.
137 * If only one value is given, the perms table is assumed to be empty.
138 *
139 * unpersist([perms,] value)
140 * Where 'perms' is a table with the inverse mapping as used when
141 * persisting the data via persist() and 'value' is the string with the
142 * persisted data returned by persist(). Returns the unpersisted value.
143 * If only one value is given, the perms table is assumed to be empty.
144 */
145LUA_API int luaopen_eris(lua_State* L);
146
147#endif
148
0149
=== added file 'src/scripting/eris/lapi.c'
--- src/scripting/eris/lapi.c 1970-01-01 00:00:00 +0000
+++ src/scripting/eris/lapi.c 2014-02-22 14:58:17 +0000
@@ -0,0 +1,1284 @@
1/*
2** $Id: lapi.c,v 2.171.1.1 2013/04/12 18:48:47 roberto Exp $
3** Lua API
4** See Copyright Notice in lua.h
5*/
6
7
8#include <stdarg.h>
9#include <string.h>
10
11#define lapi_c
12#define LUA_CORE
13
14#include "lua.h"
15
16#include "lapi.h"
17#include "ldebug.h"
18#include "ldo.h"
19#include "lfunc.h"
20#include "lgc.h"
21#include "lmem.h"
22#include "lobject.h"
23#include "lstate.h"
24#include "lstring.h"
25#include "ltable.h"
26#include "ltm.h"
27#include "lundump.h"
28#include "lvm.h"
29
30
31
32const char lua_ident[] =
33 "$LuaVersion: " LUA_COPYRIGHT " $"
34 "$LuaAuthors: " LUA_AUTHORS " $";
35
36
37/* value at a non-valid index */
38#define NONVALIDVALUE cast(TValue *, luaO_nilobject)
39
40/* corresponding test */
41#define isvalid(o) ((o) != luaO_nilobject)
42
43/* test for pseudo index */
44#define ispseudo(i) ((i) <= LUA_REGISTRYINDEX)
45
46/* test for valid but not pseudo index */
47#define isstackindex(i, o) (isvalid(o) && !ispseudo(i))
48
49#define api_checkvalidindex(L, o) api_check(L, isvalid(o), "invalid index")
50
51#define api_checkstackindex(L, i, o) \
52 api_check(L, isstackindex(i, o), "index not in the stack")
53
54
55static TValue *index2addr (lua_State *L, int idx) {
56 CallInfo *ci = L->ci;
57 if (idx > 0) {
58 TValue *o = ci->func + idx;
59 api_check(L, idx <= ci->top - (ci->func + 1), "unacceptable index");
60 if (o >= L->top) return NONVALIDVALUE;
61 else return o;
62 }
63 else if (!ispseudo(idx)) { /* negative index */
64 api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index");
65 return L->top + idx;
66 }
67 else if (idx == LUA_REGISTRYINDEX)
68 return &G(L)->l_registry;
69 else { /* upvalues */
70 idx = LUA_REGISTRYINDEX - idx;
71 api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");
72 if (ttislcf(ci->func)) /* light C function? */
73 return NONVALIDVALUE; /* it has no upvalues */
74 else {
75 CClosure *func = clCvalue(ci->func);
76 return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : NONVALIDVALUE;
77 }
78 }
79}
80
81
82/*
83** to be called by 'lua_checkstack' in protected mode, to grow stack
84** capturing memory errors
85*/
86static void growstack (lua_State *L, void *ud) {
87 int size = *(int *)ud;
88 luaD_growstack(L, size);
89}
90
91
92LUA_API int lua_checkstack (lua_State *L, int size) {
93 int res;
94 CallInfo *ci = L->ci;
95 lua_lock(L);
96 if (L->stack_last - L->top > size) /* stack large enough? */
97 res = 1; /* yes; check is OK */
98 else { /* no; need to grow stack */
99 int inuse = cast_int(L->top - L->stack) + EXTRA_STACK;
100 if (inuse > LUAI_MAXSTACK - size) /* can grow without overflow? */
101 res = 0; /* no */
102 else /* try to grow stack */
103 res = (luaD_rawrunprotected(L, &growstack, &size) == LUA_OK);
104 }
105 if (res && ci->top < L->top + size)
106 ci->top = L->top + size; /* adjust frame top */
107 lua_unlock(L);
108 return res;
109}
110
111
112LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) {
113 int i;
114 if (from == to) return;
115 lua_lock(to);
116 api_checknelems(from, n);
117 api_check(from, G(from) == G(to), "moving among independent states");
118 api_check(from, to->ci->top - to->top >= n, "not enough elements to move");
119 from->top -= n;
120 for (i = 0; i < n; i++) {
121 setobj2s(to, to->top++, from->top + i);
122 }
123 lua_unlock(to);
124}
125
126
127LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) {
128 lua_CFunction old;
129 lua_lock(L);
130 old = G(L)->panic;
131 G(L)->panic = panicf;
132 lua_unlock(L);
133 return old;
134}
135
136
137LUA_API const lua_Number *lua_version (lua_State *L) {
138 static const lua_Number version = LUA_VERSION_NUM;
139 if (L == NULL) return &version;
140 else return G(L)->version;
141}
142
143
144
145/*
146** basic stack manipulation
147*/
148
149
150/*
151** convert an acceptable stack index into an absolute index
152*/
153LUA_API int lua_absindex (lua_State *L, int idx) {
154 return (idx > 0 || ispseudo(idx))
155 ? idx
156 : cast_int(L->top - L->ci->func + idx);
157}
158
159
160LUA_API int lua_gettop (lua_State *L) {
161 return cast_int(L->top - (L->ci->func + 1));
162}
163
164
165LUA_API void lua_settop (lua_State *L, int idx) {
166 StkId func = L->ci->func;
167 lua_lock(L);
168 if (idx >= 0) {
169 api_check(L, idx <= L->stack_last - (func + 1), "new top too large");
170 while (L->top < (func + 1) + idx)
171 setnilvalue(L->top++);
172 L->top = (func + 1) + idx;
173 }
174 else {
175 api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
176 L->top += idx+1; /* `subtract' index (index is negative) */
177 }
178 lua_unlock(L);
179}
180
181
182LUA_API void lua_remove (lua_State *L, int idx) {
183 StkId p;
184 lua_lock(L);
185 p = index2addr(L, idx);
186 api_checkstackindex(L, idx, p);
187 while (++p < L->top) setobjs2s(L, p-1, p);
188 L->top--;
189 lua_unlock(L);
190}
191
192
193LUA_API void lua_insert (lua_State *L, int idx) {
194 StkId p;
195 StkId q;
196 lua_lock(L);
197 p = index2addr(L, idx);
198 api_checkstackindex(L, idx, p);
199 for (q = L->top; q > p; q--) /* use L->top as a temporary */
200 setobjs2s(L, q, q - 1);
201 setobjs2s(L, p, L->top);
202 lua_unlock(L);
203}
204
205
206static void moveto (lua_State *L, TValue *fr, int idx) {
207 TValue *to = index2addr(L, idx);
208 api_checkvalidindex(L, to);
209 setobj(L, to, fr);
210 if (idx < LUA_REGISTRYINDEX) /* function upvalue? */
211 luaC_barrier(L, clCvalue(L->ci->func), fr);
212 /* LUA_REGISTRYINDEX does not need gc barrier
213 (collector revisits it before finishing collection) */
214}
215
216
217LUA_API void lua_replace (lua_State *L, int idx) {
218 lua_lock(L);
219 api_checknelems(L, 1);
220 moveto(L, L->top - 1, idx);
221 L->top--;
222 lua_unlock(L);
223}
224
225
226LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) {
227 TValue *fr;
228 lua_lock(L);
229 fr = index2addr(L, fromidx);
230 moveto(L, fr, toidx);
231 lua_unlock(L);
232}
233
234
235LUA_API void lua_pushvalue (lua_State *L, int idx) {
236 lua_lock(L);
237 setobj2s(L, L->top, index2addr(L, idx));
238 api_incr_top(L);
239 lua_unlock(L);
240}
241
242
243
244/*
245** access functions (stack -> C)
246*/
247
248
249LUA_API int lua_type (lua_State *L, int idx) {
250 StkId o = index2addr(L, idx);
251 return (isvalid(o) ? ttypenv(o) : LUA_TNONE);
252}
253
254
255LUA_API const char *lua_typename (lua_State *L, int t) {
256 UNUSED(L);
257 return ttypename(t);
258}
259
260
261LUA_API int lua_iscfunction (lua_State *L, int idx) {
262 StkId o = index2addr(L, idx);
263 return (ttislcf(o) || (ttisCclosure(o)));
264}
265
266
267LUA_API int lua_isnumber (lua_State *L, int idx) {
268 TValue n;
269 const TValue *o = index2addr(L, idx);
270 return tonumber(o, &n);
271}
272
273
274LUA_API int lua_isstring (lua_State *L, int idx) {
275 int t = lua_type(L, idx);
276 return (t == LUA_TSTRING || t == LUA_TNUMBER);
277}
278
279
280LUA_API int lua_isuserdata (lua_State *L, int idx) {
281 const TValue *o = index2addr(L, idx);
282 return (ttisuserdata(o) || ttislightuserdata(o));
283}
284
285
286LUA_API int lua_rawequal (lua_State *L, int index1, int index2) {
287 StkId o1 = index2addr(L, index1);
288 StkId o2 = index2addr(L, index2);
289 return (isvalid(o1) && isvalid(o2)) ? luaV_rawequalobj(o1, o2) : 0;
290}
291
292
293LUA_API void lua_arith (lua_State *L, int op) {
294 StkId o1; /* 1st operand */
295 StkId o2; /* 2nd operand */
296 lua_lock(L);
297 if (op != LUA_OPUNM) /* all other operations expect two operands */
298 api_checknelems(L, 2);
299 else { /* for unary minus, add fake 2nd operand */
300 api_checknelems(L, 1);
301 setobjs2s(L, L->top, L->top - 1);
302 L->top++;
303 }
304 o1 = L->top - 2;
305 o2 = L->top - 1;
306 if (ttisnumber(o1) && ttisnumber(o2)) {
307 setnvalue(o1, luaO_arith(op, nvalue(o1), nvalue(o2)));
308 }
309 else
310 luaV_arith(L, o1, o1, o2, cast(TMS, op - LUA_OPADD + TM_ADD));
311 L->top--;
312 lua_unlock(L);
313}
314
315
316LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) {
317 StkId o1, o2;
318 int i = 0;
319 lua_lock(L); /* may call tag method */
320 o1 = index2addr(L, index1);
321 o2 = index2addr(L, index2);
322 if (isvalid(o1) && isvalid(o2)) {
323 switch (op) {
324 case LUA_OPEQ: i = equalobj(L, o1, o2); break;
325 case LUA_OPLT: i = luaV_lessthan(L, o1, o2); break;
326 case LUA_OPLE: i = luaV_lessequal(L, o1, o2); break;
327 default: api_check(L, 0, "invalid option");
328 }
329 }
330 lua_unlock(L);
331 return i;
332}
333
334
335LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *isnum) {
336 TValue n;
337 const TValue *o = index2addr(L, idx);
338 if (tonumber(o, &n)) {
339 if (isnum) *isnum = 1;
340 return nvalue(o);
341 }
342 else {
343 if (isnum) *isnum = 0;
344 return 0;
345 }
346}
347
348
349LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *isnum) {
350 TValue n;
351 const TValue *o = index2addr(L, idx);
352 if (tonumber(o, &n)) {
353 lua_Integer res;
354 lua_Number num = nvalue(o);
355 lua_number2integer(res, num);
356 if (isnum) *isnum = 1;
357 return res;
358 }
359 else {
360 if (isnum) *isnum = 0;
361 return 0;
362 }
363}
364
365
366LUA_API lua_Unsigned lua_tounsignedx (lua_State *L, int idx, int *isnum) {
367 TValue n;
368 const TValue *o = index2addr(L, idx);
369 if (tonumber(o, &n)) {
370 lua_Unsigned res;
371 lua_Number num = nvalue(o);
372 lua_number2unsigned(res, num);
373 if (isnum) *isnum = 1;
374 return res;
375 }
376 else {
377 if (isnum) *isnum = 0;
378 return 0;
379 }
380}
381
382
383LUA_API int lua_toboolean (lua_State *L, int idx) {
384 const TValue *o = index2addr(L, idx);
385 return !l_isfalse(o);
386}
387
388
389LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {
390 StkId o = index2addr(L, idx);
391 if (!ttisstring(o)) {
392 lua_lock(L); /* `luaV_tostring' may create a new string */
393 if (!luaV_tostring(L, o)) { /* conversion failed? */
394 if (len != NULL) *len = 0;
395 lua_unlock(L);
396 return NULL;
397 }
398 luaC_checkGC(L);
399 o = index2addr(L, idx); /* previous call may reallocate the stack */
400 lua_unlock(L);
401 }
402 if (len != NULL) *len = tsvalue(o)->len;
403 return svalue(o);
404}
405
406
407LUA_API size_t lua_rawlen (lua_State *L, int idx) {
408 StkId o = index2addr(L, idx);
409 switch (ttypenv(o)) {
410 case LUA_TSTRING: return tsvalue(o)->len;
411 case LUA_TUSERDATA: return uvalue(o)->len;
412 case LUA_TTABLE: return luaH_getn(hvalue(o));
413 default: return 0;
414 }
415}
416
417
418LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) {
419 StkId o = index2addr(L, idx);
420 if (ttislcf(o)) return fvalue(o);
421 else if (ttisCclosure(o))
422 return clCvalue(o)->f;
423 else return NULL; /* not a C function */
424}
425
426
427LUA_API void *lua_touserdata (lua_State *L, int idx) {
428 StkId o = index2addr(L, idx);
429 switch (ttypenv(o)) {
430 case LUA_TUSERDATA: return (rawuvalue(o) + 1);
431 case LUA_TLIGHTUSERDATA: return pvalue(o);
432 default: return NULL;
433 }
434}
435
436
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to status/vote changes: