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.
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.
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.
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.
27 package CodeGenerator;
32 use Carp qw<longmess>;
36 my $useGenerator = "";
37 my $useOutputDir = "";
38 my $useOutputHeadersDir = "";
39 my $useDirectories = "";
41 my $writeDependencies = 0;
43 my $targetIdlFilePath = "";
45 my $codeGenerator = 0;
49 my %integerTypeHash = (
55 "unsigned long long" => 1,
57 "unsigned short" => 1,
60 my %floatingPointTypeHash = (
62 "unrestricted float" => 1,
64 "unrestricted double" => 1,
67 my %primitiveTypeHash = ( "boolean" => 1, "void" => 1, "Date" => 1 );
69 # WebCore types used directly in IDL files.
70 my %webCoreTypeHash = (
72 "SerializedScriptValue" => 1,
75 my %dictionaryTypes = ();
76 my %dictionaryTypeImplementationNameOverrides = ();
78 my %enumTypeHash = ();
79 my %enumTypeImplementationNameOverrides = ();
82 my %typedArrayTypes = (
84 "ArrayBufferView" => 1,
94 "Uint8ClampedArray" => 1,
97 my %nonPointerTypeHash = ( "DOMTimeStamp" => 1 );
99 my %svgAttributesInHTMLHash = (
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"
135 my %svgTypeWithWritablePropertiesNeedingTearOff = (
140 # Cache of IDL file pathnames.
142 my $cachedInterfaces = {};
143 my $cachedExternalDictionaries = {};
149 my $mess = longmess();
155 # Default constructor
161 $useDirectories = shift;
162 $useGenerator = shift;
163 $useOutputDir = shift;
164 $useOutputHeadersDir = shift;
165 $preprocessor = shift;
166 $writeDependencies = shift;
168 $targetIdlFilePath = shift;
170 bless($reference, $object);
177 $useDocument = shift;
180 my $ifaceName = "CodeGenerator" . $useGenerator;
181 require $ifaceName . ".pm";
183 foreach my $dictionary (@{$useDocument->dictionaries}) {
184 $dictionaryTypes{$dictionary->name} = 1;
185 if ($dictionary->extendedAttributes->{"ImplementedAs"}) {
186 $dictionaryTypeImplementationNameOverrides{$dictionary->name} = $dictionary->extendedAttributes->{"ImplementedAs"};
190 foreach my $enumeration (@{$useDocument->enumerations}) {
191 $enumTypeHash{$enumeration->name} = $enumeration->values;
192 if ($enumeration->extendedAttributes->{"ImplementedAs"}) {
193 $enumTypeImplementationNameOverrides{$enumeration->name} = $enumeration->extendedAttributes->{"ImplementedAs"};
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;
207 my $interfaces = $useDocument->interfaces;
209 die "Multiple interfaces per document are not supported" if @$interfaces > 1;
211 my $interface = @$interfaces[0];
212 print "Generating $useGenerator bindings code for IDL interface \"" . $interface->name . "\"...\n" if $verbose;
213 $codeGenerator->GenerateInterface($interface, $defines, $useDocument->enumerations, $useDocument->dictionaries);
214 $codeGenerator->WriteData($interface, $useOutputDir, $useOutputHeadersDir);
218 my $dictionaries = $useDocument->dictionaries;
219 if (@$dictionaries) {
220 die "Multiple standalone dictionaries per document are not supported" if @$dictionaries > 1;
222 my $dictionary = @$dictionaries[0];
223 print "Generating $useGenerator bindings code for IDL dictionary \"" . $dictionary->name . "\"...\n" if $verbose;
224 $codeGenerator->GenerateDictionary($dictionary, $useDocument->enumerations);
225 $codeGenerator->WriteData($dictionary, $useOutputDir, $useOutputHeadersDir);
229 die "Processing document " . $useDocument->fileName . " did not generate anything"
236 my $ifaceName = "CodeGenerator" . $useGenerator;
237 require $ifaceName . ".pm";
239 # Dynamically load external code generation perl module
240 $codeGenerator = $ifaceName->new($object, $writeDependencies, $verbose);
241 return $codeGenerator->FileNamePrefix();
247 my $fileName = shift;
248 my $contents = shift;
250 # FIXME: We should only write content if it is different from what is in the file.
251 # But that would mean running more often the binding generator, see https://bugs.webkit.org/show_bug.cgi?id=131756
252 open FH, ">", $fileName or die "Couldn't open $fileName: $!\n";
260 my $interface = shift;
261 my $beforeRecursion = shift;
262 my $afterRecursion = shift;
266 my $outerInterface = shift;
267 my $currentInterface = shift;
269 for (@{$currentInterface->parents}) {
270 my $interfaceName = $_;
271 my $parentInterface = $object->ParseInterface($outerInterface, $interfaceName);
273 if ($beforeRecursion) {
274 &$beforeRecursion($parentInterface) eq 'prune' and next;
276 &$recurse($outerInterface, $parentInterface);
277 &$afterRecursion($parentInterface) if $afterRecursion;
281 &$recurse($interface, $interface);
286 my ($object, $interface, $functionName) = @_;
288 $object->ForAllParents($interface, undef, sub {
289 my $currentInterface = shift;
290 foreach my $function (@{$currentInterface->functions}) {
291 if ($function->signature->name eq $functionName) {
292 $indexer = $function->signature;
300 sub IDLFileForInterface
303 my $interfaceName = shift;
306 my $sourceRoot = $ENV{SOURCE_ROOT};
307 my @directories = map { $_ = "$sourceRoot/$_" if $sourceRoot && -d "$sourceRoot/$_"; $_ } @$useDirectories;
308 push(@directories, ".");
313 $idlFiles->{$1} = $File::Find::name if /^([A-Z].*)\.idl$/;
314 $File::Find::prune = 1 if /^\../;
316 find($wanted, @directories);
319 return $idlFiles->{$interfaceName};
322 sub GetAttributeFromInterface()
325 my $outerInterface = shift;
326 my $interfaceName = shift;
327 my $attributeName = shift;
329 my $interface = $object->ParseInterface($outerInterface, $interfaceName);
330 for my $attribute (@{$interface->attributes}) {
331 return $attribute if $attribute->signature->name eq $attributeName;
333 die("Could not find attribute '$attributeName' on interface '$interfaceName'.");
339 my $outerInterface = shift;
340 my $interfaceName = shift;
342 return undef if $interfaceName eq 'Object';
343 return undef if $interfaceName eq 'UNION';
345 if (exists $cachedInterfaces->{$interfaceName}) {
346 return $cachedInterfaces->{$interfaceName};
349 # Step #1: Find the IDL file associated with 'interface'
350 my $filename = $object->IDLFileForInterface($interfaceName)
351 or assert("Could NOT find IDL file for interface \"$interfaceName\", reachable from \"" . $outerInterface->name . "\"!\n");
353 print " | |> Parsing parent IDL \"$filename\" for interface \"$interfaceName\"\n" if $verbose;
355 # Step #2: Parse the found IDL file (in quiet mode).
356 my $parser = IDLParser->new(1);
357 my $document = $parser->Parse($filename, $defines, $preprocessor);
359 foreach my $interface (@{$document->interfaces}) {
360 if ($interface->name eq $interfaceName) {
361 $cachedInterfaces->{$interfaceName} = $interface;
366 die("Could NOT find interface definition for $interfaceName in $filename");
369 # Helpers for all CodeGenerator***.pm modules
371 sub SkipIncludeHeader
376 # FIXME: This is a lot like !IsRefPtrType. Maybe they could share code?
378 return 1 if $object->IsPrimitiveType($type);
379 return 1 if $object->IsTypedArrayType($type);
380 return 1 if $type eq "Array";
381 return 1 if $type eq "BufferSource";
382 return 1 if $type eq "DOMString" or $type eq "USVString";
383 return 1 if $type eq "DOMTimeStamp";
384 return 1 if $type eq "SVGNumber";
385 return 1 if $type eq "any";
392 my ($object, $type) = @_;
394 return 1 if $integerTypeHash{$type};
395 return 1 if $floatingPointTypeHash{$type};
399 sub IsStringOrEnumType
401 my ($object, $type) = @_;
403 return 1 if $type eq "DOMString" or $type eq "USVString";
404 return 1 if $object->IsEnumType($type);
410 my ($object, $type) = @_;
412 return 1 if $integerTypeHash{$type};
416 sub IsFloatingPointType
418 my ($object, $type) = @_;
420 return 1 if $floatingPointTypeHash{$type};
426 my ($object, $type) = @_;
428 return 1 if $primitiveTypeHash{$type};
429 return 1 if $object->IsNumericType($type);
433 # Currently used outside WebKit in an internal Apple project; can be removed soon.
436 my ($object, $type) = @_;
438 return 1 if $type eq "DOMString";
439 return 1 if $type eq "USVString";
445 my ($object, $type) = @_;
447 return 1 if exists $enumTypeHash{$type};
453 my ($object, $type) = @_;
455 return @{$enumTypeHash{$type}};
458 sub HasEnumImplementationNameOverride
460 my ($object, $type) = @_;
462 return 1 if exists $enumTypeImplementationNameOverrides{$type};
466 sub GetEnumImplementationNameOverride
468 my ($object, $type) = @_;
470 return $enumTypeImplementationNameOverrides{$type};
473 sub GetDictionaryByName
475 my ($object, $name) = @_;
476 die "GetDictionaryByName() was called with an undefined dictionary name" unless defined($name);
478 for my $dictionary (@{$useDocument->dictionaries}) {
479 return $dictionary if $dictionary->name eq $name;
482 return $cachedExternalDictionaries->{$name} if exists($cachedExternalDictionaries->{$name});
484 # Find the IDL file associated with the dictionary.
485 my $filename = $object->IDLFileForInterface($name) or return;
487 # Do a fast check to see if it seems to contain a dictionary.
488 my $fileContents = slurp($filename);
490 if ($fileContents =~ /\bdictionary\s+$name/gs) {
492 my $parser = IDLParser->new(1);
493 my $document = $parser->Parse($filename, $defines, $preprocessor);
495 foreach my $dictionary (@{$document->dictionaries}) {
496 next unless $dictionary->name eq $name;
498 $cachedExternalDictionaries->{$name} = $dictionary;
499 my $implementedAs = $dictionary->extendedAttributes->{ImplementedAs};
500 $dictionaryTypeImplementationNameOverrides{$dictionary->name} = $implementedAs if $implementedAs;
504 $cachedExternalDictionaries->{$name} = undef;
509 my ($object, $type) = @_;
511 return $type =~ /^[A-Z]/ && defined($object->GetDictionaryByName($type));
514 # A dictionary defined in its own IDL file.
515 sub IsExternalDictionaryType
517 my ($object, $type) = @_;
519 return $object->IsDictionaryType($type) && defined($cachedExternalDictionaries->{$type});
522 sub HasDictionaryImplementationNameOverride
524 my ($object, $type) = @_;
526 return 1 if exists $dictionaryTypeImplementationNameOverrides{$type};
530 sub GetDictionaryImplementationNameOverride
532 my ($object, $type) = @_;
534 return $dictionaryTypeImplementationNameOverrides{$type};
539 my ($object, $type) = @_;
541 return 1 if $nonPointerTypeHash{$type};
542 return 1 if $object->IsPrimitiveType($type);
546 sub IsSVGTypeNeedingTearOff
551 return 1 if exists $svgTypeNeedingTearOff{$type};
555 sub IsSVGTypeWithWritablePropertiesNeedingTearOff
560 return 1 if $svgTypeWithWritablePropertiesNeedingTearOff{$type};
569 return 1 if $typedArrayTypes{$type};
578 return 0 if $object->IsPrimitiveType($type);
579 return 0 if $object->IsDictionaryType($type);
580 return 0 if $object->IsEnumType($type);
581 return 0 if $object->IsSequenceOrFrozenArrayType($type);
582 return 0 if $type eq "DOMString" or $type eq "USVString";
583 return 0 if $type eq "any";
588 sub GetSVGTypeNeedingTearOff
593 return $svgTypeNeedingTearOff{$type} if exists $svgTypeNeedingTearOff{$type};
597 sub GetSVGWrappedTypeNeedingTearOff
602 my $svgTypeNeedingTearOff = $object->GetSVGTypeNeedingTearOff($type);
603 return $svgTypeNeedingTearOff if not $svgTypeNeedingTearOff;
605 if ($svgTypeNeedingTearOff =~ /SVGPropertyTearOff/) {
606 $svgTypeNeedingTearOff =~ s/SVGPropertyTearOff<//;
607 } elsif ($svgTypeNeedingTearOff =~ /SVGListPropertyTearOff/) {
608 $svgTypeNeedingTearOff =~ s/SVGListPropertyTearOff<//;
609 } elsif ($svgTypeNeedingTearOff =~ /SVGStaticListPropertyTearOff/) {
610 $svgTypeNeedingTearOff =~ s/SVGStaticListPropertyTearOff<//;
611 } elsif ($svgTypeNeedingTearOff =~ /SVGTransformListPropertyTearOff/) {
612 $svgTypeNeedingTearOff =~ s/SVGTransformListPropertyTearOff<//;
615 $svgTypeNeedingTearOff =~ s/>//;
616 return $svgTypeNeedingTearOff;
619 sub IsSVGAnimatedType
624 return $type =~ /^SVGAnimated/;
632 return $type =~ /^sequence</;
635 sub GetSequenceInnerType
640 return $1 if $type =~ /^sequence<([\w\d_\s]+)>.*/;
644 sub IsFrozenArrayType
649 return $type =~ /^FrozenArray</;
652 sub GetFrozenArrayInnerType
657 return $1 if $type =~ /^FrozenArray<([\w\d_\s]+)>.*/;
661 sub IsSequenceOrFrozenArrayType
666 return $object->IsSequenceType($type) || $object->IsFrozenArrayType($type);
669 sub GetSequenceOrFrozenArrayInnerType
674 return $object->GetSequenceInnerType($type) if $object->IsSequenceType($type);
675 return $object->GetFrozenArrayInnerType($type) if $object->IsFrozenArrayType($type);
679 # These match WK_lcfirst and WK_ucfirst defined in builtins_generator.py.
680 # Uppercase the first letter while respecting WebKit style guidelines.
681 # E.g., xmlEncoding becomes XMLEncoding, but xmlllang becomes Xmllang.
684 my ($object, $param) = @_;
685 my $ret = ucfirst($param);
686 $ret =~ s/Xml/XML/ if $ret =~ /^Xml[^a-z]/;
687 $ret =~ s/Svg/SVG/ if $ret =~ /^Svg/;
692 # Lowercase the first letter while respecting WebKit style guidelines.
693 # URL becomes url, but SetURL becomes setURL.
696 my ($object, $param) = @_;
697 my $ret = lcfirst($param);
698 $ret =~ s/dOM/dom/ if $ret =~ /^dOM/;
699 $ret =~ s/hTML/html/ if $ret =~ /^hTML/;
700 $ret =~ s/uRL/url/ if $ret =~ /^uRL/;
701 $ret =~ s/jS/js/ if $ret =~ /^jS/;
702 $ret =~ s/xML/xml/ if $ret =~ /^xML/;
703 $ret =~ s/xSLT/xslt/ if $ret =~ /^xSLT/;
704 $ret =~ s/cSS/css/ if $ret =~ /^cSS/;
705 $ret =~ s/rTC/rtc/ if $ret =~ /^rTC/;
707 # For HTML5 FileSystem API Flags attributes.
708 # (create is widely used to instantiate an object and must be avoided.)
709 $ret =~ s/^create/isCreate/ if $ret =~ /^create$/;
710 $ret =~ s/^exclusive/isExclusive/ if $ret =~ /^exclusive$/;
718 open my $fh, '<', $file or die;
728 $string =~ s/^\s+|\s+$//g;
732 # Return the C++ namespace that a given attribute name string is defined in.
733 sub NamespaceForAttributeName
735 my ($object, $interfaceName, $attributeName) = @_;
736 return "SVGNames" if $interfaceName =~ /^SVG/ && !$svgAttributesInHTMLHash{$attributeName};
740 # Identifies overloaded functions and for each function adds an array with
741 # links to its respective overloads (including itself).
742 sub LinkOverloadedFunctions
744 my ($object, $interface) = @_;
746 my %nameToFunctionsMap = ();
747 foreach my $function (@{$interface->functions}) {
748 my $name = $function->signature->name;
749 $nameToFunctionsMap{$name} = [] if !exists $nameToFunctionsMap{$name};
750 push(@{$nameToFunctionsMap{$name}}, $function);
751 $function->{overloads} = $nameToFunctionsMap{$name};
752 $function->{overloadIndex} = @{$nameToFunctionsMap{$name}};
756 foreach my $constructor (@{$interface->constructors}) {
757 $constructor->{overloads} = $interface->constructors;
758 $constructor->{overloadIndex} = $index;
763 sub AttributeNameForGetterAndSetter
765 my ($generator, $attribute) = @_;
767 my $attributeName = $attribute->signature->name;
768 if ($attribute->signature->extendedAttributes->{"ImplementedAs"}) {
769 $attributeName = $attribute->signature->extendedAttributes->{"ImplementedAs"};
771 my $attributeType = $attribute->signature->type;
773 # SVG animated types need to use a special attribute name.
774 # The rest of the special casing for SVG animated types is handled in the language-specific code generators.
775 $attributeName .= "Animated" if $generator->IsSVGAnimatedType($attributeType);
777 return $attributeName;
780 sub ContentAttributeName
782 my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
784 my $contentAttributeName = $attribute->signature->extendedAttributes->{"Reflect"};
785 return undef if !$contentAttributeName;
787 $contentAttributeName = lc $generator->AttributeNameForGetterAndSetter($attribute) if $contentAttributeName eq "VALUE_IS_MISSING";
789 my $namespace = $generator->NamespaceForAttributeName($interfaceName, $contentAttributeName);
791 $implIncludes->{"${namespace}.h"} = 1;
792 return "WebCore::${namespace}::${contentAttributeName}Attr";
797 my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
799 my $contentAttributeName = $generator->ContentAttributeName($implIncludes, $interfaceName, $attribute);
801 if (!$contentAttributeName) {
802 return ($generator->WK_lcfirst($generator->AttributeNameForGetterAndSetter($attribute)));
805 my $attributeType = $attribute->signature->type;
808 if ($attribute->signature->extendedAttributes->{"URL"}) {
809 $functionName = "getURLAttribute";
810 } elsif ($attributeType eq "boolean") {
811 $functionName = "hasAttributeWithoutSynchronization";
812 } elsif ($attributeType eq "long") {
813 $functionName = "getIntegralAttribute";
814 } elsif ($attributeType eq "unsigned long") {
815 $functionName = "getUnsignedIntegralAttribute";
817 if ($contentAttributeName eq "WebCore::HTMLNames::idAttr") {
818 $functionName = "getIdAttribute";
819 $contentAttributeName = "";
820 } elsif ($contentAttributeName eq "WebCore::HTMLNames::nameAttr") {
821 $functionName = "getNameAttribute";
822 $contentAttributeName = "";
823 } elsif ($generator->IsSVGAnimatedType($attributeType)) {
824 $functionName = "getAttribute";
826 $functionName = "attributeWithoutSynchronization";
830 return ($functionName, $contentAttributeName);
835 my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
837 my $contentAttributeName = $generator->ContentAttributeName($implIncludes, $interfaceName, $attribute);
839 if (!$contentAttributeName) {
840 return ("set" . $generator->WK_ucfirst($generator->AttributeNameForGetterAndSetter($attribute)));
843 my $attributeType = $attribute->signature->type;
846 if ($attributeType eq "boolean") {
847 $functionName = "setBooleanAttribute";
848 } elsif ($attributeType eq "long") {
849 $functionName = "setIntegralAttribute";
850 } elsif ($attributeType eq "unsigned long") {
851 $functionName = "setUnsignedIntegralAttribute";
852 } elsif ($generator->IsSVGAnimatedType($attributeType)) {
853 $functionName = "setAttribute";
855 $functionName = "setAttributeWithoutSynchronization";
858 return ($functionName, $contentAttributeName);
866 return 0 if !$object->IsRefPtrType($type);
867 return 0 if $object->IsTypedArrayType($type);
868 return 0 if $type eq "BufferSource";
869 return 0 if $type eq "UNION";
870 return 0 if $webCoreTypeHash{$type};
875 sub getInterfaceExtendedAttributesFromName
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.
881 my $interfaceName = shift;
883 my $idlFile = $object->IDLFileForInterface($interfaceName) or assert("Could NOT find IDL file for interface \"$interfaceName\"!\n");
885 open FILE, "<", $idlFile or die;
889 my $fileContents = join('', @lines);
891 my $extendedAttributes = {};
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;
905 return $extendedAttributes;
908 sub ComputeIsCallbackInterface
913 return 0 unless $object->IsWrapperType($type);
915 my $idlFile = $object->IDLFileForInterface($type) or assert("Could NOT find IDL file for interface \"$type\"!\n");
917 open FILE, "<", $idlFile or die;
921 my $fileContents = join('', @lines);
922 return ($fileContents =~ /callback\s+interface\s+(\w+)/gs);
925 my %isCallbackInterface = ();
927 sub IsCallbackInterface
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.
933 my ($object, $type) = @_;
935 return $isCallbackInterface{$type} if exists $isCallbackInterface{$type};
936 my $result = ComputeIsCallbackInterface($object, $type);
937 $isCallbackInterface{$type} = $result;
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
949 return 0 unless $object->IsCallbackInterface($type);
951 my $idlFile = $object->IDLFileForInterface($type) or assert("Could NOT find IDL file for interface \"$type\"!\n");
953 open FILE, "<", $idlFile or die;
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;
967 return 1 if ($key eq "Callback" && $value eq "FunctionOnly");
974 my %isFunctionOnlyCallbackInterface = ();
976 sub IsFunctionOnlyCallbackInterface
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.
982 my ($object, $type) = @_;
984 return $isFunctionOnlyCallbackInterface{$type} if exists $isFunctionOnlyCallbackInterface{$type};
985 my $result = ComputeIsFunctionOnlyCallbackInterface($object, $type);
986 $isFunctionOnlyCallbackInterface{$type} = $result;
990 sub GenerateConditionalString
992 my $generator = shift;
995 my $conditional = $node->extendedAttributes->{"Conditional"};
997 return $generator->GenerateConditionalStringFromAttributeValue($conditional);
1003 sub GenerateConditionalStringFromAttributeValue
1005 my $generator = shift;
1006 my $conditional = shift;
1010 my $expression = $_;
1012 map { $conjunction{$_} = 1; } split(/&/, $expression);
1013 $expression = "ENABLE(" . join(") && ENABLE(", sort keys %conjunction) . ")";
1014 $disjunction{$expression} = 1
1015 } split(/\|/, $conditional);
1017 return "1" if keys %disjunction == 0;
1018 return (%disjunction)[0] if keys %disjunction == 1;
1022 my $expression = $_;
1023 $expression = "($expression)" if $expression =~ / /;
1024 push @parenthesized, $expression;
1025 } sort keys %disjunction;
1027 return join(" || ", @parenthesized);
1030 sub GenerateCompileTimeCheckForEnumsIfNeeded
1032 my ($generator, $interface) = @_;
1034 return () if $interface->extendedAttributes->{"DoNotCheckConstants"} || !@{$interface->constants};
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;
1046 push(@checks, "\n");
1050 sub ExtendedAttributeContains
1053 my $callWith = shift;
1054 return 0 unless $callWith;
1055 my $keyword = shift;
1057 my @callWithKeywords = split /\s*\&\s*/, $callWith;
1058 return grep { $_ eq $keyword } @callWithKeywords;
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
1066 my $interface = shift;
1067 my $interfaceName = $interface->extendedAttributes->{"InterfaceName"};
1068 return $interfaceName ? $interfaceName : $interface->name;
1071 sub InheritsInterface
1074 my $interface = shift;
1075 my $interfaceName = shift;
1078 return 1 if $interfaceName eq $interface->name;
1079 $object->ForAllParents($interface, sub {
1080 my $currentInterface = shift;
1081 if ($currentInterface->name eq $interfaceName) {
1090 sub InheritsExtendedAttribute
1093 my $interface = shift;
1094 my $extendedAttribute = shift;
1097 return 1 if $interface->extendedAttributes->{$extendedAttribute};
1098 $object->ForAllParents($interface, sub {
1099 my $currentInterface = shift;
1100 if ($currentInterface->extendedAttributes->{$extendedAttribute}) {
1109 sub ShouldPassWrapperByReference
1112 my $parameter = shift;
1113 my $interface = shift;
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);