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