pidl:NDR/Client.pm: use dcerpc_binding_handl_call_params* if a dcerpc pipe is used
[metze/samba/wip.git] / pidl / lib / Parse / Pidl / Samba4 / NDR / Client.pm
index b4954ca62410438a04e8b5e7d8c6e958cedd7995..f9d743f82b5b6ab551c80717d00dee091f7ab5e3 100644 (file)
@@ -11,7 +11,7 @@ use Exporter;
 @EXPORT_OK = qw(Parse);
 
 use Parse::Pidl qw(fatal warning error);
-use Parse::Pidl::Util qw(has_property ParseExpr);
+use Parse::Pidl::Util qw(has_property ParseExpr genpad);
 use Parse::Pidl::NDR qw(ContainsPipe);
 use Parse::Pidl::Typelist qw(mapTypeName);
 use Parse::Pidl::Samba4 qw(choose_header is_intree DeclLong);
@@ -29,15 +29,6 @@ sub pidl_hdr($$) { my ($self, $txt) = @_; $self->{res_hdr} .= "$txt\n"; }
 sub pidl_both($$) { my ($self, $txt) = @_; $self->{hdr} .= "$txt\n"; $self->{res_hdr} .= "$txt\n"; }
 sub fn_declare($$) { my ($self,$n) = @_; $self->pidl($n); $self->pidl_hdr("$n;"); }
 
-sub genpad($)
-{
-       my ($s) = @_;
-       my $nt = int((length($s)+1)/8);
-       my $lt = ($nt*8)-1;
-       my $ns = (length($s)-$lt);
-       return "\t"x($nt)." "x($ns);
-}
-
 sub new($)
 {
        my ($class) = shift;
@@ -61,8 +52,26 @@ sub ParseFunction_r_State($$$$)
        my ($self, $if, $fn, $name) = @_;
        my $uname = uc $name;
 
+       my $in_pipes = 0;
+       foreach my $e (@{$fn->{ELEMENTS}}) {
+               next unless grep(/in/, @{$e->{DIRECTION}});
+               next unless ContainsPipe($e, $e->{LEVELS}[0]);
+               $in_pipes++;
+       }
+       my $out_pipes = 0;
+       foreach my $e (@{$fn->{ELEMENTS}}) {
+               next unless grep(/out/, @{$e->{DIRECTION}});
+               next unless ContainsPipe($e, $e->{LEVELS}[0]);
+               $out_pipes++;
+       }
+
        $self->pidl("struct dcerpc_$name\_r_state {");
        $self->indent;
+       if ($in_pipes > 0 or $out_pipes > 0) {
+               $self->pidl("struct dcerpc_pipe_handle_connection *in_pipes[$in_pipes];") if ($in_pipes > 0);
+               $self->pidl("struct dcerpc_pipe_handle_connection *out_pipes[$out_pipes];") if ($out_pipes > 0);
+               $self->pidl("struct dcerpc_binding_handle_call_params params;");
+       }
        $self->pidl("TALLOC_CTX *out_mem_ctx;");
        $self->deindent;
        $self->pidl("};");
@@ -123,9 +132,58 @@ sub ParseFunction_r_Send($$$$)
        }
        $self->pidl("");
 
