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