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::Samba4::EJS;
10 use Parse::Pidl::Typelist;
11 use Parse::Pidl::Util qw(has_property);
13 use vars qw($VERSION);
45 $tabs = substr($tabs, 0, -1);
48 # this should probably be in ndr.pm
49 sub GenerateStructEnv($)
54 foreach my $e (@{$x->{ELEMENTS}}) {
56 $env{$e->{NAME}} = "r->$e->{NAME}";
65 sub GenerateFunctionInEnv($)
70 foreach my $e (@{$fn->{ELEMENTS}}) {
71 if (grep (/in/, @{$e->{DIRECTION}})) {
72 $env{$e->{NAME}} = "r->in.$e->{NAME}";
79 sub GenerateFunctionOutEnv($)
84 foreach my $e (@{$fn->{ELEMENTS}}) {
85 if (grep (/out/, @{$e->{DIRECTION}})) {
86 $env{$e->{NAME}} = "r->out.$e->{NAME}";
87 } elsif (grep (/in/, @{$e->{DIRECTION}})) {
88 $env{$e->{NAME}} = "r->in.$e->{NAME}";
99 if ($var_name =~ /^\*(.*)$/) {
101 } elsif ($var_name =~ /^\&(.*)$/) {
102 return "&($var_name)";
110 my $var_name = shift;
112 if ($var_name =~ /^\&(.*)$/) {
119 #####################################################################
120 # work out is a parse function should be declared static or not
125 if (has_property($fn, "public")) {
133 ###########################
134 # pull a scalar element
135 sub EjsPullScalar($$$$$)
137 my ($e, $l, $var, $name, $env) = @_;
139 return if (has_property($e, "value"));
141 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
142 $var = get_pointer_to($var);
143 # have to handle strings specially :(
144 if ($e->{TYPE} eq "string" && $pl && $pl->{TYPE} eq "POINTER") {
145 $var = get_pointer_to($var);
147 pidl "NDR_CHECK(ejs_pull_$e->{TYPE}(ejs, v, $name, $var));";
150 ###########################
151 # pull a pointer element
152 sub EjsPullPointer($$$$$)
154 my ($e, $l, $var, $name, $env) = @_;
155 pidl "if (ejs_pull_null(ejs, v, $name)) {";
161 pidl "EJS_ALLOC(ejs, $var);";
162 $var = get_value_of($var);
163 EjsPullElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
168 ###########################
169 # pull a string element
170 sub EjsPullString($$$$$)
172 my ($e, $l, $var, $name, $env) = @_;
173 $var = get_pointer_to($var);
174 pidl "NDR_CHECK(ejs_pull_string(ejs, v, $name, $var));";
178 ###########################
179 # pull an array element
180 sub EjsPullArray($$$$$)
182 my ($e, $l, $var, $name, $env) = @_;
183 my $nl = Parse::Pidl::NDR::GetNextLevel($e, $l);
184 my $length = Parse::Pidl::Util::ParseExpr($l->{LENGTH_IS}, $env);
185 my $size = Parse::Pidl::Util::ParseExpr($l->{SIZE_IS}, $env);
186 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
187 if ($pl && $pl->{TYPE} eq "POINTER") {
188 $var = get_pointer_to($var);
190 # uint8 arrays are treated as data blobs
191 if ($nl->{TYPE} eq 'DATA' && $e->{TYPE} eq 'uint8') {
192 if (!$l->{IS_FIXED}) {
193 pidl "EJS_ALLOC_N(ejs, $var, $size);";
195 pidl "ejs_pull_array_uint8(ejs, v, $name, $var, $length);";
198 my $avar = $var . "[i]";
202 if (!$l->{IS_FIXED}) {
203 pidl "EJS_ALLOC_N(ejs, $var, $size);";
205 pidl "for (i=0;i<$length;i++) {";
207 pidl "char *id = talloc_asprintf(ejs, \"%s.%u\", $name, i);";
208 EjsPullElement($e, $nl, $avar, "id", $env);
209 pidl "talloc_free(id);";
212 pidl "ejs_push_uint32(ejs, v, $name \".length\", &i);";
217 ###########################
218 # pull a switch element
219 sub EjsPullSwitch($$$$$)
221 my ($e, $l, $var, $name, $env) = @_;
222 my $switch_var = Parse::Pidl::Util::ParseExpr($l->{SWITCH_IS}, $env);
223 pidl "ejs_set_switch(ejs, $switch_var);";
224 EjsPullElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
227 ###########################
228 # pull a structure element
229 sub EjsPullElement($$$$$)
231 my ($e, $l, $var, $name, $env) = @_;
232 if (has_property($e, "charset")) {
233 EjsPullString($e, $l, $var, $name, $env);
234 } elsif ($l->{TYPE} eq "ARRAY") {
235 EjsPullArray($e, $l, $var, $name, $env);
236 } elsif ($l->{TYPE} eq "DATA") {
237 EjsPullScalar($e, $l, $var, $name, $env);
238 } elsif (($l->{TYPE} eq "POINTER")) {
239 EjsPullPointer($e, $l, $var, $name, $env);
240 } elsif (($l->{TYPE} eq "SWITCH")) {
241 EjsPullSwitch($e, $l, $var, $name, $env);
243 pidl "return ejs_panic(ejs, \"unhandled pull type $l->{TYPE}\");";
247 #############################################
248 # pull a structure/union element at top level
249 sub EjsPullElementTop($$)
253 my $l = $e->{LEVELS}[0];
254 my $var = Parse::Pidl::Util::ParseExpr($e->{NAME}, $env);
255 my $name = "\"$e->{NAME}\"";
256 EjsPullElement($e, $l, $var, $name, $env);
259 ###########################
261 sub EjsStructPull($$)
265 my $env = GenerateStructEnv($d);
266 fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, struct $name *r)");
269 pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, name));";
270 foreach my $e (@{$d->{ELEMENTS}}) {
271 EjsPullElementTop($e, $env);
273 pidl "return NT_STATUS_OK;";
278 ###########################
284 my $have_default = 0;
285 my $env = GenerateStructEnv($d);
286 fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, union $name *r)");
289 pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, name));";
290 pidl "switch (ejs->switch_var) {";
292 foreach my $e (@{$d->{ELEMENTS}}) {
293 if ($e->{CASE} eq "default") {
298 if ($e->{TYPE} ne "EMPTY") {
299 EjsPullElementTop($e, $env);
304 if (! $have_default) {
307 pidl "return ejs_panic(ejs, \"Bad switch value\");";
312 pidl "return NT_STATUS_OK;";
317 ##############################################
318 # put the enum elements in the constants array
319 sub EjsEnumConstant($)
323 foreach my $e (@{$d->{ELEMENTS}}) {
326 if ($el =~ /^(.*)=\s*(.*)\s*$/) {
330 $constants{$el} = $v;
335 ###########################
342 fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, enum $name *r)");
346 pidl "NDR_CHECK(ejs_pull_enum(ejs, v, name, &e));";
348 pidl "return NT_STATUS_OK;";
353 ###########################
355 sub EjsBitmapPull($$)
359 my $type_fn = $d->{BASE_TYPE};
360 my($type_decl) = Parse::Pidl::Typelist::mapType($d->{BASE_TYPE});
361 fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, $type_decl *r)");
364 pidl "return ejs_pull_$type_fn(ejs, v, name, r);";
370 ###########################
371 # generate a structure pull
372 sub EjsTypedefPull($)
375 return if (has_property($d, "noejs"));
376 if ($d->{DATA}->{TYPE} eq 'STRUCT') {
377 EjsStructPull($d->{NAME}, $d->{DATA});
378 } elsif ($d->{DATA}->{TYPE} eq 'UNION') {
379 EjsUnionPull($d->{NAME}, $d->{DATA});
380 } elsif ($d->{DATA}->{TYPE} eq 'ENUM') {
381 EjsEnumPull($d->{NAME}, $d->{DATA});
382 } elsif ($d->{DATA}->{TYPE} eq 'BITMAP') {
383 EjsBitmapPull($d->{NAME}, $d->{DATA});
385 warn "Unhandled pull typedef $d->{NAME} of type $d->{DATA}->{TYPE}";
389 #####################
390 # generate a function
391 sub EjsPullFunction($)
394 my $env = GenerateFunctionInEnv($d);
395 my $name = $d->{NAME};
397 pidl "\nstatic NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, struct $name *r)";
400 pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, \"input\"));";
402 # we pull non-array elements before array elements as arrays
403 # may have length_is() or size_is() properties that depend
404 # on the non-array elements
405 foreach my $e (@{$d->{ELEMENTS}}) {
406 next unless (grep(/in/, @{$e->{DIRECTION}}));
407 next if (has_property($e, "length_is") ||
408 has_property($e, "size_is"));
409 EjsPullElementTop($e, $env);
412 foreach my $e (@{$d->{ELEMENTS}}) {
413 next unless (grep(/in/, @{$e->{DIRECTION}}));
414 next unless (has_property($e, "length_is") ||
415 has_property($e, "size_is"));
416 EjsPullElementTop($e, $env);
419 pidl "return NT_STATUS_OK;";
425 ###########################
426 # push a scalar element
427 sub EjsPushScalar($$$$$)
429 my ($e, $l, $var, $name, $env) = @_;
430 # have to handle strings specially :(
431 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
432 if ($e->{TYPE} ne "string" || ($pl && $pl->{TYPE} eq "POINTER")) {
433 $var = get_pointer_to($var);
435 pidl "NDR_CHECK(ejs_push_$e->{TYPE}(ejs, v, $name, $var));";
438 ###########################
439 # push a string element
440 sub EjsPushString($$$$$)
442 my ($e, $l, $var, $name, $env) = @_;
443 pidl "NDR_CHECK(ejs_push_string(ejs, v, $name, $var));";
446 ###########################
447 # push a pointer element
448 sub EjsPushPointer($$$$$)
450 my ($e, $l, $var, $name, $env) = @_;
451 pidl "if (NULL == $var) {";
453 pidl "NDR_CHECK(ejs_push_null(ejs, v, $name));";
457 $var = get_value_of($var);
458 EjsPushElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
463 ###########################
464 # push a switch element
465 sub EjsPushSwitch($$$$$)
467 my ($e, $l, $var, $name, $env) = @_;
468 my $switch_var = Parse::Pidl::Util::ParseExpr($l->{SWITCH_IS}, $env);
469 pidl "ejs_set_switch(ejs, $switch_var);";
470 EjsPushElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
474 ###########################
475 # push an array element
476 sub EjsPushArray($$$$$)
478 my ($e, $l, $var, $name, $env) = @_;
479 my $nl = Parse::Pidl::NDR::GetNextLevel($e, $l);
480 my $length = Parse::Pidl::Util::ParseExpr($l->{LENGTH_IS}, $env);
481 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
482 if ($pl && $pl->{TYPE} eq "POINTER") {
483 $var = get_pointer_to($var);
485 # uint8 arrays are treated as data blobs
486 if ($nl->{TYPE} eq 'DATA' && $e->{TYPE} eq 'uint8') {
487 pidl "ejs_push_array_uint8(ejs, v, $name, $var, $length);";
490 my $avar = $var . "[i]";
494 pidl "for (i=0;i<$length;i++) {";
496 pidl "const char *id = talloc_asprintf(ejs, \"%s.%u\", $name, i);";
497 EjsPushElement($e, $nl, $avar, "id", $env);
500 pidl "ejs_push_uint32(ejs, v, $name \".length\", &i);";
505 ################################
506 # push a structure/union element
507 sub EjsPushElement($$$$$)
509 my ($e, $l, $var, $name, $env) = @_;
510 if (has_property($e, "charset")) {
511 EjsPushString($e, $l, $var, $name, $env);
512 } elsif ($l->{TYPE} eq "ARRAY") {
513 EjsPushArray($e, $l, $var, $name, $env);
514 } elsif ($l->{TYPE} eq "DATA") {
515 EjsPushScalar($e, $l, $var, $name, $env);
516 } elsif (($l->{TYPE} eq "POINTER")) {
517 EjsPushPointer($e, $l, $var, $name, $env);
518 } elsif (($l->{TYPE} eq "SWITCH")) {
519 EjsPushSwitch($e, $l, $var, $name, $env);
521 pidl "return ejs_panic(ejs, \"unhandled push type $l->{TYPE}\");";
525 #############################################
526 # push a structure/union element at top level
527 sub EjsPushElementTop($$)
531 my $l = $e->{LEVELS}[0];
532 my $var = Parse::Pidl::Util::ParseExpr($e->{NAME}, $env);
533 my $name = "\"$e->{NAME}\"";
534 EjsPushElement($e, $l, $var, $name, $env);
537 ###########################
539 sub EjsStructPush($$)
543 my $env = GenerateStructEnv($d);
544 fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const struct $name *r)");
547 pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, name));";
548 foreach my $e (@{$d->{ELEMENTS}}) {
549 EjsPushElementTop($e, $env);
551 pidl "return NT_STATUS_OK;";
556 ###########################
562 my $have_default = 0;
563 my $env = GenerateStructEnv($d);
564 fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const union $name *r)");
567 pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, name));";
568 pidl "switch (ejs->switch_var) {";
570 foreach my $e (@{$d->{ELEMENTS}}) {
571 if ($e->{CASE} eq "default") {
576 if ($e->{TYPE} ne "EMPTY") {
577 EjsPushElementTop($e, $env);
582 if (! $have_default) {
585 pidl "return ejs_panic(ejs, \"Bad switch value\");";
590 pidl "return NT_STATUS_OK;";
595 ###########################
602 fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const enum $name *r)");
605 pidl "unsigned e = *r;";
606 pidl "NDR_CHECK(ejs_push_enum(ejs, v, name, &e));";
607 pidl "return NT_STATUS_OK;";
612 ###########################
614 sub EjsBitmapPush($$)
618 my $type_fn = $d->{BASE_TYPE};
619 my($type_decl) = Parse::Pidl::Typelist::mapType($d->{BASE_TYPE});
620 # put the bitmap elements in the constants array
621 foreach my $e (@{$d->{ELEMENTS}}) {
622 if ($e =~ /^(\w*)\s*(.*)\s*$/) {
625 $constants{$bname} = $v;
628 fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const $type_decl *r)");
631 pidl "return ejs_push_$type_fn(ejs, v, name, r);";
637 ###########################
638 # generate a structure push
639 sub EjsTypedefPush($)
642 return if (has_property($d, "noejs"));
644 if ($d->{DATA}->{TYPE} eq 'STRUCT') {
645 EjsStructPush($d->{NAME}, $d->{DATA});
646 } elsif ($d->{DATA}->{TYPE} eq 'UNION') {
647 EjsUnionPush($d->{NAME}, $d->{DATA});
648 } elsif ($d->{DATA}->{TYPE} eq 'ENUM') {
649 EjsEnumPush($d->{NAME}, $d->{DATA});
650 } elsif ($d->{DATA}->{TYPE} eq 'BITMAP') {
651 EjsBitmapPush($d->{NAME}, $d->{DATA});
653 warn "Unhandled push typedef $d->{NAME} of type $d->{DATA}->{TYPE}";
658 #####################
659 # generate a function
660 sub EjsPushFunction($)
663 my $env = GenerateFunctionOutEnv($d);
665 pidl "\nstatic NTSTATUS ejs_push_$d->{NAME}(struct ejs_rpc *ejs, struct MprVar *v, const struct $d->{NAME} *r)";
668 pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, \"output\"));";
670 foreach my $e (@{$d->{ELEMENTS}}) {
671 next unless (grep(/out/, @{$e->{DIRECTION}}));
672 EjsPushElementTop($e, $env);
675 if ($d->{RETURN_TYPE}) {
676 my $t = $d->{RETURN_TYPE};
677 pidl "NDR_CHECK(ejs_push_$t(ejs, v, \"result\", &r->out.result));";
680 pidl "return NT_STATUS_OK;";
686 #################################
687 # generate a ejs mapping function
692 my $name = $d->{NAME};
693 my $callnum = uc("DCERPC_$name");
694 my $table = "&dcerpc_table_$iface";
696 pidl "static int ejs_$name(int eid, int argc, struct MprVar **argv)";
699 pidl "return ejs_rpc_call(eid, argc, argv, $table, $callnum, (ejs_pull_function_t)ejs_pull_$name, (ejs_push_function_t)ejs_push_$name);";
709 $constants{$const->{NAME}} = $const->{VALUE};
712 #####################################################################
713 # parse the interface definitions
716 my($interface,$needed) = @_;
718 my $name = $interface->{NAME};
722 pidl_hdr "#ifndef _HEADER_EJS_$interface->{NAME}\n";
723 pidl_hdr "#define _HEADER_EJS_$interface->{NAME}\n\n";
725 if (has_property($interface, "depends")) {
726 foreach (split / /, $interface->{PROPERTIES}->{depends}) {
727 pidl_hdr "#include \"librpc/gen_ndr/ndr_$_\_ejs\.h\"\n";
733 foreach my $d (@{$interface->{TYPES}}) {
734 ($needed->{"push_$d->{NAME}"}) && EjsTypedefPush($d);
735 ($needed->{"pull_$d->{NAME}"}) && EjsTypedefPull($d);
738 foreach my $d (@{$interface->{FUNCTIONS}}) {
739 next if not defined($d->{OPNUM});
740 next if Parse::Pidl::Util::has_property($d, "noejs");
744 EjsFunction($d, $name);
746 push (@fns, $d->{NAME});
749 foreach my $d (@{$interface->{CONSTS}}) {
753 pidl "static int ejs_$name\_init(int eid, int argc, struct MprVar **argv)";
756 pidl "struct MprVar *obj = mprInitObject(eid, \"$name\", argc, argv);";
758 pidl "mprSetCFunction(obj, \"$_\", ejs_$_);";
760 foreach my $v (keys %constants) {
761 my $value = $constants{$v};
762 if (substr($value, 0, 1) eq "\"") {
763 pidl "mprSetVar(obj, \"$v\", mprString($value));";
765 pidl "mprSetVar(obj, \"$v\", mprCreateNumberVar($value));";
768 pidl "return ejs_rpc_init(obj, \"$name\");";
772 pidl "NTSTATUS ejs_init_$name(void)";
775 pidl "ejsDefineCFunction(-1, \"$name\_init\", ejs_$name\_init, NULL, MPR_VAR_SCRIPT_HANDLE);";
776 pidl "return NT_STATUS_OK;";
781 pidl_hdr "#endif /* _HEADER_EJS_$interface->{NAME} */\n";
784 #####################################################################
785 # parse a parsed IDL into a C header
791 $ejs_hdr =~ s/.h$/_ejs.h/;
795 pidl_hdr "/* header auto-generated by pidl */\n\n";
798 /* EJS wrapper functions auto-generated by pidl */
799 #include \"includes.h\"
800 #include \"lib/appweb/ejs/ejs.h\"
801 #include \"scripting/ejs/ejsrpc.h\"
802 #include \"scripting/ejs/smbcalls.h\"
803 #include \"librpc/gen_ndr/ndr_misc_ejs.h\"
805 #include \"$ejs_hdr\"
811 foreach my $x (@{$ndr}) {
812 ($x->{TYPE} eq "INTERFACE") && NeededInterface($x, \%needed);
815 foreach my $x (@{$ndr}) {
816 ($x->{TYPE} eq "INTERFACE") && EjsInterface($x, \%needed);
819 return ($res_hdr, $res);
822 sub NeededFunction($$)
824 my ($fn,$needed) = @_;
825 $needed->{"pull_$fn->{NAME}"} = 1;
826 $needed->{"push_$fn->{NAME}"} = 1;
827 foreach my $e (@{$fn->{ELEMENTS}}) {
828 if (grep (/in/, @{$e->{DIRECTION}})) {
829 $needed->{"pull_$e->{TYPE}"} = 1;
831 if (grep (/out/, @{$e->{DIRECTION}})) {
832 $needed->{"push_$e->{TYPE}"} = 1;
837 sub NeededTypedef($$)
839 my ($t,$needed) = @_;
840 if (Parse::Pidl::Util::has_property($t, "public")) {
841 $needed->{"pull_$t->{NAME}"} = not Parse::Pidl::Util::has_property($t, "noejs");
842 $needed->{"push_$t->{NAME}"} = not Parse::Pidl::Util::has_property($t, "noejs");
844 if ($t->{DATA}->{TYPE} ne "STRUCT" &&
845 $t->{DATA}->{TYPE} ne "UNION") {
848 for my $e (@{$t->{DATA}->{ELEMENTS}}) {
849 if ($needed->{"pull_$t->{NAME}"}) {
850 $needed->{"pull_$e->{TYPE}"} = 1;
852 if ($needed->{"push_$t->{NAME}"}) {
853 $needed->{"push_$e->{TYPE}"} = 1;
858 #####################################################################
859 # work out what parse functions are needed
860 sub NeededInterface($$)
862 my ($interface,$needed) = @_;
863 foreach my $d (@{$interface->{FUNCTIONS}}) {
864 NeededFunction($d, $needed);
866 foreach my $d (reverse @{$interface->{TYPES}}) {
867 NeededTypedef($d, $needed);