-       $self->pidl("subreq = dcerpc_binding_handle_call_send(state, ev, h,");
-       $self->pidl("\t\tNULL, &ndr_table_$if->{NAME},");
-       $self->pidl("\t\tNDR_$uname, $submem, r);");
+       my $in_pipes = 0;
+       foreach my $e (@{$fn->{ELEMENTS}}) {
+               next unless grep(/in/, @{$e->{DIRECTION}});
+               next unless ContainsPipe($e, $e->{LEVELS}[0]);
+               $self->pidl("if (r->in.$e->{NAME} == NULL) {");
+               $self->indent;
+               $self->pidl("tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);");
+               $self->pidl("return tevent_req_post(req, ev);");
+               $self->deindent;
+               $self->pidl("}");
+               $self->pidl("state->in_pipes[$in_pipes] = r->in.".$e->{NAME}."->pc;");
+               $in_pipes++;
+       }
+       my $out_pipes = 0;
+       foreach my $e (@{$fn->{ELEMENTS}}) {
+               next unless grep(/out/, @{$e->{DIRECTION}});
+               next unless ContainsPipe($e, $e->{LEVELS}[0]);
+               $self->pidl("if (r->out.$e->{NAME} == NULL) {");
+               $self->indent;
+               $self->pidl("tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);");
+               $self->pidl("return tevent_req_post(req, ev);");
+               $self->deindent;
+               $self->pidl("}");
+               $self->pidl("state->out_pipes[$out_pipes] = r->out.".$e->{NAME}."->pc;");
+               $out_pipes++;
+       }
+
+       if ($in_pipes > 0 or $out_pipes > 0) {
+               $self->pidl("state->params.r_mem = $submem;");
+               $self->pidl("state->params.r_ptr = r;");
+               $self->pidl("state->params.in.num_pipes = $in_pipes;");
+               if ($in_pipes > 0) {
+                       $self->pidl("state->params.in.pipes = state->in_pipes;");
+               } else {
+                       $self->pidl("state->params.in.pipes = NULL;");
+               }
+               $self->pidl("state->params.out.num_pipes = $out_pipes;");
+               if ($out_pipes > 0) {
+                       $self->pidl("state->params.out.pipes = state->out_pipes;");
+               } else {
+                       $self->pidl("state->params.out.pipes = NULL;");
+               }
+               $self->pidl("");
+
+               $self->pidl("subreq = dcerpc_binding_handle_call_params_send(state, ev, h,");
+               $self->pidl("\t\tNULL, &ndr_table_$if->{NAME},");
+               $self->pidl("\t\tNDR_$uname, &state->params);");
+       } else {
+               $self->pidl("subreq = dcerpc_binding_handle_call_send(state, ev, h,");
+               $self->pidl("\t\tNULL, &ndr_table_$if->{NAME},");
+               $self->pidl("\t\tNDR_$uname, $submem, r);");
+       }
        $self->pidl("if (tevent_req_nomem(subreq, req)) {");
        $self->indent;
        $self->pidl("return tevent_req_post(req, ev);");
@@ -147,6 +205,19 @@ sub ParseFunction_r_Done($$$$)
 
        my $proto = "static void dcerpc_$name\_r_done(struct tevent_req *subreq)";
 
+       my $in_pipes = 0;
+       foreach my $e (@{$fn->{ELEMENTS}}) {
+               next unless grep(/in/, @{$e->{DIRECTION}});
+               next unless ContainsPipe($e, $e->{LEVELS}[0]);
+               $in_pipes++;
+       }
+       my $out_pipes = 0;
+       foreach my $e (@{$fn->{ELEMENTS}}) {
+               next unless grep(/out/, @{$e->{DIRECTION}});
+               next unless ContainsPipe($e, $e->{LEVELS}[0]);
+               $out_pipes++;
+       }
+
        $self->pidl("$proto");
        $self->pidl("{");
        $self->indent;
@@ -157,7 +228,11 @@ sub ParseFunction_r_Done($$$$)
        $self->pidl("NTSTATUS status;");
        $self->pidl("");
 
-       $self->pidl("status = dcerpc_binding_handle_call_recv(subreq);");
+       if ($in_pipes > 0 or $out_pipes > 0) {
+               $self->pidl("status = dcerpc_binding_handle_call_params_recv(subreq);");
+       } else {
+               $self->pidl("status = dcerpc_binding_handle_call_recv(subreq);");
+       }
        $self->pidl("TALLOC_FREE(subreq);");
        $self->pidl("if (tevent_req_nterror(req, status)) {");
        $self->indent;
@@ -259,7 +334,7 @@ sub HeaderProperties($$)
        my($props,$ignores) = @_;
        my $ret = "";
 
