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")) {
127 pidl "_PUBLIC_ $decl";
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)) {";
157 if ($l->{POINTER_TYPE} eq "ref") {
158 pidl "return NT_STATUS_INVALID_PARAMETER_MIX;";
165 pidl "EJS_ALLOC(ejs, $var);";
166 $var = get_value_of($var);
167 EjsPullElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
172 ###########################
173 # pull a string element
174 sub EjsPullString($$$$$)
176 my ($e, $l, $var, $name, $env) = @_;
177 $var = get_pointer_to($var);
178 pidl "NDR_CHECK(ejs_pull_string(ejs, v, $name, $var));";
182 ###########################
183 # pull an array element
184 sub EjsPullArray($$$$$)
186 my ($e, $l, $var, $name, $env) = @_;
187 my $nl = Parse::Pidl::NDR::GetNextLevel($e, $l);
188 my $length = Parse::Pidl::Util::ParseExpr($l->{LENGTH_IS}, $env);
189 my $size = Parse::Pidl::Util::ParseExpr($l->{SIZE_IS}, $env);
190 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
191 if ($pl && $pl->{TYPE} eq "POINTER") {
192 $var = get_pointer_to($var);
194 # uint8 arrays are treated as data blobs
195 if ($nl->{TYPE} eq 'DATA' && $e->{TYPE} eq 'uint8') {
196 if (!$l->{IS_FIXED}) {
197 pidl "EJS_ALLOC_N(ejs, $var, $size);";
199 pidl "ejs_pull_array_uint8(ejs, v, $name, $var, $length);";
202 my $avar = $var . "[i]";
206 if (!$l->{IS_FIXED}) {
207 pidl "EJS_ALLOC_N(ejs, $var, $size);";
209 pidl "for (i=0;i<$length;i++) {";
211 pidl "char *id = talloc_asprintf(ejs, \"%s.%u\", $name, i);";
212 EjsPullElement($e, $nl, $avar, "id", $env);
213 pidl "talloc_free(id);";
216 pidl "ejs_push_uint32(ejs, v, $name \".length\", &i);";
221 ###########################
222 # pull a switch element
223 sub EjsPullSwitch($$$$$)
225 my ($e, $l, $var, $name, $env) = @_;
226 my $switch_var = Parse::Pidl::Util::ParseExpr($l->{SWITCH_IS}, $env);
227 pidl "ejs_set_switch(ejs, $switch_var);";
228 EjsPullElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
231 ###########################
232 # pull a structure element
233 sub EjsPullElement($$$$$)
235 my ($e, $l, $var, $name, $env) = @_;
236 if (has_property($e, "charset")) {
237 EjsPullString($e, $l, $var, $name, $env);
238 } elsif ($l->{TYPE} eq "ARRAY") {
239 EjsPullArray($e, $l, $var, $name, $env);
240 } elsif ($l->{TYPE} eq "DATA") {
241 EjsPullScalar($e, $l, $var, $name, $env);
242 } elsif (($l->{TYPE} eq "POINTER")) {
243 EjsPullPointer($e, $l, $var, $name, $env);
244 } elsif (($l->{TYPE} eq "SWITCH")) {
245 EjsPullSwitch($e, $l, $var, $name, $env);
247 pidl "return ejs_panic(ejs, \"unhandled pull type $l->{TYPE}\");";
251 #############################################
252 # pull a structure/union element at top level
253 sub EjsPullElementTop($$)
257 my $l = $e->{LEVELS}[0];
258 my $var = Parse::Pidl::Util::ParseExpr($e->{NAME}, $env);
259 my $name = "\"$e->{NAME}\"";
260 EjsPullElement($e, $l, $var, $name, $env);
263 ###########################
265 sub EjsStructPull($$)
269 my $env = GenerateStructEnv($d);
270 fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, struct $name *r)");
273 pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, name));";
274 foreach my $e (@{$d->{ELEMENTS}}) {
275 EjsPullElementTop($e, $env);
277 pidl "return NT_STATUS_OK;";
282 ###########################
288 my $have_default = 0;
289 my $env = GenerateStructEnv($d);
290 fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, union $name *r)");
293 pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, name));";
294 pidl "switch (ejs->switch_var) {";
296 foreach my $e (@{$d->{ELEMENTS}}) {
297 if ($e->{CASE} eq "default") {
302 if ($e->{TYPE} ne "EMPTY") {
303 EjsPullElementTop($e, $env);
308 if (! $have_default) {
311 pidl "return ejs_panic(ejs, \"Bad switch value\");";
316 pidl "return NT_STATUS_OK;";
321 ##############################################
322 # put the enum elements in the constants array
323 sub EjsEnumConstant($)
327 foreach my $e (@{$d->{ELEMENTS}}) {
330 if ($el =~ /^(.*)=\s*(.*)\s*$/) {
334 $constants{$el} = $v;
339 ###########################
346 fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, enum $name *r)");
350 pidl "NDR_CHECK(ejs_pull_enum(ejs, v, name, &e));";
352 pidl "return NT_STATUS_OK;";
357 ###########################
359 sub EjsBitmapPull($$)
363 my $type_fn = $d->{BASE_TYPE};
364 my($type_decl) = Parse::Pidl::Typelist::mapType($d->{BASE_TYPE});
365 fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, $type_decl *r)");
368 pidl "return ejs_pull_$type_fn(ejs, v, name, r);";
374 ###########################
375 # generate a structure pull
376 sub EjsTypedefPull($)
379 return if (has_property($d, "noejs"));
380 if ($d->{DATA}->{TYPE} eq 'STRUCT') {
381 EjsStructPull($d->{NAME}, $d->{DATA});
382 } elsif ($d->{DATA}->{TYPE} eq 'UNION') {
383 EjsUnionPull($d->{NAME}, $d->{DATA});
384 } elsif ($d->{DATA}->{TYPE} eq 'ENUM') {
385 EjsEnumPull($d->{NAME}, $d->{DATA});
386 } elsif ($d->{DATA}->{TYPE} eq 'BITMAP') {
387 EjsBitmapPull($d->{NAME}, $d->{DATA});
389 warn "Unhandled pull typedef $d->{NAME} of type $d->{DATA}->{TYPE}";
393 #####################
394 # generate a function
395 sub EjsPullFunction($)
398 my $env = GenerateFunctionInEnv($d);
399 my $name = $d->{NAME};
401 pidl "\nstatic NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, struct $name *r)";
404 pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, \"input\"));";
406 # we pull non-array elements before array elements as arrays
407 # may have length_is() or size_is() properties that depend
408 # on the non-array elements
409 foreach my $e (@{$d->{ELEMENTS}}) {
410 next unless (grep(/in/, @{$e->{DIRECTION}}));
411 next if (has_property($e, "length_is") ||
412 has_property($e, "size_is"));
413 EjsPullElementTop($e, $env);
416 foreach my $e (@{$d->{ELEMENTS}}) {
417 next unless (grep(/in/, @{$e->{DIRECTION}}));
418 next unless (has_property($e, "length_is") ||
419 has_property($e, "size_is"));
420 EjsPullElementTop($e, $env);
423 pidl "return NT_STATUS_OK;";
429 ###########################
430 # push a scalar element
431 sub EjsPushScalar($$$$$)
433 my ($e, $l, $var, $name, $env) = @_;
434 # have to handle strings specially :(
435 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
436 if ($e->{TYPE} ne "string" || ($pl && $pl->{TYPE} eq "POINTER")) {
437 $var = get_pointer_to($var);
439 pidl "NDR_CHECK(ejs_push_$e->{TYPE}(ejs, v, $name, $var));";
442 ###########################
443 # push a string element
444 sub EjsPushString($$$$$)
446 my ($e, $l, $var, $name, $env) = @_;
447 pidl "NDR_CHECK(ejs_push_string(ejs, v, $name, $var));";
450 ###########################
451 # push a pointer element
452 sub EjsPushPointer($$$$$)
454 my ($e, $l, $var, $name, $env) = @_;
455 pidl "if (NULL == $var) {";
457 if ($l->{POINTER_TYPE} eq "ref") {
458 pidl "return NT_STATUS_INVALID_PARAMETER_MIX;";
460 pidl "NDR_CHECK(ejs_push_null(ejs, v, $name));";
465 $var = get_value_of($var);
466 EjsPushElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
471 ###########################
472 # push a switch element
473 sub EjsPushSwitch($$$$$)
475 my ($e, $l, $var, $name, $env) = @_;
476 my $switch_var = Parse::Pidl::Util::ParseExpr($l->{SWITCH_IS}, $env);
477 pidl "ejs_set_switch(ejs, $switch_var);";
478 EjsPushElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
482 ###########################
483 # push an array element
484 sub EjsPushArray($$$$$)
486 my ($e, $l, $var, $name, $env) = @_;
487 my $nl = Parse::Pidl::NDR::GetNextLevel($e, $l);
488 my $length = Parse::Pidl::Util::ParseExpr($l->{LENGTH_IS}, $env);
489 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
490 if ($pl && $pl->{TYPE} eq "POINTER") {
491 $var = get_pointer_to($var);
493 # uint8 arrays are treated as data blobs
494 if ($nl->{TYPE} eq 'DATA' && $e->{TYPE} eq 'uint8') {
495 pidl "ejs_push_array_uint8(ejs, v, $name, $var, $length);";
498 my $avar = $var . "[i]";
502 pidl "for (i=0;i<$length;i++) {";
504 pidl "const char *id = talloc_asprintf(ejs, \"%s.%u\", $name, i);";
505 EjsPushElement($e, $nl, $avar, "id", $env);
508 pidl "ejs_push_uint32(ejs, v, $name \".length\", &i);";
513 ################################
514 # push a structure/union element
515 sub EjsPushElement($$$$$)
517 my ($e, $l, $var, $name, $env) = @_;
518 if (has_property($e, "charset")) {
519 EjsPushString($e, $l, $var, $name, $env);
520 } elsif ($l->{TYPE} eq "ARRAY") {
521 EjsPushArray($e, $l, $var, $name, $env);
522 } elsif ($l->{TYPE} eq "DATA") {
523 EjsPushScalar($e, $l, $var, $name, $env);
524 } elsif (($l->{TYPE} eq "POINTER")) {
525 EjsPushPointer($e, $l, $var, $name, $env);
526 } elsif (($l->{TYPE} eq "SWITCH")) {
527 EjsPushSwitch($e, $l, $var, $name, $env);
529 pidl "return ejs_panic(ejs, \"unhandled push type $l->{TYPE}\");";
533 #############################################
534 # push a structure/union element at top level
535 sub EjsPushElementTop($$)
539 my $l = $e->{LEVELS}[0];
540 my $var = Parse::Pidl::Util::ParseExpr($e->{NAME}, $env);
541 my $name = "\"$e->{NAME}\"";
542 EjsPushElement($e, $l, $var, $name, $env);
545 ###########################
547 sub EjsStructPush($$)
551 my $env = GenerateStructEnv($d);
552 fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const struct $name *r)");
555 pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, name));";
556 foreach my $e (@{$d->{ELEMENTS}}) {
557 EjsPushElementTop($e, $env);
559 pidl "return NT_STATUS_OK;";
564 ###########################
570 my $have_default = 0;
571 my $env = GenerateStructEnv($d);
572 fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const union $name *r)");
575 pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, name));";
576 pidl "switch (ejs->switch_var) {";
578 foreach my $e (@{$d->{ELEMENTS}}) {
579 if ($e->{CASE} eq "default") {
584 if ($e->{TYPE} ne "EMPTY") {
585 EjsPushElementTop($e, $env);
590 if (! $have_default) {
593 pidl "return ejs_panic(ejs, \"Bad switch value\");";
598 pidl "return NT_STATUS_OK;";
603 ###########################
610 fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const enum $name *r)");
613 pidl "unsigned e = *r;";
614 pidl "NDR_CHECK(ejs_push_enum(ejs, v, name, &e));";
615 pidl "return NT_STATUS_OK;";
620 ###########################
622 sub EjsBitmapPush($$)
626 my $type_fn = $d->{BASE_TYPE};
627 my($type_decl) = Parse::Pidl::Typelist::mapType($d->{BASE_TYPE});
628 # put the bitmap elements in the constants array
629 foreach my $e (@{$d->{ELEMENTS}}) {
630 if ($e =~ /^(\w*)\s*(.*)\s*$/) {
633 $constants{$bname} = $v;
636 fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const $type_decl *r)");
639 pidl "return ejs_push_$type_fn(ejs, v, name, r);";
645 ###########################
646 # generate a structure push
647 sub EjsTypedefPush($)
650 return if (has_property($d, "noejs"));
652 if ($d->{DATA}->{TYPE} eq 'STRUCT') {
653 EjsStructPush($d->{NAME}, $d->{DATA});
654 } elsif ($d->{DATA}->{TYPE} eq 'UNION') {
655 EjsUnionPush($d->{NAME}, $d->{DATA});
656 } elsif ($d->{DATA}->{TYPE} eq 'ENUM') {
657 EjsEnumPush($d->{NAME}, $d->{DATA});
658 } elsif ($d->{DATA}->{TYPE} eq 'BITMAP') {
659 EjsBitmapPush($d->{NAME}, $d->{DATA});
661 warn "Unhandled push typedef $d->{NAME} of type $d->{DATA}->{TYPE}";
666 #####################
667 # generate a function
668 sub EjsPushFunction($)
671 my $env = GenerateFunctionOutEnv($d);
673 pidl "\nstatic NTSTATUS ejs_push_$d->{NAME}(struct ejs_rpc *ejs, struct MprVar *v, const struct $d->{NAME} *r)";
676 pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, \"output\"));";
678 foreach my $e (@{$d->{ELEMENTS}}) {
679 next unless (grep(/out/, @{$e->{DIRECTION}}));
680 EjsPushElementTop($e, $env);
683 if ($d->{RETURN_TYPE}) {
684 my $t = $d->{RETURN_TYPE};
685 pidl "NDR_CHECK(ejs_push_$t(ejs, v, \"result\", &r->out.result));";
688 pidl "return NT_STATUS_OK;";
694 #################################
695 # generate a ejs mapping function
700 my $name = $d->{NAME};
701 my $callnum = uc("DCERPC_$name");
702 my $table = "&dcerpc_table_$iface";
704 pidl "static int ejs_$name(int eid, int argc, struct MprVar **argv)";
707 pidl "return ejs_rpc_call(eid, argc, argv, $table, $callnum, (ejs_pull_function_t)ejs_pull_$name, (ejs_push_function_t)ejs_push_$name);";
717 $constants{$const->{NAME}} = $const->{VALUE};
720 #####################################################################
721 # parse the interface definitions
724 my($interface,$needed) = @_;
726 my $name = $interface->{NAME};
730 pidl_hdr "#ifndef _HEADER_EJS_$interface->{NAME}\n";
731 pidl_hdr "#define _HEADER_EJS_$interface->{NAME}\n\n";
733 if (has_property($interface, "depends")) {
734 foreach (split / /, $interface->{PROPERTIES}->{depends}) {
735 pidl_hdr "#include \"librpc/gen_ndr/ndr_$_\_ejs\.h\"\n";
741 foreach my $d (@{$interface->{TYPES}}) {
742 ($needed->{"push_$d->{NAME}"}) && EjsTypedefPush($d);
743 ($needed->{"pull_$d->{NAME}"}) && EjsTypedefPull($d);
746 foreach my $d (@{$interface->{FUNCTIONS}}) {
747 next if not defined($d->{OPNUM});
748 next if Parse::Pidl::Util::has_property($d, "noejs");
752 EjsFunction($d, $name);
754 push (@fns, $d->{NAME});
757 foreach my $d (@{$interface->{CONSTS}}) {
761 pidl "static int ejs_$name\_init(int eid, int argc, struct MprVar **argv)";
764 pidl "struct MprVar *obj = mprInitObject(eid, \"$name\", argc, argv);";
766 pidl "mprSetCFunction(obj, \"$_\", ejs_$_);";
768 foreach my $v (keys %constants) {
769 my $value = $constants{$v};
770 if (substr($value, 0, 1) eq "\"") {
771 pidl "mprSetVar(obj, \"$v\", mprString($value));";
773 pidl "mprSetVar(obj, \"$v\", mprCreateNumberVar($value));";
776 pidl "return ejs_rpc_init(obj, \"$name\");";
780 pidl "NTSTATUS ejs_init_$name(void)";
783 pidl "ejsDefineCFunction(-1, \"$name\_init\", ejs_$name\_init, NULL, MPR_VAR_SCRIPT_HANDLE);";
784 pidl "return NT_STATUS_OK;";
789 pidl_hdr "#endif /* _HEADER_EJS_$interface->{NAME} */\n";
792 #####################################################################
793 # parse a parsed IDL into a C header
799 $ejs_hdr =~ s/.h$/_ejs.h/;
803 pidl_hdr "/* header auto-generated by pidl */\n\n";
806 /* EJS wrapper functions auto-generated by pidl */
807 #include \"includes.h\"
808 #include \"librpc/rpc/dcerpc.h\"
809 #include \"lib/appweb/ejs/ejs.h\"
810 #include \"scripting/ejs/ejsrpc.h\"
811 #include \"scripting/ejs/smbcalls.h\"
812 #include \"librpc/gen_ndr/ndr_misc_ejs.h\"
814 #include \"$ejs_hdr\"
820 foreach my $x (@{$ndr}) {
821 ($x->{TYPE} eq "INTERFACE") && NeededInterface($x, \%needed);
824 foreach my $x (@{$ndr}) {
825 ($x->{TYPE} eq "INTERFACE") && EjsInterface($x, \%needed);
828 return ($res_hdr, $res);
831 sub NeededFunction($$)
833 my ($fn,$needed) = @_;
834 $needed->{"pull_$fn->{NAME}"} = 1;
835 $needed->{"push_$fn->{NAME}"} = 1;
836 foreach my $e (@{$fn->{ELEMENTS}}) {
837 if (grep (/in/, @{$e->{DIRECTION}})) {
838 $needed->{"pull_$e->{TYPE}"} = 1;
840 if (grep (/out/, @{$e->{DIRECTION}})) {
841 $needed->{"push_$e->{TYPE}"} = 1;
846 sub NeededTypedef($$)
848 my ($t,$needed) = @_;
849 if (Parse::Pidl::Util::has_property($t, "public")) {
850 $needed->{"pull_$t->{NAME}"} = not Parse::Pidl::Util::has_property($t, "noejs");
851 $needed->{"push_$t->{NAME}"} = not Parse::Pidl::Util::has_property($t, "noejs");
853 if ($t->{DATA}->{TYPE} ne "STRUCT" &&
854 $t->{DATA}->{TYPE} ne "UNION") {
857 for my $e (@{$t->{DATA}->{ELEMENTS}}) {
858 if ($needed->{"pull_$t->{NAME}"}) {
859 $needed->{"pull_$e->{TYPE}"} = 1;
861 if ($needed->{"push_$t->{NAME}"}) {
862 $needed->{"push_$e->{TYPE}"} = 1;
867 #####################################################################
868 # work out what parse functions are needed
869 sub NeededInterface($$)
871 my ($interface,$needed) = @_;
872 foreach my $d (@{$interface->{FUNCTIONS}}) {
873 NeededFunction($d, $needed);
875 foreach my $d (reverse @{$interface->{TYPES}}) {
876 NeededTypedef($d, $needed);