[CodeGenerator] Only allow a single interface or standalone dictionary per WebIDL...
[WebKit-https.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::Find;
32 use Carp qw<longmess>;
33 use Data::Dumper;
34
35 my $useDocument = "";
36 my $useGenerator = "";
37 my $useOutputDir = "";
38 my $useOutputHeadersDir = "";
39 my $useDirectories = "";
40 my $preprocessor;
41 my $writeDependencies = 0;
42 my $defines = "";
43 my $targetIdlFilePath = "";
44
45 my $codeGenerator = 0;
46
47 my $verbose = 0;
48
49 my %integerTypeHash = (
50     "byte" => 1,
51     "long long" => 1,
52     "long" => 1,
53     "octet" => 1,
54     "short" => 1,
55     "unsigned long long" => 1,
56     "unsigned long" => 1,
57     "unsigned short" => 1,
58 );
59
60 my %floatingPointTypeHash = (
61     "float" => 1,
62     "unrestricted float" => 1,
63     "double" => 1,
64     "unrestricted double" => 1,
65 );
66
67 my %primitiveTypeHash = ( "boolean" => 1, "void" => 1, "Date" => 1 );
68
69 # WebCore types used directly in IDL files.
70 my %webCoreTypeHash = (
71     "Dictionary" => 1,
72     "SerializedScriptValue" => 1,
73 );
74
75 my %dictionaryTypes = ();
76 my %dictionaryTypeImplementationNameOverrides = ();
77
78 my %enumTypeHash = ();
79 my %enumTypeImplementationNameOverrides = ();
80
81
82 my %typedArrayTypes = (
83     "ArrayBuffer" => 1,
84     "ArrayBufferView" => 1,
85     "DataView" => 1,
86     "Float32Array" => 1,
87     "Float64Array" => 1,
88     "Int16Array" => 1,
89     "Int32Array" => 1,
90     "Int8Array" => 1,
91     "Uint16Array" => 1,
92     "Uint32Array" => 1,
93     "Uint8Array" => 1,
94     "Uint8ClampedArray" => 1,
95 );
96
97 my %nonPointerTypeHash = ( "DOMTimeStamp" => 1 );
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 my %svgTypeNeedingTearOff = (
119     "SVGAngle" => "SVGPropertyTearOff<SVGAngle>",
120     "SVGLength" => "SVGPropertyTearOff<SVGLength>",
121     "SVGLengthList" => "SVGListPropertyTearOff<SVGLengthList>",
122     "SVGMatrix" => "SVGPropertyTearOff<SVGMatrix>",
123     "SVGNumber" => "SVGPropertyTearOff<float>",
124     "SVGNumberList" => "SVGListPropertyTearOff<SVGNumberList>",
125     "SVGPathSegList" => "SVGPathSegListPropertyTearOff",
126     "SVGPoint" => "SVGPropertyTearOff<SVGPoint>",
127     "SVGPointList" => "SVGListPropertyTearOff<SVGPointList>",
128     "SVGPreserveAspectRatio" => "SVGPropertyTearOff<SVGPreserveAspectRatio>",
129     "SVGRect" => "SVGPropertyTearOff<FloatRect>",
130     "SVGStringList" => "SVGStaticListPropertyTearOff<SVGStringList>",
131     "SVGTransform" => "SVGPropertyTearOff<SVGTransform>",
132     "SVGTransformList" => "SVGTransformListPropertyTearOff"
133 );
134
135 my %svgTypeWithWritablePropertiesNeedingTearOff = (
136     "SVGPoint" => 1,
137     "SVGMatrix" => 1
138 );
139
140 # Cache of IDL file pathnames.
141 my $idlFiles;
142 my $cachedInterfaces = {};
143 my $cachedExternalDictionaries = {};
144
145 sub assert
146 {
147     my $message = shift;
148     
149     my $mess = longmess();
150     print Dumper($mess);
151
152     die $message;
153 }
154
155 # Default constructor
156 sub new
157 {
158     my $object = shift;
159     my $reference = { };
160
161     $useDirectories = shift;
162     $useGenerator = shift;
163     $useOutputDir = shift;
164     $useOutputHeadersDir = shift;
165     $preprocessor = shift;
166     $writeDependencies = shift;
167     $verbose = shift;
168     $targetIdlFilePath = shift;
169
170     bless($reference, $object);
171     return $reference;
172 }
173
174 sub ProcessDocument
175 {
176     my $object = shift;
177     $useDocument = shift;
178     $defines = shift;
179
180     my $ifaceName = "CodeGenerator" . $useGenerator;
181     require $ifaceName . ".pm";
182
183     foreach my $dictionary (@{$useDocument->dictionaries}) {
184         $dictionaryTypes{$dictionary->name} = 1;
185         if ($dictionary->extendedAttributes->{"ImplementedAs"}) {
186             $dictionaryTypeImplementationNameOverrides{$dictionary->name} = $dictionary->extendedAttributes->{"ImplementedAs"};
187         }
188     }
189
190     foreach my $enumeration (@{$useDocument->enumerations}) {
191         $enumTypeHash{$enumeration->name} = $enumeration->values;
192         if ($enumeration->extendedAttributes->{"ImplementedAs"}) {
193             $enumTypeImplementationNameOverrides{$enumeration->name} = $enumeration->extendedAttributes->{"ImplementedAs"};
194         }
195     }
196
197     # Dynamically load external code generation perl module
198     $codeGenerator = $ifaceName->new($object, $writeDependencies, $verbose, $targetIdlFilePath);
199     unless (defined($codeGenerator)) {
200         my $interfaces = $useDocument->interfaces;
201         foreach my $interface (@$interfaces) {
202             print "Skipping $useGenerator code generation for IDL interface \"" . $interface->name . "\".\n" if $verbose;
203         }
204         return;
205     }
206
207     my $interfaces = $useDocument->interfaces;
208     if (@$interfaces) {
209         die "Multiple interfaces per document are not supported" if @$interfaces > 1;
210
211         my $interface = @$interfaces[0];
212         print "Generating $useGenerator bindings code for IDL interface \"" . $interface->name . "\"...\n" if $verbose;
213         $codeGenerator->GenerateInterface($interface, $defines, $useDocument->enumerations, $useDocument->dictionaries);
214         $codeGenerator->WriteData($interface, $useOutputDir, $useOutputHeadersDir);
215         return;
216     }
217
218     my $dictionaries = $useDocument->dictionaries;
219     if (@$dictionaries) {
220         die "Multiple standalone dictionaries per document are not supported" if @$dictionaries > 1;
221
222         my $dictionary = @$dictionaries[0];
223         print "Generating $useGenerator bindings code for IDL dictionary \"" . $dictionary->name . "\"...\n" if $verbose;
224         $codeGenerator->GenerateDictionary($dictionary, $useDocument->enumerations);
225         $codeGenerator->WriteData($dictionary, $useOutputDir, $useOutputHeadersDir);
226         return;
227     }
228
229     die "Processing document " . $useDocument->fileName . " did not generate anything"
230 }
231
232 sub FileNamePrefix
233 {
234     my $object = shift;
235
236     my $ifaceName = "CodeGenerator" . $useGenerator;
237     require $ifaceName . ".pm";
238
239     # Dynamically load external code generation perl module
240     $codeGenerator = $ifaceName->new($object, $writeDependencies, $verbose);
241     return $codeGenerator->FileNamePrefix();
242 }
243
244 sub UpdateFile
245 {
246     my $object = shift;
247     my $fileName = shift;
248     my $contents = shift;
249
250     # FIXME: We should only write content if it is different from what is in the file.
251     # But that would mean running more often the binding generator, see https://bugs.webkit.org/show_bug.cgi?id=131756
252     open FH, ">", $fileName or die "Couldn't open $fileName: $!\n";
253     print FH $contents;
254     close FH;
255 }
256
257 sub ForAllParents
258 {
259     my $object = shift;
260     my $interface = shift;
261     my $beforeRecursion = shift;
262     my $afterRecursion = shift;
263
264     my $recurse;
265     $recurse = sub {
266         my $outerInterface = shift;
267         my $currentInterface = shift;
268
269         for (@{$currentInterface->parents}) {
270             my $interfaceName = $_;
271             my $parentInterface = $object->ParseInterface($outerInterface, $interfaceName);
272
273             if ($beforeRecursion) {
274                 &$beforeRecursion($parentInterface) eq 'prune' and next;
275             }
276             &$recurse($outerInterface, $parentInterface);
277             &$afterRecursion($parentInterface) if $afterRecursion;
278         }
279     };
280
281     &$recurse($interface, $interface);
282 }
283
284 sub FindSuperMethod
285 {
286     my ($object, $interface, $functionName) = @_;
287     my $indexer;
288     $object->ForAllParents($interface, undef, sub {
289         my $currentInterface = shift;
290         foreach my $function (@{$currentInterface->functions}) {
291             if ($function->signature->name eq $functionName) {
292                 $indexer = $function->signature;
293                 return 'prune';
294             }
295         }
296     });
297     return $indexer;
298 }
299
300 sub IDLFileForInterface
301 {
302     my $object = shift;
303     my $interfaceName = shift;
304
305     unless ($idlFiles) {
306         my $sourceRoot = $ENV{SOURCE_ROOT};
307         my @directories = map { $_ = "$sourceRoot/$_" if $sourceRoot && -d "$sourceRoot/$_"; $_ } @$useDirectories;
308         push(@directories, ".");
309
310         $idlFiles = { };
311
312         my $wanted = sub {
313             $idlFiles->{$1} = $File::Find::name if /^([A-Z].*)\.idl$/;
314             $File::Find::prune = 1 if /^\../;
315         };
316         find($wanted, @directories);
317     }
318
319     return $idlFiles->{$interfaceName};
320 }
321
322 sub GetAttributeFromInterface()
323 {
324     my $object = shift;
325     my $outerInterface = shift;
326     my $interfaceName = shift;
327     my $attributeName = shift;
328
329     my $interface = $object->ParseInterface($outerInterface, $interfaceName);
330     for my $attribute (@{$interface->attributes}) {
331         return $attribute if $attribute->signature->name eq $attributeName;
332     }
333     die("Could not find attribute '$attributeName' on interface '$interfaceName'.");
334 }
335
336 sub ParseInterface
337 {
338     my $object = shift;
339     my $outerInterface = shift;
340     my $interfaceName = shift;
341
342     return undef if $interfaceName eq 'Object';
343     return undef if $interfaceName eq 'UNION';
344
345     if (exists $cachedInterfaces->{$interfaceName}) {
346         return $cachedInterfaces->{$interfaceName};
347     }
348
349     # Step #1: Find the IDL file associated with 'interface'
350     my $filename = $object->IDLFileForInterface($interfaceName)
351         or assert("Could NOT find IDL file for interface \"$interfaceName\", reachable from \"" . $outerInterface->name . "\"!\n");
352
353     print "  |  |>  Parsing parent IDL \"$filename\" for interface \"$interfaceName\"\n" if $verbose;
354
355     # Step #2: Parse the found IDL file (in quiet mode).
356     my $parser = IDLParser->new(1);
357     my $document = $parser->Parse($filename, $defines, $preprocessor);
358
359     foreach my $interface (@{$document->interfaces}) {
360         if ($interface->name eq $interfaceName) {
361             $cachedInterfaces->{$interfaceName} = $interface;
362             return $interface;
363         }
364     }
365
366     die("Could NOT find interface definition for $interfaceName in $filename");
367 }
368
369 # Helpers for all CodeGenerator***.pm modules
370
371 sub SkipIncludeHeader
372 {
373     my $object = shift;
374     my $type = shift;
375
376     # FIXME: This is a lot like !IsRefPtrType. Maybe they could share code?
377
378     return 1 if $object->IsPrimitiveType($type);
379     return 1 if $object->IsTypedArrayType($type);
380     return 1 if $type eq "Array";
381     return 1 if $type eq "BufferSource";
382     return 1 if $type eq "DOMString" or $type eq "USVString";
383     return 1 if $type eq "DOMTimeStamp";
384     return 1 if $type eq "SVGNumber";
385     return 1 if $type eq "any";
386
387     return 0;
388 }
389
390 sub IsNumericType
391 {
392     my ($object, $type) = @_;
393
394     return 1 if $integerTypeHash{$type};
395     return 1 if $floatingPointTypeHash{$type};
396     return 0;
397 }
398
399 sub IsStringOrEnumType
400 {
401     my ($object, $type) = @_;
402     
403     return 1 if $type eq "DOMString" or $type eq "USVString";
404     return 1 if $object->IsEnumType($type);
405     return 0;
406 }
407
408 sub IsIntegerType
409 {
410     my ($object, $type) = @_;
411
412     return 1 if $integerTypeHash{$type};
413     return 0;
414 }
415
416 sub IsFloatingPointType
417 {
418     my ($object, $type) = @_;
419
420     return 1 if $floatingPointTypeHash{$type};
421     return 0;
422 }
423
424 sub IsPrimitiveType
425 {
426     my ($object, $type) = @_;
427
428     return 1 if $primitiveTypeHash{$type};
429     return 1 if $object->IsNumericType($type);
430     return 0;
431 }
432
433 # Currently used outside WebKit in an internal Apple project; can be removed soon.
434 sub IsStringType
435 {
436     my ($object, $type) = @_;
437
438     return 1 if $type eq "DOMString";
439     return 1 if $type eq "USVString";
440     return 0;
441 }
442
443 sub IsEnumType
444 {
445     my ($object, $type) = @_;
446
447     return 1 if exists $enumTypeHash{$type};
448     return 0;
449 }
450
451 sub ValidEnumValues
452 {
453     my ($object, $type) = @_;
454
455     return @{$enumTypeHash{$type}};
456 }
457
458 sub HasEnumImplementationNameOverride
459 {
460     my ($object, $type) = @_;
461
462     return 1 if exists $enumTypeImplementationNameOverrides{$type};
463     return 0;
464 }
465
466 sub GetEnumImplementationNameOverride
467 {
468     my ($object, $type) = @_;
469
470     return $enumTypeImplementationNameOverrides{$type};
471 }
472
473 sub GetDictionaryByName
474 {
475     my ($object, $name) = @_;
476     die "GetDictionaryByName() was called with an undefined dictionary name" unless defined($name);
477
478     for my $dictionary (@{$useDocument->dictionaries}) {
479         return $dictionary if $dictionary->name eq $name;
480     }
481
482     return $cachedExternalDictionaries->{$name} if exists($cachedExternalDictionaries->{$name});
483
484     # Find the IDL file associated with the dictionary.
485     my $filename = $object->IDLFileForInterface($name) or return;
486
487     # Do a fast check to see if it seems to contain a dictionary.
488     my $fileContents = slurp($filename);
489
490     if ($fileContents =~ /\bdictionary\s+$name/gs) {
491         # Parse the IDL.
492         my $parser = IDLParser->new(1);
493         my $document = $parser->Parse($filename, $defines, $preprocessor);
494
495         foreach my $dictionary (@{$document->dictionaries}) {
496             next unless $dictionary->name eq $name;
497
498             $cachedExternalDictionaries->{$name} = $dictionary;
499             my $implementedAs = $dictionary->extendedAttributes->{ImplementedAs};
500             $dictionaryTypeImplementationNameOverrides{$dictionary->name} = $implementedAs if $implementedAs;
501             return $dictionary;
502         }
503     }
504     $cachedExternalDictionaries->{$name} = undef;
505 }
506
507 sub IsDictionaryType
508 {
509     my ($object, $type) = @_;
510
511     return $type =~ /^[A-Z]/ && defined($object->GetDictionaryByName($type));
512 }
513
514 # A dictionary defined in its own IDL file.
515 sub IsExternalDictionaryType
516 {
517     my ($object, $type) = @_;
518
519     return $object->IsDictionaryType($type) && defined($cachedExternalDictionaries->{$type});
520 }
521
522 sub HasDictionaryImplementationNameOverride
523 {
524     my ($object, $type) = @_;
525
526     return 1 if exists $dictionaryTypeImplementationNameOverrides{$type};
527     return 0;
528 }
529
530 sub GetDictionaryImplementationNameOverride
531 {
532     my ($object, $type) = @_;
533
534     return $dictionaryTypeImplementationNameOverrides{$type};
535 }
536
537 sub IsNonPointerType
538 {
539     my ($object, $type) = @_;
540
541     return 1 if $nonPointerTypeHash{$type};
542     return 1 if $object->IsPrimitiveType($type);
543     return 0;
544 }
545
546 sub IsSVGTypeNeedingTearOff
547 {
548     my $object = shift;
549     my $type = shift;
550
551     return 1 if exists $svgTypeNeedingTearOff{$type};
552     return 0;
553 }
554
555 sub IsSVGTypeWithWritablePropertiesNeedingTearOff
556 {
557     my $object = shift;
558     my $type = shift;
559
560     return 1 if $svgTypeWithWritablePropertiesNeedingTearOff{$type};
561     return 0;
562 }
563
564 sub IsTypedArrayType
565 {
566     my $object = shift;
567     my $type = shift;
568
569     return 1 if $typedArrayTypes{$type};
570     return 0;
571 }
572
573 sub IsRefPtrType
574 {
575     my $object = shift;
576     my $type = shift;
577
578     return 0 if $object->IsPrimitiveType($type);
579     return 0 if $object->IsDictionaryType($type);
580     return 0 if $object->IsEnumType($type);
581     return 0 if $object->IsSequenceOrFrozenArrayType($type);
582     return 0 if $type eq "DOMString" or $type eq "USVString";
583     return 0 if $type eq "any";
584
585     return 1;
586 }
587
588 sub GetSVGTypeNeedingTearOff
589 {
590     my $object = shift;
591     my $type = shift;
592
593     return $svgTypeNeedingTearOff{$type} if exists $svgTypeNeedingTearOff{$type};
594     return undef;
595 }
596
597 sub GetSVGWrappedTypeNeedingTearOff
598 {
599     my $object = shift;
600     my $type = shift;
601
602     my $svgTypeNeedingTearOff = $object->GetSVGTypeNeedingTearOff($type);
603     return $svgTypeNeedingTearOff if not $svgTypeNeedingTearOff;
604
605     if ($svgTypeNeedingTearOff =~ /SVGPropertyTearOff/) {
606         $svgTypeNeedingTearOff =~ s/SVGPropertyTearOff<//;
607     } elsif ($svgTypeNeedingTearOff =~ /SVGListPropertyTearOff/) {
608         $svgTypeNeedingTearOff =~ s/SVGListPropertyTearOff<//;
609     } elsif ($svgTypeNeedingTearOff =~ /SVGStaticListPropertyTearOff/) {
610         $svgTypeNeedingTearOff =~ s/SVGStaticListPropertyTearOff<//;
611     }  elsif ($svgTypeNeedingTearOff =~ /SVGTransformListPropertyTearOff/) {
612         $svgTypeNeedingTearOff =~ s/SVGTransformListPropertyTearOff<//;
613     } 
614
615     $svgTypeNeedingTearOff =~ s/>//;
616     return $svgTypeNeedingTearOff;
617 }
618
619 sub IsSVGAnimatedType
620 {
621     my $object = shift;
622     my $type = shift;
623
624     return $type =~ /^SVGAnimated/;
625 }
626
627 sub IsSequenceType
628 {
629     my $object = shift;
630     my $type = shift;
631
632     return $type =~ /^sequence</;
633 }
634
635 sub GetSequenceInnerType
636 {
637     my $object = shift;
638     my $type = shift;
639
640     return $1 if $type =~ /^sequence<([\w\d_\s]+)>.*/;
641     return "";
642 }
643
644 sub IsFrozenArrayType
645 {
646     my $object = shift;
647     my $type = shift;
648
649     return $type =~ /^FrozenArray</;
650 }
651
652 sub GetFrozenArrayInnerType
653 {
654     my $object = shift;
655     my $type = shift;
656
657     return $1 if $type =~ /^FrozenArray<([\w\d_\s]+)>.*/;
658     return "";
659 }
660
661 sub IsSequenceOrFrozenArrayType
662 {
663     my $object = shift;
664     my $type = shift;
665
666     return $object->IsSequenceType($type) || $object->IsFrozenArrayType($type);
667 }
668
669 sub GetSequenceOrFrozenArrayInnerType
670 {
671     my $object = shift;
672     my $type = shift;
673
674     return $object->GetSequenceInnerType($type) if $object->IsSequenceType($type);
675     return $object->GetFrozenArrayInnerType($type) if $object->IsFrozenArrayType($type);
676     return "";
677 }
678
679 # These match WK_lcfirst and WK_ucfirst defined in builtins_generator.py.
680 # Uppercase the first letter while respecting WebKit style guidelines.
681 # E.g., xmlEncoding becomes XMLEncoding, but xmlllang becomes Xmllang.
682 sub WK_ucfirst
683 {
684     my ($object, $param) = @_;
685     my $ret = ucfirst($param);
686     $ret =~ s/Xml/XML/ if $ret =~ /^Xml[^a-z]/;
687     $ret =~ s/Svg/SVG/ if $ret =~ /^Svg/;
688
689     return $ret;
690 }
691
692 # Lowercase the first letter while respecting WebKit style guidelines.
693 # URL becomes url, but SetURL becomes setURL.
694 sub WK_lcfirst
695 {
696     my ($object, $param) = @_;
697     my $ret = lcfirst($param);
698     $ret =~ s/dOM/dom/ if $ret =~ /^dOM/;
699     $ret =~ s/hTML/html/ if $ret =~ /^hTML/;
700     $ret =~ s/uRL/url/ if $ret =~ /^uRL/;
701     $ret =~ s/jS/js/ if $ret =~ /^jS/;
702     $ret =~ s/xML/xml/ if $ret =~ /^xML/;
703     $ret =~ s/xSLT/xslt/ if $ret =~ /^xSLT/;
704     $ret =~ s/cSS/css/ if $ret =~ /^cSS/;
705     $ret =~ s/rTC/rtc/ if $ret =~ /^rTC/;
706
707     # For HTML5 FileSystem API Flags attributes.
708     # (create is widely used to instantiate an object and must be avoided.)
709     $ret =~ s/^create/isCreate/ if $ret =~ /^create$/;
710     $ret =~ s/^exclusive/isExclusive/ if $ret =~ /^exclusive$/;
711
712     return $ret;
713 }
714
715 sub slurp {
716     my $file = shift;
717
718     open my $fh, '<', $file or die;
719     local $/ = undef;
720     my $content = <$fh>;
721     close $fh;
722     return $content;
723 }
724
725 sub trim
726 {
727     my $string = shift;
728     $string =~ s/^\s+|\s+$//g;
729     return $string;
730 }
731
732 # Return the C++ namespace that a given attribute name string is defined in.
733 sub NamespaceForAttributeName
734 {
735     my ($object, $interfaceName, $attributeName) = @_;
736     return "SVGNames" if $interfaceName =~ /^SVG/ && !$svgAttributesInHTMLHash{$attributeName};
737     return "HTMLNames";
738 }
739
740 # Identifies overloaded functions and for each function adds an array with
741 # links to its respective overloads (including itself).
742 sub LinkOverloadedFunctions
743 {
744     my ($object, $interface) = @_;
745
746     my %nameToFunctionsMap = ();
747     foreach my $function (@{$interface->functions}) {
748         my $name = $function->signature->name;
749         $nameToFunctionsMap{$name} = [] if !exists $nameToFunctionsMap{$name};
750         push(@{$nameToFunctionsMap{$name}}, $function);
751         $function->{overloads} = $nameToFunctionsMap{$name};
752         $function->{overloadIndex} = @{$nameToFunctionsMap{$name}};
753     }
754
755     my $index = 1;
756     foreach my $constructor (@{$interface->constructors}) {
757         $constructor->{overloads} = $interface->constructors;
758         $constructor->{overloadIndex} = $index;
759         $index++;
760     }
761 }
762
763 sub AttributeNameForGetterAndSetter
764 {
765     my ($generator, $attribute) = @_;
766
767     my $attributeName = $attribute->signature->name;
768     if ($attribute->signature->extendedAttributes->{"ImplementedAs"}) {
769         $attributeName = $attribute->signature->extendedAttributes->{"ImplementedAs"};
770     }
771     my $attributeType = $attribute->signature->type;
772
773     # SVG animated types need to use a special attribute name.
774     # The rest of the special casing for SVG animated types is handled in the language-specific code generators.
775     $attributeName .= "Animated" if $generator->IsSVGAnimatedType($attributeType);
776
777     return $attributeName;
778 }
779
780 sub ContentAttributeName
781 {
782     my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
783
784     my $contentAttributeName = $attribute->signature->extendedAttributes->{"Reflect"};
785     return undef if !$contentAttributeName;
786
787     $contentAttributeName = lc $generator->AttributeNameForGetterAndSetter($attribute) if $contentAttributeName eq "VALUE_IS_MISSING";
788
789     my $namespace = $generator->NamespaceForAttributeName($interfaceName, $contentAttributeName);
790
791     $implIncludes->{"${namespace}.h"} = 1;
792     return "WebCore::${namespace}::${contentAttributeName}Attr";
793 }
794
795 sub GetterExpression
796 {
797     my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
798
799     my $contentAttributeName = $generator->ContentAttributeName($implIncludes, $interfaceName, $attribute);
800
801     if (!$contentAttributeName) {
802         return ($generator->WK_lcfirst($generator->AttributeNameForGetterAndSetter($attribute)));
803     }
804
805     my $attributeType = $attribute->signature->type;
806
807     my $functionName;
808     if ($attribute->signature->extendedAttributes->{"URL"}) {
809         $functionName = "getURLAttribute";
810     } elsif ($attributeType eq "boolean") {
811         $functionName = "hasAttributeWithoutSynchronization";
812     } elsif ($attributeType eq "long") {
813         $functionName = "getIntegralAttribute";
814     } elsif ($attributeType eq "unsigned long") {
815         $functionName = "getUnsignedIntegralAttribute";
816     } else {
817         if ($contentAttributeName eq "WebCore::HTMLNames::idAttr") {
818             $functionName = "getIdAttribute";
819             $contentAttributeName = "";
820         } elsif ($contentAttributeName eq "WebCore::HTMLNames::nameAttr") {
821             $functionName = "getNameAttribute";
822             $contentAttributeName = "";
823         } elsif ($generator->IsSVGAnimatedType($attributeType)) {
824             $functionName = "getAttribute";
825         } else {
826             $functionName = "attributeWithoutSynchronization";
827         }
828     }
829
830     return ($functionName, $contentAttributeName);
831 }
832
833 sub SetterExpression
834 {
835     my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
836
837     my $contentAttributeName = $generator->ContentAttributeName($implIncludes, $interfaceName, $attribute);
838
839     if (!$contentAttributeName) {
840         return ("set" . $generator->WK_ucfirst($generator->AttributeNameForGetterAndSetter($attribute)));
841     }
842
843     my $attributeType = $attribute->signature->type;
844
845     my $functionName;
846     if ($attributeType eq "boolean") {
847         $functionName = "setBooleanAttribute";
848     } elsif ($attributeType eq "long") {
849         $functionName = "setIntegralAttribute";
850     } elsif ($attributeType eq "unsigned long") {
851         $functionName = "setUnsignedIntegralAttribute";
852     } elsif ($generator->IsSVGAnimatedType($attributeType)) {
853         $functionName = "setAttribute";
854     } else {
855         $functionName = "setAttributeWithoutSynchronization";
856     }
857
858     return ($functionName, $contentAttributeName);
859 }
860
861 sub IsWrapperType
862 {
863     my $object = shift;
864     my $type = shift;
865
866     return 0 if !$object->IsRefPtrType($type);
867     return 0 if $object->IsTypedArrayType($type);
868     return 0 if $type eq "BufferSource";
869     return 0 if $type eq "UNION";
870     return 0 if $webCoreTypeHash{$type};
871
872     return 1;
873 }
874
875 sub getInterfaceExtendedAttributesFromName
876 {
877     # FIXME: It's bad to have a function like this that opens another IDL file to answer a question.
878     # Overusing this kind of function can make things really slow. Lets avoid these if we can.
879
880     my $object = shift;
881     my $interfaceName = shift;
882
883     my $idlFile = $object->IDLFileForInterface($interfaceName) or assert("Could NOT find IDL file for interface \"$interfaceName\"!\n");
884
885     open FILE, "<", $idlFile or die;
886     my @lines = <FILE>;
887     close FILE;
888
889     my $fileContents = join('', @lines);
890
891     my $extendedAttributes = {};
892
893     if ($fileContents =~ /\[(.*)\]\s+(callback interface|interface|exception)\s+(\w+)/gs) {
894         my @parts = split(',', $1);
895         foreach my $part (@parts) {
896             my @keyValue = split('=', $part);
897             my $key = trim($keyValue[0]);
898             next unless length($key);
899             my $value = "VALUE_IS_MISSING";
900             $value = trim($keyValue[1]) if @keyValue > 1;
901             $extendedAttributes->{$key} = $value;
902         }
903     }
904
905     return $extendedAttributes;
906 }
907
908 sub ComputeIsCallbackInterface
909 {
910   my $object = shift;
911   my $type = shift;
912
913   return 0 unless $object->IsWrapperType($type);
914
915   my $idlFile = $object->IDLFileForInterface($type) or assert("Could NOT find IDL file for interface \"$type\"!\n");
916
917   open FILE, "<", $idlFile or die;
918   my @lines = <FILE>;
919   close FILE;
920
921   my $fileContents = join('', @lines);
922   return ($fileContents =~ /callback\s+interface\s+(\w+)/gs);
923 }
924
925 my %isCallbackInterface = ();
926
927 sub IsCallbackInterface
928 {
929     # FIXME: It's bad to have a function like this that opens another IDL file to answer a question.
930     # Overusing this kind of function can make things really slow. Lets avoid these if we can.
931     # To mitigate that, lets cache what we learn in a hash so we don't open the same file over and over.
932
933     my ($object, $type) = @_;
934
935     return $isCallbackInterface{$type} if exists $isCallbackInterface{$type};
936     my $result = ComputeIsCallbackInterface($object, $type);
937     $isCallbackInterface{$type} = $result;
938     return $result;
939 }
940
941 # Callback interface with [Callback=FunctionOnly].
942 # FIXME: This should be a callback function:
943 # https://heycam.github.io/webidl/#idl-callback-functions
944 sub ComputeIsFunctionOnlyCallbackInterface
945 {
946   my $object = shift;
947   my $type = shift;
948
949   return 0 unless $object->IsCallbackInterface($type);
950
951   my $idlFile = $object->IDLFileForInterface($type) or assert("Could NOT find IDL file for interface \"$type\"!\n");
952
953   open FILE, "<", $idlFile or die;
954   my @lines = <FILE>;
955   close FILE;
956
957   my $fileContents = join('', @lines);
958   if ($fileContents =~ /\[(.*)\]\s+callback\s+interface\s+(\w+)/gs) {
959       my @parts = split(',', $1);
960       foreach my $part (@parts) {
961           my @keyValue = split('=', $part);
962           my $key = trim($keyValue[0]);
963           next unless length($key);
964           my $value = "VALUE_IS_MISSING";
965           $value = trim($keyValue[1]) if @keyValue > 1;
966
967           return 1 if ($key eq "Callback" && $value eq "FunctionOnly");
968       }
969   }
970
971   return 0;
972 }
973
974 my %isFunctionOnlyCallbackInterface = ();
975
976 sub IsFunctionOnlyCallbackInterface
977 {
978     # FIXME: It's bad to have a function like this that opens another IDL file to answer a question.
979     # Overusing this kind of function can make things really slow. Lets avoid these if we can.
980     # To mitigate that, lets cache what we learn in a hash so we don't open the same file over and over.
981
982     my ($object, $type) = @_;
983
984     return $isFunctionOnlyCallbackInterface{$type} if exists $isFunctionOnlyCallbackInterface{$type};
985     my $result = ComputeIsFunctionOnlyCallbackInterface($object, $type);
986     $isFunctionOnlyCallbackInterface{$type} = $result;
987     return $result;
988 }
989
990 sub GenerateConditionalString
991 {
992     my $generator = shift;
993     my $node = shift;
994
995     my $conditional = $node->extendedAttributes->{"Conditional"};
996     if ($conditional) {
997         return $generator->GenerateConditionalStringFromAttributeValue($conditional);
998     } else {
999         return "";
1000     }
1001 }
1002
1003 sub GenerateConditionalStringFromAttributeValue
1004 {
1005     my $generator = shift;
1006     my $conditional = shift;
1007
1008     my %disjunction;
1009     map {
1010         my $expression = $_;
1011         my %conjunction;
1012         map { $conjunction{$_} = 1; } split(/&/, $expression);
1013         $expression = "ENABLE(" . join(") && ENABLE(", sort keys %conjunction) . ")";
1014         $disjunction{$expression} = 1
1015     } split(/\|/, $conditional);
1016
1017     return "1" if keys %disjunction == 0;
1018     return (%disjunction)[0] if keys %disjunction == 1;
1019
1020     my @parenthesized;
1021     map {
1022         my $expression = $_;
1023         $expression = "($expression)" if $expression =~ / /;
1024         push @parenthesized, $expression;
1025     } sort keys %disjunction;
1026
1027     return join(" || ", @parenthesized);
1028 }
1029
1030 sub GenerateCompileTimeCheckForEnumsIfNeeded
1031 {
1032     my ($generator, $interface) = @_;
1033
1034     return () if $interface->extendedAttributes->{"DoNotCheckConstants"} || !@{$interface->constants};
1035
1036     my @checks = ();
1037     foreach my $constant (@{$interface->constants}) {
1038         my $className = $constant->extendedAttributes->{"ImplementedBy"} || $interface->name;
1039         my $name = $constant->extendedAttributes->{"Reflect"} || $constant->name;
1040         my $value = $constant->value;
1041         my $conditional = $constant->extendedAttributes->{"Conditional"};
1042         push(@checks, "#if " . $generator->GenerateConditionalStringFromAttributeValue($conditional) . "\n") if $conditional;
1043         push(@checks, "static_assert(${className}::$name == $value, \"$name in $className does not match value from IDL\");\n");
1044         push(@checks, "#endif\n") if $conditional;
1045     }
1046     push(@checks, "\n");
1047     return @checks;
1048 }
1049
1050 sub ExtendedAttributeContains
1051 {
1052     my $object = shift;
1053     my $callWith = shift;
1054     return 0 unless $callWith;
1055     my $keyword = shift;
1056
1057     my @callWithKeywords = split /\s*\&\s*/, $callWith;
1058     return grep { $_ eq $keyword } @callWithKeywords;
1059 }
1060
1061 # FIXME: This is backwards. We currently name the interface and the IDL files with the implementation name. We
1062 # should use the real interface name in the IDL files and then use ImplementedAs to map this to the implementation name.
1063 sub GetVisibleInterfaceName
1064 {
1065     my $object = shift;
1066     my $interface = shift;
1067     my $interfaceName = $interface->extendedAttributes->{"InterfaceName"};
1068     return $interfaceName ? $interfaceName : $interface->name;
1069 }
1070
1071 sub InheritsInterface
1072 {
1073     my $object = shift;
1074     my $interface = shift;
1075     my $interfaceName = shift;
1076     my $found = 0;
1077
1078     return 1 if $interfaceName eq $interface->name;
1079     $object->ForAllParents($interface, sub {
1080         my $currentInterface = shift;
1081         if ($currentInterface->name eq $interfaceName) {
1082             $found = 1;
1083         }
1084         return 1 if $found;
1085     }, 0);
1086
1087     return $found;
1088 }
1089
1090 sub InheritsExtendedAttribute
1091 {
1092     my $object = shift;
1093     my $interface = shift;
1094     my $extendedAttribute = shift;
1095     my $found = 0;
1096
1097     return 1 if $interface->extendedAttributes->{$extendedAttribute};
1098     $object->ForAllParents($interface, sub {
1099         my $currentInterface = shift;
1100         if ($currentInterface->extendedAttributes->{$extendedAttribute}) {
1101             $found = 1;
1102         }
1103         return 1 if $found;
1104     }, 0);
1105
1106     return $found;
1107 }
1108
1109 sub ShouldPassWrapperByReference
1110 {
1111     my $object = shift;
1112     my $parameter = shift;
1113     my $interface = shift;
1114
1115     return 0 if $parameter->isVariadic;
1116     return 0 if $parameter->isNullable;
1117     return 0 if !$object->IsWrapperType($parameter->type) && !$object->IsTypedArrayType($parameter->type);
1118     return 0 if $object->IsSVGTypeNeedingTearOff($parameter->type);
1119
1120     return 1;
1121 }
1122
1123 1;