-       foreach my $d (keys %{$props}) {
+       foreach my $d (sort(keys %{$props})) {
                next if (grep(/^$d$/, @$ignores));
                if($props->{$d} ne "1") {
                        $ret.= "$d($props->{$d}),";
@@ -505,7 +580,7 @@ sub ParseFunction_Send($$$$)
 
        if (defined($fn->{RETURN_TYPE})) {
                $self->pidl("/* Result */");
-               $self->pidl("ZERO_STRUCT(state->orig.out.result);");
+               $self->pidl("NDR_ZERO_STRUCT(state->orig.out.result);");
                $self->pidl("");
        }
 
@@ -594,7 +669,7 @@ sub ParseFunction_Done($$$$)
        }
 
        $self->pidl("/* Reset temporary structure */");
-       $self->pidl("ZERO_STRUCT(state->tmp);");
+       $self->pidl("NDR_ZERO_STRUCT(state->tmp);");
        $self->pidl("");
 
        $self->pidl("tevent_req_done(req);");
@@ -707,7 +782,7 @@ sub ParseFunction_Sync($$$$)
 
        if (defined($fn->{RETURN_TYPE})) {
                $self->pidl("/* Result */");
-               $self->pidl("ZERO_STRUCT(r.out.result);");
+               $self->pidl("NDR_ZERO_STRUCT(r.out.result);");
                $self->pidl("");
        }
 
@@ -747,24 +822,6 @@ sub ParseFunction($$$)
 {
        my ($self, $if, $fn) = @_;
 
-       if ($self->ParseFunctionHasPipes($fn)) {
-               $self->pidl_both("/*");
-               $self->pidl_both(" * The following function is skipped because");
-               $self->pidl_both(" * it uses pipes:");
-               $self->pidl_both(" *");
-               $self->pidl_both(" * dcerpc_$fn->{NAME}_r_send()");
-               $self->pidl_both(" * dcerpc_$fn->{NAME}_r_recv()");
-               $self->pidl_both(" * dcerpc_$fn->{NAME}_r()");
-               $self->pidl_both(" *");
-               $self->pidl_both(" * dcerpc_$fn->{NAME}_send()");
-               $self->pidl_both(" * dcerpc_$fn->{NAME}_recv()");
-               $self->pidl_both(" * dcerpc_$fn->{NAME}()");
-               $self->pidl_both(" */");
-               $self->pidl_both("");
-               warning($fn->{ORIGINAL}, "$fn->{NAME}: dcerpc client does not support pipe yet");
-               return;
-       }
-
        $self->ParseFunction_r_State($if, $fn, $fn->{NAME});
        $self->ParseFunction_r_Send($if, $fn, $fn->{NAME});
        $self->ParseFunction_r_Done($if, $fn, $fn->{NAME});
@@ -822,6 +879,379 @@ sub ParseFunction($$$)
        $self->pidl_hdr("");
 }
 
+sub ParsePipeCreate($$$)
+{
+       my ($self, $t, $cname) = @_;
+
+       $self->pidl_both("/* $t->{NAME} */");
+
+       $self->pidl_hdr("struct $t->{NAME};");
+       $self->pidl("struct $t->{NAME} {");
+       $self->indent;
+       $self->pidl("struct dcerpc_pipe_handle_connection *pc;");
+       $self->deindent;
+       $self->pidl("};");
+       $self->pidl("");
+
+       my $proto = "struct $t->{NAME} *dcerpc_$t->{NAME}\_create(TALLOC_CTX *mem_ctx)";
+
+       $self->pidl_hdr("$proto;");
+       $self->pidl("$proto");
+       $self->pidl("{");
+       $self->indent;
+
+       $self->pidl("struct $t->{NAME} *p;");
+       $self->pidl("");
+
+       $self->pidl("p = talloc_zero(mem_ctx, struct $t->{NAME});");
+       $self->pidl("if (p == NULL) {");
+       $self->indent;
+       $self->pidl("return NULL;");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+
+       my $cname = "$t->{NAME}_chunk";
+
+       $self->pidl("/* the pipe is disconnected by default */");
+       $self->pidl("p->pc = dcerpc_pipe_handle_connection_create(p,");
+       $self->pidl("\t\t\t\"$cname\",");
+       $self->pidl("\t\t\tsizeof(struct $cname));");
+       $self->pidl("if (p->pc == NULL) {");
+       $self->indent;
+       $self->pidl("TALLOC_FREE(p);");
+       $self->pidl("return NULL;");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+
+       $self->pidl("return p;");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+}
+
+sub ParsePipeChunkPush_State($$$)
+{
+       my ($self, $t, $cname) = @_;
+
+       my $state_str = "struct dcerpc_$cname\_push_state";
+       my $done_fn = "dcerpc_$cname\_push_done";
+
+       $self->pidl("$state_str {");
+       $self->indent;
+       $self->pidl("uint8_t dummy;");
+       $self->deindent;
+       $self->pidl("};");
+       $self->pidl("");
+
+       $self->pidl("static void $done_fn(struct tevent_req *subreq);");
+       $self->pidl("");
+}
+
+sub ParsePipeChunkPush_Send($$$)
+{
+       my ($self, $t, $cname) = @_;
+
+       my $fn_args = "";
+       my $state_str = "struct dcerpc_$cname\_push_state";
+       my $done_fn = "dcerpc_$cname\_push_done";
+       my $fn_str = "struct tevent_req *dcerpc_$cname\_push_send";
+       my $pad = genpad($fn_str);
+
+       $fn_args .= "TALLOC_CTX *mem_ctx";
+       $fn_args .= ",\n" . $pad . "struct tevent_context *ev";
+       $fn_args .= ",\n" . $pad . "struct $t->{NAME} *p";
+       $fn_args .= ",\n" . $pad . "const struct $cname *chunk";
+
+       $self->fn_declare("$fn_str($fn_args)");
+       $self->pidl("{");
+       $self->indent;
+       $self->pidl("struct tevent_req *req;");
+       $self->pidl("$state_str *state;");
+       $self->pidl("struct tevent_req *subreq;");
+       $self->pidl("");
+
+       $self->pidl("req = tevent_req_create(mem_ctx, &state,");
+       $self->pidl("\t\t\t$state_str);");
+       $self->pidl("if (req == NULL) {");
+       $self->indent;
+       $self->pidl("return NULL;");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+
+       $self->pidl("subreq = dcerpc_pipe_handle_push_send(state, ev,");
+       $self->pidl("\t\t\tp->pc, chunk);");
+       $self->pidl("if (tevent_req_nomem(subreq, req)) {");
+       $self->indent;
+       $self->pidl("return tevent_req_post(req, ev);");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("tevent_req_set_callback(subreq, $done_fn, req);");
+       $self->pidl("");
+
+       $self->pidl("return req;");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+}
+
+sub ParsePipeChunkPush_Done($$$)
+{
+       my ($self, $t, $cname) = @_;
+
+       my $fn_args = "";
+       my $state_str = "struct dcerpc_$cname\_push_state";
+       my $fn_str = "static void dcerpc_$cname\_push_done";
+       my $pad = genpad($fn_str);
+
+       $fn_args .= "struct tevent_req *subreq";
+
+       $self->pidl("$fn_str($fn_args)");
+       $self->pidl("{");
+       $self->indent;
+       $self->pidl("struct tevent_req *req =");
+       $self->pidl("\ttevent_req_callback_data(subreq,");
+       $self->pidl("\tstruct tevent_req);");
+       # unused
+       #$self->pidl("$state_str *state =");
+       #$self->pidl("\ttevent_req_data(req,");
+       #$self->pidl("\t$state_str);");
+       $self->pidl("NTSTATUS status;");
+       $self->pidl("");
+
+       $self->pidl("status = dcerpc_pipe_handle_push_recv(subreq);");
+       $self->pidl("TALLOC_FREE(subreq);");
+       $self->pidl("if (tevent_req_nterror(req, status)) {");
+       $self->indent;
+       $self->pidl("return;");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+
+       $self->pidl("tevent_req_done(req);");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+}
+
+sub ParsePipeChunkPush_Recv($$$)
+{
+       my ($self, $t, $cname) = @_;
+
+       my $fn_args = "";
+       my $state_str = "struct dcerpc_$cname\_push_state";
+       my $fn_str = "NTSTATUS dcerpc_$cname\_push_recv";
+       my $pad = genpad($fn_str);
+
+       $fn_args .= "struct tevent_req *req";
+
+       $self->fn_declare("$fn_str($fn_args)");
+       $self->pidl("{");
+       $self->indent;
+       $self->pidl("$state_str *state =");
+       $self->pidl("\ttevent_req_data(req,");
+       $self->pidl("\t$state_str);");
+       $self->pidl("NTSTATUS status;");
+       $self->pidl("");
+
+       $self->pidl("if (tevent_req_is_nterror(req, &status)) {");
+       $self->indent;
+       $self->pidl("/*");
+       $self->pidl(" * We want the tevent_req_data() protection,");
+       $self->pidl(" * but don't need 'state', so just pretend we use it");
+       $self->pidl(" * in order to avoid compiler warnings.");
+       $self->pidl(" */");
+       $self->pidl("state->dummy = 0;");
+       $self->pidl("tevent_req_received(req);");
+       $self->pidl("return status;");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+
+       $self->pidl("tevent_req_received(req);");
+       $self->pidl("return NT_STATUS_OK;");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+}
+
+sub ParsePipeChunkPull_State($$$)
+{
+       my ($self, $t, $cname) = @_;
+
+       my $state_str = "struct dcerpc_$cname\_pull_state";
+       my $done_fn = "dcerpc_$cname\_pull_done";
+
+       $self->pidl("$state_str {");
+       $self->indent;
+       $self->pidl("struct $cname *chunk;");
+       $self->deindent;
+       $self->pidl("};");
+       $self->pidl("");
+
+       $self->pidl("static void $done_fn(struct tevent_req *subreq);");
+       $self->pidl("");
+}
+
+sub ParsePipeChunkPull_Send($$$)
+{
+       my ($self, $t, $cname) = @_;
+
+       my $fn_args = "";
+       my $state_str = "struct dcerpc_$cname\_pull_state";
+       my $done_fn = "dcerpc_$cname\_pull_done";
+       my $fn_str = "struct tevent_req *dcerpc_$cname\_pull_send";
+       my $pad = genpad($fn_str);
+
+       $fn_args .= "TALLOC_CTX *mem_ctx";
+       $fn_args .= ",\n" . $pad . "struct tevent_context *ev";
+       $fn_args .= ",\n" . $pad . "struct $t->{NAME} *p";
+
+       $self->fn_declare("$fn_str($fn_args)");
+       $self->pidl("{");
+       $self->indent;
+       $self->pidl("struct tevent_req *req;");
+       $self->pidl("$state_str *state;");
+       $self->pidl("struct tevent_req *subreq;");
+       $self->pidl("");
+
+       $self->pidl("req = tevent_req_create(mem_ctx, &state,");
+       $self->pidl("\t\t\t$state_str);");
+       $self->pidl("if (req == NULL) {");
+       $self->indent;
+       $self->pidl("return NULL;");
+       $self->deindent;
+       $self->pidl("}");
+
+       $self->pidl("state->chunk = talloc_zero(state, struct $cname);");
+       $self->pidl("if (tevent_req_nomem(state->chunk, req)) {");
+       $self->indent;
+       $self->pidl("return tevent_req_post(req, ev);");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+
+       $self->pidl("subreq = dcerpc_pipe_handle_pull_send(state, ev,");
+       $self->pidl("\t\t\tp->pc, state->chunk, state->chunk);");
+       $self->pidl("if (tevent_req_nomem(subreq, req)) {");
+       $self->indent;
+       $self->pidl("return tevent_req_post(req, ev);");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("tevent_req_set_callback(subreq, $done_fn, req);");
+       $self->pidl("");
+
+       $self->pidl("return req;");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+}
+
+sub ParsePipeChunkPull_Done($$$)
+{
+       my ($self, $t, $cname) = @_;
+
+       my $fn_args = "";
+       my $state_str = "struct dcerpc_$cname\_pull_state";
+       my $fn_str = "static void dcerpc_$cname\_pull_done";
+       my $pad = genpad($fn_str);
+
+       $fn_args .= "struct tevent_req *subreq";
+
+       $self->pidl("$fn_str($fn_args)");
+       $self->pidl("{");
+       $self->indent;
+       $self->pidl("struct tevent_req *req =");
+       $self->pidl("\ttevent_req_callback_data(subreq,");
+       $self->pidl("\tstruct tevent_req);");
+       # unused
+       #$self->pidl("$state_str *state =");
+       #$self->pidl("\ttevent_req_data(req,");
+       #$self->pidl("\t$state_str);");
+       $self->pidl("NTSTATUS status;");
+       $self->pidl("");
+
+       $self->pidl("status = dcerpc_pipe_handle_pull_recv(subreq);");
+       $self->pidl("TALLOC_FREE(subreq);");
+       $self->pidl("if (tevent_req_nterror(req, status)) {");
+       $self->indent;
+       $self->pidl("return;");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+
+       $self->pidl("tevent_req_done(req);");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+}
+
+sub ParsePipeChunkPull_Recv($$$)
+{
+       my ($self, $t, $cname) = @_;
+
+       my $fn_args = "";
+       my $state_str = "struct dcerpc_$cname\_pull_state";
+       my $fn_str = "NTSTATUS dcerpc_$cname\_pull_recv";
+       my $pad = genpad($fn_str);
+
+       $fn_args .= "struct tevent_req *req";
+       $fn_args .= ",\n" . $pad . "TALLOC_CTX *mem_ctx";
+       $fn_args .= ",\n" . $pad . "struct $cname **chunk";
+
+       $self->fn_declare("$fn_str($fn_args)");
+       $self->pidl("{");
+       $self->indent;
+       $self->pidl("$state_str *state =");
+       $self->pidl("\ttevent_req_data(req,");
+       $self->pidl("\t$state_str);");
+       $self->pidl("NTSTATUS status;");
+       $self->pidl("");
+
+       $self->pidl("if (tevent_req_is_nterror(req, &status)) {");
+       $self->indent;
+       $self->pidl("tevent_req_received(req);");
+       $self->pidl("return status;");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+
+       $self->pidl("*chunk = talloc_move(mem_ctx, &state->chunk);");
+       $self->pidl("");
+
+       $self->pidl("tevent_req_received(req);");
+       $self->pidl("return NT_STATUS_OK;");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("");
+}
+
+sub ParsePipe($$)
+{
+       my ($self, $t) = @_;
+
+       my $pipe = $t;
+       $pipe = $t->{DATA} if ($t->{TYPE} eq "TYPEDEF");
+       my $struct = $pipe->{DATA};
+
+       my $name = "$struct->{NAME}";
+
+       $self->ParsePipeCreate($t, $name);
+
+       $self->ParsePipeChunkPush_State($t, $name);
+       $self->ParsePipeChunkPush_Send($t, $name);
+       $self->ParsePipeChunkPush_Done($t, $name);
+       $self->ParsePipeChunkPush_Recv($t, $name);
+
+       $self->ParsePipeChunkPull_State($t, $name);
+       $self->ParsePipeChunkPull_Send($t, $name);
+       $self->ParsePipeChunkPull_Done($t, $name);
+       $self->ParsePipeChunkPull_Recv($t, $name);
+       $self->pidl_both("");
+}
+
 my %done;
 
 #####################################################################
@@ -843,6 +1273,13 @@ sub ParseInterface($$)
        $self->pidl("/* $if->{NAME} - client functions generated by pidl */");
        $self->pidl("");
 
+       foreach my $t (reverse @{$if->{TYPES}}) {
+               next unless Parse::Pidl::Typelist::typeIs($t, "PIPE");
+
+               $self->ParsePipe($t);
+               $done{$t->{NAME}} = 1;
+       }
+
        foreach my $fn (@{$if->{FUNCTIONS}}) {
                next if defined($done{$fn->{NAME}});
                next if has_property($fn, "noopnum");