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