[Web IDL] Drop support for legacy [ConstructorConditional=*]
[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::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);
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 "DOMString" or $type eq "USVString";
375     return 1 if $type eq "DOMTimeStamp";
376     return 1 if $type eq "SVGNumber";
377     return 1 if $type eq "any";
378
379     return 0;
380 }
381
382 sub IsConstructorTemplate
383 {
384     my $object = shift;
385     my $interface = shift;
386     my $template = shift;
387
388     return $interface->extendedAttributes->{"ConstructorTemplate"} && $interface->extendedAttributes->{"ConstructorTemplate"} eq $template;
389 }
390
391 sub IsNumericType
392 {
393     my ($object, $type) = @_;
394
395     return 1 if $integerTypeHash{$type};
396     return 1 if $floatingPointTypeHash{$type};
397     return 0;
398 }
399
400 sub IsStringOrEnumType
401 {
402     my ($object, $type) = @_;
403     
404     return 1 if $type eq "DOMString" or $type eq "USVString";
405     return 1 if $object->IsEnumType($type);
406     return 0;
407 }
408
409 sub IsIntegerType
410 {
411     my ($object, $type) = @_;
412
413     return 1 if $integerTypeHash{$type};
414     return 0;
415 }
416
417 sub IsFloatingPointType
418 {
419     my ($object, $type) = @_;
420
421     return 1 if $floatingPointTypeHash{$type};
422     return 0;
423 }
424
425 sub IsPrimitiveType
426 {
427     my ($object, $type) = @_;
428
429     return 1 if $primitiveTypeHash{$type};
430     return 1 if $object->IsNumericType($type);
431     return 0;
432 }
433
434 # Currently used outside WebKit in an internal Apple project; can be removed soon.
435 sub IsStringType
436 {
437     my ($object, $type) = @_;
438
439     return 1 if $type eq "DOMString";
440     return 1 if $type eq "USVString";
441     return 0;
442 }
443
444 sub IsEnumType
445 {
446     my ($object, $type) = @_;
447
448     return 1 if exists $enumTypeHash{$type};
449     return 0;
450 }
451
452 sub ValidEnumValues
453 {
454     my ($object, $type) = @_;
455
456     return @{$enumTypeHash{$type}};
457 }
458
459 sub HasEnumImplementationNameOverride
460 {
461     my ($object, $type) = @_;
462
463     return 1 if exists $enumTypeImplementationNameOverrides{$type};
464     return 0;
465 }
466
467 sub GetEnumImplementationNameOverride
468 {
469     my ($object, $type) = @_;
470
471     return $enumTypeImplementationNameOverrides{$type};
472 }
473
474 sub GetDictionaryByName
475 {
476     my ($object, $name) = @_;
477     die "GetDictionaryByName() was called with an undefined dictionary name" unless defined($name);
478
479     for my $dictionary (@{$useDocument->dictionaries}) {
480         return $dictionary if $dictionary->name eq $name;
481     }
482
483     return $cachedExternalDictionaries->{$name} if exists($cachedExternalDictionaries->{$name});
484
485     # Find the IDL file associated with the dictionary.
486     my $filename = $object->IDLFileForInterface($name) or return;
487
488     # Do a fast check to see if it seems to contain a dictionary.
489     my $fileContents = slurp($filename);
490
491     if ($fileContents =~ /\bdictionary\s+$name/gs) {
492         # Parse the IDL.
493         my $parser = IDLParser->new(1);
494         my $document = $parser->Parse($filename, $defines, $preprocessor);
495
496         foreach my $dictionary (@{$document->dictionaries}) {
497             next unless $dictionary->name eq $name;
498
499             $cachedExternalDictionaries->{$name} = $dictionary;
500             my $implementedAs = $dictionary->extendedAttributes->{ImplementedAs};
501             $dictionaryTypeImplementationNameOverrides{$dictionary->name} = $implementedAs if $implementedAs;
502             return $dictionary;
503         }
504     }
505     $cachedExternalDictionaries->{$name} = undef;
506 }
507
508 sub IsDictionaryType
509 {
510     my ($object, $type) = @_;
511
512     return $type =~ /^[A-Z]/ && defined($object->GetDictionaryByName($type));
513 }
514
515 # A dictionary defined in its own IDL file.
516 sub IsExternalDictionaryType
517 {
518     my ($object, $type) = @_;
519
520     return $object->IsDictionaryType($type) && defined($cachedExternalDictionaries->{$type});
521 }
522
523 sub HasDictionaryImplementationNameOverride
524 {
525     my ($object, $type) = @_;
526
527     return 1 if exists $dictionaryTypeImplementationNameOverrides{$type};
528     return 0;
529 }
530
531 sub GetDictionaryImplementationNameOverride
532 {
533     my ($object, $type) = @_;
534
535     return $dictionaryTypeImplementationNameOverrides{$type};
536 }
537
538 sub IsNonPointerType
539 {
540     my ($object, $type) = @_;
541
542     return 1 if $nonPointerTypeHash{$type};
543     return 1 if $object->IsPrimitiveType($type);
544     return 0;
545 }
546
547 sub IsSVGTypeNeedingTearOff
548 {
549     my $object = shift;
550     my $type = shift;
551
552     return 1 if exists $svgTypeNeedingTearOff{$type};
553     return 0;
554 }
555
556 sub IsSVGTypeWithWritablePropertiesNeedingTearOff
557 {
558     my $object = shift;
559     my $type = shift;
560
561     return 1 if $svgTypeWithWritablePropertiesNeedingTearOff{$type};
562     return 0;
563 }
564
565 sub IsTypedArrayType
566 {
567     my $object = shift;
568     my $type = shift;
569
570     return 1 if $typedArrayTypes{$type};
571     return 0;
572 }
573
574 sub IsRefPtrType
575 {
576     my $object = shift;
577     my $type = shift;
578
579     return 0 if $object->IsPrimitiveType($type);
580     return 0 if $object->IsDictionaryType($type);
581     return 0 if $object->IsEnumType($type);
582     return 0 if $object->IsSequenceOrFrozenArrayType($type);
583     return 0 if $type eq "DOMString" or $type eq "USVString";
584     return 0 if $type eq "any";
585
586     return 1;
587 }
588
589 sub GetSVGTypeNeedingTearOff
590 {
591     my $object = shift;
592     my $type = shift;
593
594     return $svgTypeNeedingTearOff{$type} if exists $svgTypeNeedingTearOff{$type};
595     return undef;
596 }
597
598 sub GetSVGWrappedTypeNeedingTearOff
599 {
600     my $object = shift;
601     my $type = shift;
602
603     my $svgTypeNeedingTearOff = $object->GetSVGTypeNeedingTearOff($type);
604     return $svgTypeNeedingTearOff if not $svgTypeNeedingTearOff;
605
606     if ($svgTypeNeedingTearOff =~ /SVGPropertyTearOff/) {
607         $svgTypeNeedingTearOff =~ s/SVGPropertyTearOff<//;
608     } elsif ($svgTypeNeedingTearOff =~ /SVGListPropertyTearOff/) {
609         $svgTypeNeedingTearOff =~ s/SVGListPropertyTearOff<//;
610     } elsif ($svgTypeNeedingTearOff =~ /SVGStaticListPropertyTearOff/) {
611         $svgTypeNeedingTearOff =~ s/SVGStaticListPropertyTearOff<//;
612     }  elsif ($svgTypeNeedingTearOff =~ /SVGTransformListPropertyTearOff/) {
613         $svgTypeNeedingTearOff =~ s/SVGTransformListPropertyTearOff<//;
614     } 
615
616     $svgTypeNeedingTearOff =~ s/>//;
617     return $svgTypeNeedingTearOff;
618 }
619
620 sub IsSVGAnimatedType
621 {
622     my $object = shift;
623     my $type = shift;
624
625     return $type =~ /^SVGAnimated/;
626 }
627
628 sub IsSequenceType
629 {
630     my $object = shift;
631     my $type = shift;
632
633     return $type =~ /^sequence</;
634 }
635
636 sub GetSequenceInnerType
637 {
638     my $object = shift;
639     my $type = shift;
640
641     return $1 if $type =~ /^sequence<([\w\d_\s]+)>.*/;
642     return "";
643 }
644
645 sub IsFrozenArrayType
646 {
647     my $object = shift;
648     my $type = shift;
649
650     return $type =~ /^FrozenArray</;
651 }
652
653 sub GetFrozenArrayInnerType
654 {
655     my $object = shift;
656     my $type = shift;
657
658     return $1 if $type =~ /^FrozenArray<([\w\d_\s]+)>.*/;
659     return "";
660 }
661
662 sub IsSequenceOrFrozenArrayType
663 {
664     my $object = shift;
665     my $type = shift;
666
667     return $object->IsSequenceType($type) || $object->IsFrozenArrayType($type);
668 }
669
670 sub GetSequenceOrFrozenArrayInnerType
671 {
672     my $object = shift;
673     my $type = shift;
674
675     return $object->GetSequenceInnerType($type) if $object->IsSequenceType($type);
676     return $object->GetFrozenArrayInnerType($type) if $object->IsFrozenArrayType($type);
677     return "";
678 }
679
680 # These match WK_lcfirst and WK_ucfirst defined in builtins_generator.py.
681 # Uppercase the first letter while respecting WebKit style guidelines.
682 # E.g., xmlEncoding becomes XMLEncoding, but xmlllang becomes Xmllang.
683 sub WK_ucfirst
684 {
685     my ($object, $param) = @_;
686     my $ret = ucfirst($param);
687     $ret =~ s/Xml/XML/ if $ret =~ /^Xml[^a-z]/;
688     $ret =~ s/Svg/SVG/ if $ret =~ /^Svg/;
689
690     return $ret;
691 }
692
693 # Lowercase the first letter while respecting WebKit style guidelines.
694 # URL becomes url, but SetURL becomes setURL.
695 sub WK_lcfirst
696 {
697     my ($object, $param) = @_;
698     my $ret = lcfirst($param);
699     $ret =~ s/dOM/dom/ if $ret =~ /^dOM/;
700     $ret =~ s/hTML/html/ if $ret =~ /^hTML/;
701     $ret =~ s/uRL/url/ if $ret =~ /^uRL/;
702     $ret =~ s/jS/js/ if $ret =~ /^jS/;
703     $ret =~ s/xML/xml/ if $ret =~ /^xML/;
704     $ret =~ s/xSLT/xslt/ if $ret =~ /^xSLT/;
705     $ret =~ s/cSS/css/ if $ret =~ /^cSS/;
706     $ret =~ s/rTC/rtc/ if $ret =~ /^rTC/;
707
708     # For HTML5 FileSystem API Flags attributes.
709     # (create is widely used to instantiate an object and must be avoided.)
710     $ret =~ s/^create/isCreate/ if $ret =~ /^create$/;
711     $ret =~ s/^exclusive/isExclusive/ if $ret =~ /^exclusive$/;
712
713     return $ret;
714 }
715
716 sub slurp {
717     my $file = shift;
718
719     open my $fh, '<', $file or die;
720     local $/ = undef;
721     my $content = <$fh>;
722     close $fh;
723     return $content;
724 }
725
726 sub trim
727 {
728     my $string = shift;
729     $string =~ s/^\s+|\s+$//g;
730     return $string;
731 }
732
733 # Return the C++ namespace that a given attribute name string is defined in.
734 sub NamespaceForAttributeName
735 {
736     my ($object, $interfaceName, $attributeName) = @_;
737     return "SVGNames" if $interfaceName =~ /^SVG/ && !$svgAttributesInHTMLHash{$attributeName};
738     return "HTMLNames";
739 }
740
741 # Identifies overloaded functions and for each function adds an array with
742 # links to its respective overloads (including itself).
743 sub LinkOverloadedFunctions
744 {
745     my ($object, $interface) = @_;
746
747     my %nameToFunctionsMap = ();
748     foreach my $function (@{$interface->functions}) {
749         my $name = $function->signature->name;
750         $nameToFunctionsMap{$name} = [] if !exists $nameToFunctionsMap{$name};
751         push(@{$nameToFunctionsMap{$name}}, $function);
752         $function->{overloads} = $nameToFunctionsMap{$name};
753         $function->{overloadIndex} = @{$nameToFunctionsMap{$name}};
754     }
755
756     my $index = 1;
757     foreach my $constructor (@{$interface->constructors}) {
758         $constructor->{overloads} = $interface->constructors;
759         $constructor->{overloadIndex} = $index;
760         $index++;
761     }
762 }
763
764 sub AttributeNameForGetterAndSetter
765 {
766     my ($generator, $attribute) = @_;
767
768     my $attributeName = $attribute->signature->name;
769     if ($attribute->signature->extendedAttributes->{"ImplementedAs"}) {
770         $attributeName = $attribute->signature->extendedAttributes->{"ImplementedAs"};
771     }
772     my $attributeType = $attribute->signature->type;
773
774     # SVG animated types need to use a special attribute name.
775     # The rest of the special casing for SVG animated types is handled in the language-specific code generators.
776     $attributeName .= "Animated" if $generator->IsSVGAnimatedType($attributeType);
777
778     return $attributeName;
779 }
780
781 sub ContentAttributeName
782 {
783     my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
784
785     my $contentAttributeName = $attribute->signature->extendedAttributes->{"Reflect"};
786     return undef if !$contentAttributeName;
787
788     $contentAttributeName = lc $generator->AttributeNameForGetterAndSetter($attribute) if $contentAttributeName eq "VALUE_IS_MISSING";
789
790     my $namespace = $generator->NamespaceForAttributeName($interfaceName, $contentAttributeName);
791
792     $implIncludes->{"${namespace}.h"} = 1;
793     return "WebCore::${namespace}::${contentAttributeName}Attr";
794 }
795
796 sub GetterExpression
797 {
798     my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
799
800     my $contentAttributeName = $generator->ContentAttributeName($implIncludes, $interfaceName, $attribute);
801
802     if (!$contentAttributeName) {
803         return ($generator->WK_lcfirst($generator->AttributeNameForGetterAndSetter($attribute)));
804     }
805
806     my $attributeType = $attribute->signature->type;
807
808     my $functionName;
809     if ($attribute->signature->extendedAttributes->{"URL"}) {
810         $functionName = "getURLAttribute";
811     } elsif ($attributeType eq "boolean") {
812         $functionName = "hasAttributeWithoutSynchronization";
813     } elsif ($attributeType eq "long") {
814         $functionName = "getIntegralAttribute";
815     } elsif ($attributeType eq "unsigned long") {
816         $functionName = "getUnsignedIntegralAttribute";
817     } else {
818         if ($contentAttributeName eq "WebCore::HTMLNames::idAttr") {
819             $functionName = "getIdAttribute";
820             $contentAttributeName = "";
821         } elsif ($contentAttributeName eq "WebCore::HTMLNames::nameAttr") {
822             $functionName = "getNameAttribute";
823             $contentAttributeName = "";
824         } elsif ($generator->IsSVGAnimatedType($attributeType)) {
825             $functionName = "getAttribute";
826         } else {
827             $functionName = "attributeWithoutSynchronization";
828         }
829     }
830
831     return ($functionName, $contentAttributeName);
832 }
833
834 sub SetterExpression
835 {
836     my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
837
838     my $contentAttributeName = $generator->ContentAttributeName($implIncludes, $interfaceName, $attribute);
839
840     if (!$contentAttributeName) {
841         return ("set" . $generator->WK_ucfirst($generator->AttributeNameForGetterAndSetter($attribute)));
842     }
843
844     my $attributeType = $attribute->signature->type;
845
846     my $functionName;
847     if ($attributeType eq "boolean") {
848         $functionName = "setBooleanAttribute";
849     } elsif ($attributeType eq "long") {
850         $functionName = "setIntegralAttribute";
851     } elsif ($attributeType eq "unsigned long") {
852         $functionName = "setUnsignedIntegralAttribute";
853     } elsif ($generator->IsSVGAnimatedType($attributeType)) {
854         $functionName = "setAttribute";
855     } else {
856         $functionName = "setAttributeWithoutSynchronization";
857     }
858
859     return ($functionName, $contentAttributeName);
860 }
861
862 sub IsWrapperType
863 {
864     my $object = shift;
865     my $type = shift;
866
867     return 0 if !$object->IsRefPtrType($type);
868     return 0 if $object->IsTypedArrayType($type);
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;