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