1 /*
2 Copyright 2012 Jared Krinke.
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 */
22
23 #include <setjmp.h>
24 #include <lauxlib.h>
25
26 #include "r_script.h"
27 #include "r_log.h"
28 #include "r_assert.h"
29 #include "r_layer_stack.h"
30
31 /* Panic function for errors on non-protected calls */
l_panic(lua_State * ls)32 int l_panic(lua_State *ls)
33 {
34 r_state_t *rs = r_script_get_r_state(ls);
35
36 if (rs != NULL)
37 {
38 r_log_error(rs, lua_tostring(ls, -1));
39 lua_pop(ls, 1);
40 longjmp(rs->script_error_return_point, 0);
41 }
42
43 return 0;
44 }
45
r_script_start(r_state_t * rs)46 r_status_t r_script_start(r_state_t *rs)
47 {
48 /* Create a new Lua state */
49 r_status_t status = (rs != NULL) ? R_SUCCESS : R_F_INVALID_POINTER;
50 R_ASSERT(R_SUCCEEDED(status));
51
52 if (R_SUCCEEDED(status))
53 {
54 rs->script_state = luaL_newstate();
55 status = (rs->script_state != NULL) ? R_SUCCESS : R_F_OUT_OF_MEMORY;
56
57 if (R_SUCCEEDED(status))
58 {
59 lua_State *ls = rs->script_state;
60
61 /* Store pointer to r_state_t */
62 lua_pushlightuserdata(ls, ls);
63 lua_pushlightuserdata(ls, rs);
64 lua_rawset(ls, LUA_REGISTRYINDEX);
65
66 status = r_script_setup(rs);
67 }
68 }
69
70 return status;
71 }
72
73 /* TODO: Cleanup functions should return status... */
r_script_end(r_state_t * rs)74 void r_script_end(r_state_t *rs)
75 {
76 r_status_t status = (rs != NULL && rs->script_state != NULL) ? R_SUCCESS : R_F_INVALID_POINTER;
77 R_ASSERT(R_SUCCEEDED(status));
78
79 if (R_SUCCEEDED(status))
80 {
81 lua_close(rs->script_state);
82 }
83 }
84
r_script_call_internal(r_state_t * rs,int argument_count,int result_count,r_boolean_t use_error_handler)85 static r_status_t r_script_call_internal(r_state_t *rs, int argument_count, int result_count, r_boolean_t use_error_handler)
86 {
87 r_status_t status = (rs != NULL && rs->script_state != NULL) ? R_SUCCESS : R_F_INVALID_POINTER;
88 R_ASSERT(R_SUCCEEDED(status));
89
90 if (R_SUCCEEDED(status))
91 {
92 lua_State *ls = rs->script_state;
93
94 /* TODO: Implement an error handler function and use it here */
95 status = (lua_pcall(ls, argument_count, result_count, 0) == 0) ? R_SUCCESS : RS_FAILURE;
96
97 if (R_FAILED(status))
98 {
99 /* Propagate the error to the active layer's error handler if one exists */
100 int error_message_index = lua_gettop(ls);
101
102 if (use_error_handler)
103 {
104 /* Get the active layer (and ensure one exists) */
105 r_layer_t *layer = NULL;
106 r_status_t layer_status = r_layer_stack_get_active_layer(rs, &layer);
107
108 if (R_SUCCEEDED(layer_status))
109 {
110 layer_status = (layer != NULL) ? R_SUCCESS : RS_F_NO_ACTIVE_LAYER;
111 }
112
113 if (R_SUCCEEDED(layer_status))
114 {
115 /* Get the error handler function and verify type */
116 layer_status = r_object_ref_push(rs, (r_object_t*)layer, &layer->error_occurred);
117
118 if (R_SUCCEEDED(layer_status))
119 {
120 layer_status = (lua_type(ls, -1) == LUA_TFUNCTION) ? R_SUCCESS : RS_F_INCORRECT_TYPE;
121
122 if (R_SUCCEEDED(layer_status))
123 {
124 /* Push arguments */
125 layer_status = r_object_push(rs, (r_object_t*)layer);
126
127 if (R_SUCCEEDED(layer_status))
128 {
129 /* Call the error handler (but don't go into infinite recursion if there's an error in the handler) */
130 lua_pushvalue(ls, error_message_index);
131 layer_status = r_script_call_internal(rs, 2, 0, R_FALSE);
132
133 if (R_SUCCEEDED(layer_status))
134 {
135 /* TODO: Make sure that this behavior (return success if the error handler was called) is documented! */
136 status = R_S_ERROR_HANDLED;
137 }
138 }
139 else
140 {
141 lua_pop(ls, 1);
142 }
143 }
144 else
145 {
146 lua_pop(ls, 1);
147 }
148 }
149 }
150 }
151
152 /* If error handlers aren't being used (or one couldn't be found or it failed), just print the error */
153 if (R_FAILED(status))
154 {
155 r_log_error(rs, lua_tostring(ls, -1));
156
157 {
158 lua_Debug dbg;
159 r_status_t status_local = R_SUCCESS;
160 int level = 0;
161
162 while (lua_getstack(ls, level++, &dbg) == 1 && R_SUCCEEDED(status_local))
163 {
164 status_local = lua_getinfo(ls, "nSl", &dbg) ? R_SUCCESS : R_FAILURE;
165
166 if (R_SUCCEEDED(status_local))
167 {
168 fprintf(stdout, "Frame %d: %s: %s, line %d\n", level, dbg.source, dbg.name, dbg.currentline);
169 fflush(stdout);
170 }
171 }
172 }
173 }
174
175 lua_pop(ls, 1);
176 }
177 }
178
179 return status;
180 }
181
r_script_call(r_state_t * rs,int argument_count,int result_count)182 r_status_t r_script_call(r_state_t *rs, int argument_count, int result_count)
183 {
184 return r_script_call_internal(rs, argument_count, result_count, R_TRUE);
185 }
186
r_script_get_r_state(lua_State * ls)187 r_state_t *r_script_get_r_state(lua_State *ls)
188 {
189 r_state_t *rs;
190
191 lua_pushlightuserdata(ls, ls);
192 lua_rawget(ls, LUA_REGISTRYINDEX);
193 rs = (r_state_t*)lua_touserdata(ls, -1);
194 lua_pop(ls, 1);
195
196 return rs;
197 }
198
r_script_verify_arguments(r_state_t * rs,int expected_argument_count,const r_script_argument_t * expected_arguments)199 r_status_t r_script_verify_arguments(r_state_t *rs, int expected_argument_count, const r_script_argument_t *expected_arguments)
200 {
201 return r_script_verify_arguments_with_optional(rs, expected_argument_count, expected_argument_count, expected_arguments);
202 }
203
r_script_verify_arguments_with_optional(r_state_t * rs,int min_argument_count,int max_argument_count,const r_script_argument_t * expected_arguments)204 r_status_t r_script_verify_arguments_with_optional(r_state_t *rs, int min_argument_count, int max_argument_count, const r_script_argument_t *expected_arguments)
205 {
206 r_status_t status = (rs != NULL && rs->script_state != NULL && (max_argument_count == 0 || expected_arguments != NULL)) ? R_SUCCESS : R_F_INVALID_POINTER;
207 R_ASSERT(R_SUCCEEDED(status));
208
209 if (R_SUCCEEDED(status))
210 {
211 lua_State *ls = rs->script_state;
212 int argument_count = lua_gettop(ls);
213
214 /* Verify argument count */
215 status = (argument_count >= min_argument_count && argument_count <= max_argument_count) ? R_SUCCESS : RS_F_ARGUMENT_COUNT;
216
217 /* Verify argument types */
218 if (R_SUCCEEDED(status))
219 {
220 int i;
221
222 for (i = 0; i < argument_count && R_SUCCEEDED(status); ++i)
223 {
224 switch (expected_arguments[i].script_type)
225 {
226 case LUA_TUSERDATA:
227 /* Verify this is a object and it is the correct object type */
228 status = (lua_type(ls, i + 1) == LUA_TUSERDATA) ? R_SUCCESS : RS_F_INCORRECT_TYPE;
229
230 if (R_SUCCEEDED(status))
231 {
232 r_object_t *object = (r_object_t*)lua_touserdata(ls, i + 1);
233
234 status = (object->header->type == expected_arguments[i].object_type) ? R_SUCCESS : RS_F_INCORRECT_TYPE;
235 }
236 break;
237
238 default:
239 /* Just verify the script type */
240 status = (lua_type(ls, i + 1) == expected_arguments[i].script_type) ? R_SUCCESS : RS_F_INCORRECT_TYPE;
241 break;
242 }
243 }
244 }
245 }
246
247 return status;
248 }
249
r_script_register_node_list(r_state_t * rs,const r_script_node_t * nodes,int root_index,r_object_ref_t * root_ref)250 static r_status_t r_script_register_node_list(r_state_t *rs, const r_script_node_t *nodes, int root_index, r_object_ref_t *root_ref)
251 {
252 r_status_t status = (rs != NULL && rs->script_state != NULL && nodes != NULL) ? R_SUCCESS : R_F_INVALID_POINTER;
253 R_SCRIPT_ENTER();
254 R_ASSERT(R_SUCCEEDED(status));
255
256 /* Verify root */
257 if (R_SUCCEEDED(status))
258 {
259 r_boolean_t valid_index = (root_index > 0 || root_index == LUA_REGISTRYINDEX || root_index == LUA_GLOBALSINDEX) ? R_TRUE : R_FALSE;
260 r_boolean_t valid_ref = (root_index == 0 && nodes->name[0] == '\0' && nodes[1].name == NULL) ? R_TRUE : R_FALSE;
261
262 status = (valid_index || valid_ref) ? R_SUCCESS : R_F_INVALID_ARGUMENT;
263
264 if (R_SUCCEEDED(status))
265 {
266 /* Register the nodes */
267 lua_State *ls = rs->script_state;
268 const r_script_node_t *node = NULL;
269
270 for (node = nodes; node->name != NULL && R_SUCCEEDED(status); ++node)
271 {
272 /* Push the name, if necessary */
273 if (valid_index)
274 {
275 lua_pushstring(ls, node->name);
276 }
277
278 /* Push the value */
279 switch (node->type)
280 {
281 case R_SCRIPT_NODE_TYPE_TABLE:
282 lua_newtable(ls);
283
284 /* Register children, if necessary */
285 if (node->table_children != NULL)
286 {
287 status = r_script_register_node_list(rs, node->table_children, lua_gettop(ls), NULL);
288 }
289 break;
290
291 case R_SCRIPT_NODE_TYPE_FUNCTION:
292 R_ASSERT(node->function_func != NULL);
293 lua_pushcfunction(ls, node->function_func);
294 break;
295
296 default:
297 R_ASSERT(0);
298 status = R_F_INVALID_ARGUMENT;
299 }
300
301 /* Set the key-value pair */
302 if (valid_index)
303 {
304 if (R_SUCCEEDED(status))
305 {
306 lua_rawset(ls, root_index);
307 }
308 else
309 {
310 lua_pop(ls, 1);
311 }
312 }
313 else
314 {
315 if (R_SUCCEEDED(status))
316 {
317 switch (node->type)
318 {
319 case R_SCRIPT_NODE_TYPE_TABLE:
320 status = r_object_table_ref_write(rs, NULL, root_ref, lua_gettop(ls));
321 break;
322
323 case R_SCRIPT_NODE_TYPE_FUNCTION:
324 status = r_object_function_ref_write(rs, NULL, root_ref, lua_gettop(ls));
325 break;
326
327 default:
328 R_ASSERT(0);
329 status = R_F_INVALID_ARGUMENT;
330 }
331 }
332
333 lua_pop(ls, 1);
334 }
335 }
336 }
337 }
338
339 R_SCRIPT_EXIT(0);
340
341 return status;
342 }
343
r_script_register_node_internal(r_state_t * rs,const r_script_node_t * node,int root_index,r_object_ref_t * root_ref)344 static r_status_t r_script_register_node_internal(r_state_t *rs, const r_script_node_t *node, int root_index, r_object_ref_t *root_ref)
345 {
346 r_status_t status = (node != NULL) ? R_SUCCESS : R_F_INVALID_POINTER;
347 R_ASSERT(R_SUCCEEDED(status));
348
349 /* Register the node */
350 if (R_SUCCEEDED(status))
351 {
352 r_script_node_t nodes[] = { { node->name, node->type, node->table_children, node->function_func }, { NULL } };
353
354 status = r_script_register_node_list(rs, nodes, root_index, root_ref);
355 }
356
357 return status;
358 }
359
r_script_register_node(r_state_t * rs,const r_script_node_t * node,int root_index)360 r_status_t r_script_register_node(r_state_t *rs, const r_script_node_t *node, int root_index)
361 {
362 r_status_t status = (rs != NULL && rs->script_state != NULL && node != NULL) ? R_SUCCESS : R_F_INVALID_POINTER;
363 R_ASSERT(R_SUCCEEDED(status));
364
365 /* Register the node */
366 if (R_SUCCEEDED(status))
367 {
368 status = r_script_register_node_internal(rs, node, root_index, NULL);
369 }
370
371 return status;
372 }
373
r_script_register_nodes(r_state_t * rs,const r_script_node_root_t * node_roots)374 r_status_t r_script_register_nodes(r_state_t *rs, const r_script_node_root_t *node_roots)
375 {
376 r_status_t status = (rs != NULL && node_roots != NULL) ? R_SUCCESS : R_F_INVALID_POINTER;
377 R_SCRIPT_ENTER();
378 R_ASSERT(R_SUCCEEDED(status));
379
380 if (R_SUCCEEDED(status))
381 {
382 const r_script_node_root_t *node_root = NULL;
383
384 for (node_root = node_roots; (node_root->index != 0 || node_root->ref != 0) && R_SUCCEEDED(status); ++node_root)
385 {
386 status = r_script_register_node_internal(rs, &node_root->node, node_root->index, node_root->ref);
387 }
388 }
389
390 R_SCRIPT_EXIT(0);
391
392 return status;
393 }
394