################################################### # EJS function wrapper generator # Copyright jelmer@samba.org 2005 # Copyright Andrew Tridgell 2005 # released under the GNU GPL package EjsClient; use strict; use pidl::typelist; my($res); sub pidl ($) { $res .= shift; } # this should probably be in ndr.pm sub GenerateStructEnv($) { my $x = shift; my %env; foreach my $e (@{$x->{ELEMENTS}}) { if ($e->{NAME}) { $env{$e->{NAME}} = "r->$e->{NAME}"; } } $env{"this"} = "r"; return \%env; } sub GenerateFunctionInEnv($) { my $fn = shift; my %env; foreach my $e (@{$fn->{ELEMENTS}}) { if (grep (/in/, @{$e->{DIRECTION}})) { $env{$e->{NAME}} = "r->in.$e->{NAME}"; } } return \%env; } sub GenerateFunctionOutEnv($) { my $fn = shift; my %env; foreach my $e (@{$fn->{ELEMENTS}}) { if (grep (/out/, @{$e->{DIRECTION}})) { $env{$e->{NAME}} = "r->out.$e->{NAME}"; } elsif (grep (/in/, @{$e->{DIRECTION}})) { $env{$e->{NAME}} = "r->in.$e->{NAME}"; } } return \%env; } sub get_pointer_to($) { my $var_name = shift; if ($var_name =~ /^\*(.*)$/) { return $1; } elsif ($var_name =~ /^\&(.*)$/) { return "&($var_name)"; } else { return "&$var_name"; } } sub get_value_of($) { my $var_name = shift; if ($var_name =~ /^\&(.*)$/) { return $1; } else { return "*$var_name"; } } ########################### # pull a scalar element sub EjsPullScalar($$$$$) { my ($e, $l, $var, $name, $env) = @_; $var = get_pointer_to($var); pidl "\tNDR_CHECK(ejs_pull_$e->{TYPE}(ejs, v, $name, $var));\n"; } ########################### # pull a pointer element sub EjsPullPointer($$$$$) { my ($e, $l, $var, $name, $env) = @_; pidl "EJS_ALLOC(ejs, $var);\n"; $var = get_value_of($var); EjsPullElement($e, Ndr::GetNextLevel($e, $l), $var, $name, $env); } ########################### # pull a string element sub EjsPullString($$$$$) { my ($e, $l, $var, $name, $env) = @_; $var = get_pointer_to($var); pidl "\tNDR_CHECK(ejs_pull_string(ejs, v, $name, $var));\n"; } ########################### # pull an arrar element sub EjsPullArray($$$$$) { my ($e, $l, $var, $name, $env) = @_; my $length = util::ParseExpr($l->{LENGTH_IS}, $env); pidl "{ uint32_t i; EJS_ALLOC_N(ejs, $var, $length); for (i=0;i<$length;i++) {\n"; pidl "\tconst char *id = talloc_asprintf(ejs, \"%s.%u\", $name, i);\n"; EjsPullElement($e, Ndr::GetNextLevel($e, $l), $var . "[i]", "id", $env); pidl "}\nejs_push_uint32(ejs, v, $name \".length\", &i); }\n"; } ########################### # pull a switch element sub EjsPullSwitch($$$$$) { my ($e, $l, $var, $name, $env) = @_; my $switch_var = util::ParseExpr($l->{SWITCH_IS}, $env); pidl "ejs_set_switch(ejs, $switch_var);\n"; EjsPullElement($e, Ndr::GetNextLevel($e, $l), $var, $name, $env); } ########################### # pull a structure element sub EjsPullElement($$$$$) { my ($e, $l, $var, $name, $env) = @_; if (util::has_property($e, "charset")) { EjsPullString($e, $l, $var, $name, $env); } elsif ($l->{TYPE} eq "ARRAY") { EjsPullArray($e, $l, $var, $name, $env); } elsif ($l->{TYPE} eq "DATA") { EjsPullScalar($e, $l, $var, $name, $env); } elsif (($l->{TYPE} eq "POINTER")) { EjsPullPointer($e, $l, $var, $name, $env); } elsif (($l->{TYPE} eq "SWITCH")) { EjsPullSwitch($e, $l, $var, $name, $env); } else { pidl "return ejs_panic(ejs, \"unhandled pull type $l->{TYPE}\");\n"; } } ############################################# # pull a structure/union element at top level sub EjsPullElementTop($$) { my $e = shift; my $env = shift; my $l = $e->{LEVELS}[0]; my $var = util::ParseExpr($e->{NAME}, $env); my $name = "\"$e->{NAME}\""; EjsPullElement($e, $l, $var, $name, $env); } ########################### # pull a struct sub EjsStructPull($$) { my $name = shift; my $d = shift; my $env = GenerateStructEnv($d); pidl "\nstatic NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, struct $name *r)\n{\n"; pidl "\tNDR_CHECK(ejs_pull_struct_start(ejs, &v, name));\n"; foreach my $e (@{$d->{ELEMENTS}}) { EjsPullElementTop($e, $env); } pidl "\treturn NT_STATUS_OK;\n"; pidl "}\n\n"; } ########################### # pull a union sub EjsUnionPull($$) { my $name = shift; my $d = shift; my $env = GenerateStructEnv($d); pidl "\nstatic NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, union $name *r)\n{\n"; pidl "return ejs_panic(ejs, \"union pull not handled\");\n"; pidl "}\n\n"; } ########################### # pull a enum sub EjsEnumPull($$) { my $name = shift; my $d = shift; pidl "\nstatic NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, enum $name *r)\n{\n"; pidl "\tunsigned e;\n"; pidl "\tNDR_CHECK(ejs_pull_enum(ejs, v, name, &e));\n"; pidl "\t*r = e;\n"; pidl "\treturn NT_STATUS_OK;\n"; pidl "}\n\n"; } ########################### # pull a bitmap sub EjsBitmapPull($$) { # ignored for now } ########################### # generate a structure pull sub EjsTypedefPull($) { my $d = shift; if ($d->{DATA}->{TYPE} eq 'STRUCT') { EjsStructPull($d->{NAME}, $d->{DATA}); } elsif ($d->{DATA}->{TYPE} eq 'UNION') { EjsUnionPull($d->{NAME}, $d->{DATA}); } elsif ($d->{DATA}->{TYPE} eq 'ENUM') { EjsEnumPull($d->{NAME}, $d->{DATA}); } elsif ($d->{DATA}->{TYPE} eq 'BITMAP') { EjsBitmapPull($d->{NAME}, $d->{DATA}); } else { warn "Unhandled pull typedef $d->{NAME} of type $d->{DATA}->{TYPE}\n"; } } ##################### # generate a function sub EjsPullFunction($) { my $d = shift; my $env = GenerateFunctionInEnv($d); my $name = $d->{NAME}; pidl "\nstatic NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, struct $name *r)\n"; pidl "{\n"; pidl "\tNDR_CHECK(ejs_pull_struct_start(ejs, &v, \"input\"));\n"; foreach my $e (@{$d->{ELEMENTS}}) { next unless (grep(/in/, @{$e->{DIRECTION}})); EjsPullElementTop($e, $env); } pidl "\treturn NT_STATUS_OK;\n"; pidl "}\n\n"; } ########################### # push a scalar element sub EjsPushScalar($$$$$) { my ($e, $l, $var, $name, $env) = @_; $var = get_pointer_to($var); pidl "\tNDR_CHECK(ejs_push_$e->{TYPE}(ejs, v, $name, $var));\n"; } ########################### # push a string element sub EjsPushString($$$$$) { my ($e, $l, $var, $name, $env) = @_; pidl "\tNDR_CHECK(ejs_push_string(ejs, v, $name, $var));\n"; } ########################### # push a pointer element sub EjsPushPointer($$$$$) { my ($e, $l, $var, $name, $env) = @_; $var = get_value_of($var); EjsPushElement($e, Ndr::GetNextLevel($e, $l), $var, $name, $env); } ########################### # push a switch element sub EjsPushSwitch($$$$$) { my ($e, $l, $var, $name, $env) = @_; my $switch_var = util::ParseExpr($l->{SWITCH_IS}, $env); pidl "ejs_set_switch(ejs, $switch_var);\n"; EjsPushElement($e, Ndr::GetNextLevel($e, $l), $var, $name, $env); } ########################### # push an arrar element sub EjsPushArray($$$$$) { my ($e, $l, $var, $name, $env) = @_; my $length = util::ParseExpr($l->{LENGTH_IS}, $env); pidl "{ uint32_t i; for (i=0;i<$length;i++) {\n"; pidl "\tconst char *id = talloc_asprintf(ejs, \"%s.%u\", $name, i);\n"; EjsPushElement($e, Ndr::GetNextLevel($e, $l), $var . "[i]", "id", $env); pidl "}\nejs_push_uint32(ejs, v, $name \".length\", &i); }\n"; } ################################ # push a structure/union element sub EjsPushElement($$$$$) { my ($e, $l, $var, $name, $env) = @_; if (util::has_property($e, "charset")) { EjsPushString($e, $l, $var, $name, $env); } elsif ($l->{TYPE} eq "ARRAY") { EjsPushArray($e, $l, $var, $name, $env); } elsif ($l->{TYPE} eq "DATA") { EjsPushScalar($e, $l, $var, $name, $env); } elsif (($l->{TYPE} eq "POINTER")) { EjsPushPointer($e, $l, $var, $name, $env); } elsif (($l->{TYPE} eq "SWITCH")) { EjsPushSwitch($e, $l, $var, $name, $env); } else { pidl "return ejs_panic(ejs, \"unhandled push type $l->{TYPE}\");\n"; } } ############################################# # push a structure/union element at top level sub EjsPushElementTop($$) { my $e = shift; my $env = shift; my $l = $e->{LEVELS}[0]; my $var = util::ParseExpr($e->{NAME}, $env); my $name = "\"$e->{NAME}\""; EjsPushElement($e, $l, $var, $name, $env); } ########################### # push a struct sub EjsStructPush($$) { my $name = shift; my $d = shift; my $env = GenerateStructEnv($d); pidl "\nstatic NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const struct $name *r)\n{\n"; pidl "\tNDR_CHECK(ejs_push_struct_start(ejs, &v, name));\n"; foreach my $e (@{$d->{ELEMENTS}}) { EjsPushElementTop($e, $env); } pidl "\treturn NT_STATUS_OK;\n"; pidl "}\n\n"; } ########################### # push a union sub EjsUnionPush($$) { my $name = shift; my $d = shift; my $have_default = 0; my $env = GenerateStructEnv($d); pidl "\nstatic NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const union $name *r)\n{\n"; pidl "switch (ejs->switch_var) {\n"; foreach my $e (@{$d->{ELEMENTS}}) { if ($e->{CASE} eq "default") { $have_default = 1; } pidl "$e->{CASE}:"; if ($e->{TYPE} ne "EMPTY") { EjsPushElementTop($e, $env); } pidl "break;\n"; } if (! $have_default) { pidl "default:"; pidl "\treturn ejs_panic(ejs, \"Bad switch value\");"; } pidl "}\nreturn NT_STATUS_OK;\n}\n"; } ########################### # push a enum sub EjsEnumPush($$) { my $name = shift; my $d = shift; pidl "\nstatic NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const enum $name *r)\n{\n"; pidl "\tunsigned e = *r;\n"; pidl "\tNDR_CHECK(ejs_push_enum(ejs, v, name, &e));\n"; pidl "\treturn NT_STATUS_OK;\n"; pidl "}\n\n"; } ########################### # push a bitmap sub EjsBitmapPush($$) { # ignored for now } ########################### # generate a structure push sub EjsTypedefPush($) { my $d = shift; if ($d->{DATA}->{TYPE} eq 'STRUCT') { EjsStructPush($d->{NAME}, $d->{DATA}); } elsif ($d->{DATA}->{TYPE} eq 'UNION') { EjsUnionPush($d->{NAME}, $d->{DATA}); } elsif ($d->{DATA}->{TYPE} eq 'ENUM') { EjsEnumPush($d->{NAME}, $d->{DATA}); } elsif ($d->{DATA}->{TYPE} eq 'BITMAP') { EjsBitmapPush($d->{NAME}, $d->{DATA}); } else { warn "Unhandled push typedef $d->{NAME} of type $d->{DATA}->{TYPE}\n"; } } ##################### # generate a function sub EjsPushFunction($) { my $d = shift; my $env = GenerateFunctionOutEnv($d); pidl "\nstatic NTSTATUS ejs_push_$d->{NAME}(struct ejs_rpc *ejs, struct MprVar *v, const struct $d->{NAME} *r)\n"; pidl "{\n"; pidl "\tNDR_CHECK(ejs_push_struct_start(ejs, &v, \"output\"));\n"; foreach my $e (@{$d->{ELEMENTS}}) { next unless (grep(/out/, @{$e->{DIRECTION}})); EjsPushElementTop($e, $env); } pidl "\treturn NT_STATUS_OK;\n"; pidl "}\n\n"; } ################################# # generate a ejs mapping function sub EjsFunction($) { my $d = shift; my $name = $d->{NAME}; pidl "static int ejs_$name(int eid, int argc, struct MprVar **argv)\n"; pidl "{\n"; pidl "\treturn ejs_rpc_call(eid, argc, argv, \"$name\", (ejs_pull_function_t)ejs_pull_$name, (ejs_push_function_t)ejs_push_$name);\n"; pidl "}\n\n"; } ##################################################################### # parse the interface definitions sub EjsInterface($) { my($interface) = shift; my @fns = (); my $name = $interface->{NAME}; foreach my $d (@{$interface->{TYPEDEFS}}) { EjsTypedefPush($d); EjsTypedefPull($d); } foreach my $d (@{$interface->{FUNCTIONS}}) { next if not defined($d->{OPNUM}); EjsPullFunction($d); EjsPushFunction($d); EjsFunction($d); push (@fns, $d->{NAME}); } pidl "void setup_ejs_$name(void)\n"; pidl "{\n"; foreach (@fns) { pidl "\tejsDefineCFunction(-1, \"dcerpc_$_\", ejs_$_, NULL, MPR_VAR_SCRIPT_HANDLE);\n"; } pidl "}\n"; } ##################################################################### # parse a parsed IDL into a C header sub Parse($$) { my($ndr,$hdr) = @_; $res = ""; pidl " /* EJS wrapper functions auto-generated by pidl */ #include \"includes.h\" #include \"lib/ejs/ejs.h\" #include \"$hdr\" #include \"scripting/ejs/ejsrpc.h\" "; foreach my $x (@{$ndr}) { if ($x->{TYPE} eq "INTERFACE") { ($x->{TYPE} eq "INTERFACE") && EjsInterface($x); } } return $res; } 1;