8e7cf40d9026924f24a20008a0c68fc61cb7d4c9
[ira/wip.git] / source / pidl / lib / Parse / Pidl / NDR.pm
1 ###################################################
2 # Samba4 NDR info tree generator
3 # Copyright tridge@samba.org 2000-2003
4 # Copyright tpot@samba.org 2001
5 # Copyright jelmer@samba.org 2004-2006
6 # released under the GNU GPL
7
8 =pod
9
10 =head1 NAME
11
12 Parse::Pidl::NDR - NDR parsing information generator
13
14 =head1 DESCRIPTION
15
16 Return a table describing the order in which the parts of an element
17 should be parsed
18 Possible level types:
19  - POINTER
20  - ARRAY
21  - SUBCONTEXT
22  - SWITCH
23  - DATA
24
25 =head1 AUTHOR
26
27 Jelmer Vernooij <jelmer@samba.org>
28
29 =cut
30
31 package Parse::Pidl::NDR;
32
33 require Exporter;
34 use vars qw($VERSION);
35 $VERSION = '0.01';
36 @ISA = qw(Exporter);
37 @EXPORT = qw(GetPrevLevel GetNextLevel ContainsDeferred ContainsString);
38 @EXPORT_OK = qw(GetElementLevelTable ParseElement ValidElement align_type mapToScalar ParseType can_contain_deferred);
39
40 use strict;
41 use Parse::Pidl qw(warning fatal);
42 use Parse::Pidl::Typelist qw(hasType getType expandAlias);
43 use Parse::Pidl::Util qw(has_property property_matches);
44
45 # Alignment of the built-in scalar types
46 my $scalar_alignment = {
47         'void' => 0,
48         'char' => 1,
49         'int8' => 1,
50         'uint8' => 1,
51         'int16' => 2,
52         'uint16' => 2,
53         'int32' => 4,
54         'uint32' => 4,
55         'hyper' => 8,
56         'pointer' => 8,
57         'dlong' => 4,
58         'udlong' => 4,
59         'udlongr' => 4,
60         'DATA_BLOB' => 4,
61         'string' => 4,
62         'string_array' => 4, #???
63         'time_t' => 4,
64         'NTTIME' => 4,
65         'NTTIME_1sec' => 4,
66         'NTTIME_hyper' => 8,
67         'WERROR' => 4,
68         'NTSTATUS' => 4,
69         'COMRESULT' => 4,
70         'nbt_string' => 4,
71         'wrepl_nbt_name' => 4,
72         'ipv4address' => 4
73 };
74
75 sub GetElementLevelTable($)
76 {
77         my $e = shift;
78
79         my $order = [];
80         my $is_deferred = 0;
81         my @bracket_array = ();
82         my @length_is = ();
83         my @size_is = ();
84         my $pointer_idx = 0;
85
86         if (has_property($e, "size_is")) {
87                 @size_is = split /,/, has_property($e, "size_is");
88         }
89
90         if (has_property($e, "length_is")) {
91                 @length_is = split /,/, has_property($e, "length_is");
92         }
93
94         if (defined($e->{ARRAY_LEN})) {
95                 @bracket_array = @{$e->{ARRAY_LEN}};
96         }
97
98         if (has_property($e, "out")) {
99                 my $needptrs = 1;
100
101                 if (has_property($e, "string")) { $needptrs++; }
102                 if ($#bracket_array >= 0) { $needptrs = 0; }
103
104                 warning($e, "[out] argument `$e->{NAME}' not a pointer") if ($needptrs > $e->{POINTERS});
105         }
106
107         # Parse the [][][][] style array stuff
108         for my $i (0 .. $#bracket_array) {
109                 my $d = $bracket_array[$#bracket_array - $i];
110                 my $size = $d;
111                 my $length = $d;
112                 my $is_surrounding = 0;
113                 my $is_varying = 0;
114                 my $is_conformant = 0;
115                 my $is_string = 0;
116                 my $is_fixed = 0;
117                 my $is_inline = 0;
118
119                 if ($d eq "*") {
120                         $is_conformant = 1;
121                         if ($size = shift @size_is) {
122                         } elsif ((scalar(@size_is) == 0) and has_property($e, "string")) {
123                                 $is_string = 1;
124                                 delete($e->{PROPERTIES}->{string});
125                         } else {
126                                 fatal($e, "Must specify size_is() for conformant array!")
127                         }
128
129                         if (($length = shift @length_is) or $is_string) {
130                                 $is_varying = 1;
131                         } else {
132                                 $length = $size;
133                         }
134
135                         if ($e == $e->{PARENT}->{ELEMENTS}[-1] 
136                                 and $e->{PARENT}->{TYPE} ne "FUNCTION") {
137                                 $is_surrounding = 1;
138                         }
139                 }
140
141                 $is_fixed = 1 if (not $is_conformant and Parse::Pidl::Util::is_constant($size));
142                 $is_inline = 1 if (not $is_conformant and not Parse::Pidl::Util::is_constant($size));
143
144                 push (@$order, {
145                         TYPE => "ARRAY",
146                         SIZE_IS => $size,
147                         LENGTH_IS => $length,
148                         IS_DEFERRED => $is_deferred,
149                         IS_SURROUNDING => $is_surrounding,
150                         IS_ZERO_TERMINATED => $is_string,
151                         IS_VARYING => $is_varying,
152                         IS_CONFORMANT => $is_conformant,
153                         IS_FIXED => $is_fixed,
154                         IS_INLINE => $is_inline
155                 });
156         }
157
158         # Next, all the pointers
159         foreach my $i (1..$e->{POINTERS}) {
160                 my $pt = pointer_type($e);
161
162                 my $level = "EMBEDDED";
163                 # Top level "ref" pointers do not have a referrent identifier
164                 $level = "TOP" if ( defined($pt) 
165                                 and $i == 1
166                                 and $e->{PARENT}->{TYPE} eq "FUNCTION");
167
168                 push (@$order, { 
169                         TYPE => "POINTER",
170                         # for now, there can only be one pointer type per element
171                         POINTER_TYPE => pointer_type($e),
172                         POINTER_INDEX => $pointer_idx,
173                         IS_DEFERRED => "$is_deferred",
174                         LEVEL => $level
175                 });
176
177                 warning($e, "top-level \[out\] pointer `$e->{NAME}' is not a \[ref\] pointer") 
178                         if ($i == 1 and pointer_type($e) ne "ref" and 
179                                 $e->{PARENT}->{TYPE} eq "FUNCTION" and 
180                                 not has_property($e, "in"));
181
182                 $pointer_idx++;
183                 
184                 # everything that follows will be deferred
185                 $is_deferred = 1 if ($e->{PARENT}->{TYPE} ne "FUNCTION");
186
187                 my $array_size = shift @size_is;
188                 my $array_length;
189                 my $is_varying;
190                 my $is_conformant;
191                 my $is_string = 0;
192                 if ($array_size) {
193                         $is_conformant = 1;
194                         if ($array_length = shift @length_is) {
195                                 $is_varying = 1;
196                         } else {
197                                 $array_length = $array_size;
198                                 $is_varying =0;
199                         }
200                 } 
201                 
202                 if (scalar(@size_is) == 0 and has_property($e, "string") and 
203                     $i == $e->{POINTERS}) {
204                         $is_string = 1;
205                         $is_varying = $is_conformant = has_property($e, "noheader")?0:1;
206                         delete($e->{PROPERTIES}->{string});
207                 }
208
209                 if ($array_size or $is_string) {
210                         push (@$order, {
211                                 TYPE => "ARRAY",
212                                 SIZE_IS => $array_size,
213                                 LENGTH_IS => $array_length,
214                                 IS_DEFERRED => $is_deferred,
215                                 IS_SURROUNDING => 0,
216                                 IS_ZERO_TERMINATED => $is_string,
217                                 IS_VARYING => $is_varying,
218                                 IS_CONFORMANT => $is_conformant,
219                                 IS_FIXED => 0,
220                                 IS_INLINE => 0
221                         });
222
223                         $is_deferred = 0;
224                 } 
225         }
226
227         if (defined(has_property($e, "subcontext"))) {
228                 my $hdr_size = has_property($e, "subcontext");
229                 my $subsize = has_property($e, "subcontext_size");
230                 if (not defined($subsize)) { 
231                         $subsize = -1; 
232                 }
233                 
234                 push (@$order, {
235                         TYPE => "SUBCONTEXT",
236                         HEADER_SIZE => $hdr_size,
237                         SUBCONTEXT_SIZE => $subsize,
238                         IS_DEFERRED => $is_deferred,
239                         COMPRESSION => has_property($e, "compression"),
240                 });
241         }
242
243         if (my $switch = has_property($e, "switch_is")) {
244                 push (@$order, {
245                         TYPE => "SWITCH", 
246                         SWITCH_IS => $switch,
247                         IS_DEFERRED => $is_deferred
248                 });
249         }
250
251         if (scalar(@size_is) > 0) {
252                 fatal($e, "size_is() on non-array element");
253         }
254
255         if (scalar(@length_is) > 0) {
256                 fatal($e, "length_is() on non-array element");
257         }
258
259         if (has_property($e, "string")) {
260                 fatal($e, "string() attribute on non-array element");
261         }
262
263         push (@$order, {
264                 TYPE => "DATA",
265                 DATA_TYPE => $e->{TYPE},
266                 IS_DEFERRED => $is_deferred,
267                 CONTAINS_DEFERRED => can_contain_deferred($e->{TYPE}),
268                 IS_SURROUNDING => 0 #FIXME
269         });
270
271         my $i = 0;
272         foreach (@$order) { $_->{LEVEL_INDEX} = $i; $i+=1; }
273
274         return $order;
275 }
276
277 #####################################################################
278 # see if a type contains any deferred data 
279 sub can_contain_deferred($)
280 {
281         sub can_contain_deferred($);
282         my ($type) = @_;
283
284         return 1 unless (hasType($type)); # assume the worst
285
286         $type = getType($type);
287
288         return 0 if (Parse::Pidl::Typelist::is_scalar($type));
289
290         return can_contain_deferred($type->{DATA}) if ($type->{TYPE} eq "TYPEDEF");
291
292         return 0 unless defined($type->{ELEMENTS});
293
294         foreach (@{$type->{ELEMENTS}}) {
295                 return 1 if ($_->{POINTERS});
296                 return 1 if (can_contain_deferred ($_->{TYPE}));
297         }
298         
299         return 0;
300 }
301
302 sub pointer_type($)
303 {
304         my $e = shift;
305
306         return undef unless $e->{POINTERS};
307         
308         return "ref" if (has_property($e, "ref"));
309         return "full" if (has_property($e, "ptr"));
310         return "sptr" if (has_property($e, "sptr"));
311         return "unique" if (has_property($e, "unique"));
312         return "relative" if (has_property($e, "relative"));
313         return "ignore" if (has_property($e, "ignore"));
314
315         return undef;
316 }
317
318 #####################################################################
319 # work out the correct alignment for a structure or union
320 sub find_largest_alignment($)
321 {
322         my $s = shift;
323
324         my $align = 1;
325         for my $e (@{$s->{ELEMENTS}}) {
326                 my $a = 1;
327
328                 if ($e->{POINTERS}) {
329                         $a = 4; 
330                 } elsif (has_property($e, "subcontext")) { 
331                         $a = 1;
332                 } elsif (has_property($e, "transmit_as")) {
333                         $a = align_type($e->{PROPERTIES}->{transmit_as});
334                 } else {
335                         $a = align_type($e->{TYPE}); 
336                 }
337
338                 $align = $a if ($align < $a);
339         }
340
341         return $align;
342 }
343
344 #####################################################################
345 # align a type
346 sub align_type($)
347 {
348         sub align_type($);
349         my ($e) = @_;
350
351         if (ref($e) eq "HASH" and $e->{TYPE} eq "SCALAR") {
352                 return $scalar_alignment->{$e->{NAME}};
353         }
354
355         unless (hasType($e)) {
356             # it must be an external type - all we can do is guess 
357                 # print "Warning: assuming alignment of unknown type '$e' is 4\n";
358             return 4;
359         }
360
361         my $dt = getType($e);
362
363         if ($dt->{TYPE} eq "TYPEDEF") {
364                 return align_type($dt->{DATA});
365         } elsif ($dt->{TYPE} eq "ENUM") {
366                 return align_type(Parse::Pidl::Typelist::enum_type_fn($dt));
367         } elsif ($dt->{TYPE} eq "BITMAP") {
368                 return align_type(Parse::Pidl::Typelist::bitmap_type_fn($dt));
369         } elsif (($dt->{TYPE} eq "STRUCT") or ($dt->{TYPE} eq "UNION")) {
370                 return find_largest_alignment($dt);
371         }
372
373         die("Unknown data type type $dt->{TYPE}");
374 }
375
376 sub ParseElement($$)
377 {
378         my ($e, $pointer_default) = @_;
379
380         $e->{TYPE} = expandAlias($e->{TYPE});
381
382         if (ref($e->{TYPE}) eq "HASH") {
383                 $e->{TYPE} = ParseType($e->{TYPE}, $pointer_default);
384         }
385
386         return {
387                 NAME => $e->{NAME},
388                 TYPE => $e->{TYPE},
389                 PROPERTIES => $e->{PROPERTIES},
390                 LEVELS => GetElementLevelTable($e),
391                 REPRESENTATION_TYPE => ($e->{PROPERTIES}->{represent_as} or $e->{TYPE}),
392                 ALIGN => align_type($e->{TYPE}),
393                 ORIGINAL => $e
394         };
395 }
396
397 sub ParseStruct($$)
398 {
399         my ($struct, $pointer_default) = @_;
400         my @elements = ();
401         my $surrounding = undef;
402
403         return {
404                 TYPE => "STRUCT",
405                 NAME => $struct->{NAME},
406                 SURROUNDING_ELEMENT => undef,
407                 ELEMENTS => undef,
408                 PROPERTIES => $struct->{PROPERTIES},
409                 ORIGINAL => $struct,
410                 ALIGN => undef
411         } unless defined($struct->{ELEMENTS});
412
413         CheckPointerTypes($struct, $pointer_default);
414
415         foreach my $x (@{$struct->{ELEMENTS}}) 
416         {
417                 my $e = ParseElement($x, $pointer_default);
418                 if ($x != $struct->{ELEMENTS}[-1] and 
419                         $e->{LEVELS}[0]->{IS_SURROUNDING}) {
420                         fatal($x, "conformant member not at end of struct");
421                 }
422                 push @elements, $e;
423         }
424
425         my $e = $elements[-1];
426         if (defined($e) and defined($e->{LEVELS}[0]->{IS_SURROUNDING}) and
427                 $e->{LEVELS}[0]->{IS_SURROUNDING}) {
428                 $surrounding = $e;
429         }
430
431         if (defined $e->{TYPE} && $e->{TYPE} eq "string"
432             &&  property_matches($e, "flag", ".*LIBNDR_FLAG_STR_CONFORMANT.*")) {
433                 $surrounding = $struct->{ELEMENTS}[-1];
434         }
435
436         my $align = undef;
437         if ($struct->{NAME}) {
438                 $align = align_type($struct->{NAME});
439         }
440                 
441         return {
442                 TYPE => "STRUCT",
443                 NAME => $struct->{NAME},
444                 SURROUNDING_ELEMENT => $surrounding,
445                 ELEMENTS => \@elements,
446                 PROPERTIES => $struct->{PROPERTIES},
447                 ORIGINAL => $struct,
448                 ALIGN => $align
449         };
450 }
451
452 sub ParseUnion($$)
453 {
454         my ($e, $pointer_default) = @_;
455         my @elements = ();
456         my $hasdefault = 0;
457         my $switch_type = has_property($e, "switch_type");
458         unless (defined($switch_type)) { $switch_type = "uint32"; }
459         if (has_property($e, "nodiscriminant")) { $switch_type = undef; }
460
461         return {
462                 TYPE => "UNION",
463                 NAME => $e->{NAME},
464                 SWITCH_TYPE => $switch_type,
465                 ELEMENTS => undef,
466                 PROPERTIES => $e->{PROPERTIES},
467                 HAS_DEFAULT => $hasdefault,
468                 ORIGINAL => $e
469         } unless defined($e->{ELEMENTS});
470
471         CheckPointerTypes($e, $pointer_default);
472
473         foreach my $x (@{$e->{ELEMENTS}}) 
474         {
475                 my $t;
476                 if ($x->{TYPE} eq "EMPTY") {
477                         $t = { TYPE => "EMPTY" };
478                 } else {
479                         $t = ParseElement($x, $pointer_default);
480                 }
481                 if (has_property($x, "default")) {
482                         $t->{CASE} = "default";
483                         $hasdefault = 1;
484                 } elsif (defined($x->{PROPERTIES}->{case})) {
485                         $t->{CASE} = "case $x->{PROPERTIES}->{case}";
486                 } else {
487                         die("Union element $x->{NAME} has neither default nor case property");
488                 }
489                 push @elements, $t;
490         }
491
492         return {
493                 TYPE => "UNION",
494                 NAME => $e->{NAME},
495                 SWITCH_TYPE => $switch_type,
496                 ELEMENTS => \@elements,
497                 PROPERTIES => $e->{PROPERTIES},
498                 HAS_DEFAULT => $hasdefault,
499                 ORIGINAL => $e
500         };
501 }
502
503 sub ParseEnum($$)
504 {
505         my ($e, $pointer_default) = @_;
506
507         return {
508                 TYPE => "ENUM",
509                 NAME => $e->{NAME},
510                 BASE_TYPE => Parse::Pidl::Typelist::enum_type_fn($e),
511                 ELEMENTS => $e->{ELEMENTS},
512                 PROPERTIES => $e->{PROPERTIES},
513                 ORIGINAL => $e
514         };
515 }
516
517 sub ParseBitmap($$)
518 {
519         my ($e, $pointer_default) = @_;
520
521         return {
522                 TYPE => "BITMAP",
523                 NAME => $e->{NAME},
524                 BASE_TYPE => Parse::Pidl::Typelist::bitmap_type_fn($e),
525                 ELEMENTS => $e->{ELEMENTS},
526                 PROPERTIES => $e->{PROPERTIES},
527                 ORIGINAL => $e
528         };
529 }
530
531 sub ParseType($$)
532 {
533         my ($d, $pointer_default) = @_;
534
535         my $data = {
536                 STRUCT => \&ParseStruct,
537                 UNION => \&ParseUnion,
538                 ENUM => \&ParseEnum,
539                 BITMAP => \&ParseBitmap,
540                 TYPEDEF => \&ParseTypedef,
541         }->{$d->{TYPE}}->($d, $pointer_default);
542
543         return $data;
544 }
545
546 sub ParseTypedef($$)
547 {
548         my ($d, $pointer_default) = @_;
549
550         if (defined($d->{DATA}->{PROPERTIES}) && !defined($d->{PROPERTIES})) {
551                 $d->{PROPERTIES} = $d->{DATA}->{PROPERTIES};
552         }
553
554         my $data = ParseType($d->{DATA}, $pointer_default);
555         $data->{ALIGN} = align_type($d->{NAME});
556
557         return {
558                 NAME => $d->{NAME},
559                 TYPE => $d->{TYPE},
560                 PROPERTIES => $d->{PROPERTIES},
561                 DATA => $data,
562                 ORIGINAL => $d
563         };
564 }
565
566 sub ParseConst($$)
567 {
568         my ($ndr,$d) = @_;
569
570         return $d;
571 }
572
573 sub ParseFunction($$$)
574 {
575         my ($ndr,$d,$opnum) = @_;
576         my @elements = ();
577         my $rettype = undef;
578         my $thisopnum = undef;
579
580         CheckPointerTypes($d, $ndr->{PROPERTIES}->{pointer_default_top});
581
582         if (not defined($d->{PROPERTIES}{noopnum})) {
583                 $thisopnum = ${$opnum};
584                 ${$opnum}++;
585         }
586
587         foreach my $x (@{$d->{ELEMENTS}}) {
588                 my $e = ParseElement($x, $ndr->{PROPERTIES}->{pointer_default});
589                 push (@{$e->{DIRECTION}}, "in") if (has_property($x, "in"));
590                 push (@{$e->{DIRECTION}}, "out") if (has_property($x, "out"));
591
592                 push (@elements, $e);
593         }
594
595         if ($d->{RETURN_TYPE} ne "void") {
596                 $rettype = expandAlias($d->{RETURN_TYPE});
597         }
598         
599         my $async = 0;
600         if (has_property($d, "async")) { $async = 1; }
601         
602         return {
603                         NAME => $d->{NAME},
604                         TYPE => "FUNCTION",
605                         OPNUM => $thisopnum,
606                         ASYNC => $async,
607                         RETURN_TYPE => $rettype,
608                         PROPERTIES => $d->{PROPERTIES},
609                         ELEMENTS => \@elements,
610                         ORIGINAL => $d
611                 };
612 }
613
614 sub CheckPointerTypes($$)
615 {
616         my ($s,$default) = @_;
617
618         return unless defined($s->{ELEMENTS});
619
620         foreach my $e (@{$s->{ELEMENTS}}) {
621                 if ($e->{POINTERS} and not defined(pointer_type($e))) {
622                         $e->{PROPERTIES}->{$default} = 1;
623                 }
624         }
625 }
626
627 sub FindNestedTypes($$)
628 {
629         sub FindNestedTypes($$);
630         my ($l, $t) = @_;
631
632         return if not defined($t->{ELEMENTS});
633         return if ($t->{TYPE} eq "ENUM");
634         return if ($t->{TYPE} eq "BITMAP");
635
636         foreach (@{$t->{ELEMENTS}}) {
637                 if (ref($_->{TYPE}) eq "HASH") {
638                         push (@$l, $_->{TYPE}) if (defined($_->{TYPE}->{NAME}));
639                         FindNestedTypes($l, $_->{TYPE});
640                 }
641         }
642 }
643
644 sub ParseInterface($)
645 {
646         my $idl = shift;
647         my @types = ();
648         my @consts = ();
649         my @functions = ();
650         my @endpoints;
651         my $opnum = 0;
652         my $version;
653
654         if (not has_property($idl, "pointer_default")) {
655                 # MIDL defaults to "ptr" in DCE compatible mode (/osf)
656                 # and "unique" in Microsoft Extensions mode (default)
657                 $idl->{PROPERTIES}->{pointer_default} = "unique";
658         }
659
660         if (not has_property($idl, "pointer_default_top")) {
661                 $idl->{PROPERTIES}->{pointer_default_top} = "ref";
662         } else {
663                 warning($idl, "pointer_default_top() is a pidl extension and should not be used");
664         }
665
666         foreach my $d (@{$idl->{DATA}}) {
667                 if ($d->{TYPE} eq "FUNCTION") {
668                         push (@functions, ParseFunction($idl, $d, \$opnum));
669                 } elsif ($d->{TYPE} eq "CONST") {
670                         push (@consts, ParseConst($idl, $d));
671                 } else {
672                         push (@types, ParseType($d, $idl->{PROPERTIES}->{pointer_default}));
673                         FindNestedTypes(\@types, $d);
674                 }
675         }
676
677         $version = "0.0";
678
679         if(defined $idl->{PROPERTIES}->{version}) { 
680                 $version = $idl->{PROPERTIES}->{version}; 
681         }
682
683         # If no endpoint is set, default to the interface name as a named pipe
684         if (!defined $idl->{PROPERTIES}->{endpoint}) {
685                 push @endpoints, "\"ncacn_np:[\\\\pipe\\\\" . $idl->{NAME} . "]\"";
686         } else {
687                 @endpoints = split / /, $idl->{PROPERTIES}->{endpoint};
688         }
689
690         return { 
691                 NAME => $idl->{NAME},
692                 UUID => lc(has_property($idl, "uuid")),
693                 VERSION => $version,
694                 TYPE => "INTERFACE",
695                 PROPERTIES => $idl->{PROPERTIES},
696                 FUNCTIONS => \@functions,
697                 CONSTS => \@consts,
698                 TYPES => \@types,
699                 ENDPOINTS => \@endpoints
700         };
701 }
702
703 # Convert a IDL tree to a NDR tree
704 # Gives a result tree describing all that's necessary for easily generating
705 # NDR parsers / generators
706 sub Parse($)
707 {
708         my $idl = shift;
709
710         return undef unless (defined($idl));
711
712         Parse::Pidl::NDR::Validate($idl);
713         
714         my @ndr = ();
715
716         foreach (@{$idl}) {
717                 ($_->{TYPE} eq "CPP_QUOTE") && push(@ndr, $_);
718                 ($_->{TYPE} eq "INTERFACE") && push(@ndr, ParseInterface($_));
719                 ($_->{TYPE} eq "IMPORT") && push(@ndr, $_);
720         }
721
722         return \@ndr;
723 }
724
725 sub GetNextLevel($$)
726 {
727         my $e = shift;
728         my $fl = shift;
729
730         my $seen = 0;
731
732         foreach my $l (@{$e->{LEVELS}}) {
733                 return $l if ($seen);
734                 ($seen = 1) if ($l == $fl);
735         }
736
737         return undef;
738 }
739
740 sub GetPrevLevel($$)
741 {
742         my ($e,$fl) = @_;
743         my $prev = undef;
744
745         foreach my $l (@{$e->{LEVELS}}) {
746                 (return $prev) if ($l == $fl);
747                 $prev = $l;
748         }
749
750         return undef;
751 }
752
753 sub ContainsString($)
754 {
755         my ($e) = @_;
756
757         foreach my $l (@{$e->{LEVELS}}) {
758                 return 1 if ($l->{TYPE} eq "ARRAY" and $l->{IS_ZERO_TERMINATED});
759         }
760
761         return 0;
762 }
763
764 sub ContainsDeferred($$)
765 {
766         my ($e,$l) = @_;
767
768         return 1 if ($l->{CONTAINS_DEFERRED});
769
770         while ($l = GetNextLevel($e,$l))
771         {
772                 return 1 if ($l->{IS_DEFERRED}); 
773                 return 1 if ($l->{CONTAINS_DEFERRED});
774         } 
775         
776         return 0;
777 }
778
779 sub el_name($)
780 {
781         my $e = shift;
782
783         if ($e->{PARENT} && $e->{PARENT}->{NAME}) {
784                 return "$e->{PARENT}->{NAME}.$e->{NAME}";
785         }
786
787         if ($e->{PARENT} && $e->{PARENT}->{PARENT}->{NAME}) {
788                 return "$e->{PARENT}->{PARENT}->{NAME}.$e->{NAME}";
789         }
790
791         if ($e->{PARENT}) {
792                 return "$e->{PARENT}->{NAME}.$e->{NAME}";
793         }
794
795         return $e->{NAME};
796 }
797
798 ###################################
799 # find a sibling var in a structure
800 sub find_sibling($$)
801 {
802         my($e,$name) = @_;
803         my($fn) = $e->{PARENT};
804
805         if ($name =~ /\*(.*)/) {
806                 $name = $1;
807         }
808
809         for my $e2 (@{$fn->{ELEMENTS}}) {
810                 return $e2 if ($e2->{NAME} eq $name);
811         }
812
813         return undef;
814 }
815
816 my %property_list = (
817         # interface
818         "helpstring"            => ["INTERFACE", "FUNCTION"],
819         "version"               => ["INTERFACE"],
820         "uuid"                  => ["INTERFACE"],
821         "endpoint"              => ["INTERFACE"],
822         "pointer_default"       => ["INTERFACE"],
823         "pointer_default_top"   => ["INTERFACE"],
824         "helper"                => ["INTERFACE"],
825         "authservice"           => ["INTERFACE"],
826
827         # dcom
828         "object"                => ["INTERFACE"],
829         "local"                 => ["INTERFACE", "FUNCTION"],
830         "iid_is"                => ["ELEMENT"],
831         "call_as"               => ["FUNCTION"],
832         "idempotent"            => ["FUNCTION"],
833
834         # function
835         "noopnum"               => ["FUNCTION"],
836         "in"                    => ["ELEMENT"],
837         "out"                   => ["ELEMENT"],
838         "async"                 => ["FUNCTION"],
839
840         # pointer
841         "ref"                   => ["ELEMENT"],
842         "ptr"                   => ["ELEMENT"],
843         "unique"                => ["ELEMENT"],
844         "ignore"                => ["ELEMENT"],
845         "relative"              => ["ELEMENT"],
846         "relative_base"         => ["TYPEDEF"],
847
848         "gensize"               => ["TYPEDEF"],
849         "value"                 => ["ELEMENT"],
850         "flag"                  => ["ELEMENT", "TYPEDEF"],
851
852         # generic
853         "public"                => ["FUNCTION", "TYPEDEF"],
854         "nopush"                => ["FUNCTION", "TYPEDEF"],
855         "nopull"                => ["FUNCTION", "TYPEDEF"],
856         "nosize"                => ["FUNCTION", "TYPEDEF"],
857         "noprint"               => ["FUNCTION", "TYPEDEF"],
858         "noejs"                 => ["FUNCTION", "TYPEDEF"],
859
860         # union
861         "switch_is"             => ["ELEMENT"],
862         "switch_type"           => ["ELEMENT", "TYPEDEF"],
863         "nodiscriminant"        => ["TYPEDEF"],
864         "case"                  => ["ELEMENT"],
865         "default"               => ["ELEMENT"],
866
867         "represent_as"          => ["ELEMENT"],
868         "transmit_as"           => ["ELEMENT"],
869
870         # subcontext
871         "subcontext"            => ["ELEMENT"],
872         "subcontext_size"       => ["ELEMENT"],
873         "compression"           => ["ELEMENT"],
874
875         # enum
876         "enum8bit"              => ["TYPEDEF"],
877         "enum16bit"             => ["TYPEDEF"],
878         "v1_enum"               => ["TYPEDEF"],
879
880         # bitmap
881         "bitmap8bit"            => ["TYPEDEF"],
882         "bitmap16bit"           => ["TYPEDEF"],
883         "bitmap32bit"           => ["TYPEDEF"],
884         "bitmap64bit"           => ["TYPEDEF"],
885
886         # array
887         "range"                 => ["ELEMENT"],
888         "size_is"               => ["ELEMENT"],
889         "string"                => ["ELEMENT"],
890         "noheader"              => ["ELEMENT"],
891         "charset"               => ["ELEMENT"],
892         "length_is"             => ["ELEMENT"],
893 );
894
895 #####################################################################
896 # check for unknown properties
897 sub ValidProperties($$)
898 {
899         my ($e,$t) = @_;
900
901         return unless defined $e->{PROPERTIES};
902
903         foreach my $key (keys %{$e->{PROPERTIES}}) {
904                 warning($e, el_name($e) . ": unknown property '$key'")
905                         unless defined($property_list{$key});
906
907                 fatal($e, el_name($e) . ": property '$key' not allowed on '$t'")
908                         unless grep($t, @{$property_list{$key}});
909         }
910 }
911
912 sub mapToScalar($)
913 {
914         sub mapToScalar($);
915         my $t = shift;
916         return $t->{NAME} if (ref($t) eq "HASH" and $t->{TYPE} eq "SCALAR");
917         my $ti = getType($t);
918
919         if (not defined ($ti)) {
920                 return undef;
921         } elsif ($ti->{TYPE} eq "TYPEDEF") {
922                 return mapToScalar($ti->{DATA});
923         } elsif ($ti->{TYPE} eq "ENUM") {
924                 return Parse::Pidl::Typelist::enum_type_fn($ti);
925         } elsif ($ti->{TYPE} eq "BITMAP") {
926                 return Parse::Pidl::Typelist::bitmap_type_fn($ti);
927         }
928
929         return undef;
930 }
931
932 #####################################################################
933 # validate an element
934 sub ValidElement($)
935 {
936         my $e = shift;
937
938         ValidProperties($e,"ELEMENT");
939
940         # Check whether switches are used correctly.
941         if (my $switch = has_property($e, "switch_is")) {
942                 my $e2 = find_sibling($e, $switch);
943                 my $type = getType($e->{TYPE});
944
945                 if (defined($type) and $type->{DATA}->{TYPE} ne "UNION") {
946                         fatal($e, el_name($e) . ": switch_is() used on non-union type $e->{TYPE} which is a $type->{DATA}->{TYPE}");
947                 }
948
949                 if (not has_property($type->{DATA}, "nodiscriminant") and defined($e2)) {
950                         my $discriminator_type = has_property($type->{DATA}, "switch_type");
951                         $discriminator_type = "uint32" unless defined ($discriminator_type);
952
953                         my $t1 = mapToScalar($discriminator_type);
954
955                         if (not defined($t1)) {
956                                 fatal($e, el_name($e) . ": unable to map discriminator type '$discriminator_type' to scalar");
957                         }
958
959                         my $t2 = mapToScalar($e2->{TYPE});
960                         if (not defined($t2)) {
961                                 fatal($e, el_name($e) . ": unable to map variable used for switch_is() to scalar");
962                         }
963
964                         if ($t1 ne $t2) {
965                                 warning($e, el_name($e) . ": switch_is() is of type $e2->{TYPE} ($t2), while discriminator type for union $type->{NAME} is $discriminator_type ($t1)");
966                         }
967                 }
968         }
969
970         if (has_property($e, "subcontext") and has_property($e, "represent_as")) {
971                 fatal($e, el_name($e) . " : subcontext() and represent_as() can not be used on the same element");
972         }
973
974         if (has_property($e, "subcontext") and has_property($e, "transmit_as")) {
975                 fatal($e, el_name($e) . " : subcontext() and transmit_as() can not be used on the same element");
976         }
977
978         if (has_property($e, "represent_as") and has_property($e, "transmit_as")) {
979                 fatal($e, el_name($e) . " : represent_as() and transmit_as() can not be used on the same element");
980         }
981
982         if (has_property($e, "represent_as") and has_property($e, "value")) {
983                 fatal($e, el_name($e) . " : represent_as() and value() can not be used on the same element");
984         }
985
986         if (has_property($e, "subcontext")) {
987                 warning($e, "subcontext() is deprecated. Use represent_as() or transmit_as() instead");
988         }
989
990         if (defined (has_property($e, "subcontext_size")) and not defined(has_property($e, "subcontext"))) {
991                 fatal($e, el_name($e) . " : subcontext_size() on non-subcontext element");
992         }
993
994         if (defined (has_property($e, "compression")) and not defined(has_property($e, "subcontext"))) {
995                 fatal($e, el_name($e) . " : compression() on non-subcontext element");
996         }
997
998         if (!$e->{POINTERS} && (
999                 has_property($e, "ptr") or
1000                 has_property($e, "unique") or
1001                 has_property($e, "relative") or
1002                 has_property($e, "ref"))) {
1003                 fatal($e, el_name($e) . " : pointer properties on non-pointer element\n");      
1004         }
1005 }
1006
1007 #####################################################################
1008 # validate an enum
1009 sub ValidEnum($)
1010 {
1011         my ($enum) = @_;
1012
1013         ValidProperties($enum, "ENUM");
1014 }
1015
1016 #####################################################################
1017 # validate a bitmap
1018 sub ValidBitmap($)
1019 {
1020         my ($bitmap) = @_;
1021
1022         ValidProperties($bitmap, "BITMAP");
1023 }
1024
1025 #####################################################################
1026 # validate a struct
1027 sub ValidStruct($)
1028 {
1029         my($struct) = shift;
1030
1031         ValidProperties($struct, "STRUCT");
1032
1033         return unless defined($struct->{ELEMENTS});
1034
1035         foreach my $e (@{$struct->{ELEMENTS}}) {
1036                 $e->{PARENT} = $struct;
1037                 ValidElement($e);
1038         }
1039 }
1040
1041 #####################################################################
1042 # parse a union
1043 sub ValidUnion($)
1044 {
1045         my($union) = shift;
1046
1047         ValidProperties($union,"UNION");
1048
1049         if (has_property($union->{PARENT}, "nodiscriminant") and has_property($union->{PARENT}, "switch_type")) {
1050                 fatal($union->{PARENT}, $union->{PARENT}->{NAME} . ": switch_type() on union without discriminant");
1051         }
1052
1053         return unless defined($union->{ELEMENTS});
1054
1055         foreach my $e (@{$union->{ELEMENTS}}) {
1056                 $e->{PARENT} = $union;
1057
1058                 if (defined($e->{PROPERTIES}->{default}) and 
1059                         defined($e->{PROPERTIES}->{case})) {
1060                         fatal($e, "Union member $e->{NAME} can not have both default and case properties!");
1061                 }
1062                 
1063                 unless (defined ($e->{PROPERTIES}->{default}) or 
1064                                 defined ($e->{PROPERTIES}->{case})) {
1065                         fatal($e, "Union member $e->{NAME} must have default or case property");
1066                 }
1067
1068                 if (has_property($e, "ref")) {
1069                         fatal($e, el_name($e) . " : embedded ref pointers are not supported yet\n");
1070                 }
1071
1072
1073                 ValidElement($e);
1074         }
1075 }
1076
1077 #####################################################################
1078 # parse a typedef
1079 sub ValidTypedef($)
1080 {
1081         my($typedef) = shift;
1082         my $data = $typedef->{DATA};
1083
1084         ValidProperties($typedef, "TYPEDEF");
1085
1086         $data->{PARENT} = $typedef;
1087
1088         ValidType($data) if (ref($data) eq "HASH");
1089 }
1090
1091 #####################################################################
1092 # validate a function
1093 sub ValidFunction($)
1094 {
1095         my($fn) = shift;
1096
1097         ValidProperties($fn,"FUNCTION");
1098
1099         foreach my $e (@{$fn->{ELEMENTS}}) {
1100                 $e->{PARENT} = $fn;
1101                 if (has_property($e, "ref") && !$e->{POINTERS}) {
1102                         fatal($e, "[ref] variables must be pointers ($fn->{NAME}/$e->{NAME})");
1103                 }
1104                 ValidElement($e);
1105         }
1106 }
1107
1108 #####################################################################
1109 # validate a type
1110 sub ValidType($)
1111 {
1112         my ($t) = @_;
1113
1114         { 
1115                 TYPEDEF => \&ValidTypedef,
1116                 STRUCT => \&ValidStruct,
1117                 UNION => \&ValidUnion,
1118                 ENUM => \&ValidEnum,
1119                 BITMAP => \&ValidBitmap
1120         }->{$t->{TYPE}}->($t);
1121 }
1122
1123 #####################################################################
1124 # parse the interface definitions
1125 sub ValidInterface($)
1126 {
1127         my($interface) = shift;
1128         my($data) = $interface->{DATA};
1129
1130         if (has_property($interface, "helper")) {
1131                 warning($interface, "helper() is pidl-specific and deprecated. Use `include' instead");
1132         }
1133
1134         ValidProperties($interface,"INTERFACE");
1135
1136         if (has_property($interface, "pointer_default")) {
1137                 if (not grep (/$interface->{PROPERTIES}->{pointer_default}/, 
1138                                         ("ref", "unique", "ptr"))) {
1139                         fatal($interface, "Unknown default pointer type `$interface->{PROPERTIES}->{pointer_default}'");
1140                 }
1141         }
1142
1143         if (has_property($interface, "object")) {
1144                 if (has_property($interface, "version") && 
1145                         $interface->{PROPERTIES}->{version} != 0) {
1146                         fatal($interface, "Object interfaces must have version 0.0 ($interface->{NAME})");
1147                 }
1148
1149                 if (!defined($interface->{BASE}) && 
1150                         not ($interface->{NAME} eq "IUnknown")) {
1151                         fatal($interface, "Object interfaces must all derive from IUnknown ($interface->{NAME})");
1152                 }
1153         }
1154                 
1155         foreach my $d (@{$data}) {
1156                 ($d->{TYPE} eq "FUNCTION") && ValidFunction($d);
1157                 ($d->{TYPE} eq "TYPEDEF" or 
1158                  $d->{TYPE} eq "STRUCT" or
1159                  $d->{TYPE} eq "UNION" or 
1160                  $d->{TYPE} eq "ENUM" or
1161                  $d->{TYPE} eq "BITMAP") && ValidType($d);
1162         }
1163
1164 }
1165
1166 #####################################################################
1167 # Validate an IDL structure
1168 sub Validate($)
1169 {
1170         my($idl) = shift;
1171
1172         foreach my $x (@{$idl}) {
1173                 ($x->{TYPE} eq "INTERFACE") && 
1174                     ValidInterface($x);
1175                 ($x->{TYPE} eq "IMPORTLIB") &&
1176                         fatal($x, "importlib() not supported");
1177         }
1178 }
1179
1180 1;