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