All prototypes should call didBecomePrototype()
[WebKit.git] / Source / WebCore / bindings / scripts / CodeGenerator.pm
1 #
2 # WebKit IDL parser
3 #
4 # Copyright (C) 2005 Nikolas Zimmermann <wildfox@kde.org>
5 # Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com>
6 # Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
7 # Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au>
8 # Copyright (C) Research In Motion Limited 2010. All rights reserved.
9 # Copyright (C) 2013 Samsung Electronics. All rights reserved.
10 #
11 # This library is free software; you can redistribute it and/or
12 # modify it under the terms of the GNU Library General Public
13 # License as published by the Free Software Foundation; either
14 # version 2 of the License, or (at your option) any later version.
15 #
16 # This library is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 # Library General Public License for more details.
20 #
21 # You should have received a copy of the GNU Library General Public License
22 # along with this library; see the file COPYING.LIB.  If not, write to
23 # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 # Boston, MA 02110-1301, USA.
25 #
26
27 package CodeGenerator;
28
29 use strict;
30
31 use File::Basename;
32 use File::Find;
33 use Carp qw<longmess>;
34 use Data::Dumper;
35
36 my $useDocument = "";
37 my $useGenerator = "";
38 my $useOutputDir = "";
39 my $useOutputHeadersDir = "";
40 my $useDirectories = "";
41 my $preprocessor;
42 my $idlAttributes;
43 my $writeDependencies = 0;
44 my $defines = "";
45 my $targetIdlFilePath = "";
46
47 my $codeGenerator = 0;
48
49 my $verbose = 0;
50
51 my %integerTypeHash = (
52     "byte" => 1,
53     "long long" => 1,
54     "long" => 1,
55     "octet" => 1,
56     "short" => 1,
57     "unsigned long long" => 1,
58     "unsigned long" => 1,
59     "unsigned short" => 1,
60 );
61
62 my %floatingPointTypeHash = (
63     "float" => 1,
64     "unrestricted float" => 1,
65     "double" => 1,
66     "unrestricted double" => 1,
67 );
68
69 my %stringTypeHash = (
70     "ByteString" => 1,
71     "DOMString" => 1,
72     "USVString" => 1,
73 );
74
75 my %bufferSourceTypes = (
76     "ArrayBuffer" => 1,
77     "ArrayBufferView" => 1,
78     "DataView" => 1,
79     "Float32Array" => 1,
80     "Float64Array" => 1,
81     "Int16Array" => 1,
82     "Int32Array" => 1,
83     "Int8Array" => 1,
84     "Uint16Array" => 1,
85     "Uint32Array" => 1,
86     "Uint8Array" => 1,
87     "Uint8ClampedArray" => 1,
88 );
89
90 my %primitiveTypeHash = ( 
91     "boolean" => 1, 
92     "void" => 1,
93     "Date" => 1
94 );
95
96 my %dictionaryTypeImplementationNameOverrides = ();
97 my %enumTypeImplementationNameOverrides = ();
98
99 my %svgAttributesInHTMLHash = (
100     "class" => 1,
101     "id" => 1,
102     "onabort" => 1,
103     "onclick" => 1,
104     "onerror" => 1,
105     "onload" => 1,
106     "onmousedown" => 1,
107     "onmouseenter" => 1,
108     "onmouseleave" => 1,
109     "onmousemove" => 1,
110     "onmouseout" => 1,
111     "onmouseover" => 1,
112     "onmouseup" => 1,
113     "onresize" => 1,
114     "onscroll" => 1,
115     "onunload" => 1,
116 );
117
118 # Cache of IDL file pathnames.
119 my $idlFiles;
120 my $cachedInterfaces = {};
121 my $cachedExternalDictionaries = {};
122 my $cachedExternalEnumerations = {};
123 my $cachedTypes = {};
124
125 sub assert
126 {
127     my $message = shift;
128     
129     my $mess = longmess();
130     print Dumper($mess);
131
132     die $message;
133 }
134
135 # Default constructor
136 sub new
137 {
138     my $object = shift;
139     my $reference = { };
140
141     $useDirectories = shift;
142     $useGenerator = shift;
143     $useOutputDir = shift;
144     $useOutputHeadersDir = shift;
145     $preprocessor = shift;
146     $writeDependencies = shift;
147     $verbose = shift;
148     $targetIdlFilePath = shift;
149     $idlAttributes = shift;
150
151     bless($reference, $object);
152     return $reference;
153 }
154
155 sub ProcessDocument
156 {
157     my $object = shift;
158     $useDocument = shift;
159     $defines = shift;
160
161     my $ifaceName = "CodeGenerator" . $useGenerator;
162     require $ifaceName . ".pm";
163
164     foreach my $dictionary (@{$useDocument->dictionaries}) {
165         if ($dictionary->extendedAttributes->{"ImplementedAs"}) {
166             $dictionaryTypeImplementationNameOverrides{$dictionary->type->name} = $dictionary->extendedAttributes->{"ImplementedAs"};
167         }
168     }
169
170     foreach my $enumeration (@{$useDocument->enumerations}) {
171         if ($enumeration->extendedAttributes->{"ImplementedAs"}) {
172             $enumTypeImplementationNameOverrides{$enumeration->type->name} = $enumeration->extendedAttributes->{"ImplementedAs"};
173         }
174     }
175
176     # Dynamically load external code generation perl module
177     $codeGenerator = $ifaceName->new($object, $writeDependencies, $verbose, $targetIdlFilePath);
178     unless (defined($codeGenerator)) {
179         my $interfaces = $useDocument->interfaces;
180         foreach my $interface (@$interfaces) {
181             print "Skipping $useGenerator code generation for IDL interface \"" . $interface->type->name . "\".\n" if $verbose;
182         }
183         return;
184     }
185
186     my $interfaces = $useDocument->interfaces;
187     if (@$interfaces) {
188         die "Multiple interfaces per document are not supported" if @$interfaces > 1;
189
190         my $interface = @$interfaces[0];
191         print "Generating $useGenerator bindings code for IDL interface \"" . $interface->type->name . "\"...\n" if $verbose;
192         $codeGenerator->GenerateInterface($interface, $defines, $useDocument->enumerations, $useDocument->dictionaries);
193         $codeGenerator->WriteData($interface, $useOutputDir, $useOutputHeadersDir);
194         return;
195     }
196
197     my $callbackFunctions = $useDocument->callbackFunctions;
198     if (@$callbackFunctions) {
199         die "Multiple standalone callback functions per document are not supported" if @$callbackFunctions > 1;
200
201         my $callbackFunction = @$callbackFunctions[0];
202         print "Generating $useGenerator bindings code for IDL callback function \"" . $callbackFunction->type->name . "\"...\n" if $verbose;
203         $codeGenerator->GenerateCallbackFunction($callbackFunction, $useDocument->enumerations, $useDocument->dictionaries);
204         $codeGenerator->WriteData($callbackFunction, $useOutputDir, $useOutputHeadersDir);
205         return;
206     }
207
208     my $dictionaries = $useDocument->dictionaries;
209     if (@$dictionaries) {
210         my $dictionary;
211         my $otherDictionaries;
212         if (@$dictionaries == 1) {
213             $dictionary = @$dictionaries[0];
214         } else {
215             my $primaryDictionaryName = fileparse($targetIdlFilePath, ".idl");
216             for my $candidate (@$dictionaries) {
217                 if ($candidate->type->name eq $primaryDictionaryName) {
218                     $dictionary = $candidate;
219                 } else {
220                     push @$otherDictionaries, $candidate;
221                 }
222             }
223             die "Multiple dictionaries per document are only supported if one matches the filename" unless $dictionary;
224         }
225
226         print "Generating $useGenerator bindings code for IDL dictionary \"" . $dictionary->type->name . "\"...\n" if $verbose;
227         $codeGenerator->GenerateDictionary($dictionary, $useDocument->enumerations, $otherDictionaries);
228         $codeGenerator->WriteData($dictionary, $useOutputDir, $useOutputHeadersDir);
229         return;
230     }
231     
232     my $enumerations = $useDocument->enumerations;
233     if (@$enumerations) {
234         die "Multiple standalone enumerations per document are not supported" if @$enumerations > 1;
235
236         my $enumeration = @$enumerations[0];
237         print "Generating $useGenerator bindings code for IDL enumeration \"" . $enumeration->type->name . "\"...\n" if $verbose;
238         $codeGenerator->GenerateEnumeration($enumeration);
239         $codeGenerator->WriteData($enumeration, $useOutputDir, $useOutputHeadersDir);
240         return;
241     }
242
243     die "Processing document " . $useDocument->fileName . " did not generate anything"
244 }
245
246 sub FileNamePrefix
247 {
248     my $object = shift;
249
250     my $ifaceName = "CodeGenerator" . $useGenerator;
251     require $ifaceName . ".pm";
252
253     # Dynamically load external code generation perl module
254     $codeGenerator = $ifaceName->new($object, $writeDependencies, $verbose);
255     return $codeGenerator->FileNamePrefix();
256 }
257
258 sub UpdateFile
259 {
260     my $object = shift;
261     my $fileName = shift;
262     my $contents = shift;
263
264     # FIXME: We should only write content if it is different from what is in the file.
265     # But that would mean running more often the binding generator, see https://bugs.webkit.org/show_bug.cgi?id=131756
266     open FH, ">", $fileName or die "Couldn't open $fileName: $!\n";
267     print FH $contents;
268     close FH;
269 }
270
271 sub ForAllParents
272 {
273     my $object = shift;
274     my $interface = shift;
275     my $beforeRecursion = shift;
276     my $afterRecursion = shift;
277
278     my $recurse;
279     $recurse = sub {
280         my $outerInterface = shift;
281         my $currentInterface = shift;
282
283         if  ($currentInterface->parentType) {
284             my $interfaceName = $currentInterface->parentType->name;
285             my $parentInterface = $object->ParseInterface($outerInterface, $interfaceName);
286
287             if ($beforeRecursion) {
288                 &$beforeRecursion($parentInterface) eq 'prune' and next;
289             }
290             &$recurse($outerInterface, $parentInterface);
291             &$afterRecursion($parentInterface) if $afterRecursion;
292         }
293     };
294
295     &$recurse($interface, $interface);
296 }
297
298 sub IDLFileForInterface
299 {
300     my $object = shift;
301     my $interfaceName = shift;
302
303     unless ($idlFiles) {
304         my $sourceRoot = $ENV{SOURCE_ROOT};
305         my @directories = map { $_ = "$sourceRoot/$_" if $sourceRoot && -d "$sourceRoot/$_"; $_ } @$useDirectories;
306         push(@directories, ".");
307
308         $idlFiles = { };
309
310         my $wanted = sub {
311             $idlFiles->{$1} = $File::Find::name if /^([A-Z].*)\.idl$/;
312             $File::Find::prune = 1 if /^\../;
313         };
314         find($wanted, @directories);
315     }
316
317     return $idlFiles->{$interfaceName};
318 }
319
320 sub GetInterfaceForType
321 {
322     my ($object, $currentInterface, $type) = @_;
323
324     return undef unless $object->IsInterfaceType($type);
325
326     return $object->ParseInterface($currentInterface, $type->name);
327 }
328
329 sub GetAttributeFromInterface
330 {
331     my ($object, $outerInterface, $interfaceName, $attributeName) = @_;
332
333     my $interface = $object->ParseInterface($outerInterface, $interfaceName);
334     for my $attribute (@{$interface->attributes}) {
335         return $attribute if $attribute->name eq $attributeName;
336     }
337     die("Could not find attribute '$attributeName' on interface '$interfaceName'.");
338 }
339
340 sub ParseInterface
341 {
342     my $object = shift;
343     my $outerInterface = shift;
344     my $interfaceName = shift;
345
346     return undef if $interfaceName eq 'Object';
347     return undef if $interfaceName eq 'UNION';
348
349     if (exists $cachedInterfaces->{$interfaceName}) {
350         return $cachedInterfaces->{$interfaceName};
351     }
352
353     # Step #1: Find the IDL file associated with 'interface'
354     my $filename = $object->IDLFileForInterface($interfaceName)
355         or assert("Could NOT find IDL file for interface \"$interfaceName\", reachable from \"" . $outerInterface->type->name . "\"!\n");
356
357     print "  |  |>  Parsing parent IDL \"$filename\" for interface \"$interfaceName\"\n" if $verbose;
358
359     # Step #2: Parse the found IDL file (in quiet mode).
360     my $parser = IDLParser->new(1);
361     my $document = $parser->Parse($filename, $defines, $preprocessor, $idlAttributes);
362
363     foreach my $interface (@{$document->interfaces}) {
364         if ($interface->type->name eq $interfaceName) {
365             $cachedInterfaces->{$interfaceName} = $interface;
366             return $interface;
367         }
368     }
369
370     die("Could NOT find interface definition for $interfaceName in $filename");
371 }
372
373 sub ParseType
374 {
375     my ($object, $typeString) = @_;
376
377     return $cachedTypes->{$typeString} if exists($cachedTypes->{$typeString});
378
379     my $parser = IDLParser->new(1);
380     my $type = $parser->ParseType($typeString, $idlAttributes);
381
382     $cachedTypes->{$typeString} = $type;
383
384     return $type;
385 }
386
387 # Helpers for all CodeGenerator***.pm modules
388
389 sub IsNumericType
390 {
391     my ($object, $type) = @_;
392
393     assert("Not a type") if ref($type) ne "IDLType";
394
395     return 1 if $integerTypeHash{$type->name};
396     return 1 if $floatingPointTypeHash{$type->name};
397     return 0;
398 }
399
400 sub IsStringOrEnumType
401 {
402     my ($object, $type) = @_;
403     
404     assert("Not a type") if ref($type) ne "IDLType";
405
406     return 1 if $object->IsStringType($type);
407     return 1 if $object->IsEnumType($type);
408     return 0;
409 }
410
411 sub IsIntegerType
412 {
413     my ($object, $type) = @_;
414
415     assert("Not a type") if ref($type) ne "IDLType";
416
417     return 1 if $integerTypeHash{$type->name};
418     return 0;
419 }
420
421 sub IsFloatingPointType
422 {
423     my ($object, $type) = @_;
424
425     assert("Not a type") if ref($type) ne "IDLType";
426
427     return 1 if $floatingPointTypeHash{$type->name};
428     return 0;
429 }
430
431 sub IsPrimitiveType
432 {
433     my ($object, $type) = @_;
434
435     assert("Not a type") if ref($type) ne "IDLType";
436
437     return 1 if $primitiveTypeHash{$type->name};
438     return 1 if $object->IsNumericType($type);
439     return 0;
440 }
441
442 sub IsStringType
443 {
444     my ($object, $type) = @_;
445
446     assert("Not a type") if ref($type) ne "IDLType";
447
448     return 1 if $stringTypeHash{$type->name};
449     return 0;
450 }
451
452 sub IsEnumType
453 {
454     my ($object, $type) = @_;
455
456     assert("Not a type") if ref($type) ne "IDLType";
457
458     return defined($object->GetEnumByType($type));
459 }
460
461 sub GetEnumByType
462 {
463     my ($object, $type) = @_;
464
465     assert("Not a type") if ref($type) ne "IDLType";
466
467     my $name = $type->name;
468
469     die "GetEnumByType() was called with an undefined enumeration name" unless defined($name);
470
471     for my $enumeration (@{$useDocument->enumerations}) {
472         return $enumeration if $enumeration->type->name eq $name;
473     }
474
475     return $cachedExternalEnumerations->{$name} if exists($cachedExternalEnumerations->{$name});
476
477     # Find the IDL file associated with the dictionary.
478     my $filename = $object->IDLFileForInterface($name) or return;
479
480     # Do a fast check to see if it seems to contain a dictionary.
481     my $fileContents = slurp($filename);
482
483     if ($fileContents =~ /\benum\s+$name/gs) {
484         # Parse the IDL.
485         my $parser = IDLParser->new(1);
486         my $document = $parser->Parse($filename, $defines, $preprocessor, $idlAttributes);
487
488         foreach my $enumeration (@{$document->enumerations}) {
489             next unless $enumeration->type->name eq $name;
490
491             $cachedExternalEnumerations->{$name} = $enumeration;
492             my $implementedAs = $enumeration->extendedAttributes->{ImplementedAs};
493             $enumTypeImplementationNameOverrides{$enumeration->type->name} = $implementedAs if $implementedAs;
494             return $enumeration;
495         }
496     }
497     $cachedExternalEnumerations->{$name} = undef;
498 }
499
500 # An enumeration defined in its own IDL file.
501 sub IsExternalEnumType
502 {
503     my ($object, $type) = @_;
504
505     assert("Not a type") if ref($type) ne "IDLType";
506
507     return $object->IsEnumType($type) && defined($cachedExternalEnumerations->{$type->name});
508 }
509
510 sub HasEnumImplementationNameOverride
511 {
512     my ($object, $type) = @_;
513
514     assert("Not a type") if ref($type) ne "IDLType";
515
516     return 1 if exists $enumTypeImplementationNameOverrides{$type->name};
517     return 0;
518 }
519
520 sub GetEnumImplementationNameOverride
521 {
522     my ($object, $type) = @_;
523
524     assert("Not a type") if ref($type) ne "IDLType";
525
526     return $enumTypeImplementationNameOverrides{$type->name};
527 }
528
529 sub GetDictionaryByType
530 {
531     my ($object, $type) = @_;
532
533     assert("Not a type") if ref($type) ne "IDLType";
534
535     my $name = $type->name;
536
537     die "GetDictionaryByType() was called with an undefined dictionary name" unless defined($name);
538
539     for my $dictionary (@{$useDocument->dictionaries}) {
540         return $dictionary if $dictionary->type->name eq $name;
541     }
542
543     return $cachedExternalDictionaries->{$name} if exists($cachedExternalDictionaries->{$name});
544
545     # Find the IDL file associated with the dictionary.
546     my $filename = $object->IDLFileForInterface($name) or return;
547
548     # Do a fast check to see if it seems to contain a dictionary.
549     my $fileContents = slurp($filename);
550
551     if ($fileContents =~ /\bdictionary\s+$name/gs) {
552         # Parse the IDL.
553         my $parser = IDLParser->new(1);
554         my $document = $parser->Parse($filename, $defines, $preprocessor, $idlAttributes);
555
556         foreach my $dictionary (@{$document->dictionaries}) {
557             next unless $dictionary->type->name eq $name;
558
559             $cachedExternalDictionaries->{$name} = $dictionary;
560             my $implementedAs = $dictionary->extendedAttributes->{ImplementedAs};
561             $dictionaryTypeImplementationNameOverrides{$dictionary->type->name} = $implementedAs if $implementedAs;
562             return $dictionary;
563         }
564     }
565     $cachedExternalDictionaries->{$name} = undef;
566 }
567
568 sub IsDictionaryType
569 {
570     my ($object, $type) = @_;
571
572     assert("Not a type") if ref($type) ne "IDLType";
573
574     return $type->name =~ /^[A-Z]/ && defined($object->GetDictionaryByType($type));
575 }
576
577 # A dictionary defined in its own IDL file.
578 sub IsExternalDictionaryType
579 {
580     my ($object, $type) = @_;
581
582     assert("Not a type") if ref($type) ne "IDLType";
583
584     return $object->IsDictionaryType($type) && defined($cachedExternalDictionaries->{$type->name});
585 }
586
587 sub HasDictionaryImplementationNameOverride
588 {
589     my ($object, $type) = @_;
590
591     assert("Not a type") if ref($type) ne "IDLType";
592
593     return 1 if exists $dictionaryTypeImplementationNameOverrides{$type->name};
594     return 0;
595 }
596
597 sub GetDictionaryImplementationNameOverride
598 {
599     my ($object, $type) = @_;
600
601     assert("Not a type") if ref($type) ne "IDLType";
602
603     return $dictionaryTypeImplementationNameOverrides{$type->name};
604 }
605
606 sub IsSVGAnimatedTypeName
607 {
608     my ($object, $typeName) = @_;
609
610     return $typeName =~ /^SVGAnimated/;
611 }
612
613 sub IsSVGAnimatedType
614 {
615     my ($object, $type) = @_;
616
617     assert("Not a type") if ref($type) ne "IDLType";
618
619     return $object->IsSVGAnimatedTypeName($type->name);
620 }
621
622 sub IsSVGPathSegTypeName
623 {
624     my ($object, $typeName) = @_;
625
626     return $typeName =~ /^SVGPathSeg/;
627 }
628
629 sub IsSVGPathSegType
630 {
631     my ($object, $type) = @_;
632
633     assert("Not a type") if ref($type) ne "IDLType";
634
635     return $object->IsSVGPathSegTypeName($type->name);
636 }
637
638 sub IsConstructorType
639 {
640     my ($object, $type) = @_;
641
642     assert("Not a type") if ref($type) ne "IDLType";
643
644     return $type->name =~ /Constructor$/;
645 }
646
647 sub IsSequenceType
648 {
649     my ($object, $type) = @_;
650
651     assert("Not a type") if ref($type) ne "IDLType";
652
653     return $type->name eq "sequence";
654 }
655
656 sub IsFrozenArrayType
657 {
658     my ($object, $type) = @_;
659
660     assert("Not a type") if ref($type) ne "IDLType";
661
662     return $type->name eq "FrozenArray";
663 }
664
665 sub IsSequenceOrFrozenArrayType
666 {
667     my ($object, $type) = @_;
668
669     assert("Not a type") if ref($type) ne "IDLType";
670
671     return $object->IsSequenceType($type) || $object->IsFrozenArrayType($type);
672 }
673
674 sub IsRecordType
675 {
676     my ($object, $type) = @_;
677
678     assert("Not a type") if ref($type) ne "IDLType";
679
680     return $type->name eq "record";
681 }
682
683 sub IsBufferSourceType
684 {
685     my ($object, $type) = @_;
686
687     assert("Not a type") if ref($type) ne "IDLType";
688
689     return 1 if $bufferSourceTypes{$type->name};
690     return 0;
691 }
692
693 sub IsPromiseType
694 {
695     my ($object, $type) = @_;
696
697     assert("Not a type") if ref($type) ne "IDLType";
698
699     return $type->name eq "Promise";
700 }
701
702 # These match WK_lcfirst and WK_ucfirst defined in builtins_generator.py.
703 # Uppercase the first letter while respecting WebKit style guidelines.
704 # E.g., xmlEncoding becomes XMLEncoding, but xmlllang becomes Xmllang.
705 sub WK_ucfirst
706 {
707     my ($object, $param) = @_;
708
709     my $ret = ucfirst($param);
710     $ret =~ s/Xml/XML/ if $ret =~ /^Xml[^a-z]/;
711     $ret =~ s/Svg/SVG/ if $ret =~ /^Svg/;
712     $ret =~ s/Srgb/SRGB/ if $ret =~ /^Srgb/;
713     $ret =~ s/Cenc/cenc/ if $ret =~ /^Cenc/;
714     $ret =~ s/Cbcs/cbcs/ if $ret =~ /^Cbcs/;
715
716     return $ret;
717 }
718
719 # Lowercase the first letter while respecting WebKit style guidelines.
720 # URL becomes url, but SetURL becomes setURL.
721 sub WK_lcfirst
722 {
723     my ($object, $param) = @_;
724
725     my $ret = lcfirst($param);
726     $ret =~ s/dOM/dom/ if $ret =~ /^dOM/;
727     $ret =~ s/hTML/html/ if $ret =~ /^hTML/;
728     $ret =~ s/uRL/url/ if $ret =~ /^uRL/;
729     $ret =~ s/jS/js/ if $ret =~ /^jS/;
730     $ret =~ s/xML/xml/ if $ret =~ /^xML/;
731     $ret =~ s/xSLT/xslt/ if $ret =~ /^xSLT/;
732     $ret =~ s/cSS/css/ if $ret =~ /^cSS/;
733     $ret =~ s/rTC/rtc/ if $ret =~ /^rTC/;
734
735     # For HTML5 FileSystem API Flags attributes.
736     # (create is widely used to instantiate an object and must be avoided.)
737     $ret =~ s/^create/isCreate/ if $ret =~ /^create$/;
738     $ret =~ s/^exclusive/isExclusive/ if $ret =~ /^exclusive$/;
739
740     return $ret;
741 }
742
743 sub slurp
744 {
745     my $file = shift;
746
747     open my $fh, '<', $file or die;
748     local $/ = undef;
749     my $content = <$fh>;
750     close $fh;
751     return $content;
752 }
753
754 sub trim
755 {
756     my $string = shift;
757
758     $string =~ s/^\s+|\s+$//g;
759     return $string;
760 }
761
762 # Return the C++ namespace that a given attribute name string is defined in.
763 sub NamespaceForAttributeName
764 {
765     my ($object, $interfaceName, $attributeName) = @_;
766
767     return "SVGNames" if $interfaceName =~ /^SVG/ && !$svgAttributesInHTMLHash{$attributeName};
768     return "HTMLNames";
769 }
770
771 # Identifies overloaded functions and for each function adds an array with
772 # links to its respective overloads (including itself).
773 sub LinkOverloadedOperations
774 {
775     my ($object, $interface) = @_;
776
777     my %nameToOperationsMap = ();
778     foreach my $operation (@{$interface->operations}) {
779         my $name = $operation->name;
780         $nameToOperationsMap{$name} = [] if !exists $nameToOperationsMap{$name};
781         push(@{$nameToOperationsMap{$name}}, $operation);
782         $operation->{overloads} = $nameToOperationsMap{$name};
783         $operation->{overloadIndex} = @{$nameToOperationsMap{$name}};
784     }
785
786     my $index = 1;
787     foreach my $constructor (@{$interface->constructors}) {
788         $constructor->{overloads} = $interface->constructors;
789         $constructor->{overloadIndex} = $index;
790         $index++;
791     }
792 }
793
794 sub AttributeNameForGetterAndSetter
795 {
796     my ($generator, $attribute) = @_;
797
798     my $attributeName = $attribute->name;
799     if ($attribute->extendedAttributes->{"ImplementedAs"}) {
800         $attributeName = $attribute->extendedAttributes->{"ImplementedAs"};
801     }
802     my $attributeType = $attribute->type;
803
804     # SVG animated types need to use a special attribute name.
805     # The rest of the special casing for SVG animated types is handled in the language-specific code generators.
806     $attributeName .= "Animated" if $generator->IsSVGAnimatedType($attributeType);
807
808     return $attributeName;
809 }
810
811 sub ContentAttributeName
812 {
813     my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
814
815     my $contentAttributeName = $attribute->extendedAttributes->{"Reflect"};
816     return undef if !$contentAttributeName;
817
818     $contentAttributeName = lc $generator->AttributeNameForGetterAndSetter($attribute) if $contentAttributeName eq "VALUE_IS_MISSING";
819
820     my $namespace = $generator->NamespaceForAttributeName($interfaceName, $contentAttributeName);
821
822     $implIncludes->{"${namespace}.h"} = 1;
823     return "WebCore::${namespace}::${contentAttributeName}Attr";
824 }
825
826 sub GetterExpression
827 {
828     my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
829
830     my $contentAttributeName = $generator->ContentAttributeName($implIncludes, $interfaceName, $attribute);
831
832     if (!$contentAttributeName) {
833         return ($generator->WK_lcfirst($generator->AttributeNameForGetterAndSetter($attribute)));
834     }
835
836     my $attributeType = $attribute->type;
837
838     my $functionName;
839     if ($attribute->extendedAttributes->{"URL"}) {
840         $functionName = "getURLAttribute";
841     } elsif ($attributeType->name eq "boolean") {
842         $functionName = "hasAttributeWithoutSynchronization";
843     } elsif ($attributeType->name eq "long") {
844         $functionName = "getIntegralAttribute";
845     } elsif ($attributeType->name eq "unsigned long") {
846         $functionName = "getUnsignedIntegralAttribute";
847     } else {
848         if ($contentAttributeName eq "WebCore::HTMLNames::idAttr") {
849             $functionName = "getIdAttribute";
850             $contentAttributeName = "";
851         } elsif ($contentAttributeName eq "WebCore::HTMLNames::nameAttr") {
852             $functionName = "getNameAttribute";
853             $contentAttributeName = "";
854         } elsif ($generator->IsSVGAnimatedType($attributeType)) {
855             $functionName = "getAttribute";
856         } else {
857             $functionName = "attributeWithoutSynchronization";
858         }
859     }
860
861     return ($functionName, $contentAttributeName);
862 }
863
864 sub SetterExpression
865 {
866     my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
867
868     my $contentAttributeName = $generator->ContentAttributeName($implIncludes, $interfaceName, $attribute);
869
870     if (!$contentAttributeName) {
871         return ("set" . $generator->WK_ucfirst($generator->AttributeNameForGetterAndSetter($attribute)));
872     }
873
874     my $attributeType = $attribute->type;
875
876     my $functionName;
877     if ($attributeType->name eq "boolean") {
878         $functionName = "setBooleanAttribute";
879     } elsif ($attributeType->name eq "long") {
880         $functionName = "setIntegralAttribute";
881     } elsif ($attributeType->name eq "unsigned long") {
882         $functionName = "setUnsignedIntegralAttribute";
883     } elsif ($generator->IsSVGAnimatedType($attributeType)) {
884         $functionName = "setAttribute";
885     } else {
886         $functionName = "setAttributeWithoutSynchronization";
887     }
888
889     return ($functionName, $contentAttributeName);
890 }
891
892 sub IsBuiltinType
893 {
894     my ($object, $type) = @_;
895
896     assert("Not a type") if ref($type) ne "IDLType";
897
898     return 1 if $object->IsPrimitiveType($type);
899     return 1 if $object->IsSequenceOrFrozenArrayType($type);
900     return 1 if $object->IsRecordType($type);
901     return 1 if $object->IsStringType($type);
902     return 1 if $object->IsBufferSourceType($type);
903     return 1 if $type->isUnion;
904     return 1 if $type->name eq "EventListener";
905     return 1 if $type->name eq "JSON";
906     return 1 if $type->name eq "Promise";
907     return 1 if $type->name eq "ScheduledAction";
908     return 1 if $type->name eq "SerializedScriptValue";
909     return 1 if $type->name eq "XPathNSResolver";
910     return 1 if $type->name eq "any";
911     return 1 if $type->name eq "object";
912
913     return 0;
914 }
915
916 sub IsInterfaceType
917 {
918     my ($object, $type) = @_;
919
920     assert("Not a type") if ref($type) ne "IDLType";
921
922     return 0 if $object->IsBuiltinType($type);
923     return 0 if $object->IsDictionaryType($type);
924     return 0 if $object->IsEnumType($type);
925
926     return 1;
927 }
928
929 sub IsWrapperType
930 {
931     my ($object, $type) = @_;
932
933     assert("Not a type") if ref($type) ne "IDLType";
934
935     return 1 if $object->IsInterfaceType($type);
936     return 1 if $type->name eq "XPathNSResolver";
937
938     return 0;
939 }
940
941 sub InheritsSerializable
942 {
943     my ($object, $interface) = @_;
944
945     my $anyParentIsSerializable = 0;
946     $object->ForAllParents($interface, sub {
947         my $parentInterface = shift;
948         $anyParentIsSerializable = 1 if $parentInterface->serializable;
949     }, 0);
950
951     return $anyParentIsSerializable;
952 }
953
954 sub IsSerializableType
955 {
956     my ($object, $interface, $type) = @_;
957
958     # https://heycam.github.io/webidl/#dfn-serializable-type
959
960     return 1 if $type->name eq "boolean";
961     return 1 if $object->IsNumericType($type);
962     return 1 if $object->IsEnumType($type);
963     return 1 if $object->IsStringType($type);
964     return 0 if $type->name eq "EventHandler";
965
966     if ($type->isUnion || $object->IsDictionaryType($type)) {
967         die "Serializers for union and dictionary types are not currently supported.\n";
968     }
969
970     if ($object->IsSequenceOrFrozenArrayType($type)) {
971         my $subtype = @{$type->subtypes}[0];
972
973         # FIXME: webkit.org/b/194439 [WebIDL] Support serializing sequences and FrozenArrays of interfaces
974         return 0 if $object->IsInterfaceType($subtype);
975
976         return $object->IsSerializableType($interface, $subtype);
977     }
978
979     return 0 if !$object->IsInterfaceType($type);
980
981     my $interfaceForType = $object->GetInterfaceForType($interface, $type);
982     if ($interfaceForType) {
983         return 1 if $interfaceForType->serializable;
984         return $object->InheritsSerializable($interfaceForType);
985     }
986
987     return 0;
988 }
989
990 sub hasCachedAttributeOrCustomGetterExtendedAttribute
991 {
992     my ($attribute) = @_;
993     return $attribute->extendedAttributes->{CachedAttribute} || $attribute->extendedAttributes->{CustomGetter};
994 }
995
996 sub IsSerializableAttribute
997 {
998     my ($object, $interface, $attribute) = @_;
999
1000     if ($object->IsSequenceType($attribute->type) && hasCachedAttributeOrCustomGetterExtendedAttribute($attribute)) {
1001         die "Serializers for sequence types with CachedAttribute or CustomGetter extended attributes are not currently supported.\n";
1002     }
1003
1004     return $object->IsSerializableType($interface, $attribute->type);
1005 }
1006
1007 sub GetInterfaceExtendedAttributesFromName
1008 {
1009     # FIXME: It's bad to have a function like this that opens another IDL file to answer a question.
1010     # Overusing this kind of function can make things really slow. Lets avoid these if we can.
1011
1012     my ($object, $interfaceName) = @_;
1013
1014     my $idlFile = $object->IDLFileForInterface($interfaceName) or assert("Could NOT find IDL file for interface \"$interfaceName\"!\n");
1015
1016     open FILE, "<", $idlFile or die;
1017     my @lines = <FILE>;
1018     close FILE;
1019
1020     my $fileContents = join('', @lines);
1021
1022     my $extendedAttributes = {};
1023
1024     if ($fileContents =~ /\[(.*)\]\s+(callback interface|interface|exception)\s+(\w+)/gs) {
1025         my @parts = split(',', $1);
1026         foreach my $part (@parts) {
1027             my @keyValue = split('=', $part);
1028             my $key = trim($keyValue[0]);
1029             next unless length($key);
1030             my $value = "VALUE_IS_MISSING";
1031             $value = trim($keyValue[1]) if @keyValue > 1;
1032             $extendedAttributes->{$key} = $value;
1033         }
1034     }
1035
1036     return $extendedAttributes;
1037 }
1038
1039 sub ComputeIsCallbackInterface
1040 {
1041     my ($object, $type) = @_;
1042
1043     assert("Not a type") if ref($type) ne "IDLType";
1044
1045     return 0 unless $object->IsInterfaceType($type);
1046     return 0 if $type->name eq "WindowProxy";
1047
1048     my $typeName = $type->name;
1049     my $idlFile = $object->IDLFileForInterface($typeName) or assert("Could NOT find IDL file for interface \"$typeName\"!\n");
1050
1051     open FILE, "<", $idlFile or die;
1052     my @lines = <FILE>;
1053     close FILE;
1054
1055     my $fileContents = join('', @lines);
1056     return ($fileContents =~ /callback\s+interface\s+(\w+)/gs);
1057 }
1058
1059 my %isCallbackInterface = ();
1060
1061 sub IsCallbackInterface
1062 {
1063     # FIXME: It's bad to have a function like this that opens another IDL file to answer a question.
1064     # Overusing this kind of function can make things really slow. Lets avoid these if we can.
1065     # To mitigate that, lets cache what we learn in a hash so we don't open the same file over and over.
1066
1067     my ($object, $type) = @_;
1068
1069     assert("Not a type") if ref($type) ne "IDLType";
1070
1071     return $isCallbackInterface{$type->name} if exists $isCallbackInterface{$type->name};
1072     my $result = $object->ComputeIsCallbackInterface($type);
1073     $isCallbackInterface{$type->name} = $result;
1074     return $result;
1075 }
1076
1077 sub ComputeIsCallbackFunction
1078 {
1079     my ($object, $type) = @_;
1080
1081     assert("Not a type") if ref($type) ne "IDLType";
1082
1083     return 0 unless $object->IsInterfaceType($type);
1084     return 0 if $type->name eq "WindowProxy";
1085
1086     my $typeName = $type->name;
1087     my $idlFile = $object->IDLFileForInterface($typeName) or assert("Could NOT find IDL file for interface \"$typeName\"!\n");
1088
1089     open FILE, "<", $idlFile or die;
1090     my @lines = <FILE>;
1091     close FILE;
1092
1093     my $fileContents = join('', @lines);
1094     return ($fileContents =~ /(.*)callback\s+(\w+)\s+=/gs);
1095 }
1096
1097 my %isCallbackFunction = ();
1098
1099 sub IsCallbackFunction
1100 {
1101     # FIXME: It's bad to have a function like this that opens another IDL file to answer a question.
1102     # Overusing this kind of function can make things really slow. Lets avoid these if we can.
1103     # To mitigate that, lets cache what we learn in a hash so we don't open the same file over and over.
1104
1105     my ($object, $type) = @_;
1106
1107     assert("Not a type") if ref($type) ne "IDLType";
1108
1109     return $isCallbackFunction{$type->name} if exists $isCallbackFunction{$type->name};
1110     my $result = $object->ComputeIsCallbackFunction($type);
1111     $isCallbackFunction{$type->name} = $result;
1112     return $result;
1113 }
1114
1115 sub GenerateConditionalString
1116 {
1117     my ($generator, $node) = @_;
1118
1119     my $conditional = $node->extendedAttributes->{"Conditional"};
1120     if ($conditional) {
1121         return $generator->GenerateConditionalStringFromAttributeValue($conditional);
1122     } else {
1123         return "";
1124     }
1125 }
1126
1127 sub GenerateConditionalStringFromAttributeValue
1128 {
1129     my ($generator, $conditional) = @_;
1130
1131     my %disjunction;
1132     map {
1133         my $expression = $_;
1134         my %conjunction;
1135         map { $conjunction{$_} = 1; } split(/&/, $expression);
1136         $expression = "ENABLE(" . join(") && ENABLE(", sort keys %conjunction) . ")";
1137         $disjunction{$expression} = 1
1138     } split(/\|/, $conditional);
1139
1140     return "1" if keys %disjunction == 0;
1141     return (%disjunction)[0] if keys %disjunction == 1;
1142
1143     my @parenthesized;
1144     map {
1145         my $expression = $_;
1146         $expression = "($expression)" if $expression =~ / /;
1147         push @parenthesized, $expression;
1148     } sort keys %disjunction;
1149
1150     return join(" || ", @parenthesized);
1151 }
1152
1153 sub GenerateCompileTimeCheckForEnumsIfNeeded
1154 {
1155     my ($generator, $interface) = @_;
1156
1157     return () if $interface->extendedAttributes->{"DoNotCheckConstants"} || !@{$interface->constants};
1158
1159     my $baseScope = $interface->extendedAttributes->{"ConstantsScope"} || $interface->type->name;
1160
1161     my @checks = ();
1162     foreach my $constant (@{$interface->constants}) {
1163         my $scope = $constant->extendedAttributes->{"ImplementedBy"} || $baseScope;
1164         my $name = $constant->extendedAttributes->{"Reflect"} || $constant->name;
1165         my $value = $constant->value;
1166         my $conditional = $constant->extendedAttributes->{"Conditional"};
1167         push(@checks, "#if " . $generator->GenerateConditionalStringFromAttributeValue($conditional) . "\n") if $conditional;
1168         push(@checks, "static_assert(${scope}::${name} == ${value}, \"${name} in ${scope} does not match value from IDL\");\n");
1169         push(@checks, "#endif\n") if $conditional;
1170     }
1171     push(@checks, "\n");
1172     return @checks;
1173 }
1174
1175 sub ExtendedAttributeContains
1176 {
1177     my $object = shift;
1178     my $callWith = shift;
1179     return 0 unless $callWith;
1180     my $keyword = shift;
1181
1182     my @callWithKeywords = split /\s*\&\s*/, $callWith;
1183     return grep { $_ eq $keyword } @callWithKeywords;
1184 }
1185
1186 # FIXME: This is backwards. We currently name the interface and the IDL files with the implementation name. We
1187 # should use the real interface name in the IDL files and then use ImplementedAs to map this to the implementation name.
1188 sub GetVisibleInterfaceName
1189 {
1190     my ($object, $interface) = @_;
1191
1192     my $interfaceName = $interface->extendedAttributes->{"InterfaceName"};
1193     return $interfaceName ? $interfaceName : $interface->type->name;
1194 }
1195
1196 sub InheritsInterface
1197 {
1198     my ($object, $interface, $interfaceName) = @_;
1199
1200     return 1 if $interfaceName eq $interface->type->name;
1201
1202     my $found = 0;
1203     $object->ForAllParents($interface, sub {
1204         my $currentInterface = shift;
1205         if ($currentInterface->type->name eq $interfaceName) {
1206             $found = 1;
1207         }
1208         return 1 if $found;
1209     }, 0);
1210
1211     return $found;
1212 }
1213
1214 sub InheritsExtendedAttribute
1215 {
1216     my ($object, $interface, $extendedAttribute) = @_;
1217
1218     return 1 if $interface->extendedAttributes->{$extendedAttribute};
1219
1220     my $found = 0;
1221     $object->ForAllParents($interface, sub {
1222         my $currentInterface = shift;
1223         if ($currentInterface->extendedAttributes->{$extendedAttribute}) {
1224             $found = 1;
1225         }
1226         return 1 if $found;
1227     }, 0);
1228
1229     return $found;
1230 }
1231
1232
1233 1;