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