BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file call2.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * @section DESCRIPTION
30 *
31 *
32 * Synopsis:
33 * call(string template, list args)
34 *
35 * Description:
36 * Calls a process template. The 'template' argument is the name of the process
37 * template to call, and the 'list' argument is a list of arguments for the
38 * process template. Calling a process template is roughly equivalent to placing
39 * the statements within that template into the place of call(), except for the
40 * points presented next. The 'template' argument can be a special value "<none>",
41 * which makes call() a no-op.
42 *
43 * The process created from the called template will be able to access the arguments
44 * that were given in the 'args' argument to call() via the '_argN' predefined\
45 * objects (e.g. _arg0 for the first argumens), and also via '_args' for the entire
46 * argument list.
47 *
48 * The called process also will be able to access objects within the calling
49 * process as seen by the call() statement. However such any access needs to happen
50 * via a special '_caller' predefined object. For example, if there is a statement
51 * 'var("a") x;' somewhere above the call() statement, the called process can access
52 * it as '_caller.x'.
53 *
54 * Note that call() preserves backtracking semantics, i.e. when a statement within
55 * the called process goes down after having gone up, the behaviour really is as
56 * if the call() statement was replaced with the statements in the called template,
57 * (disregarding variable resolution).
58 *
59 * Because the template name is an argument, call() can be used for branching.
60 * For example, if we have an object 'x' with the value "true" or "false", a
61 * branch can be performed by defining two process templates, 'branch_true'
62 * and 'branch_false', and branching with the following code:
63 *
64 * concat("branch_", x) name;
65 * call(name, {});
66 *
67 *
68 * Synopsis:
69 * call_with_caller_target(string template, list args, string caller_target)
70 *
71 * Description:
72 * Like call(), except that the target of the '_caller' predefined object is
73 * specified by the 'caller_target' argument. This is indented to be used from
74 * generic code for user-specified callbacks, allowing the user to easily refer to
75 * his own objects from inside the callback.
76 *
77 * The 'caller_target' must be a non-empty string referring to an actual object;
78 * there is no choice of 'caller_target' that would make call_with_caller_target()
79 * equivalent to call().
80 *
81 *
82 * Synopsis:
83 * embcall(string template)
84 *
85 * Description:
86 * Like call, but makes its own scope directly available in the called
87 * template process, instead of via _caller. Also, doesn not provide any
88 * arguments to the template process.
89 *
90 *
91 * Synopsis:
92 * inline_code(string template)
93 * inline_code::call(list args)
94 *
95 * Description:
96 * The inline_code() acts as a proxy object for calling the specified template.
97 * The inline_code::call calls the template of the corresponding inline_code
98 * instance, in a manner similar to a simple "call". The called template will
99 * have access to the following resources:
100 * - The arguments will be available in the usual fashion.
101 * - The scope of the inline_code::call instance will be available as "_caller".
102 * - The scope of the inline_code instance will be directly available.
103 * - The scope of the inline_code instance will also be available as "_scope".
104 * This is useful to access shadowed names, e.g. "_caller" and the arguments
105 * stuff (_argN, _args).
106 */
107  
108 #include <stdlib.h>
109 #include <string.h>
110  
111 #include <misc/debug.h>
112 #include <misc/offset.h>
113 #include <structure/LinkedList0.h>
114 #include <ncd/extra/NCDFastNames.h>
115  
116 #include <ncd/module_common.h>
117  
118 #include <generated/blog_channel_ncd_call2.h>
119  
120 #define STATE_WORKING 1
121 #define STATE_UP 2
122 #define STATE_WAITING 3
123 #define STATE_TERMINATING 4
124 #define STATE_NONE 5
125  
126 struct instance;
127  
128 typedef void (*call_extra_free_cb) (struct instance *o);
129  
130 struct instance {
131 NCDModuleInst *i;
132 call_extra_free_cb extra_free_cb;
133 NCDModuleProcess process;
134 int state;
135 };
136  
137 struct instance_with_caller_target {
138 struct instance base;
139 NCDFastNames names;
140 };
141  
142 struct inline_code {
143 NCDModuleInst *i;
144 NCDValRef template_name;
145 LinkedList0 calls_list;
146 };
147  
148 struct inline_code_call {
149 struct instance base;
150 struct inline_code *ic;
151 LinkedList0Node ic_node;
152 };
153  
154 static void process_handler_event (NCDModuleProcess *process, int event);
155 static int process_func_getspecialobj_embed (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object);
156 static int process_func_getspecialobj_noembed (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object);
157 static int process_func_getspecialobj_with_caller_target (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object);
158 static int caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object);
159 static int caller_obj_func_getobj_with_caller_target (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object);
160 static void func_new_templ (void *vo, NCDModuleInst *i, NCDValRef template_name, NCDValRef args, NCDModuleProcess_func_getspecialobj func_getspecialobj, call_extra_free_cb extra_free_cb);
161 static void instance_free (struct instance *o);
162 static void call_with_caller_target_extra_free (struct instance *bo);
163 static void inline_code_extra_free (struct instance *bo);
164 static int inline_code_call_process_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object);
165 static int inline_code_scope_obj_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object);
166  
167 static void process_handler_event (NCDModuleProcess *process, int event)
168 {
169 struct instance *o = UPPER_OBJECT(process, struct instance, process);
170  
171 switch (event) {
172 case NCDMODULEPROCESS_EVENT_UP: {
173 ASSERT(o->state == STATE_WORKING)
174  
175 // signal up
176 NCDModuleInst_Backend_Up(o->i);
177  
178 // set state up
179 o->state = STATE_UP;
180 } break;
181  
182 case NCDMODULEPROCESS_EVENT_DOWN: {
183 ASSERT(o->state == STATE_UP)
184  
185 // signal down
186 NCDModuleInst_Backend_Down(o->i);
187  
188 // set state waiting
189 o->state = STATE_WAITING;
190 } break;
191  
192 case NCDMODULEPROCESS_EVENT_TERMINATED: {
193 ASSERT(o->state == STATE_TERMINATING)
194  
195 // die finally
196 instance_free(o);
197 return;
198 } break;
199  
200 default: ASSERT(0);
201 }
202 }
203  
204 static int process_func_getspecialobj_embed (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
205 {
206 struct instance *o = UPPER_OBJECT(process, struct instance, process);
207  
208 return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
209 }
210  
211 static int process_func_getspecialobj_noembed (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
212 {
213 struct instance *o = UPPER_OBJECT(process, struct instance, process);
214  
215 if (name == NCD_STRING_CALLER) {
216 *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, caller_obj_func_getobj);
217 return 1;
218 }
219  
220 return 0;
221 }
222  
223 static int process_func_getspecialobj_with_caller_target (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
224 {
225 struct instance *o = UPPER_OBJECT(process, struct instance, process);
226  
227 if (name == NCD_STRING_CALLER) {
228 *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, caller_obj_func_getobj_with_caller_target);
229 return 1;
230 }
231  
232 return 0;
233 }
234  
235 static int caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object)
236 {
237 struct instance *o = NCDObject_DataPtr(obj);
238  
239 return NCDModuleInst_Backend_GetObj(o->i, name, out_object);
240 }
241  
242 static int caller_obj_func_getobj_with_caller_target (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object)
243 {
244 struct instance_with_caller_target *o_ch = NCDObject_DataPtr(obj);
245  
246 NCD_string_id_t *names = NCDFastNames_GetNames(&o_ch->names);
247  
248 NCDObject object;
249 if (!NCDModuleInst_Backend_GetObj(o_ch->base.i, names[0], &object)) {
250 return 0;
251 }
252  
253 NCDObject obj2;
254 if (!NCDObject_ResolveObjExprCompact(&object, names + 1, NCDFastNames_GetNumNames(&o_ch->names) - 1, &obj2)) {
255 return 0;
256 }
257  
258 if (name == NCD_STRING_EMPTY) {
259 *out_object = obj2;
260 return 1;
261 }
262  
263 return NCDObject_GetObj(&obj2, name, out_object);
264 }
265  
266 static void func_new_templ (void *vo, NCDModuleInst *i, NCDValRef template_name, NCDValRef args, NCDModuleProcess_func_getspecialobj func_getspecialobj, call_extra_free_cb extra_free_cb)
267 {
268 ASSERT(NCDVal_IsInvalid(template_name) || NCDVal_IsString(template_name))
269 ASSERT(NCDVal_IsInvalid(args) || NCDVal_IsList(args))
270 ASSERT(func_getspecialobj)
271  
272 struct instance *o = vo;
273 o->i = i;
274 o->extra_free_cb = extra_free_cb;
275  
276 if (NCDVal_IsInvalid(template_name) || ncd_is_none(template_name)) {
277 // signal up
278 NCDModuleInst_Backend_Up(o->i);
279  
280 // set state none
281 o->state = STATE_NONE;
282 } else {
283 // create process
284 if (!NCDModuleProcess_InitValue(&o->process, o->i, template_name, args, process_handler_event)) {
285 ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed");
286 goto fail1;
287 }
288  
289 // set special functions
290 NCDModuleProcess_SetSpecialFuncs(&o->process, func_getspecialobj);
291  
292 // set state working
293 o->state = STATE_WORKING;
294 }
295  
296 return;
297  
298 fail1:
299 if (o->extra_free_cb) {
300 o->extra_free_cb(o);
301 }
302 NCDModuleInst_Backend_DeadError(i);
303 }
304  
305 static void instance_free (struct instance *o)
306 {
307 if (o->extra_free_cb) {
308 o->extra_free_cb(o);
309 }
310  
311 // free process
312 if (o->state != STATE_NONE) {
313 NCDModuleProcess_Free(&o->process);
314 }
315  
316 NCDModuleInst_Backend_Dead(o->i);
317 }
318  
319 static void func_new_call (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
320 {
321 NCDValRef template_arg;
322 NCDValRef args_arg;
323 if (!NCDVal_ListRead(params->args, 2, &template_arg, &args_arg)) {
324 ModuleLog(i, BLOG_ERROR, "wrong arity");
325 goto fail0;
326 }
327 if (!NCDVal_IsString(template_arg) || !NCDVal_IsList(args_arg)) {
328 ModuleLog(i, BLOG_ERROR, "wrong type");
329 goto fail0;
330 }
331  
332 func_new_templ(vo, i, template_arg, args_arg, process_func_getspecialobj_noembed, NULL);
333 return;
334  
335 fail0:
336 NCDModuleInst_Backend_DeadError(i);
337 }
338  
339 static void func_new_call_with_caller_target (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
340 {
341 NCDValRef template_arg;
342 NCDValRef args_arg;
343 NCDValRef caller_target_arg;
344 if (!NCDVal_ListRead(params->args, 3, &template_arg, &args_arg, &caller_target_arg)) {
345 ModuleLog(i, BLOG_ERROR, "wrong arity");
346 goto fail0;
347 }
348 if (!NCDVal_IsString(template_arg) || !NCDVal_IsList(args_arg) || !NCDVal_IsString(caller_target_arg)) {
349 ModuleLog(i, BLOG_ERROR, "wrong type");
350 goto fail0;
351 }
352  
353 struct instance_with_caller_target *o = vo;
354  
355 int res = NCDFastNames_Init(&o->names, i->params->iparams->string_index, NCDVal_StringMemRef(caller_target_arg));
356 if (!res) {
357 ModuleLog(i, BLOG_ERROR, "NCDFastNames_Init failed");
358 goto fail0;
359 }
360  
361 func_new_templ(vo, i, template_arg, args_arg, process_func_getspecialobj_with_caller_target, call_with_caller_target_extra_free);
362 return;
363  
364 fail0:
365 NCDModuleInst_Backend_DeadError(i);
366 }
367  
368 static void call_with_caller_target_extra_free (struct instance *bo)
369 {
370 struct instance_with_caller_target *o = (void *)bo;
371  
372 NCDFastNames_Free(&o->names);
373 }
374  
375 static void func_new_embcall (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
376 {
377 NCDValRef template_arg;
378 if (!NCDVal_ListRead(params->args, 1, &template_arg)) {
379 ModuleLog(i, BLOG_ERROR, "wrong arity");
380 goto fail0;
381 }
382 if (!NCDVal_IsString(template_arg)) {
383 ModuleLog(i, BLOG_ERROR, "wrong type");
384 goto fail0;
385 }
386  
387 func_new_templ(vo, i, template_arg, NCDVal_NewInvalid(), process_func_getspecialobj_embed, NULL);
388 return;
389  
390 fail0:
391 NCDModuleInst_Backend_DeadError(i);
392 }
393  
394 static void func_die (void *vo)
395 {
396 struct instance *o = vo;
397 ASSERT(o->state != STATE_TERMINATING)
398  
399 // if none, die now
400 if (o->state == STATE_NONE) {
401 instance_free(o);
402 return;
403 }
404  
405 // request process to terminate
406 NCDModuleProcess_Terminate(&o->process);
407  
408 // set state terminating
409 o->state = STATE_TERMINATING;
410 }
411  
412 static void func_clean (void *vo)
413 {
414 struct instance *o = vo;
415 if (o->state != STATE_WAITING) {
416 return;
417 }
418  
419 // allow process to continue
420 NCDModuleProcess_Continue(&o->process);
421  
422 // set state working
423 o->state = STATE_WORKING;
424 }
425  
426 static int func_getobj (void *vo, NCD_string_id_t name, NCDObject *out_object)
427 {
428 struct instance *o = vo;
429  
430 if (o->state == STATE_NONE) {
431 return 0;
432 }
433  
434 return NCDModuleProcess_GetObj(&o->process, name, out_object);
435 }
436  
437 static void inline_code_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
438 {
439 NCDValRef template_arg;
440 if (!NCDVal_ListRead(params->args, 1, &template_arg)) {
441 ModuleLog(i, BLOG_ERROR, "wrong arity");
442 goto fail0;
443 }
444 if (!NCDVal_IsString(template_arg)) {
445 ModuleLog(i, BLOG_ERROR, "wrong type");
446 goto fail0;
447 }
448  
449 struct inline_code *o = vo;
450 o->i = i;
451 o->template_name = template_arg;
452 LinkedList0_Init(&o->calls_list);
453  
454 NCDModuleInst_Backend_Up(i);
455 return;
456  
457 fail0:
458 NCDModuleInst_Backend_DeadError(i);
459 }
460  
461 static void inline_code_die (void *vo)
462 {
463 struct inline_code *o = vo;
464  
465 for (LinkedList0Node *ln = LinkedList0_GetFirst(&o->calls_list); ln; ln = LinkedList0Node_Next(ln)) {
466 struct inline_code_call *call = UPPER_OBJECT(ln, struct inline_code_call, ic_node);
467 ASSERT(call->ic == o)
468 call->ic = NULL;
469 }
470  
471 NCDModuleInst_Backend_Dead(o->i);
472 }
473  
474 static void inline_code_call_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
475 {
476 struct inline_code_call *o = vo;
477  
478 o->ic = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user);
479 LinkedList0_Prepend(&o->ic->calls_list, &o->ic_node);
480  
481 func_new_templ(vo, i, o->ic->template_name, params->args, inline_code_call_process_getspecialobj, inline_code_extra_free);
482 }
483  
484 static void inline_code_extra_free (struct instance *bo)
485 {
486 struct inline_code_call *o = (void *)bo;
487  
488 if (o->ic) {
489 LinkedList0_Remove(&o->ic->calls_list, &o->ic_node);
490 }
491 }
492  
493 static int inline_code_call_process_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object)
494 {
495 struct inline_code_call *o = UPPER_OBJECT(process, struct inline_code_call, base.process);
496  
497 if (name == NCD_STRING_CALLER) {
498 *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, caller_obj_func_getobj);
499 return 1;
500 }
501  
502 if (!o->ic) {
503 ModuleLog(o->base.i, BLOG_ERROR, "inline_code object is gone");
504 return 0;
505 }
506  
507 if (name == NCD_STRING_SCOPE) {
508 *out_object = NCDObject_Build(-1, o->ic, NCDObject_no_getvar, inline_code_scope_obj_getobj);
509 return 1;
510 }
511  
512 return NCDModuleInst_Backend_GetObj(o->ic->i, name, out_object);
513 }
514  
515 static int inline_code_scope_obj_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object)
516 {
517 struct inline_code *ic = NCDObject_DataPtr(obj);
518  
519 return NCDModuleInst_Backend_GetObj(ic->i, name, out_object);
520 }
521  
522 static struct NCDModule modules[] = {
523 {
524 .type = "call",
525 .func_new2 = func_new_call,
526 .func_die = func_die,
527 .func_clean = func_clean,
528 .func_getobj = func_getobj,
529 .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN,
530 .alloc_size = sizeof(struct instance)
531 }, {
532 .type = "call_with_caller_target",
533 .func_new2 = func_new_call_with_caller_target,
534 .func_die = func_die,
535 .func_clean = func_clean,
536 .func_getobj = func_getobj,
537 .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN,
538 .alloc_size = sizeof(struct instance_with_caller_target)
539 }, {
540 .type = "embcall",
541 .func_new2 = func_new_embcall,
542 .func_die = func_die,
543 .func_clean = func_clean,
544 .func_getobj = func_getobj,
545 .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN,
546 .alloc_size = sizeof(struct instance)
547 }, {
548 .type = "inline_code",
549 .func_new2 = inline_code_new,
550 .func_die = inline_code_die,
551 .alloc_size = sizeof(struct inline_code)
552 }, {
553 .type = "inline_code::call",
554 .func_new2 = inline_code_call_new,
555 .func_die = func_die,
556 .func_clean = func_clean,
557 .func_getobj = func_getobj,
558 .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN,
559 .alloc_size = sizeof(struct inline_code_call)
560 }, {
561 .type = NULL
562 }
563 };
564  
565 const struct NCDModuleGroup ncdmodule_call2 = {
566 .modules = modules
567 };