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