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