# # WebKit IDL parser # # Copyright (C) 2005 Nikolas Zimmermann # Copyright (C) 2006 Samuel Weinig # Copyright (C) 2007 Apple Inc. All rights reserved. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public License # aint with this library; see the file COPYING.LIB. If not, write to # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # package CodeGenerator; my $useDocument = ""; my $useGenerator = ""; my $useOutputDir = ""; my $useDirectories = ""; my $useLayerOnTop = 0; my $preprocessor; my $codeGenerator = 0; my $verbose = 0; my %primitiveTypeHash = ("int" => 1, "short" => 1, "long" => 1, "unsigned int" => 1, "unsigned short" => 1, "unsigned long" => 1, "float" => 1, "double" => 1, "boolean" => 1, "void" => 1); my %podTypeHash = ("RGBColor" => 1, "SVGLength" => 1, "SVGPoint" => 1, "SVGRect" => 1, "SVGNumber" => 1, "SVGMatrix" => 1, "SVGTransform" => 1); my %stringTypeHash = ("DOMString" => 1, "AtomicString" => 1); my %nonPointerTypeHash = ("DOMTimeStamp" => 1, "CompareHow" => 1, "SVGPaintType" => 1); my %svgAnimatedTypeHash = ("SVGAnimatedAngle" => 1, "SVGAnimatedBoolean" => 1, "SVGAnimatedEnumeration" => 1, "SVGAnimatedInteger" => 1, "SVGAnimatedLength" => 1, "SVGAnimatedLengthList" => 1, "SVGAnimatedNumber" => 1, "SVGAnimatedNumberList" => 1, "SVGAnimatedPreserveAspectRatio" => 1, "SVGAnimatedRect" => 1, "SVGAnimatedString" => 1, "SVGAnimatedTransformList" => 1); # Helpers for 'ScanDirectory' my $endCondition = 0; my $foundFilename = ""; my @foundFilenames = (); my $ignoreParent = 1; my $defines = ""; # Default constructor sub new { my $object = shift; my $reference = { }; $useDirectories = shift; $useGenerator = shift; $useOutputDir = shift; $useLayerOnTop = shift; $preprocessor = shift; bless($reference, $object); return $reference; } sub StripModule($) { my $object = shift; my $name = shift; $name =~ s/[a-zA-Z0-9]*:://; return $name; } sub ProcessDocument { my $object = shift; $useDocument = shift; $defines = shift; my $ifaceName = "CodeGenerator" . $useGenerator; # Dynamically load external code generation perl module require $ifaceName . ".pm"; $codeGenerator = $ifaceName->new($object, $useOutputDir, $useLayerOnTop, $preprocessor); unless (defined($codeGenerator)) { my $classes = $useDocument->classes; foreach my $class (@$classes) { print "Skipping $useGenerator code generation for IDL interface \"" . $class->name . "\".\n" if $verbose; } return; } # Start the actual code generation! $codeGenerator->GenerateModule($useDocument, $defines); my $classes = $useDocument->classes; foreach my $class (@$classes) { print "Generating $useGenerator bindings code for IDL interface \"" . $class->name . "\"...\n" if $verbose; $codeGenerator->GenerateInterface($class, $defines); } $codeGenerator->finish(); } sub AddMethodsConstantsAndAttributesFromParentClasses { # For the passed interface, recursively parse all parent # IDLs in order to find out all inherited properties/methods. my $object = shift; my $dataNode = shift; my @parents = @{$dataNode->parents}; my $parentsMax = @{$dataNode->parents}; my $constantsRef = $dataNode->constants; my $functionsRef = $dataNode->functions; my $attributesRef = $dataNode->attributes; # Exception: For the DOM 'Node' is our topmost baseclass, not EventTargetNode. return if $parentsMax eq 1 and $parents[0] eq "EventTargetNode"; foreach (@{$dataNode->parents}) { if ($ignoreParent) { # Ignore first parent class, already handled by the generation itself. $ignoreParent = 0; next; } my $interface = $object->StripModule($_); # Step #1: Find the IDL file associated with 'interface' $endCondition = 0; $foundFilename = ""; foreach (@{$useDirectories}) { $object->ScanDirectory("$interface.idl", $_, $_, 0) if ($foundFilename eq ""); } if ($foundFilename ne "") { print " | |> Parsing parent IDL \"$foundFilename\" for interface \"$interface\"\n" if $verbose; # Step #2: Parse the found IDL file (in quiet mode). my $parser = IDLParser->new(1); my $document = $parser->Parse($foundFilename, $defines, $preprocessor); foreach my $class (@{$document->classes}) { # Step #3: Enter recursive parent search AddMethodsConstantsAndAttributesFromParentClasses($object, $class); # Step #4: Collect constants & functions & attributes of this parent-class my $constantsMax = @{$class->constants}; my $functionsMax = @{$class->functions}; my $attributesMax = @{$class->attributes}; print " | |> -> Inheriting $constantsMax constants, $functionsMax functions, $attributesMax attributes...\n | |>\n" if $verbose; # Step #5: Concatenate data push(@$constantsRef, $_) foreach (@{$class->constants}); push(@$functionsRef, $_) foreach (@{$class->functions}); push(@$attributesRef, $_) foreach (@{$class->attributes}); } } else { die("Could NOT find specified parent interface \"$interface\"!\n"); } } } sub GetMethodsAndAttributesFromParentClasses { # For the passed interface, recursively parse all parent # IDLs in order to find out all inherited properties/methods. my $object = shift; my $dataNode = shift; my @parents = @{$dataNode->parents}; return if @{$dataNode->parents} == 0; my @parentList = (); foreach (@{$dataNode->parents}) { my $interface = $object->StripModule($_); if ($interface eq "EventTargetNode") { $interface = "Node"; } # Step #1: Find the IDL file associated with 'interface' $endCondition = 0; $foundFilename = ""; foreach (@{$useDirectories}) { $object->ScanDirectory("${interface}.idl", $_, $_, 0) if $foundFilename eq ""; } die("Could NOT find specified parent interface \"$interface\"!\n") if $foundFilename eq ""; print " | |> Parsing parent IDL \"$foundFilename\" for interface \"$interface\"\n" if $verbose; # Step #2: Parse the found IDL file (in quiet mode). my $parser = IDLParser->new(1); my $document = $parser->Parse($foundFilename, $defines); foreach my $class (@{$document->classes}) { # Step #3: Enter recursive parent search push(@parentList, GetMethodsAndAttributesFromParentClasses($object, $class)); # Step #4: Collect constants & functions & attributes of this parent-class # print " | |> -> Inheriting $functionsMax functions amd $attributesMax attributes...\n | |>\n" if $verbose; my $hash = { "name" => $class->name, "functions" => $class->functions, "attributes" => $class->attributes }; # Step #5: Concatenate data unshift(@parentList, $hash); } } return @parentList; } sub ParseInterface { my ($object, $interfaceName) = @_; # Step #1: Find the IDL file associated with 'interface' $endCondition = 0; $foundFilename = ""; foreach (@{$useDirectories}) { $object->ScanDirectory("${interfaceName}.idl", $_, $_, 0) if $foundFilename eq ""; } die "Could NOT find specified parent interface \"$interfaceName\"!\n" if $foundFilename eq ""; print " | |> Parsing parent IDL \"$foundFilename\" for interface \"$interfaceName\"\n" if $verbose; # Step #2: Parse the found IDL file (in quiet mode). my $parser = IDLParser->new(1); my $document = $parser->Parse($foundFilename, $defines); foreach my $interface (@{$document->classes}) { return $interface if $interface->name eq $interfaceName; } die "Interface definition not found"; } # Helpers for all CodeGenerator***.pm modules sub IsPodType { my $object = shift; my $type = shift; return 1 if $podTypeHash{$type}; return 0; } sub IsPrimitiveType { my $object = shift; my $type = shift; return 1 if $primitiveTypeHash{$type}; return 0; } sub IsStringType { my $object = shift; my $type = shift; return 1 if $stringTypeHash{$type}; return 0; } sub IsNonPointerType { my $object = shift; my $type = shift; return 1 if $nonPointerTypeHash{$type} or $primitiveTypeHash{$type}; return 0; } sub IsSVGAnimatedType { my $object = shift; my $type = shift; return 1 if $svgAnimatedTypeHash{$type}; return 0; } # Internal Helper sub ScanDirectory { my $object = shift; my $interface = shift; my $directory = shift; my $useDirectory = shift; my $reportAllFiles = shift; return if ($endCondition eq 1) and ($reportAllFiles eq 0); my $sourceRoot = $ENV{SOURCE_ROOT}; my $thisDir = $sourceRoot ? "$sourceRoot/$directory" : $directory; if (!opendir(DIR, $thisDir)) { opendir(DIR, $directory) or die "[ERROR] Can't open directory $thisDir or $directory: \"$!\"\n"; $thisDir = $directory; } my @names = readdir(DIR) or die "[ERROR] Cant't read directory $thisDir \"$!\"\n"; closedir(DIR); foreach my $name (@names) { # Skip if we already found the right file or # if we encounter 'exotic' stuff (ie. '.', '..', '.svn') next if ($endCondition eq 1) or ($name =~ /^\./); # Recurisvely enter directory if (-d "$thisDir/$name") { $object->ScanDirectory($interface, "$directory/$name", $useDirectory, $reportAllFiles); next; } # Check wheter we found the desired file my $condition = ($name eq $interface); $condition = 1 if ($interface eq "allidls") and ($name =~ /\.idl$/); if ($condition) { $foundFilename = "$thisDir/$name"; if ($reportAllFiles eq 0) { $endCondition = 1; } else { push(@foundFilenames, $foundFilename); } } } } # Uppercase the first letter while respecting WebKit style guidelines. # E.g., xmlEncoding becomes XMLEncoding, but xmlllang becomes Xmllang. sub WK_ucfirst { my ($object, $param) = @_; my $ret = ucfirst($param); $ret =~ s/Xml/XML/ if $ret =~ /^Xml[^a-z]/; return $ret; } # Lowercase the first letter while respecting WebKit style guidelines. # URL becomes url, but SetURL becomes setURL. sub WK_lcfirst { my ($object, $param) = @_; my $ret = lcfirst($param); $ret =~ s/uRL/url/ if $ret =~ /^uRL/; $ret =~ s/jS/js/ if $ret =~ /^jS/; return $ret; } 1;