1 ###################################################
2 # EJS function wrapper generator
3 # Copyright jelmer@samba.org 2005
4 # Copyright Andrew Tridgell 2005
5 # released under the GNU GPL
7 package Parse::Pidl::Samba::EJS;
10 use Parse::Pidl::Typelist;
11 use Parse::Pidl::Util qw(has_property);
34 $tabs = substr($tabs, 0, -1);
37 # this should probably be in ndr.pm
38 sub GenerateStructEnv($)
43 foreach my $e (@{$x->{ELEMENTS}}) {
45 $env{$e->{NAME}} = "r->$e->{NAME}";
54 sub GenerateFunctionInEnv($)
59 foreach my $e (@{$fn->{ELEMENTS}}) {
60 if (grep (/in/, @{$e->{DIRECTION}})) {
61 $env{$e->{NAME}} = "r->in.$e->{NAME}";
68 sub GenerateFunctionOutEnv($)
73 foreach my $e (@{$fn->{ELEMENTS}}) {
74 if (grep (/out/, @{$e->{DIRECTION}})) {
75 $env{$e->{NAME}} = "r->out.$e->{NAME}";
76 } elsif (grep (/in/, @{$e->{DIRECTION}})) {
77 $env{$e->{NAME}} = "r->in.$e->{NAME}";
88 if ($var_name =~ /^\*(.*)$/) {
90 } elsif ($var_name =~ /^\&(.*)$/) {
91 return "&($var_name)";
101 if ($var_name =~ /^\&(.*)$/) {
108 #####################################################################
109 # work out is a parse function should be declared static or not
114 return "" if (has_property($fn, "public"));
118 ###########################
119 # pull a scalar element
120 sub EjsPullScalar($$$$$)
122 my ($e, $l, $var, $name, $env) = @_;
124 return if (has_property($e, "value"));
126 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
127 $var = get_pointer_to($var);
128 # have to handle strings specially :(
129 if ($e->{TYPE} eq "string" && $pl && $pl->{TYPE} eq "POINTER") {
130 $var = get_pointer_to($var);
132 pidl "NDR_CHECK(ejs_pull_$e->{TYPE}(ejs, v, $name, $var));";
135 ###########################
136 # pull a pointer element
137 sub EjsPullPointer($$$$$)
139 my ($e, $l, $var, $name, $env) = @_;
140 pidl "if (ejs_pull_null(ejs, v, $name)) {";
146 pidl "EJS_ALLOC(ejs, $var);";
147 $var = get_value_of($var);
148 EjsPullElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
153 ###########################
154 # pull a string element
155 sub EjsPullString($$$$$)
157 my ($e, $l, $var, $name, $env) = @_;
158 $var = get_pointer_to($var);
159 pidl "NDR_CHECK(ejs_pull_string(ejs, v, $name, $var));";
163 ###########################
164 # pull an array element
165 sub EjsPullArray($$$$$)
167 my ($e, $l, $var, $name, $env) = @_;
168 my $length = Parse::Pidl::Util::ParseExpr($l->{LENGTH_IS}, $env);
169 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
170 if ($pl && $pl->{TYPE} eq "POINTER") {
171 $var = get_pointer_to($var);
173 my $avar = $var . "[i]";
177 if (!$l->{IS_FIXED}) {
178 pidl "EJS_ALLOC_N(ejs, $var, $length);";
180 pidl "for (i=0;i<$length;i++) {";
182 pidl "char *id = talloc_asprintf(ejs, \"%s.%u\", $name, i);";
183 EjsPullElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $avar, "id", $env);
184 pidl "talloc_free(id);";
187 pidl "ejs_push_uint32(ejs, v, $name \".length\", &i);";
192 ###########################
193 # pull a switch element
194 sub EjsPullSwitch($$$$$)
196 my ($e, $l, $var, $name, $env) = @_;
197 my $switch_var = Parse::Pidl::Util::ParseExpr($l->{SWITCH_IS}, $env);
198 pidl "ejs_set_switch(ejs, $switch_var);";
199 EjsPullElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
202 ###########################
203 # pull a structure element
204 sub EjsPullElement($$$$$)
206 my ($e, $l, $var, $name, $env) = @_;
207 if (has_property($e, "charset")) {
208 EjsPullString($e, $l, $var, $name, $env);
209 } elsif ($l->{TYPE} eq "ARRAY") {
210 EjsPullArray($e, $l, $var, $name, $env);
211 } elsif ($l->{TYPE} eq "DATA") {
212 EjsPullScalar($e, $l, $var, $name, $env);
213 } elsif (($l->{TYPE} eq "POINTER")) {
214 EjsPullPointer($e, $l, $var, $name, $env);
215 } elsif (($l->{TYPE} eq "SWITCH")) {
216 EjsPullSwitch($e, $l, $var, $name, $env);
218 pidl "return ejs_panic(ejs, \"unhandled pull type $l->{TYPE}\");";
222 #############################################
223 # pull a structure/union element at top level
224 sub EjsPullElementTop($$)
228 my $l = $e->{LEVELS}[0];
229 my $var = Parse::Pidl::Util::ParseExpr($e->{NAME}, $env);
230 my $name = "\"$e->{NAME}\"";
231 EjsPullElement($e, $l, $var, $name, $env);
234 ###########################
236 sub EjsStructPull($$)
240 my $env = GenerateStructEnv($d);
242 pidl "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, struct $name *r)\n{";
244 pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, name));";
245 foreach my $e (@{$d->{ELEMENTS}}) {
246 EjsPullElementTop($e, $env);
248 pidl "return NT_STATUS_OK;";
253 ###########################
259 my $have_default = 0;
260 my $env = GenerateStructEnv($d);
262 pidl "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, union $name *r)\n{";
264 pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, name));";
265 pidl "switch (ejs->switch_var) {";
267 foreach my $e (@{$d->{ELEMENTS}}) {
268 if ($e->{CASE} eq "default") {
273 if ($e->{TYPE} ne "EMPTY") {
274 EjsPullElementTop($e, $env);
279 if (! $have_default) {
282 pidl "return ejs_panic(ejs, \"Bad switch value\");";
287 pidl "return NT_STATUS_OK;";
292 ##############################################
293 # put the enum elements in the constants array
294 sub EjsEnumConstant($)
298 foreach my $e (@{$d->{ELEMENTS}}) {
301 if ($el =~ /^(.*)=\s*(.*)\s*$/) {
305 $constants{$el} = $v;
310 ###########################
318 pidl "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, enum $name *r)\n{";
321 pidl "NDR_CHECK(ejs_pull_enum(ejs, v, name, &e));";
323 pidl "return NT_STATUS_OK;";
328 ###########################
330 sub EjsBitmapPull($$)
334 my $type_fn = $d->{BASE_TYPE};
335 my($type_decl) = Parse::Pidl::Typelist::mapType($d->{BASE_TYPE});
337 pidl "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, $type_decl *r)\n{";
339 pidl "return ejs_pull_$type_fn(ejs, v, name, r);";
345 ###########################
346 # generate a structure pull
347 sub EjsTypedefPull($)
350 return if (has_property($d, "noejs"));
351 if ($d->{DATA}->{TYPE} eq 'STRUCT') {
352 EjsStructPull($d->{NAME}, $d->{DATA});
353 } elsif ($d->{DATA}->{TYPE} eq 'UNION') {
354 EjsUnionPull($d->{NAME}, $d->{DATA});
355 } elsif ($d->{DATA}->{TYPE} eq 'ENUM') {
356 EjsEnumPull($d->{NAME}, $d->{DATA});
357 } elsif ($d->{DATA}->{TYPE} eq 'BITMAP') {
358 EjsBitmapPull($d->{NAME}, $d->{DATA});
360 warn "Unhandled pull typedef $d->{NAME} of type $d->{DATA}->{TYPE}";
364 #####################
365 # generate a function
366 sub EjsPullFunction($)
369 my $env = GenerateFunctionInEnv($d);
370 my $name = $d->{NAME};
372 pidl "\nstatic NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, struct $name *r)";
375 pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, \"input\"));";
377 foreach my $e (@{$d->{ELEMENTS}}) {
378 next unless (grep(/in/, @{$e->{DIRECTION}}));
379 EjsPullElementTop($e, $env);
382 pidl "return NT_STATUS_OK;";
388 ###########################
389 # push a scalar element
390 sub EjsPushScalar($$$$$)
392 my ($e, $l, $var, $name, $env) = @_;
393 # have to handle strings specially :(
394 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
395 if ($e->{TYPE} ne "string" || ($pl && $pl->{TYPE} eq "POINTER")) {
396 $var = get_pointer_to($var);
398 pidl "NDR_CHECK(ejs_push_$e->{TYPE}(ejs, v, $name, $var));";
401 ###########################
402 # push a string element
403 sub EjsPushString($$$$$)
405 my ($e, $l, $var, $name, $env) = @_;
406 pidl "NDR_CHECK(ejs_push_string(ejs, v, $name, $var));";
409 ###########################
410 # push a pointer element
411 sub EjsPushPointer($$$$$)
413 my ($e, $l, $var, $name, $env) = @_;
414 pidl "if (NULL == $var) {";
416 pidl "NDR_CHECK(ejs_push_null(ejs, v, $name));";
420 $var = get_value_of($var);
421 EjsPushElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
426 ###########################
427 # push a switch element
428 sub EjsPushSwitch($$$$$)
430 my ($e, $l, $var, $name, $env) = @_;
431 my $switch_var = Parse::Pidl::Util::ParseExpr($l->{SWITCH_IS}, $env);
432 pidl "ejs_set_switch(ejs, $switch_var);";
433 EjsPushElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
437 ###########################
438 # push an array element
439 sub EjsPushArray($$$$$)
441 my ($e, $l, $var, $name, $env) = @_;
442 my $length = Parse::Pidl::Util::ParseExpr($l->{LENGTH_IS}, $env);
443 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
444 if ($pl && $pl->{TYPE} eq "POINTER") {
445 $var = get_pointer_to($var);
447 my $avar = $var . "[i]";
451 pidl "for (i=0;i<$length;i++) {";
453 pidl "const char *id = talloc_asprintf(ejs, \"%s.%u\", $name, i);";
454 EjsPushElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $avar, "id", $env);
457 pidl "ejs_push_uint32(ejs, v, $name \".length\", &i);";
462 ################################
463 # push a structure/union element
464 sub EjsPushElement($$$$$)
466 my ($e, $l, $var, $name, $env) = @_;
467 if (has_property($e, "charset")) {
468 EjsPushString($e, $l, $var, $name, $env);
469 } elsif ($l->{TYPE} eq "ARRAY") {
470 EjsPushArray($e, $l, $var, $name, $env);
471 } elsif ($l->{TYPE} eq "DATA") {
472 EjsPushScalar($e, $l, $var, $name, $env);
473 } elsif (($l->{TYPE} eq "POINTER")) {
474 EjsPushPointer($e, $l, $var, $name, $env);
475 } elsif (($l->{TYPE} eq "SWITCH")) {
476 EjsPushSwitch($e, $l, $var, $name, $env);
478 pidl "return ejs_panic(ejs, \"unhandled push type $l->{TYPE}\");";
482 #############################################
483 # push a structure/union element at top level
484 sub EjsPushElementTop($$)
488 my $l = $e->{LEVELS}[0];
489 my $var = Parse::Pidl::Util::ParseExpr($e->{NAME}, $env);
490 my $name = "\"$e->{NAME}\"";
491 EjsPushElement($e, $l, $var, $name, $env);
494 ###########################
496 sub EjsStructPush($$)
500 my $env = GenerateStructEnv($d);
502 pidl "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const struct $name *r)\n{";
504 pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, name));";
505 foreach my $e (@{$d->{ELEMENTS}}) {
506 EjsPushElementTop($e, $env);
508 pidl "return NT_STATUS_OK;";
513 ###########################
519 my $have_default = 0;
520 my $env = GenerateStructEnv($d);
522 pidl "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const union $name *r)\n{";
524 pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, name));";
525 pidl "switch (ejs->switch_var) {";
527 foreach my $e (@{$d->{ELEMENTS}}) {
528 if ($e->{CASE} eq "default") {
533 if ($e->{TYPE} ne "EMPTY") {
534 EjsPushElementTop($e, $env);
539 if (! $have_default) {
542 pidl "return ejs_panic(ejs, \"Bad switch value\");";
547 pidl "return NT_STATUS_OK;";
552 ###########################
560 pidl "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const enum $name *r)\n{";
562 pidl "unsigned e = *r;";
563 pidl "NDR_CHECK(ejs_push_enum(ejs, v, name, &e));";
564 pidl "return NT_STATUS_OK;";
569 ###########################
571 sub EjsBitmapPush($$)
575 my $type_fn = $d->{BASE_TYPE};
576 my($type_decl) = Parse::Pidl::Typelist::mapType($d->{BASE_TYPE});
577 # put the bitmap elements in the constants array
578 foreach my $e (@{$d->{ELEMENTS}}) {
579 if ($e =~ /^(\w*)\s*(.*)\s*$/) {
582 $constants{$bname} = $v;
586 pidl "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const $type_decl *r)\n{";
588 pidl "return ejs_push_$type_fn(ejs, v, name, r);";
594 ###########################
595 # generate a structure push
596 sub EjsTypedefPush($)
599 return if (has_property($d, "noejs"));
600 if ($d->{DATA}->{TYPE} eq 'STRUCT') {
601 EjsStructPush($d->{NAME}, $d->{DATA});
602 } elsif ($d->{DATA}->{TYPE} eq 'UNION') {
603 EjsUnionPush($d->{NAME}, $d->{DATA});
604 } elsif ($d->{DATA}->{TYPE} eq 'ENUM') {
605 EjsEnumPush($d->{NAME}, $d->{DATA});
606 } elsif ($d->{DATA}->{TYPE} eq 'BITMAP') {
607 EjsBitmapPush($d->{NAME}, $d->{DATA});
609 warn "Unhandled push typedef $d->{NAME} of type $d->{DATA}->{TYPE}";
614 #####################
615 # generate a function
616 sub EjsPushFunction($)
619 my $env = GenerateFunctionOutEnv($d);
621 pidl "\nstatic NTSTATUS ejs_push_$d->{NAME}(struct ejs_rpc *ejs, struct MprVar *v, const struct $d->{NAME} *r)";
624 pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, \"output\"));";
626 foreach my $e (@{$d->{ELEMENTS}}) {
627 next unless (grep(/out/, @{$e->{DIRECTION}}));
628 EjsPushElementTop($e, $env);
631 pidl "return NT_STATUS_OK;";
637 #################################
638 # generate a ejs mapping function
643 my $name = $d->{NAME};
644 my $callnum = uc("DCERPC_$name");
645 my $table = "&dcerpc_table_$iface";
647 pidl "static int ejs_$name(int eid, int argc, struct MprVar **argv)";
650 pidl "return ejs_rpc_call(eid, argc, argv, $table, $callnum, (ejs_pull_function_t)ejs_pull_$name, (ejs_push_function_t)ejs_push_$name);";
660 $constants{$const->{NAME}} = $const->{VALUE};
663 #####################################################################
664 # parse the interface definitions
667 my($interface,$needed) = @_;
669 my $name = $interface->{NAME};
673 foreach my $d (@{$interface->{TYPEDEFS}}) {
674 ($needed->{"push_$d->{NAME}"}) && EjsTypedefPush($d);
675 ($needed->{"pull_$d->{NAME}"}) && EjsTypedefPull($d);
678 foreach my $d (@{$interface->{FUNCTIONS}}) {
679 next if not defined($d->{OPNUM});
683 EjsFunction($d, $name);
685 push (@fns, $d->{NAME});
688 foreach my $d (@{$interface->{CONSTS}}) {
692 pidl "static int ejs_$name\_init(int eid, int argc, struct MprVar **argv)";
695 pidl "struct MprVar obj = mprObject(\"$name\");";
697 pidl "mprSetCFunction(&obj, \"$_\", ejs_$_);";
699 foreach my $v (keys %constants) {
700 my $value = $constants{$v};
701 if (substr($value, 0, 1) eq "\"") {
702 pidl "mprSetVar(&obj, \"$v\", mprString($value));";
704 pidl "mprSetVar(&obj, \"$v\", mprCreateNumberVar($value));";
707 pidl "mpr_Return(eid, obj);";
712 pidl "NTSTATUS ejs_init_$name(void)";
715 pidl "return smbcalls_register_ejs(\"$name\_init\", ejs_$name\_init);";
720 #####################################################################
721 # parse a parsed IDL into a C header
727 $ejs_hdr =~ s/.h$/_ejs.h/;
730 /* EJS wrapper functions auto-generated by pidl */
731 #include \"includes.h\"
732 #include \"lib/appweb/ejs/ejs.h\"
733 #include \"scripting/ejs/ejsrpc.h\"
734 #include \"scripting/ejs/smbcalls.h\"
735 #include \"librpc/gen_ndr/ndr_misc_ejs.h\"
737 #include \"$ejs_hdr\"
743 foreach my $x (@{$ndr}) {
744 ($x->{TYPE} eq "INTERFACE") && NeededInterface($x, \%needed);
747 foreach my $x (@{$ndr}) {
748 ($x->{TYPE} eq "INTERFACE") && EjsInterface($x, \%needed);
755 sub NeededFunction($$)
757 my ($fn,$needed) = @_;
758 $needed->{"pull_$fn->{NAME}"} = 1;
759 $needed->{"push_$fn->{NAME}"} = 1;
760 foreach my $e (@{$fn->{ELEMENTS}}) {
761 if (grep (/in/, @{$e->{DIRECTION}})) {
762 $needed->{"pull_$e->{TYPE}"} = 1;
764 if (grep (/out/, @{$e->{DIRECTION}})) {
765 $needed->{"push_$e->{TYPE}"} = 1;
770 sub NeededTypedef($$)
772 my ($t,$needed) = @_;
773 if (Parse::Pidl::Util::has_property($t, "public")) {
774 $needed->{"pull_$t->{NAME}"} = not Parse::Pidl::Util::has_property($t, "noejs");
775 $needed->{"push_$t->{NAME}"} = not Parse::Pidl::Util::has_property($t, "noejs");
777 if ($t->{DATA}->{TYPE} ne "STRUCT" &&
778 $t->{DATA}->{TYPE} ne "UNION") {
781 for my $e (@{$t->{DATA}->{ELEMENTS}}) {
782 if ($needed->{"pull_$t->{NAME}"}) {
783 $needed->{"pull_$e->{TYPE}"} = 1;
785 if ($needed->{"push_$t->{NAME}"}) {
786 $needed->{"push_$e->{TYPE}"} = 1;
791 #####################################################################
792 # work out what parse functions are needed
793 sub NeededInterface($$)
795 my ($interface,$needed) = @_;
796 foreach my $d (@{$interface->{FUNCTIONS}}) {
797 NeededFunction($d, $needed);
799 foreach my $d (reverse @{$interface->{TYPEDEFS}}) {
800 NeededTypedef($d, $needed);