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 # check that a variable we get from ParseExpr isn't a null pointer
121 sub check_null_pointer($)
124 if ($size =~ /^\*/) {
125 my $size2 = substr($size, 1);
126 pidl "if ($size2 == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;";
131 #####################################################################
132 # work out is a parse function should be declared static or not
137 if (has_property($fn, "public")) {
139 pidl "_PUBLIC_ $decl";
145 ###########################
146 # pull a scalar element
147 sub EjsPullScalar($$$$$)
149 my ($e, $l, $var, $name, $env) = @_;
151 return if (has_property($e, "value"));
153 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
154 $var = get_pointer_to($var);
155 # have to handle strings specially :(
156 if (Parse::Pidl::Typelist::scalar_is_reference($e->{TYPE})
157 and (defined($pl) and $pl->{TYPE} eq "POINTER")) {
158 $var = get_pointer_to($var);
160 pidl "NDR_CHECK(ejs_pull_$e->{TYPE}(ejs, v, $name, $var));";
163 ###########################
164 # pull a pointer element
165 sub EjsPullPointer($$$$$)
167 my ($e, $l, $var, $name, $env) = @_;
168 pidl "if (ejs_pull_null(ejs, v, $name)) {";
170 if ($l->{POINTER_TYPE} eq "ref") {
171 pidl "return NT_STATUS_INVALID_PARAMETER_MIX;";
178 pidl "EJS_ALLOC(ejs, $var);";
179 $var = get_value_of($var);
180 EjsPullElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
185 ###########################
186 # pull a string element
187 sub EjsPullString($$$$$)
189 my ($e, $l, $var, $name, $env) = @_;
190 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
191 $var = get_pointer_to($var);
192 if (defined($pl) and $pl->{TYPE} eq "POINTER") {
193 $var = get_pointer_to($var);
195 pidl "NDR_CHECK(ejs_pull_string(ejs, v, $name, $var));";
199 ###########################
200 # pull an array element
201 sub EjsPullArray($$$$$)
203 my ($e, $l, $var, $name, $env) = @_;
204 my $nl = Parse::Pidl::NDR::GetNextLevel($e, $l);
205 my $length = Parse::Pidl::Util::ParseExpr($l->{LENGTH_IS}, $env, $e);
206 my $size = Parse::Pidl::Util::ParseExpr($l->{SIZE_IS}, $env, $e);
207 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
208 if ($pl && $pl->{TYPE} eq "POINTER") {
209 $var = get_pointer_to($var);
211 # uint8 arrays are treated as data blobs
212 if ($nl->{TYPE} eq 'DATA' && $e->{TYPE} eq 'uint8') {
213 if (!$l->{IS_FIXED}) {
214 check_null_pointer($size);
215 pidl "EJS_ALLOC_N(ejs, $var, $size);";
217 check_null_pointer($length);
218 pidl "ejs_pull_array_uint8(ejs, v, $name, $var, $length);";
221 my $avar = $var . "[i]";
225 if (!$l->{IS_FIXED}) {
226 pidl "EJS_ALLOC_N(ejs, $var, $size);";
228 pidl "for (i=0;i<$length;i++) {";
230 pidl "char *id = talloc_asprintf(ejs, \"%s.%u\", $name, i);";
231 EjsPullElement($e, $nl, $avar, "id", $env);
232 pidl "talloc_free(id);";
235 pidl "ejs_push_uint32(ejs, v, $name \".length\", &i);";
240 ###########################
241 # pull a switch element
242 sub EjsPullSwitch($$$$$)
244 my ($e, $l, $var, $name, $env) = @_;
245 my $switch_var = Parse::Pidl::Util::ParseExpr($l->{SWITCH_IS}, $env, $e);
246 pidl "ejs_set_switch(ejs, $switch_var);";
247 EjsPullElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
250 ###########################
251 # pull a structure element
252 sub EjsPullElement($$$$$)
254 my ($e, $l, $var, $name, $env) = @_;
255 if (($l->{TYPE} eq "POINTER")) {
256 EjsPullPointer($e, $l, $var, $name, $env);
257 } elsif (has_property($e, "charset")) {
258 EjsPullString($e, $l, $var, $name, $env);
259 } elsif ($l->{TYPE} eq "ARRAY") {
260 EjsPullArray($e, $l, $var, $name, $env);
261 } elsif ($l->{TYPE} eq "DATA") {
262 EjsPullScalar($e, $l, $var, $name, $env);
263 } elsif (($l->{TYPE} eq "SWITCH")) {
264 EjsPullSwitch($e, $l, $var, $name, $env);
266 pidl "return ejs_panic(ejs, \"unhandled pull type $l->{TYPE}\");";
270 #############################################
271 # pull a structure/union element at top level
272 sub EjsPullElementTop($$)
276 my $l = $e->{LEVELS}[0];
277 my $var = Parse::Pidl::Util::ParseExpr($e->{NAME}, $env, $e);
278 my $name = "\"$e->{NAME}\"";
279 EjsPullElement($e, $l, $var, $name, $env);
282 ###########################
284 sub EjsStructPull($$)
288 my $env = GenerateStructEnv($d);
289 fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, struct $name *r)");
292 pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, name));";
293 foreach my $e (@{$d->{ELEMENTS}}) {
294 EjsPullElementTop($e, $env);
296 pidl "return NT_STATUS_OK;";
301 ###########################
307 my $have_default = 0;
308 my $env = GenerateStructEnv($d);
309 fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, union $name *r)");
312 pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, name));";
313 pidl "switch (ejs->switch_var) {";
315 foreach my $e (@{$d->{ELEMENTS}}) {
316 if ($e->{CASE} eq "default") {
321 if ($e->{TYPE} ne "EMPTY") {
322 EjsPullElementTop($e, $env);
327 if (! $have_default) {
330 pidl "return ejs_panic(ejs, \"Bad switch value\");";
335 pidl "return NT_STATUS_OK;";
340 ##############################################
341 # put the enum elements in the constants array
342 sub EjsEnumConstant($)
346 foreach my $e (@{$d->{ELEMENTS}}) {
349 if ($el =~ /^(.*)=\s*(.*)\s*$/) {
353 $constants{$el} = $v;
358 ###########################
365 fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, enum $name *r)");
369 pidl "NDR_CHECK(ejs_pull_enum(ejs, v, name, &e));";
371 pidl "return NT_STATUS_OK;";
376 ###########################
378 sub EjsBitmapPull($$)
382 my $type_fn = $d->{BASE_TYPE};
383 my($type_decl) = Parse::Pidl::Typelist::mapTypeName($d->{BASE_TYPE});
384 fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, $type_decl *r)");
387 pidl "return ejs_pull_$type_fn(ejs, v, name, r);";
393 ###########################
394 # generate a structure pull
395 sub EjsTypedefPull($)
398 return if (has_property($d, "noejs"));
399 if ($d->{DATA}->{TYPE} eq 'STRUCT') {
400 EjsStructPull($d->{NAME}, $d->{DATA});
401 } elsif ($d->{DATA}->{TYPE} eq 'UNION') {
402 EjsUnionPull($d->{NAME}, $d->{DATA});
403 } elsif ($d->{DATA}->{TYPE} eq 'ENUM') {
404 EjsEnumPull($d->{NAME}, $d->{DATA});
405 } elsif ($d->{DATA}->{TYPE} eq 'BITMAP') {
406 EjsBitmapPull($d->{NAME}, $d->{DATA});
408 warn "Unhandled pull typedef $d->{NAME} of type $d->{DATA}->{TYPE}";
412 #####################
413 # generate a function
414 sub EjsPullFunction($)
417 my $env = GenerateFunctionInEnv($d);
418 my $name = $d->{NAME};
420 pidl "\nstatic NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, struct $name *r)";
423 pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, \"input\"));";
425 # we pull non-array elements before array elements as arrays
426 # may have length_is() or size_is() properties that depend
427 # on the non-array elements
428 foreach my $e (@{$d->{ELEMENTS}}) {
429 next unless (grep(/in/, @{$e->{DIRECTION}}));
430 next if (has_property($e, "length_is") || has_property($e, "size_is"));
431 EjsPullElementTop($e, $env);
434 foreach my $e (@{$d->{ELEMENTS}}) {
435 next unless (grep(/in/, @{$e->{DIRECTION}}));
436 next unless (has_property($e, "length_is") || has_property($e, "size_is"));
437 EjsPullElementTop($e, $env);
440 pidl "return NT_STATUS_OK;";
446 ###########################
447 # push a scalar element
448 sub EjsPushScalar($$$$$)
450 my ($e, $l, $var, $name, $env) = @_;
451 # have to handle strings specially :(
452 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
454 if ((not Parse::Pidl::Typelist::scalar_is_reference($e->{TYPE}))
455 or (defined($pl) and $pl->{TYPE} eq "POINTER")) {
456 $var = get_pointer_to($var);
458 pidl "NDR_CHECK(ejs_push_$e->{TYPE}(ejs, v, $name, $var));";
461 ###########################
462 # push a string element
463 sub EjsPushString($$$$$)
465 my ($e, $l, $var, $name, $env) = @_;
466 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
467 if (defined($pl) and $pl->{TYPE} eq "POINTER") {
468 $var = get_pointer_to($var);
470 pidl "NDR_CHECK(ejs_push_string(ejs, v, $name, $var));";
473 ###########################
474 # push a pointer element
475 sub EjsPushPointer($$$$$)
477 my ($e, $l, $var, $name, $env) = @_;
478 pidl "if (NULL == $var) {";
480 if ($l->{POINTER_TYPE} eq "ref") {
481 pidl "return NT_STATUS_INVALID_PARAMETER_MIX;";
483 pidl "NDR_CHECK(ejs_push_null(ejs, v, $name));";
488 $var = get_value_of($var);
489 EjsPushElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
494 ###########################
495 # push a switch element
496 sub EjsPushSwitch($$$$$)
498 my ($e, $l, $var, $name, $env) = @_;
499 my $switch_var = Parse::Pidl::Util::ParseExpr($l->{SWITCH_IS}, $env, $e);
500 pidl "ejs_set_switch(ejs, $switch_var);";
501 EjsPushElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
505 ###########################
506 # push an array element
507 sub EjsPushArray($$$$$)
509 my ($e, $l, $var, $name, $env) = @_;
510 my $nl = Parse::Pidl::NDR::GetNextLevel($e, $l);
511 my $length = Parse::Pidl::Util::ParseExpr($l->{LENGTH_IS}, $env, $e);
512 my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
513 if ($pl && $pl->{TYPE} eq "POINTER") {
514 $var = get_pointer_to($var);
516 # uint8 arrays are treated as data blobs
517 if ($nl->{TYPE} eq 'DATA' && $e->{TYPE} eq 'uint8') {
518 check_null_pointer($length);
519 pidl "ejs_push_array_uint8(ejs, v, $name, $var, $length);";
522 my $avar = $var . "[i]";
526 pidl "for (i=0;i<$length;i++) {";
528 pidl "const char *id = talloc_asprintf(ejs, \"%s.%u\", $name, i);";
529 EjsPushElement($e, $nl, $avar, "id", $env);
532 pidl "ejs_push_uint32(ejs, v, $name \".length\", &i);";
537 ################################
538 # push a structure/union element
539 sub EjsPushElement($$$$$)
541 my ($e, $l, $var, $name, $env) = @_;
542 if (($l->{TYPE} eq "POINTER")) {
543 EjsPushPointer($e, $l, $var, $name, $env);
544 } elsif (has_property($e, "charset")) {
545 EjsPushString($e, $l, $var, $name, $env);
546 } elsif ($l->{TYPE} eq "ARRAY") {
547 EjsPushArray($e, $l, $var, $name, $env);
548 } elsif ($l->{TYPE} eq "DATA") {
549 EjsPushScalar($e, $l, $var, $name, $env);
550 } elsif (($l->{TYPE} eq "SWITCH")) {
551 EjsPushSwitch($e, $l, $var, $name, $env);
553 pidl "return ejs_panic(ejs, \"unhandled push type $l->{TYPE}\");";
557 #############################################
558 # push a structure/union element at top level
559 sub EjsPushElementTop($$)
563 my $l = $e->{LEVELS}[0];
564 my $var = Parse::Pidl::Util::ParseExpr($e->{NAME}, $env, $e);
565 my $name = "\"$e->{NAME}\"";
566 EjsPushElement($e, $l, $var, $name, $env);
569 ###########################
571 sub EjsStructPush($$)
575 my $env = GenerateStructEnv($d);
576 fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const struct $name *r)");
579 pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, name));";
580 foreach my $e (@{$d->{ELEMENTS}}) {
581 EjsPushElementTop($e, $env);
583 pidl "return NT_STATUS_OK;";
588 ###########################
594 my $have_default = 0;
595 my $env = GenerateStructEnv($d);
596 fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const union $name *r)");
599 pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, name));";
600 pidl "switch (ejs->switch_var) {";
602 foreach my $e (@{$d->{ELEMENTS}}) {
603 if ($e->{CASE} eq "default") {
608 if ($e->{TYPE} ne "EMPTY") {
609 EjsPushElementTop($e, $env);
614 if (! $have_default) {
617 pidl "return ejs_panic(ejs, \"Bad switch value\");";
622 pidl "return NT_STATUS_OK;";
627 ###########################
634 fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const enum $name *r)");
637 pidl "unsigned e = *r;";
638 pidl "NDR_CHECK(ejs_push_enum(ejs, v, name, &e));";
639 pidl "return NT_STATUS_OK;";
644 ###########################
646 sub EjsBitmapPush($$)
650 my $type_fn = $d->{BASE_TYPE};
651 my($type_decl) = Parse::Pidl::Typelist::mapTypeName($d->{BASE_TYPE});
652 # put the bitmap elements in the constants array
653 foreach my $e (@{$d->{ELEMENTS}}) {
654 if ($e =~ /^(\w*)\s*(.*)\s*$/) {
657 $constants{$bname} = $v;
660 fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const $type_decl *r)");
663 pidl "return ejs_push_$type_fn(ejs, v, name, r);";
669 ###########################
670 # generate a structure push
671 sub EjsTypedefPush($)
674 return if (has_property($d, "noejs"));
676 if ($d->{DATA}->{TYPE} eq 'STRUCT') {
677 EjsStructPush($d->{NAME}, $d->{DATA});
678 } elsif ($d->{DATA}->{TYPE} eq 'UNION') {
679 EjsUnionPush($d->{NAME}, $d->{DATA});
680 } elsif ($d->{DATA}->{TYPE} eq 'ENUM') {
681 EjsEnumPush($d->{NAME}, $d->{DATA});
682 } elsif ($d->{DATA}->{TYPE} eq 'BITMAP') {
683 EjsBitmapPush($d->{NAME}, $d->{DATA});
685 warn "Unhandled push typedef $d->{NAME} of type $d->{DATA}->{TYPE}";
690 #####################
691 # generate a function
692 sub EjsPushFunction($)
695 my $env = GenerateFunctionOutEnv($d);
697 pidl "\nstatic NTSTATUS ejs_push_$d->{NAME}(struct ejs_rpc *ejs, struct MprVar *v, const struct $d->{NAME} *r)";
700 pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, \"output\"));";
702 foreach my $e (@{$d->{ELEMENTS}}) {
703 next unless (grep(/out/, @{$e->{DIRECTION}}));
704 EjsPushElementTop($e, $env);
707 if ($d->{RETURN_TYPE}) {
708 my $t = $d->{RETURN_TYPE};
709 pidl "NDR_CHECK(ejs_push_$t(ejs, v, \"result\", &r->out.result));";
712 pidl "return NT_STATUS_OK;";
718 #################################
719 # generate a ejs mapping function
724 my $name = $d->{NAME};
725 my $callnum = uc("DCERPC_$name");
726 my $table = "&dcerpc_table_$iface";
728 pidl "static int ejs_$name(int eid, int argc, struct MprVar **argv)";
731 pidl "return ejs_rpc_call(eid, argc, argv, $table, $callnum, (ejs_pull_function_t)ejs_pull_$name, (ejs_push_function_t)ejs_push_$name);";
741 $constants{$const->{NAME}} = $const->{VALUE};
750 pidl_hdr "#include \"librpc/gen_ndr/ndr_$_\_ejs\.h\"\n";
754 #####################################################################
755 # parse the interface definitions
758 my($interface,$needed) = @_;
760 my $name = $interface->{NAME};
764 pidl_hdr "#ifndef _HEADER_EJS_$interface->{NAME}\n";
765 pidl_hdr "#define _HEADER_EJS_$interface->{NAME}\n\n";
769 foreach my $d (@{$interface->{TYPES}}) {
770 ($needed->{"push_$d->{NAME}"}) && EjsTypedefPush($d);
771 ($needed->{"pull_$d->{NAME}"}) && EjsTypedefPull($d);
774 foreach my $d (@{$interface->{FUNCTIONS}}) {
775 next if not defined($d->{OPNUM});
776 next if has_property($d, "noejs");
780 EjsFunction($d, $name);
782 push (@fns, $d->{NAME});
785 foreach my $d (@{$interface->{CONSTS}}) {
789 pidl "static int ejs_$name\_init(int eid, int argc, struct MprVar **argv)";
792 pidl "struct MprVar *obj = mprInitObject(eid, \"$name\", argc, argv);";
794 pidl "mprSetCFunction(obj, \"$_\", ejs_$_);";
796 foreach my $v (keys %constants) {
797 my $value = $constants{$v};
798 if (substr($value, 0, 1) eq "\"") {
799 pidl "mprSetVar(obj, \"$v\", mprString($value));";
801 pidl "mprSetVar(obj, \"$v\", mprCreateNumberVar($value));";
804 pidl "return ejs_rpc_init(obj, \"$name\");";
808 pidl "NTSTATUS ejs_init_$name(void)";
811 pidl "ejsDefineCFunction(-1, \"$name\_init\", ejs_$name\_init, NULL, MPR_VAR_SCRIPT_HANDLE);";
812 pidl "return NT_STATUS_OK;";
817 pidl_hdr "#endif /* _HEADER_EJS_$interface->{NAME} */\n";
820 #####################################################################
821 # parse a parsed IDL into a C header
827 $ejs_hdr =~ s/.h$/_ejs.h/;
831 pidl_hdr "/* header auto-generated by pidl */\n\n";
834 /* EJS wrapper functions auto-generated by pidl */
835 #include \"includes.h\"
836 #include \"librpc/rpc/dcerpc.h\"
837 #include \"lib/appweb/ejs/ejs.h\"
838 #include \"scripting/ejs/ejsrpc.h\"
839 #include \"scripting/ejs/smbcalls.h\"
840 #include \"librpc/gen_ndr/ndr_misc_ejs.h\"
842 #include \"$ejs_hdr\"
848 foreach my $x (@{$ndr}) {
849 ($x->{TYPE} eq "INTERFACE") && NeededInterface($x, \%needed);
852 foreach my $x (@$ndr) {
853 ($x->{TYPE} eq "INTERFACE") && EjsInterface($x, \%needed);
854 ($x->{TYPE} eq "IMPORT") && EjsImport(@{$x->{PATHS}});
857 return ($res_hdr, $res);
860 sub NeededFunction($$)
862 my ($fn,$needed) = @_;
864 $needed->{"pull_$fn->{NAME}"} = 1;
865 $needed->{"push_$fn->{NAME}"} = 1;
867 foreach (@{$fn->{ELEMENTS}}) {
868 next if (has_property($_, "subcontext")); #FIXME: Support subcontexts
869 if (grep(/in/, @{$_->{DIRECTION}})) {
870 $needed->{"pull_$_->{TYPE}"} = 1;
872 if (grep(/out/, @{$_->{DIRECTION}})) {
873 $needed->{"push_$_->{TYPE}"} = 1;
878 sub NeededTypedef($$)
880 my ($t,$needed) = @_;
882 if (has_property($t, "public")) {
883 $needed->{"pull_$t->{NAME}"} = not has_property($t, "noejs");
884 $needed->{"push_$t->{NAME}"} = not has_property($t, "noejs");
887 return if (($t->{DATA}->{TYPE} ne "STRUCT") and
888 ($t->{DATA}->{TYPE} ne "UNION"));
890 foreach (@{$t->{DATA}->{ELEMENTS}}) {
891 next if (has_property($_, "subcontext")); #FIXME: Support subcontexts
892 unless (defined($needed->{"pull_$_->{TYPE}"})) {
893 $needed->{"pull_$_->{TYPE}"} = $needed->{"pull_$t->{NAME}"};
895 unless (defined($needed->{"push_$_->{TYPE}"})) {
896 $needed->{"push_$_->{TYPE}"} = $needed->{"push_$t->{NAME}"};
901 #####################################################################
902 # work out what parse functions are needed
903 sub NeededInterface($$)
905 my ($interface,$needed) = @_;
907 NeededFunction($_, $needed) foreach (@{$interface->{FUNCTIONS}});
908 NeededTypedef($_, $needed) foreach (reverse @{$interface->{TYPES}});