diff options
Diffstat (limited to 'Source/WebCore/bindings/scripts')
-rw-r--r-- | Source/WebCore/bindings/scripts/CodeGenerator.pm | 926 | ||||
-rw-r--r-- | Source/WebCore/bindings/scripts/CodeGeneratorGObject.pm | 1694 | ||||
-rw-r--r-- | Source/WebCore/bindings/scripts/CodeGeneratorJS.pm | 7226 | ||||
-rw-r--r-- | Source/WebCore/bindings/scripts/IDLAttributes.txt | 85 | ||||
-rw-r--r-- | Source/WebCore/bindings/scripts/IDLParser.pm | 1245 | ||||
-rw-r--r-- | Source/WebCore/bindings/scripts/InFilesCompiler.pm | 25 | ||||
-rw-r--r-- | Source/WebCore/bindings/scripts/InFilesParser.pm | 4 | ||||
-rw-r--r-- | Source/WebCore/bindings/scripts/StaticString.pm | 35 | ||||
-rwxr-xr-x | Source/WebCore/bindings/scripts/generate-bindings-all.pl | 245 | ||||
-rwxr-xr-x | Source/WebCore/bindings/scripts/generate-bindings.pl | 69 | ||||
-rw-r--r-- | Source/WebCore/bindings/scripts/gobject-generate-headers.pl | 98 | ||||
-rw-r--r-- | Source/WebCore/bindings/scripts/preprocess-idls.pl | 111 | ||||
-rw-r--r-- | Source/WebCore/bindings/scripts/preprocessor.pm | 29 |
13 files changed, 6512 insertions, 5280 deletions
diff --git a/Source/WebCore/bindings/scripts/CodeGenerator.pm b/Source/WebCore/bindings/scripts/CodeGenerator.pm index 943959f54..6f77d7de5 100644 --- a/Source/WebCore/bindings/scripts/CodeGenerator.pm +++ b/Source/WebCore/bindings/scripts/CodeGenerator.pm @@ -28,14 +28,16 @@ package CodeGenerator; use strict; +use File::Basename; use File::Find; +use Carp qw<longmess>; +use Data::Dumper; my $useDocument = ""; my $useGenerator = ""; my $useOutputDir = ""; my $useOutputHeadersDir = ""; my $useDirectories = ""; -my $useLayerOnTop = 0; my $preprocessor; my $writeDependencies = 0; my $defines = ""; @@ -45,59 +47,88 @@ my $codeGenerator = 0; my $verbose = 0; -my %numericTypeHash = ("int" => 1, "short" => 1, "long" => 1, "long long" => 1, - "unsigned int" => 1, "unsigned short" => 1, - "unsigned long" => 1, "unsigned long long" => 1, - "float" => 1, "double" => 1, "byte" => 1, - "octet" => 1); +my %integerTypeHash = ( + "byte" => 1, + "long long" => 1, + "long" => 1, + "octet" => 1, + "short" => 1, + "unsigned long long" => 1, + "unsigned long" => 1, + "unsigned short" => 1, +); -my %primitiveTypeHash = ( "boolean" => 1, "void" => 1, "Date" => 1); +my %floatingPointTypeHash = ( + "float" => 1, + "unrestricted float" => 1, + "double" => 1, + "unrestricted double" => 1, +); -my %stringTypeHash = ("DOMString" => 1, "AtomicString" => 1); +my %stringTypeHash = ( + "ByteString" => 1, + "DOMString" => 1, + "USVString" => 1, +); -# WebCore types used directly in IDL files. -my %webCoreTypeHash = ( - "CompareHow" => 1, - "SerializedScriptValue" => 1, - "Dictionary" => 1 +my %typedArrayTypes = ( + "ArrayBuffer" => 1, + "ArrayBufferView" => 1, + "DataView" => 1, + "Float32Array" => 1, + "Float64Array" => 1, + "Int16Array" => 1, + "Int32Array" => 1, + "Int8Array" => 1, + "Uint16Array" => 1, + "Uint32Array" => 1, + "Uint8Array" => 1, + "Uint8ClampedArray" => 1, ); -my %enumTypeHash = (); - -my %nonPointerTypeHash = ("DOMTimeStamp" => 1, "CompareHow" => 1); - -my %svgAttributesInHTMLHash = ("class" => 1, "id" => 1, "onabort" => 1, "onclick" => 1, - "onerror" => 1, "onload" => 1, "onmousedown" => 1, - "onmouseenter" => 1, "onmouseleave" => 1, - "onmousemove" => 1, "onmouseout" => 1, "onmouseover" => 1, - "onmouseup" => 1, "onresize" => 1, "onscroll" => 1, - "onunload" => 1); - -my %svgTypeNeedingTearOff = ( - "SVGAngle" => "SVGPropertyTearOff<SVGAngle>", - "SVGLength" => "SVGPropertyTearOff<SVGLength>", - "SVGLengthList" => "SVGListPropertyTearOff<SVGLengthList>", - "SVGMatrix" => "SVGPropertyTearOff<SVGMatrix>", - "SVGNumber" => "SVGPropertyTearOff<float>", - "SVGNumberList" => "SVGListPropertyTearOff<SVGNumberList>", - "SVGPathSegList" => "SVGPathSegListPropertyTearOff", - "SVGPoint" => "SVGPropertyTearOff<SVGPoint>", - "SVGPointList" => "SVGListPropertyTearOff<SVGPointList>", - "SVGPreserveAspectRatio" => "SVGPropertyTearOff<SVGPreserveAspectRatio>", - "SVGRect" => "SVGPropertyTearOff<FloatRect>", - "SVGStringList" => "SVGStaticListPropertyTearOff<SVGStringList>", - "SVGTransform" => "SVGPropertyTearOff<SVGTransform>", - "SVGTransformList" => "SVGTransformListPropertyTearOff" +my %primitiveTypeHash = ( + "boolean" => 1, + "void" => 1, + "Date" => 1 ); -my %svgTypeWithWritablePropertiesNeedingTearOff = ( - "SVGPoint" => 1, - "SVGMatrix" => 1 +my %dictionaryTypeImplementationNameOverrides = (); +my %enumTypeImplementationNameOverrides = (); + +my %svgAttributesInHTMLHash = ( + "class" => 1, + "id" => 1, + "onabort" => 1, + "onclick" => 1, + "onerror" => 1, + "onload" => 1, + "onmousedown" => 1, + "onmouseenter" => 1, + "onmouseleave" => 1, + "onmousemove" => 1, + "onmouseout" => 1, + "onmouseover" => 1, + "onmouseup" => 1, + "onresize" => 1, + "onscroll" => 1, + "onunload" => 1, ); # Cache of IDL file pathnames. my $idlFiles; my $cachedInterfaces = {}; +my $cachedExternalDictionaries = {}; +my $cachedExternalEnumerations = {}; + +sub assert +{ + my $message = shift; + + my $mess = longmess(); + print Dumper($mess); + + die $message; +} # Default constructor sub new @@ -109,7 +140,6 @@ sub new $useGenerator = shift; $useOutputDir = shift; $useOutputHeadersDir = shift; - $useLayerOnTop = shift; $preprocessor = shift; $writeDependencies = shift; $verbose = shift; @@ -128,24 +158,86 @@ sub ProcessDocument my $ifaceName = "CodeGenerator" . $useGenerator; require $ifaceName . ".pm"; - %enumTypeHash = map { $_->name => $_->values } @{$useDocument->enumerations}; + foreach my $dictionary (@{$useDocument->dictionaries}) { + if ($dictionary->extendedAttributes->{"ImplementedAs"}) { + $dictionaryTypeImplementationNameOverrides{$dictionary->type->name} = $dictionary->extendedAttributes->{"ImplementedAs"}; + } + } + + foreach my $enumeration (@{$useDocument->enumerations}) { + if ($enumeration->extendedAttributes->{"ImplementedAs"}) { + $enumTypeImplementationNameOverrides{$enumeration->type->name} = $enumeration->extendedAttributes->{"ImplementedAs"}; + } + } # Dynamically load external code generation perl module - $codeGenerator = $ifaceName->new($object, $useLayerOnTop, $preprocessor, $writeDependencies, $verbose, $targetIdlFilePath); + $codeGenerator = $ifaceName->new($object, $writeDependencies, $verbose, $targetIdlFilePath); unless (defined($codeGenerator)) { my $interfaces = $useDocument->interfaces; foreach my $interface (@$interfaces) { - print "Skipping $useGenerator code generation for IDL interface \"" . $interface->name . "\".\n" if $verbose; + print "Skipping $useGenerator code generation for IDL interface \"" . $interface->type->name . "\".\n" if $verbose; } return; } my $interfaces = $useDocument->interfaces; - foreach my $interface (@$interfaces) { - print "Generating $useGenerator bindings code for IDL interface \"" . $interface->name . "\"...\n" if $verbose; - $codeGenerator->GenerateInterface($interface, $defines); + if (@$interfaces) { + die "Multiple interfaces per document are not supported" if @$interfaces > 1; + + my $interface = @$interfaces[0]; + print "Generating $useGenerator bindings code for IDL interface \"" . $interface->type->name . "\"...\n" if $verbose; + $codeGenerator->GenerateInterface($interface, $defines, $useDocument->enumerations, $useDocument->dictionaries); $codeGenerator->WriteData($interface, $useOutputDir, $useOutputHeadersDir); + return; + } + + my $callbackFunctions = $useDocument->callbackFunctions; + if (@$callbackFunctions) { + die "Multiple standalone callback functions per document are not supported" if @$callbackFunctions > 1; + + my $callbackFunction = @$callbackFunctions[0]; + print "Generating $useGenerator bindings code for IDL callback function \"" . $callbackFunction->type->name . "\"...\n" if $verbose; + $codeGenerator->GenerateCallbackFunction($callbackFunction, $useDocument->enumerations, $useDocument->dictionaries); + $codeGenerator->WriteData($callbackFunction, $useOutputDir, $useOutputHeadersDir); + return; + } + + my $dictionaries = $useDocument->dictionaries; + if (@$dictionaries) { + my $dictionary; + my $otherDictionaries; + if (@$dictionaries == 1) { + $dictionary = @$dictionaries[0]; + } else { + my $primaryDictionaryName = fileparse($targetIdlFilePath, ".idl"); + for my $candidate (@$dictionaries) { + if ($candidate->type->name eq $primaryDictionaryName) { + $dictionary = $candidate; + } else { + push @$otherDictionaries, $candidate; + } + } + die "Multiple dictionaries per document are only supported if one matches the filename" unless $dictionary; + } + + print "Generating $useGenerator bindings code for IDL dictionary \"" . $dictionary->type->name . "\"...\n" if $verbose; + $codeGenerator->GenerateDictionary($dictionary, $useDocument->enumerations, $otherDictionaries); + $codeGenerator->WriteData($dictionary, $useOutputDir, $useOutputHeadersDir); + return; } + + my $enumerations = $useDocument->enumerations; + if (@$enumerations) { + die "Multiple standalone enumerations per document are not supported" if @$enumerations > 1; + + my $enumeration = @$enumerations[0]; + print "Generating $useGenerator bindings code for IDL enumeration \"" . $enumeration->type->name . "\"...\n" if $verbose; + $codeGenerator->GenerateEnumeration($enumeration); + $codeGenerator->WriteData($enumeration, $useOutputDir, $useOutputHeadersDir); + return; + } + + die "Processing document " . $useDocument->fileName . " did not generate anything" } sub FileNamePrefix @@ -156,7 +248,7 @@ sub FileNamePrefix require $ifaceName . ".pm"; # Dynamically load external code generation perl module - $codeGenerator = $ifaceName->new($object, $useLayerOnTop, $preprocessor, $writeDependencies, $verbose); + $codeGenerator = $ifaceName->new($object, $writeDependencies, $verbose); return $codeGenerator->FileNamePrefix(); } @@ -166,7 +258,9 @@ sub UpdateFile my $fileName = shift; my $contents = shift; - open FH, "> $fileName" or die "Couldn't open $fileName: $!\n"; + # FIXME: We should only write content if it is different from what is in the file. + # But that would mean running more often the binding generator, see https://bugs.webkit.org/show_bug.cgi?id=131756 + open FH, ">", $fileName or die "Couldn't open $fileName: $!\n"; print FH $contents; close FH; } @@ -183,8 +277,8 @@ sub ForAllParents my $outerInterface = shift; my $currentInterface = shift; - for (@{$currentInterface->parents}) { - my $interfaceName = $_; + if ($currentInterface->parentType) { + my $interfaceName = $currentInterface->parentType->name; my $parentInterface = $object->ParseInterface($outerInterface, $interfaceName); if ($beforeRecursion) { @@ -198,22 +292,6 @@ sub ForAllParents &$recurse($interface, $interface); } -sub FindSuperMethod -{ - my ($object, $interface, $functionName) = @_; - my $indexer; - $object->ForAllParents($interface, undef, sub { - my $currentInterface = shift; - foreach my $function (@{$currentInterface->functions}) { - if ($function->signature->name eq $functionName) { - $indexer = $function->signature; - return 'prune'; - } - } - }); - return $indexer; -} - sub IDLFileForInterface { my $object = shift; @@ -236,6 +314,26 @@ sub IDLFileForInterface return $idlFiles->{$interfaceName}; } +sub GetInterfaceForAttribute +{ + my ($object, $currentInterface, $attribute) = @_; + + return undef unless $object->IsInterfaceType($attribute->type); + + return $object->ParseInterface($currentInterface, $attribute->type->name); +} + +sub GetAttributeFromInterface +{ + my ($object, $outerInterface, $interfaceName, $attributeName) = @_; + + my $interface = $object->ParseInterface($outerInterface, $interfaceName); + for my $attribute (@{$interface->attributes}) { + return $attribute if $attribute->name eq $attributeName; + } + die("Could not find attribute '$attributeName' on interface '$interfaceName'."); +} + sub ParseInterface { my $object = shift; @@ -243,6 +341,7 @@ sub ParseInterface my $interfaceName = shift; return undef if $interfaceName eq 'Object'; + return undef if $interfaceName eq 'UNION'; if (exists $cachedInterfaces->{$interfaceName}) { return $cachedInterfaces->{$interfaceName}; @@ -250,7 +349,7 @@ sub ParseInterface # Step #1: Find the IDL file associated with 'interface' my $filename = $object->IDLFileForInterface($interfaceName) - or die("Could NOT find IDL file for interface \"$interfaceName\", reachable from \"" . $outerInterface->name . "\"!\n"); + or assert("Could NOT find IDL file for interface \"$interfaceName\", reachable from \"" . $outerInterface->type->name . "\"!\n"); print " | |> Parsing parent IDL \"$filename\" for interface \"$interfaceName\"\n" if $verbose; @@ -259,7 +358,7 @@ sub ParseInterface my $document = $parser->Parse($filename, $defines, $preprocessor); foreach my $interface (@{$document->interfaces}) { - if ($interface->name eq $interfaceName) { + if ($interface->type->name eq $interfaceName) { $cachedInterfaces->{$interfaceName} = $interface; return $interface; } @@ -270,197 +369,330 @@ sub ParseInterface # Helpers for all CodeGenerator***.pm modules -sub SkipIncludeHeader +sub IsNumericType { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; - return 1 if $object->IsPrimitiveType($type); + assert("Not a type") if ref($type) ne "IDLType"; - # Special case: SVGNumber.h does not exist. - return 1 if $type eq "SVGNumber"; - - # Typed arrays already included by JSDOMBinding.h. - return 1 if $object->IsTypedArrayType($type); + return 1 if $integerTypeHash{$type->name}; + return 1 if $floatingPointTypeHash{$type->name}; + return 0; +} + +sub IsStringOrEnumType +{ + my ($object, $type) = @_; + assert("Not a type") if ref($type) ne "IDLType"; + + return 1 if $object->IsStringType($type); + return 1 if $object->IsEnumType($type); return 0; } -sub IsConstructorTemplate +sub IsIntegerType { - my $object = shift; - my $interface = shift; - my $template = shift; + my ($object, $type) = @_; - return $interface->extendedAttributes->{"ConstructorTemplate"} && $interface->extendedAttributes->{"ConstructorTemplate"} eq $template; + assert("Not a type") if ref($type) ne "IDLType"; + + return 1 if $integerTypeHash{$type->name}; + return 0; } -sub IsNumericType +sub IsFloatingPointType { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; - return 1 if $numericTypeHash{$type}; + assert("Not a type") if ref($type) ne "IDLType"; + + return 1 if $floatingPointTypeHash{$type->name}; return 0; } sub IsPrimitiveType { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; - return 1 if $primitiveTypeHash{$type}; - return 1 if $numericTypeHash{$type}; + assert("Not a type") if ref($type) ne "IDLType"; + + return 1 if $primitiveTypeHash{$type->name}; + return 1 if $object->IsNumericType($type); return 0; } sub IsStringType { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; - return 1 if $stringTypeHash{$type}; + assert("Not a type") if ref($type) ne "IDLType"; + + return 1 if $stringTypeHash{$type->name}; return 0; } sub IsEnumType { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; - return 1 if exists $enumTypeHash{$type}; - return 0; + assert("Not a type") if ref($type) ne "IDLType"; + + return defined($object->GetEnumByType($type)); } -sub ValidEnumValues +sub GetEnumByType { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + my $name = $type->name; + + die "GetEnumByType() was called with an undefined enumeration name" unless defined($name); + + for my $enumeration (@{$useDocument->enumerations}) { + return $enumeration if $enumeration->type->name eq $name; + } + + return $cachedExternalEnumerations->{$name} if exists($cachedExternalEnumerations->{$name}); - return @{$enumTypeHash{$type}}; + # Find the IDL file associated with the dictionary. + my $filename = $object->IDLFileForInterface($name) or return; + + # Do a fast check to see if it seems to contain a dictionary. + my $fileContents = slurp($filename); + + if ($fileContents =~ /\benum\s+$name/gs) { + # Parse the IDL. + my $parser = IDLParser->new(1); + my $document = $parser->Parse($filename, $defines, $preprocessor); + + foreach my $enumeration (@{$document->enumerations}) { + next unless $enumeration->type->name eq $name; + + $cachedExternalEnumerations->{$name} = $enumeration; + my $implementedAs = $enumeration->extendedAttributes->{ImplementedAs}; + $enumTypeImplementationNameOverrides{$enumeration->type->name} = $implementedAs if $implementedAs; + return $enumeration; + } + } + $cachedExternalEnumerations->{$name} = undef; } -sub IsNonPointerType +# An enumeration defined in its own IDL file. +sub IsExternalEnumType { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; - return 1 if $nonPointerTypeHash{$type} or $primitiveTypeHash{$type} or $numericTypeHash{$type}; + assert("Not a type") if ref($type) ne "IDLType"; + + return $object->IsEnumType($type) && defined($cachedExternalEnumerations->{$type->name}); +} + +sub HasEnumImplementationNameOverride +{ + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + return 1 if exists $enumTypeImplementationNameOverrides{$type->name}; return 0; } -sub IsSVGTypeNeedingTearOff +sub GetEnumImplementationNameOverride { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + return $enumTypeImplementationNameOverrides{$type->name}; +} + +sub GetDictionaryByType +{ + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + my $name = $type->name; + + die "GetDictionaryByType() was called with an undefined dictionary name" unless defined($name); + + for my $dictionary (@{$useDocument->dictionaries}) { + return $dictionary if $dictionary->type->name eq $name; + } - return 1 if exists $svgTypeNeedingTearOff{$type}; + return $cachedExternalDictionaries->{$name} if exists($cachedExternalDictionaries->{$name}); + + # Find the IDL file associated with the dictionary. + my $filename = $object->IDLFileForInterface($name) or return; + + # Do a fast check to see if it seems to contain a dictionary. + my $fileContents = slurp($filename); + + if ($fileContents =~ /\bdictionary\s+$name/gs) { + # Parse the IDL. + my $parser = IDLParser->new(1); + my $document = $parser->Parse($filename, $defines, $preprocessor); + + foreach my $dictionary (@{$document->dictionaries}) { + next unless $dictionary->type->name eq $name; + + $cachedExternalDictionaries->{$name} = $dictionary; + my $implementedAs = $dictionary->extendedAttributes->{ImplementedAs}; + $dictionaryTypeImplementationNameOverrides{$dictionary->type->name} = $implementedAs if $implementedAs; + return $dictionary; + } + } + $cachedExternalDictionaries->{$name} = undef; +} + +sub IsDictionaryType +{ + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + return $type->name =~ /^[A-Z]/ && defined($object->GetDictionaryByType($type)); +} + +# A dictionary defined in its own IDL file. +sub IsExternalDictionaryType +{ + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + return $object->IsDictionaryType($type) && defined($cachedExternalDictionaries->{$type->name}); +} + +sub HasDictionaryImplementationNameOverride +{ + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + return 1 if exists $dictionaryTypeImplementationNameOverrides{$type->name}; return 0; } -sub IsSVGTypeWithWritablePropertiesNeedingTearOff +sub GetDictionaryImplementationNameOverride { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + return $dictionaryTypeImplementationNameOverrides{$type->name}; +} - return 1 if $svgTypeWithWritablePropertiesNeedingTearOff{$type}; +sub IsNonPointerType +{ + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + return 1 if $object->IsPrimitiveType($type); return 0; } sub IsTypedArrayType { - my $object = shift; - my $type = shift; - return 1 if (($type eq "ArrayBuffer") or ($type eq "ArrayBufferView")); - return 1 if (($type eq "Uint8Array") or ($type eq "Uint8ClampedArray") or ($type eq "Uint16Array") or ($type eq "Uint32Array")); - return 1 if (($type eq "Int8Array") or ($type eq "Int16Array") or ($type eq "Int32Array")); - return 1 if (($type eq "Float32Array") or ($type eq "Float64Array") or ($type eq "DataView")); + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + return 1 if $typedArrayTypes{$type->name}; return 0; } sub IsRefPtrType { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; return 0 if $object->IsPrimitiveType($type); - return 0 if $object->GetArrayType($type); - return 0 if $object->GetSequenceType($type); - return 0 if $type eq "DOMString"; + return 0 if $object->IsDictionaryType($type); return 0 if $object->IsEnumType($type); + return 0 if $object->IsSequenceOrFrozenArrayType($type); + return 0 if $object->IsRecordType($type); + return 0 if $object->IsStringType($type); + return 0 if $type->isUnion; + return 0 if $type->name eq "any"; + return 0 if $type->name eq "object"; return 1; } -sub GetSVGTypeNeedingTearOff +sub IsSVGAnimatedTypeName { - my $object = shift; - my $type = shift; + my ($object, $typeName) = @_; - return $svgTypeNeedingTearOff{$type} if exists $svgTypeNeedingTearOff{$type}; - return undef; + return $typeName =~ /^SVGAnimated/; } -sub GetSVGWrappedTypeNeedingTearOff +sub IsSVGAnimatedType { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; - my $svgTypeNeedingTearOff = $object->GetSVGTypeNeedingTearOff($type); - return $svgTypeNeedingTearOff if not $svgTypeNeedingTearOff; + return $object->IsSVGAnimatedTypeName($type->name); +} - if ($svgTypeNeedingTearOff =~ /SVGPropertyTearOff/) { - $svgTypeNeedingTearOff =~ s/SVGPropertyTearOff<//; - } elsif ($svgTypeNeedingTearOff =~ /SVGListPropertyTearOff/) { - $svgTypeNeedingTearOff =~ s/SVGListPropertyTearOff<//; - } elsif ($svgTypeNeedingTearOff =~ /SVGStaticListPropertyTearOff/) { - $svgTypeNeedingTearOff =~ s/SVGStaticListPropertyTearOff<//; - } elsif ($svgTypeNeedingTearOff =~ /SVGTransformListPropertyTearOff/) { - $svgTypeNeedingTearOff =~ s/SVGTransformListPropertyTearOff<//; - } +sub IsConstructorType +{ + my ($object, $type) = @_; - $svgTypeNeedingTearOff =~ s/>//; - return $svgTypeNeedingTearOff; + assert("Not a type") if ref($type) ne "IDLType"; + + return $type->name =~ /Constructor$/; } -sub IsSVGAnimatedType +sub IsSequenceType { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; - return $type =~ /^SVGAnimated/; + return $type->name eq "sequence"; } -sub GetSequenceType +sub IsFrozenArrayType { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; - return $1 if $type =~ /^sequence<([\w\d_\s]+)>.*/; - return ""; + return $type->name eq "FrozenArray"; } -sub GetArrayType +sub IsSequenceOrFrozenArrayType { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; - return $1 if $type =~ /^([\w\d_\s]+)\[\]/; - return ""; + return $object->IsSequenceType($type) || $object->IsFrozenArrayType($type); } -sub AssertNotSequenceType +sub IsRecordType { - my $object = shift; - my $type = shift; - die "Sequences must not be used as the type of an attribute, constant or exception field." if $object->GetSequenceType($type); + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + return $type->name eq "record"; } +# These match WK_lcfirst and WK_ucfirst defined in builtins_generator.py. # 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]/; $ret =~ s/Svg/SVG/ if $ret =~ /^Svg/; @@ -473,13 +705,16 @@ sub WK_ucfirst sub WK_lcfirst { my ($object, $param) = @_; + my $ret = lcfirst($param); + $ret =~ s/dOM/dom/ if $ret =~ /^dOM/; $ret =~ s/hTML/html/ if $ret =~ /^hTML/; $ret =~ s/uRL/url/ if $ret =~ /^uRL/; $ret =~ s/jS/js/ if $ret =~ /^jS/; $ret =~ s/xML/xml/ if $ret =~ /^xML/; $ret =~ s/xSLT/xslt/ if $ret =~ /^xSLT/; $ret =~ s/cSS/css/ if $ret =~ /^cSS/; + $ret =~ s/rTC/rtc/ if $ret =~ /^rTC/; # For HTML5 FileSystem API Flags attributes. # (create is widely used to instantiate an object and must be avoided.) @@ -489,10 +724,30 @@ sub WK_lcfirst return $ret; } +sub slurp +{ + my $file = shift; + + open my $fh, '<', $file or die; + local $/ = undef; + my $content = <$fh>; + close $fh; + return $content; +} + +sub trim +{ + my $string = shift; + + $string =~ s/^\s+|\s+$//g; + return $string; +} + # Return the C++ namespace that a given attribute name string is defined in. sub NamespaceForAttributeName { my ($object, $interfaceName, $attributeName) = @_; + return "SVGNames" if $interfaceName =~ /^SVG/ && !$svgAttributesInHTMLHash{$attributeName}; return "HTMLNames"; } @@ -505,23 +760,30 @@ sub LinkOverloadedFunctions my %nameToFunctionsMap = (); foreach my $function (@{$interface->functions}) { - my $name = $function->signature->name; + my $name = $function->name; $nameToFunctionsMap{$name} = [] if !exists $nameToFunctionsMap{$name}; push(@{$nameToFunctionsMap{$name}}, $function); $function->{overloads} = $nameToFunctionsMap{$name}; $function->{overloadIndex} = @{$nameToFunctionsMap{$name}}; } + + my $index = 1; + foreach my $constructor (@{$interface->constructors}) { + $constructor->{overloads} = $interface->constructors; + $constructor->{overloadIndex} = $index; + $index++; + } } sub AttributeNameForGetterAndSetter { my ($generator, $attribute) = @_; - my $attributeName = $attribute->signature->name; - if ($attribute->signature->extendedAttributes->{"ImplementedAs"}) { - $attributeName = $attribute->signature->extendedAttributes->{"ImplementedAs"}; + my $attributeName = $attribute->name; + if ($attribute->extendedAttributes->{"ImplementedAs"}) { + $attributeName = $attribute->extendedAttributes->{"ImplementedAs"}; } - my $attributeType = $attribute->signature->type; + my $attributeType = $attribute->type; # SVG animated types need to use a special attribute name. # The rest of the special casing for SVG animated types is handled in the language-specific code generators. @@ -534,7 +796,7 @@ sub ContentAttributeName { my ($generator, $implIncludes, $interfaceName, $attribute) = @_; - my $contentAttributeName = $attribute->signature->extendedAttributes->{"Reflect"}; + my $contentAttributeName = $attribute->extendedAttributes->{"Reflect"}; return undef if !$contentAttributeName; $contentAttributeName = lc $generator->AttributeNameForGetterAndSetter($attribute) if $contentAttributeName eq "VALUE_IS_MISSING"; @@ -555,16 +817,16 @@ sub GetterExpression return ($generator->WK_lcfirst($generator->AttributeNameForGetterAndSetter($attribute))); } - my $attributeType = $attribute->signature->type; + my $attributeType = $attribute->type; my $functionName; - if ($attribute->signature->extendedAttributes->{"URL"}) { + if ($attribute->extendedAttributes->{"URL"}) { $functionName = "getURLAttribute"; - } elsif ($attributeType eq "boolean") { - $functionName = "fastHasAttribute"; - } elsif ($attributeType eq "long") { + } elsif ($attributeType->name eq "boolean") { + $functionName = "hasAttributeWithoutSynchronization"; + } elsif ($attributeType->name eq "long") { $functionName = "getIntegralAttribute"; - } elsif ($attributeType eq "unsigned long") { + } elsif ($attributeType->name eq "unsigned long") { $functionName = "getUnsignedIntegralAttribute"; } else { if ($contentAttributeName eq "WebCore::HTMLNames::idAttr") { @@ -576,7 +838,7 @@ sub GetterExpression } elsif ($generator->IsSVGAnimatedType($attributeType)) { $functionName = "getAttribute"; } else { - $functionName = "fastGetAttribute"; + $functionName = "attributeWithoutSynchronization"; } } @@ -593,74 +855,209 @@ sub SetterExpression return ("set" . $generator->WK_ucfirst($generator->AttributeNameForGetterAndSetter($attribute))); } + my $attributeType = $attribute->type; + my $functionName; - if ($attribute->signature->type eq "boolean") { + if ($attributeType->name eq "boolean") { $functionName = "setBooleanAttribute"; - } elsif ($attribute->signature->type eq "long") { + } elsif ($attributeType->name eq "long") { $functionName = "setIntegralAttribute"; - } elsif ($attribute->signature->type eq "unsigned long") { + } elsif ($attributeType->name eq "unsigned long") { $functionName = "setUnsignedIntegralAttribute"; - } else { + } elsif ($generator->IsSVGAnimatedType($attributeType)) { $functionName = "setAttribute"; + } else { + $functionName = "setAttributeWithoutSynchronization"; } return ($functionName, $contentAttributeName); } -sub IsWrapperType +sub IsBuiltinType { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; - return 0 if $object->IsPrimitiveType($type); - return 0 if $object->GetArrayType($type); - return 0 if $object->GetSequenceType($type); + assert("Not a type") if ref($type) ne "IDLType"; + + return 1 if $object->IsPrimitiveType($type); + return 1 if $object->IsSequenceOrFrozenArrayType($type); + return 1 if $object->IsRecordType($type); + return 1 if $object->IsStringType($type); + return 1 if $object->IsTypedArrayType($type); + return 1 if $type->isUnion; + return 1 if $type->name eq "BufferSource"; + return 1 if $type->name eq "EventListener"; + return 1 if $type->name eq "JSON"; + return 1 if $type->name eq "Promise"; + return 1 if $type->name eq "SerializedScriptValue"; + return 1 if $type->name eq "XPathNSResolver"; + return 1 if $type->name eq "any"; + return 1 if $type->name eq "object"; + + return 0; +} + +sub IsInterfaceType +{ + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + return 0 if $object->IsBuiltinType($type); + return 0 if $object->IsDictionaryType($type); return 0 if $object->IsEnumType($type); - return 0 if $object->IsStringType($type); - return 0 if $object->IsTypedArrayType($type); - return 0 if $webCoreTypeHash{$type}; - return 0 if $type eq "any"; return 1; } -sub IsCallbackInterface +sub IsWrapperType { - my $object = shift; - my $type = shift; + my ($object, $type) = @_; - return 0 unless $object->IsWrapperType($type); + assert("Not a type") if ref($type) ne "IDLType"; - my $idlFile = $object->IDLFileForInterface($type) - or die("Could NOT find IDL file for interface \"$type\"!\n"); + return 1 if $object->IsInterfaceType($type); + return 1 if $type->name eq "XPathNSResolver"; - open FILE, "<", $idlFile; - my @lines = <FILE>; - close FILE; + return 0; +} + +sub IsSerializableAttribute +{ + my ($object, $currentInterface, $attribute) = @_; + + # https://heycam.github.io/webidl/#dfn-serializable-type + + my $type = $attribute->type; + return 1 if $type->name eq "boolean"; + return 1 if $object->IsNumericType($type); + return 1 if $object->IsEnumType($type); + return 1 if $object->IsStringType($type); + return 0 if $type->name eq "EventHandler"; + + if ($type->isUnion || $object->IsSequenceType($type) || $object->IsDictionaryType($type)) { + die "Serializer for non-primitive types is not currently supported\n"; + } - my $fileContents = join('', @lines); - return ($fileContents =~ /callback\s+interface\s+(\w+)/gs); + my $interface = GetInterfaceForAttribute($object, $currentInterface, $attribute); + if ($interface && $interface->serializable) { + die "Serializer for non-primitive types is not currently supported\n"; + } + + return 0; } -sub GenerateConditionalString +sub GetInterfaceExtendedAttributesFromName { - my $generator = shift; - my $node = shift; + # FIXME: It's bad to have a function like this that opens another IDL file to answer a question. + # Overusing this kind of function can make things really slow. Lets avoid these if we can. - my $conditional = $node->extendedAttributes->{"Conditional"}; - if ($conditional) { - return $generator->GenerateConditionalStringFromAttributeValue($conditional); - } else { - return ""; + my ($object, $interfaceName) = @_; + + my $idlFile = $object->IDLFileForInterface($interfaceName) or assert("Could NOT find IDL file for interface \"$interfaceName\"!\n"); + + open FILE, "<", $idlFile or die; + my @lines = <FILE>; + close FILE; + + my $fileContents = join('', @lines); + + my $extendedAttributes = {}; + + if ($fileContents =~ /\[(.*)\]\s+(callback interface|interface|exception)\s+(\w+)/gs) { + my @parts = split(',', $1); + foreach my $part (@parts) { + my @keyValue = split('=', $part); + my $key = trim($keyValue[0]); + next unless length($key); + my $value = "VALUE_IS_MISSING"; + $value = trim($keyValue[1]) if @keyValue > 1; + $extendedAttributes->{$key} = $value; + } } + + return $extendedAttributes; +} + +sub ComputeIsCallbackInterface +{ + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + return 0 unless $object->IsInterfaceType($type); + + my $typeName = $type->name; + my $idlFile = $object->IDLFileForInterface($typeName) or assert("Could NOT find IDL file for interface \"$typeName\"!\n"); + + open FILE, "<", $idlFile or die; + my @lines = <FILE>; + close FILE; + + my $fileContents = join('', @lines); + return ($fileContents =~ /callback\s+interface\s+(\w+)/gs); +} + +my %isCallbackInterface = (); + +sub IsCallbackInterface +{ + # FIXME: It's bad to have a function like this that opens another IDL file to answer a question. + # Overusing this kind of function can make things really slow. Lets avoid these if we can. + # To mitigate that, lets cache what we learn in a hash so we don't open the same file over and over. + + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + return $isCallbackInterface{$type->name} if exists $isCallbackInterface{$type->name}; + my $result = $object->ComputeIsCallbackInterface($type); + $isCallbackInterface{$type->name} = $result; + return $result; } -sub GenerateConstructorConditionalString +sub ComputeIsCallbackFunction { - my $generator = shift; - my $node = shift; + my ($object, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + return 0 unless $object->IsInterfaceType($type); + + my $typeName = $type->name; + my $idlFile = $object->IDLFileForInterface($typeName) or assert("Could NOT find IDL file for interface \"$typeName\"!\n"); + + open FILE, "<", $idlFile or die; + my @lines = <FILE>; + close FILE; + + my $fileContents = join('', @lines); + return ($fileContents =~ /(.*)callback\s+(\w+)\s+=/gs); +} + +my %isCallbackFunction = (); + +sub IsCallbackFunction +{ + # FIXME: It's bad to have a function like this that opens another IDL file to answer a question. + # Overusing this kind of function can make things really slow. Lets avoid these if we can. + # To mitigate that, lets cache what we learn in a hash so we don't open the same file over and over. + + my ($object, $type) = @_; - my $conditional = $node->extendedAttributes->{"ConstructorConditional"}; + assert("Not a type") if ref($type) ne "IDLType"; + + return $isCallbackFunction{$type->name} if exists $isCallbackFunction{$type->name}; + my $result = $object->ComputeIsCallbackFunction($type); + $isCallbackFunction{$type->name} = $result; + return $result; +} + +sub GenerateConditionalString +{ + my ($generator, $node) = @_; + + my $conditional = $node->extendedAttributes->{"Conditional"}; if ($conditional) { return $generator->GenerateConditionalStringFromAttributeValue($conditional); } else { @@ -670,51 +1067,49 @@ sub GenerateConstructorConditionalString sub GenerateConditionalStringFromAttributeValue { - my $generator = shift; - my $conditional = shift; - - my $operator = ($conditional =~ /&/ ? '&' : ($conditional =~ /\|/ ? '|' : '')); - if ($operator) { - # Avoid duplicated conditions. - my %conditions; - map { $conditions{$_} = 1 } split('\\' . $operator, $conditional); - return "ENABLE(" . join(") $operator$operator ENABLE(", sort keys %conditions) . ")"; - } else { - return "ENABLE(" . $conditional . ")"; - } + my ($generator, $conditional) = @_; + + my %disjunction; + map { + my $expression = $_; + my %conjunction; + map { $conjunction{$_} = 1; } split(/&/, $expression); + $expression = "ENABLE(" . join(") && ENABLE(", sort keys %conjunction) . ")"; + $disjunction{$expression} = 1 + } split(/\|/, $conditional); + + return "1" if keys %disjunction == 0; + return (%disjunction)[0] if keys %disjunction == 1; + + my @parenthesized; + map { + my $expression = $_; + $expression = "($expression)" if $expression =~ / /; + push @parenthesized, $expression; + } sort keys %disjunction; + + return join(" || ", @parenthesized); } sub GenerateCompileTimeCheckForEnumsIfNeeded { my ($generator, $interface) = @_; - my $interfaceName = $interface->name; - my @checks = (); - # If necessary, check that all constants are available as enums with the same value. - if (!$interface->extendedAttributes->{"DoNotCheckConstants"} && @{$interface->constants}) { - push(@checks, "\n"); - foreach my $constant (@{$interface->constants}) { - my $reflect = $constant->extendedAttributes->{"Reflect"}; - my $name = $reflect ? $reflect : $constant->name; - my $value = $constant->value; - my $conditional = $constant->extendedAttributes->{"Conditional"}; - - if ($conditional) { - my $conditionalString = $generator->GenerateConditionalStringFromAttributeValue($conditional); - push(@checks, "#if ${conditionalString}\n"); - } - if ($constant->extendedAttributes->{"ImplementedBy"}) { - push(@checks, "COMPILE_ASSERT($value == " . $constant->extendedAttributes->{"ImplementedBy"} . "::$name, ${interfaceName}Enum${name}IsWrongUseDoNotCheckConstants);\n"); - } else { - push(@checks, "COMPILE_ASSERT($value == ${interfaceName}::$name, ${interfaceName}Enum${name}IsWrongUseDoNotCheckConstants);\n"); - } + return () if $interface->extendedAttributes->{"DoNotCheckConstants"} || !@{$interface->constants}; - if ($conditional) { - push(@checks, "#endif\n"); - } - } - push(@checks, "\n"); + my $baseScope = $interface->extendedAttributes->{"ConstantsScope"} || $interface->type->name; + + my @checks = (); + foreach my $constant (@{$interface->constants}) { + my $scope = $constant->extendedAttributes->{"ImplementedBy"} || $baseScope; + my $name = $constant->extendedAttributes->{"Reflect"} || $constant->name; + my $value = $constant->value; + my $conditional = $constant->extendedAttributes->{"Conditional"}; + push(@checks, "#if " . $generator->GenerateConditionalStringFromAttributeValue($conditional) . "\n") if $conditional; + push(@checks, "static_assert(${scope}::${name} == ${value}, \"${name} in ${scope} does not match value from IDL\");\n"); + push(@checks, "#endif\n") if $conditional; } + push(@checks, "\n"); return @checks; } @@ -733,23 +1128,22 @@ sub ExtendedAttributeContains # should use the real interface name in the IDL files and then use ImplementedAs to map this to the implementation name. sub GetVisibleInterfaceName { - my $object = shift; - my $interface = shift; + my ($object, $interface) = @_; + my $interfaceName = $interface->extendedAttributes->{"InterfaceName"}; - return $interfaceName ? $interfaceName : $interface->name; + return $interfaceName ? $interfaceName : $interface->type->name; } sub InheritsInterface { - my $object = shift; - my $interface = shift; - my $interfaceName = shift; - my $found = 0; + my ($object, $interface, $interfaceName) = @_; - return 1 if $interfaceName eq $interface->name; + return 1 if $interfaceName eq $interface->type->name; + + my $found = 0; $object->ForAllParents($interface, sub { my $currentInterface = shift; - if ($currentInterface->name eq $interfaceName) { + if ($currentInterface->type->name eq $interfaceName) { $found = 1; } return 1 if $found; @@ -760,12 +1154,11 @@ sub InheritsInterface sub InheritsExtendedAttribute { - my $object = shift; - my $interface = shift; - my $extendedAttribute = shift; - my $found = 0; + my ($object, $interface, $extendedAttribute) = @_; return 1 if $interface->extendedAttributes->{$extendedAttribute}; + + my $found = 0; $object->ForAllParents($interface, sub { my $currentInterface = shift; if ($currentInterface->extendedAttributes->{$extendedAttribute}) { @@ -777,4 +1170,5 @@ sub InheritsExtendedAttribute return $found; } + 1; diff --git a/Source/WebCore/bindings/scripts/CodeGeneratorGObject.pm b/Source/WebCore/bindings/scripts/CodeGeneratorGObject.pm deleted file mode 100644 index b5b1d8e43..000000000 --- a/Source/WebCore/bindings/scripts/CodeGeneratorGObject.pm +++ /dev/null @@ -1,1694 +0,0 @@ -# Copyright (C) 2008 Luke Kenneth Casson Leighton <lkcl@lkcl.net> -# Copyright (C) 2008 Martin Soto <soto@freedesktop.org> -# Copyright (C) 2008 Alp Toker <alp@atoker.com> -# Copyright (C) 2009 Adam Dingle <adam@yorba.org> -# Copyright (C) 2009 Jim Nelson <jim@yorba.org> -# Copyright (C) 2009, 2010 Igalia S.L. -# -# 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 -# along with this library; see the file COPYING.LIB. If not, write to -# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. - -package CodeGeneratorGObject; - -use constant FileNamePrefix => "WebKitDOM"; - -# Global Variables -my %implIncludes = (); -my %hdrIncludes = (); - -my $defineTypeMacro = "G_DEFINE_TYPE"; -my $defineTypeInterfaceImplementation = ")"; -my @txtEventListeners = (); -my @txtInstallProps = (); -my @txtSetProps = (); -my @txtGetProps = (); - -my $className = ""; - -# FIXME: this should be replaced with a function that recurses up the tree -# to find the actual base type. -my %baseTypeHash = ("Object" => 1, "Node" => 1, "NodeList" => 1, "NamedNodeMap" => 1, "DOMImplementation" => 1, - "Event" => 1, "CSSRule" => 1, "CSSValue" => 1, "StyleSheet" => 1, "MediaList" => 1, - "Counter" => 1, "Rect" => 1, "RGBColor" => 1, "XPathExpression" => 1, "XPathResult" => 1, - "NodeIterator" => 1, "TreeWalker" => 1, "AbstractView" => 1, "Blob" => 1, "DOMTokenList" => 1, - "HTMLCollection" => 1); - -# Only objects derived from Node are released by the DOM object cache and can be -# transfer none. Ideally we could use GetBaseClass with the parent type to check -# whether it's Node, but unfortunately we only have the name of the return type, -# and we can't know its parent base class. Since there are fewer classes in the -# API that are not derived from Node, we will list them here to decide the -# transfer type. -my %transferFullTypeHash = ("AudioTrack" => 1, "AudioTrackList" => 1, "BarProp" => 1, "BatteryManager" => 1, - "CSSRuleList" => 1, "CSSStyleDeclaration" => 1, "CSSStyleSheet" => 1, - "DOMApplicationCache" => 1, "DOMMimeType" => 1, "DOMMimeTypeArray" => 1, "DOMNamedFlowCollection" => 1, - "DOMPlugin" => 1, "DOMPluginArray" => 1, "DOMSecurityPolicy" => 1, - "DOMSelection" => 1, "DOMSettableTokenList" => 1, "DOMStringList" => 1, - "DOMWindow" => 1, "DOMWindowCSS" => 1, "EventTarget" => 1, - "File" => 1, "FileList" => 1, "Gamepad" => 1, "GamepadList" => 1, - "Geolocation" => 1, "HTMLOptionsCollection" => 1, "History" => 1, - "KeyboardEvent" => 1, "MediaError" => 1, "MediaController" => 1, - "MouseEvent" => 1, "MediaQueryList" => 1, "Navigator" => 1, "NodeFilter" => 1, - "Performance" => 1, "PerformanceEntry" => 1, "PerformanceEntryList" => 1, "PerformanceNavigation" => 1, "PerformanceTiming" => 1, - "Range" => 1, "Screen" => 1, "SpeechSynthesis" => 1, "SpeechSynthesisVoice" => 1, - "Storage" => 1, "StyleMedia" => 1, "TextTrack" => 1, "TextTrackCueList" => 1, - "TimeRanges" => 1, "Touch" => 1, "UIEvent" => 1, "UserMessageHandler" => 1, "UserMessageHandlersNamespace" => 1, - "ValidityState" => 1, "VideoTrack" => 1, "WebKitNamedFlow" => 1, - "WebKitNamespace" => 1, "WebKitPoint" => 1, "WheelEvent" => 1, "XPathNSResolver" => 1); - -# List of function parameters that are allowed to be NULL -my $canBeNullParams = { - 'webkit_dom_document_create_attribute_ns' => ['namespaceURI'], - 'webkit_dom_document_create_element_ns' => ['namespaceURI'], - 'webkit_dom_document_create_entity_reference' => ['name'], - 'webkit_dom_document_evaluate' => ['inResult', 'resolver'], - 'webkit_dom_document_get_override_style' => ['pseudoElement'], - 'webkit_dom_dom_implementation_create_document' => ['namespaceURI', 'doctype'], - 'webkit_dom_dom_window_get_computed_style' => ['pseudoElement'], - 'webkit_dom_element_set_attribute_ns' => ['namespaceURI'], - 'webkit_dom_node_insert_before' => ['refChild'], -}; - -# Default constructor -sub new { - my $object = shift; - my $reference = { }; - - $codeGenerator = shift; - - bless($reference, $object); -} - -my $licenceTemplate = << "EOF"; -/* - * This file is part of the WebKit open source project. - * This file has been generated by generate-bindings.pl. DO NOT MODIFY! - * - * 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 - * along 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. - */ -EOF - -sub GetParentClassName { - my $interface = shift; - - return "WebKitDOMObject" unless $interface->parent; - return "WebKitDOM" . $interface->parent; -} - -sub GetParentImplClassName { - my $interface = shift; - - return "Object" unless $interface->parent; - return $interface->parent; -} - -sub IsBaseType -{ - my $type = shift; - - return 1 if $baseTypeHash{$type}; - return 0; -} - -sub GetBaseClass -{ - $parent = shift; - $interface = shift; - - return $parent if $parent eq "Object" or IsBaseType($parent); - return "Event" if $codeGenerator->InheritsInterface($interface, "Event"); - return "CSSValue" if $parent eq "SVGColor" or $parent eq "CSSValueList"; - return "Node"; -} - - -# From String::CamelCase 0.01 -sub camelize -{ - my $s = shift; - join('', map{ ucfirst $_ } split(/(?<=[A-Za-z])_(?=[A-Za-z])|\b/, $s)); -} - -sub decamelize -{ - my $s = shift; - $s =~ s{([^a-zA-Z]?)([A-Z]*)([A-Z])([a-z]?)}{ - my $fc = pos($s)==0; - my ($p0,$p1,$p2,$p3) = ($1,lc$2,lc$3,$4); - my $t = $p0 || $fc ? $p0 : '_'; - $t .= $p3 ? $p1 ? "${p1}_$p2$p3" : "$p2$p3" : "$p1$p2"; - $t; - }ge; - - # Some strings are not correctly decamelized, apply fix ups - for ($s) { - s/domcss/dom_css/; - s/domhtml/dom_html/; - s/domdom/dom_dom/; - s/domcdata/dom_cdata/; - s/domui/dom_ui/; - s/x_path/xpath/; - s/web_kit/webkit/; - s/htmli_frame/html_iframe/; - } - return $s; -} - -sub HumanReadableConditional { - my @conditional = split('_', shift); - my @upperCaseExceptions = ("SQL", "API"); - my @humanReadable; - - for $part (@conditional) { - if (!grep {$_ eq $part} @upperCaseExceptions) { - $part = camelize(lc($part)); - } - push(@humanReadable, $part); - } - - return join(' ', @humanReadable); -} - -sub GetParentGObjType { - my $interface = shift; - - return "WEBKIT_TYPE_DOM_OBJECT" unless $interface->parent; - return "WEBKIT_TYPE_DOM_" . uc(decamelize(($interface->parent))); -} - -sub GetClassName { - my $name = shift; - - return "WebKitDOM$name"; -} - -sub GetCoreObject { - my ($interfaceName, $name, $parameter) = @_; - - return "WebCore::${interfaceName}* $name = WebKit::core($parameter);"; -} - -sub SkipAttribute { - my $attribute = shift; - - if ($attribute->signature->extendedAttributes->{"Custom"} - || $attribute->signature->extendedAttributes->{"CustomGetter"}) { - return 1; - } - - my $propType = $attribute->signature->type; - if ($propType =~ /Constructor$/) { - return 1; - } - - return 1 if $attribute->isStatic; - return 1 if $codeGenerator->IsTypedArrayType($propType); - - $codeGenerator->AssertNotSequenceType($propType); - - if ($codeGenerator->GetArrayType($propType)) { - return 1; - } - - if ($codeGenerator->IsEnumType($propType)) { - return 1; - } - - # This is for DOMWindow.idl location attribute - if ($attribute->signature->name eq "location") { - return 1; - } - - # This is for HTMLInput.idl valueAsDate - if ($attribute->signature->name eq "valueAsDate") { - return 1; - } - - # This is for DOMWindow.idl Crypto attribute - if ($attribute->signature->type eq "Crypto") { - return 1; - } - - if ($attribute->signature->type eq "EventListener") { - return 1; - } - - if ($attribute->signature->type eq "MediaQueryListListener") { - return 1; - } - - # Skip indexed database attributes for now, they aren't yet supported for the GObject generator. - if ($attribute->signature->name =~ /^(?:webkit)?[Ii]ndexedDB/ or $attribute->signature->name =~ /^(?:webkit)?IDB/) { - return 1; - } - - return 0; -} - -sub SkipFunction { - my $object = shift; - my $function = shift; - my $parentNode = shift; - my $decamelize = shift; - my $prefix = shift; - - my $functionName = "webkit_dom_" . $decamelize . "_" . $prefix . decamelize($function->signature->name); - my $functionReturnType = $prefix eq "set_" ? "void" : $function->signature->type; - my $isCustomFunction = $function->signature->extendedAttributes->{"Custom"}; - my $callWith = $function->signature->extendedAttributes->{"CallWith"}; - my $isUnsupportedCallWith = $codeGenerator->ExtendedAttributeContains($callWith, "ScriptArguments") || $codeGenerator->ExtendedAttributeContains($callWith, "CallStack"); - - if (($isCustomFunction || $isUnsupportedCallWith) && - $functionName ne "webkit_dom_node_replace_child" && - $functionName ne "webkit_dom_node_insert_before" && - $functionName ne "webkit_dom_node_remove_child" && - $functionName ne "webkit_dom_node_append_child" && - $functionName ne "webkit_dom_html_collection_item" && - $functionName ne "webkit_dom_html_collection_named_item") { - return 1; - } - - # Skip functions that have callback parameters, because this code generator doesn't know - # how to auto-generate callbacks. Skip functions that have "MediaQueryListListener" or - # sequence<T> parameters, because this code generator doesn't know how to auto-generate - # MediaQueryListListener or sequence<T>. Skip EventListeners because they are handled elsewhere. - foreach my $param (@{$function->parameters}) { - if ($codeGenerator->IsCallbackInterface($param->type) || - $param->extendedAttributes->{"Clamp"} || - $param->type eq "MediaQueryListListener" || - $param->type eq "EventListener" || - $codeGenerator->GetSequenceType($param->type)) { - return 1; - } - } - - # Skip functions for which we have a custom implementation due to API breaks - if ($functionName eq "webkit_dom_html_media_element_set_current_time") { - return 1; - } - - # This is for DataTransferItemList.idl add(File) method - if ($functionName eq "webkit_dom_data_transfer_item_list_add" && @{$function->parameters} == 1) { - return 1; - } - - # Skip dispatch_event methods, except the one already deprecated. - if ($parentNode->extendedAttributes->{"EventTarget"} && $function->signature->name eq "dispatchEvent" - && $functionName ne "webkit_dom_audio_track_list_dispatch_event" - && $functionName ne "webkit_dom_battery_manager_dispatch_event" - && $functionName ne "webkit_dom_dom_application_cache_dispatch_event" - && $functionName ne "webkit_dom_dom_window_dispatch_event" - && $functionName ne "webkit_dom_node_dispatch_event" - && $functionName ne "webkit_dom_text_track_cue_dispatch_event" - && $functionName ne "webkit_dom_text_track_dispatch_event" - && $functionName ne "webkit_dom_text_track_list_dispatch_event" - && $functionName ne "webkit_dom_video_track_list_dispatch_event" - && $functionName ne "webkit_dom_webkit_named_flow_dispatch_event" - && $functionName ne "webkit_dom_test_event_target_dispatch_event") { - return 1; - } - - # Skip Console::profile() and Console::profileEnd() as they're not correctly generated for the moment. - if ($functionName eq "webkit_dom_console_profile" || $functionName eq "webkit_dom_console_profile_end") { - return 1; - } - - if ($function->signature->name eq "set" and $parentNode->extendedAttributes->{"TypedArray"}) { - return 1; - } - - if ($object eq "MediaQueryListListener") { - return 1; - } - - if ($function->signature->name eq "getSVGDocument") { - return 1; - } - - if ($function->signature->name eq "getCSSCanvasContext") { - return 1; - } - - if ($function->signature->name eq "setRangeText" && @{$function->parameters} == 1) { - return 1; - } - - if ($function->signature->name eq "timeEnd") { - return 1; - } - - if ($codeGenerator->GetSequenceType($functionReturnType)) { - return 1; - } - - if ($function->signature->name eq "supports" && @{$function->parameters} == 1) { - return 1; - } - - return 0; -} - -# Name type used in the g_value_{set,get}_* functions -sub GetGValueTypeName { - my $type = shift; - - my %types = ("DOMString", "string", - "DOMTimeStamp", "uint", - "float", "float", - "double", "double", - "boolean", "boolean", - "char", "char", - "long", "long", - "long long", "int64", - "byte", "int8", - "octet", "uint8", - "short", "int", - "uchar", "uchar", - "unsigned", "uint", - "int", "int", - "unsigned int", "uint", - "unsigned long long", "uint64", - "unsigned long", "ulong", - "unsigned short", "uint"); - - return $types{$type} ? $types{$type} : "object"; -} - -# Name type used in C declarations -sub GetGlibTypeName { - my $type = shift; - my $name = GetClassName($type); - - my %types = ("DOMString", "gchar*", - "DOMTimeStamp", "guint32", - "CompareHow", "gushort", - "float", "gfloat", - "double", "gdouble", - "boolean", "gboolean", - "char", "gchar", - "long", "glong", - "long long", "gint64", - "byte", "gint8", - "octet", "guint8", - "short", "gshort", - "uchar", "guchar", - "unsigned", "guint", - "int", "gint", - "unsigned int", "guint", - "unsigned long", "gulong", - "unsigned long long", "guint64", - "unsigned short", "gushort", - "void", "void"); - - return $types{$type} ? $types{$type} : "$name*"; -} - -sub IsGDOMClassType { - my $type = shift; - - return 0 if $codeGenerator->IsNonPointerType($type) || $codeGenerator->IsStringType($type); - return 1; -} - -sub IsPropertyReadable { - my $property = shift; - return !SkipAttribute($property); -} - -sub IsPropertyWriteable { - my $property = shift; - - if (!IsPropertyReadable($property)) { - return 0; - } - - if ($property->isReadOnly) { - return 0; - } - - my $gtype = GetGValueTypeName($property->signature->type); - my $hasGtypeSignature = $gtype eq "boolean" || $gtype eq "float" || $gtype eq "double" || - $gtype eq "int64" || $gtype eq "uint64" || - $gtype eq "long" || $gtype eq "ulong" || - $gtype eq "int" || $gtype eq "uint" || - $gtype eq "short" || $gtype eq "ushort" || - $gtype eq "int8" || $gtype eq "uint8" || - $gtype eq "char" || $gtype eq "uchar" || - $gtype eq "string"; - if (!$hasGtypeSignature) { - return 0; - } - - # FIXME: We are not generating setters for 'Replaceable' attributes now, but we should somehow. - if ($property->signature->extendedAttributes->{"Replaceable"}) { - return 0; - } - - if ($property->signature->extendedAttributes->{"CustomSetter"}) { - return 0; - } - - return 1; -} - -sub GenerateConditionalWarning -{ - my $node = shift; - my $indentSize = shift; - if (!$indentSize) { - $indentSize = 4; - } - - my $conditional = $node->extendedAttributes->{"Conditional"}; - my @warn; - - if ($conditional) { - if ($conditional =~ /&/) { - my @splitConditionals = split(/&/, $conditional); - foreach $condition (@splitConditionals) { - push(@warn, "#if !ENABLE($condition)\n"); - push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($condition) . "\")\n"); - push(@warn, "#endif\n"); - } - } elsif ($conditional =~ /\|/) { - foreach $condition (split(/\|/, $conditional)) { - push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($condition) . "\")\n"); - } - } else { - push(@warn, ' ' x $indentSize . "WEBKIT_WARN_FEATURE_NOT_PRESENT(\"" . HumanReadableConditional($conditional) . "\")\n"); - } - } - - return @warn; -} - -sub GenerateProperty { - my $attribute = shift; - my $interfaceName = shift; - my @writeableProperties = @{shift @_}; - my $parentNode = shift; - - my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); - my @conditionalWarn = GenerateConditionalWarning($attribute->signature, 8); - my $parentConditionalString = $codeGenerator->GenerateConditionalString($parentNode); - my @parentConditionalWarn = GenerateConditionalWarning($parentNode, 8); - my $camelPropName = $attribute->signature->name; - my $setPropNameFunction = $codeGenerator->WK_ucfirst($camelPropName); - my $getPropNameFunction = $codeGenerator->WK_lcfirst($camelPropName); - my $hasGetterException = $attribute->signature->extendedAttributes->{"GetterRaisesException"}; - my $hasSetterException = $attribute->signature->extendedAttributes->{"SetterRaisesException"}; - - my $propName = decamelize($camelPropName); - my $propNameCaps = uc($propName); - $propName =~ s/_/-/g; - my ${propEnum} = "PROP_${propNameCaps}"; - push(@cBodyProperties, " ${propEnum},\n"); - - my $propType = $attribute->signature->type; - my ${propGType} = decamelize($propType); - my ${ucPropGType} = uc($propGType); - - my $gtype = GetGValueTypeName($propType); - my $gparamflag = "WEBKIT_PARAM_READABLE"; - my $writeable = IsPropertyWriteable($attribute); - - my $mutableString = "read-only"; - my $hasCustomSetter = $attribute->signature->extendedAttributes->{"CustomSetter"}; - if ($writeable && $hasCustomSetter) { - $mutableString = "read-only (due to custom functions needed in webkitdom)"; - } elsif ($writeable) { - $gparamflag = "WEBKIT_PARAM_READWRITE"; - $mutableString = "read-write"; - } - - my $convertFunction = ""; - if ($gtype eq "string") { - $convertFunction = "WTF::String::fromUTF8"; - } - - my ($getterFunctionName, @getterArguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $attribute); - my ($setterFunctionName, @setterArguments) = $codeGenerator->SetterExpression(\%implIncludes, $interfaceName, $attribute); - - if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) { - my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"}; - $implIncludes{"${implementedBy}.h"} = 1; - push(@setterArguments, "${convertFunction}(g_value_get_$gtype(value))"); - unshift(@getterArguments, "coreSelf"); - unshift(@setterArguments, "coreSelf"); - $getterFunctionName = "WebCore::${implementedBy}::$getterFunctionName"; - $setterFunctionName = "WebCore::${implementedBy}::$setterFunctionName"; - } else { - push(@setterArguments, "${convertFunction}(g_value_get_$gtype(value))"); - $getterFunctionName = "coreSelf->$getterFunctionName"; - $setterFunctionName = "coreSelf->$setterFunctionName"; - } - push(@getterArguments, "isNull") if $attribute->signature->isNullable; - push(@getterArguments, "ec") if $hasGetterException; - push(@setterArguments, "ec") if $hasSetterException; - - if (grep {$_ eq $attribute} @writeableProperties) { - push(@txtSetProps, " case ${propEnum}: {\n"); - push(@txtSetProps, "#if ${parentConditionalString}\n") if $parentConditionalString; - push(@txtSetProps, "#if ${conditionalString}\n") if $conditionalString; - push(@txtSetProps, " WebCore::ExceptionCode ec = 0;\n") if $hasSetterException; - push(@txtSetProps, " ${setterFunctionName}(" . join(", ", @setterArguments) . ");\n"); - push(@txtSetProps, "#else\n") if $conditionalString; - push(@txtSetProps, @conditionalWarn) if scalar(@conditionalWarn); - push(@txtSetProps, "#endif /* ${conditionalString} */\n") if $conditionalString; - push(@txtSetProps, "#else\n") if $parentConditionalString; - push(@txtSetProps, @parentConditionalWarn) if scalar(@parentConditionalWarn); - push(@txtSetProps, "#endif /* ${parentConditionalString} */\n") if $parentConditionalString; - push(@txtSetProps, " break;\n }\n"); - } - - push(@txtGetProps, " case ${propEnum}: {\n"); - push(@txtGetProps, "#if ${parentConditionalString}\n") if $parentConditionalString; - push(@txtGetProps, "#if ${conditionalString}\n") if $conditionalString; - push(@txtGetProps, " bool isNull = false;\n") if $attribute->signature->isNullable; - push(@txtGetProps, " WebCore::ExceptionCode ec = 0;\n") if $hasGetterException; - - # FIXME: Should we return a default value when isNull == true? - - my $postConvertFunction = ""; - if ($gtype eq "string") { - push(@txtGetProps, " g_value_take_string(value, convertToUTF8String(${getterFunctionName}(" . join(", ", @getterArguments) . ")));\n"); - } elsif ($gtype eq "object") { - push(@txtGetProps, " RefPtr<WebCore::${propType}> ptr = ${getterFunctionName}(" . join(", ", @getterArguments) . ");\n"); - push(@txtGetProps, " g_value_set_object(value, WebKit::kit(ptr.get()));\n"); - } else { - push(@txtGetProps, " g_value_set_$gtype(value, ${convertFunction}${getterFunctionName}(" . join(", ", @getterArguments) . ")${postConvertFunction});\n"); - } - - push(@txtGetProps, "#else\n") if $conditionalString; - push(@txtGetProps, @conditionalWarn) if scalar(@conditionalWarn); - push(@txtGetProps, "#endif /* ${conditionalString} */\n") if $conditionalString; - push(@txtGetProps, "#else\n") if $parentConditionalString; - push(@txtGetProps, @parentConditionalWarn) if scalar(@parentConditionalWarn); - push(@txtGetProps, "#endif /* ${parentConditionalString} */\n") if $parentConditionalString; - push(@txtGetProps, " break;\n }\n"); - - my %parameterSpecOptions = ("int" => [ "G_MININT", "G_MAXINT", "0" ], - "int8" => [ "G_MININT8", "G_MAXINT8", "0" ], - "boolean" => [ "FALSE" ], - "float" => [ "-G_MAXFLOAT", "G_MAXFLOAT", "0" ], - "double" => [ "-G_MAXDOUBLE", "G_MAXDOUBLE", "0" ], - "uint64" => [ "0", "G_MAXUINT64", "0" ], - "long" => [ "G_MINLONG", "G_MAXLONG", "0" ], - "int64" => [ "G_MININT64", "G_MAXINT64", "0" ], - "ulong" => [ "0", "G_MAXULONG", "0" ], - "uint" => [ "0", "G_MAXUINT", "0" ], - "uint8" => [ "0", "G_MAXUINT8", "0" ], - "ushort" => [ "0", "G_MAXUINT16", "0" ], - "uchar" => [ "G_MININT8", "G_MAXINT8", "0" ], - "char" => [ "0", "G_MAXUINT8", "0" ], - "string" => [ '""', ], - "object" => [ "WEBKIT_TYPE_DOM_${ucPropGType}" ]); - - my $extraParameters = join(", ", @{$parameterSpecOptions{$gtype}}); - my $glibTypeName = GetGlibTypeName($propType); - my $txtInstallProp = << "EOF"; - g_object_class_install_property( - gobjectClass, - $propEnum, - g_param_spec_$gtype( - "$propName", - "$interfaceName:$propName", - "$mutableString $glibTypeName $interfaceName:$propName", - $extraParameters, - $gparamflag)); - -EOF - push(@txtInstallProps, $txtInstallProp); -} - -sub GenerateProperties { - my ($object, $interfaceName, $interface) = @_; - - my $decamelize = decamelize($interfaceName); - my $clsCaps = uc($decamelize); - my $lowerCaseIfaceName = "webkit_dom_$decamelize"; - my $parentImplClassName = GetParentImplClassName($interface); - - my $conditionGuardStart = ""; - my $conditionGuardEnd = ""; - my $conditionalString = $codeGenerator->GenerateConditionalString($interface); - if ($conditionalString) { - $conditionGuardStart = "#if ${conditionalString}"; - $conditionGuardEnd = "#endif // ${conditionalString}"; - } - - # Properties - my $implContent = ""; - my @readableProperties = grep { IsPropertyReadable($_) } @{$interface->attributes}; - my @writeableProperties = grep { IsPropertyWriteable($_) } @{$interface->attributes}; - my $numProperties = scalar @readableProperties; - - # Properties - my $privFunction = GetCoreObject($interfaceName, "coreSelf", "self"); - if ($numProperties > 0) { - $implContent = << "EOF"; -enum { - PROP_0, -EOF - push(@cBodyProperties, $implContent); - - my $txtGetProp = << "EOF"; -static void ${lowerCaseIfaceName}_get_property(GObject* object, guint propertyId, GValue* value, GParamSpec* pspec) -{ - WebCore::JSMainThreadNullState state; -EOF - push(@txtGetProps, $txtGetProp); - $txtGetProp = << "EOF"; -$conditionGuardStart - ${className}* self = WEBKIT_DOM_${clsCaps}(object); - $privFunction -$conditionGuardEnd -EOF - push(@txtGetProps, $txtGetProp); - - $txtGetProp = << "EOF"; - switch (propertyId) { -EOF - push(@txtGetProps, $txtGetProp); - - if (scalar @writeableProperties > 0) { - my $txtSetProps = << "EOF"; -static void ${lowerCaseIfaceName}_set_property(GObject* object, guint propertyId, const GValue* value, GParamSpec* pspec) -{ - WebCore::JSMainThreadNullState state; -EOF - push(@txtSetProps, $txtSetProps); - - $txtSetProps = << "EOF"; -$conditionGuardStart - ${className}* self = WEBKIT_DOM_${clsCaps}(object); - $privFunction -$conditionGuardEnd -EOF - push(@txtSetProps, $txtSetProps); - - $txtSetProps = << "EOF"; - switch (propertyId) { -EOF - push(@txtSetProps, $txtSetProps); - } - - foreach my $attribute (@readableProperties) { - GenerateProperty($attribute, $interfaceName, \@writeableProperties, $interface); - } - - push(@cBodyProperties, "};\n\n"); - - $txtGetProp = << "EOF"; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, pspec); - break; - } -} -EOF - push(@txtGetProps, $txtGetProp); - - if (scalar @writeableProperties > 0) { - $txtSetProps = << "EOF"; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, pspec); - break; - } -} -EOF - push(@txtSetProps, $txtSetProps); - } - } - - # Do not insert extra spaces when interpolating array variables - $" = ""; - - if ($parentImplClassName eq "Object") { - $implContent = << "EOF"; -static void ${lowerCaseIfaceName}_finalize(GObject* object) -{ - ${className}Private* priv = WEBKIT_DOM_${clsCaps}_GET_PRIVATE(object); -$conditionGuardStart - WebKit::DOMObjectCache::forget(priv->coreObject.get()); -$conditionGuardEnd - priv->~${className}Private(); - G_OBJECT_CLASS(${lowerCaseIfaceName}_parent_class)->finalize(object); -} - -EOF - push(@cBodyProperties, $implContent); - } - - if ($numProperties > 0) { - if (scalar @writeableProperties > 0) { - push(@cBodyProperties, @txtSetProps); - push(@cBodyProperties, "\n"); - } - push(@cBodyProperties, @txtGetProps); - push(@cBodyProperties, "\n"); - } - - # Add a constructor implementation only for direct subclasses of Object to make sure - # that the WebCore wrapped object is added only once to the DOM cache. The DOM garbage - # collector works because Node is a direct subclass of Object and the version of - # DOMObjectCache::put() that receives a Node (which is the one setting the frame) is - # always called for DOM objects derived from Node. - if ($parentImplClassName eq "Object") { - $implContent = << "EOF"; -static GObject* ${lowerCaseIfaceName}_constructor(GType type, guint constructPropertiesCount, GObjectConstructParam* constructProperties) -{ - GObject* object = G_OBJECT_CLASS(${lowerCaseIfaceName}_parent_class)->constructor(type, constructPropertiesCount, constructProperties); -$conditionGuardStart - ${className}Private* priv = WEBKIT_DOM_${clsCaps}_GET_PRIVATE(object); - priv->coreObject = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(object)->coreObject); - WebKit::DOMObjectCache::put(priv->coreObject.get(), object); -$conditionGuardEnd - return object; -} - -EOF - push(@cBodyProperties, $implContent); - } - - $implContent = << "EOF"; -static void ${lowerCaseIfaceName}_class_init(${className}Class* requestClass) -{ -EOF - push(@cBodyProperties, $implContent); - - if ($parentImplClassName eq "Object" || $numProperties > 0) { - push(@cBodyProperties, " GObjectClass* gobjectClass = G_OBJECT_CLASS(requestClass);\n"); - - if ($parentImplClassName eq "Object") { - push(@cBodyProperties, " g_type_class_add_private(gobjectClass, sizeof(${className}Private));\n"); - push(@cBodyProperties, " gobjectClass->constructor = ${lowerCaseIfaceName}_constructor;\n"); - push(@cBodyProperties, " gobjectClass->finalize = ${lowerCaseIfaceName}_finalize;\n"); - } - - if ($numProperties > 0) { - if (scalar @writeableProperties > 0) { - push(@cBodyProperties, " gobjectClass->set_property = ${lowerCaseIfaceName}_set_property;\n"); - } - push(@cBodyProperties, " gobjectClass->get_property = ${lowerCaseIfaceName}_get_property;\n"); - push(@cBodyProperties, "\n"); - push(@cBodyProperties, @txtInstallProps); - } - } - $implContent = << "EOF"; -} - -static void ${lowerCaseIfaceName}_init(${className}* request) -{ -EOF - push(@cBodyProperties, $implContent); - - if ($parentImplClassName eq "Object") { - $implContent = << "EOF"; - ${className}Private* priv = WEBKIT_DOM_${clsCaps}_GET_PRIVATE(request); - new (priv) ${className}Private(); -EOF - push(@cBodyProperties, $implContent); - } - $implContent = << "EOF"; -} - -EOF - push(@cBodyProperties, $implContent); -} - -sub GenerateHeader { - my ($object, $interfaceName, $parentClassName) = @_; - - my $implContent = ""; - - # Add the default header template - @hPrefix = split("\r", $licenceTemplate); - push(@hPrefix, "\n"); - - # Force single header include. - my $headerCheck = << "EOF"; -#if !defined(__WEBKITDOM_H_INSIDE__) && !defined(BUILDING_WEBKIT) -#error "Only <webkitdom/webkitdom.h> can be included directly." -#endif - -EOF - push(@hPrefix, $headerCheck); - - # Header guard - my $guard = $className . "_h"; - - @hPrefixGuard = << "EOF"; -#ifndef $guard -#define $guard - -EOF - - $implContent = << "EOF"; -G_BEGIN_DECLS - -EOF - - push(@hBodyPre, $implContent); - - my $decamelize = decamelize($interfaceName); - my $clsCaps = uc($decamelize); - my $lowerCaseIfaceName = "webkit_dom_$decamelize"; - - $implContent = << "EOF"; -#define WEBKIT_TYPE_DOM_${clsCaps} (${lowerCaseIfaceName}_get_type()) -#define WEBKIT_DOM_${clsCaps}(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_DOM_${clsCaps}, ${className})) -#define WEBKIT_DOM_${clsCaps}_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_DOM_${clsCaps}, ${className}Class) -#define WEBKIT_DOM_IS_${clsCaps}(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_DOM_${clsCaps})) -#define WEBKIT_DOM_IS_${clsCaps}_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_DOM_${clsCaps})) -#define WEBKIT_DOM_${clsCaps}_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WEBKIT_TYPE_DOM_${clsCaps}, ${className}Class)) - -struct _${className} { - ${parentClassName} parent_instance; -}; - -struct _${className}Class { - ${parentClassName}Class parent_class; -}; - -WEBKIT_API GType -${lowerCaseIfaceName}_get_type (void); - -EOF - - push(@hBody, $implContent); -} - -sub GetGReturnMacro { - my ($paramName, $paramIDLType, $returnType, $functionName) = @_; - - my $condition; - if ($paramIDLType eq "GError") { - $condition = "!$paramName || !*$paramName"; - } elsif (IsGDOMClassType($paramIDLType)) { - my $paramTypeCaps = uc(decamelize($paramIDLType)); - $condition = "WEBKIT_DOM_IS_${paramTypeCaps}($paramName)"; - if (ParamCanBeNull($functionName, $paramName)) { - $condition = "!$paramName || $condition"; - } - } else { - if (ParamCanBeNull($functionName, $paramName)) { - return ""; - } - $condition = "$paramName"; - } - - my $macro; - if ($returnType ne "void") { - $defaultReturn = $returnType eq "gboolean" ? "FALSE" : 0; - $macro = " g_return_val_if_fail($condition, $defaultReturn);\n"; - } else { - $macro = " g_return_if_fail($condition);\n"; - } - - return $macro; -} - -sub ParamCanBeNull { - my($functionName, $paramName) = @_; - - if (defined($functionName)) { - return scalar(grep {$_ eq $paramName} @{$canBeNullParams->{$functionName}}); - } - return 0; -} - -sub GetFunctionDeprecationInformation { - my($function, $parentNode) = @_; - - my $version; - my $replacement; - - if ($parentNode->extendedAttributes->{"EventTarget"} && $function->signature->name eq "dispatchEvent") { - # dispatchEvent is implemented already as part fo the WebKitDOMEventTarget interface. - # Mark it as deprecated for now in favor of the interface method, and skip it once - # we break the API. All other methods of WebKitDOMEventTarget interface are already - # skipped because they receive an EventListener as parameter. - $version = "2.4"; - $replacement = "webkit_dom_event_target_dispatch_event"; - } - - return ($version, $replacement); -} - -sub GetTransferTypeForReturnType { - my $returnType = shift; - - # Node is always transfer none. - return "none" if $returnType eq "Node"; - - # Any base class but Node is transfer full. - return "full" if IsBaseType($returnType); - - # Any other class not derived from Node is transfer full. - return "full" if $transferFullTypeHash{$returnType}; - return "none"; -} - -sub GenerateFunction { - my ($object, $interfaceName, $function, $prefix, $parentNode) = @_; - - my $decamelize = decamelize($interfaceName); - - if (SkipFunction($object, $function, $parentNode, $decamelize, $prefix)) { - return; - } - - my ($deprecationVersion, $deprecationReplacement) = GetFunctionDeprecationInformation($function, $parentNode); - my $functionSigType = $prefix eq "set_" ? "void" : $function->signature->type; - my $functionName = "webkit_dom_" . $decamelize . "_" . $prefix . decamelize($function->signature->name); - my $returnType = GetGlibTypeName($functionSigType); - my $returnValueIsGDOMType = IsGDOMClassType($functionSigType); - my $raisesException = $function->signature->extendedAttributes->{"RaisesException"}; - - my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); - my $parentConditionalString = $codeGenerator->GenerateConditionalString($parentNode); - my @conditionalWarn = GenerateConditionalWarning($function->signature); - my @parentConditionalWarn = GenerateConditionalWarning($parentNode); - - my $functionSig = "${className}* self"; - my $symbolSig = "${className}*"; - - my @callImplParams; - foreach my $param (@{$function->parameters}) { - my $paramIDLType = $param->type; - my $paramType = GetGlibTypeName($paramIDLType); - my $const = $paramType eq "gchar*" ? "const " : ""; - my $paramName = $param->name; - - $functionSig .= ", ${const}$paramType $paramName"; - $symbolSig .= ", ${const}$paramType"; - - my $paramIsGDOMType = IsGDOMClassType($paramIDLType); - if ($paramIsGDOMType) { - if ($paramIDLType ne "any") { - $implIncludes{"WebKitDOM${paramIDLType}Private.h"} = 1; - } - } - if ($paramIsGDOMType || ($paramIDLType eq "DOMString") || ($paramIDLType eq "CompareHow")) { - $paramName = "converted" . $codeGenerator->WK_ucfirst($paramName); - } - push(@callImplParams, $paramName); - } - - if ($returnType ne "void" && $returnValueIsGDOMType && $functionSigType ne "any") { - $implIncludes{"WebKitDOM${functionSigType}Private.h"} = 1; - } - - $functionSig .= ", GError** error" if $raisesException; - $symbolSig .= ", GError**" if $raisesException; - - push(@symbols, "$returnType $functionName($symbolSig)\n"); - - if ($deprecationVersion) { - push(@hBody, "#if !defined(WEBKIT_DISABLE_DEPRECATED)\n"); - } - - # Insert introspection annotations - push(@hBody, "/**\n"); - push(@hBody, " * ${functionName}:\n"); - push(@hBody, " * \@self: A #${className}\n"); - - foreach my $param (@{$function->parameters}) { - my $paramType = GetGlibTypeName($param->type); - # $paramType can have a trailing * in some cases - $paramType =~ s/\*$//; - my $paramName = $param->name; - my $paramAnnotations = ""; - if (ParamCanBeNull($functionName, $paramName)) { - $paramAnnotations = " (allow-none):"; - } - push(@hBody, " * \@${paramName}:${paramAnnotations} A #${paramType}\n"); - } - push(@hBody, " * \@error: #GError\n") if $raisesException; - push(@hBody, " *\n"); - my $returnTypeName = $returnType; - $returnTypeName =~ s/\*$//; - if ($returnValueIsGDOMType) { - my $transferType = GetTransferTypeForReturnType($functionSigType); - push(@hBody, " * Returns: (transfer $transferType): A #${returnTypeName}\n"); - } elsif ($returnType ne "void") { - push(@hBody, " * Returns: A #${returnTypeName}\n"); - } - if ($deprecationVersion) { - push(@hBody, " *\n"); - push(@hBody, " * Deprecated: $deprecationVersion"); - if ($deprecationReplacement) { - push(@hBody, ": Use $deprecationReplacement() instead."); - } - push(@hBody, "\n"); - } - push(@hBody, "**/\n"); - - if ($deprecationVersion && $deprecationReplacement) { - push(@hBody, "WEBKIT_DEPRECATED_FOR($deprecationReplacement) "); - } elsif ($deprecationVersion) { - push(@hBody, "WEBKIT_DEPRECATED "); - } else { - push(@hBody, "WEBKIT_API "); - } - push(@hBody, "$returnType\n$functionName($functionSig);\n"); - if ($deprecationVersion) { - push(@hBody, "#endif /* WEBKIT_DISABLE_DEPRECATED */\n"); - } - push(@hBody, "\n"); - - push(@cBody, "$returnType $functionName($functionSig)\n{\n"); - push(@cBody, "#if ${parentConditionalString}\n") if $parentConditionalString; - push(@cBody, "#if ${conditionalString}\n") if $conditionalString; - - push(@cBody, " WebCore::JSMainThreadNullState state;\n"); - - # g_return macros to check parameters of public methods. - $gReturnMacro = GetGReturnMacro("self", $interfaceName, $returnType); - push(@cBody, $gReturnMacro); - - foreach my $param (@{$function->parameters}) { - my $paramName = $param->name; - my $paramIDLType = $param->type; - my $paramTypeIsPointer = !$codeGenerator->IsNonPointerType($paramIDLType); - if ($paramTypeIsPointer) { - $gReturnMacro = GetGReturnMacro($paramName, $paramIDLType, $returnType, $functionName); - push(@cBody, $gReturnMacro); - } - } - - if ($raisesException) { - $gReturnMacro = GetGReturnMacro("error", "GError", $returnType); - push(@cBody, $gReturnMacro); - } - - # The WebKit::core implementations check for null already; no need to duplicate effort. - push(@cBody, " WebCore::${interfaceName}* item = WebKit::core(self);\n"); - - $returnParamName = ""; - foreach my $param (@{$function->parameters}) { - my $paramIDLType = $param->type; - my $paramName = $param->name; - - my $paramIsGDOMType = IsGDOMClassType($paramIDLType); - $convertedParamName = "converted" . $codeGenerator->WK_ucfirst($paramName); - if ($paramIDLType eq "DOMString") { - push(@cBody, " WTF::String ${convertedParamName} = WTF::String::fromUTF8($paramName);\n"); - } elsif ($paramIDLType eq "CompareHow") { - push(@cBody, " WebCore::Range::CompareHow ${convertedParamName} = static_cast<WebCore::Range::CompareHow>($paramName);\n"); - } elsif ($paramIsGDOMType) { - push(@cBody, " WebCore::${paramIDLType}* ${convertedParamName} = WebKit::core($paramName);\n"); - } - $returnParamName = $convertedParamName if $param->extendedAttributes->{"CustomReturn"}; - } - - my $assign = ""; - my $assignPre = ""; - my $assignPost = ""; - - # We need to special-case these Node methods because their C++ - # signature is different from what we'd expect given their IDL - # description; see Node.h. - my $functionHasCustomReturn = $functionName eq "webkit_dom_node_append_child" || - $functionName eq "webkit_dom_node_insert_before" || - $functionName eq "webkit_dom_node_replace_child" || - $functionName eq "webkit_dom_node_remove_child"; - - if ($returnType ne "void" && !$functionHasCustomReturn) { - if ($returnValueIsGDOMType) { - $assign = "RefPtr<WebCore::${functionSigType}> gobjectResult = "; - $assignPre = "WTF::getPtr("; - $assignPost = ")"; - } else { - $assign = "${returnType} result = "; - } - } - - # FIXME: Should we return a default value when isNull == true? - if ($function->signature->isNullable) { - push(@cBody, " bool isNull = false;\n"); - push(@callImplParams, "isNull"); - } - - if ($raisesException) { - push(@cBody, " WebCore::ExceptionCode ec = 0;\n"); - push(@callImplParams, "ec"); - } - - my $functionImplementationName = $function->signature->extendedAttributes->{"ImplementedAs"} || $function->signature->name; - - if ($functionHasCustomReturn) { - push(@cBody, " bool ok = item->${functionImplementationName}(" . join(", ", @callImplParams) . ");\n"); - my $customNodeAppendChild = << "EOF"; - if (ok) - return WebKit::kit($returnParamName); -EOF - push(@cBody, $customNodeAppendChild); - - if($raisesException) { - my $exceptionHandling = << "EOF"; - - WebCore::ExceptionCodeDescription ecdesc(ec); - g_set_error_literal(error, g_quark_from_string("WEBKIT_DOM"), ecdesc.code, ecdesc.name); -EOF - push(@cBody, $exceptionHandling); - } - push(@cBody, " return 0;\n"); - push(@cBody, "}\n\n"); - return; - } elsif ($functionSigType eq "DOMString") { - my $getterContentHead; - if ($prefix) { - my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $function); - push(@arguments, @callImplParams); - if ($function->signature->extendedAttributes->{"ImplementedBy"}) { - my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"}; - $implIncludes{"${implementedBy}.h"} = 1; - unshift(@arguments, "item"); - $functionName = "WebCore::${implementedBy}::${functionName}"; - } else { - $functionName = "item->${functionName}"; - } - $getterContentHead = "${assign}convertToUTF8String(${functionName}(" . join(", ", @arguments) . "));\n"; - } else { - my @arguments = @callImplParams; - if ($function->signature->extendedAttributes->{"ImplementedBy"}) { - my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"}; - $implIncludes{"${implementedBy}.h"} = 1; - unshift(@arguments, "item"); - $getterContentHead = "${assign}convertToUTF8String(WebCore::${implementedBy}::${functionImplementationName}(" . join(", ", @arguments) . "));\n"; - } else { - $getterContentHead = "${assign}convertToUTF8String(item->${functionImplementationName}(" . join(", ", @arguments) . "));\n"; - } - } - push(@cBody, " ${getterContentHead}"); - } else { - my $contentHead; - if ($prefix eq "get_") { - my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $function); - push(@arguments, @callImplParams); - if ($function->signature->extendedAttributes->{"ImplementedBy"}) { - my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"}; - $implIncludes{"${implementedBy}.h"} = 1; - unshift(@arguments, "item"); - $functionName = "WebCore::${implementedBy}::${functionName}"; - } else { - $functionName = "item->${functionName}"; - } - $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n"; - } elsif ($prefix eq "set_") { - my ($functionName, @arguments) = $codeGenerator->SetterExpression(\%implIncludes, $interfaceName, $function); - push(@arguments, @callImplParams); - if ($function->signature->extendedAttributes->{"ImplementedBy"}) { - my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"}; - $implIncludes{"${implementedBy}.h"} = 1; - unshift(@arguments, "item"); - $functionName = "WebCore::${implementedBy}::${functionName}"; - $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n"; - } else { - $functionName = "item->${functionName}"; - $contentHead = "${assign}${assignPre}${functionName}(" . join(", ", @arguments) . "${assignPost});\n"; - } - } else { - my @arguments = @callImplParams; - if ($function->signature->extendedAttributes->{"ImplementedBy"}) { - my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"}; - $implIncludes{"${implementedBy}.h"} = 1; - unshift(@arguments, "item"); - $contentHead = "${assign}${assignPre}WebCore::${implementedBy}::${functionImplementationName}(" . join(", ", @arguments) . "${assignPost});\n"; - } else { - $contentHead = "${assign}${assignPre}item->${functionImplementationName}(" . join(", ", @arguments) . "${assignPost});\n"; - } - } - push(@cBody, " ${contentHead}"); - - if($raisesException) { - my $exceptionHandling = << "EOF"; - if (ec) { - WebCore::ExceptionCodeDescription ecdesc(ec); - g_set_error_literal(error, g_quark_from_string("WEBKIT_DOM"), ecdesc.code, ecdesc.name); - } -EOF - push(@cBody, $exceptionHandling); - } - } - - if ($returnType ne "void" && !$functionHasCustomReturn) { - if ($functionSigType ne "any") { - if ($returnValueIsGDOMType) { - push(@cBody, " return WebKit::kit(gobjectResult.get());\n"); - } else { - push(@cBody, " return result;\n"); - } - } else { - push(@cBody, " return 0; // TODO: return canvas object\n"); - } - } - - if ($conditionalString) { - push(@cBody, "#else\n"); - push(@cBody, @conditionalWarn) if scalar(@conditionalWarn); - if ($returnType ne "void") { - if ($codeGenerator->IsNonPointerType($functionSigType)) { - push(@cBody, " return static_cast<${returnType}>(0);\n"); - } else { - push(@cBody, " return 0;\n"); - } - } - push(@cBody, "#endif /* ${conditionalString} */\n"); - } - - if ($parentConditionalString) { - push(@cBody, "#else\n"); - push(@cBody, @parentConditionalWarn) if scalar(@parentConditionalWarn); - if ($returnType ne "void") { - if ($codeGenerator->IsNonPointerType($functionSigType)) { - push(@cBody, " return static_cast<${returnType}>(0);\n"); - } else { - push(@cBody, " return 0;\n"); - } - } - push(@cBody, "#endif /* ${parentConditionalString} */\n"); - } - - push(@cBody, "}\n\n"); -} - -sub ClassHasFunction { - my ($class, $name) = @_; - - foreach my $function (@{$class->functions}) { - if ($function->signature->name eq $name) { - return 1; - } - } - - return 0; -} - -sub GenerateFunctions { - my ($object, $interfaceName, $interface) = @_; - - foreach my $function (@{$interface->functions}) { - $object->GenerateFunction($interfaceName, $function, "", $interface); - } - - TOP: - foreach my $attribute (@{$interface->attributes}) { - if (SkipAttribute($attribute)) { - next TOP; - } - - if ($attribute->signature->name eq "type") { - # This will conflict with the get_type() function we define to return a GType - # according to GObject conventions. Skip this for now. - next TOP; - } - - my $attrNameUpper = $codeGenerator->WK_ucfirst($attribute->signature->name); - my $getname = "get${attrNameUpper}"; - my $setname = "set${attrNameUpper}"; - if (ClassHasFunction($interface, $getname) || ClassHasFunction($interface, $setname)) { - # Very occasionally an IDL file defines getter/setter functions for one of its - # attributes; in this case we don't need to autogenerate the getter/setter. - next TOP; - } - - # Generate an attribute getter. For an attribute "foo", this is a function named - # "get_foo" which calls a DOM class method named foo(). - my $function = new domFunction(); - $function->signature($attribute->signature); - $function->signature->extendedAttributes({%{$attribute->signature->extendedAttributes}}); - if ($attribute->signature->extendedAttributes->{"GetterRaisesException"}) { - $function->signature->extendedAttributes->{"RaisesException"} = "VALUE_IS_MISSING"; - } - $object->GenerateFunction($interfaceName, $function, "get_", $interface); - - # FIXME: We are not generating setters for 'Replaceable' - # attributes now, but we should somehow. - my $custom = $attribute->signature->extendedAttributes->{"CustomSetter"}; - if ($attribute->isReadOnly || $attribute->signature->extendedAttributes->{"Replaceable"} || $custom) { - next TOP; - } - - # Generate an attribute setter. For an attribute, "foo", this is a function named - # "set_foo" which calls a DOM class method named setFoo(). - $function = new domFunction(); - - $function->signature(new domSignature()); - $function->signature->name($attribute->signature->name); - $function->signature->type($attribute->signature->type); - $function->signature->extendedAttributes({%{$attribute->signature->extendedAttributes}}); - - my $param = new domSignature(); - $param->name("value"); - $param->type($attribute->signature->type); - my %attributes = (); - $param->extendedAttributes(\%attributes); - my $arrayRef = $function->parameters; - push(@$arrayRef, $param); - - if ($attribute->signature->extendedAttributes->{"SetterRaisesException"}) { - $function->signature->extendedAttributes->{"RaisesException"} = "VALUE_IS_MISSING"; - } else { - delete $function->signature->extendedAttributes->{"RaisesException"}; - } - - $object->GenerateFunction($interfaceName, $function, "set_", $interface); - } -} - -sub GenerateCFile { - my ($object, $interfaceName, $parentClassName, $parentGObjType, $interface) = @_; - - if ($interface->extendedAttributes->{"EventTarget"}) { - $object->GenerateEventTargetIface($interface); - } - - my $implContent = ""; - - my $decamelize = decamelize($interfaceName); - my $clsCaps = uc($decamelize); - my $lowerCaseIfaceName = "webkit_dom_$decamelize"; - my $parentImplClassName = GetParentImplClassName($interface); - my $baseClassName = GetBaseClass($parentImplClassName, $interface); - - # Add a private struct only for direct subclasses of Object so that we can use RefPtr - # for the WebCore wrapped object and make sure we only increment the reference counter once. - if ($parentImplClassName eq "Object") { - my $conditionalString = $codeGenerator->GenerateConditionalString($interface); - push(@cStructPriv, "#define WEBKIT_DOM_${clsCaps}_GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE(obj, WEBKIT_TYPE_DOM_${clsCaps}, ${className}Private)\n\n"); - push(@cStructPriv, "typedef struct _${className}Private {\n"); - push(@cStructPriv, "#if ${conditionalString}\n") if $conditionalString; - push(@cStructPriv, " RefPtr<WebCore::${interfaceName}> coreObject;\n"); - push(@cStructPriv, "#endif // ${conditionalString}\n") if $conditionalString; - push(@cStructPriv, "} ${className}Private;\n\n"); - } - - $implContent = << "EOF"; -${defineTypeMacro}(${className}, ${lowerCaseIfaceName}, ${parentGObjType}${defineTypeInterfaceImplementation} - -EOF - push(@cBodyProperties, $implContent); - - if ($parentImplClassName eq "Object") { - push(@cBodyPriv, "${className}* kit(WebCore::$interfaceName* obj)\n"); - push(@cBodyPriv, "{\n"); - push(@cBodyPriv, " if (!obj)\n"); - push(@cBodyPriv, " return 0;\n\n"); - push(@cBodyPriv, " if (gpointer ret = DOMObjectCache::get(obj))\n"); - push(@cBodyPriv, " return WEBKIT_DOM_${clsCaps}(ret);\n\n"); - if (IsPolymorphic($interfaceName)) { - push(@cBodyPriv, " return wrap(obj);\n"); - } else { - push(@cBodyPriv, " return wrap${interfaceName}(obj);\n"); - } - push(@cBodyPriv, "}\n\n"); - } else { - push(@cBodyPriv, "${className}* kit(WebCore::$interfaceName* obj)\n"); - push(@cBodyPriv, "{\n"); - if (!IsPolymorphic($baseClassName)) { - push(@cBodyPriv, " if (!obj)\n"); - push(@cBodyPriv, " return 0;\n\n"); - push(@cBodyPriv, " if (gpointer ret = DOMObjectCache::get(obj))\n"); - push(@cBodyPriv, " return WEBKIT_DOM_${clsCaps}(ret);\n\n"); - push(@cBodyPriv, " return wrap${interfaceName}(obj);\n"); - } else { - push(@cBodyPriv, " return WEBKIT_DOM_${clsCaps}(kit(static_cast<WebCore::$baseClassName*>(obj)));\n"); - } - push(@cBodyPriv, "}\n\n"); - } - - $implContent = << "EOF"; -WebCore::${interfaceName}* core(${className}* request) -{ - return request ? static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(request)->coreObject) : 0; -} - -${className}* wrap${interfaceName}(WebCore::${interfaceName}* coreObject) -{ - ASSERT(coreObject); - return WEBKIT_DOM_${clsCaps}(g_object_new(WEBKIT_TYPE_DOM_${clsCaps}, "core-object", coreObject, NULL)); -} - -EOF - push(@cBodyPriv, $implContent); - - $object->GenerateProperties($interfaceName, $interface); - $object->GenerateFunctions($interfaceName, $interface); -} - -sub GenerateEndHeader { - my ($object) = @_; - - #Header guard - my $guard = $className . "_h"; - - push(@hBody, "G_END_DECLS\n\n"); - push(@hPrefixGuardEnd, "#endif /* $guard */\n"); -} - -sub IsPolymorphic { - my $type = shift; - - return scalar(grep {$_ eq $type} qw(Blob Event HTMLCollection Node StyleSheet)); -} - -sub GenerateEventTargetIface { - my $object = shift; - my $interface = shift; - - my $interfaceName = $interface->name; - my $decamelize = decamelize($interfaceName); - my $conditionalString = $codeGenerator->GenerateConditionalString($interface); - my @conditionalWarn = GenerateConditionalWarning($interface); - - $implIncludes{"GObjectEventListener.h"} = 1; - $implIncludes{"WebKitDOMEventTarget.h"} = 1; - $implIncludes{"WebKitDOMEventPrivate.h"} = 1; - - push(@cBodyProperties, "static gboolean webkit_dom_${decamelize}_dispatch_event(WebKitDOMEventTarget* target, WebKitDOMEvent* event, GError** error)\n{\n"); - push(@cBodyProperties, "#if ${conditionalString}\n") if $conditionalString; - push(@cBodyProperties, " WebCore::Event* coreEvent = WebKit::core(event);\n"); - push(@cBodyProperties, " WebCore::${interfaceName}* coreTarget = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(target)->coreObject);\n\n"); - push(@cBodyProperties, " WebCore::ExceptionCode ec = 0;\n"); - push(@cBodyProperties, " gboolean result = coreTarget->dispatchEvent(coreEvent, ec);\n"); - push(@cBodyProperties, " if (ec) {\n WebCore::ExceptionCodeDescription description(ec);\n"); - push(@cBodyProperties, " g_set_error_literal(error, g_quark_from_string(\"WEBKIT_DOM\"), description.code, description.name);\n }\n"); - push(@cBodyProperties, " return result;\n"); - push(@cBodyProperties, "#else\n") if $conditionalString; - push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn); - push(@cBodyProperties, " return false;\n#endif // ${conditionalString}\n") if $conditionalString; - push(@cBodyProperties, "}\n\n"); - - push(@cBodyProperties, "static gboolean webkit_dom_${decamelize}_add_event_listener(WebKitDOMEventTarget* target, const char* eventName, GClosure* handler, gboolean useCapture)\n{\n"); - push(@cBodyProperties, "#if ${conditionalString}\n") if $conditionalString; - push(@cBodyProperties, " WebCore::${interfaceName}* coreTarget = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(target)->coreObject);\n"); - push(@cBodyProperties, " return WebCore::GObjectEventListener::addEventListener(G_OBJECT(target), coreTarget, eventName, handler, useCapture);\n"); - push(@cBodyProperties, "#else\n") if $conditionalString; - push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn); - push(@cBodyProperties, " return false;\n#endif // ${conditionalString}\n") if $conditionalString; - push(@cBodyProperties, "}\n\n"); - - push(@cBodyProperties, "static gboolean webkit_dom_${decamelize}_remove_event_listener(WebKitDOMEventTarget* target, const char* eventName, GClosure* handler, gboolean useCapture)\n{\n"); - push(@cBodyProperties, "#if ${conditionalString}\n") if $conditionalString; - push(@cBodyProperties, " WebCore::${interfaceName}* coreTarget = static_cast<WebCore::${interfaceName}*>(WEBKIT_DOM_OBJECT(target)->coreObject);\n"); - push(@cBodyProperties, " return WebCore::GObjectEventListener::removeEventListener(G_OBJECT(target), coreTarget, eventName, handler, useCapture);\n"); - push(@cBodyProperties, "#else\n") if $conditionalString; - push(@cBodyProperties, @conditionalWarn) if scalar(@conditionalWarn); - push(@cBodyProperties, " return false;\n#endif // ${conditionalString}\n") if $conditionalString; - push(@cBodyProperties, "}\n\n"); - - push(@cBodyProperties, "static void webkit_dom_event_target_init(WebKitDOMEventTargetIface* iface)\n{\n"); - push(@cBodyProperties, " iface->dispatch_event = webkit_dom_${decamelize}_dispatch_event;\n"); - push(@cBodyProperties, " iface->add_event_listener = webkit_dom_${decamelize}_add_event_listener;\n"); - push(@cBodyProperties, " iface->remove_event_listener = webkit_dom_${decamelize}_remove_event_listener;\n}\n\n"); - - $defineTypeMacro = "G_DEFINE_TYPE_WITH_CODE"; - $defineTypeInterfaceImplementation = ", G_IMPLEMENT_INTERFACE(WEBKIT_TYPE_DOM_EVENT_TARGET, webkit_dom_event_target_init))"; -} - -sub Generate { - my ($object, $interface) = @_; - - my $parentClassName = GetParentClassName($interface); - my $parentGObjType = GetParentGObjType($interface); - my $interfaceName = $interface->name; - my $parentImplClassName = GetParentImplClassName($interface); - my $baseClassName = GetBaseClass($parentImplClassName, $interface); - - # Add the default impl header template - @cPrefix = split("\r", $licenceTemplate); - push(@cPrefix, "\n"); - - $implIncludes{"DOMObjectCache.h"} = 1; - $implIncludes{"WebKitDOMPrivate.h"} = 1; - $implIncludes{"gobject/ConvertToUTF8String.h"} = 1; - $implIncludes{"${className}Private.h"} = 1; - $implIncludes{"Document.h"} = 1; - $implIncludes{"JSMainThreadExecState.h"} = 1; - $implIncludes{"ExceptionCode.h"} = 1; - $implIncludes{"CSSImportRule.h"} = 1; - if ($parentImplClassName ne "Object" and IsPolymorphic($baseClassName)) { - $implIncludes{"WebKitDOM${baseClassName}Private.h"} = 1; - } - - $hdrIncludes{"webkitdom/${parentClassName}.h"} = 1; - - $object->GenerateHeader($interfaceName, $parentClassName); - $object->GenerateCFile($interfaceName, $parentClassName, $parentGObjType, $interface); - $object->GenerateEndHeader(); -} - -sub WriteData { - my $object = shift; - my $interface = shift; - my $outputDir = shift; - mkdir $outputDir; - - # Write a private header. - my $interfaceName = $interface->name; - my $filename = "$outputDir/" . $className . "Private.h"; - my $guard = "${className}Private_h"; - - # Add the guard if the 'Conditional' extended attribute exists - my $conditionalString = $codeGenerator->GenerateConditionalString($interface); - - open(PRIVHEADER, ">$filename") or die "Couldn't open file $filename for writing"; - - print PRIVHEADER split("\r", $licenceTemplate); - print PRIVHEADER "\n"; - - my $text = << "EOF"; -#ifndef $guard -#define $guard - -#include "${interfaceName}.h" -#include <webkitdom/${className}.h> -EOF - - print PRIVHEADER $text; - print PRIVHEADER "#if ${conditionalString}\n" if $conditionalString; - print PRIVHEADER map { "#include \"$_\"\n" } sort keys(%hdrPropIncludes); - print PRIVHEADER "\n"; - $text = << "EOF"; -namespace WebKit { -${className}* wrap${interfaceName}(WebCore::${interfaceName}*); -${className}* kit(WebCore::${interfaceName}*); -WebCore::${interfaceName}* core(${className}*); -EOF - - print PRIVHEADER $text; - - $text = << "EOF"; -} // namespace WebKit - -EOF - - print PRIVHEADER $text; - print PRIVHEADER "#endif /* ${conditionalString} */\n\n" if $conditionalString; - print PRIVHEADER "#endif /* ${guard} */\n"; - - close(PRIVHEADER); - - my $basename = FileNamePrefix . $interfaceName; - $basename =~ s/_//g; - - # Write public header. - my $fullHeaderFilename = "$outputDir/" . $basename . ".h"; - my $installedHeaderFilename = "${basename}.h"; - open(HEADER, ">$fullHeaderFilename") or die "Couldn't open file $fullHeaderFilename"; - - print HEADER @hPrefix; - print HEADER @hPrefixGuard; - print HEADER "#include <glib-object.h>\n"; - print HEADER map { "#include <$_>\n" } sort keys(%hdrIncludes); - print HEADER "#include <webkitdom/webkitdomdefines.h>\n\n"; - print HEADER @hBodyPre; - print HEADER @hBody; - print HEADER @hPrefixGuardEnd; - - close(HEADER); - - # Write the implementation sources - my $implFileName = "$outputDir/" . $basename . ".cpp"; - open(IMPL, ">$implFileName") or die "Couldn't open file $implFileName"; - - print IMPL @cPrefix; - print IMPL "#include \"config.h\"\n"; - print IMPL "#include \"$installedHeaderFilename\"\n\n"; - - # Remove the implementation header from the list of included files. - %includesCopy = %implIncludes; - print IMPL map { "#include \"$_\"\n" } sort keys(%includesCopy); - - print IMPL "#include <wtf/GetPtr.h>\n"; - print IMPL "#include <wtf/RefPtr.h>\n\n"; - print IMPL @cStructPriv; - print IMPL "#if ${conditionalString}\n\n" if $conditionalString; - - print IMPL "namespace WebKit {\n\n"; - print IMPL @cBodyPriv; - print IMPL "} // namespace WebKit\n\n"; - print IMPL "#endif // ${conditionalString}\n\n" if $conditionalString; - - print IMPL @cBodyProperties; - print IMPL @cBody; - - close(IMPL); - - # Write a symbols file. - my $symbolsFileName = "$outputDir/" . $basename . ".symbols"; - open(SYM, ">$symbolsFileName") or die "Couldn't open file $symbolsFileName"; - print SYM @symbols; - close(SYM); - - %implIncludes = (); - %hdrIncludes = (); - @hPrefix = (); - @hBody = (); - - @cPrefix = (); - @cBody = (); - @cBodyPriv = (); - @cBodyProperties = (); - @cStructPriv = (); - - @symbols = (); -} - -sub GenerateInterface { - my ($object, $interface, $defines) = @_; - - # Set up some global variables - $className = GetClassName($interface->name); - - $object->Generate($interface); -} - -1; diff --git a/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm b/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm index d3685f8e3..b71e2ce9a 100644 --- a/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm +++ b/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm @@ -3,7 +3,7 @@ # Copyright (C) 2006 Anders Carlsson <andersca@mac.com> # Copyright (C) 2006, 2007 Samuel Weinig <sam@webkit.org> # Copyright (C) 2006 Alexey Proskuryakov <ap@webkit.org> -# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. All rights reserved. +# Copyright (C) 2006-2017 Apple Inc. All rights reserved. # Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au> # Copyright (C) Research In Motion Limited 2010. All rights reserved. # Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) @@ -11,6 +11,7 @@ # Copyright (C) 2012 Ericsson AB. All rights reserved. # Copyright (C) 2007, 2008, 2009, 2012 Google Inc. # Copyright (C) 2013 Samsung Electronics. All rights reserved. +# Copyright (C) 2015, 2016 Canon 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 @@ -27,15 +28,17 @@ # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. + package CodeGeneratorJS; use strict; use constant FileNamePrefix => "JS"; +use Carp qw<longmess>; +use Data::Dumper; use Hasher; my $codeGenerator; - -my $writeDependencies = 0; +my $writeDependencies; my @headerContentHeader = (); my @headerContent = (); @@ -47,7 +50,6 @@ my @implContent = (); my %implIncludes = (); my @depsContent = (); my $numCachedAttributes = 0; -my $currentCachedAttribute = 0; my $beginAppleCopyrightForHeaderFiles = <<END; // ------- Begin Apple Copyright ------- @@ -106,6 +108,16 @@ my $headerTemplate = << "EOF"; */ EOF +sub assert +{ + my $message = shift; + + my $mess = longmess(); + print Dumper($mess); + + die $message; +} + # Default constructor sub new { @@ -113,96 +125,99 @@ sub new my $reference = { }; $codeGenerator = shift; - shift; # $useLayerOnTop - shift; # $preprocessor $writeDependencies = shift; bless($reference, $object); return $reference; } +sub GenerateEnumeration +{ + my ($object, $enumeration) = @_; + + my $className = GetEnumerationClassName($enumeration->type); + $object->GenerateEnumerationHeader($enumeration, $className); + $object->GenerateEnumerationImplementation($enumeration, $className); +} + +sub GenerateDictionary +{ + my ($object, $dictionary, $enumerations, $otherDictionaries) = @_; + + my $className = GetDictionaryClassName($dictionary->type); + $object->GenerateDictionaryHeader($dictionary, $className, $enumerations, $otherDictionaries); + $object->GenerateDictionaryImplementation($dictionary, $className, $enumerations, $otherDictionaries); +} + +sub GenerateCallbackFunction +{ + my ($object, $callbackFunction, $enumerations, $dictionaries) = @_; + + $object->GenerateCallbackFunctionHeader($callbackFunction, $enumerations, $dictionaries); + $object->GenerateCallbackFunctionImplementation($callbackFunction, $enumerations, $dictionaries); +} + sub GenerateInterface { - my $object = shift; - my $interface = shift; - my $defines = shift; + my ($object, $interface, $defines, $enumerations, $dictionaries) = @_; $codeGenerator->LinkOverloadedFunctions($interface); + AddStringifierOperationIfNeeded($interface); - # Start actual generation if ($interface->isCallback) { - $object->GenerateCallbackHeader($interface); - $object->GenerateCallbackImplementation($interface); + $object->GenerateCallbackInterfaceHeader($interface, $enumerations, $dictionaries); + $object->GenerateCallbackInterfaceImplementation($interface, $enumerations, $dictionaries); } else { - $object->GenerateHeader($interface); - $object->GenerateImplementation($interface); + $object->GenerateHeader($interface, $enumerations, $dictionaries); + $object->GenerateImplementation($interface, $enumerations, $dictionaries); } } -sub GenerateAttributeEventListenerCall +sub AddStringifierOperationIfNeeded { - my $className = shift; - my $implSetterFunctionName = shift; - my $windowEventListener = shift; + my $interface = shift; - my $wrapperObject = $windowEventListener ? "globalObject" : "castedThis"; - my @GenerateEventListenerImpl = (); + foreach my $attribute (@{$interface->attributes}) { + next unless $attribute->isStringifier; - if ($className eq "JSSVGElementInstance") { - # SVGElementInstances have to create JSEventListeners with the wrapper equal to the correspondingElement - $wrapperObject = "asObject(correspondingElementWrapper)"; + my $stringifier = IDLOperation->new(); + $stringifier->name("toString"); - push(@GenerateEventListenerImpl, <<END); - JSValue correspondingElementWrapper = toJS(exec, castedThis->globalObject(), impl.correspondingElement()); - if (correspondingElementWrapper.isObject()) -END + my $extendedAttributeList = {}; + $extendedAttributeList->{ImplementedAs} = $attribute->name; + $stringifier->extendedAttributes($extendedAttributeList); + die "stringifier can only be used on attributes of String types" unless $codeGenerator->IsStringType($attribute->type); + + # FIXME: This should use IDLParser's cloneType. + my $type = IDLType->new(); + $type->name($attribute->type->name); - # Add leading whitespace to format the impl.set... line correctly - push(@GenerateEventListenerImpl, " "); - } + $stringifier->type($type); - push(@GenerateEventListenerImpl, " impl.set$implSetterFunctionName(createJSAttributeEventListener(exec, value, $wrapperObject));\n"); - return @GenerateEventListenerImpl; + push(@{$interface->functions}, $stringifier); + last; + } } -sub GenerateEventListenerCall +sub EventHandlerAttributeEventName { - my $className = shift; - my $functionName = shift; - my $passRefPtrHandling = ($functionName eq "add") ? "" : ".get()"; - - $implIncludes{"JSEventListener.h"} = 1; - - my @GenerateEventListenerImpl = (); - my $wrapperObject = "castedThis"; - if ($className eq "JSSVGElementInstance") { - # SVGElementInstances have to create JSEventListeners with the wrapper equal to the correspondingElement - $wrapperObject = "asObject(correspondingElementWrapper)"; + my $attribute = shift; + my $eventType = $attribute->extendedAttributes->{ImplementedAs} || $attribute->name; - push(@GenerateEventListenerImpl, <<END); - JSValue correspondingElementWrapper = toJS(exec, castedThis->globalObject(), impl.correspondingElement()); - if (!correspondingElementWrapper.isObject()) - return JSValue::encode(jsUndefined()); -END - } + # Remove the "on" prefix. + $eventType = substr($eventType, 2); - push(@GenerateEventListenerImpl, <<END); - JSValue listener = exec->argument(1); - if (!listener.isObject()) - return JSValue::encode(jsUndefined()); - impl.${functionName}EventListener(exec->argument(0).toString(exec)->value(exec), JSEventListener::create(asObject(listener), $wrapperObject, false, currentWorld(exec))$passRefPtrHandling, exec->argument(2).toBoolean(exec)); - return JSValue::encode(jsUndefined()); -END - return @GenerateEventListenerImpl; + return "eventNames().${eventType}Event"; } sub GetParentClassName { my $interface = shift; - return $interface->extendedAttributes->{"JSLegacyParent"} if $interface->extendedAttributes->{"JSLegacyParent"}; - return "JSDOMWrapper" unless $interface->parent; - return "JS" . $interface->parent; + return $interface->extendedAttributes->{JSLegacyParent} if $interface->extendedAttributes->{JSLegacyParent}; + return "JSDOMObject" unless NeedsImplementationClass($interface); + return "JSDOMWrapper<" . GetImplClassName($interface) . ">" unless $interface->parentType; + return "JS" . $interface->parentType->name; } sub GetCallbackClassName @@ -212,286 +227,499 @@ sub GetCallbackClassName return "JS$className"; } -sub AddIncludesForTypeInImpl +sub GetJSCallbackDataType { - my $type = shift; - my $isCallback = @_ ? shift : 0; - - AddIncludesForType($type, $isCallback, \%implIncludes); + my $callback = shift; + + return $callback->extendedAttributes->{IsWeakCallback} ? "JSCallbackDataWeak" : "JSCallbackDataStrong"; } -sub AddIncludesForTypeInHeader +sub GetExportMacroForJSClass { - my $type = shift; - my $isCallback = @_ ? shift : 0; + my $interface = shift; + + return $interface->extendedAttributes->{ExportMacro} . " " if $interface->extendedAttributes->{ExportMacro}; + return ""; +} + +sub AddIncludesForImplementationTypeInImpl +{ + my $implementationType = shift; - AddIncludesForType($type, $isCallback, \%headerIncludes); + AddIncludesForImplementationType($implementationType, \%implIncludes); } -my %typesWithoutHeader = ( - "Array" => 1, - "DOMString" => 1, - "DOMTimeStamp" => 1, - "any" => 1 -); +sub AddIncludesForImplementationTypeInHeader +{ + my $implementationType = shift; + + AddIncludesForImplementationType($implementationType, \%headerIncludes); +} -sub SkipIncludeHeader +sub AddIncludesForImplementationType { - my $type = shift; + my ($implementationType, $includesRef) = @_; - return 1 if $codeGenerator->SkipIncludeHeader($type); - return $typesWithoutHeader{$type}; + $includesRef->{"${implementationType}.h"} = 1; } -sub AddIncludesForType +sub AddToImplIncludesForIDLType { - my $type = shift; - my $isCallback = shift; - my $includesRef = shift; + my ($type, $conditional) = @_; + + return AddToIncludesForIDLType($type, \%implIncludes, $conditional) +} - return if SkipIncludeHeader($type); +sub AddToIncludesForIDLType +{ + my ($type, $includesRef, $conditional) = @_; - # When we're finished with the one-file-per-class - # reorganization, we won't need these special cases. - if ($type eq "XPathNSResolver") { - $includesRef->{"JSXPathNSResolver.h"} = 1; - $includesRef->{"JSCustomXPathNSResolver.h"} = 1; - } elsif ($isCallback && $codeGenerator->IsWrapperType($type)) { - $includesRef->{"JS${type}.h"} = 1; - } elsif ($codeGenerator->GetSequenceType($type) or $codeGenerator->GetArrayType($type)) { - my $arrayType = $codeGenerator->GetArrayType($type); - my $sequenceType = $codeGenerator->GetSequenceType($type); - my $arrayOrSequenceType = $arrayType || $sequenceType; - - if ($arrayType eq "DOMString") { - $includesRef->{"JSDOMStringList.h"} = 1; - $includesRef->{"DOMStringList.h"} = 1; - } elsif ($codeGenerator->IsRefPtrType($arrayOrSequenceType)) { - $includesRef->{"JS${arrayOrSequenceType}.h"} = 1; - $includesRef->{"${arrayOrSequenceType}.h"} = 1; + return if $codeGenerator->IsPrimitiveType($type); + return if $codeGenerator->IsStringType($type); + return if $codeGenerator->IsTypedArrayType($type); + return if $type->name eq "any"; + return if $type->name eq "object"; + + if ($type->isUnion) { + AddToIncludes("<wtf/Variant.h>", $includesRef, $conditional); + + foreach my $memberType (@{$type->subtypes}) { + AddToIncludesForIDLType($memberType, $includesRef, $conditional); } - $includesRef->{"<runtime/JSArray.h>"} = 1; - } else { - # default, include the same named file - $includesRef->{"${type}.h"} = 1; + + return; + } + + if ($codeGenerator->IsSequenceOrFrozenArrayType($type)) { + AddToIncludes("<runtime/JSArray.h>", $includesRef, $conditional); + AddToIncludesForIDLType(@{$type->subtypes}[0], $includesRef, $conditional); + return; + } + + if ($codeGenerator->IsRecordType($type)) { + AddToIncludes("<wtf/Vector.h>", $includesRef, $conditional); + AddToIncludesForIDLType(@{$type->subtypes}[0], $includesRef, $conditional); + AddToIncludesForIDLType(@{$type->subtypes}[1], $includesRef, $conditional); + return; + } + + if ($codeGenerator->IsWrapperType($type) || $codeGenerator->IsExternalDictionaryType($type) || $codeGenerator->IsExternalEnumType($type) || $type->name eq "EventListener") { + AddToIncludes("JS" . $type->name . ".h", $includesRef, $conditional); + return; + } + + if ($type->name eq "SerializedScriptValue") { + AddToIncludes($type->name . ".h", $includesRef, $conditional); + return; } } sub AddToImplIncludes { - my $header = shift; - my $conditional = shift; + my ($header, $conditional) = @_; - if (not $conditional) { - $implIncludes{$header} = 1; - } elsif (not exists($implIncludes{$header})) { - $implIncludes{$header} = $conditional; - } else { - my $oldValue = $implIncludes{$header}; - if ($oldValue ne 1) { - my %newValue = (); - $newValue{$conditional} = 1; - foreach my $condition (split(/\|/, $oldValue)) { - $newValue{$condition} = 1; - } - $implIncludes{$header} = join("|", sort keys %newValue); - } - } + AddToIncludes($header, \%implIncludes, $conditional); } -sub IsScriptProfileType +sub AddToIncludes { - my $type = shift; - return 1 if ($type eq "ScriptProfileNode"); - return 0; + my ($header, $includesRef, $conditional) = @_; + + if (not $conditional) { + $includesRef->{$header} = 1; + } elsif (not exists($includesRef->{$header})) { + $includesRef->{$header} = $conditional; + } else { + my $oldValue = $includesRef->{$header}; + $includesRef->{$header} = "$oldValue|$conditional" if $oldValue ne 1; + } } sub IsReadonly { my $attribute = shift; - return $attribute->isReadOnly && !$attribute->signature->extendedAttributes->{"Replaceable"}; + return $attribute->isReadOnly && !$attribute->extendedAttributes->{Replaceable} && !$attribute->extendedAttributes->{PutForwards}; } -sub AddTypedefForScriptProfileType +sub AddClassForwardIfNeeded { my $type = shift; - (my $jscType = $type) =~ s/Script//; - push(@headerContent, "typedef JSC::$jscType $type;\n\n"); -} + # SVGAnimatedLength/Number/etc. are not classes so they can't be forward declared as classes. + return if $codeGenerator->IsSVGAnimatedType($type); + return if $codeGenerator->IsTypedArrayType($type); + return if $type->name eq "BufferSource"; -sub AddClassForwardIfNeeded -{ - my $interfaceName = shift; - - # SVGAnimatedLength/Number/etc. are typedefs to SVGAnimatedTemplate, so don't use class forwards for them! - unless ($codeGenerator->IsSVGAnimatedType($interfaceName) or IsScriptProfileType($interfaceName) or $codeGenerator->IsTypedArrayType($interfaceName)) { - push(@headerContent, "class $interfaceName;\n\n"); - # ScriptProfile and ScriptProfileNode are typedefs to JSC::Profile and JSC::ProfileNode. - } elsif (IsScriptProfileType($interfaceName)) { - $headerIncludes{"<profiler/ProfileNode.h>"} = 1; - AddTypedefForScriptProfileType($interfaceName); - } + push(@headerContent, "class " . $type->name . ";\n\n"); } -sub hashTableAccessor +sub GetGenerateIsReachable { - my $noStaticTables = shift; - my $className = shift; - if ($noStaticTables) { - return "get${className}Table(exec->vm())"; - } else { - return "${className}Table"; - } + my $interface = shift; + return $interface->extendedAttributes->{GenerateIsReachable}; } -sub prototypeHashTableAccessor +sub GetCustomIsReachable { - my $noStaticTables = shift; - my $className = shift; - if ($noStaticTables) { - return "get${className}PrototypeTable(exec->vm())"; - } else { - return "${className}PrototypeTable"; - } + my $interface = shift; + return $interface->extendedAttributes->{CustomIsReachable}; } -sub constructorHashTableAccessor +sub IsDOMGlobalObject { - my $noStaticTables = shift; - my $constructorClassName = shift; - if ($noStaticTables) { - return "get${constructorClassName}Table(exec->vm())"; - } else { - return "${constructorClassName}Table"; - } + my $interface = shift; + return $interface->type->name eq "DOMWindow" || $codeGenerator->InheritsInterface($interface, "WorkerGlobalScope") || $interface->type->name eq "TestGlobalObject"; } -sub GetGenerateIsReachable +sub ShouldUseGlobalObjectPrototype { my $interface = shift; - return $interface->extendedAttributes->{"GenerateIsReachable"}; + + # For workers, the global object is a DedicatedWorkerGlobalScope. + return 0 if $interface->type->name eq "WorkerGlobalScope"; + + return IsDOMGlobalObject($interface); } -sub GetCustomIsReachable +sub GenerateIndexedGetter { - my $interface = shift; - return $interface->extendedAttributes->{"CustomIsReachable"}; + my ($interface, $indexedGetterFunction) = @_; + + my @output = (); + + my @attributes = (); + push(@attributes, "ReadOnly") if !$interface->extendedAttributes->{CustomNamedSetter}; + + my $attributeString = ((@attributes > 0) ? join(" | ", @attributes) : "0"); + + my $indexedGetterFunctionName = $indexedGetterFunction->name || "item"; + my $nativeToJSConversion = NativeToJSValueUsingPointers($indexedGetterFunction, $interface, "thisObject->wrapped().${indexedGetterFunctionName}(index)", "thisObject"); + + push(@output, " slot.setValue(thisObject, ${attributeString}, ${nativeToJSConversion});\n"); + push(@output, " return true;\n"); + + return @output; } -sub IsDOMGlobalObject +sub GenerateNamedGetter { - my $interface = shift; - return $interface->name eq "DOMWindow" || $codeGenerator->InheritsInterface($interface, "WorkerGlobalScope"); + my ($interface, $namedGetterFunction) = @_; + + my @output = (); + + my @attributes = (); + push(@attributes, "ReadOnly") if !$interface->extendedAttributes->{CustomNamedSetter}; + push(@attributes, "DontEnum") if $interface->extendedAttributes->{LegacyUnenumerableNamedProperties}; + + my $attributeString = ((@attributes > 0) ? join(" | ", @attributes) : "0"); + + if ($interface->extendedAttributes->{CustomNamedGetter}) { + push(@output, " JSValue value;\n"); + push(@output, " if (thisObject->nameGetter(state, propertyName, value)) {\n"); + push(@output, " slot.setValue(thisObject, ${attributeString}, value);\n"); + } else { + my $namedGetterFunctionName = $namedGetterFunction->name || "namedItem"; + my $itemVariable = "item"; + push(@output, " auto item = thisObject->wrapped().${namedGetterFunctionName}(propertyNameToAtomicString(propertyName));\n"); + + if ($namedGetterFunction->extendedAttributes->{MayThrowException}) { + push(@output, " if (item.hasException()) {\n"); + push(@output, " auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n"); + push(@output, " propagateException(*state, throwScope, item.releaseException());\n"); + push(@output, " return true;\n"); + push(@output, " }\n\n"); + push(@output, " auto itemValue = item.releaseReturnValue();\n"); + + $itemVariable = "itemValue"; + } + + my $IDLType = GetIDLType($interface, $namedGetterFunction->type); + push(@output, " if (!${IDLType}::isNullValue(${itemVariable})) {\n"); + + my $nativeToJSConversion = NativeToJSValueUsingPointers($namedGetterFunction, $interface, $itemVariable, "thisObject", 1); + push(@output, " slot.setValue(thisObject, ${attributeString}, ${nativeToJSConversion});\n"); + } + + push(@output, " return true;\n"); + push(@output, " }\n"); + + return @output; } sub GenerateGetOwnPropertySlotBody { - my ($interface, $interfaceName, $className, $hasAttributes, $inlined) = @_; + my ($interface, $className, $indexedGetterFunction, $namedGetterFunction) = @_; - my $namespaceMaybe = ($inlined ? "JSC::" : ""); - my $namedGetterFunction = GetNamedGetterFunction($interface); - my $indexedGetterFunction = GetIndexedGetterFunction($interface); - my $hasNumericIndexedGetter = $indexedGetterFunction ? $codeGenerator->IsNumericType($indexedGetterFunction->signature->type) : 0; + my @output = (); - my @getOwnPropertySlotImpl = (); + my $ownPropertyCheck = sub { + push(@output, " if (Base::getOwnPropertySlot(thisObject, state, propertyName, slot))\n"); + push(@output, " return true;\n"); + }; - if ($interfaceName eq "NamedNodeMap" or $interfaceName =~ /^HTML\w*Collection$/) { - push(@getOwnPropertySlotImpl, " ${namespaceMaybe}JSValue proto = thisObject->prototype();\n"); - push(@getOwnPropertySlotImpl, " if (proto.isObject() && jsCast<${namespaceMaybe}JSObject*>(asObject(proto))->hasProperty(exec, propertyName))\n"); - push(@getOwnPropertySlotImpl, " return false;\n\n"); + # FIXME: As per the Web IDL specification, the prototype check is supposed to skip "named properties objects": + # https://heycam.github.io/webidl/#dfn-named-property-visibility + # https://heycam.github.io/webidl/#dfn-named-properties-object + my $prototypeCheck = sub { + push(@output, " JSValue proto = thisObject->getPrototypeDirect();\n"); + push(@output, " if (proto.isObject() && jsCast<JSObject*>(proto)->hasProperty(state, propertyName))\n"); + push(@output, " return false;\n\n"); + }; + + push(@output, "bool ${className}::getOwnPropertySlot(JSObject* object, ExecState* state, PropertyName propertyName, PropertySlot& slot)\n"); + push(@output, "{\n"); + push(@output, " auto* thisObject = jsCast<${className}*>(object);\n"); + push(@output, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n"); + + + if ($indexedGetterFunction) { + push(@output, " auto optionalIndex = parseIndex(propertyName);\n"); + push(@output, " if (optionalIndex && optionalIndex.value() < thisObject->wrapped().length()) {\n"); + push(@output, " auto index = optionalIndex.value();\n"); + push(@output, GenerateIndexedGetter($interface, $indexedGetterFunction)); + push(@output, " }\n"); + } + + my $hasNamedGetter = $namedGetterFunction || $interface->extendedAttributes->{CustomNamedGetter}; + if ($hasNamedGetter) { + if (!$interface->extendedAttributes->{OverrideBuiltins}) { + &$ownPropertyCheck(); + &$prototypeCheck(); + } + if ($indexedGetterFunction) { + push(@output, " if (!optionalIndex && thisObject->classInfo() == info() && !propertyName.isSymbol()) {\n"); + } else { + push(@output, " if (thisObject->classInfo() == info() && !propertyName.isSymbol()) {\n"); + } + push(@output, GenerateNamedGetter($interface, $namedGetterFunction)); + push(@output, " }\n"); + } + + if ($interface->extendedAttributes->{JSCustomGetOwnPropertySlotAndDescriptor}) { + push(@output, " if (thisObject->getOwnPropertySlotDelegate(state, propertyName, slot))\n"); + push(@output, " return true;\n"); + } + + if (!$hasNamedGetter || $interface->extendedAttributes->{OverrideBuiltins}) { + &$ownPropertyCheck(); } - my $manualLookupGetterGeneration = sub { - my $requiresManualLookup = ($indexedGetterFunction && !$hasNumericIndexedGetter) || $namedGetterFunction; - if ($requiresManualLookup) { - push(@getOwnPropertySlotImpl, " const ${namespaceMaybe}HashEntry* entry = getStaticValueSlotEntryWithoutCaching<$className>(exec, propertyName);\n"); - push(@getOwnPropertySlotImpl, " if (entry) {\n"); - push(@getOwnPropertySlotImpl, " slot.setCustom(thisObject, entry->attributes(), entry->propertyGetter());\n"); - push(@getOwnPropertySlotImpl, " return true;\n"); - push(@getOwnPropertySlotImpl, " }\n"); + push(@output, " return false;\n"); + push(@output, "}\n\n"); + + return @output; +} + +sub GenerateGetOwnPropertySlotBodyByIndex +{ + my ($interface, $className, $indexedGetterFunction, $namedGetterFunction) = @_; + + my @output = (); + + push(@output, "bool ${className}::getOwnPropertySlotByIndex(JSObject* object, ExecState* state, unsigned index, PropertySlot& slot)\n"); + push(@output, "{\n"); + push(@output, " auto* thisObject = jsCast<${className}*>(object);\n"); + push(@output, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n"); + + # Sink the int-to-string conversion that happens when we create a PropertyName + # to the point where we actually need it. + my $generatedPropertyName = 0; + my $propertyNameGeneration = sub { + if ($generatedPropertyName) { + return; } + push(@output, " Identifier propertyName = Identifier::from(state, index);\n"); + $generatedPropertyName = 1; }; + + if ($indexedGetterFunction) { + push(@output, " if (LIKELY(index < thisObject->wrapped().length())) {\n"); + push(@output, GenerateIndexedGetter($interface, $indexedGetterFunction)); + push(@output, " }\n"); + } - if (!$interface->extendedAttributes->{"CustomNamedGetter"}) { - &$manualLookupGetterGeneration(); + # Indexing an object with an integer that is not a supported property index should not call the named property getter. + # https://heycam.github.io/webidl/#idl-indexed-properties + if (!$indexedGetterFunction && ($namedGetterFunction || $interface->extendedAttributes->{CustomNamedGetter})) { + &$propertyNameGeneration(); + push(@output, " if (thisObject->classInfo() == info()) {\n"); + push(@output, GenerateNamedGetter($interface, $namedGetterFunction)); + push(@output, " }\n"); } - if ($indexedGetterFunction) { - push(@getOwnPropertySlotImpl, " unsigned index = propertyName.asIndex();\n"); + if ($interface->extendedAttributes->{JSCustomGetOwnPropertySlotAndDescriptor}) { + &$propertyNameGeneration(); + push(@output, " if (thisObject->getOwnPropertySlotDelegate(state, propertyName, slot))\n"); + push(@output, " return true;\n"); + } - # If the item function returns a string then we let the TreatReturnedNullStringAs handle the cases - # where the index is out of range. - if ($indexedGetterFunction->signature->type eq "DOMString") { - push(@getOwnPropertySlotImpl, " if (index != PropertyName::NotAnIndex) {\n"); - } else { - push(@getOwnPropertySlotImpl, " if (index != PropertyName::NotAnIndex && index < thisObject->impl().length()) {\n"); - } - # Assume that if there's a setter, the index will be writable - if ($interface->extendedAttributes->{"CustomIndexedSetter"}) { - push(@getOwnPropertySlotImpl, " unsigned attributes = ${namespaceMaybe}DontDelete;\n"); - } else { - push(@getOwnPropertySlotImpl, " unsigned attributes = ${namespaceMaybe}DontDelete | ${namespaceMaybe}ReadOnly;\n"); - } - if ($hasNumericIndexedGetter) { - push(@getOwnPropertySlotImpl, " slot.setValue(thisObject, attributes, thisObject->getByIndex(exec, index));\n"); + push(@output, " return Base::getOwnPropertySlotByIndex(thisObject, state, index, slot);\n"); + push(@output, "}\n\n"); + + return @output; +} + +sub GenerateGetOwnPropertyNames +{ + my ($interface, $className, $indexedGetterFunction, $namedGetterFunction) = @_; + + my @output = (); + + # Property enumeration - https://heycam.github.io/webidl/#legacy-platform-object-property-enumeration + + push(@implContent, "void ${className}::getOwnPropertyNames(JSObject* object, ExecState* state, PropertyNameArray& propertyNames, EnumerationMode mode)\n"); + push(@implContent, "{\n"); + push(@implContent, " auto* thisObject = jsCast<${className}*>(object);\n"); + push(@implContent, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n"); + + # 1. If the object supports indexed properties, then the object’s supported + # property indices are enumerated first, in numerical order. + if ($indexedGetterFunction) { + push(@implContent, " for (unsigned i = 0, count = thisObject->wrapped().length(); i < count; ++i)\n"); + push(@implContent, " propertyNames.add(Identifier::from(state, i));\n"); + } + + # 2. If the object supports named properties and doesn’t implement an interface + # with the [LegacyUnenumerableNamedProperties] extended attribute, then the + # object’s supported property names that are visible according to the named + # property visibility algorithm are enumerated next, in the order given in + # the definition of the set of supported property names. + if ($namedGetterFunction) { + if (!$interface->extendedAttributes->{LegacyUnenumerableNamedProperties}) { + push(@implContent, " for (auto& propertyName : thisObject->wrapped().supportedPropertyNames())\n"); + push(@implContent, " propertyNames.add(Identifier::fromString(state, propertyName));\n"); } else { - push(@getOwnPropertySlotImpl, " slot.setCustomIndex(thisObject, attributes, index, indexGetter);\n"); + push(@implContent, " if (mode.includeDontEnumProperties()) {\n"); + push(@implContent, " for (auto& propertyName : thisObject->wrapped().supportedPropertyNames())\n"); + push(@implContent, " propertyNames.add(Identifier::fromString(state, propertyName));\n"); + push(@implContent, " }\n"); } - push(@getOwnPropertySlotImpl, " return true;\n"); - push(@getOwnPropertySlotImpl, " }\n"); } + # 3. Finally, any enumerable own properties or properties from the object’s + # prototype chain are then enumerated, in no defined order. + push(@implContent, " Base::getOwnPropertyNames(thisObject, state, propertyNames, mode);\n"); + push(@implContent, "}\n\n"); - if ($namedGetterFunction || $interface->extendedAttributes->{"CustomNamedGetter"}) { - push(@getOwnPropertySlotImpl, " if (canGetItemsForName(exec, &thisObject->impl(), propertyName)) {\n"); - push(@getOwnPropertySlotImpl, " slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, thisObject->nameGetter);\n"); - push(@getOwnPropertySlotImpl, " return true;\n"); - push(@getOwnPropertySlotImpl, " }\n"); - if ($inlined) { - $headerIncludes{"wtf/text/AtomicString.h"} = 1; + return @output; +} + +sub GeneratePut +{ + my ($interface, $className, $indexedSetterFunction, $namedSetterFunction) = @_; + + assert("Named setters are not supported.") if $namedSetterFunction; + + my @output = (); + + push(@output, "bool ${className}::put(JSCell* cell, ExecState* state, PropertyName propertyName, JSValue value, PutPropertySlot& slot)\n"); + push(@output, "{\n"); + push(@output, " auto* thisObject = jsCast<${className}*>(cell);\n"); + push(@output, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n"); + + if ($indexedSetterFunction || $interface->extendedAttributes->{CustomIndexedSetter}) { + if ($interface->extendedAttributes->{CustomIndexedSetter}) { + push(@output, " if (auto index = parseIndex(propertyName)) {\n"); + push(@output, " thisObject->indexSetter(state, index.value(), value);\n"); + push(@output, " return true;\n"); + push(@output, " }\n"); } else { - $implIncludes{"wtf/text/AtomicString.h"} = 1; + # The second argument of the indexed setter function is the argument being converted. + my $argument = @{$indexedSetterFunction->arguments}[1]; + my ($nativeValue, $mayThrowException) = JSValueToNative($interface, $argument, "value", $indexedSetterFunction->extendedAttributes->{Conditional}, "state", "*state", "thisObject", "", ""); + + push(@output, " if (auto index = parseIndex(propertyName)) {\n"); + push(@output, " auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n"); + push(@output, " auto nativeValue = ${nativeValue};\n"); + push(@output, " RETURN_IF_EXCEPTION(throwScope, true);\n") if $mayThrowException; + + my $indexedSetterFunctionName = $indexedSetterFunction->name || "setItem"; + my $functionString = "${indexedSetterFunctionName}(index, WTFMove(nativeValue))"; + $functionString = "propagateException(*state, throwScope, ${functionString})" if NeedsExplicitPropagateExceptionCall($indexedSetterFunction); + + push(@output, " ${functionString};\n"); + push(@output, " return true;\n"); + push(@output, " }\n"); } } - - if ($interface->extendedAttributes->{"CustomNamedGetter"}) { - &$manualLookupGetterGeneration(); + + if ($interface->extendedAttributes->{CustomNamedSetter}) { + push(@output, " bool putResult = false;\n"); + push(@output, " if (thisObject->putDelegate(state, propertyName, value, slot, putResult))\n"); + push(@output, " return putResult;\n"); } - if ($interface->extendedAttributes->{"JSCustomGetOwnPropertySlotAndDescriptor"}) { - push(@getOwnPropertySlotImpl, " if (thisObject->getOwnPropertySlotDelegate(exec, propertyName, slot))\n"); - push(@getOwnPropertySlotImpl, " return true;\n"); - } + push(@output, " return Base::put(thisObject, state, propertyName, value, slot);\n"); + push(@output, "}\n\n"); + + return @output; +} + +sub GeneratePutByIndex +{ + my ($interface, $className, $indexedSetterFunction, $namedSetterFunction) = @_; + + assert("Named setters are not supported.") if $namedSetterFunction; + + my @output = (); - if ($hasAttributes) { - if ($inlined) { - die "Cannot inline if NoStaticTables is set." if ($interface->extendedAttributes->{"JSNoStaticTables"}); - push(@getOwnPropertySlotImpl, " return ${namespaceMaybe}getStaticValueSlot<$className, Base>(exec, *info()->staticPropHashTable, thisObject, propertyName, slot);\n"); + push(@output, "bool ${className}::putByIndex(JSCell* cell, ExecState* state, unsigned index, JSValue value, bool shouldThrow)\n"); + push(@output, "{\n"); + push(@output, " auto* thisObject = jsCast<${className}*>(cell);\n"); + push(@output, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n"); + + if ($indexedSetterFunction || $interface->extendedAttributes->{CustomIndexedSetter}) { + if ($interface->extendedAttributes->{CustomIndexedSetter}) { + push(@output, " if (LIKELY(index <= MAX_ARRAY_INDEX)) {\n"); + push(@output, " thisObject->indexSetter(state, index, value);\n"); + push(@output, " return true;\n"); + push(@output, " }\n"); } else { - push(@getOwnPropertySlotImpl, " return ${namespaceMaybe}getStaticValueSlot<$className, Base>(exec, " . hashTableAccessor($interface->extendedAttributes->{"JSNoStaticTables"}, $className) . ", thisObject, propertyName, slot);\n"); + # The second argument of the indexed setter function is the argument being converted. + my $argument = @{$indexedSetterFunction->arguments}[1]; + my ($nativeValue, $mayThrowException) = JSValueToNative($interface, $argument, "value", $indexedSetterFunction->extendedAttributes->{Conditional}, "state", "*state", "thisObject", "", ""); + + push(@output, " if (LIKELY(index <= MAX_ARRAY_INDEX)) {\n"); + push(@output, " auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n"); + push(@output, " auto nativeValue = ${nativeValue};\n"); + push(@output, " RETURN_IF_EXCEPTION(throwScope, true);\n") if $mayThrowException; + + my $indexedSetterFunctionName = $indexedSetterFunction->name || "setItem"; + my $functionString = "${indexedSetterFunctionName}(index, WTFMove(nativeValue))"; + $functionString = "propagateException(*state, throwScope, ${functionString})" if NeedsExplicitPropagateExceptionCall($indexedSetterFunction); + + push(@output, " ${functionString};\n"); + push(@output, " return true;\n"); + push(@output, " }\n"); } - } else { - push(@getOwnPropertySlotImpl, " return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);\n"); } - return @getOwnPropertySlotImpl; + if ($interface->extendedAttributes->{CustomNamedSetter}) { + push(@output, " Identifier propertyName = Identifier::from(state, index);\n"); + push(@output, " PutPropertySlot slot(thisObject, shouldThrow);\n"); + push(@output, " bool putResult = false;\n"); + push(@output, " if (thisObject->putDelegate(state, propertyName, value, slot, putResult))\n"); + push(@output, " return putResult;\n"); + } + + push(@output, " return Base::putByIndex(cell, state, index, value, shouldThrow);\n"); + push(@output, "}\n\n"); + + return @output; } sub GenerateHeaderContentHeader { my $interface = shift; - my $className = "JS" . $interface->name; + my $className = "JS" . $interface->type->name; my @headerContentHeader; - if ($interface->extendedAttributes->{"AppleCopyright"}) { + if ($interface->extendedAttributes->{AppleCopyright}) { @headerContentHeader = split("\r", $beginAppleCopyrightForHeaderFiles); } else { @headerContentHeader = split("\r", $headerTemplate); } - # - Add header protection - push(@headerContentHeader, "\n#ifndef $className" . "_h"); - push(@headerContentHeader, "\n#define $className" . "_h\n\n"); + push(@headerContentHeader, "\n#pragma once\n\n"); my $conditionalString = $codeGenerator->GenerateConditionalString($interface); push(@headerContentHeader, "#if ${conditionalString}\n\n") if $conditionalString; @@ -501,10 +729,10 @@ sub GenerateHeaderContentHeader sub GenerateImplementationContentHeader { my $interface = shift; - my $className = "JS" . $interface->name; + my $className = "JS" . $interface->type->name; my @implContentHeader; - if ($interface->extendedAttributes->{"AppleCopyright"}) { + if ($interface->extendedAttributes->{AppleCopyright}) { @implContentHeader = split("\r", $beginAppleCopyrightForSourceFiles); } else { @implContentHeader = split("\r", $headerTemplate); @@ -517,54 +745,157 @@ sub GenerateImplementationContentHeader return @implContentHeader; } -my %usesToJSNewlyCreated = ( - "CDATASection" => 1, - "Element" => 1, - "Node" => 1, - "Text" => 1, - "Touch" => 1, - "TouchList" => 1 -); +sub NeedsImplementationClass +{ + my ($interface) = @_; + + return 0 if $interface->extendedAttributes->{JSBuiltin}; + return 1; +} + +sub ShouldGenerateToWrapped +{ + my ($hasParent, $interface) = @_; + + return 0 if not NeedsImplementationClass($interface); + return 1 if !$hasParent or $interface->extendedAttributes->{JSGenerateToNativeObject}; + return 1 if $interface->parentType && $interface->parentType->name eq "EventTarget"; + return 0; +} + +sub ShouldGenerateWrapperOwnerCode +{ + my ($hasParent, $interface) = @_; + + return 0 if not NeedsImplementationClass($interface); + return 1 if !$hasParent; + return 1 if GetGenerateIsReachable($interface); + return 1 if GetCustomIsReachable($interface); + return 1 if $interface->extendedAttributes->{JSCustomFinalize}; + return 1 if $codeGenerator->InheritsExtendedAttribute($interface, "ActiveDOMObject"); + return 0; +} sub ShouldGenerateToJSDeclaration { my ($hasParent, $interface) = @_; - return 0 if ($interface->extendedAttributes->{"SuppressToJSObject"}); - return 1 if (!$hasParent or $interface->extendedAttributes->{"JSGenerateToJSObject"} or $interface->extendedAttributes->{"CustomToJSObject"}); + + return 0 if ($interface->extendedAttributes->{SuppressToJSObject}); + return 0 if not NeedsImplementationClass($interface); + return 0 if $interface->extendedAttributes->{CustomProxyToJSObject}; + return 1 if (!$hasParent or $interface->extendedAttributes->{JSGenerateToJSObject} or $interface->extendedAttributes->{CustomToJSObject}); + return 1 if $interface->parentType && $interface->parentType->name eq "EventTarget"; + return 1 if $interface->extendedAttributes->{Constructor} or $interface->extendedAttributes->{NamedConstructor}; return 0; } sub ShouldGenerateToJSImplementation { my ($hasParent, $interface) = @_; - return 0 if ($interface->extendedAttributes->{"SuppressToJSObject"}); - return 1 if ((!$hasParent or $interface->extendedAttributes->{"JSGenerateToJSObject"}) and !$interface->extendedAttributes->{"CustomToJSObject"}); + + return 0 if not ShouldGenerateToJSDeclaration($hasParent, $interface); + return 1 if not $interface->extendedAttributes->{CustomToJSObject}; return 0; } -sub GetAttributeGetterName +sub GetArgumentExceptionFunction { - my ($interfaceName, $className, $attribute) = @_; - if ($attribute->isStatic) { - return $codeGenerator->WK_lcfirst($className) . "Constructor" . $codeGenerator->WK_ucfirst($attribute->signature->name); + my ($interface, $argument, $argumentIndex, $quotedFunctionName) = @_; + + my $name = $argument->name; + my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($interface); + my $typeName = $argument->type->name; + + if ($codeGenerator->IsCallbackInterface($argument->type) || $codeGenerator->IsCallbackFunction($argument->type)) { + # FIXME: We should have specialized messages for callback interfaces vs. callback functions. + return "throwArgumentMustBeFunctionError(state, scope, ${argumentIndex}, \"${name}\", \"${visibleInterfaceName}\", ${quotedFunctionName});"; } - return "js" . $interfaceName . $codeGenerator->WK_ucfirst($attribute->signature->name) . ($attribute->signature->type =~ /Constructor$/ ? "Constructor" : ""); + + if ($codeGenerator->IsWrapperType($argument->type) || $codeGenerator->IsTypedArrayType($argument->type)) { + return "throwArgumentTypeError(state, scope, ${argumentIndex}, \"${name}\", \"${visibleInterfaceName}\", ${quotedFunctionName}, \"${typeName}\");"; + } + + return undef; } -sub GetAttributeSetterName +sub GetArgumentExceptionThrower { - my ($interfaceName, $className, $attribute) = @_; - if ($attribute->isStatic) { - return "set" . $codeGenerator->WK_ucfirst($className) . "Constructor" . $codeGenerator->WK_ucfirst($attribute->signature->name); + my ($interface, $argument, $argumentIndex, $quotedFunctionName) = @_; + + my $functionCall = GetArgumentExceptionFunction($interface, $argument, $argumentIndex, $quotedFunctionName); + return "[](JSC::ExecState& state, JSC::ThrowScope& scope) { " . $functionCall . " }" if $functionCall; +} + +sub GetAttributeExceptionFunction +{ + my ($interface, $attribute) = @_; + + my $name = $attribute->name; + my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($interface); + my $typeName = $attribute->type->name; + + if ($codeGenerator->IsWrapperType($attribute->type) || $codeGenerator->IsTypedArrayType($attribute->type)) { + return "throwAttributeTypeError(state, scope, \"${visibleInterfaceName}\", \"${name}\", \"${typeName}\");"; } - return "setJS" . $interfaceName . $codeGenerator->WK_ucfirst($attribute->signature->name) . ($attribute->signature->type =~ /Constructor$/ ? "Constructor" : ""); +} + +sub GetAttributeExceptionThrower +{ + my ($interface, $attribute) = @_; + + my $functionCall = GetAttributeExceptionFunction($interface, $attribute); + return "[](JSC::ExecState& state, JSC::ThrowScope& scope) { " . $functionCall . " }" if $functionCall; + +} + +sub PassArgumentExpression +{ + my ($name, $context) = @_; + + my $type = $context->type; + + return "${name}.value()" if $codeGenerator->IsEnumType($type); + return "WTFMove(${name})" if $type->isNullable; + + if ($codeGenerator->IsTypedArrayType($type)) { + return "*${name}" if $type->name eq "ArrayBuffer"; + return "${name}.releaseNonNull()"; + } + + return "${name}.releaseNonNull()" if $codeGenerator->IsCallbackInterface($type) || $codeGenerator->IsCallbackFunction($type); + return "*${name}" if $codeGenerator->IsWrapperType($type); + return "WTFMove(${name})"; +} + +sub GetAttributeGetterName +{ + my ($interface, $className, $attribute) = @_; + + return $codeGenerator->WK_lcfirst($className) . "Constructor" . $codeGenerator->WK_ucfirst($attribute->name) if $attribute->isStatic; + return GetJSBuiltinFunctionName($className, $attribute) if IsJSBuiltin($interface, $attribute); + return "js" . $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name) . ($codeGenerator->IsConstructorType($attribute->type) ? "Constructor" : ""); +} + +sub GetAttributeSetterName +{ + my ($interface, $className, $attribute) = @_; + + return "set" . $codeGenerator->WK_ucfirst($className) . "Constructor" . $codeGenerator->WK_ucfirst($attribute->name) if $attribute->isStatic; + return "set" . $codeGenerator->WK_ucfirst(GetJSBuiltinFunctionName($className, $attribute)) if IsJSBuiltin($interface, $attribute); + return "setJS" . $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name) . ($codeGenerator->IsConstructorType($attribute->type) ? "Constructor" : ""); } sub GetFunctionName { - my ($className, $function) = @_; - my $kind = $function->isStatic ? "Constructor" : "Prototype"; - return $codeGenerator->WK_lcfirst($className) . $kind . "Function" . $codeGenerator->WK_ucfirst($function->signature->name); + my ($interface, $className, $function) = @_; + + return GetJSBuiltinFunctionName($className, $function) if IsJSBuiltin($interface, $function); + + my $functionName = $function->name; + $functionName = "SymbolIterator" if $functionName eq "[Symbol.Iterator]"; + + my $kind = $function->isStatic ? "Constructor" : (OperationShouldBeOnInstance($interface, $function) ? "Instance" : "Prototype"); + return $codeGenerator->WK_lcfirst($className) . $kind . "Function" . $codeGenerator->WK_ucfirst($functionName); } sub GetSpecialAccessorFunctionForType @@ -575,10 +906,10 @@ sub GetSpecialAccessorFunctionForType my $numberOfParameters = shift; foreach my $function (@{$interface->functions}, @{$interface->anonymousFunctions}) { - my $specials = $function->signature->specials; + my $specials = $function->specials; my $specialExists = grep { $_ eq $special } @$specials; - my $parameters = $function->parameters; - if ($specialExists and scalar(@$parameters) == $numberOfParameters and $parameters->[0]->type eq $firstParameterType) { + my $arguments = $function->arguments; + if ($specialExists and scalar(@$arguments) == $numberOfParameters and $arguments->[0]->type->name eq $firstParameterType) { return $function; } } @@ -586,76 +917,787 @@ sub GetSpecialAccessorFunctionForType return 0; } +sub HasComplexGetOwnProperty +{ + my $interface = shift; + return $interface->extendedAttributes->{CheckSecurity} + || IsDOMGlobalObject($interface) + || InstanceOverridesGetOwnPropertySlot($interface); +} + +sub IsGlobalOrPrimaryGlobalInterface +{ + my $interface = shift; + + return $interface->extendedAttributes->{Global} || $interface->extendedAttributes->{PrimaryGlobal}; +} + +sub InterfaceRequiresAttributesOnInstance +{ + my $interface = shift; + my $interfaceName = $interface->type->name; + + # FIXME: All these return 1 if ... should ideally be removed. + # Some of them are unavoidable due to DOM weirdness, in which case we should + # add an IDL attribute for them. + + # FIXME: We should be able to drop this once <rdar://problem/24466097> is fixed. + return 1 if $interface->isException; + + return 1 if IsGlobalOrPrimaryGlobalInterface($interface); + + return 0; +} + +sub AttributeShouldBeOnInstance +{ + my $interface = shift; + my $attribute = shift; + + return 1 if InterfaceRequiresAttributesOnInstance($interface); + return 1 if $codeGenerator->IsConstructorType($attribute->type); + + # [Unforgeable] attributes should be on the instance. + # https://heycam.github.io/webidl/#Unforgeable + return 1 if IsUnforgeable($interface, $attribute); + + if ($interface->extendedAttributes->{CheckSecurity}) { + return 0 if $attribute->extendedAttributes->{DoNotCheckSecurity}; + return 0 if $attribute->extendedAttributes->{DoNotCheckSecurityOnGetter}; + return 1; + } + + return 0; +} + +sub NeedsRuntimeCheck +{ + my $interface = shift; + return $interface->extendedAttributes->{EnabledAtRuntime} + || $interface->extendedAttributes->{EnabledForWorld}; +} + +# https://heycam.github.io/webidl/#es-operations +sub OperationShouldBeOnInstance +{ + my $interface = shift; + my $function = shift; + + return 1 if IsGlobalOrPrimaryGlobalInterface($interface); + + # FIXME: The bindings generator does not support putting runtime-enabled operations on the instance yet (except for global objects). + return 0 if NeedsRuntimeCheck($function); + + # [Unforgeable] operations should be on the instance. https://heycam.github.io/webidl/#Unforgeable + return 1 if IsUnforgeable($interface, $function); + + return 0; +} + +sub GetJSCAttributesForAttribute +{ + my $interface = shift; + my $attribute = shift; + + my @specials = (); + push(@specials, "DontDelete") if IsUnforgeable($interface, $attribute); + + # As per Web IDL specification, constructor properties on the ECMAScript global object should not be enumerable. + my $isGlobalConstructor = $codeGenerator->IsConstructorType($attribute->type); + push(@specials, "DontEnum") if ($attribute->extendedAttributes->{NotEnumerable} || $isGlobalConstructor); + push(@specials, "ReadOnly") if IsReadonly($attribute); + push(@specials, "CustomAccessor") unless $isGlobalConstructor or IsJSBuiltin($interface, $attribute); + push(@specials, "DOMJITAttribute") if $attribute->extendedAttributes->{"DOMJIT"}; + push(@specials, "Accessor | Builtin") if IsJSBuiltin($interface, $attribute); + return (@specials > 0) ? join(" | ", @specials) : "0"; +} + sub GetIndexedGetterFunction { my $interface = shift; return GetSpecialAccessorFunctionForType($interface, "getter", "unsigned long", 1); } +sub GetIndexedSetterFunction +{ + my $interface = shift; + return GetSpecialAccessorFunctionForType($interface, "setter", "unsigned long", 2); +} + sub GetNamedGetterFunction { my $interface = shift; return GetSpecialAccessorFunctionForType($interface, "getter", "DOMString", 1); } -sub InstanceOverridesGetOwnPropertySlot +sub GetNamedSetterFunction { my $interface = shift; - my $numAttributes = @{$interface->attributes}; + return GetSpecialAccessorFunctionForType($interface, "setter", "DOMString", 2); +} - my $namedGetterFunction = GetNamedGetterFunction($interface); - my $indexedGetterFunction = GetIndexedGetterFunction($interface); - my $hasNumericIndexedGetter = $indexedGetterFunction ? $codeGenerator->IsNumericType($indexedGetterFunction->signature->type) : 0; +sub GetNamedDeleterFunction +{ + my $interface = shift; + return GetSpecialAccessorFunctionForType($interface, "deleter", "DOMString", 1); +} - my $hasImpureNamedGetter = $namedGetterFunction - || $interface->extendedAttributes->{"CustomNamedGetter"} - || $interface->extendedAttributes->{"CustomGetOwnPropertySlot"}; +sub InstanceFunctionCount +{ + my $interface = shift; + my $count = 0; - my $hasComplexGetter = $indexedGetterFunction - || $interface->extendedAttributes->{"JSCustomGetOwnPropertySlotAndDescriptor"} - || $hasImpureNamedGetter; + foreach my $function (@{$interface->functions}) { + $count++ if OperationShouldBeOnInstance($interface, $function); + } + + return $count; +} + +sub PrototypeFunctionCount +{ + my $interface = shift; + my $count = 0; + + foreach my $function (@{$interface->functions}) { + $count++ if !$function->isStatic && !OperationShouldBeOnInstance($interface, $function); + } - return $numAttributes > 0 || !$interface->extendedAttributes->{"NoInterfaceObject"} || $hasComplexGetter; + $count += scalar @{$interface->iterable->functions} if $interface->iterable; + $count += scalar @{$interface->serializable->functions} if $interface->serializable; + + return $count; +} +sub InstancePropertyCount +{ + my $interface = shift; + my $count = 0; + foreach my $attribute (@{$interface->attributes}) { + $count++ if AttributeShouldBeOnInstance($interface, $attribute); + } + $count += InstanceFunctionCount($interface); + return $count; +} + +sub PrototypePropertyCount +{ + my $interface = shift; + my $count = 0; + foreach my $attribute (@{$interface->attributes}) { + $count++ if !AttributeShouldBeOnInstance($interface, $attribute); + } + $count += PrototypeFunctionCount($interface); + $count++ if NeedsConstructorProperty($interface); + return $count; +} + +sub InstanceOverridesGetOwnPropertySlot +{ + my $interface = shift; + return $interface->extendedAttributes->{CustomGetOwnPropertySlot} + || $interface->extendedAttributes->{CustomNamedGetter} + || $interface->extendedAttributes->{JSCustomGetOwnPropertySlotAndDescriptor} + || GetIndexedGetterFunction($interface) + || GetNamedGetterFunction($interface); +} + +sub InstanceOverridesPut +{ + my $interface = shift; + return $interface->extendedAttributes->{CustomNamedSetter} + || $interface->extendedAttributes->{CustomIndexedSetter} + || GetIndexedSetterFunction($interface) + || GetNamedSetterFunction($interface); } -sub PrototypeOverridesGetOwnPropertySlot +sub PrototypeHasStaticPropertyTable { my $interface = shift; my $numConstants = @{$interface->constants}; - my $numFunctions = @{$interface->functions}; - return $numFunctions > 0 || $numConstants > 0; + return $numConstants > 0 || PrototypePropertyCount($interface) > 0; } sub InstanceOverridesPutImplementation { my $interface = shift; - return $interface->extendedAttributes->{"CustomNamedSetter"} - || $interface->extendedAttributes->{"CustomIndexedSetter"}; + return $interface->extendedAttributes->{CustomNamedSetter} + || $interface->extendedAttributes->{CustomIndexedSetter}; } sub InstanceOverridesPutDeclaration { my $interface = shift; - return $interface->extendedAttributes->{"CustomPutFunction"} - || $interface->extendedAttributes->{"CustomNamedSetter"} - || $interface->extendedAttributes->{"CustomIndexedSetter"}; + return $interface->extendedAttributes->{CustomPutFunction} + || $interface->extendedAttributes->{CustomNamedSetter} + || $interface->extendedAttributes->{CustomIndexedSetter}; } -sub GenerateHeader +sub InstanceNeedsVisitChildren +{ + my $interface = shift; + return $interface->extendedAttributes->{JSCustomMarkFunction} + || $codeGenerator->InheritsInterface($interface, "EventTarget") + || $interface->type->name eq "EventTarget" + || $interface->extendedAttributes->{ReportExtraMemoryCost} + || IsJSBuiltinConstructor($interface) +} + +sub InstanceNeedsEstimatedSize +{ + my $interface = shift; + return $interface->extendedAttributes->{ReportExtraMemoryCost}; +} + +sub GetImplClassName { - my $object = shift; my $interface = shift; - my $interfaceName = $interface->name; + return $interface->type->name; +} + +sub IsClassNameWordBoundary +{ + my ($name, $i) = @_; + + # Interpret negative numbers as distance from end of string, just as the substr function does. + $i += length($name) if $i < 0; + + return 0 if $i < 0; + return 1 if $i == 0; + return 1 if $i == length($name); + return 0 if $i > length($name); + + my $checkString = substr($name, $i - 1); + return $checkString =~ /^[^A-Z][A-Z]/ || $checkString =~ /^[A-Z][A-Z][^A-Z]/; +} + +sub IsPrefixRemovable +{ + my ($class, $name, $i) = @_; + + return IsClassNameWordBoundary($name, $i) + && (IsClassNameWordBoundary($class, $i) && substr($class, 0, $i) eq substr($name, 0, $i) + || IsClassNameWordBoundary($class, -$i) && substr($class, -$i) eq substr($name, 0, $i)); +} + +sub GetNestedClassName +{ + my ($interface, $name) = @_; + + my $class = GetImplClassName($interface); + my $member = $codeGenerator->WK_ucfirst($name); + + # Since the enumeration name will be nested in the class name's namespace, remove any words + # that happen to match the start or end of the class name. If an enumeration is named TrackType or + # TextTrackType, and the class is named TextTrack, then we will get a name like TextTrack::Type. + my $memberLength = length($member); + my $longestPrefixLength = 0; + if ($member =~ /^[A-Z]./) { + for (my $i = 2; $i < $memberLength - 1; $i++) { + $longestPrefixLength = $i if IsPrefixRemovable($class, $member, $i); + } + } + $member = substr($member, $longestPrefixLength); + + return "${class}::$member"; +} + +sub GetEnumerationClassName +{ + my ($type, $interface) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; + + if ($codeGenerator->HasEnumImplementationNameOverride($type)) { + return $codeGenerator->GetEnumImplementationNameOverride($type); + } + + my $name = $type->name; + + return $name if $codeGenerator->IsExternalEnumType($type); + return $name unless defined($interface); + + return GetNestedClassName($interface, $name); +} + +sub GetEnumerationValueName +{ + my ($name) = @_; + + return "EmptyString" if $name eq ""; + $name = join("", map { $codeGenerator->WK_ucfirst($_) } split("-", $name)); + $name = "_$name" if $name =~ /^\d/; + return $name; +} + +sub GenerateEnumerationHeader +{ + my ($object, $enumeration, $className) = @_; + + # - Add default header template and header protection. + push(@headerContentHeader, GenerateHeaderContentHeader($enumeration)); + + $headerIncludes{"$className.h"} = 1; + $headerIncludes{"JSDOMConvert.h"} = 1; + + push(@headerContent, "\nnamespace WebCore {\n\n"); + push(@headerContent, GenerateEnumerationHeaderContent($enumeration, $className)); + push(@headerContent, "} // namespace WebCore\n"); + + my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration); + push(@headerContent, "\n#endif // ${conditionalString}\n") if $conditionalString; +} + +sub GenerateEnumerationImplementation +{ + my ($object, $enumeration, $className) = @_; + + # - Add default header template + push(@implContentHeader, GenerateImplementationContentHeader($enumeration)); + + # FIXME: A little ugly to have this be a side effect instead of a return value. + AddToImplIncludes("<runtime/JSString.h>"); + AddToImplIncludes("JSDOMConvert.h"); + + push(@implContent, "\nusing namespace JSC;\n\n"); + push(@implContent, "namespace WebCore {\n\n"); + push(@implContent, GenerateEnumerationImplementationContent($enumeration, $className)); + push(@implContent, "} // namespace WebCore\n"); + + my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration); + push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString; +} + +sub GenerateEnumerationImplementationContent +{ + my ($enumeration, $className, $interface, $conditionalString) = @_; + + my $result = ""; + $result .= "#if ${conditionalString}\n\n" if $conditionalString; + + # FIXME: Change to take VM& instead of ExecState*. + $result .= "template<> JSString* convertEnumerationToJS(ExecState& state, $className enumerationValue)\n"; + $result .= "{\n"; + # FIXME: Might be nice to make this global be "const", but NeverDestroyed does not currently support that. + # FIXME: Might be nice to make the entire array be NeverDestroyed instead of each value, but not sure what the syntax for that is. + AddToImplIncludes("<wtf/NeverDestroyed.h>"); + $result .= " static NeverDestroyed<const String> values[] = {\n"; + foreach my $value (@{$enumeration->values}) { + if ($value eq "") { + $result .= " emptyString(),\n"; + } else { + $result .= " ASCIILiteral(\"$value\"),\n"; + } + } + $result .= " };\n"; + my $index = 0; + foreach my $value (@{$enumeration->values}) { + my $enumerationValueName = GetEnumerationValueName($value); + $result .= " static_assert(static_cast<size_t>(${className}::$enumerationValueName) == $index, \"${className}::$enumerationValueName is not $index as expected\");\n"; + $index++; + } + $result .= " ASSERT(static_cast<size_t>(enumerationValue) < WTF_ARRAY_LENGTH(values));\n"; + $result .= " return jsStringWithCache(&state, values[static_cast<size_t>(enumerationValue)]);\n"; + $result .= "}\n\n"; + + # FIXME: Change to take VM& instead of ExecState&. + # FIXME: Consider using toStringOrNull to make exception checking faster. + # FIXME: Consider finding a more efficient way to match against all the strings quickly. + $result .= "template<> std::optional<$className> parseEnumeration<$className>(ExecState& state, JSValue value)\n"; + $result .= "{\n"; + $result .= " auto stringValue = value.toWTFString(&state);\n"; + foreach my $value (@{$enumeration->values}) { + my $enumerationValueName = GetEnumerationValueName($value); + if ($value eq "") { + $result .= " if (stringValue.isEmpty())\n"; + } else { + $result .= " if (stringValue == \"$value\")\n"; + } + $result .= " return ${className}::${enumerationValueName};\n"; + } + $result .= " return std::nullopt;\n"; + $result .= "}\n\n"; + + $result .= "template<> $className convertEnumeration<$className>(ExecState& state, JSValue value)\n"; + $result .= "{\n"; + $result .= " VM& vm = state.vm();\n"; + $result .= " auto throwScope = DECLARE_THROW_SCOPE(vm);\n"; + $result .= " auto result = parseEnumeration<$className>(state, value);\n"; + $result .= " if (UNLIKELY(!result)) {\n"; + $result .= " throwTypeError(&state, throwScope);\n"; + $result .= " return { };\n"; + $result .= " }\n"; + $result .= " return result.value();\n"; + $result .= "}\n\n"; + + $result .= "template<> const char* expectedEnumerationValues<$className>()\n"; + $result .= "{\n"; + $result .= " return \"\\\"" . join ("\\\", \\\"", @{$enumeration->values}) . "\\\"\";\n"; + $result .= "}\n\n"; + + $result .= "#endif\n\n" if $conditionalString; + + return $result; +} + +sub GenerateEnumerationsImplementationContent +{ + my ($interface, $enumerations) = @_; + + return "" unless @$enumerations; + + # FIXME: A little ugly to have this be a side effect instead of a return value. + AddToImplIncludes("<runtime/JSString.h>"); + AddToImplIncludes("JSDOMConvert.h"); + + my $result = ""; + foreach my $enumeration (@$enumerations) { + my $className = GetEnumerationClassName($enumeration->type, $interface); + my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration); + $result .= GenerateEnumerationImplementationContent($enumeration, $className, $interface, $conditionalString); + } + return $result; +} + +sub GenerateEnumerationHeaderContent +{ + my ($enumeration, $className, $conditionalString) = @_; + + my $result = ""; + $result .= "#if ${conditionalString}\n\n" if $conditionalString; + + my $exportMacro = GetExportMacroForJSClass($enumeration); + + $result .= "template<> ${exportMacro}JSC::JSString* convertEnumerationToJS(JSC::ExecState&, $className);\n\n"; + $result .= "template<> ${exportMacro}std::optional<$className> parseEnumeration<$className>(JSC::ExecState&, JSC::JSValue);\n"; + $result .= "template<> ${exportMacro}$className convertEnumeration<$className>(JSC::ExecState&, JSC::JSValue);\n"; + $result .= "template<> ${exportMacro}const char* expectedEnumerationValues<$className>();\n\n"; + $result .= "#endif\n\n" if $conditionalString; + + return $result; +} + +sub GenerateEnumerationsHeaderContent +{ + my ($interface, $enumerations) = @_; + + return "" unless @$enumerations; + + # FIXME: Could optimize this to only generate the parts of each enumeration that are actually + # used, which would require iterating over everything in the interface. + + $headerIncludes{"JSDOMConvert.h"} = 1; + + my $result = ""; + foreach my $enumeration (@$enumerations) { + my $className = GetEnumerationClassName($enumeration->type, $interface); + my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration); + $result .= GenerateEnumerationHeaderContent($enumeration, $className, $conditionalString); + } + return $result; +} + +sub GetDictionaryClassName +{ + my ($type, $interface) = @_; + + if ($codeGenerator->HasDictionaryImplementationNameOverride($type)) { + return $codeGenerator->GetDictionaryImplementationNameOverride($type); + } + + my $name = $type->name; + return $name if $codeGenerator->IsExternalDictionaryType($type); + return $name unless defined($interface); + return GetNestedClassName($interface, $name); +} + +sub GenerateDefaultValue +{ + my ($typeScope, $context, $type, $defaultValue) = @_; + + if ($codeGenerator->IsStringType($type)) { + my $useAtomicString = $context->extendedAttributes->{AtomicString}; + if ($defaultValue eq "null") { + return $useAtomicString ? "nullAtom" : "String()"; + } elsif ($defaultValue eq "\"\"") { + return $useAtomicString ? "emptyAtom" : "emptyString()"; + } else { + return $useAtomicString ? "AtomicString(${defaultValue}, AtomicString::ConstructFromLiteral)" : "ASCIILiteral(${defaultValue})"; + } + } + + if ($codeGenerator->IsEnumType($type)) { + # FIXME: Would be nice to report an error if the value does not have quote marks around it. + # FIXME: Would be nice to report an error if the value is not one of the enumeration values. + my $className = GetEnumerationClassName($type, $typeScope); + my $enumerationValueName = GetEnumerationValueName(substr($defaultValue, 1, -1)); + return $className . "::" . $enumerationValueName; + } + if ($defaultValue eq "null") { + if ($type->isUnion) { + return "std::nullopt" if $type->isNullable; + + my $IDLType = GetIDLType($typeScope, $type); + return "convert<${IDLType}>(state, jsNull());"; + } + + return "jsNull()" if $type->name eq "any"; + return "nullptr" if $codeGenerator->IsWrapperType($type) || $codeGenerator->IsTypedArrayType($type); + return "String()" if $codeGenerator->IsStringType($type); + return "std::nullopt"; + } + + if ($defaultValue eq "[]") { + my $IDLType = GetIDLType($typeScope, $type); + return "Converter<${IDLType}>::ReturnType{ }" if $codeGenerator->IsSequenceOrFrozenArrayType($type); + + my $nativeType = GetNativeType($typeScope, $type); + return "$nativeType()" + } + + return "jsUndefined()" if $defaultValue eq "undefined"; + return "PNaN" if $defaultValue eq "NaN"; + + return $defaultValue; +} + +sub GenerateDictionaryHeaderContent +{ + my ($dictionary, $className, $conditionalString) = @_; + + my $result = ""; + $result .= "#if ${conditionalString}\n\n" if $conditionalString; + $result .= "template<> ${className} convertDictionary<$className>(JSC::ExecState&, JSC::JSValue);\n\n"; + + if ($dictionary->extendedAttributes->{JSGenerateToJSObject}) { + $result .= "JSC::JSObject* convertDictionaryToJS(JSC::ExecState&, JSDOMGlobalObject&, const ${className}&);\n\n"; + } + + $result .= "#endif\n\n" if $conditionalString; + return $result; +} + +sub GenerateDictionariesHeaderContent +{ + my ($typeScope, $allDictionaries) = @_; + + return "" unless @$allDictionaries; + + $headerIncludes{"JSDOMConvert.h"} = 1; + + my $result = ""; + foreach my $dictionary (@$allDictionaries) { + $headerIncludes{$typeScope->type->name . ".h"} = 1 if $typeScope; + my $className = GetDictionaryClassName($dictionary->type, $typeScope); + my $conditionalString = $codeGenerator->GenerateConditionalString($dictionary); + $result .= GenerateDictionaryHeaderContent($dictionary, $className, $conditionalString); + } + return $result; +} + +sub GenerateDictionaryImplementationContent +{ + my ($dictionary, $className, $interface, $conditionalString) = @_; + + my $result = ""; + + my $name = $dictionary->type->name; + my $typeScope = $interface || $dictionary; + + $result .= "#if ${conditionalString}\n\n" if $conditionalString; + + # FIXME: A little ugly to have this be a side effect instead of a return value. + AddToImplIncludes("JSDOMConvert.h"); + + # https://heycam.github.io/webidl/#es-dictionary + $result .= "template<> $className convertDictionary<$className>(ExecState& state, JSValue value)\n"; + $result .= "{\n"; + $result .= " VM& vm = state.vm();\n"; + $result .= " auto throwScope = DECLARE_THROW_SCOPE(vm);\n"; + $result .= " bool isNullOrUndefined = value.isUndefinedOrNull();\n"; + $result .= " auto* object = isNullOrUndefined ? nullptr : value.getObject();\n"; + + # 1. If Type(V) is not Undefined, Null or Object, then throw a TypeError. + $result .= " if (UNLIKELY(!isNullOrUndefined && !object)) {\n"; + $result .= " throwTypeError(&state, throwScope);\n"; + $result .= " return { };\n"; + $result .= " }\n"; + + # 2. If V is a native RegExp object, then throw a TypeError. + # FIXME: This RegExp special handling is likely to go away in the specification. + $result .= " if (UNLIKELY(object && object->type() == RegExpObjectType)) {\n"; + $result .= " throwTypeError(&state, throwScope);\n"; + $result .= " return { };\n"; + $result .= " }\n"; + + # 3. Let dict be an empty dictionary value of type D; every dictionary member is initially considered to be not present. + + # 4. Let dictionaries be a list consisting of D and all of D’s inherited dictionaries, in order from least to most derived. + my @dictionaries; + push(@dictionaries, $dictionary); + my $parentType = $dictionary->parentType; + while (defined($parentType)) { + my $parentDictionary = $codeGenerator->GetDictionaryByType($parentType); + assert("Unable to find definition for dictionary named '" . $parentType->name . "'!") unless defined($parentDictionary); + unshift(@dictionaries, $parentDictionary); + $parentType = $parentDictionary->parentType; + } + + my $arguments = ""; + my $comma = ""; + + $result .= " $className result;\n"; + + # 5. For each dictionary dictionary in dictionaries, in order: + foreach my $dictionary (@dictionaries) { + # For each dictionary member member declared on dictionary, in lexicographical order: + my @sortedMembers = sort { $a->name cmp $b->name } @{$dictionary->members}; + foreach my $member (@sortedMembers) { + $member->default("undefined") if $member->type->name eq "any" and !defined($member->default); # Use undefined as default value for member of type 'any' unless specified otherwise. + + my $type = $member->type; + AddToImplIncludesForIDLType($type); + + # 5.1. Let key be the identifier of member. + my $key = $member->name; + + # 5.2. Let value be an ECMAScript value, depending on Type(V): + $result .= " JSValue ${key}Value = isNullOrUndefined ? jsUndefined() : object->get(&state, Identifier::fromString(&state, \"${key}\"));\n"; + + my $IDLType = GetIDLType($typeScope, $type); + + # 5.3. If value is not undefined, then: + $result .= " if (!${key}Value.isUndefined()) {\n"; + $result .= " result.$key = convert<${IDLType}>(state, ${key}Value);\n"; + $result .= " RETURN_IF_EXCEPTION(throwScope, { });\n"; + + # Value is undefined. + # 5.4. Otherwise, if value is undefined but the dictionary member has a default value, then: + if (!$member->isRequired && defined $member->default) { + $result .= " } else\n"; + $result .= " result.$key = " . GenerateDefaultValue($typeScope, $member, $member->type, $member->default) . ";\n"; + } elsif ($member->isRequired) { + # 5.5. Otherwise, if value is undefined and the dictionary member is a required dictionary member, then throw a TypeError. + $result .= " } else {\n"; + $result .= " throwRequiredMemberTypeError(state, throwScope, \"". $member->name ."\", \"$name\", \"". $type->name ."\");\n"; + $result .= " return { };\n"; + $result .= " }\n"; + } else { + $result .= " }\n"; + } + } + } + + $result .= " return result;\n"; + $result .= "}\n\n"; + + if ($dictionary->extendedAttributes->{JSGenerateToJSObject}) { + $result .= "JSC::JSObject* convertDictionaryToJS(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const ${className}& dictionary)\n"; + $result .= "{\n"; + $result .= " auto& vm = state.vm();\n\n"; + + # 1. Let O be ! ObjectCreate(%ObjectPrototype%). + $result .= " auto result = constructEmptyObject(&state);\n\n"; + + # 2. Let dictionaries be a list consisting of D and all of D’s inherited dictionaries, + # in order from least to most derived. + # NOTE: This was done above. + + # 3. For each dictionary dictionary in dictionaries, in order: + foreach my $dictionary (@dictionaries) { + # 3.1. For each dictionary member member declared on dictionary, in lexicographical order: + my @sortedMembers = sort { $a->name cmp $b->name } @{$dictionary->members}; + foreach my $member (@sortedMembers) { + my $key = $member->name; + my $IDLType = GetIDLType($typeScope, $member->type); + + # 1. Let key be the identifier of member. + # 2. If the dictionary member named key is present in V, then: + # 1. Let idlValue be the value of member on V. + # 2. Let value be the result of converting idlValue to an ECMAScript value. + # 3. Perform ! CreateDataProperty(O, key, value). + if (!$member->isRequired && not defined $member->default) { + $result .= " if (!${IDLType}::isNullValue(dictionary.${key})) {\n"; + $result .= " auto ${key}Value = toJS<$IDLType>(state, globalObject, ${IDLType}::extractValueFromNullable(dictionary.${key}));\n"; + $result .= " result->putDirect(vm, JSC::Identifier::fromString(&vm, \"${key}\"), ${key}Value);\n"; + $result .= " }\n"; + } else { + $result .= " auto ${key}Value = toJS<$IDLType>(state, globalObject, dictionary.${key});\n"; + $result .= " result->putDirect(vm, JSC::Identifier::fromString(&vm, \"${key}\"), ${key}Value);\n"; + } + } + } + + $result .= " return result;\n"; + $result .= "}\n\n"; + } + + $result .= "#endif\n\n" if $conditionalString; + + return $result; +} + +sub GenerateDictionariesImplementationContent +{ + my ($typeScope, $allDictionaries) = @_; + + my $result = ""; + foreach my $dictionary (@$allDictionaries) { + my $className = GetDictionaryClassName($dictionary->type, $typeScope); + my $conditionalString = $codeGenerator->GenerateConditionalString($dictionary); + $result .= GenerateDictionaryImplementationContent($dictionary, $className, $typeScope, $conditionalString); + } + return $result; +} + +sub GetJSTypeForNode +{ + my ($interface) = @_; + + if ($codeGenerator->InheritsInterface($interface, "Document")) { + return "JSDocumentWrapperType"; + } + if ($codeGenerator->InheritsInterface($interface, "DocumentFragment")) { + return "JSDocumentFragmentNodeType"; + } + if ($codeGenerator->InheritsInterface($interface, "DocumentType")) { + return "JSDocumentTypeNodeType"; + } + if ($codeGenerator->InheritsInterface($interface, "ProcessingInstruction")) { + return "JSProcessingInstructionNodeType"; + } + if ($codeGenerator->InheritsInterface($interface, "CDATASection")) { + return "JSCDATASectionNodeType"; + } + if ($codeGenerator->InheritsInterface($interface, "Attr")) { + return "JSAttrNodeType"; + } + if ($codeGenerator->InheritsInterface($interface, "Comment")) { + return "JSCommentNodeType"; + } + if ($codeGenerator->InheritsInterface($interface, "Text")) { + return "JSTextNodeType"; + } + if ($codeGenerator->InheritsInterface($interface, "Element")) { + return "JSElementType"; + } + return "JSNodeType"; +} + +sub GenerateHeader +{ + my ($object, $interface, $enumerations, $dictionaries) = @_; + + my $interfaceName = $interface->type->name; my $className = "JS$interfaceName"; my %structureFlags = (); - my $hasLegacyParent = $interface->extendedAttributes->{"JSLegacyParent"}; - my $hasRealParent = $interface->parent; + my $hasLegacyParent = $interface->extendedAttributes->{JSLegacyParent}; + my $hasRealParent = $interface->parentType; my $hasParent = $hasLegacyParent || $hasRealParent; my $parentClassName = GetParentClassName($interface); - my $needsMarkChildren = $interface->extendedAttributes->{"JSCustomMarkFunction"} || $interface->extendedAttributes->{"EventTarget"} || $interface->name eq "EventTarget"; + my $needsVisitChildren = InstanceNeedsVisitChildren($interface); # - Add default header template and header protection push(@headerContentHeader, GenerateHeaderContentHeader($interface)); @@ -663,33 +1705,19 @@ sub GenerateHeader if ($hasParent) { $headerIncludes{"$parentClassName.h"} = 1; } else { - $headerIncludes{"JSDOMBinding.h"} = 1; - $headerIncludes{"<runtime/JSGlobalObject.h>"} = 1; + $headerIncludes{"JSDOMWrapper.h"} = 1; if ($interface->isException) { $headerIncludes{"<runtime/ErrorPrototype.h>"} = 1; - } else { - $headerIncludes{"<runtime/ObjectPrototype.h>"} = 1; } } - if ($interface->extendedAttributes->{"CustomCall"}) { - $headerIncludes{"<runtime/CallData.h>"} = 1; - } + $headerIncludes{"<runtime/CallData.h>"} = 1 if $interface->extendedAttributes->{CustomCall}; - if ($hasParent && $interface->extendedAttributes->{"JSGenerateToNativeObject"}) { - $headerIncludes{"$interfaceName.h"} = 1; - } - - $headerIncludes{"<runtime/JSObject.h>"} = 1; - $headerIncludes{"SVGElement.h"} = 1 if $className =~ /^JSSVG/; + $headerIncludes{"$interfaceName.h"} = 1 if $hasParent && $interface->extendedAttributes->{JSGenerateToNativeObject}; - my $implType = $interfaceName; - my ($svgPropertyType, $svgListPropertyType, $svgNativeType) = GetSVGPropertyTypes($implType); - $implType = $svgNativeType if $svgNativeType; + $headerIncludes{"SVGElement.h"} = 1 if $className =~ /^JSSVG/; - my $svgPropertyOrListPropertyType; - $svgPropertyOrListPropertyType = $svgPropertyType if $svgPropertyType; - $svgPropertyOrListPropertyType = $svgListPropertyType if $svgListPropertyType; + my $implType = GetImplClassName($interface); my $numConstants = @{$interface->constants}; my $numAttributes = @{$interface->attributes}; @@ -697,228 +1725,303 @@ sub GenerateHeader push(@headerContent, "\nnamespace WebCore {\n\n"); - if ($codeGenerator->IsSVGAnimatedType($interfaceName)) { + if ($codeGenerator->IsSVGAnimatedType($interface->type)) { $headerIncludes{"$interfaceName.h"} = 1; } else { # Implementation class forward declaration if (IsDOMGlobalObject($interface)) { - AddClassForwardIfNeeded($interfaceName) unless $svgPropertyOrListPropertyType; + AddClassForwardIfNeeded($interface->type); } } - AddClassForwardIfNeeded("JSDOMWindowShell") if $interfaceName eq "DOMWindow"; - AddClassForwardIfNeeded("JSDictionary") if $codeGenerator->IsConstructorTemplate($interface, "Event"); + push(@headerContent, "class JSDOMWindowShell;\n\n") if $interfaceName eq "DOMWindow"; + + my $exportMacro = GetExportMacroForJSClass($interface); # Class declaration - push(@headerContent, "class $className : public $parentClassName {\n"); + push(@headerContent, "class $exportMacro$className : public $parentClassName {\n"); # Static create methods push(@headerContent, "public:\n"); - push(@headerContent, " typedef $parentClassName Base;\n"); + push(@headerContent, " using Base = $parentClassName;\n"); + push(@headerContent, " using DOMWrapped = $implType;\n") if $hasRealParent; + if ($interfaceName eq "DOMWindow") { - push(@headerContent, " static $className* create(JSC::VM& vm, JSC::Structure* structure, PassRefPtr<$implType> impl, JSDOMWindowShell* windowShell)\n"); + push(@headerContent, " static $className* create(JSC::VM& vm, JSC::Structure* structure, Ref<$implType>&& impl, JSDOMWindowShell* windowShell)\n"); push(@headerContent, " {\n"); - push(@headerContent, " $className* ptr = new (NotNull, JSC::allocateCell<$className>(vm.heap)) ${className}(vm, structure, impl, windowShell);\n"); + push(@headerContent, " $className* ptr = new (NotNull, JSC::allocateCell<$className>(vm.heap)) ${className}(vm, structure, WTFMove(impl), windowShell);\n"); push(@headerContent, " ptr->finishCreation(vm, windowShell);\n"); - push(@headerContent, " vm.heap.addFinalizer(ptr, destroy);\n"); push(@headerContent, " return ptr;\n"); push(@headerContent, " }\n\n"); } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope")) { - push(@headerContent, " static $className* create(JSC::VM& vm, JSC::Structure* structure, PassRefPtr<$implType> impl)\n"); + push(@headerContent, " static $className* create(JSC::VM& vm, JSC::Structure* structure, Ref<$implType>&& impl, JSC::JSProxy* proxy)\n"); push(@headerContent, " {\n"); - push(@headerContent, " $className* ptr = new (NotNull, JSC::allocateCell<$className>(vm.heap)) ${className}(vm, structure, impl);\n"); - push(@headerContent, " ptr->finishCreation(vm);\n"); - push(@headerContent, " vm.heap.addFinalizer(ptr, destroy);\n"); + push(@headerContent, " $className* ptr = new (NotNull, JSC::allocateCell<$className>(vm.heap)) ${className}(vm, structure, WTFMove(impl));\n"); + push(@headerContent, " ptr->finishCreation(vm, proxy);\n"); push(@headerContent, " return ptr;\n"); push(@headerContent, " }\n\n"); - } elsif ($interface->extendedAttributes->{"MasqueradesAsUndefined"}) { - AddIncludesForTypeInHeader($implType) unless $svgPropertyOrListPropertyType; - push(@headerContent, " static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, PassRefPtr<$implType> impl)\n"); + } elsif ($interface->extendedAttributes->{MasqueradesAsUndefined}) { + AddIncludesForImplementationTypeInHeader($implType); + push(@headerContent, " static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<$implType>&& impl)\n"); push(@headerContent, " {\n"); - push(@headerContent, " globalObject->masqueradesAsUndefinedWatchpoint()->fireAll();\n"); - push(@headerContent, " $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, globalObject, impl);\n"); + push(@headerContent, " globalObject->masqueradesAsUndefinedWatchpoint()->fireAll(globalObject->vm(), \"Allocated masquerading object\");\n"); + push(@headerContent, " $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject, WTFMove(impl));\n"); push(@headerContent, " ptr->finishCreation(globalObject->vm());\n"); push(@headerContent, " return ptr;\n"); push(@headerContent, " }\n\n"); + } elsif (!NeedsImplementationClass($interface)) { + push(@headerContent, " static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject)\n"); + push(@headerContent, " {\n"); + push(@headerContent, " $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject);\n"); + push(@headerContent, " ptr->finishCreation(globalObject->vm());\n"); + push(@headerContent, " return ptr;\n"); + push(@headerContent, " }\n\n"); } else { - AddIncludesForTypeInHeader($implType) unless $svgPropertyOrListPropertyType; - push(@headerContent, " static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, PassRefPtr<$implType> impl)\n"); + AddIncludesForImplementationTypeInHeader($implType); + push(@headerContent, " static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<$implType>&& impl)\n"); push(@headerContent, " {\n"); - push(@headerContent, " $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, globalObject, impl);\n"); + push(@headerContent, " $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject, WTFMove(impl));\n"); push(@headerContent, " ptr->finishCreation(globalObject->vm());\n"); push(@headerContent, " return ptr;\n"); push(@headerContent, " }\n\n"); } - if (IsDOMGlobalObject($interface)) { - push(@headerContent, " static const bool needsDestruction = false;\n\n"); - } + push(@headerContent, " static const bool needsDestruction = false;\n\n") if IsDOMGlobalObject($interface); + + $structureFlags{"JSC::HasStaticPropertyTable"} = 1 if InstancePropertyCount($interface) > 0; # Prototype - push(@headerContent, " static JSC::JSObject* createPrototype(JSC::VM&, JSC::JSGlobalObject*);\n") unless IsDOMGlobalObject($interface); + unless (ShouldUseGlobalObjectPrototype($interface)) { + push(@headerContent, " static JSC::JSObject* createPrototype(JSC::VM&, JSC::JSGlobalObject*);\n"); + push(@headerContent, " static JSC::JSObject* prototype(JSC::VM&, JSC::JSGlobalObject*);\n"); + } + + # JSValue to implementation type + if (ShouldGenerateToWrapped($hasParent, $interface)) { + my $nativeType = GetNativeType($interface, $interface->type); - $headerTrailingIncludes{"${className}Custom.h"} = 1 if $interface->extendedAttributes->{"JSCustomHeader"}; + # FIXME: Add extended attribute for this. + my @toWrappedArguments = (); + push(@toWrappedArguments, "JSC::VM&"); + push(@toWrappedArguments, "JSC::ExecState&") if $interface->type->name eq "XPathNSResolver"; + push(@toWrappedArguments, "JSC::JSValue"); + + my $export = ""; + $export = "WEBCORE_EXPORT " if $interface->extendedAttributes->{ExportToWrappedFunction}; + push(@headerContent, " static $export$nativeType toWrapped(" . join(", ", @toWrappedArguments) . ");\n"); + } + + $headerTrailingIncludes{"${className}Custom.h"} = 1 if $interface->extendedAttributes->{JSCustomHeader}; my $namedGetterFunction = GetNamedGetterFunction($interface); my $indexedGetterFunction = GetIndexedGetterFunction($interface); - my $hasNumericIndexedGetter = $indexedGetterFunction ? $codeGenerator->IsNumericType($indexedGetterFunction->signature->type) : 0; - my $hasImpureNamedGetter = - $namedGetterFunction - || $interface->extendedAttributes->{"CustomNamedGetter"} - || $interface->extendedAttributes->{"CustomGetOwnPropertySlot"}; + my $hasNamedGetter = $namedGetterFunction + || $interface->extendedAttributes->{CustomNamedGetter}; + + my $hasComplexGetter = $indexedGetterFunction + || $interface->extendedAttributes->{JSCustomGetOwnPropertySlotAndDescriptor} + || $interface->extendedAttributes->{CustomGetOwnPropertySlot} + || $hasNamedGetter; - my $hasComplexGetter = - $indexedGetterFunction - || $interface->extendedAttributes->{"JSCustomGetOwnPropertySlotAndDescriptor"} - || $hasImpureNamedGetter; - my $hasGetter = InstanceOverridesGetOwnPropertySlot($interface); - if ($hasImpureNamedGetter) { - $structureFlags{"JSC::HasImpureGetOwnPropertySlot"} = 1; - } - if ($interface->extendedAttributes->{"NewImpurePropertyFiresWatchpoints"}) { - $structureFlags{"JSC::NewImpurePropertyFiresWatchpoints"} = 1; + if ($hasNamedGetter) { + if ($interface->extendedAttributes->{OverrideBuiltins}) { + $structureFlags{"JSC::GetOwnPropertySlotIsImpure"} = 1; + } else { + $structureFlags{"JSC::GetOwnPropertySlotIsImpureForPropertyAbsence"} = 1; + } } + $structureFlags{"JSC::NewImpurePropertyFiresWatchpoints"} = 1 if $interface->extendedAttributes->{NewImpurePropertyFiresWatchpoints}; + $structureFlags{"JSC::IsImmutablePrototypeExoticObject"} = 1 if $interface->extendedAttributes->{IsImmutablePrototypeExoticObject}; + $structureFlags{"JSC::TypeOfShouldCallGetCallData"} = 1 if $interface->extendedAttributes->{CustomCall}; # Getters if ($hasGetter) { push(@headerContent, " static bool getOwnPropertySlot(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&);\n"); - push(@headerContent, " static bool getOwnPropertySlotByIndex(JSC::JSObject*, JSC::ExecState*, unsigned propertyName, JSC::PropertySlot&);\n") if ($hasComplexGetter); - push(@headerContent, " bool getOwnPropertySlotDelegate(JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&);\n") if $interface->extendedAttributes->{"JSCustomGetOwnPropertySlotAndDescriptor"}; $structureFlags{"JSC::OverridesGetOwnPropertySlot"} = 1; - $structureFlags{"JSC::InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero"} = 1; + + if ($hasComplexGetter) { + push(@headerContent, " static bool getOwnPropertySlotByIndex(JSC::JSObject*, JSC::ExecState*, unsigned propertyName, JSC::PropertySlot&);\n"); + $structureFlags{"JSC::InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero"} = 1; + } } my $overridesPut = InstanceOverridesPutDeclaration($interface); # Getters if ($overridesPut) { - push(@headerContent, " static void put(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&);\n"); - push(@headerContent, " static void putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName, JSC::JSValue, bool shouldThrow);\n"); - push(@headerContent, " bool putDelegate(JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&);\n") if $interface->extendedAttributes->{"CustomNamedSetter"}; + push(@headerContent, " static bool put(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&);\n"); + push(@headerContent, " static bool putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName, JSC::JSValue, bool shouldThrow);\n"); } if (!$hasParent) { push(@headerContent, " static void destroy(JSC::JSCell*);\n"); - push(@headerContent, " ~${className}();\n"); } # Class info if ($interfaceName eq "Node") { - push(@headerContent, "protected:"); - push(@headerContent, " static WEBKIT_EXPORTDATA const JSC::ClassInfo s_info;\n"); - push(@headerContent, "public:"); - push(@headerContent, " static const JSC::ClassInfo* info() { return &s_info; }\n\n"); + push(@headerContent, "\n"); + push(@headerContent, "protected:\n"); + push(@headerContent, " static const JSC::ClassInfo s_info;\n"); + push(@headerContent, "public:\n"); + push(@headerContent, " static constexpr const JSC::ClassInfo* info() { return &s_info; }\n\n"); } else { + push(@headerContent, "\n"); push(@headerContent, " DECLARE_INFO;\n\n"); } + # Structure ID - if ($interfaceName eq "DOMWindow") { - $structureFlags{"JSC::ImplementsHasInstance"} = 1; - } + $structureFlags{"JSC::ImplementsHasInstance | JSC::ImplementsDefaultHasInstance"} = 1 if $interfaceName eq "DOMWindow"; push(@headerContent, " static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)\n"); push(@headerContent, " {\n"); if (IsDOMGlobalObject($interface)) { push(@headerContent, " return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::GlobalObjectType, StructureFlags), info());\n"); + } elsif ($codeGenerator->InheritsInterface($interface, "Node")) { + my $type = GetJSTypeForNode($interface); + push(@headerContent, " return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSType($type), StructureFlags), info());\n"); + } elsif ($codeGenerator->InheritsInterface($interface, "Event")) { + push(@headerContent, " return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSType(JSEventType), StructureFlags), info());\n"); } else { push(@headerContent, " return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());\n"); } push(@headerContent, " }\n\n"); # Custom pushEventHandlerScope function - push(@headerContent, " JSC::JSScope* pushEventHandlerScope(JSC::ExecState*, JSC::JSScope*) const;\n\n") if $interface->extendedAttributes->{"JSCustomPushEventHandlerScope"}; + push(@headerContent, " JSC::JSScope* pushEventHandlerScope(JSC::ExecState*, JSC::JSScope*) const;\n\n") if $interface->extendedAttributes->{JSCustomPushEventHandlerScope}; # Custom call functions - push(@headerContent, " static JSC::CallType getCallData(JSC::JSCell*, JSC::CallData&);\n\n") if $interface->extendedAttributes->{"CustomCall"}; + push(@headerContent, " static JSC::CallType getCallData(JSC::JSCell*, JSC::CallData&);\n\n") if $interface->extendedAttributes->{CustomCall}; # Custom deleteProperty function - push(@headerContent, " static bool deleteProperty(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName);\n") if $interface->extendedAttributes->{"CustomDeleteProperty"}; - push(@headerContent, " static bool deletePropertyByIndex(JSC::JSCell*, JSC::ExecState*, unsigned);\n") if $interface->extendedAttributes->{"CustomDeleteProperty"}; + push(@headerContent, " static bool deleteProperty(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName);\n") if $interface->extendedAttributes->{CustomDeleteProperty}; + push(@headerContent, " static bool deletePropertyByIndex(JSC::JSCell*, JSC::ExecState*, unsigned);\n") if $interface->extendedAttributes->{CustomDeleteProperty}; # Custom getPropertyNames function exists on DOMWindow if ($interfaceName eq "DOMWindow") { - push(@headerContent, " static void getPropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode mode = JSC::ExcludeDontEnumProperties);\n"); + push(@headerContent, " static void getPropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode = JSC::EnumerationMode());\n"); + push(@headerContent, " static void getGenericPropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode = JSC::EnumerationMode());\n"); + push(@headerContent, " static void getStructurePropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode = JSC::EnumerationMode());\n"); + push(@headerContent, " static uint32_t getEnumerableLength(JSC::ExecState*, JSC::JSObject*);\n"); $structureFlags{"JSC::OverridesGetPropertyNames"} = 1; } # Custom getOwnPropertyNames function - if ($interface->extendedAttributes->{"CustomEnumerateProperty"} || $indexedGetterFunction) { - push(@headerContent, " static void getOwnPropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode mode = JSC::ExcludeDontEnumProperties);\n"); + if ($interface->extendedAttributes->{CustomEnumerateProperty} || $indexedGetterFunction || $namedGetterFunction) { + push(@headerContent, " static void getOwnPropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode = JSC::EnumerationMode());\n"); $structureFlags{"JSC::OverridesGetPropertyNames"} = 1; } # Custom defineOwnProperty function - push(@headerContent, " static bool defineOwnProperty(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, const JSC::PropertyDescriptor&, bool shouldThrow);\n") if $interface->extendedAttributes->{"JSCustomDefineOwnProperty"}; + push(@headerContent, " static bool defineOwnProperty(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, const JSC::PropertyDescriptor&, bool shouldThrow);\n") if $interface->extendedAttributes->{JSCustomDefineOwnProperty}; - # Override toBoolean to return false for objects that want to 'MasqueradesAsUndefined'. - if ($interface->extendedAttributes->{"MasqueradesAsUndefined"}) { - $structureFlags{"JSC::MasqueradesAsUndefined"} = 1; - } + # Custom getPrototype / setPrototype functions. + push (@headerContent, " static JSC::JSValue getPrototype(JSC::JSObject*, JSC::ExecState*);\n") if $interface->extendedAttributes->{CustomGetPrototype}; + push (@headerContent, " static bool setPrototype(JSC::JSObject*, JSC::ExecState*, JSC::JSValue, bool shouldThrowIfCantSet);\n") if $interface->extendedAttributes->{CustomSetPrototype}; + + # Custom toStringName function. + push (@headerContent, " static String toStringName(const JSC::JSObject*, JSC::ExecState*);\n") if $interface->extendedAttributes->{CustomToStringName}; + + # Custom preventExtensions function. + push(@headerContent, " static bool preventExtensions(JSC::JSObject*, JSC::ExecState*);\n") if $interface->extendedAttributes->{CustomPreventExtensions}; + + $structureFlags{"JSC::MasqueradesAsUndefined"} = 1 if $interface->extendedAttributes->{MasqueradesAsUndefined}; # Constructor object getter - unless ($interface->extendedAttributes->{"NoInterfaceObject"}) { - push(@headerContent, " static JSC::JSValue getConstructor(JSC::VM&, JSC::JSGlobalObject*);\n"); - push(@headerContent, " static JSC::JSValue getNamedConstructor(JSC::VM&, JSC::JSGlobalObject*);\n") if $interface->extendedAttributes->{"NamedConstructor"}; + unless ($interface->extendedAttributes->{NoInterfaceObject}) { + push(@headerContent, " static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*);\n"); + push(@headerContent, " static JSC::JSValue getNamedConstructor(JSC::VM&, JSC::JSGlobalObject*);\n") if $interface->extendedAttributes->{NamedConstructor}; } + # Serializer function. + push(@headerContent, " static JSC::JSObject* serialize(JSC::ExecState*, JS${interfaceName}* thisObject, JSC::ThrowScope&);\n") if $interface->serializable; + my $numCustomFunctions = 0; my $numCustomAttributes = 0; + my $hasForwardDeclaringFunctions = 0; + my $hasForwardDeclaringAttributes = 0; + + my $hasDOMJITAttributes = 0; + # Attribute and function enums if ($numAttributes > 0) { foreach (@{$interface->attributes}) { my $attribute = $_; - $numCustomAttributes++ if HasCustomGetter($attribute->signature->extendedAttributes); - $numCustomAttributes++ if HasCustomSetter($attribute->signature->extendedAttributes); - if ($attribute->signature->extendedAttributes->{"CachedAttribute"}) { - my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); + $numCustomAttributes++ if HasCustomGetter($attribute->extendedAttributes); + $numCustomAttributes++ if HasCustomSetter($attribute->extendedAttributes); + if ($attribute->extendedAttributes->{CachedAttribute}) { + my $conditionalString = $codeGenerator->GenerateConditionalString($attribute); push(@headerContent, "#if ${conditionalString}\n") if $conditionalString; - push(@headerContent, " JSC::WriteBarrier<JSC::Unknown> m_" . $attribute->signature->name . ";\n"); + push(@headerContent, " mutable JSC::WriteBarrier<JSC::Unknown> m_" . $attribute->name . ";\n"); $numCachedAttributes++; - $needsMarkChildren = 1; + $needsVisitChildren = 1; push(@headerContent, "#endif\n") if $conditionalString; } + $hasDOMJITAttributes = 1 if $attribute->extendedAttributes->{"DOMJIT"}; + + $hasForwardDeclaringAttributes = 1 if $attribute->extendedAttributes->{ForwardDeclareInHeader}; } } # visit function - if ($needsMarkChildren) { - push(@headerContent, " static void visitChildren(JSCell*, JSC::SlotVisitor&);\n\n"); - $structureFlags{"JSC::OverridesVisitChildren"} = 1; + if ($needsVisitChildren) { + push(@headerContent, " static void visitChildren(JSCell*, JSC::SlotVisitor&);\n"); + push(@headerContent, " void visitAdditionalChildren(JSC::SlotVisitor&);\n") if $interface->extendedAttributes->{JSCustomMarkFunction}; + push(@headerContent, "\n"); + + if ($interface->extendedAttributes->{JSCustomMarkFunction}) { + # We assume that the logic in visitAdditionalChildren is highly volatile, and during a + # concurrent GC or in between eden GCs something may happen that would lead to this + # logic behaving differently. Since this could mark objects or add opaque roots, this + # means that after any increment of mutator resumption in a concurrent GC and at least + # once during any eden GC we need to re-execute visitAdditionalChildren on any objects + # that we had executed it on before. We do this using the DOM's own MarkingConstraint, + # which will call visitOutputConstraints on all objects in the DOM's own + # outputConstraintSubspace. visitOutputConstraints is the name JSC uses for the method + # that the GC calls to ask an object is it would like to mark anything else after the + # program resumed since the last call to visitChildren or visitOutputConstraints. Since + # this just calls visitAdditionalChildren, you usually don't have to worry about this. + push(@headerContent, " static void visitOutputConstraints(JSCell*, JSC::SlotVisitor&);\n"); + my $subspaceFunc = IsDOMGlobalObject($interface) ? "globalObjectOutputConstraintSubspaceFor" : "outputConstraintSubspaceFor"; + push(@headerContent, " template<typename> static JSC::Subspace* subspaceFor(JSC::VM& vm) { return $subspaceFunc(vm); }\n"); + } + } + + if (InstanceNeedsEstimatedSize($interface)) { + push(@headerContent, " static size_t estimatedSize(JSCell*);\n"); } if ($numCustomAttributes > 0) { push(@headerContent, "\n // Custom attributes\n"); foreach my $attribute (@{$interface->attributes}) { - my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); - if (HasCustomGetter($attribute->signature->extendedAttributes)) { + my $conditionalString = $codeGenerator->GenerateConditionalString($attribute); + if (HasCustomGetter($attribute->extendedAttributes)) { push(@headerContent, "#if ${conditionalString}\n") if $conditionalString; - my $methodName = $codeGenerator->WK_lcfirst($attribute->signature->name); - push(@headerContent, " JSC::JSValue " . $methodName . "(JSC::ExecState*) const;\n"); + my $methodName = $codeGenerator->WK_lcfirst($attribute->name); + push(@headerContent, " JSC::JSValue " . $methodName . "(JSC::ExecState&) const;\n"); push(@headerContent, "#endif\n") if $conditionalString; } - if (HasCustomSetter($attribute->signature->extendedAttributes) && !IsReadonly($attribute)) { + if (HasCustomSetter($attribute->extendedAttributes) && !IsReadonly($attribute)) { push(@headerContent, "#if ${conditionalString}\n") if $conditionalString; - push(@headerContent, " void set" . $codeGenerator->WK_ucfirst($attribute->signature->name) . "(JSC::ExecState*, JSC::JSValue);\n"); + push(@headerContent, " void set" . $codeGenerator->WK_ucfirst($attribute->name) . "(JSC::ExecState&, JSC::JSValue);\n"); push(@headerContent, "#endif\n") if $conditionalString; } } } foreach my $function (@{$interface->functions}) { - $numCustomFunctions++ if HasCustomMethod($function->signature->extendedAttributes); + $numCustomFunctions++ if HasCustomMethod($function->extendedAttributes); + $hasForwardDeclaringFunctions = 1 if $function->extendedAttributes->{ForwardDeclareInHeader}; } if ($numCustomFunctions > 0) { my $inAppleCopyright = 0; push(@headerContent, "\n // Custom functions\n"); foreach my $function (@{$interface->functions}) { - # PLATFORM_IOS - my $needsAppleCopyright = $function->signature->extendedAttributes->{"AppleCopyright"}; - if ($needsAppleCopyright) { + if ($function->extendedAttributes->{AppleCopyright}) { if (!$inAppleCopyright) { push(@headerContent, $beginAppleCopyrightForHeaderFiles); $inAppleCopyright = 1; @@ -927,88 +2030,83 @@ sub GenerateHeader push(@headerContent, $endAppleCopyright); $inAppleCopyright = 0; } - # end PLATFORM_IOS - next unless HasCustomMethod($function->signature->extendedAttributes); + next unless HasCustomMethod($function->extendedAttributes); next if $function->{overloads} && $function->{overloadIndex} != 1; - my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); + my $conditionalString = $codeGenerator->GenerateConditionalString($function); push(@headerContent, "#if ${conditionalString}\n") if $conditionalString; - my $functionImplementationName = $function->signature->extendedAttributes->{"ImplementedAs"} || $codeGenerator->WK_lcfirst($function->signature->name); - push(@headerContent, " " . ($function->isStatic ? "static " : "") . "JSC::JSValue " . $functionImplementationName . "(JSC::ExecState*);\n"); + my $functionImplementationName = $function->extendedAttributes->{ImplementedAs} || $codeGenerator->WK_lcfirst($function->name); + push(@headerContent, " " . ($function->isStatic ? "static " : "") . "JSC::JSValue " . $functionImplementationName . "(JSC::ExecState&);\n"); push(@headerContent, "#endif\n") if $conditionalString; } push(@headerContent, $endAppleCopyright) if $inAppleCopyright; } - if (!$hasParent) { - push(@headerContent, " $implType& impl() const { return *m_impl; }\n"); - push(@headerContent, " void releaseImpl() { m_impl->deref(); m_impl = 0; }\n\n"); - push(@headerContent, " void releaseImplIfNotNull()\n"); - push(@headerContent, " {\n"); - push(@headerContent, " if (m_impl) {\n"); - push(@headerContent, " m_impl->deref();\n"); - push(@headerContent, " m_impl = 0;\n"); - push(@headerContent, " }\n"); - push(@headerContent, " }\n\n"); - push(@headerContent, "private:\n"); - push(@headerContent, " $implType* m_impl;\n"); - } else { - push(@headerContent, " $interfaceName& impl() const\n"); - push(@headerContent, " {\n"); - push(@headerContent, " return static_cast<$interfaceName&>(Base::impl());\n"); - push(@headerContent, " }\n"); + if (NeedsImplementationClass($interface)) { + if ($hasParent) { + push(@headerContent, " $interfaceName& wrapped() const\n"); + push(@headerContent, " {\n"); + push(@headerContent, " return static_cast<$interfaceName&>(Base::wrapped());\n"); + push(@headerContent, " }\n"); + } + } + + # structure flags + if (%structureFlags) { + push(@headerContent, "public:\n"); + push(@headerContent, " static const unsigned StructureFlags = "); + foreach my $structureFlag (sort (keys %structureFlags)) { + push(@headerContent, $structureFlag . " | "); + } + push(@headerContent, "Base::StructureFlags;\n"); } push(@headerContent, "protected:\n"); + # Constructor if ($interfaceName eq "DOMWindow") { - push(@headerContent, " $className(JSC::VM&, JSC::Structure*, PassRefPtr<$implType>, JSDOMWindowShell*);\n"); + push(@headerContent, " $className(JSC::VM&, JSC::Structure*, Ref<$implType>&&, JSDOMWindowShell*);\n"); + } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope")) { + push(@headerContent, " $className(JSC::VM&, JSC::Structure*, Ref<$implType>&&);\n"); + } elsif (!NeedsImplementationClass($interface)) { + push(@headerContent, " $className(JSC::Structure*, JSDOMGlobalObject&);\n\n"); + } else { + push(@headerContent, " $className(JSC::Structure*, JSDOMGlobalObject&, Ref<$implType>&&);\n\n"); + } + + if ($interfaceName eq "DOMWindow") { + push(@headerContent, " void finishCreation(JSC::VM&, JSDOMWindowShell*);\n"); } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope")) { - push(@headerContent, " $className(JSC::VM&, JSC::Structure*, PassRefPtr<$implType>);\n"); + push(@headerContent, " void finishCreation(JSC::VM&, JSC::JSProxy*);\n"); } else { - push(@headerContent, " $className(JSC::Structure*, JSDOMGlobalObject*, PassRefPtr<$implType>);\n"); push(@headerContent, " void finishCreation(JSC::VM&);\n"); } - # structure flags - push(@headerContent, " static const unsigned StructureFlags = "); - foreach my $structureFlag (sort (keys %structureFlags)) { - push(@headerContent, $structureFlag . " | "); + if ($interface->extendedAttributes->{JSCustomGetOwnPropertySlotAndDescriptor}) { + push(@headerContent, " bool getOwnPropertySlotDelegate(JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&);\n"); } - push(@headerContent, "Base::StructureFlags;\n"); - # Index getter - if ($indexedGetterFunction) { - if ($hasNumericIndexedGetter) { - push(@headerContent, " JSC::JSValue getByIndex(JSC::ExecState*, unsigned index);\n"); - } else { - push(@headerContent, " static JSC::EncodedJSValue indexGetter(JSC::ExecState*, JSC::EncodedJSValue, JSC::EncodedJSValue, unsigned);\n"); - } + if ($interface->extendedAttributes->{CustomNamedGetter}) { + push(@headerContent, " bool nameGetter(JSC::ExecState*, JSC::PropertyName, JSC::JSValue&);\n"); } - # Index setter - if ($interface->extendedAttributes->{"CustomIndexedSetter"}) { - push(@headerContent, " void indexSetter(JSC::ExecState*, unsigned index, JSC::JSValue);\n"); + if ($interface->extendedAttributes->{CustomNamedSetter}) { + push(@headerContent, " bool putDelegate(JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&, bool& putResult);\n"); } - # Name getter - if ($namedGetterFunction || $interface->extendedAttributes->{"CustomNamedGetter"}) { - push(@headerContent, "private:\n"); - push(@headerContent, " static bool canGetItemsForName(JSC::ExecState*, $interfaceName*, JSC::PropertyName);\n"); - push(@headerContent, " static JSC::EncodedJSValue nameGetter(JSC::ExecState*, JSC::EncodedJSValue, JSC::EncodedJSValue, JSC::PropertyName);\n"); + + if ($interface->extendedAttributes->{CustomIndexedSetter}) { + push(@headerContent, " void indexSetter(JSC::ExecState*, unsigned index, JSC::JSValue);\n"); } push(@headerContent, "};\n\n"); - if (!$hasParent || - GetGenerateIsReachable($interface) || - GetCustomIsReachable($interface) || - $interface->extendedAttributes->{"JSCustomFinalize"} || - $codeGenerator->InheritsExtendedAttribute($interface, "ActiveDOMObject")) { + if (ShouldGenerateWrapperOwnerCode($hasParent, $interface)) { if ($interfaceName ne "Node" && $codeGenerator->InheritsInterface($interface, "Node")) { $headerIncludes{"JSNode.h"} = 1; push(@headerContent, "class JS${interfaceName}Owner : public JSNodeOwner {\n"); } else { push(@headerContent, "class JS${interfaceName}Owner : public JSC::WeakHandleOwner {\n"); } + $headerIncludes{"<wtf/NeverDestroyed.h>"} = 1; push(@headerContent, "public:\n"); push(@headerContent, " virtual bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor&);\n"); push(@headerContent, " virtual void finalize(JSC::Handle<JSC::Unknown>, void* context);\n"); @@ -1016,99 +2114,42 @@ sub GenerateHeader push(@headerContent, "\n"); push(@headerContent, "inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld&, $implType*)\n"); push(@headerContent, "{\n"); - push(@headerContent, " DEFINE_STATIC_LOCAL(JS${interfaceName}Owner, js${interfaceName}Owner, ());\n"); - push(@headerContent, " return &js${interfaceName}Owner;\n"); + push(@headerContent, " static NeverDestroyed<JS${interfaceName}Owner> owner;\n"); + push(@headerContent, " return &owner.get();\n"); push(@headerContent, "}\n"); push(@headerContent, "\n"); - push(@headerContent, "inline void* wrapperContext(DOMWrapperWorld& world, $implType*)\n"); + push(@headerContent, "inline void* wrapperKey($implType* wrappableObject)\n"); push(@headerContent, "{\n"); - push(@headerContent, " return &world;\n"); + push(@headerContent, " return wrappableObject;\n"); push(@headerContent, "}\n"); push(@headerContent, "\n"); } if (ShouldGenerateToJSDeclaration($hasParent, $interface)) { - push(@headerContent, "JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, $implType*);\n"); - } - if (!$hasParent || $interface->extendedAttributes->{"JSGenerateToNativeObject"}) { - if ($interfaceName eq "NodeFilter") { - push(@headerContent, "PassRefPtr<NodeFilter> toNodeFilter(JSC::VM&, JSC::JSValue);\n"); - } elsif ($interfaceName eq "DOMStringList") { - push(@headerContent, "PassRefPtr<DOMStringList> toDOMStringList(JSC::ExecState*, JSC::JSValue);\n"); + # Node and NodeList have custom inline implementations which thus cannot be exported. + # FIXME: The special case for Node and NodeList should probably be implemented via an IDL attribute. + if ($implType eq "Node" or $implType eq "NodeList") { + push(@headerContent, "JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, $implType&);\n"); } else { - push(@headerContent, "$implType* to${interfaceName}(JSC::JSValue);\n"); + push(@headerContent, $exportMacro."JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, $implType&);\n"); } - } - if ($usesToJSNewlyCreated{$interfaceName}) { - push(@headerContent, "JSC::JSValue toJSNewlyCreated(JSC::ExecState*, JSDOMGlobalObject*, $interfaceName*);\n"); - } - - push(@headerContent, "\n"); - - # Add prototype declaration. - %structureFlags = (); - push(@headerContent, "class ${className}Prototype : public JSC::JSNonFinalObject {\n"); - push(@headerContent, "public:\n"); - push(@headerContent, " typedef JSC::JSNonFinalObject Base;\n"); - unless (IsDOMGlobalObject($interface)) { - push(@headerContent, " static JSC::JSObject* self(JSC::VM&, JSC::JSGlobalObject*);\n"); - } - - push(@headerContent, " static ${className}Prototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)\n"); - push(@headerContent, " {\n"); - push(@headerContent, " ${className}Prototype* ptr = new (NotNull, JSC::allocateCell<${className}Prototype>(vm.heap)) ${className}Prototype(vm, globalObject, structure);\n"); - push(@headerContent, " ptr->finishCreation(vm);\n"); - push(@headerContent, " return ptr;\n"); - push(@headerContent, " }\n\n"); - - push(@headerContent, " DECLARE_INFO;\n"); - if (PrototypeOverridesGetOwnPropertySlot($interface)) { - push(@headerContent, " static bool getOwnPropertySlot(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&);\n"); - $structureFlags{"JSC::OverridesGetOwnPropertySlot"} = 1; - } - if ($interface->extendedAttributes->{"JSCustomMarkFunction"} or $needsMarkChildren) { - $structureFlags{"JSC::OverridesVisitChildren"} = 1; - } - push(@headerContent, - " static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)\n" . - " {\n" . - " return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());\n" . - " }\n"); - if ($interface->extendedAttributes->{"JSCustomNamedGetterOnPrototype"}) { - push(@headerContent, " static void put(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&);\n"); - push(@headerContent, " bool putDelegate(JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&);\n"); - } + push(@headerContent, "inline JSC::JSValue toJS(JSC::ExecState* state, JSDOMGlobalObject* globalObject, $implType* impl) { return impl ? toJS(state, globalObject, *impl) : JSC::jsNull(); }\n"); - # Custom defineOwnProperty function - push(@headerContent, " static bool defineOwnProperty(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, const JSC::PropertyDescriptor&, bool shouldThrow);\n") if $interface->extendedAttributes->{"JSCustomDefineOwnPropertyOnPrototype"}; - - push(@headerContent, "\nprivate:\n"); - push(@headerContent, " ${className}Prototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure) : JSC::JSNonFinalObject(vm, structure) { }\n"); - - # structure flags - push(@headerContent, "protected:\n"); - push(@headerContent, " static const unsigned StructureFlags = "); - foreach my $structureFlag (sort (keys %structureFlags)) { - push(@headerContent, $structureFlag . " | "); - } - push(@headerContent, "Base::StructureFlags;\n"); + push(@headerContent, "JSC::JSValue toJSNewlyCreated(JSC::ExecState*, JSDOMGlobalObject*, Ref<$implType>&&);\n"); + push(@headerContent, "inline JSC::JSValue toJSNewlyCreated(JSC::ExecState* state, JSDOMGlobalObject* globalObject, RefPtr<$implType>&& impl) { return impl ? toJSNewlyCreated(state, globalObject, impl.releaseNonNull()) : JSC::jsNull(); }\n"); + } - push(@headerContent, "};\n\n"); + push(@headerContent, "\n"); - if (!$interface->extendedAttributes->{"NoInterfaceObject"}) { - $headerIncludes{"JSDOMBinding.h"} = 1; - if ($interface->extendedAttributes->{"NamedConstructor"}) { - $headerIncludes{"DOMConstructorWithDocument.h"} = 1; - } - GenerateConstructorDeclaration(\@headerContent, $className, $interface, $interfaceName); - } + GeneratePrototypeDeclaration(\@headerContent, $className, $interface) if HeaderNeedsPrototypeDeclaration($interface); - if ($numFunctions > 0) { + if ($hasForwardDeclaringFunctions) { my $inAppleCopyright = 0; push(@headerContent,"// Functions\n\n"); foreach my $function (@{$interface->functions}) { next if $function->{overloadIndex} && $function->{overloadIndex} > 1; - my $needsAppleCopyright = $function->signature->extendedAttributes->{"AppleCopyright"}; - if ($needsAppleCopyright) { + next unless $function->extendedAttributes->{ForwardDeclareInHeader}; + + if ($function->extendedAttributes->{AppleCopyright}) { if (!$inAppleCopyright) { push(@headerContent, $beginAppleCopyrightForHeaderFiles); $inAppleCopyright = 1; @@ -1118,296 +2159,619 @@ sub GenerateHeader $inAppleCopyright = 0; } - my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); + my $conditionalAttribute = getConditionalForFunctionConsideringOverloads($function); + my $conditionalString = $conditionalAttribute ? $codeGenerator->GenerateConditionalStringFromAttributeValue($conditionalAttribute) : undef; push(@headerContent, "#if ${conditionalString}\n") if $conditionalString; - my $functionName = GetFunctionName($className, $function); + my $functionName = GetFunctionName($interface, $className, $function); push(@headerContent, "JSC::EncodedJSValue JSC_HOST_CALL ${functionName}(JSC::ExecState*);\n"); push(@headerContent, "#endif\n") if $conditionalString; } push(@headerContent, $endAppleCopyright) if $inAppleCopyright; + push(@headerContent,"\n"); } - if ($numAttributes > 0 || !$interface->extendedAttributes->{"NoInterfaceObject"}) { + if ($hasForwardDeclaringAttributes) { push(@headerContent,"// Attributes\n\n"); foreach my $attribute (@{$interface->attributes}) { - my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); + next unless $attribute->extendedAttributes->{ForwardDeclareInHeader}; + + my $conditionalString = $codeGenerator->GenerateConditionalString($attribute); push(@headerContent, "#if ${conditionalString}\n") if $conditionalString; - my $getter = GetAttributeGetterName($interfaceName, $className, $attribute); - push(@headerContent, "JSC::EncodedJSValue ${getter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::EncodedJSValue, JSC::PropertyName);\n"); + my $getter = GetAttributeGetterName($interface, $className, $attribute); + push(@headerContent, "JSC::EncodedJSValue ${getter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::PropertyName);\n"); if (!IsReadonly($attribute)) { - my $setter = GetAttributeSetterName($interfaceName, $className, $attribute); - push(@headerContent, "void ${setter}(JSC::ExecState*, JSC::JSObject*, JSC::EncodedJSValue, JSC::EncodedJSValue);\n"); + my $setter = GetAttributeSetterName($interface, $className, $attribute); + push(@headerContent, "bool ${setter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::EncodedJSValue);\n"); } push(@headerContent, "#endif\n") if $conditionalString; } - - if (!$interface->extendedAttributes->{"NoInterfaceObject"}) { - my $getter = "js" . $interfaceName . "Constructor"; - push(@headerContent, "JSC::EncodedJSValue ${getter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::EncodedJSValue, JSC::PropertyName);\n"); - } + } - if ($interface->extendedAttributes->{"ReplaceableConstructor"}) { - my $constructorFunctionName = "setJS" . $interfaceName . "Constructor"; - push(@headerContent, "void ${constructorFunctionName}(JSC::ExecState*, JSC::JSObject*, JSC::EncodedJSValue, JSC::EncodedJSValue);\n"); + if ($hasDOMJITAttributes) { + $headerIncludes{"<domjit/DOMJITGetterSetter.h>"} = 1; + push(@headerContent,"// DOMJIT emitters for attributes\n\n"); + foreach my $attribute (@{$interface->attributes}) { + next unless $attribute->extendedAttributes->{"DOMJIT"}; + assert("Only DOMJIT=Getter is supported for attributes") unless $codeGenerator->ExtendedAttributeContains($attribute->extendedAttributes->{DOMJIT}, "Getter"); + + my $interfaceName = $interface->type->name; + my $className = $interfaceName . $codeGenerator->WK_ucfirst($attribute->name); + my $domJITClassName = $className . "DOMJIT"; + + push(@headerContent, "JSC::DOMJIT::GetterSetter* domJITGetterSetterFor$className(void);\n"); + + push(@headerContent, "class ${domJITClassName} : public JSC::DOMJIT::GetterSetter {\n"); + push(@headerContent, "public:\n"); + push(@headerContent, " ${domJITClassName}();\n"); + push(@headerContent, "#if ENABLE(JIT)\n"); + push(@headerContent, " Ref<JSC::DOMJIT::Patchpoint> checkDOM() override;\n"); + push(@headerContent, " Ref<JSC::DOMJIT::CallDOMGetterPatchpoint> callDOMGetter() override;\n"); + push(@headerContent, "#endif\n"); + push(@headerContent, "};\n\n"); } } - if ($numConstants > 0) { - push(@headerContent,"// Constants\n\n"); - foreach my $constant (@{$interface->constants}) { - my $conditionalString = $codeGenerator->GenerateConditionalString($constant); - push(@headerContent, "#if ${conditionalString}\n") if $conditionalString; - my $getter = "js" . $interfaceName . $codeGenerator->WK_ucfirst($constant->name); - push(@headerContent, "JSC::EncodedJSValue ${getter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::EncodedJSValue, JSC::PropertyName);\n"); - push(@headerContent, "#endif\n") if $conditionalString; - } + if (HasCustomConstructor($interface)) { + push(@headerContent, "// Custom constructor\n"); + push(@headerContent, "JSC::EncodedJSValue JSC_HOST_CALL construct${className}(JSC::ExecState&);\n\n"); } + if (NeedsImplementationClass($interface)) { + push(@headerContent, "template<> struct JSDOMWrapperConverterTraits<${implType}> {\n"); + push(@headerContent, " using WrapperClass = ${className};\n"); + push(@headerContent, " using ToWrappedReturnType = " . GetNativeType($interface, $interface->type) . ";\n"); + push(@headerContent, "};\n"); + } + + push(@headerContent, GenerateEnumerationsHeaderContent($interface, $enumerations)); + push(@headerContent, GenerateDictionariesHeaderContent($interface, $dictionaries)); + my $conditionalString = $codeGenerator->GenerateConditionalString($interface); - push(@headerContent, "\n} // namespace WebCore\n\n"); - push(@headerContent, "#endif // ${conditionalString}\n\n") if $conditionalString; - push(@headerContent, "#endif\n"); + push(@headerContent, "\n} // namespace WebCore\n"); + push(@headerContent, "\n#endif // ${conditionalString}\n") if $conditionalString; - if ($interface->extendedAttributes->{"AppleCopyright"}) { + if ($interface->extendedAttributes->{AppleCopyright}) { + push(@headerContent, "\n"); push(@headerContent, split("\r", $endAppleCopyright)); } + + # - Generate dependencies. + if ($writeDependencies) { + my @ancestors; + $codeGenerator->ForAllParents($interface, sub { + my $currentInterface = shift; + push(@ancestors, $currentInterface->type->name); + }, 0); + for my $dictionary (@$dictionaries) { + my $parentType = $dictionary->parentType; + while (defined($parentType)) { + push(@ancestors, $parentType->name) if $codeGenerator->IsExternalDictionaryType($parentType); + my $parentDictionary = $codeGenerator->GetDictionaryByType($parentType); + assert("Unable to find definition for dictionary named '" . $parentType->name . "'!") unless defined($parentDictionary); + $parentType = $parentDictionary->parentType; + } + } + push(@depsContent, "$className.h : ", join(" ", map { "$_.idl" } @ancestors), "\n"); + push(@depsContent, map { "$_.idl :\n" } @ancestors); + } } -sub GenerateAttributesHashTable($$) +sub GeneratePropertiesHashTable { - my ($object, $interface) = @_; + my ($object, $interface, $isInstance, $hashKeys, $hashSpecials, $hashValue1, $hashValue2, $conditionals, $runtimeEnabledFunctions, $runtimeEnabledAttributes) = @_; # FIXME: These should be functions on $interface. - my $interfaceName = $interface->name; + my $interfaceName = $interface->type->name; my $className = "JS$interfaceName"; - # - Add all attributes in a hashtable definition - my $numAttributes = @{$interface->attributes}; - $numAttributes++ if !$interface->extendedAttributes->{"NoInterfaceObject"}; + # - Add all properties in a hashtable definition + my $propertyCount = $isInstance ? InstancePropertyCount($interface) : PrototypePropertyCount($interface); - return 0 if !$numAttributes; - - my $hashSize = $numAttributes; - my $hashName = $className . "Table"; + if (!$isInstance && NeedsConstructorProperty($interface)) { + die if !$propertyCount; + push(@$hashKeys, "constructor"); + my $getter = "js" . $interfaceName . "Constructor"; + push(@$hashValue1, $getter); - my @hashKeys = (); - my @hashSpecials = (); - my @hashValue1 = (); - my @hashValue2 = (); - my %conditionals = (); + my $setter = "setJS" . $interfaceName . "Constructor"; + push(@$hashValue2, $setter); + push(@$hashSpecials, "DontEnum"); + } - my @entries = (); + return 0 if !$propertyCount; foreach my $attribute (@{$interface->attributes}) { next if ($attribute->isStatic); - my $name = $attribute->signature->name; - push(@hashKeys, $name); + next if AttributeShouldBeOnInstance($interface, $attribute) != $isInstance; - my @specials = (); - # As per Web IDL specification, constructor properties on the ECMAScript global object should be - # configurable and should not be enumerable. - my $is_global_constructor = $attribute->signature->type =~ /Constructor$/; - push(@specials, "DontDelete") unless ($attribute->signature->extendedAttributes->{"Deletable"} || $is_global_constructor); - push(@specials, "DontEnum") if ($attribute->signature->extendedAttributes->{"NotEnumerable"} || $is_global_constructor); - push(@specials, "ReadOnly") if IsReadonly($attribute); - my $special = (@specials > 0) ? join(" | ", @specials) : "0"; - push(@hashSpecials, $special); + # Global objects add RuntimeEnabled attributes after creation so do not add them to the static table. + if ($isInstance && NeedsRuntimeCheck($attribute)) { + $propertyCount -= 1; + next; + } - my $getter = GetAttributeGetterName($interfaceName, $className, $attribute); - push(@hashValue1, $getter); + my $name = $attribute->name; + push(@$hashKeys, $name); - if (IsReadonly($attribute)) { - push(@hashValue2, "0"); + my $special = GetJSCAttributesForAttribute($interface, $attribute); + push(@$hashSpecials, $special); + + if ($attribute->extendedAttributes->{"DOMJIT"}) { + push(@$hashValue1, "domJITGetterSetterFor" . $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name)); + push(@$hashValue2, "0"); } else { - my $setter = GetAttributeSetterName($interfaceName, $className, $attribute); - push(@hashValue2, $setter); + my $getter = GetAttributeGetterName($interface, $className, $attribute); + push(@$hashValue1, $getter); + + if (IsReadonly($attribute)) { + push(@$hashValue2, "0"); + } else { + my $setter = GetAttributeSetterName($interface, $className, $attribute); + push(@$hashValue2, $setter); + } } - my $conditional = $attribute->signature->extendedAttributes->{"Conditional"}; - if ($conditional) { - $conditionals{$name} = $conditional; + my $conditional = $attribute->extendedAttributes->{Conditional}; + $conditionals->{$name} = $conditional if $conditional; + + if (NeedsRuntimeCheck($attribute)) { + push(@$runtimeEnabledAttributes, $attribute); } } - if (!$interface->extendedAttributes->{"NoInterfaceObject"}) { - push(@hashKeys, "constructor"); - my $getter = "js" . $interfaceName . "Constructor"; - push(@hashValue1, $getter); - if ($interface->extendedAttributes->{"ReplaceableConstructor"}) { - my $setter = "setJS" . $interfaceName . "Constructor"; - push(@hashValue2, $setter); - push(@hashSpecials, "DontEnum | DontDelete"); - } else { - push(@hashValue2, "0"); - push(@hashSpecials, "DontEnum | ReadOnly"); + my @functions = @{$interface->functions}; + push(@functions, @{$interface->iterable->functions}) if IsKeyValueIterableInterface($interface); + push(@functions, @{$interface->serializable->functions}) if $interface->serializable; + foreach my $function (@functions) { + next if ($function->extendedAttributes->{PrivateIdentifier} and not $function->extendedAttributes->{PublicIdentifier}); + next if ($function->isStatic); + next if $function->{overloadIndex} && $function->{overloadIndex} > 1; + next if OperationShouldBeOnInstance($interface, $function) != $isInstance; + next if $function->name eq "[Symbol.Iterator]"; + + # Global objects add RuntimeEnabled operations after creation so do not add them to the static table. + if ($isInstance && NeedsRuntimeCheck($function)) { + $propertyCount -= 1; + next; + } + + my $name = $function->name; + push(@$hashKeys, $name); + + my $functionName = GetFunctionName($interface, $className, $function); + push(@$hashValue1, $functionName); + + my $functionLength = GetFunctionLength($function); + + if ($function->extendedAttributes->{DOMJIT}) { + push(@$hashValue2, "&DOMJITSignatureFor" . $interface->type->name . $codeGenerator->WK_ucfirst($function->name)); + } else { + push(@$hashValue2, $functionLength); + } + + push(@$hashSpecials, ComputeFunctionSpecial($interface, $function)); + + my $conditional = getConditionalForFunctionConsideringOverloads($function); + $conditionals->{$name} = $conditional if $conditional; + + if (NeedsRuntimeCheck($function)) { + push(@$runtimeEnabledFunctions, $function); } } - $object->GenerateHashTable($hashName, $hashSize, - \@hashKeys, \@hashSpecials, - \@hashValue1, \@hashValue2, - \%conditionals); - return $numAttributes; + return $propertyCount; } -sub GenerateParametersCheckExpression +# This computes an effective overload set for a given operation / constructor, +# which represents the allowable invocations.This set is used as input for +# the Web IDL overload resolution algorithm. +# http://heycam.github.io/webidl/#dfn-effective-overload-set +sub ComputeEffectiveOverloadSet { - my $numParameters = shift; - my $function = shift; + my ($overloads) = @_; + + my %allSets; + my $addTuple = sub { + my $tuple = shift; + # The Web IDL specification uses a flat set of tuples but we use a hash where the key is the + # number of parameters and the value is the set of tuples for the given number of parameters. + my $length = scalar(@{@$tuple[1]}); + if (!exists($allSets{$length})) { + $allSets{$length} = [ $tuple ]; + } else { + push(@{$allSets{$length}}, $tuple); + } + }; - my @andExpression = (); - push(@andExpression, "argsCount == $numParameters"); - my $parameterIndex = 0; - my %usedArguments = (); - foreach my $parameter (@{$function->parameters}) { - last if $parameterIndex >= $numParameters; - my $value = "arg$parameterIndex"; - my $type = $parameter->type; - - # Only DOMString or wrapper types are checked. - # For DOMString with StrictTypeChecking only Null, Undefined and Object - # are accepted for compatibility. Otherwise, no restrictions are made to - # match the non-overloaded behavior. - # FIXME: Implement WebIDL overload resolution algorithm. - if ($codeGenerator->IsStringType($type)) { - if ($parameter->extendedAttributes->{"StrictTypeChecking"}) { - push(@andExpression, "(${value}.isUndefinedOrNull() || ${value}.isString() || ${value}.isObject())"); - $usedArguments{$parameterIndex} = 1; - } - } elsif ($codeGenerator->IsCallbackInterface($parameter->type)) { - # For Callbacks only checks if the value is null or object. - push(@andExpression, "(${value}.isNull() || ${value}.isFunction())"); - $usedArguments{$parameterIndex} = 1; - } elsif ($codeGenerator->GetArrayType($type) || $codeGenerator->GetSequenceType($type)) { - # FIXME: Add proper support for T[], T[]?, sequence<T> - if ($parameter->isNullable) { - push(@andExpression, "(${value}.isNull() || (${value}.isObject() && isJSArray(${value})))"); + my $m = LengthOfLongestFunctionParameterList($overloads); + foreach my $overload (@{$overloads}) { + my $n = @{$overload->arguments}; + my @t; + my @o; + my $isVariadic = 0; + foreach my $argument (@{$overload->arguments}) { + push(@t, $argument->type); + if ($argument->isOptional) { + push(@o, "optional"); + } elsif ($argument->isVariadic) { + push(@o, "variadic"); + $isVariadic = 1; } else { - push(@andExpression, "(${value}.isObject() && isJSArray(${value}))"); + push(@o, "required"); } - $usedArguments{$parameterIndex} = 1; - } elsif (!IsNativeType($type)) { - if ($parameter->isNullable) { - push(@andExpression, "(${value}.isNull() || (${value}.isObject() && asObject(${value})->inherits(JS${type}::info())))"); - } else { - push(@andExpression, "(${value}.isObject() && asObject(${value})->inherits(JS${type}::info()))"); + } + &$addTuple([$overload, [@t], [@o]]); + if ($isVariadic) { + my @newT = @t; + my @newO = @o; + for (my $i = $n; $i < $m; $i++) { + push(@newT, $t[-1]); + push(@newO, "variadic"); + &$addTuple([$overload, [@newT], [@newO]]); } - $usedArguments{$parameterIndex} = 1; } - $parameterIndex++; + for (my $i = $n - 1; $i >= 0; $i--) { + my $argument = @{$overload->arguments}[$i]; + last unless ($argument->isOptional || $argument->isVariadic); + pop(@t); + pop(@o); + &$addTuple([$overload, [@t], [@o]]); + } } - my $res = join(" && ", @andExpression); - $res = "($res)" if @andExpression > 1; - return ($res, sort {$a <=> $b} (keys %usedArguments)); + return %allSets; } -# As per Web IDL specification, the length of a function Object is -# its number of mandatory parameters. -sub GetFunctionLength +sub IsIDLTypeDistinguishableWithUnionForOverloadResolution { - my $function = shift; + my ($type, $unionSubtypes) = @_; - my $numMandatoryParams = 0; - foreach my $parameter (@{$function->parameters}) { - # Abort as soon as we find the first optional parameter as no mandatory - # parameter can follow an optional one. - last if $parameter->isOptional; - $numMandatoryParams++; - } - return $numMandatoryParams; + assert("First type should not be a union") if $type->isUnion; + for my $unionSubType (@$unionSubtypes) { + return 0 unless AreTypesDistinguishableForOverloadResolution($type, $unionSubType); + } + return 1; } -sub GenerateFunctionParametersCheck +# Determines if two types are distinguishable in the context of overload resolution, +# according to the Web IDL specification: +# http://heycam.github.io/webidl/#dfn-distinguishable +sub AreTypesDistinguishableForOverloadResolution { - my $function = shift; + my ($typeA, $typeB) = @_; - my @orExpression = (); - my $numParameters = 0; - my @neededArguments = (); - my $hasVariadic = 0; - my $numMandatoryParams = @{$function->parameters}; - - foreach my $parameter (@{$function->parameters}) { - if ($parameter->isOptional) { - my ($expression, @usedArguments) = GenerateParametersCheckExpression($numParameters, $function); - push(@orExpression, $expression); - push(@neededArguments, @usedArguments); - $numMandatoryParams--; + my $isDictionary = sub { + my $type = shift; + return $codeGenerator->IsDictionaryType($type); + }; + my $isCallbackFunctionOrDictionary = sub { + my $type = shift; + return $codeGenerator->IsCallbackFunction($type) || &$isDictionary($type); + }; + + # Two types are distinguishable for overload resolution if at most one of the two includes a nullable type. + return 0 if $typeA->isNullable && $typeB->isNullable; + + # Union types: typeA and typeB are distinguishable if: + # - Both types are either a union type or nullable union type, and each member type of the one is + # distinguishable with each member type of the other. + # - One type is a union type or nullable union type, the other is neither a union type nor a nullable + # union type, and each member type of the first is distinguishable with the second. + if ($typeA->isUnion && $typeB->isUnion) { + for my $unionASubType (@{$typeA->subtypes}) { + return 0 unless IsIDLTypeDistinguishableWithUnionForOverloadResolution($unionASubType, $typeB->subtypes); } - if ($parameter->isVariadic) { - $hasVariadic = 1; - last; + return 1; + } elsif ($typeA->isUnion) { + return IsIDLTypeDistinguishableWithUnionForOverloadResolution($typeB, $typeA->subtypes); + } elsif ($typeB->isUnion) { + return IsIDLTypeDistinguishableWithUnionForOverloadResolution($typeA, $typeB->subtypes); + } + + return 0 if $typeA->name eq $typeB->name; + return 0 if $typeA->name eq "object" or $typeB->name eq "object"; + return 0 if $codeGenerator->IsNumericType($typeA) && $codeGenerator->IsNumericType($typeB); + return 0 if $codeGenerator->IsStringOrEnumType($typeA) && $codeGenerator->IsStringOrEnumType($typeB); + return 0 if &$isDictionary($typeA) && &$isDictionary($typeB); + return 0 if $codeGenerator->IsCallbackInterface($typeA) && $codeGenerator->IsCallbackInterface($typeB); + return 0 if &$isCallbackFunctionOrDictionary($typeA) && &$isCallbackFunctionOrDictionary($typeB); + return 0 if $codeGenerator->IsSequenceOrFrozenArrayType($typeA) && $codeGenerator->IsSequenceOrFrozenArrayType($typeB); + # FIXME: return 0 if $typeA and $typeB are both exception types. + return 1; +} + +# If there is more than one entry in an effective overload set that has a given type list length, +# then for those entries there must be an index i such that for each pair of entries the types +# at index i are distinguishable. The lowest such index is termed the distinguishing argument index. +# http://heycam.github.io/webidl/#dfn-distinguishing-argument-index +sub GetDistinguishingArgumentIndex +{ + my ($function, $S) = @_; + + # FIXME: Consider all the tuples, not just the 2 first ones? + my $firstTupleTypes = @{@{$S}[0]}[1]; + my $secondTupleTypes = @{@{$S}[1]}[1]; + for (my $index = 0; $index < scalar(@$firstTupleTypes); $index++) { + return $index if AreTypesDistinguishableForOverloadResolution(@{$firstTupleTypes}[$index], @{$secondTupleTypes}[$index]); + } + die "Undistinguishable overloads for operation " . $function->name . " with length: " . scalar(@$firstTupleTypes); +} + +sub GetOverloadThatMatches +{ + my ($S, $parameterIndex, $matches) = @_; + + for my $tuple (@{$S}) { + my $type = @{@{$tuple}[1]}[$parameterIndex]; + my $optionality = @{@{$tuple}[2]}[$parameterIndex]; + if ($type->isUnion) { + for my $subtype (GetFlattenedMemberTypes($type)) { + return @{$tuple}[0] if $matches->($subtype, $optionality); + } + } else { + return @{$tuple}[0] if $matches->($type, $optionality); } - $numParameters++; } - if (!$hasVariadic) { - my ($expression, @usedArguments) = GenerateParametersCheckExpression($numParameters, $function); - push(@orExpression, $expression); - push(@neededArguments, @usedArguments); +} + +sub GetOverloadThatMatchesIgnoringUnionSubtypes +{ + my ($S, $parameterIndex, $matches) = @_; + + for my $tuple (@{$S}) { + my $type = @{@{$tuple}[1]}[$parameterIndex]; + my $optionality = @{@{$tuple}[2]}[$parameterIndex]; + return @{$tuple}[0] if $matches->($type, $optionality); } - return ($numMandatoryParams, join(" || ", @orExpression), @neededArguments); } -sub GenerateOverloadedFunction +sub getConditionalForFunctionConsideringOverloads { my $function = shift; - my $interface = shift; - my $interfaceName = shift; - # Generate code for choosing the correct overload to call. Overloads are - # chosen based on the total number of arguments passed and the type of - # values passed in non-primitive argument slots. When more than a single - # overload is applicable, precedence is given according to the order of - # declaration in the IDL. + return $function->extendedAttributes->{Conditional} unless $function->{overloads}; - my $kind = $function->isStatic ? "Constructor" : "Prototype"; - my $functionName = "js${interfaceName}${kind}Function" . $codeGenerator->WK_ucfirst($function->signature->name); + my %conditions; + foreach my $overload (@{$function->{overloads}}) { + my $conditional = $overload->extendedAttributes->{Conditional}; + return unless $conditional; + $conditions{$conditional} = 1; + } + return join("|", keys %conditions); +} - push(@implContent, "EncodedJSValue JSC_HOST_CALL ${functionName}(ExecState* exec)\n"); - push(@implContent, <<END); +# Implements the overload resolution algorithm, as defined in the Web IDL specification: +# http://heycam.github.io/webidl/#es-overloads +sub GenerateOverloadedFunctionOrConstructor { - size_t argsCount = exec->argumentCount(); + my ($function, $interface, $isConstructor) = @_; + my %allSets = ComputeEffectiveOverloadSet($function->{overloads}); + + my $interfaceName = $interface->type->name; + my $className = "JS$interfaceName"; + my $functionName; + if ($isConstructor) { + $functionName = "construct${className}"; + } else { + my $kind = $function->isStatic ? "Constructor" : (OperationShouldBeOnInstance($interface, $function) ? "Instance" : "Prototype"); + $functionName = "js${interfaceName}${kind}Function" . $codeGenerator->WK_ucfirst($function->name); + } + + my $generateOverloadCallIfNecessary = sub { + my ($overload, $condition, $include) = @_; + return unless $overload; + my $conditionalString = $codeGenerator->GenerateConditionalString($overload); + push(@implContent, "#if ${conditionalString}\n") if $conditionalString; + push(@implContent, " if ($condition)\n ") if $condition; + push(@implContent, " return ${functionName}$overload->{overloadIndex}(state);\n"); + push(@implContent, "#endif\n") if $conditionalString; + AddToImplIncludes($include, $overload->extendedAttributes->{"Conditional"}) if $include; + }; + my $isOptionalParameter = sub { + my ($type, $optionality) = @_; + return $optionality eq "optional"; + }; + my $isDictionaryOrRecordParameter = sub { + my ($type, $optionality) = @_; + return $codeGenerator->IsDictionaryType($type) || $codeGenerator->IsRecordType($type); + }; + my $isNullableOrDictionaryOrRecordOrUnionContainingOne = sub { + my ($type, $optionality) = @_; + return 1 if $type->isNullable; + if ($type->isUnion) { + for my $subtype (GetFlattenedMemberTypes($type)) { + return 1 if $type->isNullable || &$isDictionaryOrRecordParameter($subtype, $optionality); + } + return 0; + } else { + return &$isDictionaryOrRecordParameter($type, $optionality); + } + }; + my $isRegExpOrObjectParameter = sub { + my ($type, $optionality) = @_; + return $type->name eq "RegExp" || $type->name eq "object"; + }; + my $isObjectOrErrorParameter = sub { + my ($type, $optionality) = @_; + return $type->name eq "object" || $type->name eq "Error"; + }; + my $isObjectOrErrorOrDOMExceptionParameter = sub { + my ($type, $optionality) = @_; + return 1 if &$isObjectOrErrorParameter($type, $optionality); + return $type->name eq "DOMException"; + }; + my $isObjectOrCallbackFunctionParameter = sub { + my ($type, $optionality) = @_; + return $type->name eq "object" || $codeGenerator->IsCallbackFunction($type); + }; + my $isSequenceOrFrozenArrayParameter = sub { + my ($type, $optionality) = @_; + return $codeGenerator->IsSequenceOrFrozenArrayType($type); + }; + my $isDictionaryOrRecordOrObjectOrCallbackInterfaceParameter = sub { + my ($type, $optionality) = @_; + return 1 if &$isDictionaryOrRecordParameter($type, $optionality); + return 1 if $type->name eq "object"; + return 1 if $codeGenerator->IsCallbackInterface($type) && !$codeGenerator->IsCallbackFunction($type); + return 0; + }; + my $isBooleanParameter = sub { + my ($type, $optionality) = @_; + return $type->name eq "boolean"; + }; + my $isNumericParameter = sub { + my ($type, $optionality) = @_; + return $codeGenerator->IsNumericType($type); + }; + my $isStringOrEnumParameter = sub { + my ($type, $optionality) = @_; + return $codeGenerator->IsStringOrEnumType($type); + }; + my $isAnyParameter = sub { + my ($type, $optionality) = @_; + return $type->name eq "any"; + }; + + my $maxArgCount = LengthOfLongestFunctionParameterList($function->{overloads}); + + my $conditionalAttribute = getConditionalForFunctionConsideringOverloads($function); + my $conditionalString = $conditionalAttribute ? $codeGenerator->GenerateConditionalStringFromAttributeValue($conditionalAttribute) : undef; + push(@implContent, "#if ${conditionalString}\n") if $conditionalString; + if ($isConstructor) { + push(@implContent, "template<> EncodedJSValue JSC_HOST_CALL ${className}Constructor::construct(ExecState* state)\n"); + } else { + push(@implContent, "EncodedJSValue JSC_HOST_CALL ${functionName}(ExecState* state)\n"); + } + push(@implContent, <<END); +{ + VM& vm = state->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + UNUSED_PARAM(throwScope); + size_t argsCount = std::min<size_t>($maxArgCount, state->argumentCount()); +END + + for my $length ( sort keys %allSets ) { + push(@implContent, <<END); + if (argsCount == $length) { END + my $S = $allSets{$length}; + if (scalar(@$S) > 1) { + my $d = GetDistinguishingArgumentIndex($function, $S); + push(@implContent, " JSValue distinguishingArg = state->uncheckedArgument($d);\n"); + + my $overload = GetOverloadThatMatchesIgnoringUnionSubtypes($S, $d, \&$isOptionalParameter); + &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isUndefined()"); + + $overload = GetOverloadThatMatchesIgnoringUnionSubtypes($S, $d, \&$isNullableOrDictionaryOrRecordOrUnionContainingOne); + &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isUndefinedOrNull()"); + + for my $tuple (@{$S}) { + my $overload = @{$tuple}[0]; + my $type = @{@{$tuple}[1]}[$d]; + + my @subtypes = $type->isUnion ? GetFlattenedMemberTypes($type) : ( $type ); + for my $subtype (@subtypes) { + if ($codeGenerator->IsWrapperType($subtype) || $codeGenerator->IsTypedArrayType($subtype)) { + if ($subtype->name eq "DOMWindow") { + AddToImplIncludes("JSDOMWindowShell.h"); + &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isObject() && (asObject(distinguishingArg)->inherits(vm, JSDOMWindowShell::info()) || asObject(distinguishingArg)->inherits(vm, JSDOMWindow::info()))"); + } else { + &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isObject() && asObject(distinguishingArg)->inherits(vm, JS" . $subtype->name . "::info())"); + } + } + } + } - my %fetchedArguments = (); - my $leastNumMandatoryParams = 255; + $overload = GetOverloadThatMatches($S, $d, \&$isRegExpOrObjectParameter); + &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isObject() && asObject(distinguishingArg)->type() == RegExpObjectType"); - foreach my $overload (@{$function->{overloads}}) { - my ($numMandatoryParams, $parametersCheck, @neededArguments) = GenerateFunctionParametersCheck($overload); - $leastNumMandatoryParams = $numMandatoryParams if ($numMandatoryParams < $leastNumMandatoryParams); + $overload = GetOverloadThatMatches($S, $d, \&$isObjectOrErrorOrDOMExceptionParameter); + &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isObject() && asObject(distinguishingArg)->inherits(vm, JSDOMCoreException::info())"); - foreach my $parameterIndex (@neededArguments) { - next if exists $fetchedArguments{$parameterIndex}; - push(@implContent, " JSValue arg$parameterIndex(exec->argument($parameterIndex));\n"); - $fetchedArguments{$parameterIndex} = 1; - } + $overload = GetOverloadThatMatches($S, $d, \&$isObjectOrErrorParameter); + &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isObject() && asObject(distinguishingArg)->type() == ErrorInstanceType"); - my $conditionalString = $codeGenerator->GenerateConditionalString($overload->signature); - push(@implContent, "#if ${conditionalString}\n") if $conditionalString; + $overload = GetOverloadThatMatches($S, $d, \&$isObjectOrCallbackFunctionParameter); + &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isFunction()"); + + # FIXME: Avoid invoking GetMethod(object, Symbol.iterator) again in convert<IDLSequence<T>>(...). + $overload = GetOverloadThatMatches($S, $d, \&$isSequenceOrFrozenArrayParameter); + &$generateOverloadCallIfNecessary($overload, "hasIteratorMethod(*state, distinguishingArg)", "<runtime/IteratorOperations.h>"); + + $overload = GetOverloadThatMatches($S, $d, \&$isDictionaryOrRecordOrObjectOrCallbackInterfaceParameter); + &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isObject() && asObject(distinguishingArg)->type() != RegExpObjectType"); + + my $booleanOverload = GetOverloadThatMatches($S, $d, \&$isBooleanParameter); + &$generateOverloadCallIfNecessary($booleanOverload, "distinguishingArg.isBoolean()"); - push(@implContent, " if ($parametersCheck)\n"); - push(@implContent, " return ${functionName}$overload->{overloadIndex}(exec);\n"); - push(@implContent, "#endif\n\n") if $conditionalString; + my $numericOverload = GetOverloadThatMatches($S, $d, \&$isNumericParameter); + &$generateOverloadCallIfNecessary($numericOverload, "distinguishingArg.isNumber()"); + # Fallbacks. + $overload = GetOverloadThatMatches($S, $d, \&$isStringOrEnumParameter); + if ($overload) { + &$generateOverloadCallIfNecessary($overload); + } elsif ($numericOverload) { + &$generateOverloadCallIfNecessary($numericOverload); + } elsif ($booleanOverload) { + &$generateOverloadCallIfNecessary($booleanOverload); + } else { + $overload = GetOverloadThatMatches($S, $d, \&$isAnyParameter); + &$generateOverloadCallIfNecessary($overload); + } + } else { + # Only 1 overload with this number of parameters. + my $overload = @{@{$S}[0]}[0]; + &$generateOverloadCallIfNecessary($overload); + } + push(@implContent, <<END); + } +END } - if ($leastNumMandatoryParams >= 1) { - push(@implContent, " if (argsCount < $leastNumMandatoryParams)\n"); - push(@implContent, " return throwVMError(exec, createNotEnoughArgumentsError(exec));\n"); + my $minArgCount = GetFunctionLength($function); + if ($minArgCount > 0) { + push(@implContent, " return argsCount < $minArgCount ? throwVMError(state, throwScope, createNotEnoughArgumentsError(state)) : throwVMTypeError(state, throwScope);\n") + } else { + push(@implContent, " return throwVMTypeError(state, throwScope);\n") } - push(@implContent, <<END); - return throwVMTypeError(exec); + push(@implContent, "}\n"); + push(@implContent, "#endif\n") if $conditionalString; + push(@implContent, "\n"); } -END +# As per Web IDL specification, the length of a function Object is its number of mandatory parameters. +sub GetFunctionLength +{ + my $function = shift; + + my $getOverloadLength = sub { + my $function = shift; + + my $length = 0; + foreach my $argument (@{$function->arguments}) { + last if $argument->isOptional || $argument->isVariadic; + $length++; + } + return $length; + }; + + my $length = &$getOverloadLength($function); + foreach my $overload (@{$function->{overloads}}) { + my $newLength = &$getOverloadLength($overload); + $length = $newLength if $newLength < $length; + } + return $length; } -sub GetNativeTypeForConversions +sub LengthOfLongestFunctionParameterList { - my $interface = shift; - my $interfaceName = $interface->name; - $interfaceName = $codeGenerator->GetSVGTypeNeedingTearOff($interfaceName) if $codeGenerator->IsSVGTypeNeedingTearOff($interfaceName); - return $interfaceName; + my ($overloads) = @_; + my $result = 0; + foreach my $overload (@{$overloads}) { + my @arguments = @{$overload->arguments}; + $result = @arguments if $result < @arguments; + } + return $result; } # See http://refspecs.linux-foundation.org/cxxabi-1.83.html. @@ -1418,7 +2782,7 @@ sub GetGnuVTableRefForInterface if (!$vtableName) { return "0"; } - my $typename = GetNativeTypeForConversions($interface); + my $typename = $interface->type->name; my $offset = GetGnuVTableOffsetForType($typename); return "&" . $vtableName . "[" . $offset . "]"; } @@ -1426,7 +2790,7 @@ sub GetGnuVTableRefForInterface sub GetGnuVTableNameForInterface { my $interface = shift; - my $typename = GetNativeTypeForConversions($interface); + my $typename = $interface->type->name; my $templatePosition = index($typename, "<"); return "" if $templatePosition != -1; return "" if GetImplementationLacksVTableForInterface($interface); @@ -1437,13 +2801,13 @@ sub GetGnuVTableNameForInterface sub GetGnuMangledNameForInterface { my $interface = shift; - my $typename = GetNativeTypeForConversions($interface); + my $typename = $interface->type->name; my $templatePosition = index($typename, "<"); if ($templatePosition != -1) { return ""; } my $mangledType = length($typename) . $typename; - my $namespace = GetNamespaceForInterface($interface); + my $namespace = "WebCore"; my $mangledNamespace = "N" . length($namespace) . $namespace; return $mangledNamespace . $mangledType . "E"; } @@ -1487,7 +2851,7 @@ sub GetWinVTableRefForInterface sub GetWinVTableNameForInterface { my $interface = shift; - my $typename = GetNativeTypeForConversions($interface); + my $typename = $interface->type->name; my $templatePosition = index($typename, "<"); return "" if $templatePosition != -1; return "" if GetImplementationLacksVTableForInterface($interface); @@ -1498,27 +2862,21 @@ sub GetWinVTableNameForInterface sub GetWinMangledNameForInterface { my $interface = shift; - my $typename = GetNativeTypeForConversions($interface); - my $namespace = GetNamespaceForInterface($interface); + my $typename = $interface->type->name; + my $namespace = "WebCore"; return $typename . "@" . $namespace . "@@"; } -sub GetNamespaceForInterface -{ - my $interface = shift; - return $interface->extendedAttributes->{"ImplementationNamespace"} || "WebCore"; -} - sub GetImplementationLacksVTableForInterface { my $interface = shift; - return $interface->extendedAttributes->{"ImplementationLacksVTable"}; + return $interface->extendedAttributes->{ImplementationLacksVTable}; } sub GetSkipVTableValidationForInterface { my $interface = shift; - return $interface->extendedAttributes->{"SkipVTableValidation"}; + return $interface->extendedAttributes->{SkipVTableValidation}; } # URL becomes url, but SetURL becomes setURL. @@ -1526,12 +2884,13 @@ sub ToMethodName { my $param = shift; my $ret = lcfirst($param); + $ret =~ s/cSS/css/ if $ret =~ /^cSS/; + $ret =~ s/dOM/dom/ if $ret =~ /^dOM/; $ret =~ s/hTML/html/ if $ret =~ /^hTML/; - $ret =~ s/uRL/url/ if $ret =~ /^uRL/; $ret =~ s/jS/js/ if $ret =~ /^jS/; + $ret =~ s/uRL/url/ if $ret =~ /^uRL/; $ret =~ s/xML/xml/ if $ret =~ /^xML/; $ret =~ s/xSLT/xslt/ if $ret =~ /^xSLT/; - $ret =~ s/cSS/css/ if $ret =~ /^cSS/; # For HTML5 FileSystem API Flags attributes. # (create is widely used to instantiate an object and must be avoided.) @@ -1542,57 +2901,282 @@ sub ToMethodName } # Returns the RuntimeEnabledFeatures function name that is hooked up to check if a method/attribute is enabled. +# NOTE: Parameter passed in must have both an 'extendedAttributes' property. +# (e.g. DOMInterface, DOMAttribute, DOMOperation, DOMIterable, etc.) sub GetRuntimeEnableFunctionName { - my $signature = shift; + my $context = shift; + + AddToImplIncludes("RuntimeEnabledFeatures.h"); + + if ($context->extendedAttributes->{EnabledForWorld}) { + return "worldForDOMObject(this)." . ToMethodName($context->extendedAttributes->{EnabledForWorld}); + } # If a parameter is given (e.g. "EnabledAtRuntime=FeatureName") return the RuntimeEnabledFeatures::sharedFeatures().{FeatureName}Enabled() method. - return "RuntimeEnabledFeatures::sharedFeatures()." . ToMethodName($signature->extendedAttributes->{"EnabledAtRuntime"}) . "Enabled" if ($signature->extendedAttributes->{"EnabledAtRuntime"} && $signature->extendedAttributes->{"EnabledAtRuntime"} ne "VALUE_IS_MISSING"); + if ($context->extendedAttributes->{EnabledAtRuntime} && $context->extendedAttributes->{EnabledAtRuntime} ne "VALUE_IS_MISSING") { + return "RuntimeEnabledFeatures::sharedFeatures()." . ToMethodName($context->extendedAttributes->{EnabledAtRuntime}) . "Enabled"; + } # Otherwise return a function named RuntimeEnabledFeatures::sharedFeatures().{methodName}Enabled(). - return "RuntimeEnabledFeatures::sharedFeatures()." . ToMethodName($signature->name) . "Enabled"; + return "RuntimeEnabledFeatures::sharedFeatures()." . ToMethodName($context->name) . "Enabled"; +} + +sub GetCastingHelperForThisObject +{ + my $interface = shift; + my $interfaceName = $interface->type->name; + return "jsDynamicDowncast<JS$interfaceName*>"; +} + +# http://heycam.github.io/webidl/#Unscopable +sub addUnscopableProperties +{ + my $interface = shift; + + my @unscopables; + foreach my $functionOrAttribute (@{$interface->functions}, @{$interface->attributes}) { + push(@unscopables, $functionOrAttribute->name) if $functionOrAttribute->extendedAttributes->{Unscopable}; + } + return if scalar(@unscopables) == 0; + + AddToImplIncludes("<runtime/ObjectConstructor.h>"); + push(@implContent, " JSObject& unscopables = *constructEmptyObject(globalObject()->globalExec(), globalObject()->nullPrototypeObjectStructure());\n"); + foreach my $unscopable (@unscopables) { + push(@implContent, " unscopables.putDirect(vm, Identifier::fromString(&vm, \"$unscopable\"), jsBoolean(true));\n"); + } + push(@implContent, " putDirectWithoutTransition(vm, vm.propertyNames->unscopablesSymbol, &unscopables, DontEnum | ReadOnly);\n"); +} + +sub GetUnsafeArgumentType +{ + my ($interface, $type) = @_; + + my $IDLType = GetIDLType($interface, $type); + return "DOMJIT::IDLJSArgumentType<${IDLType}>"; +} + +sub GetArgumentTypeFilter +{ + my ($interface, $type) = @_; + + my $IDLType = GetIDLType($interface, $type); + return "DOMJIT::IDLArgumentTypeFilter<${IDLType}>::value"; +} + +sub GetResultTypeFilter +{ + my ($interface, $type) = @_; + + my $IDLType = GetIDLType($interface, $type); + return "DOMJIT::IDLResultTypeFilter<${IDLType}>::value"; +} + +sub GetAttributeWithName +{ + my ($interface, $attributeName) = @_; + + foreach my $attribute (@{$interface->attributes}) { + return $attribute if $attribute->name eq $attributeName; + } +} + +# https://heycam.github.io/webidl/#es-iterator +sub InterfaceNeedsIterator +{ + my ($interface) = @_; + + return 1 if $interface->iterable; + if (GetIndexedGetterFunction($interface)) { + my $lengthAttribute = GetAttributeWithName($interface, "length"); + return 1 if $lengthAttribute and $codeGenerator->IsIntegerType($lengthAttribute->type); + } + # FIXME: This should return 1 for maplike and setlike once we support them. + return 0; } sub GenerateImplementation { - my ($object, $interface) = @_; + my ($object, $interface, $enumerations, $dictionaries) = @_; - my $interfaceName = $interface->name; + my $interfaceName = $interface->type->name; my $className = "JS$interfaceName"; - my $hasLegacyParent = $interface->extendedAttributes->{"JSLegacyParent"}; - my $hasRealParent = $interface->parent; + my $hasLegacyParent = $interface->extendedAttributes->{JSLegacyParent}; + my $hasRealParent = $interface->parentType; my $hasParent = $hasLegacyParent || $hasRealParent; my $parentClassName = GetParentClassName($interface); my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($interface); - my $eventTarget = $interface->extendedAttributes->{"EventTarget"} || ($codeGenerator->InheritsInterface($interface, "EventTarget") && $interface->name ne "EventTarget"); - my $needsMarkChildren = $interface->extendedAttributes->{"JSCustomMarkFunction"} || $interface->extendedAttributes->{"EventTarget"} || $interface->name eq "EventTarget"; + my $eventTarget = $codeGenerator->InheritsInterface($interface, "EventTarget") && $interface->type->name ne "EventTarget"; + my $needsVisitChildren = InstanceNeedsVisitChildren($interface); my $namedGetterFunction = GetNamedGetterFunction($interface); my $indexedGetterFunction = GetIndexedGetterFunction($interface); - my $hasNumericIndexedGetter = $indexedGetterFunction ? $codeGenerator->IsNumericType($indexedGetterFunction->signature->type) : 0; # - Add default header template push(@implContentHeader, GenerateImplementationContentHeader($interface)); + $implIncludes{"JSDOMBinding.h"} = 1; + $implIncludes{"JSDOMBindingCaller.h"} = 1; + $implIncludes{"JSDOMExceptionHandling.h"} = 1; + $implIncludes{"JSDOMWrapperCache.h"} = 1; $implIncludes{"<wtf/GetPtr.h>"} = 1; $implIncludes{"<runtime/PropertyNameArray.h>"} = 1 if $indexedGetterFunction; - AddIncludesForTypeInImpl($interfaceName); + my $implType = GetImplClassName($interface); + + AddJSBuiltinIncludesIfNeeded($interface); @implContent = (); push(@implContent, "\nusing namespace JSC;\n\n"); push(@implContent, "namespace WebCore {\n\n"); - my $numAttributes = GenerateAttributesHashTable($object, $interface); + push(@implContent, GenerateEnumerationsImplementationContent($interface, $enumerations)); + push(@implContent, GenerateDictionariesImplementationContent($interface, $dictionaries)); + + my @functions = @{$interface->functions}; + push(@functions, @{$interface->iterable->functions}) if IsKeyValueIterableInterface($interface); + push(@functions, @{$interface->serializable->functions}) if $interface->serializable; my $numConstants = @{$interface->constants}; - my $numFunctions = @{$interface->functions}; + my $numFunctions = @functions; + my $numAttributes = @{$interface->attributes}; + + if ($numFunctions > 0) { + my $inAppleCopyright = 0; + push(@implContent,"// Functions\n\n"); + foreach my $function (@functions) { + next if $function->{overloadIndex} && $function->{overloadIndex} > 1; + next if $function->extendedAttributes->{ForwardDeclareInHeader}; + next if IsJSBuiltin($interface, $function); + + if ($function->extendedAttributes->{AppleCopyright}) { + if (!$inAppleCopyright) { + push(@implContent, $beginAppleCopyrightForHeaderFiles); + $inAppleCopyright = 1; + } + } elsif ($inAppleCopyright) { + push(@implContent, $endAppleCopyright); + $inAppleCopyright = 0; + } + + my $conditionalAttribute = getConditionalForFunctionConsideringOverloads($function); + my $conditionalString = $conditionalAttribute ? $codeGenerator->GenerateConditionalStringFromAttributeValue($conditionalAttribute) : undef; + push(@implContent, "#if ${conditionalString}\n") if $conditionalString; + my $functionName = GetFunctionName($interface, $className, $function); + push(@implContent, "JSC::EncodedJSValue JSC_HOST_CALL ${functionName}(JSC::ExecState*);\n"); + if ($function->extendedAttributes->{DOMJIT}) { + $implIncludes{"DOMJITIDLType.h"} = 1; + my $unsafeFunctionName = "unsafe" . $codeGenerator->WK_ucfirst($functionName); + my $functionSignature = "JSC::EncodedJSValue JIT_OPERATION ${unsafeFunctionName}(JSC::ExecState*, $className*"; + foreach my $argument (@{$function->arguments}) { + my $type = $argument->type; + my $argumentType = GetUnsafeArgumentType($interface, $type); + $functionSignature .= ", ${argumentType}"; + } + push(@implContent, $functionSignature . ");\n"); + } + push(@implContent, "#endif\n") if $conditionalString; + } + + push(@implContent, $endAppleCopyright) if $inAppleCopyright; + push(@implContent, "\n"); + } + + if ($numAttributes > 0 || NeedsConstructorProperty($interface)) { + push(@implContent, "// Attributes\n\n"); + + foreach my $attribute (@{$interface->attributes}) { + next if $attribute->extendedAttributes->{ForwardDeclareInHeader}; + next if IsJSBuiltin($interface, $attribute); - # - Add all constants - if (!$interface->extendedAttributes->{"NoInterfaceObject"}) { - my $hashSize = $numConstants; + my $conditionalString = $codeGenerator->GenerateConditionalString($attribute); + push(@implContent, "#if ${conditionalString}\n") if $conditionalString; + my $getter = GetAttributeGetterName($interface, $className, $attribute); + push(@implContent, "JSC::EncodedJSValue ${getter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::PropertyName);\n"); + if (!IsReadonly($attribute)) { + my $setter = GetAttributeSetterName($interface, $className, $attribute); + push(@implContent, "bool ${setter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::EncodedJSValue);\n"); + } + push(@implContent, "#endif\n") if $conditionalString; + } + + if (NeedsConstructorProperty($interface)) { + my $getter = "js" . $interfaceName . "Constructor"; + push(@implContent, "JSC::EncodedJSValue ${getter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::PropertyName);\n"); + } + + my $constructorFunctionName = "setJS" . $interfaceName . "Constructor"; + push(@implContent, "bool ${constructorFunctionName}(JSC::ExecState*, JSC::EncodedJSValue, JSC::EncodedJSValue);\n"); + + push(@implContent, "\n"); + } + + if ($numFunctions > 0) { + foreach my $function (@functions) { + next unless $function->extendedAttributes->{DOMJIT}; + $implIncludes{"DOMJITIDLTypeFilter.h"} = 1; + $implIncludes{"DOMJITCheckDOM.h"} = 1; + $implIncludes{"DOMJITAbstractHeapRepository.h"} = 1; + + my $isOverloaded = $function->{overloads} && @{$function->{overloads}} > 1; + die "Overloads is not supported in DOMJIT" if $isOverloaded; + die "Currently ReadDOM value is only allowed" unless $codeGenerator->ExtendedAttributeContains($function->extendedAttributes->{DOMJIT}, "ReadDOM"); + + my $interfaceName = $interface->type->name; + my $functionName = GetFunctionName($interface, $className, $function); + my $unsafeFunctionName = "unsafe" . $codeGenerator->WK_ucfirst($functionName); + my $domJITSignatureName = "DOMJITSignatureFor" . $interface->type->name . $codeGenerator->WK_ucfirst($function->name); + my $classInfo = "JS" . $interface->type->name . "::info()"; + my $resultType = GetResultTypeFilter($interface, $function->type); + my $domJITSignatureHeader = "static const JSC::DOMJIT::Signature ${domJITSignatureName}((uintptr_t)${unsafeFunctionName},"; + my $domJITSignatureFooter = "$classInfo, JSC::DOMJIT::Effect::forRead(DOMJIT::AbstractHeapRepository::DOM), ${resultType}"; + foreach my $argument (@{$function->arguments}) { + my $type = $argument->type; + my $argumentType = GetArgumentTypeFilter($interface, $type); + $domJITSignatureFooter .= ", ${argumentType}"; + } + $domJITSignatureFooter .= ");"; + my $conditionalString = $codeGenerator->GenerateConditionalString($function); + push(@implContent, "#if ${conditionalString}\n") if $conditionalString; + push(@implContent, "#if ENABLE(JIT)\n"); + push(@implContent, "$domJITSignatureHeader DOMJIT::checkDOM<$interfaceName>, $domJITSignatureFooter\n"); + push(@implContent, "#else\n"); + push(@implContent, "$domJITSignatureHeader nullptr, $domJITSignatureFooter\n"); + push(@implContent, "#endif\n"); + push(@implContent, "#endif\n") if $conditionalString; + push(@implContent, "\n"); + } + } + + GeneratePrototypeDeclaration(\@implContent, $className, $interface) if !HeaderNeedsPrototypeDeclaration($interface); + + GenerateConstructorDeclaration(\@implContent, $className, $interface) if NeedsConstructorProperty($interface); + + my @hashKeys = (); + my @hashValue1 = (); + my @hashValue2 = (); + my @hashSpecials = (); + my %conditionals = (); + my $hashName = $className . "Table"; + my @runtimeEnabledFunctions = (); + my @runtimeEnabledAttributes = (); + + # Generate hash table for properties on the instance. + my $numInstanceProperties = GeneratePropertiesHashTable($object, $interface, 1, + \@hashKeys, \@hashSpecials, + \@hashValue1, \@hashValue2, + \%conditionals, \@runtimeEnabledFunctions, \@runtimeEnabledAttributes); + + $object->GenerateHashTable($hashName, $numInstanceProperties, + \@hashKeys, \@hashSpecials, + \@hashValue1, \@hashValue2, + \%conditionals, 0) if $numInstanceProperties > 0; + + # - Add all interface object (aka constructor) properties (constants, static attributes, static operations). + if (NeedsConstructorProperty($interface)) { + my $hashSize = 0; my $hashName = $className . "ConstructorTable"; my @hashKeys = (); @@ -1601,855 +3185,762 @@ sub GenerateImplementation my @hashSpecials = (); my %conditionals = (); - # FIXME: we should not need a function for every constant. + my $needsConstructorTable = 0; + foreach my $constant (@{$interface->constants}) { my $name = $constant->name; push(@hashKeys, $name); - my $getter = "js" . $interfaceName . $codeGenerator->WK_ucfirst($name); - push(@hashValue1, $getter); + push(@hashValue1, $constant->value); push(@hashValue2, "0"); - push(@hashSpecials, "DontDelete | ReadOnly"); + push(@hashSpecials, "DontDelete | ReadOnly | ConstantInteger"); - my $implementedBy = $constant->extendedAttributes->{"ImplementedBy"}; - if ($implementedBy) { - $implIncludes{"${implementedBy}.h"} = 1; - } - my $conditional = $constant->extendedAttributes->{"Conditional"}; - if ($conditional) { - $conditionals{$name} = $conditional; - } + my $implementedBy = $constant->extendedAttributes->{ImplementedBy}; + $implIncludes{"${implementedBy}.h"} = 1 if $implementedBy; + + my $conditional = $constant->extendedAttributes->{Conditional}; + $conditionals{$name} = $conditional if $conditional; + + $hashSize++; } foreach my $attribute (@{$interface->attributes}) { next unless ($attribute->isStatic); - my $name = $attribute->signature->name; + my $name = $attribute->name; push(@hashKeys, $name); my @specials = (); - push(@specials, "DontDelete") unless $attribute->signature->extendedAttributes->{"Deletable"}; + push(@specials, "DontDelete") if IsUnforgeable($interface, $attribute); push(@specials, "ReadOnly") if IsReadonly($attribute); + push(@specials, "DOMJITAttribute") if $attribute->extendedAttributes->{"DOMJIT"}; my $special = (@specials > 0) ? join(" | ", @specials) : "0"; push(@hashSpecials, $special); - my $getter = GetAttributeGetterName($interfaceName, $className, $attribute); - push(@hashValue1, $getter); - - if (IsReadonly($attribute)) { + if ($attribute->extendedAttributes->{"DOMJIT"}) { + push(@hashValue1, "domJITGetterSetterFor" . $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name)); push(@hashValue2, "0"); } else { - my $setter = GetAttributeSetterName($interfaceName, $className, $attribute); - push(@hashValue2, $setter); - } + my $getter = GetAttributeGetterName($interface, $className, $attribute); + push(@hashValue1, $getter); - my $conditional = $attribute->signature->extendedAttributes->{"Conditional"}; - if ($conditional) { - $conditionals{$name} = $conditional; + if (IsReadonly($attribute)) { + push(@hashValue2, "0"); + } else { + my $setter = GetAttributeSetterName($interface, $className, $attribute); + push(@hashValue2, $setter); + } } + + my $conditional = $attribute->extendedAttributes->{Conditional}; + $conditionals{$name} = $conditional if $conditional; + + $hashSize++; } foreach my $function (@{$interface->functions}) { next unless ($function->isStatic); next if $function->{overloadIndex} && $function->{overloadIndex} > 1; - my $name = $function->signature->name; + my $name = $function->name; push(@hashKeys, $name); - my $functionName = GetFunctionName($className, $function); + my $functionName = GetFunctionName($interface, $className, $function); push(@hashValue1, $functionName); my $functionLength = GetFunctionLength($function); - push(@hashValue2, $functionLength); + if ($function->extendedAttributes->{DOMJIT}) { + push(@hashValue2, "DOMJITFunctionFor" . $interface->type->name . $codeGenerator->WK_ucfirst($function->name)); + } else { + push(@hashValue2, $functionLength); + } - my @specials = (); - push(@specials, "DontDelete") if $interface->extendedAttributes->{"OperationsNotDeletable"} - || $function->signature->extendedAttributes->{"NotDeletable"}; - push(@specials, "DontEnum") if $function->signature->extendedAttributes->{"NotEnumerable"}; - push(@specials, "JSC::Function"); - my $special = (@specials > 0) ? join(" | ", @specials) : "0"; - push(@hashSpecials, $special); + push(@hashSpecials, ComputeFunctionSpecial($interface, $function)); - my $conditional = $function->signature->extendedAttributes->{"Conditional"}; - if ($conditional) { - $conditionals{$name} = $conditional; - } + my $conditional = $function->extendedAttributes->{Conditional}; + $conditionals{$name} = $conditional if $conditional; + + $hashSize++; } $object->GenerateHashTable($hashName, $hashSize, \@hashKeys, \@hashSpecials, \@hashValue1, \@hashValue2, - \%conditionals); + \%conditionals, 1) if $hashSize > 0; push(@implContent, $codeGenerator->GenerateCompileTimeCheckForEnumsIfNeeded($interface)); my $protoClassName = "${className}Prototype"; - GenerateConstructorDefinitions(\@implContent, $className, $protoClassName, $interfaceName, $visibleInterfaceName, $interface); - if ($interface->extendedAttributes->{"NamedConstructor"}) { - GenerateConstructorDefinitions(\@implContent, $className, $protoClassName, $interfaceName, $interface->extendedAttributes->{"NamedConstructor"}, $interface, "GeneratingNamedConstructor"); - } + GenerateConstructorDefinitions(\@implContent, $className, $protoClassName, $visibleInterfaceName, $interface); + + my $namedConstructor = $interface->extendedAttributes->{NamedConstructor}; + GenerateConstructorDefinitions(\@implContent, $className, $protoClassName, $namedConstructor, $interface, "GeneratingNamedConstructor") if $namedConstructor; } # - Add functions and constants to a hashtable definition - my $hashSize = $numFunctions + $numConstants; - my $hashName = $className . "PrototypeTable"; - my @hashKeys = (); - my @hashValue1 = (); - my @hashValue2 = (); - my @hashSpecials = (); - my %conditionals = (); + $hashName = $className . "PrototypeTable"; + + @hashKeys = (); + @hashValue1 = (); + @hashValue2 = (); + @hashSpecials = (); + %conditionals = (); + @runtimeEnabledFunctions = (); + @runtimeEnabledAttributes = (); + + # Generate hash table for properties on the prototype. + my $numPrototypeProperties = GeneratePropertiesHashTable($object, $interface, 0, + \@hashKeys, \@hashSpecials, + \@hashValue1, \@hashValue2, + \%conditionals, \@runtimeEnabledFunctions, \@runtimeEnabledAttributes); + my $hashSize = $numPrototypeProperties; - # FIXME: we should not need a function for every constant. foreach my $constant (@{$interface->constants}) { my $name = $constant->name; - push(@hashKeys, $name); - my $getter = "js" . $interfaceName . $codeGenerator->WK_ucfirst($name); - push(@hashValue1, $getter); - push(@hashValue2, "0"); - push(@hashSpecials, "DontDelete | ReadOnly"); - - my $conditional = $constant->extendedAttributes->{"Conditional"}; - if ($conditional) { - $conditionals{$name} = $conditional; - } - } - foreach my $function (@{$interface->functions}) { - next if ($function->isStatic); - next if $function->{overloadIndex} && $function->{overloadIndex} > 1; - my $name = $function->signature->name; push(@hashKeys, $name); + push(@hashValue1, $constant->value); + push(@hashValue2, "0"); + push(@hashSpecials, "DontDelete | ReadOnly | ConstantInteger"); - my $functionName = GetFunctionName($className, $function); - push(@hashValue1, $functionName); - - my $functionLength = GetFunctionLength($function); - push(@hashValue2, $functionLength); - - my @specials = (); - push(@specials, "DontDelete") if $interface->extendedAttributes->{"OperationsNotDeletable"} - || $function->signature->extendedAttributes->{"NotDeletable"}; - push(@specials, "DontEnum") if $function->signature->extendedAttributes->{"NotEnumerable"}; - push(@specials, "JSC::Function"); - my $special = (@specials > 0) ? join(" | ", @specials) : "0"; - push(@hashSpecials, $special); + my $conditional = $constant->extendedAttributes->{Conditional}; + $conditionals{$name} = $conditional if $conditional; - my $conditional = $function->signature->extendedAttributes->{"Conditional"}; - if ($conditional) { - $conditionals{$name} = $conditional; - } + $hashSize++; } + my $justGenerateValueArray = !IsDOMGlobalObject($interface); + $object->GenerateHashTable($hashName, $hashSize, \@hashKeys, \@hashSpecials, \@hashValue1, \@hashValue2, - \%conditionals); + \%conditionals, $justGenerateValueArray); - if ($interface->extendedAttributes->{"JSNoStaticTables"}) { - push(@implContent, "static const HashTable& get${className}PrototypeTable(VM& vm)\n"); - push(@implContent, "{\n"); - push(@implContent, " return getHashTableForGlobalData(vm, ${className}PrototypeTable);\n"); - push(@implContent, "}\n\n"); - push(@implContent, "const ClassInfo ${className}Prototype::s_info = { \"${visibleInterfaceName}Prototype\", &Base::s_info, 0, get${className}PrototypeTable, CREATE_METHOD_TABLE(${className}Prototype) };\n\n"); + if ($justGenerateValueArray) { + push(@implContent, "const ClassInfo ${className}Prototype::s_info = { \"${visibleInterfaceName}Prototype\", &Base::s_info, 0, CREATE_METHOD_TABLE(${className}Prototype) };\n\n"); } else { - push(@implContent, "const ClassInfo ${className}Prototype::s_info = { \"${visibleInterfaceName}Prototype\", &Base::s_info, &${className}PrototypeTable, 0, CREATE_METHOD_TABLE(${className}Prototype) };\n\n"); - } - unless (IsDOMGlobalObject($interface)) { - push(@implContent, "JSObject* ${className}Prototype::self(VM& vm, JSGlobalObject* globalObject)\n"); - push(@implContent, "{\n"); - push(@implContent, " return getDOMPrototype<${className}>(vm, globalObject);\n"); - push(@implContent, "}\n\n"); + push(@implContent, "const ClassInfo ${className}Prototype::s_info = { \"${visibleInterfaceName}Prototype\", &Base::s_info, &${className}PrototypeTable, CREATE_METHOD_TABLE(${className}Prototype) };\n\n"); } - if (PrototypeOverridesGetOwnPropertySlot($interface)) { - push(@implContent, "bool ${className}Prototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)\n"); + if (PrototypeHasStaticPropertyTable($interface) && !IsGlobalOrPrimaryGlobalInterface($interface)) { + push(@implContent, "void ${className}Prototype::finishCreation(VM& vm)\n"); push(@implContent, "{\n"); - push(@implContent, " ${className}Prototype* thisObject = jsCast<${className}Prototype*>(object);\n"); - - if ($numConstants eq 0 && $numFunctions eq 0) { - push(@implContent, " return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);\n"); - } elsif ($numConstants eq 0) { - push(@implContent, " return getStaticFunctionSlot<JSObject>(exec, " . prototypeHashTableAccessor($interface->extendedAttributes->{"JSNoStaticTables"}, $className) . ", thisObject, propertyName, slot);\n"); - } elsif ($numFunctions eq 0) { - push(@implContent, " return getStaticValueSlot<${className}Prototype, JSObject>(exec, " . prototypeHashTableAccessor($interface->extendedAttributes->{"JSNoStaticTables"}, $className) . ", thisObject, propertyName, slot);\n"); - } else { - push(@implContent, " return getStaticPropertySlot<${className}Prototype, JSObject>(exec, " . prototypeHashTableAccessor($interface->extendedAttributes->{"JSNoStaticTables"}, $className) . ", thisObject, propertyName, slot);\n"); + push(@implContent, " Base::finishCreation(vm);\n"); + push(@implContent, " reifyStaticProperties(vm, ${className}PrototypeTableValues, *this);\n"); + + my @runtimeEnabledProperties = @runtimeEnabledFunctions; + push(@runtimeEnabledProperties, @runtimeEnabledAttributes); + foreach my $functionOrAttribute (@runtimeEnabledProperties) { + my $conditionalString = $codeGenerator->GenerateConditionalString($functionOrAttribute); + push(@implContent, "#if ${conditionalString}\n") if $conditionalString; + AddToImplIncludes("RuntimeEnabledFeatures.h"); + my $enable_function = GetRuntimeEnableFunctionName($functionOrAttribute); + my $name = $functionOrAttribute->name; + push(@implContent, " if (!${enable_function}()) {\n"); + push(@implContent, " Identifier propertyName = Identifier::fromString(&vm, reinterpret_cast<const LChar*>(\"$name\"), strlen(\"$name\"));\n"); + push(@implContent, " VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable);\n"); + push(@implContent, " JSObject::deleteProperty(this, globalObject()->globalExec(), propertyName);\n"); + push(@implContent, " }\n"); + push(@implContent, "#endif\n") if $conditionalString; } - push(@implContent, "}\n\n"); - } - if ($interface->extendedAttributes->{"JSCustomNamedGetterOnPrototype"}) { - push(@implContent, "void ${className}Prototype::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)\n"); - push(@implContent, "{\n"); - push(@implContent, " ${className}Prototype* thisObject = jsCast<${className}Prototype*>(cell);\n"); - push(@implContent, " if (thisObject->putDelegate(exec, propertyName, value, slot))\n"); - push(@implContent, " return;\n"); - push(@implContent, " Base::put(thisObject, exec, propertyName, value, slot);\n"); + foreach my $function (@{$interface->functions}) { + next unless ($function->extendedAttributes->{PrivateIdentifier}); + AddToImplIncludes("WebCoreJSClientData.h"); + my $conditionalString = $codeGenerator->GenerateConditionalString($function); + push(@implContent, "#if ${conditionalString}\n") if $conditionalString; + push(@implContent, " putDirect(vm, static_cast<JSVMClientData*>(vm.clientData)->builtinNames()." . $function->name . "PrivateName(), JSFunction::create(vm, globalObject(), 0, String(), " . GetFunctionName($interface, $className, $function) . "), ReadOnly | DontEnum);\n"); + push(@implContent, "#endif\n") if $conditionalString; + } + + if (InterfaceNeedsIterator($interface)) { + if (IsKeyValueIterableInterface($interface)) { + my $functionName = GetFunctionName($interface, $className, @{$interface->iterable->functions}[0]); + push(@implContent, " putDirect(vm, vm.propertyNames->iteratorSymbol, JSFunction::create(vm, globalObject(), 0, ASCIILiteral(\"[Symbol.Iterator]\"), $functionName), DontEnum);\n"); + } else { + AddToImplIncludes("<builtins/BuiltinNames.h>"); + push(@implContent, " putDirect(vm, vm.propertyNames->iteratorSymbol, globalObject()->arrayPrototype()->getDirect(vm, vm.propertyNames->builtinNames().valuesPrivateName()), DontEnum);\n"); + } + } + push(@implContent, " addValueIterableMethods(*globalObject(), *this);\n") if $interface->iterable and !IsKeyValueIterableInterface($interface); + + addUnscopableProperties($interface); + push(@implContent, "}\n\n"); } - # - Initialize static ClassInfo object - if ($numAttributes > 0 && $interface->extendedAttributes->{"JSNoStaticTables"}) { - push(@implContent, "static const HashTable& get${className}Table(VM& vm)\n"); + if ($interface->extendedAttributes->{JSCustomNamedGetterOnPrototype}) { + push(@implContent, "bool ${className}Prototype::put(JSCell* cell, ExecState* state, PropertyName propertyName, JSValue value, PutPropertySlot& slot)\n"); push(@implContent, "{\n"); - push(@implContent, " return getHashTableForGlobalData(vm, ${className}Table);\n"); + push(@implContent, " auto* thisObject = jsCast<${className}Prototype*>(cell);\n"); + push(@implContent, " bool putResult = false;\n"); + push(@implContent, " if (thisObject->putDelegate(state, propertyName, value, slot, putResult))\n"); + push(@implContent, " return putResult;\n"); + push(@implContent, " return Base::put(thisObject, state, propertyName, value, slot);\n"); push(@implContent, "}\n\n"); } + # - Initialize static ClassInfo object push(@implContent, "const ClassInfo $className" . "::s_info = { \"${visibleInterfaceName}\", &Base::s_info, "); - if ($numAttributes > 0 && !$interface->extendedAttributes->{"JSNoStaticTables"}) { + if ($numInstanceProperties > 0) { push(@implContent, "&${className}Table"); } else { push(@implContent, "0"); } - if ($numAttributes > 0 && $interface->extendedAttributes->{"JSNoStaticTables"}) { - push(@implContent, ", get${className}Table "); - } else { - push(@implContent, ", 0 "); - } push(@implContent, ", CREATE_METHOD_TABLE($className) };\n\n"); - my $implType = $interfaceName; - my ($svgPropertyType, $svgListPropertyType, $svgNativeType) = GetSVGPropertyTypes($implType); - $implType = $svgNativeType if $svgNativeType; - - my $svgPropertyOrListPropertyType; - $svgPropertyOrListPropertyType = $svgPropertyType if $svgPropertyType; - $svgPropertyOrListPropertyType = $svgListPropertyType if $svgListPropertyType; - # Constructor if ($interfaceName eq "DOMWindow") { - AddIncludesForTypeInImpl("JSDOMWindowShell"); - push(@implContent, "${className}::$className(VM& vm, Structure* structure, PassRefPtr<$implType> impl, JSDOMWindowShell* shell)\n"); - push(@implContent, " : $parentClassName(vm, structure, impl, shell)\n"); + AddIncludesForImplementationTypeInImpl("JSDOMWindowShell"); + push(@implContent, "${className}::$className(VM& vm, Structure* structure, Ref<$implType>&& impl, JSDOMWindowShell* shell)\n"); + push(@implContent, " : $parentClassName(vm, structure, WTFMove(impl), shell)\n"); push(@implContent, "{\n"); push(@implContent, "}\n\n"); } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope")) { - AddIncludesForTypeInImpl($interfaceName); - push(@implContent, "${className}::$className(VM& vm, Structure* structure, PassRefPtr<$implType> impl)\n"); - push(@implContent, " : $parentClassName(vm, structure, impl)\n"); + AddIncludesForImplementationTypeInImpl($interfaceName); + push(@implContent, "${className}::$className(VM& vm, Structure* structure, Ref<$implType>&& impl)\n"); + push(@implContent, " : $parentClassName(vm, structure, WTFMove(impl))\n"); push(@implContent, "{\n"); push(@implContent, "}\n\n"); + } elsif (!NeedsImplementationClass($interface)) { + push(@implContent, "${className}::$className(Structure* structure, JSDOMGlobalObject& globalObject)\n"); + push(@implContent, " : $parentClassName(structure, globalObject) { }\n\n"); } else { - push(@implContent, "${className}::$className(Structure* structure, JSDOMGlobalObject* globalObject, PassRefPtr<$implType> impl)\n"); - if ($hasParent) { - push(@implContent, " : $parentClassName(structure, globalObject, impl)\n"); - } else { - push(@implContent, " : $parentClassName(structure, globalObject)\n"); - push(@implContent, " , m_impl(impl.leakRef())\n"); - } + push(@implContent, "${className}::$className(Structure* structure, JSDOMGlobalObject& globalObject, Ref<$implType>&& impl)\n"); + push(@implContent, " : $parentClassName(structure, globalObject, WTFMove(impl))\n"); push(@implContent, "{\n"); push(@implContent, "}\n\n"); + } + if ($interfaceName eq "DOMWindow") { + push(@implContent, "void ${className}::finishCreation(VM& vm, JSDOMWindowShell* shell)\n"); + push(@implContent, "{\n"); + push(@implContent, " Base::finishCreation(vm, shell);\n\n"); + } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope")) { + push(@implContent, "void ${className}::finishCreation(VM& vm, JSProxy* proxy)\n"); + push(@implContent, "{\n"); + push(@implContent, " Base::finishCreation(vm, proxy);\n\n"); + } else { push(@implContent, "void ${className}::finishCreation(VM& vm)\n"); push(@implContent, "{\n"); push(@implContent, " Base::finishCreation(vm);\n"); - push(@implContent, " ASSERT(inherits(info()));\n"); - push(@implContent, "}\n\n"); + push(@implContent, " ASSERT(inherits(vm, info()));\n\n"); + } + + if ($interfaceName eq "Location") { + push(@implContent, " putDirect(vm, vm.propertyNames->valueOf, globalObject()->objectProtoValueOfFunction(), DontDelete | ReadOnly | DontEnum);\n"); + push(@implContent, " putDirect(vm, vm.propertyNames->toPrimitiveSymbol, jsUndefined(), DontDelete | ReadOnly | DontEnum);\n"); } - unless (IsDOMGlobalObject($interface)) { + # Support for RuntimeEnabled attributes on instances. + foreach my $attribute (@{$interface->attributes}) { + next unless NeedsRuntimeCheck($attribute); + next unless AttributeShouldBeOnInstance($interface, $attribute); + + my $conditionalString = $codeGenerator->GenerateConditionalString($attribute); + push(@implContent, "#if ${conditionalString}\n") if $conditionalString; + my $enable_function = GetRuntimeEnableFunctionName($attribute); + my $attributeName = $attribute->name; + push(@implContent, " if (${enable_function}()) {\n"); + my $getter = GetAttributeGetterName($interface, $className, $attribute); + my $setter = IsReadonly($attribute) ? "nullptr" : GetAttributeSetterName($interface, $className, $attribute); + push(@implContent, " auto* customGetterSetter = CustomGetterSetter::create(vm, $getter, $setter);\n"); + my $jscAttributes = GetJSCAttributesForAttribute($interface, $attribute); + push(@implContent, " putDirectCustomAccessor(vm, vm.propertyNames->$attributeName, customGetterSetter, attributesForStructure($jscAttributes));\n"); + push(@implContent, " }\n"); + push(@implContent, "#endif\n") if $conditionalString; + } + + # Support PrivateIdentifier attributes on instances. + foreach my $attribute (@{$interface->attributes}) { + next unless $attribute->extendedAttributes->{PrivateIdentifier}; + next unless AttributeShouldBeOnInstance($interface, $attribute); + + AddToImplIncludes("WebCoreJSClientData.h"); + my $conditionalString = $codeGenerator->GenerateConditionalString($attribute); + my $attributeName = $attribute->name; + my $getter = GetAttributeGetterName($interface, $className, $attribute); + + push(@implContent, "#if ${conditionalString}\n") if $conditionalString; + push(@implContent, " putDirectCustomAccessor(vm, static_cast<JSVMClientData*>(vm.clientData)->builtinNames()." . $attributeName . "PrivateName(), CustomGetterSetter::create(vm, $getter, nullptr), attributesForStructure(DontDelete | ReadOnly));\n"); + push(@implContent, "#endif\n") if $conditionalString; + } + + # Support for RuntimeEnabled operations on instances. + foreach my $function (@{$interface->functions}) { + next unless NeedsRuntimeCheck($function); + next unless OperationShouldBeOnInstance($interface, $function); + next if $function->{overloadIndex} && $function->{overloadIndex} > 1; + + my $conditionalString = $codeGenerator->GenerateConditionalString($function); + push(@implContent, "#if ${conditionalString}\n") if $conditionalString; + my $enable_function = GetRuntimeEnableFunctionName($function); + my $functionName = $function->name; + my $implementationFunction = GetFunctionName($interface, $className, $function); + my $functionLength = GetFunctionLength($function); + my $jsAttributes = ComputeFunctionSpecial($interface, $function); + push(@implContent, " if (${enable_function}())\n"); + + my $propertyName = "vm.propertyNames->$functionName"; + $propertyName = "static_cast<JSVMClientData*>(vm.clientData)->builtinNames()." . $functionName . "PrivateName()" if $function->extendedAttributes->{PrivateIdentifier}; + if (IsJSBuiltin($interface, $function)) { + push(@implContent, " putDirectBuiltinFunction(vm, this, $propertyName, $implementationFunction(vm), attributesForStructure($jsAttributes));\n"); + } else { + push(@implContent, " putDirectNativeFunction(vm, this, $propertyName, $functionLength, $implementationFunction, NoIntrinsic, attributesForStructure($jsAttributes));\n"); + } + push(@implContent, "#endif\n") if $conditionalString; + } + push(@implContent, "}\n\n"); + + unless (ShouldUseGlobalObjectPrototype($interface)) { push(@implContent, "JSObject* ${className}::createPrototype(VM& vm, JSGlobalObject* globalObject)\n"); push(@implContent, "{\n"); - if ($hasParent && $parentClassName ne "JSC::DOMNodeFilter") { - push(@implContent, " return ${className}Prototype::create(vm, globalObject, ${className}Prototype::createStructure(vm, globalObject, ${parentClassName}Prototype::self(vm, globalObject)));\n"); + if ($interface->parentType) { + my $parentClassNameForPrototype = "JS" . $interface->parentType->name; + push(@implContent, " return ${className}Prototype::create(vm, globalObject, ${className}Prototype::createStructure(vm, globalObject, ${parentClassNameForPrototype}::prototype(vm, globalObject)));\n"); } else { my $prototype = $interface->isException ? "errorPrototype" : "objectPrototype"; push(@implContent, " return ${className}Prototype::create(vm, globalObject, ${className}Prototype::createStructure(vm, globalObject, globalObject->${prototype}()));\n"); } push(@implContent, "}\n\n"); + + push(@implContent, "JSObject* ${className}::prototype(VM& vm, JSGlobalObject* globalObject)\n"); + push(@implContent, "{\n"); + push(@implContent, " return getDOMPrototype<${className}>(vm, globalObject);\n"); + push(@implContent, "}\n\n"); } if (!$hasParent) { - # FIXME: This destroy function should not be necessary, as - # a finalizer should be called for each DOM object wrapper. - # However, that seems not to be the case, so this has been - # added back to avoid leaking while we figure out why the - # finalizers are not always getting called. The work tracking - # the finalizer issue is being tracked in http://webkit.org/b/75451 push(@implContent, "void ${className}::destroy(JSC::JSCell* cell)\n"); push(@implContent, "{\n"); push(@implContent, " ${className}* thisObject = static_cast<${className}*>(cell);\n"); push(@implContent, " thisObject->${className}::~${className}();\n"); push(@implContent, "}\n\n"); - - # We also need a destructor for the allocateCell to work properly with the destructor-free part of the heap. - # Otherwise, these destroy functions/destructors won't get called. - push(@implContent, "${className}::~${className}()\n"); - push(@implContent, "{\n"); - push(@implContent, " releaseImplIfNotNull();\n"); - push(@implContent, "}\n\n"); } my $hasGetter = InstanceOverridesGetOwnPropertySlot($interface); # Attributes if ($hasGetter) { - if (!$interface->extendedAttributes->{"CustomGetOwnPropertySlot"}) { - push(@implContent, "bool ${className}::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)\n"); - push(@implContent, "{\n"); - push(@implContent, " ${className}* thisObject = jsCast<${className}*>(object);\n"); - push(@implContent, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n"); - push(@implContent, GenerateGetOwnPropertySlotBody($interface, $interfaceName, $className, $numAttributes > 0, 0)); - push(@implContent, "}\n\n"); + if (!$interface->extendedAttributes->{CustomGetOwnPropertySlot}) { + push(@implContent, GenerateGetOwnPropertySlotBody($interface, $className, $indexedGetterFunction, $namedGetterFunction)); } if ($indexedGetterFunction || $namedGetterFunction - || $interface->extendedAttributes->{"CustomNamedGetter"} - || $interface->extendedAttributes->{"JSCustomGetOwnPropertySlotAndDescriptor"}) { - push(@implContent, "bool ${className}::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned index, PropertySlot& slot)\n"); - push(@implContent, "{\n"); - push(@implContent, " ${className}* thisObject = jsCast<${className}*>(object);\n"); - push(@implContent, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n"); + || $interface->extendedAttributes->{CustomNamedGetter} + || $interface->extendedAttributes->{JSCustomGetOwnPropertySlotAndDescriptor}) { + push(@implContent, GenerateGetOwnPropertySlotBodyByIndex($interface, $className, $indexedGetterFunction, $namedGetterFunction)); + } - # Sink the int-to-string conversion that happens when we create a PropertyName - # to the point where we actually need it. - my $generatedPropertyName = 0; - my $propertyNameGeneration = sub { - if ($generatedPropertyName) { - return; - } - push(@implContent, " PropertyName propertyName = Identifier::from(exec, index);\n"); - $generatedPropertyName = 1; - }; + } - if ($indexedGetterFunction) { - if ($indexedGetterFunction->signature->type eq "DOMString") { - push(@implContent, " if (index <= MAX_ARRAY_INDEX) {\n"); - } else { - push(@implContent, " if (index < thisObject->impl().length()) {\n"); - } - # Assume that if there's a setter, the index will be writable - if ($interface->extendedAttributes->{"CustomIndexedSetter"}) { - push(@implContent, " unsigned attributes = DontDelete;\n"); - } else { - push(@implContent, " unsigned attributes = DontDelete | ReadOnly;\n"); - } - if ($hasNumericIndexedGetter) { - push(@implContent, " slot.setValue(thisObject, attributes, thisObject->getByIndex(exec, index));\n"); - } else { - push(@implContent, " slot.setCustomIndex(thisObject, attributes, index, thisObject->indexGetter);\n"); - } - push(@implContent, " return true;\n"); - push(@implContent, " }\n"); - } - if ($namedGetterFunction || $interface->extendedAttributes->{"CustomNamedGetter"}) { - &$propertyNameGeneration(); - push(@implContent, " if (canGetItemsForName(exec, &thisObject->impl(), propertyName)) {\n"); - push(@implContent, " slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, thisObject->nameGetter);\n"); - push(@implContent, " return true;\n"); - push(@implContent, " }\n"); - $implIncludes{"wtf/text/AtomicString.h"} = 1; - } + if (($indexedGetterFunction || $namedGetterFunction) && !$interface->extendedAttributes->{CustomEnumerateProperty}) { + push(@implContent, GenerateGetOwnPropertyNames($interface, $className, $indexedGetterFunction, $namedGetterFunction)); + } - if ($interface->extendedAttributes->{"JSCustomGetOwnPropertySlotAndDescriptor"}) { - &$propertyNameGeneration(); - push(@implContent, " if (thisObject->getOwnPropertySlotDelegate(exec, propertyName, slot))\n"); - push(@implContent, " return true;\n"); + my $namedSetterFunction = GetNamedSetterFunction($interface); + my $indexedSetterFunction = GetIndexedSetterFunction($interface); + + my $hasSetter = InstanceOverridesPut($interface); + if ($hasSetter) { + if (!$interface->extendedAttributes->{CustomPutFunction}) { + push(@implContent, GeneratePut($interface, $className, $indexedSetterFunction, $namedSetterFunction)); + + if ($interface->extendedAttributes->{CustomIndexedSetter} || $interface->extendedAttributes->{CustomNamedSetter}) { + push(@implContent, GeneratePutByIndex($interface, $className, $indexedSetterFunction, $namedSetterFunction)); } + } + } - push(@implContent, " return Base::getOwnPropertySlotByIndex(thisObject, exec, index, slot);\n"); + if ($numAttributes > 0) { + my $castingFunction = $interface->extendedAttributes->{"CustomProxyToJSObject"} ? "to${className}" : GetCastingHelperForThisObject($interface); + # FIXME: Remove ImplicitThis keyword as it is no longer defined by WebIDL spec and is only used in DOMWindow. + if ($interface->extendedAttributes->{"ImplicitThis"}) { + push(@implContent, "template<> inline ${className}* BindingCaller<${className}>::castForAttribute(ExecState& state, EncodedJSValue thisValue)\n"); + push(@implContent, "{\n"); + push(@implContent, " auto decodedThisValue = JSValue::decode(thisValue);\n"); + push(@implContent, " if (decodedThisValue.isUndefinedOrNull())\n"); + push(@implContent, " decodedThisValue = state.thisValue().toThis(&state, NotStrictMode);\n"); + push(@implContent, " return $castingFunction(state.vm(), decodedThisValue);"); + push(@implContent, "}\n\n"); + } else { + push(@implContent, "template<> inline ${className}* BindingCaller<${className}>::castForAttribute(ExecState& state, EncodedJSValue thisValue)\n"); + push(@implContent, "{\n"); + push(@implContent, " return $castingFunction(state.vm(), JSValue::decode(thisValue));\n"); push(@implContent, "}\n\n"); } + } + if ($numFunctions > 0 && $interfaceName ne "EventTarget") { + # FIXME: Make consistent castForAttibute and castForOperation in case of CustomProxyToJSObject. + my $castingFunction = $interface->extendedAttributes->{"CustomProxyToJSObject"} ? "to${className}" : GetCastingHelperForThisObject($interface); + my $thisValue = $interface->extendedAttributes->{"CustomProxyToJSObject"} ? "state.thisValue().toThis(&state, NotStrictMode)" : "state.thisValue()"; + push(@implContent, "template<> inline ${className}* BindingCaller<${className}>::castForOperation(ExecState& state)\n"); + push(@implContent, "{\n"); + push(@implContent, " return $castingFunction(state.vm(), $thisValue);\n"); + push(@implContent, "}\n\n"); } + + $numAttributes = $numAttributes + 1 if NeedsConstructorProperty($interface); if ($numAttributes > 0) { foreach my $attribute (@{$interface->attributes}) { - my $name = $attribute->signature->name; - my $type = $attribute->signature->type; - my $isNullable = $attribute->signature->isNullable; - $codeGenerator->AssertNotSequenceType($type); - my $getFunctionName = GetAttributeGetterName($interfaceName, $className, $attribute); - my $implGetterFunctionName = $codeGenerator->WK_lcfirst($attribute->signature->extendedAttributes->{"ImplementedAs"} || $name); - - my $attributeConditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); - push(@implContent, "#if ${attributeConditionalString}\n") if $attributeConditionalString; + next if IsJSBuiltin($interface, $attribute); - push(@implContent, "EncodedJSValue ${getFunctionName}(ExecState* exec, EncodedJSValue slotBase, EncodedJSValue thisValue, PropertyName)\n"); - push(@implContent, "{\n"); + my $name = $attribute->name; + my $type = $attribute->type; + my $getFunctionName = GetAttributeGetterName($interface, $className, $attribute); + my $implGetterFunctionName = $codeGenerator->WK_lcfirst($attribute->extendedAttributes->{ImplementedAs} || $name); - if (!$attribute->isStatic || $attribute->signature->type =~ /Constructor$/) { - if ($interfaceName eq "DOMWindow") { - push(@implContent, " ${className}* castedThis = jsCast<$className*>(JSValue::decode(slotBase));\n"); - push(@implContent, " UNUSED_PARAM(thisValue);\n"); - } else { - push(@implContent, " ${className}* castedThis = jsDynamicCast<$className*>(JSValue::decode(thisValue));\n"); - push(@implContent, " UNUSED_PARAM(slotBase);\n"); - push(@implContent, " if (!castedThis)\n"); - push(@implContent, " return throwVMTypeError(exec);\n"); + my $attributeConditionalString = $codeGenerator->GenerateConditionalString($attribute); + push(@implContent, "#if ${attributeConditionalString}\n") if $attributeConditionalString; + + if (!$attribute->isStatic || $codeGenerator->IsConstructorType($type)) { + my $templateParameters = "${getFunctionName}Getter"; + if ($attribute->extendedAttributes->{LenientThis}) { + $templateParameters .= ", CastedThisErrorBehavior::ReturnEarly"; + } elsif (IsReturningPromise($attribute)) { + $templateParameters .= ", CastedThisErrorBehavior::RejectPromise"; } + + push(@implContent, "static inline JSValue ${getFunctionName}Getter(ExecState&, ${className}&, ThrowScope& throwScope);\n\n"); + + push(@implContent, "EncodedJSValue ${getFunctionName}(ExecState* state, EncodedJSValue thisValue, PropertyName)\n"); + push(@implContent, "{\n"); + push(@implContent, " return BindingCaller<${className}>::attribute<${templateParameters}>(state, thisValue, \"$name\");\n"); + push(@implContent, "}\n\n"); + + push(@implContent, "static inline JSValue ${getFunctionName}Getter(ExecState& state, ${className}& thisObject, ThrowScope& throwScope)\n"); + push(@implContent, "{\n"); + push(@implContent, " UNUSED_PARAM(throwScope);\n"); } else { - push(@implContent, " UNUSED_PARAM(thisValue);\n"); - push(@implContent, " UNUSED_PARAM(slotBase);\n"); + push(@implContent, "static inline JSValue ${getFunctionName}Getter(ExecState&);\n\n"); + + push(@implContent, "EncodedJSValue ${getFunctionName}(ExecState* state, EncodedJSValue, PropertyName)\n"); + push(@implContent, "{\n"); + push(@implContent, " ASSERT(state);\n"); + push(@implContent, " return JSValue::encode(${getFunctionName}Getter(*state));\n"); + push(@implContent, "}\n\n"); + + push(@implContent, "static inline JSValue ${getFunctionName}Getter(ExecState& state)\n"); + push(@implContent, "{\n"); } + push(@implContent, " UNUSED_PARAM(state);\n"); # Global constructors can be disabled at runtime. - if ($attribute->signature->type =~ /Constructor$/) { - if ($attribute->signature->extendedAttributes->{"EnabledAtRuntime"}) { - AddToImplIncludes("RuntimeEnabledFeatures.h"); - my $enable_function = GetRuntimeEnableFunctionName($attribute->signature); - push(@implContent, " if (!${enable_function}())\n"); - push(@implContent, " return JSValue::encode(jsUndefined());\n"); - } elsif ($attribute->signature->extendedAttributes->{"EnabledBySetting"}) { + if ($codeGenerator->IsConstructorType($type)) { + if ($attribute->extendedAttributes->{EnabledBySetting}) { AddToImplIncludes("Frame.h"); AddToImplIncludes("Settings.h"); - my $enable_function = ToMethodName($attribute->signature->extendedAttributes->{"EnabledBySetting"}) . "Enabled"; - push(@implContent, " if (!castedThis->impl().frame())\n"); - push(@implContent, " return JSValue::encode(jsUndefined());\n"); - push(@implContent, " Settings& settings = castedThis->impl().frame()->settings();\n"); + my $enable_function = ToMethodName($attribute->extendedAttributes->{EnabledBySetting}) . "Enabled"; + push(@implContent, " if (UNLIKELY(!thisObject.wrapped().frame()))\n"); + push(@implContent, " return jsUndefined();\n"); + push(@implContent, " Settings& settings = thisObject.wrapped().frame()->settings();\n"); push(@implContent, " if (!settings.$enable_function())\n"); - push(@implContent, " return JSValue::encode(jsUndefined());\n"); + push(@implContent, " return jsUndefined();\n"); } } - if ($attribute->signature->extendedAttributes->{"CachedAttribute"}) { - $needsMarkChildren = 1; - } - - if ($interface->extendedAttributes->{"CheckSecurity"} && - !$attribute->signature->extendedAttributes->{"DoNotCheckSecurity"} && - !$attribute->signature->extendedAttributes->{"DoNotCheckSecurityOnGetter"}) { - push(@implContent, " if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, castedThis->impl()))\n"); - push(@implContent, " return JSValue::encode(jsUndefined());\n"); - } + $needsVisitChildren = 1 if $attribute->extendedAttributes->{CachedAttribute}; - if (HasCustomGetter($attribute->signature->extendedAttributes)) { - push(@implContent, " return JSValue::encode(castedThis->$implGetterFunctionName(exec));\n"); - } elsif ($attribute->signature->extendedAttributes->{"CheckSecurityForNode"}) { - $implIncludes{"JSDOMBinding.h"} = 1; - push(@implContent, " $interfaceName& impl = castedThis->impl();\n"); - push(@implContent, " return JSValue::encode(shouldAllowAccessToNode(exec, impl." . $attribute->signature->name . "()) ? " . NativeToJSValue($attribute->signature, 0, $interfaceName, "impl.$implGetterFunctionName()", "castedThis") . " : jsNull());\n"); - } elsif ($type eq "EventListener") { - $implIncludes{"EventListener.h"} = 1; - push(@implContent, " UNUSED_PARAM(exec);\n"); - push(@implContent, " $interfaceName& impl = castedThis->impl();\n"); - push(@implContent, " if (EventListener* listener = impl.$implGetterFunctionName()) {\n"); - push(@implContent, " if (const JSEventListener* jsListener = JSEventListener::cast(listener)) {\n"); - if ($interfaceName eq "Document" || $codeGenerator->InheritsInterface($interface, "WorkerGlobalScope")) { - push(@implContent, " if (JSObject* jsFunction = jsListener->jsFunction(&impl))\n"); + if ($interface->extendedAttributes->{CheckSecurity} && + !$attribute->extendedAttributes->{DoNotCheckSecurity} && + !$attribute->extendedAttributes->{DoNotCheckSecurityOnGetter}) { + AddToImplIncludes("JSDOMBindingSecurity.h"); + if ($interfaceName eq "DOMWindow") { + push(@implContent, " if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, thisObject.wrapped(), ThrowSecurityError))\n"); } else { - push(@implContent, " if (JSObject* jsFunction = jsListener->jsFunction(impl.scriptExecutionContext()))\n"); + push(@implContent, " if (!BindingSecurity::shouldAllowAccessToFrame(&state, thisObject.wrapped().frame(), ThrowSecurityError))\n"); } - push(@implContent, " return JSValue::encode(jsFunction);\n"); - push(@implContent, " }\n"); - push(@implContent, " }\n"); - push(@implContent, " return JSValue::encode(jsNull());\n"); - } elsif ($attribute->signature->type =~ /Constructor$/) { - my $constructorType = $attribute->signature->type; + push(@implContent, " return jsUndefined();\n"); + } + + if (HasCustomGetter($attribute->extendedAttributes)) { + push(@implContent, " return thisObject.$implGetterFunctionName(state);\n"); + } elsif ($type->name eq "EventHandler") { + $implIncludes{"EventNames.h"} = 1; + my $getter = $attribute->extendedAttributes->{WindowEventHandler} ? "windowEventHandlerAttribute" + : $attribute->extendedAttributes->{DocumentEventHandler} ? "documentEventHandlerAttribute" + : "eventHandlerAttribute"; + my $eventName = EventHandlerAttributeEventName($attribute); + push(@implContent, " return $getter(thisObject.wrapped(), $eventName, worldForDOMObject(&thisObject));\n"); + } elsif ($codeGenerator->IsConstructorType($attribute->type)) { + my $constructorType = $attribute->type->name; $constructorType =~ s/Constructor$//; - # When Constructor attribute is used by DOMWindow.idl, it's correct to pass castedThis as the global object - # When JSDOMWrappers have a back-pointer to the globalObject we can pass castedThis->globalObject() + # When Constructor attribute is used by DOMWindow.idl, it's correct to pass thisObject as the global object + # When JSDOMWrappers have a back-pointer to the globalObject we can pass thisObject->globalObject() if ($interfaceName eq "DOMWindow") { my $named = ($constructorType =~ /Named$/) ? "Named" : ""; $constructorType =~ s/Named$//; - push(@implContent, " return JSValue::encode(JS" . $constructorType . "::get${named}Constructor(exec->vm(), castedThis));\n"); + push(@implContent, " return JS" . $constructorType . "::get${named}Constructor(state.vm(), &thisObject);\n"); } else { - AddToImplIncludes("JS" . $constructorType . ".h", $attribute->signature->extendedAttributes->{"Conditional"}); - push(@implContent, " return JSValue::encode(JS" . $constructorType . "::getConstructor(exec->vm(), castedThis->globalObject()));\n"); - } - } elsif (!$attribute->signature->extendedAttributes->{"GetterRaisesException"}) { - push(@implContent, " UNUSED_PARAM(exec);\n") if !$attribute->signature->extendedAttributes->{"CallWith"}; - push(@implContent, " bool isNull = false;\n") if $isNullable; - - my $cacheIndex = 0; - if ($attribute->signature->extendedAttributes->{"CachedAttribute"}) { - $cacheIndex = $currentCachedAttribute; - $currentCachedAttribute++; - push(@implContent, " if (JSValue cachedValue = castedThis->m_" . $attribute->signature->name . ".get())\n"); - push(@implContent, " return JSValue::encode(cachedValue);\n"); + AddToImplIncludes("JS" . $constructorType . ".h", $attribute->extendedAttributes->{Conditional}); + push(@implContent, " return JS" . $constructorType . "::getConstructor(state.vm(), thisObject.globalObject());\n"); } - - my @callWithArgs = GenerateCallWith($attribute->signature->extendedAttributes->{"CallWith"}, \@implContent, "JSValue::encode(jsUndefined())"); - - if ($svgListPropertyType) { - push(@implContent, " JSValue result = " . NativeToJSValue($attribute->signature, 0, $interfaceName, "castedThis->impl().$implGetterFunctionName(" . (join ", ", @callWithArgs) . ")", "castedThis") . ";\n"); - } elsif ($svgPropertyOrListPropertyType) { - push(@implContent, " $svgPropertyOrListPropertyType& impl = castedThis->impl().propertyReference();\n"); - if ($svgPropertyOrListPropertyType eq "float") { # Special case for JSSVGNumber - push(@implContent, " JSValue result = " . NativeToJSValue($attribute->signature, 0, $interfaceName, "impl", "castedThis") . ";\n"); - } else { - push(@implContent, " JSValue result = " . NativeToJSValue($attribute->signature, 0, $interfaceName, "impl.$implGetterFunctionName(" . (join ", ", @callWithArgs) . ")", "castedThis") . ";\n"); - - } - } else { - my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $attribute); - push(@arguments, "isNull") if $isNullable; - if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) { - my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"}; - $implIncludes{"${implementedBy}.h"} = 1; - $functionName = "${implementedBy}::${functionName}"; - unshift(@arguments, "&impl") if !$attribute->isStatic; - } elsif ($attribute->isStatic) { - $functionName = "${interfaceName}::${functionName}"; - } else { - $functionName = "impl.${functionName}"; - } - - unshift(@arguments, @callWithArgs); - my $jsType = NativeToJSValue($attribute->signature, 0, $interfaceName, "${functionName}(" . join(", ", @arguments) . ")", "castedThis"); - push(@implContent, " $interfaceName& impl = castedThis->impl();\n") if !$attribute->isStatic; - if ($codeGenerator->IsSVGAnimatedType($type)) { - push(@implContent, " RefPtr<$type> obj = $jsType;\n"); - push(@implContent, " JSValue result = toJS(exec, castedThis->globalObject(), obj.get());\n"); - } else { - push(@implContent, " JSValue result = $jsType;\n"); - } - - if ($isNullable) { - push(@implContent, " if (isNull)\n"); - push(@implContent, " return JSValue::encode(jsNull());\n"); - } - } - - push(@implContent, " castedThis->m_" . $attribute->signature->name . ".set(exec->vm(), castedThis, result);\n") if ($attribute->signature->extendedAttributes->{"CachedAttribute"}); - push(@implContent, " return JSValue::encode(result);\n"); - } else { - my @arguments = ("ec"); - push(@implContent, " ExceptionCode ec = 0;\n"); - - if ($isNullable) { - push(@implContent, " bool isNull = false;\n"); - unshift(@arguments, "isNull"); + if ($attribute->extendedAttributes->{CachedAttribute}) { + push(@implContent, " if (JSValue cachedValue = thisObject.m_" . $attribute->name . ".get())\n"); + push(@implContent, " return cachedValue;\n"); } - unshift(@arguments, GenerateCallWith($attribute->signature->extendedAttributes->{"CallWith"}, \@implContent, "JSValue::encode(jsUndefined())")); + my @callWithArgs = GenerateCallWithUsingReferences($attribute->extendedAttributes->{CallWith}, \@implContent, "jsUndefined()"); - if ($svgPropertyOrListPropertyType) { - push(@implContent, " $svgPropertyOrListPropertyType impl(*castedThis->impl());\n"); - push(@implContent, " JSC::JSValue result = " . NativeToJSValue($attribute->signature, 0, $interfaceName, "impl.$implGetterFunctionName(" . join(", ", @arguments) . ")", "castedThis") . ";\n"); + my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $attribute); + my $implementedBy = $attribute->extendedAttributes->{ImplementedBy}; + if ($implementedBy) { + $implIncludes{"${implementedBy}.h"} = 1; + $functionName = "WebCore::${implementedBy}::${functionName}"; + unshift(@arguments, "impl") if !$attribute->isStatic; + } elsif ($attribute->isStatic) { + $functionName = "${interfaceName}::${functionName}"; } else { - push(@implContent, " $interfaceName& impl = castedThis->impl();\n"); - push(@implContent, " JSC::JSValue result = " . NativeToJSValue($attribute->signature, 0, $interfaceName, "impl.$implGetterFunctionName(" . join(", ", @arguments) . ")", "castedThis") . ";\n"); + $functionName = "impl.${functionName}"; } - if ($isNullable) { - push(@implContent, " if (isNull)\n"); - push(@implContent, " return JSValue::encode(jsNull());\n"); - } + unshift(@arguments, @callWithArgs); + my $jsType = NativeToJSValueUsingReferences($attribute, $interface, "${functionName}(" . join(", ", @arguments) . ")", "thisObject"); + push(@implContent, " auto& impl = thisObject.wrapped();\n") if !$attribute->isStatic; + push(@implContent, " JSValue result = $jsType;\n"); - push(@implContent, " setDOMException(exec, ec);\n"); - push(@implContent, " return JSValue::encode(result);\n"); + push(@implContent, " thisObject.m_" . $attribute->name . ".set(state.vm(), &thisObject, result);\n") if $attribute->extendedAttributes->{CachedAttribute}; + push(@implContent, " return result;\n"); } push(@implContent, "}\n\n"); - push(@implContent, "#endif\n") if $attributeConditionalString; + if ($attribute->extendedAttributes->{"DOMJIT"}) { + assert("Only DOMJIT=Getter is supported for attributes") unless $codeGenerator->ExtendedAttributeContains($attribute->extendedAttributes->{DOMJIT}, "Getter"); + + $implIncludes{"<wtf/NeverDestroyed.h>"} = 1; + $implIncludes{"DOMJITIDLTypeFilter.h"} = 1; + my $generatorName = $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name); + my $domJITClassName = $generatorName . "DOMJIT"; + my $getter = GetAttributeGetterName($interface, $generatorName, $attribute); + my $setter = IsReadonly($attribute) ? "nullptr" : GetAttributeSetterName($interface, $generatorName, $attribute); + my $resultType = GetResultTypeFilter($interface, $attribute->type); + push(@implContent, "${domJITClassName}::${domJITClassName}()\n"); + push(@implContent, " : JSC::DOMJIT::GetterSetter($getter, $setter, ${className}::info(), $resultType)\n"); + push(@implContent, "{\n"); + push(@implContent, "}\n\n"); - push(@implContent, "\n"); + push(@implContent, "JSC::DOMJIT::GetterSetter* domJITGetterSetterFor" . $generatorName . "()\n"); + push(@implContent, "{\n"); + push(@implContent, " static NeverDestroyed<${domJITClassName}> compiler;\n"); + push(@implContent, " return &compiler.get();\n"); + push(@implContent, "}\n\n"); + } + + push(@implContent, "#endif\n\n") if $attributeConditionalString; } - if (!$interface->extendedAttributes->{"NoInterfaceObject"}) { + if (NeedsConstructorProperty($interface)) { my $constructorFunctionName = "js" . $interfaceName . "Constructor"; - push(@implContent, "EncodedJSValue ${constructorFunctionName}(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue, PropertyName)\n"); + push(@implContent, "EncodedJSValue ${constructorFunctionName}(ExecState* state, EncodedJSValue thisValue, PropertyName)\n"); push(@implContent, "{\n"); - if ($interfaceName eq "DOMWindow") { - push(@implContent, " ${className}* domObject = jsDynamicCast<$className*>(JSValue::decode(thisValue));\n"); - push(@implContent, " if (!domObject) {\n"); - push(@implContent, " if (JSDOMWindowShell* shell = jsDynamicCast<JSDOMWindowShell*>(JSValue::decode(thisValue)))\n"); - push(@implContent, " domObject = shell->window();\n"); - push(@implContent, " }\n"); + push(@implContent, " VM& vm = state->vm();\n"); + push(@implContent, " auto throwScope = DECLARE_THROW_SCOPE(vm);\n"); + push(@implContent, " ${className}Prototype* domObject = jsDynamicDowncast<${className}Prototype*>(vm, JSValue::decode(thisValue));\n"); + push(@implContent, " if (UNLIKELY(!domObject))\n"); + push(@implContent, " return throwVMTypeError(state, throwScope);\n"); + + if (!$interface->extendedAttributes->{NoInterfaceObject}) { + push(@implContent, " return JSValue::encode(${className}::getConstructor(state->vm(), domObject->globalObject()));\n"); } else { - push(@implContent, " ${className}* domObject = jsDynamicCast<$className*>(JSValue::decode(thisValue));\n"); - push(@implContent, " if (!domObject)\n"); - push(@implContent, " return throwVMTypeError(exec);\n"); - } - push(@implContent, " if (!domObject)\n"); - push(@implContent, " return throwVMTypeError(exec);\n"); - - if ($interface->extendedAttributes->{"CheckSecurity"}) { - push(@implContent, " if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, domObject->impl()))\n"); - push(@implContent, " return JSValue::encode(jsUndefined());\n"); + push(@implContent, " JSValue constructor = ${className}Constructor::create(state->vm(), ${className}Constructor::createStructure(state->vm(), *domObject->globalObject(), domObject->globalObject()->objectPrototype()), *jsCast<JSDOMGlobalObject*>(domObject->globalObject()));\n"); + push(@implContent, " // Shadowing constructor property to ensure reusing the same constructor object\n"); + push(@implContent, " domObject->putDirect(state->vm(), state->propertyNames().constructor, constructor, DontEnum | ReadOnly);\n"); + push(@implContent, " return JSValue::encode(constructor);\n"); } - - push(@implContent, " return JSValue::encode(${className}::getConstructor(exec->vm(), domObject->globalObject()));\n"); push(@implContent, "}\n\n"); } + my $constructorFunctionName = "setJS" . $interfaceName . "Constructor"; - # Check if we have any writable attributes - my $hasReadWriteProperties = 0; - foreach my $attribute (@{$interface->attributes}) { - $hasReadWriteProperties = 1 if !IsReadonly($attribute) && !$attribute->isStatic; - } + push(@implContent, "bool ${constructorFunctionName}(ExecState* state, EncodedJSValue thisValue, EncodedJSValue encodedValue)\n"); + push(@implContent, "{\n"); + push(@implContent, " VM& vm = state->vm();\n"); + push(@implContent, " auto throwScope = DECLARE_THROW_SCOPE(vm);\n"); + push(@implContent, " JSValue value = JSValue::decode(encodedValue);\n"); + push(@implContent, " ${className}Prototype* domObject = jsDynamicDowncast<${className}Prototype*>(vm, JSValue::decode(thisValue));\n"); + push(@implContent, " if (UNLIKELY(!domObject)) {\n"); + push(@implContent, " throwVMTypeError(state, throwScope);\n"); + push(@implContent, " return false;\n"); + push(@implContent, " }\n"); - my $overridesPutImplementation = InstanceOverridesPutImplementation($interface); - my $hasSetter = $hasReadWriteProperties; + push(@implContent, " // Shadowing a built-in constructor\n"); - if ($hasSetter || $overridesPutImplementation) { - if ($overridesPutImplementation) { - push(@implContent, "void ${className}::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)\n"); - push(@implContent, "{\n"); - push(@implContent, " ${className}* thisObject = jsCast<${className}*>(cell);\n"); - push(@implContent, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n"); - if ($interface->extendedAttributes->{"CustomIndexedSetter"}) { - push(@implContent, " unsigned index = propertyName.asIndex();\n"); - push(@implContent, " if (index != PropertyName::NotAnIndex) {\n"); - push(@implContent, " thisObject->indexSetter(exec, index, value);\n"); - push(@implContent, " return;\n"); - push(@implContent, " }\n"); - } - if ($interface->extendedAttributes->{"CustomNamedSetter"}) { - push(@implContent, " if (thisObject->putDelegate(exec, propertyName, value, slot))\n"); - push(@implContent, " return;\n"); - } + push(@implContent, " return domObject->putDirect(state->vm(), state->propertyNames().constructor, value);\n"); + push(@implContent, "}\n\n"); + } + + foreach my $attribute (@{$interface->attributes}) { + if (!IsReadonly($attribute)) { + next if IsJSBuiltin($interface, $attribute); + + my $name = $attribute->name; + my $type = $attribute->type; + my $putFunctionName = GetAttributeSetterName($interface, $className, $attribute); + my $implSetterFunctionName = $codeGenerator->WK_ucfirst($name); - push(@implContent, " Base::put(thisObject, exec, propertyName, value, slot);\n"); + my $attributeConditionalString = $codeGenerator->GenerateConditionalString($attribute); + push(@implContent, "#if ${attributeConditionalString}\n") if $attributeConditionalString; + + if (!$attribute->isStatic) { + my $setterFunction = "${putFunctionName}Function"; + my $templateParameters = $setterFunction; + $templateParameters .= ", CastedThisErrorBehavior::ReturnEarly" if $attribute->extendedAttributes->{LenientThis}; + push(@implContent, "static inline bool ${setterFunction}(ExecState&, ${className}&, JSValue, ThrowScope&);\n\n"); + + push(@implContent, "bool ${putFunctionName}(ExecState* state, EncodedJSValue thisValue, EncodedJSValue encodedValue)\n"); + push(@implContent, "{\n"); + push(@implContent, " return BindingCaller<${className}>::setAttribute<${templateParameters}>(state, thisValue, encodedValue, \"$name\");\n"); push(@implContent, "}\n\n"); - if ($interface->extendedAttributes->{"CustomIndexedSetter"} || $interface->extendedAttributes->{"CustomNamedSetter"}) { - push(@implContent, "void ${className}::putByIndex(JSCell* cell, ExecState* exec, unsigned index, JSValue value, bool shouldThrow)\n"); - push(@implContent, "{\n"); - push(@implContent, " ${className}* thisObject = jsCast<${className}*>(cell);\n"); - push(@implContent, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n"); - if ($interface->extendedAttributes->{"CustomIndexedSetter"}) { - push(@implContent, " if (index <= MAX_ARRAY_INDEX) {\n"); - push(@implContent, " UNUSED_PARAM(shouldThrow);\n"); - push(@implContent, " thisObject->indexSetter(exec, index, value);\n"); - push(@implContent, " return;\n"); - push(@implContent, " }\n"); - } + push(@implContent, "static inline bool ${setterFunction}(ExecState& state, ${className}& thisObject, JSValue value, ThrowScope& throwScope)\n"); + push(@implContent, "{\n"); + push(@implContent, " UNUSED_PARAM(state);\n"); + push(@implContent, " UNUSED_PARAM(throwScope);\n"); + } else { + push(@implContent, "bool ${putFunctionName}(ExecState* statePointer, EncodedJSValue, EncodedJSValue encodedValue)\n"); + push(@implContent, "{\n"); + push(@implContent, " ASSERT(statePointer);\n"); + push(@implContent, " auto& state = *statePointer;\n"); + push(@implContent, " UNUSED_PARAM(state);\n"); + push(@implContent, " auto value = JSValue::decode(encodedValue);\n"); + } - if ($interface->extendedAttributes->{"CustomNamedSetter"}) { - push(@implContent, " PropertyName propertyName = Identifier::from(exec, index);\n"); - push(@implContent, " PutPropertySlot slot(thisObject, shouldThrow);\n"); - push(@implContent, " if (thisObject->putDelegate(exec, propertyName, value, slot))\n"); - push(@implContent, " return;\n"); - } + if ($attribute->extendedAttributes->{CEReactions}) { + push(@implContent, " CustomElementReactionStack customElementReactionStack;\n"); + $implIncludes{"CustomElementReactionQueue.h"} = 1; + } - push(@implContent, " Base::putByIndex(cell, exec, index, value, shouldThrow);\n"); - push(@implContent, "}\n\n"); + if ($interface->extendedAttributes->{CheckSecurity} && !$attribute->extendedAttributes->{DoNotCheckSecurity} && !$attribute->extendedAttributes->{DoNotCheckSecurityOnSetter}) { + AddToImplIncludes("JSDOMBindingSecurity.h"); + if ($interfaceName eq "DOMWindow") { + push(@implContent, " if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, thisObject.wrapped(), ThrowSecurityError))\n"); + } else { + push(@implContent, " if (!BindingSecurity::shouldAllowAccessToFrame(&state, thisObject.wrapped().frame(), ThrowSecurityError))\n"); } + push(@implContent, " return false;\n"); } - if ($hasReadWriteProperties) { - foreach my $attribute (@{$interface->attributes}) { - if (!IsReadonly($attribute)) { - my $name = $attribute->signature->name; - my $type = $attribute->signature->type; - my $putFunctionName = GetAttributeSetterName($interfaceName, $className, $attribute); - my $implSetterFunctionName = $codeGenerator->WK_ucfirst($name); - my $setterRaisesException = $attribute->signature->extendedAttributes->{"SetterRaisesException"}; - - my $attributeConditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); - push(@implContent, "#if ${attributeConditionalString}\n") if $attributeConditionalString; - - push(@implContent, "void ${putFunctionName}(ExecState* exec, JSObject* /* baseObject */, EncodedJSValue"); - push(@implContent, " thisValue") if !$attribute->isStatic; - push(@implContent, ", EncodedJSValue encodedValue)\n"); - push(@implContent, "{\n"); - push(@implContent, " JSValue value = JSValue::decode(encodedValue);\n"); - push(@implContent, " UNUSED_PARAM(exec);\n"); - if (!$attribute->isStatic) { - push(@implContent, " ${className}* castedThis = jsDynamicCast<${className}*>(JSValue::decode(thisValue));\n"); - if ($interfaceName eq "DOMWindow") { - push(@implContent, " if (!castedThis) {\n"); - push(@implContent, " if (JSDOMWindowShell* shell = jsDynamicCast<JSDOMWindowShell*>(JSValue::decode(thisValue)))\n"); - push(@implContent, " castedThis = shell->window();\n"); - push(@implContent, " }\n"); - } - push(@implContent, " if (!castedThis) {\n"); - push(@implContent, " throwVMTypeError(exec);\n"); - push(@implContent, " return;\n"); - push(@implContent, " }\n"); - } - if ($interface->extendedAttributes->{"CheckSecurity"} && !$attribute->signature->extendedAttributes->{"DoNotCheckSecurity"}) { - if ($interfaceName eq "DOMWindow") { - push(@implContent, " if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, castedThis->impl()))\n"); - } else { - push(@implContent, " if (!shouldAllowAccessToFrame(exec, castedThis->impl().frame()))\n"); - } - push(@implContent, " return;\n"); + if (HasCustomSetter($attribute->extendedAttributes)) { + push(@implContent, " thisObject.set$implSetterFunctionName(state, value);\n"); + push(@implContent, " return true;\n"); + } elsif ($type->name eq "EventHandler") { + $implIncludes{"JSEventListener.h"} = 1; + my $eventName = EventHandlerAttributeEventName($attribute); + # FIXME: Find a way to do this special case without hardcoding the class and attribute names here. + if ((($interfaceName eq "DOMWindow") or ($interfaceName eq "WorkerGlobalScope")) and $name eq "onerror") { + $implIncludes{"JSErrorHandler.h"} = 1; + push(@implContent, " thisObject.wrapped().setAttributeEventListener($eventName, createJSErrorHandler(&state, value, &thisObject), worldForDOMObject(&thisObject));\n"); + } else { + $implIncludes{"JSEventListener.h"} = 1; + my $setter = $attribute->extendedAttributes->{WindowEventHandler} ? "setWindowEventHandlerAttribute" + : $attribute->extendedAttributes->{DocumentEventHandler} ? "setDocumentEventHandlerAttribute" + : "setEventHandlerAttribute"; + push(@implContent, " $setter(state, thisObject, thisObject.wrapped(), $eventName, value);\n"); + } + push(@implContent, " return true;\n"); + } elsif ($codeGenerator->IsConstructorType($type)) { + my $constructorType = $type->name; + $constructorType =~ s/Constructor$//; + # $constructorType ~= /Constructor$/ indicates that it is NamedConstructor. + # We do not generate the header file for NamedConstructor of class XXXX, + # since we generate the NamedConstructor declaration into the header file of class XXXX. + if ($constructorType ne "any" and $constructorType !~ /Named$/) { + AddToImplIncludes("JS" . $constructorType . ".h", $attribute->extendedAttributes->{Conditional}); + } + push(@implContent, " // Shadowing a built-in constructor.\n"); + push(@implContent, " return thisObject.putDirect(state.vm(), Identifier::fromString(&state, \"$name\"), value);\n"); + } elsif ($attribute->extendedAttributes->{Replaceable}) { + push(@implContent, " // Shadowing a built-in property.\n"); + if (AttributeShouldBeOnInstance($interface, $attribute)) { + push(@implContent, " return replaceStaticPropertySlot(state.vm(), &thisObject, Identifier::fromString(&state, \"$name\"), value);\n"); + } else { + push(@implContent, " return thisObject.putDirect(state.vm(), Identifier::fromString(&state, \"$name\"), value);\n"); + } + } else { + if (!$attribute->isStatic) { + my $putForwards = $attribute->extendedAttributes->{PutForwards}; + if ($putForwards) { + my $implGetterFunctionName = $codeGenerator->WK_lcfirst($attribute->extendedAttributes->{ImplementedAs} || $name); + my $forwardedAttribute = $codeGenerator->GetAttributeFromInterface($interface, $type->name, $putForwards); + + if ($forwardedAttribute->extendedAttributes->{CEReactions}) { + push(@implContent, " CustomElementReactionStack customElementReactionStack;\n"); + $implIncludes{"CustomElementReactionQueue.h"} = 1; } - if (HasCustomSetter($attribute->signature->extendedAttributes)) { - push(@implContent, " castedThis->set$implSetterFunctionName(exec, value);\n"); - } elsif ($type eq "EventListener") { - $implIncludes{"JSEventListener.h"} = 1; - push(@implContent, " UNUSED_PARAM(exec);\n"); - my $windowEventListener = $attribute->signature->extendedAttributes->{"JSWindowEventListener"}; - if ($windowEventListener) { - push(@implContent, " JSDOMGlobalObject* globalObject = castedThis->globalObject();\n"); - } - push(@implContent, " $interfaceName& impl = castedThis->impl();\n"); - if ((($interfaceName eq "DOMWindow") or ($interfaceName eq "WorkerGlobalScope")) and $name eq "onerror") { - $implIncludes{"JSErrorHandler.h"} = 1; - push(@implContent, " impl.set$implSetterFunctionName(createJSErrorHandler(exec, value, castedThis));\n"); - } else { - push(@implContent, GenerateAttributeEventListenerCall($className, $implSetterFunctionName, $windowEventListener)); - } - } elsif ($attribute->signature->type =~ /Constructor$/) { - my $constructorType = $attribute->signature->type; - $constructorType =~ s/Constructor$//; - # $constructorType ~= /Constructor$/ indicates that it is NamedConstructor. - # We do not generate the header file for NamedConstructor of class XXXX, - # since we generate the NamedConstructor declaration into the header file of class XXXX. - if ($constructorType ne "any" and $constructorType !~ /Named$/) { - AddToImplIncludes("JS" . $constructorType . ".h", $attribute->signature->extendedAttributes->{"Conditional"}); - } - push(@implContent, " // Shadowing a built-in constructor\n"); - if ($interfaceName eq "DOMWindow" && $className eq "JSblah") { - # FIXME: This branch never executes and should be removed. - push(@implContent, " castedThis->putDirect(exec->vm(), exec->propertyNames().constructor, value);\n"); - } else { - push(@implContent, " castedThis->putDirect(exec->vm(), Identifier(exec, \"$name\"), value);\n"); - } - } elsif ($attribute->signature->extendedAttributes->{"Replaceable"}) { - push(@implContent, " // Shadowing a built-in object\n"); - push(@implContent, " castedThis->putDirect(exec->vm(), Identifier(exec, \"$name\"), value);\n"); + if ($type->isNullable) { + push(@implContent, " RefPtr<" . $type->name . "> forwardedImpl = thisObject.wrapped().${implGetterFunctionName}();\n"); + push(@implContent, " if (!forwardedImpl)\n"); + push(@implContent, " return false;\n"); + push(@implContent, " auto& impl = *forwardedImpl;\n"); } else { - if (!$attribute->isStatic) { - push(@implContent, " $implType& impl = castedThis->impl();\n"); - } - push(@implContent, " ExceptionCode ec = 0;\n") if $setterRaisesException; - - # If the "StrictTypeChecking" extended attribute is present, and the attribute's type is an - # interface type, then if the incoming value does not implement that interface, a TypeError - # is thrown rather than silently passing NULL to the C++ code. - # Per the Web IDL and ECMAScript specifications, incoming values can always be converted to - # both strings and numbers, so do not throw TypeError if the attribute is of these types. - if ($attribute->signature->extendedAttributes->{"StrictTypeChecking"}) { - $implIncludes{"<runtime/Error.h>"} = 1; - - my $argType = $attribute->signature->type; - if ($codeGenerator->IsWrapperType($argType)) { - push(@implContent, " if (!value.isUndefinedOrNull() && !value.inherits(JS${argType}::info())) {\n"); - push(@implContent, " throwVMTypeError(exec);\n"); - push(@implContent, " return;\n"); - push(@implContent, " };\n"); - } - } - - push(@implContent, " " . GetNativeTypeFromSignature($attribute->signature) . " nativeValue(" . JSValueToNative($attribute->signature, "value") . ");\n"); - push(@implContent, " if (exec->hadException())\n"); - push(@implContent, " return;\n"); - - if ($codeGenerator->IsEnumType($type)) { - my @enumValues = $codeGenerator->ValidEnumValues($type); - my @enumChecks = (); - foreach my $enumValue (@enumValues) { - push(@enumChecks, "nativeValue != \"$enumValue\""); - } - push (@implContent, " if (" . join(" && ", @enumChecks) . ")\n"); - push (@implContent, " return;\n"); - } - - if ($svgPropertyOrListPropertyType) { - if ($svgPropertyType) { - push(@implContent, " if (impl.isReadOnly()) {\n"); - push(@implContent, " setDOMException(exec, NO_MODIFICATION_ALLOWED_ERR);\n"); - push(@implContent, " return;\n"); - push(@implContent, " }\n"); - $implIncludes{"ExceptionCode.h"} = 1; - } - push(@implContent, " $svgPropertyOrListPropertyType& podImpl = impl.propertyReference();\n"); - if ($svgPropertyOrListPropertyType eq "float") { # Special case for JSSVGNumber - push(@implContent, " podImpl = nativeValue;\n"); - } else { - push(@implContent, " podImpl.set$implSetterFunctionName(nativeValue"); - push(@implContent, ", ec") if $setterRaisesException; - push(@implContent, ");\n"); - push(@implContent, " setDOMException(exec, ec);\n") if $setterRaisesException; - } - if ($svgPropertyType) { - if ($setterRaisesException) { - push(@implContent, " if (!ec)\n"); - push(@implContent, " impl.commitChange();\n"); - } else { - push(@implContent, " impl.commitChange();\n"); - } - } - } else { - my ($functionName, @arguments) = $codeGenerator->SetterExpression(\%implIncludes, $interfaceName, $attribute); - if ($codeGenerator->IsTypedArrayType($attribute->signature->type) and not $attribute->signature->type eq "ArrayBuffer") { - push(@arguments, "nativeValue.get()"); - } else { - push(@arguments, "nativeValue"); - } - if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) { - my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"}; - $implIncludes{"${implementedBy}.h"} = 1; - unshift(@arguments, "&impl") if !$attribute->isStatic; - $functionName = "${implementedBy}::${functionName}"; - } elsif ($attribute->isStatic) { - $functionName = "${interfaceName}::${functionName}"; - } else { - $functionName = "impl.${functionName}"; - } - - unshift(@arguments, GenerateCallWith($attribute->signature->extendedAttributes->{"CallWith"}, \@implContent, "")); - - push(@arguments, "ec") if $setterRaisesException; - push(@implContent, " ${functionName}(" . join(", ", @arguments) . ");\n"); - push(@implContent, " setDOMException(exec, ec);\n") if $setterRaisesException; - } + # Attribute is not nullable, the implementation is expected to return a reference. + push(@implContent, " Ref<" . $type->name . "> forwardedImpl = thisObject.wrapped().${implGetterFunctionName}();\n"); + push(@implContent, " auto& impl = forwardedImpl.get();\n"); } - - push(@implContent, "}\n\n"); - push(@implContent, "#endif\n") if $attributeConditionalString; - push(@implContent, "\n"); + $attribute = $forwardedAttribute; + $type = $attribute->type; + } else { + push(@implContent, " auto& impl = thisObject.wrapped();\n"); } } - } - } - if ($interface->extendedAttributes->{"ReplaceableConstructor"}) { - my $constructorFunctionName = "setJS" . $interfaceName . "Constructor"; + my $globalObjectReference = $attribute->isStatic ? "*jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject())" : "*thisObject.globalObject()"; + my $exceptionThrower = GetAttributeExceptionThrower($interface, $attribute); - push(@implContent, "void ${constructorFunctionName}(ExecState* exec, JSObject*, EncodedJSValue thisValue, EncodedJSValue encodedValue)\n"); - push(@implContent, "{\n"); - push(@implContent, " JSValue value = JSValue::decode(encodedValue);"); - push(@implContent, " ${className}* castedThis = jsDynamicCast<${className}*>(JSValue::decode(thisValue));\n"); - if ($interfaceName eq "DOMWindow") { - push(@implContent, " if (!castedThis) {\n"); - push(@implContent, " if (JSDOMWindowShell* shell = jsDynamicCast<JSDOMWindowShell*>(JSValue::decode(thisValue)))\n"); - push(@implContent, " castedThis = shell->window();\n"); - push(@implContent, " }\n"); - } - push(@implContent, " if (!castedThis) {\n"); - push(@implContent, " throwVMTypeError(exec);\n"); - push(@implContent, " return;\n"); - push(@implContent, " }\n"); - if ($interface->extendedAttributes->{"CheckSecurity"}) { - if ($interfaceName eq "DOMWindow") { - push(@implContent, " if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, castedThis->impl()))\n"); + my ($nativeValue, $mayThrowException) = JSValueToNative($interface, $attribute, "value", $attribute->extendedAttributes->{Conditional}, "&state", "state", "thisObject", $globalObjectReference, $exceptionThrower); + + push(@implContent, " auto nativeValue = $nativeValue;\n"); + push(@implContent, " RETURN_IF_EXCEPTION(throwScope, false);\n") if $mayThrowException; + + if ($codeGenerator->IsEnumType($type)) { + push (@implContent, " if (UNLIKELY(!nativeValue))\n"); + push (@implContent, " return false;\n"); + } + + my ($functionName, @arguments) = $codeGenerator->SetterExpression(\%implIncludes, $interfaceName, $attribute); + + push(@arguments, PassArgumentExpression("nativeValue", $attribute)); + + my $implementedBy = $attribute->extendedAttributes->{ImplementedBy}; + if ($implementedBy) { + AddToImplIncludes("${implementedBy}.h", $attribute->extendedAttributes->{Conditional}); + unshift(@arguments, "impl") if !$attribute->isStatic; + $functionName = "WebCore::${implementedBy}::${functionName}"; + } elsif ($attribute->isStatic) { + $functionName = "${interfaceName}::${functionName}"; } else { - push(@implContent, " if (!shouldAllowAccessToFrame(exec, castedThis->impl().frame()))\n"); + $functionName = "impl.${functionName}"; } - push(@implContent, " return;\n"); - } - push(@implContent, " // Shadowing a built-in constructor\n"); + unshift(@arguments, GenerateCallWithUsingReferences($attribute->extendedAttributes->{SetterCallWith}, \@implContent, "false")); + unshift(@arguments, GenerateCallWithUsingReferences($attribute->extendedAttributes->{CallWith}, \@implContent, "false")); - if ($interfaceName eq "DOMWindow") { - push(@implContent, " castedThis->putDirect(exec->vm(), exec->propertyNames().constructor, value);\n"); - } else { - die "No way to handle interface with ReplaceableConstructor extended attribute: $interfaceName"; + my $functionString = "$functionName(" . join(", ", @arguments) . ")"; + $functionString = "propagateException(state, throwScope, $functionString)" if $attribute->extendedAttributes->{SetterMayThrowException}; + + push(@implContent, " $functionString;\n"); + push(@implContent, " return true;\n"); } + push(@implContent, "}\n\n"); + push(@implContent, "#endif\n") if $attributeConditionalString; + push(@implContent, "\n"); } } - if ($indexedGetterFunction && !$interface->extendedAttributes->{"CustomEnumerateProperty"}) { - push(@implContent, "void ${className}::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)\n"); - push(@implContent, "{\n"); - push(@implContent, " ${className}* thisObject = jsCast<${className}*>(object);\n"); - push(@implContent, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n"); - push(@implContent, " for (unsigned i = 0, count = thisObject->impl().length(); i < count; ++i)\n"); - push(@implContent, " propertyNames.add(Identifier::from(exec, i));\n"); - push(@implContent, " Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode);\n"); - push(@implContent, "}\n\n"); - } - - if (!$interface->extendedAttributes->{"NoInterfaceObject"}) { - push(@implContent, "JSValue ${className}::getConstructor(VM& vm, JSGlobalObject* globalObject)\n{\n"); - push(@implContent, " return getDOMConstructor<${className}Constructor>(vm, jsCast<JSDOMGlobalObject*>(globalObject));\n"); + if (!$interface->extendedAttributes->{NoInterfaceObject}) { + push(@implContent, "JSValue ${className}::getConstructor(VM& vm, const JSGlobalObject* globalObject)\n{\n"); + push(@implContent, " return getDOMConstructor<${className}Constructor>(vm, *jsCast<const JSDOMGlobalObject*>(globalObject));\n"); push(@implContent, "}\n\n"); - if ($interface->extendedAttributes->{"NamedConstructor"}) { + if ($interface->extendedAttributes->{NamedConstructor}) { push(@implContent, "JSValue ${className}::getNamedConstructor(VM& vm, JSGlobalObject* globalObject)\n{\n"); - push(@implContent, " return getDOMConstructor<${className}NamedConstructor>(vm, jsCast<JSDOMGlobalObject*>(globalObject));\n"); + push(@implContent, " return getDOMConstructor<${className}NamedConstructor>(vm, *jsCast<JSDOMGlobalObject*>(globalObject));\n"); push(@implContent, "}\n\n"); } } @@ -2458,8 +3949,8 @@ sub GenerateImplementation if ($numFunctions > 0) { my $inAppleCopyright = 0; foreach my $function (@{$interface->functions}) { - my $needsAppleCopyright = $function->signature->extendedAttributes->{"AppleCopyright"}; - if ($needsAppleCopyright) { + next if IsJSBuiltin($interface, $function); + if ($function->extendedAttributes->{AppleCopyright}) { if (!$inAppleCopyright) { push(@implContent, $beginAppleCopyrightForSourceFiles); $inAppleCopyright = 1; @@ -2469,240 +3960,271 @@ sub GenerateImplementation $inAppleCopyright = 0; } - my $isCustom = HasCustomMethod($function->signature->extendedAttributes); + my $isCustom = HasCustomMethod($function->extendedAttributes); my $isOverloaded = $function->{overloads} && @{$function->{overloads}} > 1; - my $raisesException = $function->signature->extendedAttributes->{"RaisesException"}; next if $isCustom && $isOverloaded && $function->{overloadIndex} > 1; - AddIncludesForTypeInImpl($function->signature->type) unless $isCustom; + AddToImplIncludesForIDLType($function->type) unless $isCustom or IsReturningPromise($function); - my $functionName = GetFunctionName($className, $function); + my $functionName = GetFunctionName($interface, $className, $function); - my $conditional = $function->signature->extendedAttributes->{"Conditional"}; + my $conditional = $function->extendedAttributes->{Conditional}; if ($conditional) { my $conditionalString = $codeGenerator->GenerateConditionalStringFromAttributeValue($conditional); push(@implContent, "#if ${conditionalString}\n"); } - + my $functionReturn = "EncodedJSValue JSC_HOST_CALL"; if (!$isCustom && $isOverloaded) { # Append a number to an overloaded method's name to make it unique: $functionName = $functionName . $function->{overloadIndex}; - # Make this function static to avoid compiler warnings, since we - # don't generate a prototype for it in the header. - push(@implContent, "static "); + # Make this function static to avoid compiler warnings, since we don't generate a prototype for it in the header. + $functionReturn = "static inline EncodedJSValue"; + } + + my $functionImplementationName = $function->extendedAttributes->{ImplementedAs} || $codeGenerator->WK_lcfirst($function->name); + + AddToImplIncludes("JSDOMPromise.h") if IsReturningPromise($function); + + if (!$function->isStatic) { + my $classParameterType = $className eq "JSEventTarget" ? "JSEventTargetWrapper*" : "${className}*"; + my $optionalPromiseParameter = (IsReturningPromise($function) && !$isCustom) ? " Ref<DeferredPromise>&&," : ""; + push(@implContent, "static inline JSC::EncodedJSValue ${functionName}Caller(JSC::ExecState*, ${classParameterType},${optionalPromiseParameter} JSC::ThrowScope&);\n"); + push(@implContent, "\n"); } - my $functionImplementationName = $function->signature->extendedAttributes->{"ImplementedAs"} || $codeGenerator->WK_lcfirst($function->signature->name); + if (IsReturningPromise($function) && !$isCustom) { + my $scope = $interface->extendedAttributes->{Exposed} ? "WindowOrWorker" : "WindowOnly"; + push(@implContent, <<END); +static EncodedJSValue ${functionName}Promise(ExecState*, Ref<DeferredPromise>&&); + +${functionReturn} ${functionName}(ExecState* state) +{ + ASSERT(state); + return JSValue::encode(callPromiseFunction<${functionName}Promise, PromiseExecutionScope::${scope}>(*state)); +} + +static inline EncodedJSValue ${functionName}Promise(ExecState* state, Ref<DeferredPromise>&& promise) +END + } else { + push(@implContent, "${functionReturn} ${functionName}(ExecState* state)\n"); + } - push(@implContent, "EncodedJSValue JSC_HOST_CALL ${functionName}(ExecState* exec)\n"); push(@implContent, "{\n"); $implIncludes{"<runtime/Error.h>"} = 1; + if ($function->extendedAttributes->{CEReactions}) { + push(@implContent, " CustomElementReactionStack customElementReactionStack;\n"); + $implIncludes{"CustomElementReactionQueue.h"} = 1; + } + if ($function->isStatic) { if ($isCustom) { GenerateArgumentsCountCheck(\@implContent, $function, $interface); - push(@implContent, " return JSValue::encode(${className}::" . $functionImplementationName . "(exec));\n"); + push(@implContent, " return JSValue::encode(${className}::" . $functionImplementationName . "(state));\n"); } else { - GenerateArgumentsCountCheck(\@implContent, $function, $interface); + push(@implContent, " VM& vm = state->vm();\n"); + push(@implContent, " auto throwScope = DECLARE_THROW_SCOPE(vm);\n"); + push(@implContent, " UNUSED_PARAM(throwScope);\n"); - push(@implContent, " ExceptionCode ec = 0;\n") if $raisesException; + GenerateArgumentsCountCheck(\@implContent, $function, $interface); - my $numParameters = @{$function->parameters}; - my ($functionString, $dummy) = GenerateParametersCheck(\@implContent, $function, $interface, $numParameters, $interfaceName, $functionImplementationName, $svgPropertyType, $svgPropertyOrListPropertyType, $svgListPropertyType); - GenerateImplementationFunctionCall($function, $functionString, " ", $svgPropertyType, $interfaceName); + my ($functionString, $dummy) = GenerateParametersCheck(\@implContent, $function, $interface, $functionImplementationName); + GenerateImplementationFunctionCall($function, $functionString, " ", $interface); } } else { - if ($interfaceName eq "DOMWindow") { - push(@implContent, " $className* castedThis = toJSDOMWindow(exec->hostThisValue().toThis(exec, NotStrictMode));\n"); - push(@implContent, " if (!castedThis)\n"); - push(@implContent, " return throwVMTypeError(exec);\n"); - } elsif ($interface->extendedAttributes->{"WorkerGlobalScope"}) { - push(@implContent, " $className* castedThis = to${className}(exec->hostThisValue().toThis(exec, NotStrictMode));\n"); - push(@implContent, " if (!castedThis)\n"); - push(@implContent, " return throwVMTypeError(exec);\n"); + my $methodName = $function->name; + if (IsReturningPromise($function) && !$isCustom) { + my $templateParameters = "${functionName}Caller"; + $templateParameters .= ", CastedThisErrorBehavior::Assert" if ($function->extendedAttributes->{PrivateIdentifier} and not $function->extendedAttributes->{PublicIdentifier}); + push(@implContent, " return BindingCaller<$className>::callPromiseOperation<${templateParameters}>(state, WTFMove(promise), \"${methodName}\");\n"); + push(@implContent, "}\n"); + push(@implContent, "\n"); + push(@implContent, "static inline JSC::EncodedJSValue ${functionName}Caller(JSC::ExecState* state, ${className}* castedThis, Ref<DeferredPromise>&& promise, JSC::ThrowScope& throwScope)\n"); } else { - push(@implContent, " JSValue thisValue = exec->hostThisValue();\n"); - push(@implContent, " $className* castedThis = jsDynamicCast<$className*>(thisValue);\n"); - push(@implContent, " if (!castedThis)\n"); - push(@implContent, " return throwVMTypeError(exec);\n"); + my $classParameterType = $className eq "JSEventTarget" ? "JSEventTargetWrapper*" : "${className}*"; + my $templateParameters = "${functionName}Caller"; + if ($function->extendedAttributes->{PrivateIdentifier} and not $function->extendedAttributes->{PublicIdentifier}) { + $templateParameters .= ", CastedThisErrorBehavior::Assert"; + } elsif (IsReturningPromise($function)) { + # FIXME: We need this specific handling for custom promise-returning functions. + # It would be better to have the casted-this code calling the promise-specific code. + $templateParameters .= ", CastedThisErrorBehavior::RejectPromise" if IsReturningPromise($function); + } + + push(@implContent, " return BindingCaller<$className>::callOperation<${templateParameters}>(state, \"${methodName}\");\n"); + push(@implContent, "}\n"); + push(@implContent, "\n"); + push(@implContent, "static inline JSC::EncodedJSValue ${functionName}Caller(JSC::ExecState* state, ${classParameterType} castedThis, JSC::ThrowScope& throwScope)\n"); } - push(@implContent, " ASSERT_GC_OBJECT_INHERITS(castedThis, ${className}::info());\n"); + push(@implContent, "{\n"); + push(@implContent, " UNUSED_PARAM(state);\n"); + push(@implContent, " UNUSED_PARAM(throwScope);\n"); - if ($interface->extendedAttributes->{"CheckSecurity"} and - !$function->signature->extendedAttributes->{"DoNotCheckSecurity"}) { - push(@implContent, " if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, castedThis->impl()))\n"); + if ($interface->extendedAttributes->{CheckSecurity} and !$function->extendedAttributes->{DoNotCheckSecurity}) { + AddToImplIncludes("JSDOMBindingSecurity.h"); + if ($interfaceName eq "DOMWindow") { + push(@implContent, " if (!BindingSecurity::shouldAllowAccessToDOMWindow(state, castedThis->wrapped(), ThrowSecurityError))\n"); + } else { + push(@implContent, " if (!BindingSecurity::shouldAllowAccessToFrame(state, castedThis->wrapped().frame(), ThrowSecurityError))\n"); + } push(@implContent, " return JSValue::encode(jsUndefined());\n"); } if ($isCustom) { - push(@implContent, " return JSValue::encode(castedThis->" . $functionImplementationName . "(exec));\n"); + push(@implContent, " return JSValue::encode(castedThis->" . $functionImplementationName . "(*state));\n"); } else { - push(@implContent, " $implType& impl = castedThis->impl();\n"); - if ($svgPropertyType) { - push(@implContent, " if (impl.isReadOnly()) {\n"); - push(@implContent, " setDOMException(exec, NO_MODIFICATION_ALLOWED_ERR);\n"); - push(@implContent, " return JSValue::encode(jsUndefined());\n"); - push(@implContent, " }\n"); - push(@implContent, " $svgPropertyType& podImpl = impl.propertyReference();\n"); - $implIncludes{"ExceptionCode.h"} = 1; - } + push(@implContent, " auto& impl = castedThis->wrapped();\n"); - # For compatibility with legacy content, the EventListener calls are generated without GenerateArgumentsCountCheck. - if ($function->signature->name eq "addEventListener") { - push(@implContent, GenerateEventListenerCall($className, "add")); - } elsif ($function->signature->name eq "removeEventListener") { - push(@implContent, GenerateEventListenerCall($className, "remove")); - } else { - GenerateArgumentsCountCheck(\@implContent, $function, $interface); + GenerateArgumentsCountCheck(\@implContent, $function, $interface); + + my ($functionString, $dummy) = GenerateParametersCheck(\@implContent, $function, $interface, $functionImplementationName); + GenerateImplementationFunctionCall($function, $functionString, " ", $interface); + } + } - push(@implContent, " ExceptionCode ec = 0;\n") if $raisesException; + push(@implContent, "}\n\n"); - if ($function->signature->extendedAttributes->{"CheckSecurityForNode"}) { - push(@implContent, " if (!shouldAllowAccessToNode(exec, impl." . $function->signature->name . "(" . ($raisesException ? "ec" : "") .")))\n"); - push(@implContent, " return JSValue::encode(jsNull());\n"); - $implIncludes{"JSDOMBinding.h"} = 1; - } + if ($function->extendedAttributes->{DOMJIT}) { + $implIncludes{"<interpreter/FrameTracers.h>"} = 1; + my $unsafeFunctionName = "unsafe" . $codeGenerator->WK_ucfirst($functionName); + push(@implContent, "JSC::EncodedJSValue JIT_OPERATION ${unsafeFunctionName}(JSC::ExecState* state, $className* castedThis"); + foreach my $argument (@{$function->arguments}) { + my $type = $argument->type; + my $argumentType = GetUnsafeArgumentType($interface, $type); + my $name = $argument->name; + my $encodedName = "encoded" . $codeGenerator->WK_ucfirst($name); + push(@implContent, ", ${argumentType} ${encodedName}"); + } + push(@implContent, ")\n"); + push(@implContent, "{\n"); + push(@implContent, " UNUSED_PARAM(state);\n"); + push(@implContent, " VM& vm = state->vm();\n"); + push(@implContent, " JSC::NativeCallFrameTracer tracer(&vm, state);\n"); + push(@implContent, " auto throwScope = DECLARE_THROW_SCOPE(vm);\n"); + push(@implContent, " UNUSED_PARAM(throwScope);\n"); + push(@implContent, " auto& impl = castedThis->wrapped();\n"); + my @arguments; + my $implFunctionName; + my $implementedBy = $function->extendedAttributes->{ImplementedBy}; + + if ($implementedBy) { + AddToImplIncludes("${implementedBy}.h", $function->extendedAttributes->{Conditional}); + unshift(@arguments, "impl") if !$function->isStatic; + $implFunctionName = "WebCore::${implementedBy}::${functionImplementationName}"; + } elsif ($function->isStatic) { + $implFunctionName = "${interfaceName}::${functionImplementationName}"; + } else { + $implFunctionName = "impl.${functionImplementationName}"; + } - my $numParameters = @{$function->parameters}; - my ($functionString, $dummy) = GenerateParametersCheck(\@implContent, $function, $interface, $numParameters, $interfaceName, $functionImplementationName, $svgPropertyType, $svgPropertyOrListPropertyType, $svgListPropertyType); - GenerateImplementationFunctionCall($function, $functionString, " ", $svgPropertyType, $interfaceName); + foreach my $argument (@{$function->arguments}) { + my $value = ""; + my $type = $argument->type; + my $name = $argument->name; + my $encodedName = "encoded" . $codeGenerator->WK_ucfirst($name); + my $nativeType = GetNativeType($interface, $argument->type); + my $shouldPassByReference = ShouldPassArgumentByReference($argument); + + if (!$shouldPassByReference && ($codeGenerator->IsWrapperType($type) || $codeGenerator->IsTypedArrayType($type))) { + $implIncludes{"<runtime/Error.h>"} = 1; + my ($nativeValue, $mayThrowException) = UnsafeToNative($interface, $argument, $encodedName, $function->extendedAttributes->{Conditional}); + push(@implContent, " $nativeType $name = nullptr;\n"); + push(@implContent, " $name = $nativeValue;\n"); + push(@implContent, " RETURN_IF_EXCEPTION(throwScope, encodedJSValue());\n") if $mayThrowException; + $value = "WTFMove($name)"; + } else { + my ($nativeValue, $mayThrowException) = UnsafeToNative($interface, $argument, $encodedName, $function->extendedAttributes->{Conditional}); + push(@implContent, " auto $name = ${nativeValue};\n"); + $value = "WTFMove($name)"; + push(@implContent, " RETURN_IF_EXCEPTION(throwScope, encodedJSValue());\n") if $mayThrowException; + } + + if ($shouldPassByReference) { + $value = "*$name"; } + push(@arguments, $value); } + my $functionString = "$implFunctionName(" . join(", ", @arguments) . ")"; + $functionString = "propagateException(*state, throwScope, $functionString)" if NeedsExplicitPropagateExceptionCall($function); + push(@implContent, " return JSValue::encode(" . NativeToJSValueUsingPointers($function, $interface, $functionString, "castedThis") . ");\n"); + push(@implContent, "}\n\n"); } - push(@implContent, "}\n\n"); push(@implContent, "#endif\n\n") if $conditional; - if (!$isCustom && $isOverloaded && $function->{overloadIndex} == @{$function->{overloads}}) { - # Generate a function dispatching call to the rest of the overloads. - GenerateOverloadedFunction($function, $interface, $interfaceName); - } - + # Generate a function dispatching call to the rest of the overloads. + GenerateOverloadedFunctionOrConstructor($function, $interface, 0) if !$isCustom && $isOverloaded && $function->{overloadIndex} == @{$function->{overloads}}; } push(@implContent, $endAppleCopyright) if $inAppleCopyright; } - if ($needsMarkChildren && !$interface->extendedAttributes->{"JSCustomMarkFunction"}) { + + GenerateImplementationIterableFunctions($interface) if $interface->iterable; + GenerateSerializerFunction($interface, $className) if $interface->serializable; + + if ($needsVisitChildren) { push(@implContent, "void ${className}::visitChildren(JSCell* cell, SlotVisitor& visitor)\n"); push(@implContent, "{\n"); - push(@implContent, " ${className}* thisObject = jsCast<${className}*>(cell);\n"); + push(@implContent, " auto* thisObject = jsCast<${className}*>(cell);\n"); push(@implContent, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n"); - push(@implContent, " COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);\n"); - push(@implContent, " ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());\n"); push(@implContent, " Base::visitChildren(thisObject, visitor);\n"); - if ($interface->extendedAttributes->{"EventTarget"} || $interface->name eq "EventTarget") { - push(@implContent, " thisObject->impl().visitJSEventListeners(visitor);\n"); + push(@implContent, " thisObject->visitAdditionalChildren(visitor);\n") if $interface->extendedAttributes->{JSCustomMarkFunction}; + if ($interface->extendedAttributes->{ReportExtraMemoryCost}) { + push(@implContent, " visitor.reportExtraMemoryVisited(thisObject->wrapped().memoryCost());\n"); + if ($interface->extendedAttributes->{ReportExternalMemoryCost}) {; + push(@implContent, "#if ENABLE(RESOURCE_USAGE)\n"); + push(@implContent, " visitor.reportExternalMemoryVisited(thisObject->wrapped().externalMemoryCost());\n"); + push(@implContent, "#endif\n"); + } } if ($numCachedAttributes > 0) { foreach (@{$interface->attributes}) { my $attribute = $_; - if ($attribute->signature->extendedAttributes->{"CachedAttribute"}) { - push(@implContent, " visitor.append(&thisObject->m_" . $attribute->signature->name . ");\n"); + if ($attribute->extendedAttributes->{CachedAttribute}) { + push(@implContent, " visitor.append(thisObject->m_" . $attribute->name . ");\n"); } } } push(@implContent, "}\n\n"); - } - - # Cached attributes are indeed allowed when there is a custom mark/visitChildren function. - # The custom function must make sure to account for the cached attribute. - # Uncomment the below line to temporarily enforce generated mark functions when cached attributes are present. - # die "Can't generate binding for class with cached attribute and custom mark." if (($numCachedAttributes > 0) and ($interface->extendedAttributes->{"JSCustomMarkFunction"})); - - if ($numConstants > 0) { - push(@implContent, "// Constant getters\n\n"); - - foreach my $constant (@{$interface->constants}) { - my $getter = "js" . $interfaceName . $codeGenerator->WK_ucfirst($constant->name); - my $conditional = $constant->extendedAttributes->{"Conditional"}; - - if ($conditional) { - my $conditionalString = $codeGenerator->GenerateConditionalStringFromAttributeValue($conditional); - push(@implContent, "#if ${conditionalString}\n"); - } - - # FIXME: this casts into int to match our previous behavior which turned 0xFFFFFFFF in -1 for NodeFilter.SHOW_ALL - push(@implContent, "EncodedJSValue ${getter}(ExecState* exec, EncodedJSValue, EncodedJSValue, PropertyName)\n"); + if ($interface->extendedAttributes->{JSCustomMarkFunction}) { + push(@implContent, "void ${className}::visitOutputConstraints(JSCell* cell, SlotVisitor& visitor)\n"); push(@implContent, "{\n"); - if ($constant->type eq "DOMString") { - push(@implContent, " return JSValue::encode(jsStringOrNull(exec, String(" . $constant->value . ")));\n"); - } else { - push(@implContent, " UNUSED_PARAM(exec);\n"); - push(@implContent, " return JSValue::encode(jsNumber(static_cast<int>(" . $constant->value . ")));\n"); - } + push(@implContent, " auto* thisObject = jsCast<${className}*>(cell);\n"); + push(@implContent, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n"); + push(@implContent, " Base::visitOutputConstraints(thisObject, visitor);\n"); + push(@implContent, " thisObject->visitAdditionalChildren(visitor);\n"); push(@implContent, "}\n\n"); - push(@implContent, "#endif\n") if $conditional; } } - if ($indexedGetterFunction && !$hasNumericIndexedGetter) { - push(@implContent, "\nEncodedJSValue ${className}::indexGetter(ExecState* exec, EncodedJSValue slotBase, EncodedJSValue, unsigned index)\n"); + if (InstanceNeedsEstimatedSize($interface)) { + push(@implContent, "size_t ${className}::estimatedSize(JSCell* cell)\n"); push(@implContent, "{\n"); - push(@implContent, " ${className}* thisObj = jsCast<$className*>(JSValue::decode(slotBase));\n"); - push(@implContent, " ASSERT_GC_OBJECT_INHERITS(thisObj, info());\n"); - if ($indexedGetterFunction->signature->type eq "DOMString") { - $implIncludes{"URL.h"} = 1; - push(@implContent, " return JSValue::encode(jsStringOrUndefined(exec, thisObj->impl().item(index)));\n"); - } else { - push(@implContent, " return JSValue::encode(toJS(exec, thisObj->globalObject(), thisObj->impl().item(index)));\n"); - } + push(@implContent, " auto* thisObject = jsCast<${className}*>(cell);\n"); + push(@implContent, " return Base::estimatedSize(thisObject) + thisObject->wrapped().memoryCost();\n"); push(@implContent, "}\n\n"); - if ($interfaceName =~ /^HTML\w*Collection$/ or $interfaceName eq "RadioNodeList") { - $implIncludes{"JSNode.h"} = 1; - $implIncludes{"Node.h"} = 1; - } } - if ($hasNumericIndexedGetter) { - push(@implContent, "\nJSValue ${className}::getByIndex(ExecState*, unsigned index)\n"); - push(@implContent, "{\n"); - push(@implContent, " ASSERT_GC_OBJECT_INHERITS(this, info());\n"); - push(@implContent, " double result = impl().item(index);\n"); - # jsNumber conversion doesn't suppress signalling NaNs, so enforce that here. - push(@implContent, " if (std::isnan(result))\n"); - push(@implContent, " return jsNaN();\n"); - push(@implContent, " return JSValue(result);\n"); - push(@implContent, "}\n\n"); - if ($interfaceName =~ /^HTML\w*Collection$/) { + # Cached attributes are indeed allowed when there is a custom mark/visitChildren function. + # The custom function must make sure to account for the cached attribute. + # Uncomment the below line to temporarily enforce generated mark functions when cached attributes are present. + # die "Can't generate binding for class with cached attribute and custom mark." if $numCachedAttributes > 0 and $interface->extendedAttributes->{JSCustomMarkFunction}; + + if ($indexedGetterFunction) { + $implIncludes{"URL.h"} = 1 if $indexedGetterFunction->type->name eq "DOMString"; + if ($interfaceName =~ /^HTML\w*Collection$/ or $interfaceName eq "RadioNodeList") { $implIncludes{"JSNode.h"} = 1; $implIncludes{"Node.h"} = 1; } } - if ($interfaceName eq "DOMNamedFlowCollection") { - if ($namedGetterFunction) { - push(@implContent, "bool ${className}::canGetItemsForName(ExecState*, $interfaceName* collection, PropertyName propertyName)\n"); - push(@implContent, "{\n"); - push(@implContent, " return collection->hasNamedItem(propertyNameToAtomicString(propertyName));\n"); - push(@implContent, "}\n\n"); - push(@implContent, "EncodedJSValue ${className}::nameGetter(ExecState* exec, EncodedJSValue slotBase, EncodedJSValue, PropertyName propertyName)\n"); - push(@implContent, "{\n"); - push(@implContent, " ${className}* thisObj = jsCast<$className*>(JSValue::decode(slotBase));\n"); - push(@implContent, " return JSValue::encode(toJS(exec, thisObj->globalObject(), thisObj->impl().namedItem(propertyNameToAtomicString(propertyName))));\n"); - push(@implContent, "}\n\n"); - } - } - - if ((!$hasParent && !GetCustomIsReachable($interface)) || GetGenerateIsReachable($interface) || $codeGenerator->InheritsExtendedAttribute($interface, "ActiveDOMObject")) { - if (GetGenerateIsReachable($interface)) { - push(@implContent, "static inline bool isObservable(JS${interfaceName}* js${interfaceName})\n"); - push(@implContent, "{\n"); - push(@implContent, " if (js${interfaceName}->hasCustomProperties())\n"); - push(@implContent, " return true;\n"); - if ($eventTarget) { - push(@implContent, " if (js${interfaceName}->impl().hasEventListeners())\n"); - push(@implContent, " return true;\n"); - } - push(@implContent, " return false;\n"); - push(@implContent, "}\n\n"); - } - + if (ShouldGenerateWrapperOwnerCode($hasParent, $interface) && !GetCustomIsReachable($interface)) { push(@implContent, "bool JS${interfaceName}Owner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, SlotVisitor& visitor)\n"); push(@implContent, "{\n"); # All ActiveDOMObjects implement hasPendingActivity(), but not all of them @@ -2711,25 +4233,25 @@ sub GenerateImplementation # their pending activities complete. To wallpaper over this bug, JavaScript # wrappers unconditionally keep ActiveDOMObjects with pending activity alive. # FIXME: Fix this lifetime issue in the DOM, and move this hasPendingActivity - # check below the isObservable check. + # check just above the (GetGenerateIsReachable($interface) eq "Impl") check below. my $emittedJSCast = 0; if ($codeGenerator->InheritsExtendedAttribute($interface, "ActiveDOMObject")) { - push(@implContent, " JS${interfaceName}* js${interfaceName} = jsCast<JS${interfaceName}*>(handle.get().asCell());\n"); + push(@implContent, " auto* js${interfaceName} = jsCast<JS${interfaceName}*>(handle.slot()->asCell());\n"); $emittedJSCast = 1; - push(@implContent, " if (js${interfaceName}->impl().hasPendingActivity())\n"); + push(@implContent, " if (js${interfaceName}->wrapped().hasPendingActivity())\n"); push(@implContent, " return true;\n"); } - if ($codeGenerator->InheritsExtendedAttribute($interface, "EventTarget")) { + if ($codeGenerator->InheritsInterface($interface, "EventTarget")) { if (!$emittedJSCast) { - push(@implContent, " JS${interfaceName}* js${interfaceName} = jsCast<JS${interfaceName}*>(handle.get().asCell());\n"); + push(@implContent, " auto* js${interfaceName} = jsCast<JS${interfaceName}*>(handle.slot()->asCell());\n"); $emittedJSCast = 1; } - push(@implContent, " if (js${interfaceName}->impl().isFiringEventListeners())\n"); + push(@implContent, " if (js${interfaceName}->wrapped().isFiringEventListeners())\n"); push(@implContent, " return true;\n"); } if ($codeGenerator->InheritsInterface($interface, "Node")) { if (!$emittedJSCast) { - push(@implContent, " JS${interfaceName}* js${interfaceName} = jsCast<JS${interfaceName}*>(handle.get().asCell());\n"); + push(@implContent, " auto* js${interfaceName} = jsCast<JS${interfaceName}*>(handle.slot()->asCell());\n"); $emittedJSCast = 1; } push(@implContent, " if (JSNodeOwner::isReachableFromOpaqueRoots(handle, 0, visitor))\n"); @@ -2737,40 +4259,40 @@ sub GenerateImplementation } if (GetGenerateIsReachable($interface)) { if (!$emittedJSCast) { - push(@implContent, " JS${interfaceName}* js${interfaceName} = jsCast<JS${interfaceName}*>(handle.get().asCell());\n"); + push(@implContent, " auto* js${interfaceName} = jsCast<JS${interfaceName}*>(handle.slot()->asCell());\n"); $emittedJSCast = 1; } - push(@implContent, " if (!isObservable(js${interfaceName}))\n"); - push(@implContent, " return false;\n"); my $rootString; if (GetGenerateIsReachable($interface) eq "Impl") { - $rootString = " ${implType}* root = &js${interfaceName}->impl();\n"; + $rootString = " ${implType}* root = &js${interfaceName}->wrapped();\n"; } elsif (GetGenerateIsReachable($interface) eq "ImplWebGLRenderingContext") { - $rootString = " WebGLRenderingContext* root = js${interfaceName}->impl().context();\n"; + $rootString = " WebGLRenderingContextBase* root = WTF::getPtr(js${interfaceName}->wrapped().context());\n"; } elsif (GetGenerateIsReachable($interface) eq "ImplFrame") { - $rootString = " Frame* root = js${interfaceName}->impl().frame();\n"; + $rootString = " Frame* root = WTF::getPtr(js${interfaceName}->wrapped().frame());\n"; $rootString .= " if (!root)\n"; $rootString .= " return false;\n"; } elsif (GetGenerateIsReachable($interface) eq "ImplDocument") { - $rootString = " Document* root = js${interfaceName}->impl().document();\n"; + $rootString = " Document* root = WTF::getPtr(js${interfaceName}->wrapped().document());\n"; $rootString .= " if (!root)\n"; $rootString .= " return false;\n"; } elsif (GetGenerateIsReachable($interface) eq "ImplElementRoot") { $implIncludes{"Element.h"} = 1; $implIncludes{"JSNodeCustom.h"} = 1; - $rootString = " Element* element = js${interfaceName}->impl().element();\n"; + $rootString = " Element* element = WTF::getPtr(js${interfaceName}->wrapped().element());\n"; $rootString .= " if (!element)\n"; $rootString .= " return false;\n"; $rootString .= " void* root = WebCore::root(element);\n"; - } elsif ($interfaceName eq "CanvasRenderingContext") { - $rootString = " void* root = WebCore::root(js${interfaceName}->impl().canvas());\n"; } elsif (GetGenerateIsReachable($interface) eq "ImplOwnerNodeRoot") { $implIncludes{"Element.h"} = 1; $implIncludes{"JSNodeCustom.h"} = 1; - $rootString = " void* root = WebCore::root(js${interfaceName}->impl().ownerNode());\n"; + $rootString = " void* root = WebCore::root(js${interfaceName}->wrapped().ownerNode());\n"; + } elsif (GetGenerateIsReachable($interface) eq "ImplScriptExecutionContext") { + $rootString = " ScriptExecutionContext* root = WTF::getPtr(js${interfaceName}->wrapped().scriptExecutionContext());\n"; + $rootString .= " if (!root)\n"; + $rootString .= " return false;\n"; } else { - $rootString = " void* root = WebCore::root(&js${interfaceName}->impl());\n"; + $rootString = " void* root = WebCore::root(&js${interfaceName}->wrapped());\n"; } push(@implContent, $rootString); @@ -2785,17 +4307,12 @@ sub GenerateImplementation push(@implContent, "}\n\n"); } - if (!$interface->extendedAttributes->{"JSCustomFinalize"} && - (!$hasParent || - GetGenerateIsReachable($interface) || - GetCustomIsReachable($interface) || - $codeGenerator->InheritsExtendedAttribute($interface, "ActiveDOMObject"))) { + if (ShouldGenerateWrapperOwnerCode($hasParent, $interface) && !$interface->extendedAttributes->{JSCustomFinalize}) { push(@implContent, "void JS${interfaceName}Owner::finalize(JSC::Handle<JSC::Unknown> handle, void* context)\n"); push(@implContent, "{\n"); - push(@implContent, " JS${interfaceName}* js${interfaceName} = jsCast<JS${interfaceName}*>(handle.get().asCell());\n"); - push(@implContent, " DOMWrapperWorld& world = *static_cast<DOMWrapperWorld*>(context);\n"); - push(@implContent, " uncacheWrapper(world, &js${interfaceName}->impl(), js${interfaceName});\n"); - push(@implContent, " js${interfaceName}->releaseImpl();\n"); + push(@implContent, " auto* js${interfaceName} = static_cast<JS${interfaceName}*>(handle.slot()->asCell());\n"); + push(@implContent, " auto& world = *static_cast<DOMWrapperWorld*>(context);\n"); + push(@implContent, " uncacheWrapper(world, &js${interfaceName}->wrapped(), js${interfaceName});\n"); push(@implContent, "}\n\n"); } @@ -2813,26 +4330,15 @@ extern "C" { extern void (*const ${vtableRefWin}[])(); } extern "C" { extern void* ${vtableNameGnu}[]; } #endif #endif -END - push(@implContent, "JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, $implType* impl)\n"); - push(@implContent, "{\n"); - push(@implContent, <<END); - if (!impl) - return jsNull(); END - if ($svgPropertyType) { - push(@implContent, " if (JSValue result = getExistingWrapper<$className, $implType>(exec, impl))\n"); - push(@implContent, " return result;\n"); - } else { - push(@implContent, " if (JSValue result = getExistingWrapper<$className>(exec, impl))\n"); - push(@implContent, " return result;\n"); - } + push(@implContent, "JSC::JSValue toJSNewlyCreated(JSC::ExecState*, JSDOMGlobalObject* globalObject, Ref<$implType>&& impl)\n"); + push(@implContent, "{\n"); push(@implContent, <<END) if $vtableNameGnu; #if ENABLE(BINDING_INTEGRITY) - void* actualVTablePointer = *(reinterpret_cast<void**>(impl)); + void* actualVTablePointer = *(reinterpret_cast<void**>(impl.ptr())); #if PLATFORM(WIN) void* expectedVTablePointer = reinterpret_cast<void*>(${vtableRefWin}); #else @@ -2840,7 +4346,7 @@ END #if COMPILER(CLANG) // If this fails $implType does not have a vtable, so you need to add the // ImplementationLacksVTable attribute to the interface definition - COMPILE_ASSERT(__is_polymorphic($implType), ${implType}_is_not_polymorphic); + static_assert(__is_polymorphic($implType), "${implType} is not polymorphic"); #endif #endif // If you hit this assertion you either have a use after free bug, or @@ -2850,33 +4356,35 @@ END RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); #endif END - push(@implContent, <<END) if $interface->extendedAttributes->{"ImplementationLacksVTable"}; + push(@implContent, <<END) if $interface->extendedAttributes->{ImplementationLacksVTable}; #if COMPILER(CLANG) // If you hit this failure the interface definition has the ImplementationLacksVTable // attribute. You should remove that attribute. If the class has subclasses // that may be passed through this toJS() function you should use the SkipVTableValidation // attribute to $interfaceName. - COMPILE_ASSERT(!__is_polymorphic($implType), ${implType}_is_polymorphic_but_idl_claims_not_to_be); + static_assert(!__is_polymorphic($implType), "${implType} is polymorphic but the IDL claims it is not"); #endif END - push(@implContent, <<END); - ReportMemoryCost<$implType>::reportMemoryCost(exec, impl); + push(@implContent, <<END) if $interface->extendedAttributes->{ReportExtraMemoryCost}; + globalObject->vm().heap.reportExtraMemoryAllocated(impl->memoryCost()); END - if ($svgPropertyType) { - push(@implContent, " return createNewWrapper<$className, $implType>(exec, globalObject, impl);\n"); - } else { - push(@implContent, " return createNewWrapper<$className>(exec, globalObject, impl);\n"); - } + push(@implContent, " return createWrapper<$implType>(globalObject, WTFMove(impl));\n"); + push(@implContent, "}\n\n"); + push(@implContent, "JSC::JSValue toJS(JSC::ExecState* state, JSDOMGlobalObject* globalObject, $implType& impl)\n"); + push(@implContent, "{\n"); + push(@implContent, " return wrap(state, globalObject, impl);\n"); push(@implContent, "}\n\n"); } - if ((!$hasParent or $interface->extendedAttributes->{"JSGenerateToNativeObject"}) and !$interface->extendedAttributes->{"JSCustomToNativeObject"}) { - push(@implContent, "$implType* to${interfaceName}(JSC::JSValue value)\n"); + if (ShouldGenerateToWrapped($hasParent, $interface) and !$interface->extendedAttributes->{JSCustomToNativeObject}) { + push(@implContent, "$implType* ${className}::toWrapped(JSC::VM& vm, JSC::JSValue value)\n"); push(@implContent, "{\n"); - push(@implContent, " return value.inherits(${className}::info()) ? &jsCast<$className*>(value)->impl() : 0"); - push(@implContent, ";\n}\n"); + push(@implContent, " if (auto* wrapper = " . GetCastingHelperForThisObject($interface) . "(vm, value))\n"); + push(@implContent, " return &wrapper->wrapped();\n"); + push(@implContent, " return nullptr;\n"); + push(@implContent, "}\n"); } push(@implContent, "\n}\n"); @@ -2885,30 +4393,160 @@ END push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString; } +sub GenerateSerializerFunction +{ + my ($interface, $className) = @_; + + my $interfaceName = $interface->type->name; + + my $parentSerializerInterface = 0; + if ($interface->serializable->hasInherit) { + $codeGenerator->ForAllParents($interface, sub { + my $parentInterface = shift; + if ($parentInterface->serializable && !$parentSerializerInterface) { + $parentSerializerInterface = $parentInterface; + } + }, 0); + die "Failed to find parent interface with \"serializer\" for \"inherit\" serializer in $interfaceName\n" if !$parentSerializerInterface; + } + + my @serializedAttributes = (); + + foreach my $attributeName (@{$interface->serializable->attributes}) { + my $foundAttribute = 0; + foreach my $attribute (@{$interface->attributes}) { + if ($attributeName eq $attribute->name) { + $foundAttribute = 1; + if ($codeGenerator->IsSerializableAttribute($interface, $attribute)) { + push(@serializedAttributes, $attribute); + last; + } + die "Explicit \"serializer\" attribute \"$attributeName\" is not serializable\n" if !$interface->serializable->hasAttribute; + last; + } + } + die "Failed to find \"serializer\" attribute \"$attributeName\" in $interfaceName\n" if !$foundAttribute; + } + + my $serializerFunctionName = "toJSON"; + my $serializerNativeFunctionName = $codeGenerator->WK_lcfirst($className) . "PrototypeFunction" . $codeGenerator->WK_ucfirst($serializerFunctionName); + + AddToImplIncludes("<runtime/ObjectConstructor.h>"); + + push(@implContent, "JSC::JSObject* JS${interfaceName}::serialize(ExecState* state, JS${interfaceName}* thisObject, ThrowScope& throwScope)\n"); + push(@implContent, "{\n"); + push(@implContent, " auto& vm = state->vm();\n"); + + if ($interface->serializable->hasInherit) { + my $parentSerializerInterfaceName = $parentSerializerInterface->type->name; + push(@implContent, " auto* result = JS${parentSerializerInterfaceName}::serialize(state, thisObject, throwScope);\n"); + } else { + push(@implContent, " auto* result = constructEmptyObject(state);\n"); + } + push(@implContent, "\n"); + + foreach my $attribute (@serializedAttributes) { + my $name = $attribute->name; + my $getFunctionName = GetAttributeGetterName($interface, $className, $attribute); + push(@implContent, " auto ${name}Value = ${getFunctionName}Getter(*state, *thisObject, throwScope);\n"); + push(@implContent, " ASSERT(!throwScope.exception());\n"); + push(@implContent, " result->putDirect(vm, Identifier::fromString(&vm, \"${name}\"), ${name}Value);\n"); + push(@implContent, "\n"); + } + + push(@implContent, " return result;\n"); + push(@implContent, "}\n"); + push(@implContent, "\n"); + + push(@implContent, "static inline EncodedJSValue ${serializerNativeFunctionName}Caller(ExecState* state, JS${interfaceName}* thisObject, JSC::ThrowScope& throwScope)\n"); + push(@implContent, "{\n"); + push(@implContent, " return JSValue::encode(JS${interfaceName}::serialize(state, thisObject, throwScope));\n"); + push(@implContent, "}\n"); + push(@implContent, "\n"); + push(@implContent, "EncodedJSValue JSC_HOST_CALL ${serializerNativeFunctionName}(ExecState* state)\n"); + push(@implContent, "{\n"); + push(@implContent, " return BindingCaller<JS$interfaceName>::callOperation<${serializerNativeFunctionName}Caller>(state, \"$serializerFunctionName\");\n"); + push(@implContent, "}\n"); + push(@implContent, "\n"); +} + +sub GenerateCallWithUsingReferences +{ + my ($callWith, $outputArray, $returnValue, $function) = @_; + + my $statePointer = "&state"; + my $stateReference = "state"; + my $globalObject = "jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject())"; + + return GenerateCallWith($callWith, $outputArray, $returnValue, $function, $statePointer, $stateReference, $globalObject); +} + +# FIXME: We should remove GenerateCallWithUsingPointers and combine GenerateCallWithUsingReferences and GenerateCallWith +sub GenerateCallWithUsingPointers +{ + my ($callWith, $outputArray, $returnValue, $function) = @_; + + my $statePointer = "state"; + my $stateReference = "*state"; + my $globalObject = "jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject())"; + + return GenerateCallWith($callWith, $outputArray, $returnValue, $function, $statePointer, $stateReference, $globalObject); +} + sub GenerateCallWith { - my $callWith = shift; + my ($callWith, $outputArray, $returnValue, $function, $statePointer, $stateReference, $globalObject) = @_; + return () unless $callWith; - my $outputArray = shift; - my $returnValue = shift; - my $function = shift; my @callWithArgs; - if ($codeGenerator->ExtendedAttributeContains($callWith, "ScriptState")) { - push(@callWithArgs, "exec"); - } + push(@callWithArgs, $stateReference) if $codeGenerator->ExtendedAttributeContains($callWith, "ScriptState"); + push(@callWithArgs, "*${globalObject}") if $codeGenerator->ExtendedAttributeContains($callWith, "GlobalObject"); if ($codeGenerator->ExtendedAttributeContains($callWith, "ScriptExecutionContext")) { - push(@$outputArray, " ScriptExecutionContext* scriptContext = jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject())->scriptExecutionContext();\n"); - push(@$outputArray, " if (!scriptContext)\n"); + push(@$outputArray, " auto* context = $globalObject->scriptExecutionContext();\n"); + push(@$outputArray, " if (!context)\n"); push(@$outputArray, " return" . ($returnValue ? " " . $returnValue : "") . ";\n"); - push(@callWithArgs, "scriptContext"); + push(@callWithArgs, "*context"); + } + if ($codeGenerator->ExtendedAttributeContains($callWith, "Document")) { + $implIncludes{"Document.h"} = 1; + push(@$outputArray, " auto* context = $globalObject->scriptExecutionContext();\n"); + push(@$outputArray, " if (!context)\n"); + push(@$outputArray, " return" . ($returnValue ? " " . $returnValue : "") . ";\n"); + push(@$outputArray, " ASSERT(context->isDocument());\n"); + push(@$outputArray, " auto& document = downcast<Document>(*context);\n"); + push(@callWithArgs, "document"); + } + if ($codeGenerator->ExtendedAttributeContains($callWith, "CallerDocument")) { + $implIncludes{"Document.h"} = 1; + $implIncludes{"JSDOMWindowBase.h"} = 1; + push(@$outputArray, " auto* document = callerDOMWindow($statePointer).document();\n"); + push(@$outputArray, " if (!document)\n"); + push(@$outputArray, " return" . ($returnValue ? " " . $returnValue : "") . ";\n"); + push(@callWithArgs, "*document"); } if ($function and $codeGenerator->ExtendedAttributeContains($callWith, "ScriptArguments")) { - push(@$outputArray, " RefPtr<ScriptArguments> scriptArguments(createScriptArguments(exec, " . @{$function->parameters} . "));\n"); - $implIncludes{"ScriptArguments.h"} = 1; - $implIncludes{"ScriptCallStackFactory.h"} = 1; - push(@callWithArgs, "scriptArguments.release()"); + push(@$outputArray, " Ref<Inspector::ScriptArguments> scriptArguments(Inspector::createScriptArguments($statePointer, " . @{$function->arguments} . "));\n"); + $implIncludes{"<inspector/ScriptArguments.h>"} = 1; + $implIncludes{"<inspector/ScriptCallStackFactory.h>"} = 1; + push(@callWithArgs, "WTFMove(scriptArguments)"); + } + if ($codeGenerator->ExtendedAttributeContains($callWith, "ActiveWindow")) { + $implIncludes{"JSDOMWindowBase.h"} = 1; + push(@callWithArgs, "activeDOMWindow($statePointer)"); + } + if ($codeGenerator->ExtendedAttributeContains($callWith, "FirstWindow")) { + $implIncludes{"JSDOMWindowBase.h"} = 1; + push(@callWithArgs, "firstDOMWindow($statePointer)"); + + } + if ($codeGenerator->ExtendedAttributeContains($callWith, "CallerWindow")) { + $implIncludes{"JSDOMWindowBase.h"} = 1; + push(@callWithArgs, "callerDOMWindow($statePointer)"); + + } + return @callWithArgs; } @@ -2918,795 +4556,1132 @@ sub GenerateArgumentsCountCheck my $function = shift; my $interface = shift; - my $numMandatoryParams = @{$function->parameters}; - foreach my $param (reverse(@{$function->parameters})) { - if ($param->isOptional or $param->isVariadic) { - $numMandatoryParams--; + my $numMandatoryArguments = @{$function->arguments}; + foreach my $argument (reverse(@{$function->arguments})) { + if ($argument->isOptional or $argument->isVariadic) { + $numMandatoryArguments--; } else { last; } } - if ($numMandatoryParams >= 1) - { - push(@$outputArray, " if (exec->argumentCount() < $numMandatoryParams)\n"); - push(@$outputArray, " return throwVMError(exec, createNotEnoughArgumentsError(exec));\n"); + if ($numMandatoryArguments >= 1) { + push(@$outputArray, " if (UNLIKELY(state->argumentCount() < $numMandatoryArguments))\n"); + push(@$outputArray, " return throwVMError(state, throwScope, createNotEnoughArgumentsError(state));\n"); } } -sub GenerateParametersCheck +my %automaticallyGeneratedDefaultValues = ( + "any" => "undefined", + + # toString() will convert undefined to the string "undefined"; + # (note that this optimizes a behavior that is almost never useful) + "DOMString" => "\"undefined\"", + "USVString" => "\"undefined\"", + + # JSValue::toBoolean() will convert undefined to false. + "boolean" => "false", + + # JSValue::toInt*() / JSValue::toUint*() will convert undefined to 0. + "byte" => "0", + "long long" => "0", + "long" => "0", + "octet" => "0", + "short" => "0", + "unsigned long long" => "0", + "unsigned long" => "0", + "unsigned short" => "0", + + # toNumber() / toFloat() convert undefined to NaN. + "double" => "NaN", + "float" => "NaN", + "unrestricted double" => "NaN", + "unrestricted float" => "NaN", +); + +sub WillConvertUndefinedToDefaultParameterValue { - my $outputArray = shift; - my $function = shift; - my $interface = shift; - my $numParameters = shift; - my $interfaceName = shift; - my $functionImplementationName = shift; - my $svgPropertyType = shift; - my $svgPropertyOrListPropertyType = shift; - my $svgListPropertyType = shift; + my ($parameterType, $defaultValue) = @_; + + my $automaticallyGeneratedDefaultValue = $automaticallyGeneratedDefaultValues{$parameterType->name}; + return 1 if defined $automaticallyGeneratedDefaultValue && $automaticallyGeneratedDefaultValue eq $defaultValue; - my $argsIndex = 0; - my $hasOptionalArguments = 0; - my $raisesException = $function->signature->extendedAttributes->{"RaisesException"}; + return 1 if $defaultValue eq "null" && $codeGenerator->IsWrapperType($parameterType); + return 1 if $defaultValue eq "[]" && $codeGenerator->IsDictionaryType($parameterType); + return 0; +} + +sub NeedsExplicitPropagateExceptionCall +{ + my ($function) = @_; + + return 0 unless $function->extendedAttributes->{MayThrowException}; + + return 1 if $function->type && $function->type->name eq "void"; + return 1 if IsReturningPromise($function); + + return 0; +} + +sub GenerateParametersCheck +{ + my ($outputArray, $function, $interface, $functionImplementationName) = @_; + + my $interfaceName = $interface->type->name; + my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($interface); my @arguments; my $functionName; - my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"}; + my $implementedBy = $function->extendedAttributes->{ImplementedBy}; + my $numArguments = @{$function->arguments}; + my $conditional = $function->extendedAttributes->{Conditional}; + if ($implementedBy) { - AddToImplIncludes("${implementedBy}.h"); - unshift(@arguments, "&impl") if !$function->isStatic; - $functionName = "${implementedBy}::${functionImplementationName}"; + AddToImplIncludes("${implementedBy}.h", $conditional); + unshift(@arguments, "impl") if !$function->isStatic; + $functionName = "WebCore::${implementedBy}::${functionImplementationName}"; } elsif ($function->isStatic) { $functionName = "${interfaceName}::${functionImplementationName}"; - } elsif ($svgPropertyOrListPropertyType and !$svgListPropertyType) { - $functionName = "podImpl.${functionImplementationName}"; } else { $functionName = "impl.${functionImplementationName}"; } - if (!$function->signature->extendedAttributes->{"Constructor"}) { - push(@arguments, GenerateCallWith($function->signature->extendedAttributes->{"CallWith"}, \@$outputArray, "JSValue::encode(jsUndefined())", $function)); + my $quotedFunctionName; + if (!$function->extendedAttributes->{Constructor}) { + my $name = $function->name; + $quotedFunctionName = "\"$name\""; + push(@arguments, GenerateCallWithUsingPointers($function->extendedAttributes->{CallWith}, \@$outputArray, "JSValue::encode(jsUndefined())", $function)); + } else { + $quotedFunctionName = "nullptr"; } - $implIncludes{"ExceptionCode.h"} = 1; - $implIncludes{"JSDOMBinding.h"} = 1; + my $argumentIndex = 0; + foreach my $argument (@{$function->arguments}) { + my $type = $argument->type; - foreach my $parameter (@{$function->parameters}) { - my $argType = $parameter->type; - - # Optional arguments with [Optional] should generate an early call with fewer arguments. - # Optional arguments with [Optional=...] should not generate the early call. - # Optional Dictionary arguments always considered to have default of empty dictionary. - my $optional = $parameter->isOptional; - my $defaultAttribute = $parameter->extendedAttributes->{"Default"}; - if ($optional && !$defaultAttribute && $argType ne "Dictionary" && !$codeGenerator->IsCallbackInterface($parameter->type)) { - # Generate early call if there are enough parameters. - if (!$hasOptionalArguments) { - push(@$outputArray, "\n size_t argsCount = exec->argumentCount();\n"); - $hasOptionalArguments = 1; - } - push(@$outputArray, " if (argsCount <= $argsIndex) {\n"); + die "Optional arguments of non-nullable wrapper types are not supported" if $argument->isOptional && !$type->isNullable && $codeGenerator->IsWrapperType($type); + die "Optional arguments preceding variadic arguments are not supported" if ($argument->isOptional && @{$function->arguments}[$numArguments - 1]->isVariadic); + + if ($argument->isOptional && !defined($argument->default)) { + # As per Web IDL, optional dictionary arguments are always considered to have a default value of an empty dictionary, unless otherwise specified. + $argument->default("[]") if $codeGenerator->IsDictionaryType($type); - my @optionalCallbackArguments = @arguments; - push(@optionalCallbackArguments, "ec") if $raisesException; - my $functionString = "$functionName(" . join(", ", @optionalCallbackArguments) . ")"; - GenerateImplementationFunctionCall($function, $functionString, " " x 2, $svgPropertyType, $interfaceName); - push(@$outputArray, " }\n\n"); + # Treat undefined the same as an empty sequence Or frozen array. + $argument->default("[]") if $codeGenerator->IsSequenceOrFrozenArrayType($type); + + # We use undefined as default value for optional arguments of type 'any' unless specified otherwise. + $argument->default("undefined") if $type->name eq "any"; + + # We use the null string as default value for arguments of type DOMString unless specified otherwise. + $argument->default("null") if $codeGenerator->IsStringType($type); + + # As per Web IDL, passing undefined for a nullable argument is treated as null. Therefore, use null as + # default value for nullable arguments unless otherwise specified. + $argument->default("null") if $type->isNullable; + + # For callback arguments, the generated bindings treat undefined as null, so use null as implicit default value. + $argument->default("null") if $codeGenerator->IsCallbackInterface($type) || $codeGenerator->IsCallbackFunction($type); } - my $name = $parameter->name; - - if ($argType eq "XPathNSResolver") { - push(@$outputArray, " RefPtr<XPathNSResolver> customResolver;\n"); - push(@$outputArray, " XPathNSResolver* resolver = toXPathNSResolver(exec->argument($argsIndex));\n"); - push(@$outputArray, " if (!resolver) {\n"); - push(@$outputArray, " customResolver = JSCustomXPathNSResolver::create(exec, exec->argument($argsIndex));\n"); - push(@$outputArray, " if (exec->hadException())\n"); - push(@$outputArray, " return JSValue::encode(jsUndefined());\n"); - push(@$outputArray, " resolver = customResolver.get();\n"); - push(@$outputArray, " }\n"); - } elsif ($codeGenerator->IsCallbackInterface($parameter->type)) { - my $callbackClassName = GetCallbackClassName($argType); - $implIncludes{"$callbackClassName.h"} = 1; - if ($optional) { - push(@$outputArray, " RefPtr<$argType> $name;\n"); - push(@$outputArray, " if (!exec->argument($argsIndex).isUndefinedOrNull()) {\n"); - push(@$outputArray, " if (!exec->uncheckedArgument($argsIndex).isFunction())\n"); - push(@$outputArray, " return throwVMTypeError(exec);\n"); - if ($function->isStatic) { - AddToImplIncludes("CallbackFunction.h"); - push(@$outputArray, " $name = createFunctionOnlyCallback<${callbackClassName}>(exec, jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), exec->uncheckedArgument($argsIndex));\n"); - } else { - push(@$outputArray, " $name = ${callbackClassName}::create(asObject(exec->uncheckedArgument($argsIndex)), castedThis->globalObject());\n"); - } - push(@$outputArray, " }\n"); - } else { - push(@$outputArray, " if (!exec->argument($argsIndex).isFunction())\n"); - push(@$outputArray, " return throwVMTypeError(exec);\n"); - if ($function->isStatic) { - AddToImplIncludes("CallbackFunction.h"); - push(@$outputArray, " RefPtr<$argType> $name = createFunctionOnlyCallback<${callbackClassName}>(exec, jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), exec->uncheckedArgument($argsIndex));\n"); - } else { - push(@$outputArray, " RefPtr<$argType> $name = ${callbackClassName}::create(asObject(exec->uncheckedArgument($argsIndex)), castedThis->globalObject());\n"); - } - } - } elsif ($parameter->extendedAttributes->{"Clamp"}) { - my $nativeValue = "${name}NativeValue"; - push(@$outputArray, " $argType $name = 0;\n"); - push(@$outputArray, " double $nativeValue = exec->argument($argsIndex).toNumber(exec);\n"); - push(@$outputArray, " if (exec->hadException())\n"); - push(@$outputArray, " return JSValue::encode(jsUndefined());\n\n"); - push(@$outputArray, " if (!std::isnan($nativeValue))\n"); - push(@$outputArray, " $name = clampTo<$argType>($nativeValue);\n\n"); - } elsif ($parameter->isVariadic) { - my $nativeElementType; - if ($argType eq "DOMString") { - $nativeElementType = "String"; - } else { - $nativeElementType = GetNativeType($argType); - if ($nativeElementType =~ />$/) { - $nativeElementType .= " "; - } - } + my $name = $argument->name; + my $value = $name; - if (!IsNativeType($argType)) { - push(@$outputArray, " Vector<$nativeElementType> $name;\n"); - push(@$outputArray, " for (unsigned i = $argsIndex, count = exec->argumentCount(); i < count; ++i) {\n"); - push(@$outputArray, " if (!exec->uncheckedArgument(i).inherits(JS${argType}::info()))\n"); - push(@$outputArray, " return throwVMTypeError(exec);\n"); - push(@$outputArray, " $name.append(to$argType(exec->uncheckedArgument(i)));\n"); - push(@$outputArray, " }\n") - } else { - push(@$outputArray, " Vector<$nativeElementType> $name = toNativeArguments<$nativeElementType>(exec, $argsIndex);\n"); - # Check if the type conversion succeeded. - push(@$outputArray, " if (exec->hadException())\n"); - push(@$outputArray, " return JSValue::encode(jsUndefined());\n"); - } + if ($argument->isVariadic) { + AddToImplIncludes("JSDOMConvertVariadic.h", $conditional); + AddToImplIncludesForIDLType($type, $conditional); + + my $IDLType = GetIDLType($interface, $type); - } elsif ($codeGenerator->IsEnumType($argType)) { - $implIncludes{"<runtime/Error.h>"} = 1; + push(@$outputArray, " auto ${name} = convertVariadicArguments<${IDLType}>(*state, ${argumentIndex});\n"); + push(@$outputArray, " RETURN_IF_EXCEPTION(throwScope, encodedJSValue());\n"); + + $value = "WTFMove(${name}.arguments.value())"; + + } elsif ($codeGenerator->IsEnumType($type)) { + AddToImplIncludes("<runtime/Error.h>", $conditional); + + my $className = GetEnumerationClassName($type, $interface); + my $nativeType = $className; + my $optionalValue = "optionalValue"; + my $defineOptionalValue = "auto optionalValue"; + my $indent = ""; - my $argValue = "exec->argument($argsIndex)"; - push(@$outputArray, " const String ${name}(${argValue}.isEmpty() ? String() : ${argValue}.toString(exec)->value(exec));\n"); - push(@$outputArray, " if (exec->hadException())\n"); - push(@$outputArray, " return JSValue::encode(jsUndefined());\n"); + die "Variadic argument is already handled here" if $argument->isVariadic; + my $argumentLookupMethod = $argument->isOptional ? "argument" : "uncheckedArgument"; - my @enumValues = $codeGenerator->ValidEnumValues($argType); - my @enumChecks = (); - foreach my $enumValue (@enumValues) { - push(@enumChecks, "${name} != \"$enumValue\""); + if ($argument->isOptional && !defined($argument->default)) { + $nativeType = "std::optional<$className>"; + $optionalValue = $name; + $defineOptionalValue = $name; } - push (@$outputArray, " if (" . join(" && ", @enumChecks) . ")\n"); - push (@$outputArray, " return throwVMTypeError(exec);\n"); - } else { - # If the "StrictTypeChecking" extended attribute is present, and the argument's type is an - # interface type, then if the incoming value does not implement that interface, a TypeError - # is thrown rather than silently passing NULL to the C++ code. - # Per the Web IDL and ECMAScript semantics, incoming values can always be converted to both - # strings and numbers, so do not throw TypeError if the argument is of these types. - if ($function->signature->extendedAttributes->{"StrictTypeChecking"}) { - $implIncludes{"<runtime/Error.h>"} = 1; - - my $argValue = "exec->argument($argsIndex)"; - if ($codeGenerator->IsWrapperType($argType)) { - push(@$outputArray, " if (exec->argumentCount() > $argsIndex && !${argValue}.isUndefinedOrNull() && !${argValue}.inherits(JS${argType}::info()))\n"); - push(@$outputArray, " return throwVMTypeError(exec);\n"); + + push(@$outputArray, " auto ${name}Value = state->$argumentLookupMethod($argumentIndex);\n"); + push(@$outputArray, " $nativeType $name;\n"); + + if ($argument->isOptional) { + if (!defined $argument->default) { + push(@$outputArray, " if (!${name}Value.isUndefined()) {\n"); + } else { + push(@$outputArray, " if (${name}Value.isUndefined())\n"); + push(@$outputArray, " $name = " . GenerateDefaultValue($interface, $argument, $argument->type, $argument->default) . ";\n"); + push(@$outputArray, " else {\n"); } + $indent = " "; } - push(@$outputArray, " " . GetNativeTypeFromSignature($parameter) . " $name(" . JSValueToNative($parameter, $optional && $defaultAttribute && $defaultAttribute eq "NullString" ? "argumentOrNull(exec, $argsIndex)" : "exec->argument($argsIndex)") . ");\n"); + $implIncludes{"JS$className.h"} = 1 if $codeGenerator->IsExternalEnumType($type); + push(@$outputArray, "$indent $defineOptionalValue = parseEnumeration<$className>(*state, ${name}Value);\n"); + push(@$outputArray, "$indent RETURN_IF_EXCEPTION(throwScope, encodedJSValue());\n"); + push(@$outputArray, "$indent if (UNLIKELY(!$optionalValue))\n"); + push(@$outputArray, "$indent return throwArgumentMustBeEnumError(*state, throwScope, $argumentIndex, \"$name\", \"$visibleInterfaceName\", $quotedFunctionName, expectedEnumerationValues<$className>());\n"); + push(@$outputArray, "$indent $name = optionalValue.value();\n") if $optionalValue ne $name; - # If a parameter is "an index" and it's negative it should throw an INDEX_SIZE_ERR exception. - # But this needs to be done in the bindings, because the type is unsigned and the fact that it - # was negative will be lost by the time we're inside the DOM. - if ($parameter->extendedAttributes->{"IsIndex"}) { - push(@$outputArray, " if ($name < 0) {\n"); - push(@$outputArray, " setDOMException(exec, INDEX_SIZE_ERR);\n"); - push(@$outputArray, " return JSValue::encode(jsUndefined());\n"); - push(@$outputArray, " }\n"); - } + push(@$outputArray, " }\n") if $indent ne ""; + } else { + my $outer; + my $inner; + my $nativeType = GetNativeType($interface, $argument->type); + + die "Variadic argument is already handled here" if $argument->isVariadic; + my $argumentLookupMethod = $argument->isOptional ? "argument" : "uncheckedArgument"; + + if ($argument->isOptional && defined($argument->default) && !WillConvertUndefinedToDefaultParameterValue($type, $argument->default)) { + my $defaultValue = $argument->default; - # Check if the type conversion succeeded. - push(@$outputArray, " if (exec->hadException())\n"); - push(@$outputArray, " return JSValue::encode(jsUndefined());\n"); + $defaultValue = GenerateDefaultValue($interface, $argument, $argument->type, $argument->default); - if ($codeGenerator->IsSVGTypeNeedingTearOff($argType) and not $interfaceName =~ /List$/) { - push(@$outputArray, " if (!$name) {\n"); - push(@$outputArray, " setDOMException(exec, TYPE_MISMATCH_ERR);\n"); - push(@$outputArray, " return JSValue::encode(jsUndefined());\n"); - push(@$outputArray, " }\n"); + $outer = "state->$argumentLookupMethod($argumentIndex).isUndefined() ? $defaultValue : "; + $inner = "state->uncheckedArgument($argumentIndex)"; + } elsif ($argument->isOptional && !defined($argument->default)) { + # Use std::optional<>() for optional arguments that are missing or undefined and that do not have a default value in the IDL. + $outer = "state->$argumentLookupMethod($argumentIndex).isUndefined() ? std::optional<$nativeType>() : "; + $inner = "state->uncheckedArgument($argumentIndex)"; + } else { + $outer = ""; + $inner = "state->$argumentLookupMethod($argumentIndex)"; } + + my $globalObjectReference = $function->isStatic ? "*jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject())" : "*castedThis->globalObject()"; + my $argumentExceptionThrower = GetArgumentExceptionThrower($interface, $argument, $argumentIndex, $quotedFunctionName); + + my ($nativeValue, $mayThrowException) = JSValueToNative($interface, $argument, $inner, $conditional, "state", "*state", "*castedThis", $globalObjectReference, $argumentExceptionThrower); + + push(@$outputArray, " auto $name = ${outer}${nativeValue};\n"); + push(@$outputArray, " RETURN_IF_EXCEPTION(throwScope, encodedJSValue());\n") if $mayThrowException; + + $value = PassArgumentExpression($name, $argument); } - if ($argType eq "NodeFilter" || ($codeGenerator->IsTypedArrayType($argType) and not $argType eq "ArrayBuffer")) { - push @arguments, "$name.get()"; - } elsif ($codeGenerator->IsSVGTypeNeedingTearOff($argType) and not $interfaceName =~ /List$/) { - push @arguments, "$name->propertyReference()"; - } else { - push @arguments, $name; + push(@arguments, $value); + $argumentIndex++; + } + + push(@arguments, "WTFMove(promise)") if IsReturningPromise($function); + + my $functionString = "$functionName(" . join(", ", @arguments) . ")"; + $functionString = "propagateException(*state, throwScope, $functionString)" if NeedsExplicitPropagateExceptionCall($function); + + return ($functionString, scalar @arguments); +} + +sub GenerateDictionaryHeader +{ + my ($object, $dictionary, $className, $enumerations, $otherDictionaries) = @_; + + # - Add default header template and header protection. + push(@headerContentHeader, GenerateHeaderContentHeader($dictionary)); + + $headerIncludes{"$className.h"} = 1; + $headerIncludes{"JSDOMConvert.h"} = 1; + + push(@headerContent, "\nnamespace WebCore {\n\n"); + push(@headerContent, GenerateDictionaryHeaderContent($dictionary, $className)); + push(@headerContent, GenerateEnumerationsHeaderContent($dictionary, $enumerations)); + push(@headerContent, GenerateDictionariesHeaderContent($dictionary, $otherDictionaries)) if $otherDictionaries; + push(@headerContent, "} // namespace WebCore\n"); + + my $conditionalString = $codeGenerator->GenerateConditionalString($dictionary); + push(@headerContent, "\n#endif // ${conditionalString}\n") if $conditionalString; + + # - Generate dependencies. + if ($writeDependencies) { + my @ancestors; + my $parentType = $dictionary->parentType; + while (defined($parentType)) { + push(@ancestors, $parentType->name) if $codeGenerator->IsExternalDictionaryType($parentType); + my $parentDictionary = $codeGenerator->GetDictionaryByType($parentType); + assert("Unable to find definition for dictionary named '" . $parentType->name . "'!") unless $parentDictionary; + $parentType = $parentDictionary->parentType; } - $argsIndex++; + push(@depsContent, "$className.h : ", join(" ", map { "$_.idl" } @ancestors), "\n"); + push(@depsContent, map { "$_.idl :\n" } @ancestors); } +} + +sub GenerateDictionaryImplementation +{ + my ($object, $dictionary, $className, $enumerations, $otherDictionaries) = @_; + + # - Add default header template + push(@implContentHeader, GenerateImplementationContentHeader($dictionary)); - push(@arguments, "ec") if $raisesException; + push(@implContent, "\nusing namespace JSC;\n\n"); + push(@implContent, "namespace WebCore {\n\n"); + push(@implContent, GenerateDictionaryImplementationContent($dictionary, $className)); + push(@implContent, GenerateEnumerationsImplementationContent($dictionary, $enumerations)); + push(@implContent, GenerateDictionariesImplementationContent($dictionary, $otherDictionaries)) if $otherDictionaries; + push(@implContent, "} // namespace WebCore\n"); - return ("$functionName(" . join(", ", @arguments) . ")", scalar @arguments); + my $conditionalString = $codeGenerator->GenerateConditionalString($dictionary); + push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString; } -sub GenerateCallbackHeader +sub GenerateCallbackFunctionHeader { - my $object = shift; - my $interface = shift; + my ($object, $callbackFunction, $enumerations, $dictionaries) = @_; - my $interfaceName = $interface->name; - my $className = "JS$interfaceName"; + push(@headerContentHeader, GenerateHeaderContentHeader($callbackFunction)); - # - Add default header template and header protection - push(@headerContentHeader, GenerateHeaderContentHeader($interface)); + push(@headerContent, "\nnamespace WebCore {\n\n"); + + my @functions = (); + push(@functions, $callbackFunction->operation); + my @constants = (); - $headerIncludes{"ActiveDOMCallback.h"} = 1; - $headerIncludes{"$interfaceName.h"} = 1; - $headerIncludes{"JSCallbackData.h"} = 1; - $headerIncludes{"<wtf/Forward.h>"} = 1; + $object->GenerateCallbackHeaderContent($callbackFunction, \@functions, \@constants, \@headerContent, \%headerIncludes); + + push(@headerContent, GenerateEnumerationsHeaderContent($callbackFunction, $enumerations)); + push(@headerContent, GenerateDictionariesHeaderContent($callbackFunction, $dictionaries)); + + push(@headerContent, "} // namespace WebCore\n"); + + my $conditionalString = $codeGenerator->GenerateConditionalString($callbackFunction); + push(@headerContent, "\n#endif // ${conditionalString}\n") if $conditionalString; +} + +sub GenerateCallbackFunctionImplementation +{ + my ($object, $callbackFunction, $enumerations, $dictionaries) = @_; + + push(@implContentHeader, GenerateImplementationContentHeader($callbackFunction)); + + push(@implContent, "\nusing namespace JSC;\n\n"); + push(@implContent, "namespace WebCore {\n\n"); + + push(@implContent, GenerateEnumerationsImplementationContent($callbackFunction, $enumerations)); + push(@implContent, GenerateDictionariesImplementationContent($callbackFunction, $dictionaries)); + + my @functions = (); + push(@functions, $callbackFunction->operation); + my @constants = (); + + $object->GenerateCallbackImplementationContent($callbackFunction, \@functions, \@constants, \@implContent, \%implIncludes); + + push(@implContent, "} // namespace WebCore\n"); + + my $conditionalString = $codeGenerator->GenerateConditionalString($callbackFunction); + push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString; +} + +sub GenerateCallbackInterfaceHeader +{ + my ($object, $callbackInterface, $enumerations, $dictionaries) = @_; + + push(@headerContentHeader, GenerateHeaderContentHeader($callbackInterface)); push(@headerContent, "\nnamespace WebCore {\n\n"); - push(@headerContent, "class $className : public $interfaceName, public ActiveDOMCallback {\n"); - push(@headerContent, "public:\n"); + + $object->GenerateCallbackHeaderContent($callbackInterface, $callbackInterface->functions, $callbackInterface->constants, \@headerContent, \%headerIncludes); + + push(@headerContent, GenerateEnumerationsHeaderContent($callbackInterface, $enumerations)); + push(@headerContent, GenerateDictionariesHeaderContent($callbackInterface, $dictionaries)); + + push(@headerContent, "} // namespace WebCore\n"); + + my $conditionalString = $codeGenerator->GenerateConditionalString($callbackInterface); + push(@headerContent, "\n#endif // ${conditionalString}\n") if $conditionalString; +} + +sub GenerateCallbackInterfaceImplementation +{ + my ($object, $callbackInterface, $enumerations, $dictionaries) = @_; + + push(@implContentHeader, GenerateImplementationContentHeader($callbackInterface)); + + push(@implContent, "\nusing namespace JSC;\n\n"); + push(@implContent, "namespace WebCore {\n\n"); + + push(@implContent, GenerateEnumerationsImplementationContent($callbackInterface, $enumerations)); + push(@implContent, GenerateDictionariesImplementationContent($callbackInterface, $dictionaries)); + + $object->GenerateCallbackImplementationContent($callbackInterface, $callbackInterface->functions, $callbackInterface->constants, \@implContent, \%implIncludes); + + push(@implContent, "} // namespace WebCore\n"); + + my $conditionalString = $codeGenerator->GenerateConditionalString($callbackInterface); + push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString; +} + +sub GenerateCallbackHeaderContent +{ + my ($object, $interfaceOrCallback, $functions, $constants, $contentRef, $includesRef) = @_; + + my $name = $interfaceOrCallback->type->name; + my $callbackDataType = GetJSCallbackDataType($interfaceOrCallback); + my $className = "JS${name}"; + + $includesRef->{"ActiveDOMCallback.h"} = 1; + $includesRef->{"JSCallbackData.h"} = 1; + $includesRef->{"<wtf/Forward.h>"} = 1; + $includesRef->{"${name}.h"} = 1; + + push(@$contentRef, "class $className : public ${name}, public ActiveDOMCallback {\n"); + push(@$contentRef, "public:\n"); # The static create() method. - push(@headerContent, " static PassRefPtr<$className> create(JSC::JSObject* callback, JSDOMGlobalObject* globalObject)\n"); - push(@headerContent, " {\n"); - push(@headerContent, " return adoptRef(new $className(callback, globalObject));\n"); - push(@headerContent, " }\n\n"); + push(@$contentRef, " static Ref<$className> create(JSC::JSObject* callback, JSDOMGlobalObject* globalObject)\n"); + push(@$contentRef, " {\n"); + push(@$contentRef, " return adoptRef(*new ${className}(callback, globalObject));\n"); + push(@$contentRef, " }\n\n"); - # ScriptExecutionContext - push(@headerContent, " virtual ScriptExecutionContext* scriptExecutionContext() const { return ContextDestructionObserver::scriptExecutionContext(); }\n\n"); + push(@$contentRef, " virtual ScriptExecutionContext* scriptExecutionContext() const { return ContextDestructionObserver::scriptExecutionContext(); }\n\n"); - # Destructor - push(@headerContent, " virtual ~$className();\n"); + push(@$contentRef, " virtual ~$className();\n"); - if ($interface->extendedAttributes->{"CallbackNeedsOperatorEqual"}) { - push(@headerContent, " virtual bool operator==(const $interfaceName&) const;\n\n") - } + push(@$contentRef, " ${callbackDataType}* callbackData() { return m_data; }\n"); + + push(@$contentRef, " static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*);\n") if @{$constants}; + + push(@$contentRef, " virtual bool operator==(const ${name}&) const;\n\n") if $interfaceOrCallback->extendedAttributes->{CallbackNeedsOperatorEqual}; # Functions - my $numFunctions = @{$interface->functions}; + my $numFunctions = @{$functions}; if ($numFunctions > 0) { - push(@headerContent, "\n // Functions\n"); - foreach my $function (@{$interface->functions}) { - my @params = @{$function->parameters}; - if (!$function->signature->extendedAttributes->{"Custom"} && - !(GetNativeType($function->signature->type) eq "bool")) { - push(@headerContent, " COMPILE_ASSERT(false)"); + push(@$contentRef, "\n // Functions\n"); + foreach my $function (@{$functions}) { + my @arguments = (); + foreach my $argument (@{$function->arguments}) { + push(@arguments, GetNativeTypeForCallbacks($argument->type, $interfaceOrCallback) . " " . $argument->name); } - push(@headerContent, " virtual " . GetNativeTypeForCallbacks($function->signature->type) . " " . $function->signature->name . "("); + # FIXME: Add support for non-void return types (the bool actually is returning exception state), for non-custom functions. + my $nativeReturnType = $function->extendedAttributes->{Custom} ? GetNativeTypeForCallbacks($function->type, $interfaceOrCallback) : "bool"; + + # FIXME: Change the default name (used for callback functions) to something other than handleEvent. It makes little sense. + my $functionName = $function->name ? $function->name : "handleEvent"; - my @args = (); - foreach my $param (@params) { - push(@args, GetNativeTypeForCallbacks($param->type) . " " . $param->name); - } - push(@headerContent, join(", ", @args)); - - push(@headerContent, ");\n"); + push(@$contentRef, " virtual ${nativeReturnType} ${functionName}(" . join(", ", @arguments) . ");\n"); } } - push(@headerContent, "\nprivate:\n"); + push(@$contentRef, "\nprivate:\n"); # Constructor - push(@headerContent, " $className(JSC::JSObject* callback, JSDOMGlobalObject*);\n\n"); + push(@$contentRef, " ${className}(JSC::JSObject*, JSDOMGlobalObject*);\n\n"); # Private members - push(@headerContent, " JSCallbackData* m_data;\n"); - push(@headerContent, "};\n\n"); + push(@$contentRef, " ${callbackDataType}* m_data;\n"); + push(@$contentRef, "};\n\n"); - push(@headerContent, "} // namespace WebCore\n\n"); - my $conditionalString = $codeGenerator->GenerateConditionalString($interface); - push(@headerContent, "#endif // ${conditionalString}\n\n") if $conditionalString; - push(@headerContent, "#endif\n"); + # toJS(). + push(@$contentRef, "JSC::JSValue toJS(${name}&);\n"); + push(@$contentRef, "inline JSC::JSValue toJS(${name}* impl) { return impl ? toJS(*impl) : JSC::jsNull(); }\n\n"); } -sub GenerateCallbackImplementation +sub GenerateCallbackImplementationContent { - my ($object, $interface) = @_; + my ($object, $interfaceOrCallback, $functions, $constants, $contentRef, $includesRef) = @_; - my $interfaceName = $interface->name; - my $className = "JS$interfaceName"; + my $name = $interfaceOrCallback->type->name; + my $callbackDataType = GetJSCallbackDataType($interfaceOrCallback); + my $visibleName = $codeGenerator->GetVisibleInterfaceName($interfaceOrCallback); + my $className = "JS${name}"; - # - Add default header template - push(@implContentHeader, GenerateImplementationContentHeader($interface)); - - $implIncludes{"ScriptExecutionContext.h"} = 1; - $implIncludes{"<runtime/JSLock.h>"} = 1; - - @implContent = (); - - push(@implContent, "\nusing namespace JSC;\n\n"); - push(@implContent, "namespace WebCore {\n\n"); + $includesRef->{"ScriptExecutionContext.h"} = 1; + $includesRef->{"<runtime/JSLock.h>"} = 1; # Constructor - push(@implContent, "${className}::${className}(JSObject* callback, JSDOMGlobalObject* globalObject)\n"); - if ($interface->extendedAttributes->{"CallbackNeedsOperatorEqual"}) { - push(@implContent, " : ${interfaceName}(${className}Type)\n"); + push(@$contentRef, "${className}::${className}(JSObject* callback, JSDOMGlobalObject* globalObject)\n"); + if ($interfaceOrCallback->extendedAttributes->{CallbackNeedsOperatorEqual}) { + push(@$contentRef, " : ${name}(${className}Type)\n"); } else { - push(@implContent, " : ${interfaceName}()\n"); + push(@$contentRef, " : ${name}()\n"); } - push(@implContent, " , ActiveDOMCallback(globalObject->scriptExecutionContext())\n"); - push(@implContent, " , m_data(new JSCallbackData(callback, globalObject))\n"); - push(@implContent, "{\n"); - push(@implContent, "}\n\n"); + push(@$contentRef, " , ActiveDOMCallback(globalObject->scriptExecutionContext())\n"); + push(@$contentRef, " , m_data(new ${callbackDataType}(callback, globalObject, this))\n"); + push(@$contentRef, "{\n"); + push(@$contentRef, "}\n\n"); # Destructor - push(@implContent, "${className}::~${className}()\n"); - push(@implContent, "{\n"); - push(@implContent, " ScriptExecutionContext* context = scriptExecutionContext();\n"); - push(@implContent, " // When the context is destroyed, all tasks with a reference to a callback\n"); - push(@implContent, " // should be deleted. So if the context is 0, we are on the context thread.\n"); - push(@implContent, " if (!context || context->isContextThread())\n"); - push(@implContent, " delete m_data;\n"); - push(@implContent, " else\n"); - push(@implContent, " context->postTask(DeleteCallbackDataTask::create(m_data));\n"); - push(@implContent, "#ifndef NDEBUG\n"); - push(@implContent, " m_data = 0;\n"); - push(@implContent, "#endif\n"); - push(@implContent, "}\n\n"); + push(@$contentRef, "${className}::~${className}()\n"); + push(@$contentRef, "{\n"); + push(@$contentRef, " ScriptExecutionContext* context = scriptExecutionContext();\n"); + push(@$contentRef, " // When the context is destroyed, all tasks with a reference to a callback\n"); + push(@$contentRef, " // should be deleted. So if the context is 0, we are on the context thread.\n"); + push(@$contentRef, " if (!context || context->isContextThread())\n"); + push(@$contentRef, " delete m_data;\n"); + push(@$contentRef, " else\n"); + push(@$contentRef, " context->postTask(DeleteCallbackDataTask(m_data));\n"); + push(@$contentRef, "#ifndef NDEBUG\n"); + push(@$contentRef, " m_data = nullptr;\n"); + push(@$contentRef, "#endif\n"); + push(@$contentRef, "}\n\n"); + + if ($interfaceOrCallback->extendedAttributes->{CallbackNeedsOperatorEqual}) { + push(@$contentRef, "bool ${className}::operator==(const ${name}& other) const\n"); + push(@$contentRef, "{\n"); + push(@$contentRef, " if (other.type() != type())\n"); + push(@$contentRef, " return false;\n"); + push(@$contentRef, " return static_cast<const ${className}*>(&other)->m_data->callback() == m_data->callback();\n"); + push(@$contentRef, "}\n\n"); + } + + # Constants. + my $numConstants = @{$constants}; + if ($numConstants > 0) { + GenerateConstructorDeclaration($contentRef, $className, $interfaceOrCallback, $name); - if ($interface->extendedAttributes->{"CallbackNeedsOperatorEqual"}) { - push(@implContent, "bool ${className}::operator==(const ${interfaceName}& other) const\n"); - push(@implContent, "{\n"); - push(@implContent, " if (other.type() != type())\n"); - push(@implContent, " return false;\n"); - push(@implContent, " return static_cast<const ${className}*>(&other)->m_data->callback() == m_data->callback();\n"); - push(@implContent, "}\n\n"); + my $hashSize = 0; + my $hashName = $className . "ConstructorTable"; + + my @hashKeys = (); + my @hashValue1 = (); + my @hashValue2 = (); + my @hashSpecials = (); + my %conditionals = (); + + foreach my $constant (@{$constants}) { + my $name = $constant->name; + push(@hashKeys, $name); + push(@hashValue1, $constant->value); + push(@hashValue2, "0"); + push(@hashSpecials, "DontDelete | ReadOnly | ConstantInteger"); + + my $implementedBy = $constant->extendedAttributes->{ImplementedBy}; + $implIncludes{"${implementedBy}.h"} = 1 if $implementedBy; + + my $conditional = $constant->extendedAttributes->{Conditional}; + $conditionals{$name} = $conditional if $conditional; + + $hashSize++; + } + $object->GenerateHashTable($hashName, $hashSize, \@hashKeys, \@hashSpecials, \@hashValue1, \@hashValue2, \%conditionals, 1) if $hashSize > 0; + + push(@$contentRef, $codeGenerator->GenerateCompileTimeCheckForEnumsIfNeeded($interfaceOrCallback)); + + GenerateConstructorDefinitions($contentRef, $className, "", $visibleName, $interfaceOrCallback); + + push(@$contentRef, "JSValue ${className}::getConstructor(VM& vm, const JSGlobalObject* globalObject)\n{\n"); + push(@$contentRef, " return getDOMConstructor<${className}Constructor>(vm, *jsCast<const JSDOMGlobalObject*>(globalObject));\n"); + push(@$contentRef, "}\n\n"); } - # Functions - my $numFunctions = @{$interface->functions}; + + # Functions. + my $numFunctions = @{$functions}; if ($numFunctions > 0) { - push(@implContent, "\n// Functions\n"); - foreach my $function (@{$interface->functions}) { - my @params = @{$function->parameters}; - if ($function->signature->extendedAttributes->{"Custom"} || - !(GetNativeType($function->signature->type) eq "bool")) { - next; - } + foreach my $function (@{$functions}) { + next if $function->extendedAttributes->{Custom}; + + assert("Unsupport return type: " . $function->type->name . ".") unless $function->type->name eq "void"; - AddIncludesForTypeInImpl($function->signature->type); - push(@implContent, "\n" . GetNativeTypeForCallbacks($function->signature->type) . " ${className}::" . $function->signature->name . "("); + AddToIncludesForIDLType($function->type, $includesRef); + + # FIXME: Change the default name (used for callback functions) to something other than handleEvent. It makes little sense. + my $functionName = $function->name ? $function->name : "handleEvent"; my @args = (); - my @argsCheck = (); - foreach my $param (@params) { - my $paramName = $param->name; - AddIncludesForTypeInImpl($param->type, 1); - push(@args, GetNativeTypeForCallbacks($param->type) . " " . $paramName); + foreach my $argument (@{$function->arguments}) { + AddToIncludesForIDLType($argument->type, $includesRef, 1); + push(@args, GetNativeTypeForCallbacks($argument->type, $interfaceOrCallback) . " " . $argument->name); } - push(@implContent, join(", ", @args)); - push(@implContent, ")\n"); - - push(@implContent, "{\n"); - push(@implContent, @argsCheck) if @argsCheck; - push(@implContent, " if (!canInvokeCallback())\n"); - push(@implContent, " return true;\n\n"); - push(@implContent, " Ref<$className> protect(*this);\n\n"); - push(@implContent, " JSLockHolder lock(m_data->globalObject()->vm());\n\n"); - if (@params) { - push(@implContent, " ExecState* exec = m_data->globalObject()->globalExec();\n"); + push(@$contentRef, "bool ${className}::${functionName}(" . join(", ", @args) . ")\n"); + push(@$contentRef, "{\n"); + push(@$contentRef, " if (!canInvokeCallback())\n"); + push(@$contentRef, " return true;\n\n"); + push(@$contentRef, " Ref<$className> protectedThis(*this);\n\n"); + push(@$contentRef, " JSLockHolder lock(m_data->globalObject()->vm());\n\n"); + push(@$contentRef, " ExecState* state = m_data->globalObject()->globalExec();\n"); + push(@$contentRef, " MarkedArgumentBuffer args;\n"); + + foreach my $argument (@{$function->arguments}) { + push(@$contentRef, " args.append(" . NativeToJSValueUsingPointers($argument, $interfaceOrCallback, $argument->name, "m_data") . ");\n"); } - push(@implContent, " MarkedArgumentBuffer args;\n"); - - foreach my $param (@params) { - my $paramName = $param->name; - if ($param->type eq "DOMString") { - push(@implContent, " args.append(jsStringWithCache(exec, ${paramName}));\n"); - } elsif ($param->type eq "boolean") { - push(@implContent, " args.append(jsBoolean(${paramName}));\n"); - } elsif ($param->type eq "SerializedScriptValue") { - push(@implContent, " args.append($paramName ? $paramName->deserialize(exec, m_data->globalObject(), 0) : jsNull());\n"); - } else { - push(@implContent, " args.append(toJS(exec, m_data->globalObject(), ${paramName}));\n"); - } + + push(@$contentRef, "\n NakedPtr<JSC::Exception> returnedException;\n"); + + if (ref($interfaceOrCallback) eq "IDLCallbackFunction") { + push(@$contentRef, " m_data->invokeCallback(args, JSCallbackData::CallbackType::Function, Identifier(), returnedException);\n"); + } else { + my $callbackType = $numFunctions > 1 ? "Object" : "FunctionOrObject"; + push(@$contentRef, " m_data->invokeCallback(args, JSCallbackData::CallbackType::${callbackType}, Identifier::fromString(state, \"${functionName}\"), returnedException);\n"); } - push(@implContent, "\n bool raisedException = false;\n"); - push(@implContent, " m_data->invokeCallback(args, &raisedException);\n"); - push(@implContent, " return !raisedException;\n"); - push(@implContent, "}\n"); + # FIXME: We currently just report the exception. We should probably add an extended attribute to indicate when + # we want the exception to be rethrown instead. + $includesRef->{"JSDOMExceptionHandling.h"} = 1; + push(@$contentRef, " if (returnedException)\n"); + push(@$contentRef, " reportException(state, returnedException);\n"); + push(@$contentRef, " return !returnedException;\n"); + push(@$contentRef, "}\n\n"); } } - push(@implContent, "\n}\n"); - my $conditionalString = $codeGenerator->GenerateConditionalString($interface); - push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString; - - if ($interface->extendedAttributes->{"AppleCopyright"}) { - push(@implContent, split("\r", $endAppleCopyright)); - } + # toJS() implementation. + push(@$contentRef, "JSC::JSValue toJS(${name}& impl)\n"); + push(@$contentRef, "{\n"); + push(@$contentRef, " if (!static_cast<${className}&>(impl).callbackData())\n"); + push(@$contentRef, " return jsNull();\n\n"); + push(@$contentRef, " return static_cast<${className}&>(impl).callbackData()->callback();\n\n"); + push(@$contentRef, "}\n\n"); } sub GenerateImplementationFunctionCall() { - my $function = shift; - my $functionString = shift; - my $indent = shift; - my $svgPropertyType = shift; - my $interfaceName = shift; + my ($function, $functionString, $indent, $interface) = @_; - my $raisesException = $function->signature->extendedAttributes->{"RaisesException"}; - - if ($function->signature->type eq "void") { + if ($function->type->name eq "void" || IsReturningPromise($function)) { push(@implContent, $indent . "$functionString;\n"); - push(@implContent, $indent . "setDOMException(exec, ec);\n") if $raisesException; - - if ($svgPropertyType and !$function->isStatic) { - if ($raisesException) { - push(@implContent, $indent . "if (!ec)\n"); - push(@implContent, $indent . " impl.commitChange();\n"); - } else { - push(@implContent, $indent . "impl.commitChange();\n"); - } - } - push(@implContent, $indent . "return JSValue::encode(jsUndefined());\n"); } else { my $thisObject = $function->isStatic ? 0 : "castedThis"; - push(@implContent, "\n" . $indent . "JSC::JSValue result = " . NativeToJSValue($function->signature, 1, $interfaceName, $functionString, $thisObject) . ";\n"); - push(@implContent, $indent . "setDOMException(exec, ec);\n") if $raisesException; + push(@implContent, $indent . "return JSValue::encode(" . NativeToJSValueUsingPointers($function, $interface, $functionString, $thisObject) . ");\n"); + } +} - if ($codeGenerator->ExtendedAttributeContains($function->signature->extendedAttributes->{"CallWith"}, "ScriptState")) { - push(@implContent, $indent . "if (exec->hadException())\n"); - push(@implContent, $indent . " return JSValue::encode(jsUndefined());\n"); - } +sub IsValueIterableInterface +{ + my $interface = shift; + return 0 unless $interface->iterable; + return 0 if length $interface->iterable->keyType; + # FIXME: See https://webkit.org/b/159140, we should die if the next check is false. + return 0 unless GetIndexedGetterFunction($interface); + return 1; +} - push(@implContent, $indent . "return JSValue::encode(result);\n"); - } +sub IsKeyValueIterableInterface +{ + my $interface = shift; + return 0 unless $interface->iterable; + return 0 if IsValueIterableInterface($interface); + return 1; } -sub GetNativeTypeFromSignature +sub GenerateImplementationIterableFunctions { - my $signature = shift; - my $type = $signature->type; + my $interface = shift; - if ($type eq "unsigned long" and $signature->extendedAttributes->{"IsIndex"}) { - # Special-case index arguments because we need to check that they aren't < 0. - return "int"; - } + my $interfaceName = $interface->type->name; + my $className = "JS$interfaceName"; + my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($interface); + + AddToImplIncludes("JSDOMIterator.h"); + + return unless IsKeyValueIterableInterface($interface); + + my $iteratorName = "${interfaceName}Iterator"; + my $iteratorPrototypeName = "${interfaceName}IteratorPrototype"; + + my $iteratorTraitsName = "${interfaceName}IteratorTraits"; + my $iteratorTraitsType = $interface->iterable->isKeyValue ? "JSDOMIteratorType::Map" : "JSDOMIteratorType::Set"; + my $iteratorTraitsKeyType = $interface->iterable->isKeyValue ? GetIDLType($interface, $interface->iterable->keyType) : "void"; + my $iteratorTraitsValueType = GetIDLType($interface, $interface->iterable->valueType); + + push(@implContent, <<END); +struct ${iteratorTraitsName} { + static constexpr JSDOMIteratorType type = ${iteratorTraitsType}; + using KeyType = ${iteratorTraitsKeyType}; + using ValueType = ${iteratorTraitsValueType}; +}; + +using ${iteratorName} = JSDOMIterator<${className}, ${iteratorTraitsName}>; +using ${iteratorPrototypeName} = JSDOMIteratorPrototype<${className}, ${iteratorTraitsName}>; + +template<> +const JSC::ClassInfo ${iteratorName}::s_info = { "${visibleInterfaceName} Iterator", &Base::s_info, 0, CREATE_METHOD_TABLE(${iteratorName}) }; + +template<> +const JSC::ClassInfo ${iteratorPrototypeName}::s_info = { "${visibleInterfaceName} Iterator", &Base::s_info, 0, CREATE_METHOD_TABLE(${iteratorPrototypeName}) }; + +END + + foreach my $function (@{$interface->iterable->functions}) { + my $propertyName = $function->name; + my $functionName = GetFunctionName($interface, $className, $function); + + if ($propertyName eq "forEach") { + push(@implContent, <<END); +static inline EncodedJSValue ${functionName}Caller(ExecState* state, JS$interfaceName* thisObject, JSC::ThrowScope& throwScope) +{ + return JSValue::encode(iteratorForEach<${iteratorName}>(*state, *thisObject, throwScope)); +} + +END + } else { + my $iterationKind = "KeyValue"; + $iterationKind = "Key" if $propertyName eq "keys"; + $iterationKind = "Value" if $propertyName eq "values"; + $iterationKind = "Value" if $propertyName eq "[Symbol.Iterator]" and not $interface->iterable->isKeyValue; + + push(@implContent, <<END); +static inline EncodedJSValue ${functionName}Caller(ExecState*, JS$interfaceName* thisObject, JSC::ThrowScope&) +{ + return JSValue::encode(iteratorCreate<${iteratorName}>(*thisObject, IterationKind::${iterationKind})); +} + +END + } + + push(@implContent, <<END); +JSC::EncodedJSValue JSC_HOST_CALL ${functionName}(JSC::ExecState* state) +{ + return BindingCaller<$className>::callOperation<${functionName}Caller>(state, "${propertyName}"); +} - return GetNativeType($type); +END + } } my %nativeType = ( - "CompareHow" => "Range::CompareHow", - "DOMString" => "const String&", - "NodeFilter" => "RefPtr<NodeFilter>", - "SerializedScriptValue" => "RefPtr<SerializedScriptValue>", + "ByteString" => "String", + "DOMString" => "String", + "USVString" => "String", "Date" => "double", - "Dictionary" => "Dictionary", - "any" => "Deprecated::ScriptValue", + "EventListener" => "RefPtr<EventListener>", + "SerializedScriptValue" => "RefPtr<SerializedScriptValue>", + "XPathNSResolver" => "RefPtr<XPathNSResolver>", + "any" => "JSC::JSValue", + "object" => "JSC::Strong<JSC::JSObject>", "boolean" => "bool", + "byte" => "int8_t", "double" => "double", "float" => "float", + "long long" => "int64_t", + "long" => "int32_t", + "octet" => "uint8_t", "short" => "int16_t", - "long" => "int", - "unsigned long" => "unsigned", + "unrestricted double" => "double", + "unrestricted float" => "float", + "unsigned long long" => "uint64_t", + "unsigned long" => "uint32_t", "unsigned short" => "uint16_t", - "long long" => "long long", - "unsigned long long" => "unsigned long long", - "byte" => "int8_t", - "octet" => "uint8_t", - "DOMTimeStamp" => "DOMTimeStamp", ); -sub GetNativeType +# http://heycam.github.io/webidl/#dfn-flattened-union-member-types +sub GetFlattenedMemberTypes { - my $type = shift; + my ($idlUnionType) = @_; - my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($type); - return "${svgNativeType}*" if $svgNativeType; - return "RefPtr<DOMStringList>" if $type eq "DOMStringList"; - return "RefPtr<${type}>" if $codeGenerator->IsTypedArrayType($type) and not $type eq "ArrayBuffer"; - return $nativeType{$type} if exists $nativeType{$type}; + my @flattenedMemberTypes = (); - my $arrayType = $codeGenerator->GetArrayType($type); - my $sequenceType = $codeGenerator->GetSequenceType($type); - my $arrayOrSequenceType = $arrayType || $sequenceType; + foreach my $memberType (@{$idlUnionType->subtypes}) { + if ($memberType->isUnion) { + push(@flattenedMemberTypes, GetFlattenedMemberTypes($memberType)); + } else { + push(@flattenedMemberTypes, $memberType); + } + } - return "Vector<" . GetNativeVectorInnerType($arrayOrSequenceType) . ">" if $arrayOrSequenceType; + return @flattenedMemberTypes; +} - if ($codeGenerator->IsEnumType($type)) { - return "const String"; +# http://heycam.github.io/webidl/#dfn-number-of-nullable-member-types +sub GetNumberOfNullableMemberTypes +{ + my ($idlUnionType) = @_; + + my $count = 0; + + foreach my $memberType (@{$idlUnionType->subtypes}) { + $count++ if $memberType->isNullable; + $count += GetNumberOfNullableMemberTypes($memberType) if $memberType->isUnion; } - # For all other types, the native type is a pointer with same type name as the IDL type. - return "${type}*"; + return $count; +} + +sub GetIDLUnionMemberTypes +{ + my ($interface, $idlUnionType) = @_; + + my $numberOfNullableMembers = GetNumberOfNullableMemberTypes($idlUnionType); + assert("Union types must only have 0 or 1 nullable types.") if $numberOfNullableMembers > 1; + + my @idlUnionMemberTypes = (); + + push(@idlUnionMemberTypes, "IDLNull") if $numberOfNullableMembers == 1; + + foreach my $memberType (GetFlattenedMemberTypes($idlUnionType)) { + push(@idlUnionMemberTypes, GetBaseIDLType($interface, $memberType)); + } + + return @idlUnionMemberTypes; +} + +sub GetBaseIDLType +{ + my ($interface, $type, $context) = @_; + + if ($context && $context->extendedAttributes->{OverrideIDLType}) { + return $context->extendedAttributes->{OverrideIDLType}; + } + + my %IDLTypes = ( + "any" => "IDLAny", + "boolean" => "IDLBoolean", + "byte" => "IDLByte", + "octet" => "IDLOctet", + "short" => "IDLShort", + "unsigned short" => "IDLUnsignedShort", + "long" => "IDLLong", + "unsigned long" => "IDLUnsignedLong", + "long long" => "IDLLongLong", + "unsigned long long" => "IDLUnsignedLongLong", + "float" => "IDLFloat", + "unrestricted float" => "IDLUnrestrictedFloat", + "double" => "IDLDouble", + "unrestricted double" => "IDLUnrestrictedDouble", + "DOMString" => "IDLDOMString", + "ByteString" => "IDLByteString", + "USVString" => "IDLUSVString", + "object" => "IDLObject", + + # Non-WebIDL extensions + "Date" => "IDLDate", + "EventListener" => "IDLEventListener<JSEventListener>", + "JSON" => "IDLJSON", + "SerializedScriptValue" => "IDLSerializedScriptValue<SerializedScriptValue>", + "XPathNSResolver" => "IDLXPathNSResolver<XPathNSResolver>", + + # Convenience type aliases + "BufferSource" => "IDLBufferSource", + ); + + return $IDLTypes{$type->name} if exists $IDLTypes{$type->name}; + return "IDLEnumeration<" . GetEnumerationClassName($type, $interface) . ">" if $codeGenerator->IsEnumType($type); + return "IDLDictionary<" . GetDictionaryClassName($type, $interface) . ">" if $codeGenerator->IsDictionaryType($type); + return "IDLSequence<" . GetIDLType($interface, @{$type->subtypes}[0]) . ">" if $codeGenerator->IsSequenceType($type); + return "IDLFrozenArray<" . GetIDLType($interface, @{$type->subtypes}[0]) . ">" if $codeGenerator->IsFrozenArrayType($type); + return "IDLRecord<" . GetIDLType($interface, @{$type->subtypes}[0]) . ", " . GetIDLType($interface, @{$type->subtypes}[1]) . ">" if $codeGenerator->IsRecordType($type); + return "IDLUnion<" . join(", ", GetIDLUnionMemberTypes($interface, $type)) . ">" if $type->isUnion; + return "IDLCallbackFunction<" . GetCallbackClassName($type->name) . ">" if $codeGenerator->IsCallbackFunction($type); + return "IDLCallbackInterface<" . GetCallbackClassName($type->name) . ">" if $codeGenerator->IsCallbackInterface($type); + + assert("Unknown type '" . $type->name . "'.\n") unless $codeGenerator->IsInterfaceType($type) || $codeGenerator->IsTypedArrayType($type); + return "IDLInterface<" . $type->name . ">"; +} + +sub GetIDLType +{ + my ($interface, $type, $context) = @_; + + my $baseIDLType = GetBaseIDLType($interface, $type, $context); + return "IDLNullable<" . $baseIDLType . ">" if $type->isNullable; + return $baseIDLType; } -sub GetNativeVectorInnerType +sub GetNativeType { - my $arrayOrSequenceType = shift; + my ($interface, $type) = @_; + + assert("Not a type") if ref($type) ne "IDLType"; - return "String" if $arrayOrSequenceType eq "DOMString"; - return $nativeType{$arrayOrSequenceType} if exists $nativeType{$arrayOrSequenceType}; - return "RefPtr<${arrayOrSequenceType}> "; + my $typeName = $type->name; + + return $nativeType{$typeName} if exists $nativeType{$typeName}; + + return GetEnumerationClassName($type, $interface) if $codeGenerator->IsEnumType($type); + return GetDictionaryClassName($type, $interface) if $codeGenerator->IsDictionaryType($type); + return "Vector<" . GetNativeInnerType(@{$type->subtypes}[0], $interface) . ">" if $codeGenerator->IsSequenceOrFrozenArrayType($type); + return "Vector<WTF::KeyValuePair<" . GetNativeInnerType(@{$type->subtypes}[0], $interface) . ", " . GetNativeInnerType(@{$type->subtypes}[1], $interface) . ">>" if $codeGenerator->IsRecordType($type); + + return "RefPtr<${typeName}>" if $codeGenerator->IsTypedArrayType($type) and $typeName ne "ArrayBuffer"; + return "${typeName}*"; +} + +sub GetNativeInnerType +{ + my ($innerType, $interface) = @_; + + my $innerTypeName = $innerType->name; + + return $nativeType{$innerTypeName} if exists $nativeType{$innerTypeName}; + + return GetEnumerationClassName($innerType, $interface) if $codeGenerator->IsEnumType($innerType); + return GetDictionaryClassName($innerType, $interface) if $codeGenerator->IsDictionaryType($innerType); + return "Vector<" . GetNativeInnerType(@{$innerType->subtypes}[0], $interface) . ">" if $codeGenerator->IsSequenceOrFrozenArrayType($innerType); + return "Vector<WTF::KeyValuePair<" . GetNativeInnerType(@{$innerType->subtypes}[0], $interface) . ", " . GetNativeInnerType(@{$innerType->subtypes}[1], $interface) . ">>" if $codeGenerator->IsRecordType($innerType); + return "RefPtr<$innerTypeName>"; } sub GetNativeTypeForCallbacks { - my $type = shift; - return "PassRefPtr<SerializedScriptValue>" if $type eq "SerializedScriptValue"; - return "PassRefPtr<DOMStringList>" if $type eq "DOMStringList"; + my ($type, $interface) = @_; - return GetNativeType($type); + return "RefPtr<SerializedScriptValue>&&" if $type->name eq "SerializedScriptValue"; + return "const String&" if $codeGenerator->IsStringType($type); + return GetNativeType($interface, $type); } -sub GetSVGPropertyTypes +sub ShouldPassArgumentByReference { - my $implType = shift; + my ($argument) = @_; - my $svgPropertyType; - my $svgListPropertyType; - my $svgNativeType; + my $type = $argument->type; - return ($svgPropertyType, $svgListPropertyType, $svgNativeType) if not $implType =~ /SVG/; - - $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($implType); - return ($svgPropertyType, $svgListPropertyType, $svgNativeType) if not $svgNativeType; - - # Append space to avoid compilation errors when using PassRefPtr<$svgNativeType> - $svgNativeType = "$svgNativeType "; - - my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($implType); - if ($svgNativeType =~ /SVGPropertyTearOff/) { - $svgPropertyType = $svgWrappedNativeType; - $headerIncludes{"$svgWrappedNativeType.h"} = 1; - $headerIncludes{"SVGAnimatedPropertyTearOff.h"} = 1; - } elsif ($svgNativeType =~ /SVGListPropertyTearOff/ or $svgNativeType =~ /SVGStaticListPropertyTearOff/) { - $svgListPropertyType = $svgWrappedNativeType; - $headerIncludes{"$svgWrappedNativeType.h"} = 1; - $headerIncludes{"SVGAnimatedListPropertyTearOff.h"} = 1; - } elsif ($svgNativeType =~ /SVGTransformListPropertyTearOff/) { - $svgListPropertyType = $svgWrappedNativeType; - $headerIncludes{"$svgWrappedNativeType.h"} = 1; - $headerIncludes{"SVGAnimatedListPropertyTearOff.h"} = 1; - $headerIncludes{"SVGTransformListPropertyTearOff.h"} = 1; - } elsif ($svgNativeType =~ /SVGPathSegListPropertyTearOff/) { - $svgListPropertyType = $svgWrappedNativeType; - $headerIncludes{"$svgWrappedNativeType.h"} = 1; - $headerIncludes{"SVGAnimatedListPropertyTearOff.h"} = 1; - $headerIncludes{"SVGPathSegListPropertyTearOff.h"} = 1; - } - - return ($svgPropertyType, $svgListPropertyType, $svgNativeType); -} - -sub IsNativeType + return 0 if $type->isNullable; + return 0 if $codeGenerator->IsCallbackInterface($type); + return 0 if $codeGenerator->IsCallbackFunction($type); + return 0 if !$codeGenerator->IsWrapperType($type) && !$codeGenerator->IsTypedArrayType($type); + + return 1; +} + +sub GetIntegerConversionConfiguration { - my $type = shift; - return exists $nativeType{$type}; + my $context = shift; + + return "IntegerConversionConfiguration::EnforceRange" if $context->extendedAttributes->{EnforceRange}; + return "IntegerConversionConfiguration::Clamp" if $context->extendedAttributes->{Clamp}; + return "IntegerConversionConfiguration::Normal"; } -sub JSValueToNative +sub GetStringConversionConfiguration { - my $signature = shift; - my $value = shift; - - my $conditional = $signature->extendedAttributes->{"Conditional"}; - my $type = $signature->type; - - return "$value.toBoolean(exec)" if $type eq "boolean"; - return "$value.toNumber(exec)" if $type eq "double"; - return "$value.toFloat(exec)" if $type eq "float"; - - my $intConversion = $signature->extendedAttributes->{"EnforceRange"} ? "EnforceRange" : "NormalConversion"; - return "toInt8(exec, $value, $intConversion)" if $type eq "byte"; - return "toUInt8(exec, $value, $intConversion)" if $type eq "octet"; - return "toInt16(exec, $value, $intConversion)" if $type eq "short"; - return "toUInt16(exec, $value, $intConversion)" if $type eq "unsigned short"; - return "toInt32(exec, $value, $intConversion)" if $type eq "long"; - return "toUInt32(exec, $value, $intConversion)" if $type eq "unsigned long"; - return "toInt64(exec, $value, $intConversion)" if $type eq "long long"; - return "toUInt64(exec, $value, $intConversion)" if $type eq "unsigned long long"; - - return "valueToDate(exec, $value)" if $type eq "Date"; - return "static_cast<Range::CompareHow>($value.toInt32(exec))" if $type eq "CompareHow"; - - if ($type eq "DOMString") { - # FIXME: This implements [TreatNullAs=NullString] and [TreatUndefinedAs=NullString], - # but the Web IDL spec requires [TreatNullAs=EmptyString] and [TreatUndefinedAs=EmptyString]. - if (($signature->extendedAttributes->{"TreatNullAs"} and $signature->extendedAttributes->{"TreatNullAs"} eq "NullString") and ($signature->extendedAttributes->{"TreatUndefinedAs"} and $signature->extendedAttributes->{"TreatUndefinedAs"} eq "NullString")) { - return "valueToStringWithUndefinedOrNullCheck(exec, $value)" - } - if (($signature->extendedAttributes->{"TreatNullAs"} and $signature->extendedAttributes->{"TreatNullAs"} eq "NullString") or $signature->extendedAttributes->{"Reflect"}) { - return "valueToStringWithNullCheck(exec, $value)" - } - # FIXME: Add the case for 'if ($signature->extendedAttributes->{"TreatUndefinedAs"} and $signature->extendedAttributes->{"TreatUndefinedAs"} eq "NullString"))'. - return "$value.isEmpty() ? String() : $value.toString(exec)->value(exec)"; - } + my $context = shift; - if ($type eq "any") { - return "exec->vm(), $value"; - } + return "StringConversionConfiguration::TreatNullAsEmptyString" if $context->extendedAttributes->{TreatNullAs} && $context->extendedAttributes->{TreatNullAs} eq "EmptyString"; + return "StringConversionConfiguration::Normal"; +} - if ($type eq "NodeFilter") { - AddToImplIncludes("JS$type.h", $conditional); - return "to$type(exec->vm(), $value)"; - } +sub JSValueToNativeDOMConvertNeedsThisObject +{ + my $type = shift; - if ($type eq "SerializedScriptValue") { - AddToImplIncludes("SerializedScriptValue.h", $conditional); - return "SerializedScriptValue::create(exec, $value, 0, 0)"; - } + return 1 if $type->name eq "EventListener"; + return 0; +} - if ($type eq "Dictionary") { - AddToImplIncludes("Dictionary.h", $conditional); - return "exec, $value"; - } +sub JSValueToNativeDOMConvertNeedsGlobalObject +{ + my $type = shift; - if ($type eq "DOMStringList" ) { - AddToImplIncludes("JSDOMStringList.h", $conditional); - return "toDOMStringList(exec, $value)"; - } + return 1 if $codeGenerator->IsCallbackInterface($type); + return 1 if $codeGenerator->IsCallbackFunction($type); + return 0; +} + +sub IsValidContextForJSValueToNative +{ + my $context = shift; - if ($codeGenerator->IsTypedArrayType($type)) { - return "to$type($value)"; - } + return ref($context) eq "IDLAttribute" || ref($context) eq "IDLArgument"; +} - AddToImplIncludes("HTMLOptionElement.h", $conditional) if $type eq "HTMLOptionElement"; - AddToImplIncludes("Event.h", $conditional) if $type eq "Event"; +# Returns (convertString, mayThrowException). - my $arrayType = $codeGenerator->GetArrayType($type); - my $sequenceType = $codeGenerator->GetSequenceType($type); - my $arrayOrSequenceType = $arrayType || $sequenceType; +sub JSValueToNative +{ + my ($interface, $context, $value, $conditional, $statePointer, $stateReference, $thisObjectReference, $globalObjectReference, $exceptionThrower) = @_; - if ($arrayOrSequenceType) { - if ($codeGenerator->IsRefPtrType($arrayOrSequenceType)) { - AddToImplIncludes("JS${arrayOrSequenceType}.h"); - return "(toRefPtrNativeArray<${arrayOrSequenceType}, JS${arrayOrSequenceType}>(exec, $value, &to${arrayOrSequenceType}))"; - } - return "toNativeArray<" . GetNativeVectorInnerType($arrayOrSequenceType) . ">(exec, $value)"; + assert("Invalid context type") if !IsValidContextForJSValueToNative($context); + + my $type = $context->type; + + # FIXME: Remove these 3 variables when all JSValueToNative use references. + $statePointer = "state" unless $statePointer; + $stateReference = "*state" unless $stateReference; + $thisObjectReference = "*castedThis" unless $thisObjectReference; + + AddToImplIncludesForIDLType($type, $conditional); + + if ($type->name eq "DOMString") { + return ("AtomicString($value.toString($statePointer)->toExistingAtomicString($statePointer))", 1) if $context->extendedAttributes->{RequiresExistingAtomicString}; + return ("$value.toString($statePointer)->toAtomicString($statePointer)", 1) if $context->extendedAttributes->{AtomicString}; } if ($codeGenerator->IsEnumType($type)) { - return "$value.isEmpty() ? String() : $value.toString(exec)->value(exec)"; + return ("parseEnumeration<" . GetEnumerationClassName($type, $interface) . ">($stateReference, $value)", 1); } - # Default, assume autogenerated type conversion routines - AddToImplIncludes("JS$type.h", $conditional); - return "to$type($value)"; + AddToImplIncludes("JSDOMConvert.h"); + + my $IDLType = GetIDLType($interface, $type); + + my @conversionArguments = (); + push(@conversionArguments, $stateReference); + push(@conversionArguments, $value); + push(@conversionArguments, $thisObjectReference) if JSValueToNativeDOMConvertNeedsThisObject($type); + push(@conversionArguments, $globalObjectReference) if JSValueToNativeDOMConvertNeedsGlobalObject($type); + push(@conversionArguments, GetIntegerConversionConfiguration($context)) if $codeGenerator->IsIntegerType($type); + push(@conversionArguments, GetStringConversionConfiguration($context)) if $codeGenerator->IsStringType($type); + push(@conversionArguments, $exceptionThrower) if $exceptionThrower; + + return ("convert<$IDLType>(" . join(", ", @conversionArguments) . ")", 1); } -sub NativeToJSValue +sub UnsafeToNative { - my $signature = shift; - my $inFunctionCall = shift; - my $interfaceName = shift; - my $value = shift; - my $thisValue = shift; + my ($interface, $context, $value, $conditional, $statePointer, $stateReference, $thisObjectReference) = @_; - my $conditional = $signature->extendedAttributes->{"Conditional"}; - my $type = $signature->type; + assert("Invalid context type") if !IsValidContextForJSValueToNative($context); - return "jsBoolean($value)" if $type eq "boolean"; + my $type = $context->type; - # Need to check Date type before IsPrimitiveType(). - if ($type eq "Date") { - return "jsDateOrNull(exec, $value)"; - } + # FIXME: Remove these 3 variables when all JSValueToNative use references. + $statePointer = "state" unless $statePointer; + $stateReference = "*state" unless $stateReference; + $thisObjectReference = "*castedThis" unless $thisObjectReference; - if ($signature->extendedAttributes->{"Reflect"} and ($type eq "unsigned long" or $type eq "unsigned short")) { - $value =~ s/getUnsignedIntegralAttribute/getIntegralAttribute/g; - return "jsNumber(std::max(0, " . $value . "))"; - } + AddToImplIncludesForIDLType($type, $conditional); - if ($codeGenerator->IsPrimitiveType($type) or $type eq "DOMTimeStamp") { - return "jsNumber($value)"; - } + # FIXME: Support more types. - if ($codeGenerator->IsEnumType($type)) { - AddToImplIncludes("<runtime/JSString.h>", $conditional); - return "jsStringWithCache(exec, $value)"; + if ($type->name eq "DOMString") { + return ("AtomicString($value->toExistingAtomicString($statePointer))", 1) if $context->extendedAttributes->{RequiresExistingAtomicString}; + return ("$value->toAtomicString($statePointer)", 1) if $context->extendedAttributes->{AtomicString}; } - if ($codeGenerator->IsStringType($type)) { - AddToImplIncludes("URL.h", $conditional); - my $conv = $signature->extendedAttributes->{"TreatReturnedNullStringAs"}; - if (defined $conv) { - return "jsStringOrNull(exec, $value)" if $conv eq "Null"; - return "jsStringOrUndefined(exec, $value)" if $conv eq "Undefined"; + AddToImplIncludes("DOMJITIDLConvert.h"); - die "Unknown value for TreatReturnedNullStringAs extended attribute"; - } - AddToImplIncludes("<runtime/JSString.h>", $conditional); - return "jsStringWithCache(exec, $value)"; + my $IDLType = GetIDLType($interface, $type); + + my @conversionArguments = (); + push(@conversionArguments, "$stateReference"); + push(@conversionArguments, "$value"); + + my @conversionStaticArguments = (); + push(@conversionStaticArguments, GetIntegerConversionConfiguration($context)) if $codeGenerator->IsIntegerType($type); + push(@conversionStaticArguments, GetStringConversionConfiguration($context)) if $codeGenerator->IsStringType($type); + + if (scalar(@conversionStaticArguments) > 0) { + return ("DOMJIT::DirectConverter<$IDLType>::directConvert<" . join(", ", @conversionStaticArguments) . ">(" . join(", ", @conversionArguments) . ")", 1); } + return ("DOMJIT::DirectConverter<$IDLType>::directConvert(" . join(", ", @conversionArguments) . ")", 1); +} + +sub NativeToJSValueDOMConvertNeedsState +{ + my ($type, $context) = @_; + + # FIXME: We need a more robust way to specify this requirement so as not + # to require specializing each type. Perhaps just requiring all override + # types to take both state and the global object would work? + if ($context->extendedAttributes->{OverrideIDLType}) { + my $overrideTypeName = $context->extendedAttributes->{OverrideIDLType}; + return 1 if $overrideTypeName eq "IDLIDBKey"; + return 1 if $overrideTypeName eq "IDLWebGLAny"; + + return 0; + } + + # FIXME: This should actually check if all the sub-objects of the union need the state. + return 1 if $type->isUnion; + return 1 if $codeGenerator->IsSequenceOrFrozenArrayType($type); + return 1 if $codeGenerator->IsRecordType($type); + return 1 if $codeGenerator->IsStringType($type); + return 1 if $codeGenerator->IsEnumType($type); + return 1 if $codeGenerator->IsDictionaryType($type); + return 1 if $codeGenerator->IsInterfaceType($type); + return 1 if $codeGenerator->IsTypedArrayType($type); + return 1 if $type->name eq "Date"; + return 1 if $type->name eq "JSON"; + return 1 if $type->name eq "SerializedScriptValue"; + return 1 if $type->name eq "XPathNSResolver"; + + return 0; +} + +sub NativeToJSValueDOMConvertNeedsGlobalObject +{ + my ($type, $context) = @_; - my $globalObject; - if ($thisValue) { - $globalObject = "$thisValue->globalObject()"; - } + # FIXME: We need a more robust way to specify this requirement so as not + # to require specializing each type. Perhaps just requiring all override + # types to take both state and the global object would work? + if ($context->extendedAttributes->{OverrideIDLType}) { + my $overrideTypeName = $context->extendedAttributes->{OverrideIDLType}; + return 1 if $overrideTypeName eq "IDLIDBKey"; + return 1 if $overrideTypeName eq "IDLWebGLAny"; + + return 0; + } + + # FIXME: This should actually check if all the sub-objects of the union need the global object. + return 1 if $type->isUnion; + return 1 if $codeGenerator->IsSequenceOrFrozenArrayType($type); + return 1 if $codeGenerator->IsRecordType($type); + return 1 if $codeGenerator->IsDictionaryType($type); + return 1 if $codeGenerator->IsInterfaceType($type); + return 1 if $codeGenerator->IsTypedArrayType($type); + return 1 if $type->name eq "SerializedScriptValue"; + return 1 if $type->name eq "XPathNSResolver"; - if ($type eq "CSSStyleDeclaration") { - AddToImplIncludes("StyleProperties.h", $conditional); - } + return 0; +} - if ($type eq "NodeList") { - AddToImplIncludes("NameNodeList.h", $conditional); - } +sub NativeToJSValueUsingReferences +{ + my ($context, $interface, $value, $thisValue, $suppressExceptionCheck) = @_; + my $stateReference = "state"; + my $wrapped = "$thisValue.wrapped()"; + my $globalObjectReference = $thisValue ? "*$thisValue.globalObject()" : "*jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject())"; - my $arrayType = $codeGenerator->GetArrayType($type); - my $sequenceType = $codeGenerator->GetSequenceType($type); - my $arrayOrSequenceType = $arrayType || $sequenceType; + return NativeToJSValue($context, $interface, $value, $stateReference, $wrapped, $globalObjectReference, $suppressExceptionCheck); +} - if ($arrayOrSequenceType) { - if ($arrayType eq "DOMString") { - AddToImplIncludes("JSDOMStringList.h", $conditional); - AddToImplIncludes("DOMStringList.h", $conditional); +# FIXME: We should remove NativeToJSValueUsingPointers and combine NativeToJSValueUsingReferences and NativeToJSValue +sub NativeToJSValueUsingPointers +{ + my ($context, $interface, $value, $thisValue, $suppressExceptionCheck) = @_; + my $stateReference = "*state"; + my $wrapped = "$thisValue->wrapped()"; + my $globalObjectReference = $thisValue ? "*$thisValue->globalObject()" : "*jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject())"; - } elsif ($codeGenerator->IsRefPtrType($arrayOrSequenceType)) { - AddToImplIncludes("JS${arrayOrSequenceType}.h", $conditional); - AddToImplIncludes("${arrayOrSequenceType}.h", $conditional); - } - AddToImplIncludes("<runtime/JSArray.h>", $conditional); + return NativeToJSValue($context, $interface, $value, $stateReference, $wrapped, $globalObjectReference, $suppressExceptionCheck); +} - return "jsArray(exec, $thisValue->globalObject(), $value)"; - } +sub IsValidContextForNativeToJSValue +{ + my $context = shift; + + return ref($context) eq "IDLAttribute" || ref($context) eq "IDLOperation" || ref($context) eq "IDLArgument"; +} - if ($type eq "any") { - if ($interfaceName eq "Document") { - AddToImplIncludes("JSCanvasRenderingContext2D.h", $conditional); - } else { - return "($value.hasNoValue() ? jsNull() : $value.jsValue())"; - } - } elsif ($type eq "SerializedScriptValue" or $type eq "any") { - AddToImplIncludes("SerializedScriptValue.h", $conditional); - return "$value ? $value->deserialize(exec, castedThis->globalObject(), 0) : jsNull()"; - } elsif ($codeGenerator->IsTypedArrayType($type)) { - # Do nothing - all headers are already included. - } else { - # Default, include header with same name. - AddToImplIncludes("JS$type.h", $conditional); - AddToImplIncludes("$type.h", $conditional) if not $codeGenerator->SkipIncludeHeader($type); - } - - return $value if $codeGenerator->IsSVGAnimatedType($type); - - if ($signature->extendedAttributes->{"ReturnNewObject"}) { - return "toJSNewlyCreated(exec, $globalObject, WTF::getPtr($value))"; - } - - if ($codeGenerator->IsSVGAnimatedType($interfaceName) or $interfaceName eq "SVGViewSpec") { - # Convert from abstract SVGProperty to real type, so the right toJS() method can be invoked. - $value = "static_cast<" . GetNativeType($type) . ">($value)"; - } elsif ($codeGenerator->IsSVGTypeNeedingTearOff($type) and not $interfaceName =~ /List$/) { - my $tearOffType = $codeGenerator->GetSVGTypeNeedingTearOff($type); - if ($codeGenerator->IsSVGTypeWithWritablePropertiesNeedingTearOff($type) and $inFunctionCall eq 0 and not defined $signature->extendedAttributes->{"Immutable"}) { - my $getter = $value; - $getter =~ s/impl\.//; - $getter =~ s/impl->//; - $getter =~ s/\(\)//; - my $updateMethod = "&${interfaceName}::update" . $codeGenerator->WK_ucfirst($getter); - - my $selfIsTearOffType = $codeGenerator->IsSVGTypeNeedingTearOff($interfaceName); - if ($selfIsTearOffType) { - AddToImplIncludes("SVGMatrixTearOff.h", $conditional); - # FIXME: Blink: Don't create a new one everytime we access the matrix property. This means, e.g, === won't work. - $value = "SVGMatrixTearOff::create(castedThis->impl(), $value)"; - } else { - AddToImplIncludes("SVGStaticPropertyTearOff.h", $conditional); - $tearOffType =~ s/SVGPropertyTearOff</SVGStaticPropertyTearOff<$interfaceName, /; - $value = "${tearOffType}::create(impl, $value, $updateMethod)"; - } - } elsif ($tearOffType =~ /SVGStaticListPropertyTearOff/) { - $value = "${tearOffType}::create(impl, $value)"; - } elsif (not $tearOffType =~ /SVG(Point|PathSeg)List/) { - $value = "${tearOffType}::create($value)"; - } +sub NativeToJSValue +{ + my ($context, $interface, $value, $stateReference, $wrapped, $globalObjectReference, $suppressExceptionCheck) = @_; + + assert("Invalid context type") if !IsValidContextForNativeToJSValue($context); + + my $conditional = $context->extendedAttributes->{Conditional}; + my $type = $context->type; + my $mayThrowException = $context->extendedAttributes->{GetterMayThrowException} || $context->extendedAttributes->{MayThrowException} && !$suppressExceptionCheck; + + # We could instead overload a function to work with optional as well as non-optional numbers, but this + # is slightly better because it guarantees we will fail to compile if the IDL file doesn't match the C++. + if ($context->extendedAttributes->{Reflect} and ($type->name eq "unsigned long" or $type->name eq "unsigned short")) { + $value =~ s/getUnsignedIntegralAttribute/getIntegralAttribute/g; + $value = "std::max(0, $value)"; } - if ($globalObject) { - return "toJS(exec, $globalObject, WTF::getPtr($value))"; - } else { - return "toJS(exec, jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), WTF::getPtr($value))"; + + AddToImplIncludesForIDLType($type, $conditional); + AddToImplIncludes("JSDOMConvert.h", $conditional); + + if ($context->extendedAttributes->{CheckSecurityForNode}) { + AddToImplIncludes("JSDOMBindingSecurity.h", $conditional); + $value = "BindingSecurity::checkSecurityForNode($stateReference, $value)"; } + + my $IDLType = GetIDLType($interface, $type, $context); + + my @conversionArguments = (); + push(@conversionArguments, $stateReference) if NativeToJSValueDOMConvertNeedsState($type, $context) || $mayThrowException; + push(@conversionArguments, $globalObjectReference) if NativeToJSValueDOMConvertNeedsGlobalObject($type, $context); + push(@conversionArguments, "throwScope") if $mayThrowException; + push(@conversionArguments, $value); + + my $functionName = $context->extendedAttributes->{NewObject} ? "toJSNewlyCreated" : "toJS"; + + return "${functionName}<${IDLType}>(" . join(", ", @conversionArguments) . ")"; } sub ceilingToPowerOf2 @@ -3722,6 +5697,69 @@ sub ceilingToPowerOf2 } # Internal Helper +sub GenerateHashTableValueArray +{ + my $keys = shift; + my $specials = shift; + my $value1 = shift; + my $value2 = shift; + my $conditionals = shift; + my $nameEntries = shift; + + my $packedSize = scalar @{$keys}; + push(@implContent, "\nstatic const HashTableValue $nameEntries\[\] =\n\{\n"); + + my $hasSetter = "false"; + + my $i = 0; + foreach my $key (@{$keys}) { + my $conditional; + my $firstTargetType; + my $secondTargetType = ""; + + if ($conditionals) { + $conditional = $conditionals->{$key}; + } + if ($conditional) { + my $conditionalString = $codeGenerator->GenerateConditionalStringFromAttributeValue($conditional); + push(@implContent, "#if ${conditionalString}\n"); + } + + if ("@$specials[$i]" =~ m/DOMJITFunction/) { + $firstTargetType = "static_cast<NativeFunction>"; + $secondTargetType = "static_cast<const JSC::DOMJIT::Signature*>"; + } elsif ("@$specials[$i]" =~ m/Function/) { + $firstTargetType = "static_cast<NativeFunction>"; + } elsif ("@$specials[$i]" =~ m/Builtin/) { + $firstTargetType = "static_cast<BuiltinGenerator>"; + } elsif ("@$specials[$i]" =~ m/ConstantInteger/) { + $firstTargetType = ""; + } elsif ("@$specials[$i]" =~ m/DOMJITAttribute/) { + $firstTargetType = "static_cast<DOMJITGetterSetterGenerator>"; + } else { + $firstTargetType = "static_cast<PropertySlot::GetValueFunc>"; + $secondTargetType = "static_cast<PutPropertySlot::PutValueFunc>"; + $hasSetter = "true"; + } + if ("@$specials[$i]" =~ m/ConstantInteger/) { + push(@implContent, " { \"$key\", @$specials[$i], NoIntrinsic, { (long long)" . $firstTargetType . "(@$value1[$i]) } },\n"); + } else { + push(@implContent, " { \"$key\", @$specials[$i], NoIntrinsic, { (intptr_t)" . $firstTargetType . "(@$value1[$i]), (intptr_t) " . $secondTargetType . "(@$value2[$i]) } },\n"); + } + if ($conditional) { + push(@implContent, "#else\n") ; + push(@implContent, " { 0, 0, NoIntrinsic, { 0, 0 } },\n"); + push(@implContent, "#endif\n") ; + } + ++$i; + } + + push(@implContent, " { 0, 0, NoIntrinsic, { 0, 0 } }\n") if (!$packedSize); + push(@implContent, "};\n\n"); + + return $hasSetter; +} + sub GenerateHashTable { my $object = shift; @@ -3733,6 +5771,34 @@ sub GenerateHashTable my $value1 = shift; my $value2 = shift; my $conditionals = shift; + my $justGenerateValueArray = shift; + + my $nameEntries = "${name}Values"; + $nameEntries =~ s/:/_/g; + my $nameIndex = "${name}Index"; + $nameIndex =~ s/:/_/g; + + if (($name =~ /Prototype/) or ($name =~ /Constructor/)) { + my $type = $name; + my $implClass; + + if ($name =~ /Prototype/) { + $type =~ s/Prototype.*//; + $implClass = $type; $implClass =~ s/Wrapper$//; + push(@implContent, "/* Hash table for prototype */\n"); + } else { + $type =~ s/Constructor.*//; + $implClass = $type; $implClass =~ s/Constructor$//; + push(@implContent, "/* Hash table for constructor */\n"); + } + } else { + push(@implContent, "/* Hash table */\n"); + } + + if ($justGenerateValueArray) { + GenerateHashTableValueArray($keys, $specials, $value1, $value2, $conditionals, $nameEntries) if $size; + return; + } # Generate size data for compact' size hash table @@ -3768,60 +5834,22 @@ sub GenerateHashTable $maxDepth = $depth if ($depth > $maxDepth); } - # Start outputing the hashtables - my $nameEntries = "${name}Values"; - $nameEntries =~ s/:/_/g; - my $hasSetter = "false"; - - if (($name =~ /Prototype/) or ($name =~ /Constructor/)) { - my $type = $name; - my $implClass; - - if ($name =~ /Prototype/) { - $type =~ s/Prototype.*//; - $implClass = $type; $implClass =~ s/Wrapper$//; - push(@implContent, "/* Hash table for prototype */\n"); - } else { - $type =~ s/Constructor.*//; - $implClass = $type; $implClass =~ s/Constructor$//; - push(@implContent, "/* Hash table for constructor */\n"); - } - } else { - push(@implContent, "/* Hash table */\n"); + push(@implContent, "\nstatic const struct CompactHashIndex ${nameIndex}\[$compactSize\] = {\n"); + for (my $i = 0; $i < $compactSize; $i++) { + my $T = -1; + if (defined($table[$i])) { $T = $table[$i]; } + my $L = -1; + if (defined($links[$i])) { $L = $links[$i]; } + push(@implContent, " { $T, $L },\n"); } + push(@implContent, "};\n\n"); # Dump the hash table - push(@implContent, "\nstatic const HashTableValue $nameEntries\[\] =\n\{\n"); - $i = 0; - foreach my $key (@{$keys}) { - my $conditional; - my $firstTargetType; - my $secondTargetType = ""; - - if ($conditionals) { - $conditional = $conditionals->{$key}; - } - if ($conditional) { - my $conditionalString = $codeGenerator->GenerateConditionalStringFromAttributeValue($conditional); - push(@implContent, "#if ${conditionalString}\n"); - } - - if ("@$specials[$i]" =~ m/Function/) { - $firstTargetType = "static_cast<NativeFunction>"; - } else { - $firstTargetType = "static_cast<PropertySlot::GetValueFunc>"; - $secondTargetType = "static_cast<PutPropertySlot::PutValueFunc>"; - $hasSetter = "true"; - } - push(@implContent, " { \"$key\", @$specials[$i], NoIntrinsic, (intptr_t)" . $firstTargetType . "(@$value1[$i]), (intptr_t) " . $secondTargetType . "(@$value2[$i]) },\n"); - push(@implContent, "#endif\n") if $conditional; - ++$i; - } + my $hasSetter = GenerateHashTableValueArray($keys, $specials, $value1, $value2, $conditionals, $nameEntries); + my $packedSize = scalar @{$keys}; - push(@implContent, " { 0, 0, NoIntrinsic, 0, 0 }\n"); - push(@implContent, "};\n\n"); my $compactSizeMask = $numEntries - 1; - push(@implContent, "static const HashTable $name = { $compactSize, $compactSizeMask, $hasSetter, $nameEntries, 0 };\n"); + push(@implContent, "static const HashTable $name = { $packedSize, $compactSizeMask, $hasSetter, $nameEntries, $nameIndex };\n"); } sub WriteData @@ -3830,11 +5858,10 @@ sub WriteData my $interface = shift; my $outputDir = shift; - my $name = $interface->name; - my $prefix = FileNamePrefix; - my $headerFileName = "$outputDir/$prefix$name.h"; - my $implFileName = "$outputDir/$prefix$name.cpp"; - my $depsFileName = "$outputDir/$prefix$name.dep"; + my $name = $interface->type->name; + my $headerFileName = "$outputDir/JS$name.h"; + my $implFileName = "$outputDir/JS$name.cpp"; + my $depsFileName = "$outputDir/JS$name.dep"; # Update a .cpp file if the contents are changed. my $contents = join "", @implContentHeader; @@ -3842,24 +5869,28 @@ sub WriteData my @includes = (); my %implIncludeConditions = (); foreach my $include (keys %implIncludes) { + next if $headerIncludes{$include}; + next if $headerTrailingIncludes{$include}; + my $condition = $implIncludes{$include}; + my $checkType = $include; $checkType =~ s/\.h//; - next if $codeGenerator->IsSVGAnimatedType($checkType); + next if $codeGenerator->IsSVGAnimatedTypeName($checkType); $include = "\"$include\"" unless $include =~ /^["<]/; # " if ($condition eq 1) { push @includes, $include; } else { - push @{$implIncludeConditions{$condition}}, $include; + push @{$implIncludeConditions{$codeGenerator->GenerateConditionalStringFromAttributeValue($condition)}}, $include; } } foreach my $include (sort @includes) { $contents .= "#include $include\n"; } foreach my $condition (sort keys %implIncludeConditions) { - $contents .= "\n#if " . $codeGenerator->GenerateConditionalStringFromAttributeValue($condition) . "\n"; + $contents .= "\n#if " . $condition . "\n"; foreach my $include (sort @{$implIncludeConditions{$condition}}) { $contents .= "#include $include\n"; } @@ -3883,7 +5914,7 @@ sub WriteData } foreach my $include (sort @includes) { # "JSClassName.h" is already included right after config.h. - next if $include eq "\"$prefix$name.h\""; + next if $include eq "\"JS$name.h\""; $contents .= "#include $include\n"; } @@ -3913,253 +5944,150 @@ sub WriteData } } -sub GenerateConstructorDeclaration +sub GeneratePrototypeDeclaration { - my $outputArray = shift; - my $className = shift; - my $interface = shift; - my $interfaceName = shift; - - my $constructorClassName = "${className}Constructor"; + my ($outputArray, $className, $interface) = @_; - push(@$outputArray, "class ${constructorClassName} : public DOMConstructorObject {\n"); - push(@$outputArray, "private:\n"); - push(@$outputArray, " ${constructorClassName}(JSC::Structure*, JSDOMGlobalObject*);\n"); - push(@$outputArray, " void finishCreation(JSC::VM&, JSDOMGlobalObject*);\n\n"); + my $prototypeClassName = "${className}Prototype"; + my %structureFlags = (); + push(@$outputArray, "class ${prototypeClassName} : public JSC::JSNonFinalObject {\n"); push(@$outputArray, "public:\n"); - push(@$outputArray, " typedef DOMConstructorObject Base;\n"); - push(@$outputArray, " static $constructorClassName* create(JSC::VM& vm, JSC::Structure* structure, JSDOMGlobalObject* globalObject)\n"); + push(@$outputArray, " using Base = JSC::JSNonFinalObject;\n"); + + push(@$outputArray, " static ${prototypeClassName}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)\n"); push(@$outputArray, " {\n"); - push(@$outputArray, " $constructorClassName* ptr = new (NotNull, JSC::allocateCell<$constructorClassName>(vm.heap)) $constructorClassName(structure, globalObject);\n"); - push(@$outputArray, " ptr->finishCreation(vm, globalObject);\n"); + push(@$outputArray, " ${className}Prototype* ptr = new (NotNull, JSC::allocateCell<${className}Prototype>(vm.heap)) ${className}Prototype(vm, globalObject, structure);\n"); + push(@$outputArray, " ptr->finishCreation(vm);\n"); push(@$outputArray, " return ptr;\n"); push(@$outputArray, " }\n\n"); - push(@$outputArray, " static bool getOwnPropertySlot(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&);\n"); push(@$outputArray, " DECLARE_INFO;\n"); - push(@$outputArray, " static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)\n"); + push(@$outputArray, + " static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)\n" . + " {\n" . + " return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());\n" . + " }\n"); + + push(@$outputArray, "\nprivate:\n"); + push(@$outputArray, " ${prototypeClassName}(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure)\n"); + push(@$outputArray, " : JSC::JSNonFinalObject(vm, structure)\n"); push(@$outputArray, " {\n"); - push(@$outputArray, " return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());\n"); push(@$outputArray, " }\n"); - push(@$outputArray, "protected:\n"); - push(@$outputArray, " static const unsigned StructureFlags = JSC::OverridesGetOwnPropertySlot | JSC::ImplementsHasInstance | DOMConstructorObject::StructureFlags;\n"); - - if (IsConstructable($interface) && !$interface->extendedAttributes->{"NamedConstructor"}) { - push(@$outputArray, " static JSC::EncodedJSValue JSC_HOST_CALL construct${className}(JSC::ExecState*);\n"); - - if (!HasCustomConstructor($interface)) { - my @constructors = @{$interface->constructors}; - if (@constructors > 1) { - foreach my $constructor (@constructors) { - my $overloadedIndex = "" . $constructor->{overloadedIndex}; - push(@$outputArray, " static JSC::EncodedJSValue JSC_HOST_CALL construct${className}${overloadedIndex}(JSC::ExecState*);\n"); - } - } + if (PrototypeHasStaticPropertyTable($interface)) { + if (IsGlobalOrPrimaryGlobalInterface($interface)) { + $structureFlags{"JSC::HasStaticPropertyTable"} = 1; + } else { + push(@$outputArray, "\n"); + push(@$outputArray, " void finishCreation(JSC::VM&);\n"); } + } - my $conditionalString = $codeGenerator->GenerateConstructorConditionalString($interface); - push(@$outputArray, "#if $conditionalString\n") if $conditionalString; - push(@$outputArray, " static JSC::ConstructType getConstructData(JSC::JSCell*, JSC::ConstructData&);\n"); - push(@$outputArray, "#endif // $conditionalString\n") if $conditionalString; + if ($interface->extendedAttributes->{JSCustomNamedGetterOnPrototype}) { + push(@$outputArray, "\n"); + push(@$outputArray, " static bool put(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&);\n"); + push(@$outputArray, " bool putDelegate(JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&, bool& putResult);\n"); } - push(@$outputArray, "};\n\n"); - if ($codeGenerator->IsConstructorTemplate($interface, "Event")) { - push(@$outputArray, "bool fill${interfaceName}Init(${interfaceName}Init&, JSDictionary&);\n\n"); + # Custom defineOwnProperty function + if ($interface->extendedAttributes->{JSCustomDefineOwnPropertyOnPrototype}) { + push(@$outputArray, "\n"); + push(@$outputArray, " static bool defineOwnProperty(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, const JSC::PropertyDescriptor&, bool shouldThrow);\n"); } - if ($interface->extendedAttributes->{"NamedConstructor"}) { - push(@$outputArray, <<END); -class JS${interfaceName}NamedConstructor : public DOMConstructorWithDocument { -public: - typedef DOMConstructorWithDocument Base; + $structureFlags{"JSC::IsImmutablePrototypeExoticObject"} = 1 if $interface->extendedAttributes->{IsImmutablePrototypeExoticObjectOnPrototype}; - static JS${interfaceName}NamedConstructor* create(JSC::VM& vm, JSC::Structure* structure, JSDOMGlobalObject* globalObject) - { - JS${interfaceName}NamedConstructor* constructor = new (NotNull, JSC::allocateCell<JS${interfaceName}NamedConstructor>(vm.heap)) JS${interfaceName}NamedConstructor(structure, globalObject); - constructor->finishCreation(vm, globalObject); - return constructor; + # structure flags + if (%structureFlags) { + push(@$outputArray, "public:\n"); + push(@$outputArray, " static const unsigned StructureFlags = "); + foreach my $structureFlag (sort (keys %structureFlags)) { + push(@$outputArray, $structureFlag . " | "); + } + push(@$outputArray, "Base::StructureFlags;\n"); } - static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) - { - return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); - } + push(@$outputArray, "};\n\n"); +} + +sub GetConstructorTemplateClassName +{ + my $interface = shift; + return "JSDOMConstructorNotConstructable" if $interface->extendedAttributes->{NamedConstructor}; + return "JSDOMConstructorNotConstructable" unless IsConstructable($interface); + return "JSDOMBuiltinConstructor" if IsJSBuiltinConstructor($interface); + return "JSDOMConstructor"; +} - DECLARE_INFO; +sub GenerateConstructorDeclaration +{ + my ($outputArray, $className, $interface) = @_; -private: - JS${interfaceName}NamedConstructor(JSC::Structure*, JSDOMGlobalObject*); - static JSC::EncodedJSValue JSC_HOST_CALL constructJS${interfaceName}(JSC::ExecState*); - static JSC::ConstructType getConstructData(JSC::JSCell*, JSC::ConstructData&); - void finishCreation(JSC::VM&, JSDOMGlobalObject*); -}; + my $interfaceName = $interface->type->name; + my $constructorClassName = "${className}Constructor"; + my $templateClassName = GetConstructorTemplateClassName($interface); -END - } + AddToImplIncludes("${templateClassName}.h"); + AddToImplIncludes("JSDOMNamedConstructor.h") if $interface->extendedAttributes->{NamedConstructor}; + + push(@$outputArray, "using $constructorClassName = $templateClassName<$className>;\n"); + push(@$outputArray, "using JS${interfaceName}NamedConstructor = JSDOMNamedConstructor<$className>;\n") if $interface->extendedAttributes->{NamedConstructor}; + push(@$outputArray, "\n"); } sub GenerateConstructorDefinitions { - my $outputArray = shift; - my $className = shift; - my $protoClassName = shift; - my $interfaceName = shift; - my $visibleInterfaceName = shift; - my $interface = shift; - my $generatingNamedConstructor = shift; + my ($outputArray, $className, $protoClassName, $visibleInterfaceName, $interface, $generatingNamedConstructor) = @_; if (IsConstructable($interface)) { my @constructors = @{$interface->constructors}; if (@constructors > 1) { foreach my $constructor (@constructors) { - GenerateConstructorDefinition($outputArray, $className, $protoClassName, $interfaceName, $visibleInterfaceName, $interface, $generatingNamedConstructor, $constructor); + GenerateConstructorDefinition($outputArray, $className, $protoClassName, $visibleInterfaceName, $interface, $generatingNamedConstructor, $constructor); } - GenerateOverloadedConstructorDefinition($outputArray, $className, $interface); + GenerateOverloadedFunctionOrConstructor(@{$interface->constructors}[0], $interface, 1); } elsif (@constructors == 1) { - GenerateConstructorDefinition($outputArray, $className, $protoClassName, $interfaceName, $visibleInterfaceName, $interface, $generatingNamedConstructor, $constructors[0]); + GenerateConstructorDefinition($outputArray, $className, $protoClassName, $visibleInterfaceName, $interface, $generatingNamedConstructor, $constructors[0]); } else { - GenerateConstructorDefinition($outputArray, $className, $protoClassName, $interfaceName, $visibleInterfaceName, $interface, $generatingNamedConstructor); + GenerateConstructorDefinition($outputArray, $className, $protoClassName, $visibleInterfaceName, $interface, $generatingNamedConstructor); } } - GenerateConstructorHelperMethods($outputArray, $className, $protoClassName, $interfaceName, $visibleInterfaceName, $interface, $generatingNamedConstructor); -} - -sub GenerateOverloadedConstructorDefinition -{ - my $outputArray = shift; - my $className = shift; - my $interface = shift; - - my $functionName = "${className}Constructor::construct${className}"; - push(@$outputArray, <<END); -EncodedJSValue JSC_HOST_CALL ${functionName}(ExecState* exec) -{ - size_t argsCount = exec->argumentCount(); -END - - my %fetchedArguments = (); - my $leastNumMandatoryParams = 255; - - my @constructors = @{$interface->constructors}; - foreach my $overload (@constructors) { - my ($numMandatoryParams, $parametersCheck, @neededArguments) = GenerateFunctionParametersCheck($overload); - $leastNumMandatoryParams = $numMandatoryParams if ($numMandatoryParams < $leastNumMandatoryParams); - - foreach my $parameterIndex (@neededArguments) { - next if exists $fetchedArguments{$parameterIndex}; - push(@$outputArray, " JSValue arg$parameterIndex(exec->argument($parameterIndex));\n"); - $fetchedArguments{$parameterIndex} = 1; - } - - push(@$outputArray, " if ($parametersCheck)\n"); - push(@$outputArray, " return ${functionName}$overload->{overloadedIndex}(exec);\n"); - } - - if ($leastNumMandatoryParams >= 1) { - push(@$outputArray, " if (argsCount < $leastNumMandatoryParams)\n"); - push(@$outputArray, " return throwVMError(exec, createNotEnoughArgumentsError(exec));\n"); - } - push(@$outputArray, <<END); - return throwVMTypeError(exec); -} - -END + GenerateConstructorHelperMethods($outputArray, $className, $protoClassName, $visibleInterfaceName, $interface, $generatingNamedConstructor); } sub GenerateConstructorDefinition { - my $outputArray = shift; - my $className = shift; - my $protoClassName = shift; - my $interfaceName = shift; - my $visibleInterfaceName = shift; - my $interface = shift; - my $generatingNamedConstructor = shift; - my $function = shift; + my ($outputArray, $className, $protoClassName, $visibleInterfaceName, $interface, $generatingNamedConstructor, $function) = @_; + return if IsJSBuiltinConstructor($interface); + + my $interfaceName = $interface->type->name; my $constructorClassName = $generatingNamedConstructor ? "${className}NamedConstructor" : "${className}Constructor"; if (IsConstructable($interface)) { - if ($codeGenerator->IsConstructorTemplate($interface, "Event")) { - $implIncludes{"JSDictionary.h"} = 1; - $implIncludes{"<runtime/Error.h>"} = 1; - - push(@$outputArray, <<END); -EncodedJSValue JSC_HOST_CALL ${constructorClassName}::construct${className}(ExecState* exec) -{ - ${constructorClassName}* jsConstructor = jsCast<${constructorClassName}*>(exec->callee()); - - ScriptExecutionContext* executionContext = jsConstructor->scriptExecutionContext(); - if (!executionContext) - return throwVMError(exec, createReferenceError(exec, "Constructor associated execution context is unavailable")); - - AtomicString eventType = exec->argument(0).toString(exec)->value(exec); - if (exec->hadException()) - return JSValue::encode(jsUndefined()); - - ${interfaceName}Init eventInit; - - JSValue initializerValue = exec->argument(1); - if (!initializerValue.isUndefinedOrNull()) { - // Given the above test, this will always yield an object. - JSObject* initializerObject = initializerValue.toObject(exec); - - // Create the dictionary wrapper from the initializer object. - JSDictionary dictionary(exec, initializerObject); - - // Attempt to fill in the EventInit. - if (!fill${interfaceName}Init(eventInit, dictionary)) - return JSValue::encode(jsUndefined()); - } - - RefPtr<${interfaceName}> event = ${interfaceName}::create(eventType, eventInit); - return JSValue::encode(toJS(exec, jsConstructor->globalObject(), event.get())); -} - -bool fill${interfaceName}Init(${interfaceName}Init& eventInit, JSDictionary& dictionary) -{ -END - - if ($interface->parent) { - my $interfaceBase = $interface->parent; - push(@implContent, <<END); - if (!fill${interfaceBase}Init(eventInit, dictionary)) - return false; - -END - } - - for (my $index = 0; $index < @{$interface->attributes}; $index++) { - my $attribute = @{$interface->attributes}[$index]; - if ($attribute->signature->extendedAttributes->{"InitializedByEventConstructor"}) { - my $attributeName = $attribute->signature->name; - my $attributeImplName = $attribute->signature->extendedAttributes->{"ImplementedAs"} || $attributeName; - push(@implContent, <<END); - if (!dictionary.tryGetProperty("${attributeName}", eventInit.${attributeImplName})) - return false; -END - } - } - - push(@$outputArray, <<END); - return true; -} - -END - } elsif (!HasCustomConstructor($interface) && (!$interface->extendedAttributes->{"NamedConstructor"} || $generatingNamedConstructor)) { - my $overloadedIndexString = ""; - if ($function->{overloadedIndex} && $function->{overloadedIndex} > 0) { - $overloadedIndexString .= $function->{overloadedIndex}; + if ($interface->extendedAttributes->{CustomConstructor}) { + push(@$outputArray, "template<> JSC::EncodedJSValue JSC_HOST_CALL ${constructorClassName}::construct(JSC::ExecState* exec)\n"); + push(@$outputArray, "{\n"); + push(@$outputArray, " ASSERT(exec);\n"); + push(@$outputArray, " return construct${className}(*exec);\n"); + push(@$outputArray, "}\n\n"); + } elsif (!HasCustomConstructor($interface) && (!$interface->extendedAttributes->{NamedConstructor} || $generatingNamedConstructor)) { + my $isOverloaded = $function->{overloads} && @{$function->{overloads}} > 1; + if ($isOverloaded) { + push(@$outputArray, "static inline EncodedJSValue construct${className}$function->{overloadIndex}(ExecState* state)\n"); + } else { + push(@$outputArray, "template<> EncodedJSValue JSC_HOST_CALL ${constructorClassName}::construct(ExecState* state)\n"); } - push(@$outputArray, "EncodedJSValue JSC_HOST_CALL ${constructorClassName}::construct${className}${overloadedIndexString}(ExecState* exec)\n"); push(@$outputArray, "{\n"); - push(@$outputArray, " ${constructorClassName}* castedThis = jsCast<${constructorClassName}*>(exec->callee());\n"); + push(@$outputArray, " VM& vm = state->vm();\n"); + push(@$outputArray, " auto throwScope = DECLARE_THROW_SCOPE(vm);\n"); + push(@$outputArray, " UNUSED_PARAM(throwScope);\n"); + push(@$outputArray, " auto* castedThis = jsCast<${constructorClassName}*>(state->jsCallee());\n"); + push(@$outputArray, " ASSERT(castedThis);\n"); my @constructorArgList; @@ -4167,71 +6095,91 @@ END GenerateArgumentsCountCheck($outputArray, $function, $interface); - if ($function->signature->extendedAttributes->{"RaisesException"} || $interface->extendedAttributes->{"ConstructorRaisesException"}) { - $implIncludes{"ExceptionCode.h"} = 1; - push(@$outputArray, " ExceptionCode ec = 0;\n"); - } - # FIXME: For now, we do not support SVG constructors. # FIXME: Currently [Constructor(...)] does not yet support optional arguments without [Default=...] - my $numParameters = @{$function->parameters}; - my ($dummy, $paramIndex) = GenerateParametersCheck($outputArray, $function, $interface, $numParameters, $interfaceName, "constructorCallback", undef, undef, undef); + my ($dummy, $paramIndex) = GenerateParametersCheck($outputArray, $function, $interface, "constructorCallback"); - if ($codeGenerator->ExtendedAttributeContains($interface->extendedAttributes->{"ConstructorCallWith"}, "ScriptExecutionContext") ) { + push(@constructorArgList, "*state") if $codeGenerator->ExtendedAttributeContains($interface->extendedAttributes->{ConstructorCallWith}, "ScriptState");; + + if ($codeGenerator->ExtendedAttributeContains($interface->extendedAttributes->{ConstructorCallWith}, "ScriptExecutionContext")) { push(@constructorArgList, "*context"); push(@$outputArray, " ScriptExecutionContext* context = castedThis->scriptExecutionContext();\n"); - push(@$outputArray, " if (!context)\n"); - push(@$outputArray, " return throwVMError(exec, createReferenceError(exec, \"${interfaceName} constructor associated document is unavailable\"));\n"); + push(@$outputArray, " if (UNLIKELY(!context))\n"); + push(@$outputArray, " return throwConstructorScriptExecutionContextUnavailableError(*state, throwScope, \"${visibleInterfaceName}\");\n"); } - if ($generatingNamedConstructor) { - push(@constructorArgList, "*castedThis->document()"); + + if ($codeGenerator->ExtendedAttributeContains($interface->extendedAttributes->{ConstructorCallWith}, "Document")) { + $implIncludes{"Document.h"} = 1; + push(@constructorArgList, "document"); + push(@$outputArray, " ScriptExecutionContext* context = castedThis->scriptExecutionContext();\n"); + push(@$outputArray, " if (UNLIKELY(!context))\n"); + push(@$outputArray, " return throwConstructorScriptExecutionContextUnavailableError(*state, throwScope, \"${visibleInterfaceName}\");\n"); + push(@$outputArray, " ASSERT(context->isDocument());\n"); + push(@$outputArray, " auto& document = downcast<Document>(*context);\n"); } + push(@constructorArgList, "*castedThis->document()") if $generatingNamedConstructor; + my $index = 0; - foreach my $parameter (@{$function->parameters}) { + foreach my $argument (@{$function->arguments}) { last if $index eq $paramIndex; - push(@constructorArgList, $parameter->name); + + push(@constructorArgList, PassArgumentExpression($argument->name, $argument)); + $index++; } - if ($interface->extendedAttributes->{"ConstructorRaisesException"}) { - push(@constructorArgList, "ec"); - } my $constructorArg = join(", ", @constructorArgList); if ($generatingNamedConstructor) { - push(@$outputArray, " RefPtr<${interfaceName}> object = ${interfaceName}::createForJSConstructor(${constructorArg});\n"); + push(@$outputArray, " auto object = ${interfaceName}::createForJSConstructor(${constructorArg});\n"); } else { - push(@$outputArray, " RefPtr<${interfaceName}> object = ${interfaceName}::create(${constructorArg});\n"); + push(@$outputArray, " auto object = ${interfaceName}::create(${constructorArg});\n"); } - if ($interface->extendedAttributes->{"ConstructorRaisesException"}) { - push(@$outputArray, " if (ec) {\n"); - push(@$outputArray, " setDOMException(exec, ec);\n"); - push(@$outputArray, " return JSValue::encode(JSValue());\n"); - push(@$outputArray, " }\n"); - } + push(@$outputArray, " RETURN_IF_EXCEPTION(throwScope, encodedJSValue());\n") if $codeGenerator->ExtendedAttributeContains($interface->extendedAttributes->{ConstructorCallWith}, "ScriptState"); + + my $IDLType = GetIDLType($interface, $interface->type); - push(@$outputArray, " return JSValue::encode(asObject(toJS(exec, castedThis->globalObject(), object.get())));\n"); + my @constructionConversionArguments = (); + push(@constructionConversionArguments, "*state"); + push(@constructionConversionArguments, "*castedThis->globalObject()"); + push(@constructionConversionArguments, "throwScope") if $interface->extendedAttributes->{ConstructorMayThrowException}; + push(@constructionConversionArguments, "WTFMove(object)"); + + push(@$outputArray, " return JSValue::encode(toJSNewlyCreated<${IDLType}>(" . join(", ", @constructionConversionArguments) . "));\n"); push(@$outputArray, "}\n\n"); } } } -sub GenerateConstructorHelperMethods +sub ConstructorHasProperties { - my $outputArray = shift; - my $className = shift; - my $protoClassName = shift; - my $interfaceName = shift; - my $visibleInterfaceName = shift; my $interface = shift; - my $generatingNamedConstructor = shift; + + foreach my $constant (@{$interface->constants}) { + return 1; + } + + foreach my $attribute (@{$interface->attributes}) { + next unless ($attribute->isStatic); + return 1; + } + + foreach my $function (@{$interface->functions}) { + next unless ($function->isStatic); + return 1; + } + + return 0; +} + +sub GenerateConstructorHelperMethods +{ + my ($outputArray, $className, $protoClassName, $visibleInterfaceName, $interface, $generatingNamedConstructor) = @_; my $constructorClassName = $generatingNamedConstructor ? "${className}NamedConstructor" : "${className}Constructor"; my $leastConstructorLength = 0; - if ($codeGenerator->IsConstructorTemplate($interface, "Event")) { - $leastConstructorLength = 1; - } elsif ($interface->extendedAttributes->{"Constructor"} || $interface->extendedAttributes->{"CustomConstructor"}) { + if ($interface->extendedAttributes->{Constructor} || $interface->extendedAttributes->{CustomConstructor}) { my @constructors = @{$interface->constructors}; my @customConstructors = @{$interface->customConstructors}; $leastConstructorLength = 255; @@ -4243,108 +6191,192 @@ sub GenerateConstructorHelperMethods $leastConstructorLength = 0; } - if ($generatingNamedConstructor) { - push(@$outputArray, "const ClassInfo ${constructorClassName}::s_info = { \"${visibleInterfaceName}Constructor\", &Base::s_info, 0, 0, CREATE_METHOD_TABLE($constructorClassName) };\n\n"); - push(@$outputArray, "${constructorClassName}::${constructorClassName}(Structure* structure, JSDOMGlobalObject* globalObject)\n"); - push(@$outputArray, " : DOMConstructorWithDocument(structure, globalObject)\n"); - push(@$outputArray, "{\n"); - push(@$outputArray, "}\n\n"); + # If the interface has a parent interface which does not have [NoInterfaceObject], then use its interface object as prototype, + # otherwise use FunctionPrototype: http://heycam.github.io/webidl/#interface-object + push(@$outputArray, "template<> JSValue ${constructorClassName}::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject)\n"); + push(@$outputArray, "{\n"); + # FIXME: IDL does not allow an interface without [NoInterfaceObject] to inherit one that is marked as [NoInterfaceObject] + # so we should be able to use our parent's interface object no matter what. However, some of our IDL files (e.g. CanvasRenderingContext2D) + # are not valid so we need this check for now. + if ($interface->parentType && !$codeGenerator->GetInterfaceExtendedAttributesFromName($interface->parentType->name)->{NoInterfaceObject}) { + my $parentClassName = "JS" . $interface->parentType->name; + push(@$outputArray, " return ${parentClassName}::getConstructor(vm, &globalObject);\n"); } else { - if ($interface->extendedAttributes->{"JSNoStaticTables"}) { - push(@$outputArray, "static const HashTable& get${constructorClassName}Table(VM& vm)\n"); - push(@$outputArray, "{\n"); - push(@$outputArray, " return getHashTableForGlobalData(vm, ${constructorClassName}Table);\n"); - push(@$outputArray, "}\n\n"); - push(@$outputArray, "const ClassInfo ${constructorClassName}::s_info = { \"${visibleInterfaceName}Constructor\", &Base::s_info, 0, get${constructorClassName}Table, CREATE_METHOD_TABLE($constructorClassName) };\n\n"); - } else { - push(@$outputArray, "const ClassInfo ${constructorClassName}::s_info = { \"${visibleInterfaceName}Constructor\", &Base::s_info, &${constructorClassName}Table, 0, CREATE_METHOD_TABLE($constructorClassName) };\n\n"); - } - - push(@$outputArray, "${constructorClassName}::${constructorClassName}(Structure* structure, JSDOMGlobalObject* globalObject)\n"); - push(@$outputArray, " : DOMConstructorObject(structure, globalObject)\n"); - push(@$outputArray, "{\n}\n\n"); + AddToImplIncludes("<runtime/FunctionPrototype.h>"); + push(@$outputArray, " UNUSED_PARAM(vm);\n"); + push(@$outputArray, " return globalObject.functionPrototype();\n"); } + push(@$outputArray, "}\n\n"); - push(@$outputArray, "void ${constructorClassName}::finishCreation(VM& vm, JSDOMGlobalObject* globalObject)\n"); + + push(@$outputArray, "template<> void ${constructorClassName}::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject)\n"); push(@$outputArray, "{\n"); - if (IsDOMGlobalObject($interface)) { - push(@$outputArray, " Base::finishCreation(vm);\n"); - push(@$outputArray, " ASSERT(inherits(info()));\n"); - push(@$outputArray, " putDirect(vm, vm.propertyNames->prototype, globalObject->prototype(), DontDelete | ReadOnly);\n"); - } elsif ($generatingNamedConstructor) { - push(@$outputArray, " Base::finishCreation(globalObject);\n"); - push(@$outputArray, " ASSERT(inherits(info()));\n"); - push(@$outputArray, " putDirect(vm, vm.propertyNames->prototype, ${className}Prototype::self(vm, globalObject), None);\n"); + + # There must exist an interface prototype object for every non-callback interface defined, regardless + # of whether the interface was declared with the [NoInterfaceObject] extended attribute. + # https://heycam.github.io/webidl/#interface-prototype-object + if (ShouldUseGlobalObjectPrototype($interface)) { + push(@$outputArray, " putDirect(vm, vm.propertyNames->prototype, globalObject.getPrototypeDirect(), DontDelete | ReadOnly | DontEnum);\n"); + } elsif ($interface->isCallback) { + push(@$outputArray, " UNUSED_PARAM(globalObject);\n"); } else { - push(@$outputArray, " Base::finishCreation(vm);\n"); - push(@$outputArray, " ASSERT(inherits(info()));\n"); - push(@$outputArray, " putDirect(vm, vm.propertyNames->prototype, ${protoClassName}::self(vm, globalObject), DontDelete | ReadOnly);\n"); + push(@$outputArray, " putDirect(vm, vm.propertyNames->prototype, ${className}::prototype(vm, &globalObject), DontDelete | ReadOnly | DontEnum);\n"); } - push(@$outputArray, " putDirect(vm, vm.propertyNames->length, jsNumber(${leastConstructorLength}), ReadOnly | DontDelete | DontEnum);\n") if defined $leastConstructorLength; - push(@$outputArray, "}\n\n"); - if (!$generatingNamedConstructor) { - my $hasStaticFunctions = 0; - foreach my $function (@{$interface->functions}) { - if ($function->isStatic) { - $hasStaticFunctions = 1; - last; - } - } + push(@$outputArray, " putDirect(vm, vm.propertyNames->name, jsNontrivialString(&vm, String(ASCIILiteral(\"$visibleInterfaceName\"))), ReadOnly | DontEnum);\n"); + push(@$outputArray, " putDirect(vm, vm.propertyNames->length, jsNumber(${leastConstructorLength}), ReadOnly | DontEnum);\n") if defined $leastConstructorLength; + push(@$outputArray, " reifyStaticProperties(vm, ${className}ConstructorTableValues, *this);\n") if ConstructorHasProperties($interface); - my $kind = $hasStaticFunctions ? "Property" : "Value"; + push(@$outputArray, "}\n\n"); - push(@$outputArray, "bool ${constructorClassName}::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)\n"); + if (IsJSBuiltinConstructor($interface)) { + push(@$outputArray, "template<> FunctionExecutable* ${constructorClassName}::initializeExecutable(VM& vm)\n"); push(@$outputArray, "{\n"); - push(@$outputArray, " return getStatic${kind}Slot<${constructorClassName}, JSDOMWrapper>(exec, " . constructorHashTableAccessor($interface->extendedAttributes->{"JSNoStaticTables"}, $constructorClassName) . ", jsCast<${constructorClassName}*>(object), propertyName, slot);\n"); - push(@$outputArray, "}\n\n"); - } - - if (IsConstructable($interface)) { - if (!$interface->extendedAttributes->{"NamedConstructor"} || $generatingNamedConstructor) { - my $conditionalString = $codeGenerator->GenerateConstructorConditionalString($interface); - push(@$outputArray, "#if $conditionalString\n") if $conditionalString; - push(@$outputArray, "ConstructType ${constructorClassName}::getConstructData(JSCell*, ConstructData& constructData)\n"); - push(@$outputArray, "{\n"); - push(@$outputArray, " constructData.native.function = construct${className};\n"); - push(@$outputArray, " return ConstructTypeHost;\n"); - push(@$outputArray, "}\n"); - push(@$outputArray, "#endif // $conditionalString\n") if $conditionalString; - push(@$outputArray, "\n"); - } + push(@$outputArray, " return " . GetJSBuiltinFunctionNameFromString($interface->type->name, "initialize" . $interface->type->name) . "(vm);\n"); + push(@$outputArray, "}\n"); + push(@$outputArray, "\n"); } + push(@$outputArray, "template<> const ClassInfo ${constructorClassName}::s_info = { \"${visibleInterfaceName}\", &Base::s_info, 0, CREATE_METHOD_TABLE($constructorClassName) };\n\n"); } sub HasCustomConstructor { my $interface = shift; - - return $interface->extendedAttributes->{"CustomConstructor"}; + return $interface->extendedAttributes->{CustomConstructor}; } sub HasCustomGetter { - my $attrExt = shift; - return $attrExt->{"Custom"} || $attrExt->{"CustomGetter"} ; + my $extendedAttributes = shift; + return $extendedAttributes->{Custom} || $extendedAttributes->{CustomGetter} ; } sub HasCustomSetter { - my $attrExt = shift; - return $attrExt->{"Custom"} || $attrExt->{"CustomSetter"}; + my $extendedAttributes = shift; + return $extendedAttributes->{Custom} || $extendedAttributes->{CustomSetter}; } sub HasCustomMethod { - my $attrExt = shift; - return $attrExt->{"Custom"}; + my $extendedAttributes = shift; + return $extendedAttributes->{Custom}; +} + +sub NeedsConstructorProperty +{ + my $interface = shift; + return !$interface->extendedAttributes->{NoInterfaceObject} || $interface->extendedAttributes->{CustomConstructor}; +} + +sub IsReturningPromise +{ + my $function = shift; + return $function->type && $function->type->name eq "Promise"; } sub IsConstructable { my $interface = shift; + return HasCustomConstructor($interface) + || $interface->extendedAttributes->{Constructor} + || $interface->extendedAttributes->{NamedConstructor} + || $interface->extendedAttributes->{JSBuiltinConstructor}; +} + +sub HeaderNeedsPrototypeDeclaration +{ + my $interface = shift; + return IsDOMGlobalObject($interface) || $interface->extendedAttributes->{JSCustomNamedGetterOnPrototype} || $interface->extendedAttributes->{JSCustomDefineOwnPropertyOnPrototype}; +} + +sub IsUnforgeable +{ + my $interface = shift; + my $property = shift; + return $property->extendedAttributes->{Unforgeable} || $interface->extendedAttributes->{Unforgeable}; +} + +sub ComputeFunctionSpecial +{ + my $interface = shift; + my $function = shift; + + my @specials = (); + push(@specials, ("DontDelete", "ReadOnly")) if IsUnforgeable($interface, $function); + push(@specials, "DontEnum") if $function->extendedAttributes->{NotEnumerable}; + if (IsJSBuiltin($interface, $function)) { + push(@specials, "JSC::Builtin"); + } + else { + push(@specials, "JSC::Function"); + } + if ($function->extendedAttributes->{"DOMJIT"}) { + push(@specials, "DOMJITFunction") if $function->extendedAttributes->{DOMJIT}; + } + return (@specials > 0) ? join(" | ", @specials) : "0"; +} + +sub IsJSBuiltin +{ + my ($interface, $object) = @_; - return HasCustomConstructor($interface) || $interface->extendedAttributes->{"Constructor"} || $interface->extendedAttributes->{"NamedConstructor"} || $interface->extendedAttributes->{"ConstructorTemplate"}; + return 0 if $object->extendedAttributes->{Custom}; + return 0 if $object->extendedAttributes->{CustomGetter}; + return 0 if $object->extendedAttributes->{CustomSetter}; + + return 1 if $object->extendedAttributes->{JSBuiltin}; + return 1 if $interface->extendedAttributes->{JSBuiltin}; + + return 0; +} + +sub IsJSBuiltinConstructor +{ + my ($interface) = @_; + + return 0 if $interface->extendedAttributes->{CustomConstructor}; + return 1 if $interface->extendedAttributes->{JSBuiltin}; + return 1 if $interface->extendedAttributes->{JSBuiltinConstructor}; + return 0; +} + +sub GetJSBuiltinFunctionName +{ + my ($className, $function) = @_; + my $scopeName = $function->extendedAttributes->{ImplementedBy}; + $scopeName = substr $className, 2 unless $scopeName; + return GetJSBuiltinFunctionNameFromString($scopeName, $function->name); +} + +sub GetJSBuiltinFunctionNameFromString +{ + my ($scopeName, $functionName) = @_; + return $codeGenerator->WK_lcfirst($scopeName) . $codeGenerator->WK_ucfirst($functionName) . "CodeGenerator"; +} + +sub GetJSBuiltinScopeName +{ + my ($interface, $object) = @_; + return $object->extendedAttributes->{ImplementedBy} || $interface->type->name; +} + +sub AddJSBuiltinIncludesIfNeeded() +{ + my $interface = shift; + + if ($interface->extendedAttributes->{JSBuiltin} || $interface->extendedAttributes->{JSBuiltinConstructor}) { + AddToImplIncludes($interface->type->name . "Builtins.h"); + return; + } + + foreach my $function (@{$interface->functions}) { + AddToImplIncludes(GetJSBuiltinScopeName($interface, $function) . "Builtins.h", $function->extendedAttributes->{Conditional}) if IsJSBuiltin($interface, $function); + } + + foreach my $attribute (@{$interface->attributes}) { + AddToImplIncludes(GetJSBuiltinScopeName($interface, $attribute) . "Builtins.h", $attribute->extendedAttributes->{Conditional}) if IsJSBuiltin($interface, $attribute); + } } 1; diff --git a/Source/WebCore/bindings/scripts/IDLAttributes.txt b/Source/WebCore/bindings/scripts/IDLAttributes.txt index 85ead715d..e872d88d9 100644 --- a/Source/WebCore/bindings/scripts/IDLAttributes.txt +++ b/Source/WebCore/bindings/scripts/IDLAttributes.txt @@ -19,54 +19,66 @@ # ActiveDOMObject -CPPPureInterface +AppleCopyright +AtomicString +CEReactions CachedAttribute CallbackNeedsOperatorEqual -CallWith=ScriptExecutionContext|ScriptState|ScriptArguments|CallStack +CallWith=Document|ScriptExecutionContext|ScriptState|ScriptArguments|CallStack|GlobalObject|ActiveWindow|FirstWindow|CallerDocument|CallerWindow CheckSecurity CheckSecurityForNode Clamp Conditional=* +ConstantsScope=* Constructor -ConstructorCallWith=ScriptExecutionContext -ConstructorConditional=* -ConstructorRaisesException -ConstructorTemplate=Event|TypedArray +ConstructorCallWith=Document|ScriptExecutionContext|ScriptState +ConstructorMayThrowException Custom CustomCall CustomConstructor CustomDeleteProperty CustomEnumerateProperty CustomGetOwnPropertySlot +CustomGetPrototype CustomGetter CustomIndexedSetter CustomIsReachable CustomNamedGetter CustomNamedSetter +CustomPreventExtensions +CustomProxyToJSObject CustomPutFunction -CustomReturn +CustomSetPrototype CustomSetter CustomToJSObject -Default=NullString|Undefined -Deletable +CustomToStringName +DOMJIT=|ReadDOM|Getter DoNotCheckConstants DoNotCheckSecurity DoNotCheckSecurityOnGetter DoNotCheckSecurityOnSetter +DocumentEventHandler EnabledAtRuntime=* EnabledBySetting=* +EnabledForWorld=* EnforceRange -EventTarget -GenerateIsReachable=|Impl|ImplWebGLRenderingContext|ImplDocument|ImplElementRoot|ImplFrame|ImplOwnerNodeRoot -GetterRaisesException -GlobalContext=DOMWindow|WorkerGlobalScope|SharedWorkerGlobalScope|DedicatedWorkerGlobalScope +ExportMacro=WEBCORE_EXPORT|WEBCORE_TESTSUPPORT_EXPORT +ExportToWrappedFunction +Exposed=* +ForwardDeclareInHeader +GenerateIsReachable=|Impl|ImplWebGLRenderingContext|ImplDocument|ImplElementRoot|ImplFrame|ImplOwnerNodeRoot|ImplScriptExecutionContext +GetterMayThrowException +Global=* Immutable -ImplementationLacksVTable -ImplementationNamespace=* ImplementedAs=* -InitializedByEventConstructor +ImplementationLacksVTable +ImplicitThis InterfaceName=* -IsIndex +IsImmutablePrototypeExoticObject +IsImmutablePrototypeExoticObjectOnPrototype +IsWeakCallback +JSBuiltin +JSBuiltinConstructor JSCustomDefineOwnProperty JSCustomDefineOwnPropertyOnPrototype JSCustomFinalize @@ -79,34 +91,33 @@ JSCustomToNativeObject JSGenerateToJSObject JSGenerateToNativeObject JSLegacyParent=* -JSNoStaticTables -JSWindowEventListener +LegacyUnenumerableNamedProperties +LenientThis MasqueradesAsUndefined +MayThrowException NamedConstructor=* NewImpurePropertyFiresWatchpoints +NewObject NoInterfaceObject NotEnumerable -NotDeletable -ObjCCustomImplementation -ObjCLegacyUnnamedParameters -ObjCPolymorphic -ObjCProtocol -OperationsNotDeletable +OverrideBuiltins +OverrideIDLType=* PassContext -RaisesException +PrimaryGlobal +PrivateIdentifier +PublicIdentifier +PutForwards=* Reflect=* Replaceable -ReplaceableConstructor -ReturnNewObject -SetterRaisesException +ReportExtraMemoryCost +ReportExternalMemoryCost +RequiresExistingAtomicString +SetterCallWith=ScriptExecutionContext|ScriptState|ScriptArguments|CallStack|GlobalObject|ActiveWindow|FirstWindow +SetterMayThrowException SkipVTableValidation -StrictTypeChecking SuppressToJSObject -TreatNullAs=NullString -TreatReturnedNullStringAs=Null|Undefined -TreatUndefinedAs=NullString -TypedArray=* +TreatNullAs=EmptyString URL - -# PLATFORM(IOS) -AppleCopyright +Unforgeable +Unscopable +WindowEventHandler diff --git a/Source/WebCore/bindings/scripts/IDLParser.pm b/Source/WebCore/bindings/scripts/IDLParser.pm index 319f62bae..2107b94a5 100644 --- a/Source/WebCore/bindings/scripts/IDLParser.pm +++ b/Source/WebCore/bindings/scripts/IDLParser.pm @@ -23,6 +23,9 @@ package IDLParser; use strict; +use Carp qw<longmess>; +use Data::Dumper; + use preprocessor; use Class::Struct; @@ -34,68 +37,132 @@ use constant OtherToken => 4; use constant EmptyToken => 5; # Used to represent a parsed IDL document -struct( idlDocument => { - interfaces => '@', # All parsed interfaces - enumerations => '@', # All parsed enumerations - fileName => '$', # file name +struct( IDLDocument => { + interfaces => '@', # List of 'IDLInterface' + enumerations => '@', # List of 'IDLEnum' + dictionaries => '@', # List of 'IDLDictionary' + callbackFunctions => '@', # List of 'IDLCallbackFunction' + fileName => '$', +}); + +# https://heycam.github.io/webidl/#idl-types +struct( IDLType => { + name => '$', # Type identifier + isNullable => '$', # Is the type Nullable (T?) + isUnion => '$', # Is the type a union (T or U) + subtypes => '@', # Array of subtypes, only valid if isUnion or sequence }); # Used to represent 'interface' blocks -struct( domInterface => { - name => '$', # Class identifier - parent => '$', # Parent class identifier - parents => '@', # Parent class identifiers (Kept for compatibility with ObjC bindings) - constants => '@', # List of 'domConstant' - functions => '@', # List of 'domFunction' - anonymousFunctions => '@', # List of 'domFunction' - attributes => '@', # List of 'domAttribute' - extendedAttributes => '$', # Extended attributes - constructors => '@', # Constructors, list of 'domFunction' - customConstructors => '@', # Custom constructors, list of 'domFunction' +struct( IDLInterface => { + type => 'IDLType', + parentType => 'IDLType', + constants => '@', # List of 'IDLConstant' + functions => '@', # List of 'IDLOperation' + anonymousFunctions => '@', # List of 'IDLOperation' + attributes => '@', # List of 'IDLAttribute' + constructors => '@', # Constructors, list of 'IDLOperation' + customConstructors => '@', # Custom constructors, list of 'IDLOperation' isException => '$', # Used for exception interfaces isCallback => '$', # Used for callback interfaces isPartial => '$', # Used for partial interfaces + iterable => '$', # Used for iterable interfaces + serializable => '$', # Used for serializable interfaces + extendedAttributes => '$', }); -# Used to represent domInterface contents (name of method, signature) -struct( domFunction => { +# Used to represent an argument to a IDLOperation. +struct( IDLArgument => { + name => '$', + type => 'IDLType', + isVariadic => '$', + isOptional => '$', + default => '$', + extendedAttributes => '$', +}); + +# https://heycam.github.io/webidl/#idl-operations +struct( IDLOperation => { + name => '$', + type => 'IDLType', # Return type + arguments => '@', # List of 'IDLArgument' isStatic => '$', - signature => '$', # Return type/Object name/extended attributes - parameters => '@', # List of 'domSignature' + specials => '@', + extendedAttributes => '$', }); -# Used to represent domInterface contents (name of attribute, signature) -struct( domAttribute => { - type => '$', # Attribute type (including namespace) + +# https://heycam.github.io/webidl/#idl-attributes +struct( IDLAttribute => { + name => '$', + type => 'IDLType', isStatic => '$', + isStringifier => '$', isReadOnly => '$', - signature => '$', # Attribute signature + extendedAttributes => '$', +}); + +# https://heycam.github.io/webidl/#idl-iterable +struct( IDLIterable => { + isKeyValue => '$', + keyType => 'IDLType', + valueType => 'IDLType', + functions => '@', # Iterable functions (entries, keys, values, [Symbol.Iterator], forEach) + extendedAttributes => '$', +}); + +# https://heycam.github.io/webidl/#idl-serializers +struct( IDLSerializable => { + attributes => '@', # List of attributes to serialize + hasAttribute => '$', # serializer = { attribute } + hasInherit => '$', # serializer = { inherit } + hasGetter => '$', # serializer = { getter } + functions => '@', # toJSON function +}); + +# https://heycam.github.io/webidl/#idl-constants +struct( IDLConstant => { + name => '$', + type => 'IDLType', + value => '$', + extendedAttributes => '$', +}); + +# https://heycam.github.io/webidl/#idl-enums +struct( IDLEnum => { + name => '$', + type => 'IDLType', + values => '@', + extendedAttributes => '$', +}); + +# https://heycam.github.io/webidl/#dfn-dictionary-member +struct( IDLDictionaryMember => { + name => '$', + type => 'IDLType', + isRequired => '$', + default => '$', + extendedAttributes => '$', }); -# Used to represent a map of 'variable name' <-> 'variable type' -struct( domSignature => { - direction => '$', # Variable direction (in or out) - name => '$', # Variable name - type => '$', # Variable type - specials => '@', # Specials - extendedAttributes => '$', # Extended attributes - isNullable => '$', # Is variable type Nullable (T?) - isVariadic => '$', # Is variable variadic (long... numbers) - isOptional => '$', # Is variable optional (optional T) +# https://heycam.github.io/webidl/#idl-dictionaries +struct( IDLDictionary => { + type => 'IDLType', + parentType => 'IDLType', + members => '@', # List of 'IDLDictionaryMember' + extendedAttributes => '$', }); -# Used to represent string constants -struct( domConstant => { - name => '$', # DOM Constant identifier - type => '$', # Type of data - value => '$', # Constant value - extendedAttributes => '$', # Extended attributes +# https://heycam.github.io/webidl/#idl-callback-functions +struct( IDLCallbackFunction => { + type => '$', + operation => 'IDLOperation', + extendedAttributes => '$', }); -# Used to represent 'enum' definitions -struct( domEnum => { - name => '$', # Enumeration identifier - values => '@', # Enumeration values (list of unique strings) +# https://heycam.github.io/webidl/#idl-typedefs +struct( IDLTypedef => { + type => 'IDLType', }); struct( Token => { @@ -103,11 +170,6 @@ struct( Token => { value => '$' # value of token }); -struct( Typedef => { - extendedAttributes => '$', # Extended attributes - type => '$', # Type of data -}); - # Maps 'typedef name' -> Typedef my %typedefs = (); @@ -129,17 +191,28 @@ sub new { return bless $self, $class; } +sub assert +{ + my $message = shift; + + my $mess = longmess(); + print Dumper($mess); + + die $message; +} + sub assertTokenValue { my $self = shift; my $token = shift; my $value = shift; my $line = shift; - my $msg = "Next token should be " . $value . ", but " . $token->value() . " at " . $self->{Line}; + my $msg = "Next token should be " . $value . ", but " . $token->value() . " on line " . $self->{Line}; if (defined ($line)) { $msg .= " IDLParser.pm:" . $line; } - die $msg unless $token->value() eq $value; + + assert $msg unless $token->value() eq $value; } sub assertTokenType @@ -147,7 +220,8 @@ sub assertTokenType my $self = shift; my $token = shift; my $type = shift; - die "Next token's type should be " . $type . ", but " . $token->type() . " at " . $self->{Line} unless $token->type() eq $type; + + assert "Next token's type should be " . $type . ", but " . $token->type() . " on line " . $self->{Line} unless $token->type() eq $type; } sub assertUnexpectedToken @@ -155,11 +229,12 @@ sub assertUnexpectedToken my $self = shift; my $token = shift; my $line = shift; - my $msg = "Unexpected token " . $token . " at " . $self->{Line}; + my $msg = "Unexpected token " . $token . " on line " . $self->{Line}; if (defined ($line)) { $msg .= " IDLParser.pm:" . $line; } - die $msg; + + assert $msg; } sub assertNoExtendedAttributesInTypedef @@ -168,11 +243,12 @@ sub assertNoExtendedAttributesInTypedef my $name = shift; my $line = shift; my $typedef = $typedefs{$name}; - my $msg = "Unexpected extendedAttributeList in typedef \"$name\" at " . $self->{Line}; + my $msg = "Unexpected extendedAttributeList in typedef \"$name\" on line " . $self->{Line}; if (defined ($line)) { $msg .= " IDLParser.pm:" . $line; } - die $msg if %{$typedef->extendedAttributes}; + + assert $msg if %{$typedef->extendedAttributes}; } sub Parse @@ -196,15 +272,19 @@ sub Parse my $next = $self->nextToken(); $self->assertTokenType($next, EmptyToken); }; - die $@ . " in $fileName" if $@; + assert $@ . " in $fileName" if $@; - my $document = idlDocument->new(); + my $document = IDLDocument->new(); $document->fileName($fileName); foreach my $definition (@definitions) { - if (ref($definition) eq "domInterface") { + if (ref($definition) eq "IDLInterface") { push(@{$document->interfaces}, $definition); - } elsif (ref($definition) eq "domEnum") { + } elsif (ref($definition) eq "IDLEnum") { push(@{$document->enumerations}, $definition); + } elsif (ref($definition) eq "IDLDictionary") { + push(@{$document->dictionaries}, $definition); + } elsif (ref($definition) eq "IDLCallbackFunction") { + push(@{$document->callbackFunctions}, $definition); } else { die "Unrecognized IDL definition kind: \"" . ref($definition) . "\""; } @@ -231,7 +311,7 @@ my $floatTokenPattern = '^(-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?| my $integerTokenPattern = '^(-?[1-9][0-9]*|-?0[Xx][0-9A-Fa-f]+|-?0[0-7]*)'; my $stringTokenPattern = '^(\"[^\"]*\")'; my $identifierTokenPattern = '^([A-Z_a-z][0-9A-Z_a-z]*)'; -my $otherTokenPattern = '^(::|\.\.\.|[^\t\n\r 0-9A-Z_a-z])'; +my $otherTokenPattern = '^(\.\.\.|[^\t\n\r 0-9A-Z_a-z])'; sub getTokenInternal { @@ -296,51 +376,75 @@ sub unquoteString die "Failed to parse string (" . $quotedString . ") at " . $self->{Line}; } -sub typeHasNullableSuffix +sub identifierRemoveNullablePrefix { my $type = shift; - return $type =~ /\?$/; + $type =~ s/^_//; + return $type; } -sub typeRemoveNullableSuffix +sub typeDescription { my $type = shift; - $type =~ s/\?//g; + + if (scalar @{$type->subtypes}) { + return $type->name . '<' . join(', ', map { typeDescription($_) } @{$type->subtypes}) . '>' . ($type->isNullable ? "?" : ""); + } + + return $type->name . ($type->isNullable ? "?" : ""); +} + +sub makeSimpleType +{ + my $typeName = shift; + + my $type = IDLType->new(); + $type->name($typeName); + return $type; } -sub identifierRemoveNullablePrefix +sub cloneType { + my $self = shift; my $type = shift; - $type =~ s/^_//; - return $type; + + my $clonedType = IDLType->new(); + $clonedType->name($type->name); + $clonedType->isNullable($type->isNullable); + $clonedType->isUnion($type->isUnion); + foreach my $subtype (@{$type->subtypes}) { + push(@{$clonedType->subtypes}, $self->cloneType($subtype)); + } + + return $clonedType; } my $nextAttribute_1 = '^(attribute|inherit|readonly)$'; my $nextPrimitiveType_1 = '^(int|long|short|unsigned)$'; my $nextPrimitiveType_2 = '^(double|float|unrestricted)$'; -my $nextArgumentList_1 = '^(\(|::|ByteString|DOMString|Date|\[|any|boolean|byte|double|float|in|int|long|object|octet|optional|sequence|short|unrestricted|unsigned)$'; -my $nextNonAnyType_1 = '^(boolean|byte|double|float|int|long|octet|short|unrestricted|unsigned)$'; -my $nextInterfaceMember_1 = '^(\(|::|ByteString|DOMString|Date|any|attribute|boolean|byte|creator|deleter|double|float|getter|inherit|int|legacycaller|long|object|octet|readonly|sequence|serializer|setter|short|static|stringifier|unrestricted|unsigned|void)$'; -my $nextOptionalIteratorInterfaceOrObject_1 = '^(;|=)$'; -my $nextAttributeOrOperationOrIterator_1 = '^(static|stringifier)$'; -my $nextAttributeOrOperationOrIterator_2 = '^(\(|::|ByteString|DOMString|Date|any|boolean|byte|creator|deleter|double|float|getter|int|legacycaller|long|object|octet|sequence|setter|short|unrestricted|unsigned|void)$'; +my $nextArgumentList_1 = '^(\(|ByteString|DOMString|USVString|Date|\[|any|boolean|byte|double|float|in|long|object|octet|optional|sequence|short|unrestricted|unsigned)$'; +my $nextNonAnyType_1 = '^(boolean|byte|double|float|long|octet|short|unrestricted|unsigned)$'; +my $nextStringType_1 = '^(ByteString|DOMString|USVString)$'; +my $nextInterfaceMember_1 = '^(\(|ByteString|DOMString|USVString|Date|any|attribute|boolean|byte|creator|deleter|double|float|getter|inherit|legacycaller|long|object|octet|readonly|sequence|serializer|setter|short|static|stringifier|unrestricted|unsigned|void)$'; +my $nextAttributeOrOperation_1 = '^(static|stringifier)$'; +my $nextAttributeOrOperation_2 = '^(\(|ByteString|DOMString|USVString|Date|any|boolean|byte|creator|deleter|double|float|getter|legacycaller|long|object|octet|sequence|setter|short|unrestricted|unsigned|void)$'; my $nextUnrestrictedFloatType_1 = '^(double|float)$'; -my $nextExtendedAttributeRest3_1 = '^(\,|::|\])$'; -my $nextExceptionField_1 = '^(\(|::|ByteString|DOMString|Date|any|boolean|byte|double|float|int|long|object|octet|sequence|short|unrestricted|unsigned)$'; -my $nextType_1 = '^(::|ByteString|DOMString|Date|any|boolean|byte|double|float|int|long|object|octet|sequence|short|unrestricted|unsigned)$'; +my $nextExtendedAttributeRest3_1 = '^(\,|\])$'; +my $nextExceptionField_1 = '^(\(|ByteString|DOMString|USVString|Date|any|boolean|byte|double|float|long|object|octet|sequence|short|unrestricted|unsigned)$'; +my $nextType_1 = '^(ByteString|DOMString|USVString|Date|any|boolean|byte|double|float|long|object|octet|sequence|short|unrestricted|unsigned)$'; my $nextSpecials_1 = '^(creator|deleter|getter|legacycaller|setter)$'; -my $nextDefinitions_1 = '^(::|callback|dictionary|enum|exception|interface|partial|typedef)$'; -my $nextExceptionMembers_1 = '^(\(|::|ByteString|DOMString|Date|\[|any|boolean|byte|const|double|float|int|long|object|octet|optional|sequence|short|unrestricted|unsigned)$'; +my $nextDefinitions_1 = '^(callback|dictionary|enum|exception|interface|partial|typedef)$'; +my $nextExceptionMembers_1 = '^(\(|ByteString|DOMString|USVString|Date|\[|any|boolean|byte|const|double|float|long|object|octet|optional|sequence|short|unrestricted|unsigned)$'; my $nextAttributeRest_1 = '^(attribute|readonly)$'; -my $nextInterfaceMembers_1 = '^(\(|::|ByteString|DOMString|Date|any|attribute|boolean|byte|const|creator|deleter|double|float|getter|inherit|int|legacycaller|long|object|octet|readonly|sequence|serializer|setter|short|static|stringifier|unrestricted|unsigned|void)$'; -my $nextSingleType_1 = '^(::|ByteString|DOMString|Date|boolean|byte|double|float|int|long|object|octet|sequence|short|unrestricted|unsigned)$'; +my $nextInterfaceMembers_1 = '^(\(|ByteString|DOMString|USVString|Date|any|attribute|boolean|byte|const|creator|deleter|double|float|getter|inherit|legacycaller|long|object|octet|readonly|sequence|serializer|setter|short|static|stringifier|unrestricted|unsigned|void)$'; +my $nextSingleType_1 = '^(ByteString|DOMString|USVString|Date|boolean|byte|double|float|long|object|octet|sequence|short|unrestricted|unsigned)$'; my $nextArgumentName_1 = '^(attribute|callback|const|creator|deleter|dictionary|enum|exception|getter|implements|inherit|interface|legacycaller|partial|serializer|setter|static|stringifier|typedef|unrestricted)$'; my $nextConstValue_1 = '^(false|true)$'; my $nextConstValue_2 = '^(-|Infinity|NaN)$'; my $nextDefinition_1 = '^(callback|interface)$'; -my $nextAttributeOrOperationRest_1 = '^(\(|::|ByteString|DOMString|Date|any|boolean|byte|double|float|int|long|object|octet|sequence|short|unrestricted|unsigned|void)$'; -my $nextUnsignedIntegerType_1 = '^(int|long|short)$'; +my $nextAttributeOrOperationRest_1 = '^(\(|ByteString|DOMString|USVString|Date|any|boolean|byte|double|float|long|object|octet|sequence|short|unrestricted|unsigned|void)$'; +my $nextUnsignedIntegerType_1 = '^(long|short)$'; my $nextDefaultValue_1 = '^(-|Infinity|NaN|false|null|true)$'; @@ -374,59 +478,71 @@ sub applyTypedefs if (!%typedefs) { return; } + + # FIXME: Add support for applying typedefs to IDLIterable. foreach my $definition (@$definitions) { - if (ref($definition) eq "domInterface") { + if (ref($definition) eq "IDLInterface") { foreach my $constant (@{$definition->constants}) { - if (exists $typedefs{$constant->type}) { - my $typedef = $typedefs{$constant->type}; - $self->assertNoExtendedAttributesInTypedef($constant->type, __LINE__); - $constant->type($typedef->type); - } + $constant->type($self->typeByApplyingTypedefs($constant->type)); } foreach my $attribute (@{$definition->attributes}) { - $self->applyTypedefsForSignature($attribute->signature); + $attribute->type($self->typeByApplyingTypedefs($attribute->type)); } - foreach my $function (@{$definition->functions}, @{$definition->anonymousFunctions}, @{$definition->constructors}, @{$definition->customConstructors}) { - $self->applyTypedefsForSignature($function->signature); - foreach my $signature (@{$function->parameters}) { - $self->applyTypedefsForSignature($signature); - } + foreach my $operation (@{$definition->functions}, @{$definition->anonymousFunctions}, @{$definition->constructors}, @{$definition->customConstructors}) { + $self->applyTypedefsToOperation($operation); } + } elsif (ref($definition) eq "IDLDictionary") { + foreach my $member (@{$definition->members}) { + $member->type($self->typeByApplyingTypedefs($member->type)); + } + } elsif (ref($definition) eq "IDLCallbackFunction") { + $self->applyTypedefsToOperation($definition->operation); } } } -sub applyTypedefsForSignature +sub applyTypedefsToOperation { my $self = shift; - my $signature = shift; + my $operation = shift; - if (!defined ($signature->type)) { - return; + if ($operation->type) { + $operation->type($self->typeByApplyingTypedefs($operation->type)); } - my $type = $signature->type; - $type =~ s/[\?\[\]]+$//g; - my $typeSuffix = $signature->type; - $typeSuffix =~ s/^[^\?\[\]]+//g; - if (exists $typedefs{$type}) { - my $typedef = $typedefs{$type}; - $signature->type($typedef->type . $typeSuffix); - copyExtendedAttributes($signature->extendedAttributes, $typedef->extendedAttributes); + foreach my $argument (@{$operation->arguments}) { + $argument->type($self->typeByApplyingTypedefs($argument->type)); } +} - # Handle union types, sequences and etc. - foreach my $name (%typedefs) { - if (!exists $typedefs{$name}) { - next; +sub typeByApplyingTypedefs +{ + my $self = shift; + my $type = shift; + + assert("Missing type") if !$type; + + my $numberOfSubtypes = scalar @{$type->subtypes}; + if ($numberOfSubtypes) { + for my $i (0..$numberOfSubtypes - 1) { + my $subtype = @{$type->subtypes}[$i]; + my $replacementSubtype = $self->typeByApplyingTypedefs($subtype); + @{$type->subtypes}[$i] = $replacementSubtype } - my $typedef = $typedefs{$name}; - my $regex = '\\b' . $name . '\\b'; - my $replacement = $typedef->type; - my $type = $signature->type; - $type =~ s/($regex)/$replacement/g; - $signature->type($type); + + return $type; } + + if (exists $typedefs{$type->name}) { + my $typedef = $typedefs{$type->name}; + + my $clonedType = $self->cloneType($typedef->type); + $clonedType->isNullable($clonedType->isNullable || $type->isNullable); + + return $self->typeByApplyingTypedefs($clonedType); + } + + return $type; } sub parseDefinition @@ -453,7 +569,7 @@ sub parseDefinition if ($next->value() eq "typedef") { return $self->parseTypedef($extendedAttributeList); } - if ($next->type() == IdentifierToken || $next->value() eq "::") { + if ($next->type() == IdentifierToken) { return $self->parseImplementsStatement($extendedAttributeList); } $self->assertUnexpectedToken($next->value(), __LINE__); @@ -499,14 +615,20 @@ sub parseInterface my $next = $self->nextToken(); if ($next->value() eq "interface") { - my $interface = domInterface->new(); + my $interface = IDLInterface->new(); $self->assertTokenValue($self->getToken(), "interface", __LINE__); my $interfaceNameToken = $self->getToken(); $self->assertTokenType($interfaceNameToken, IdentifierToken); - $interface->name(identifierRemoveNullablePrefix($interfaceNameToken->value())); - my $parents = $self->parseInheritance(); - $interface->parents($parents); - $interface->parent($parents->[0]); + + my $name = identifierRemoveNullablePrefix($interfaceNameToken->value()); + $interface->type(makeSimpleType($name)); + + $next = $self->nextToken(); + if ($next->value() eq ":") { + my $parent = $self->parseInheritance(); + $interface->parentType(makeSimpleType($parent)); + } + $self->assertTokenValue($self->getToken(), "{", __LINE__); my $interfaceMembers = $self->parseInterfaceMembers(); $self->assertTokenValue($self->getToken(), "}", __LINE__); @@ -610,14 +732,27 @@ sub parseDictionary my $next = $self->nextToken(); if ($next->value() eq "dictionary") { + my $dictionary = IDLDictionary->new(); + $dictionary->extendedAttributes($extendedAttributeList); $self->assertTokenValue($self->getToken(), "dictionary", __LINE__); - $self->assertTokenType($self->getToken(), IdentifierToken); - $self->parseInheritance(); + + my $nameToken = $self->getToken(); + $self->assertTokenType($nameToken, IdentifierToken); + + my $name = $nameToken->value(); + $dictionary->type(makeSimpleType($name)); + + $next = $self->nextToken(); + if ($next->value() eq ":") { + my $parent = $self->parseInheritance(); + $dictionary->parentType(makeSimpleType($parent)); + } + $self->assertTokenValue($self->getToken(), "{", __LINE__); - $self->parseDictionaryMembers(); + $dictionary->members($self->parseDictionaryMembers()); $self->assertTokenValue($self->getToken(), "}", __LINE__); $self->assertTokenValue($self->getToken(), ";", __LINE__); - return; + return $dictionary; } $self->assertUnexpectedToken($next->value(), __LINE__); } @@ -626,15 +761,19 @@ sub parseDictionaryMembers { my $self = shift; + my @members = (); + while (1) { my $extendedAttributeList = $self->parseExtendedAttributeListAllowEmpty(); my $next = $self->nextToken(); if ($next->type() == IdentifierToken || $next->value() =~ /$nextExceptionField_1/) { - $self->parseDictionaryMember($extendedAttributeList); + push(@members, $self->parseDictionaryMember($extendedAttributeList)); } else { last; } } + + return \@members; } sub parseDictionaryMember @@ -644,11 +783,24 @@ sub parseDictionaryMember my $next = $self->nextToken(); if ($next->type() == IdentifierToken || $next->value() =~ /$nextExceptionField_1/) { - $self->parseType(); - $self->assertTokenType($self->getToken(), IdentifierToken); - $self->parseDefault(); + my $member = IDLDictionaryMember->new(); + if ($next->value eq "required") { + $self->assertTokenValue($self->getToken(), "required", __LINE__); + $member->isRequired(1); + } else { + $member->isRequired(0); + } + $member->extendedAttributes($extendedAttributeList); + + my $type = $self->parseType(); + $member->type($type); + + my $nameToken = $self->getToken(); + $self->assertTokenType($nameToken, IdentifierToken); + $member->name($nameToken->value); + $member->default($self->parseDefault()); $self->assertTokenValue($self->getToken(), ";", __LINE__); - return; + return $member; } $self->assertUnexpectedToken($next->value(), __LINE__); } @@ -677,6 +829,7 @@ sub parseDefault $self->assertTokenValue($self->getToken(), "=", __LINE__); return $self->parseDefaultValue(); } + return undef; } sub parseDefaultValue @@ -689,6 +842,11 @@ sub parseDefaultValue if ($next->type() == StringToken) { return $self->getToken()->value(); } + if ($next->value() eq "[") { + $self->assertTokenValue($self->getToken(), "[", __LINE__); + $self->assertTokenValue($self->getToken(), "]", __LINE__); + return "[]"; + } $self->assertUnexpectedToken($next->value(), __LINE__); } @@ -699,15 +857,21 @@ sub parseException my $next = $self->nextToken(); if ($next->value() eq "exception") { - my $interface = domInterface->new(); + my $interface = IDLInterface->new(); $self->assertTokenValue($self->getToken(), "exception", __LINE__); my $exceptionNameToken = $self->getToken(); $self->assertTokenType($exceptionNameToken, IdentifierToken); - $interface->name(identifierRemoveNullablePrefix($exceptionNameToken->value())); + + my $name = identifierRemoveNullablePrefix($exceptionNameToken->value()); + $interface->type(makeSimpleType($name)); $interface->isException(1); - my $parents = $self->parseInheritance(); - $interface->parents($parents); - $interface->parent($parents->[0]); + + $next = $self->nextToken(); + if ($next->value() eq ":") { + my $parent = $self->parseInheritance(); + $interface->parentType(makeSimpleType($parent)); + } + $self->assertTokenValue($self->getToken(), "{", __LINE__); my $exceptionMembers = $self->parseExceptionMembers(); $self->assertTokenValue($self->getToken(), "}", __LINE__); @@ -743,35 +907,34 @@ sub parseExceptionMembers sub parseInheritance { my $self = shift; - my @parent = (); my $next = $self->nextToken(); if ($next->value() eq ":") { $self->assertTokenValue($self->getToken(), ":", __LINE__); - my $scopedName = $self->parseScopedName(); - push(@parent, $scopedName); - # Multiple inheritance (needed for ObjC bindings). - push(@parent, @{$self->parseIdentifiers()}); + return $self->parseName(); } - return \@parent; + $self->assertUnexpectedToken($next->value(), __LINE__); } sub parseEnum { my $self = shift; - my $extendedAttributeList = shift; # ignored: Extended attributes are not applicable to enumerations + my $extendedAttributeList = shift; my $next = $self->nextToken(); if ($next->value() eq "enum") { - my $enum = domEnum->new(); + my $enum = IDLEnum->new(); $self->assertTokenValue($self->getToken(), "enum", __LINE__); my $enumNameToken = $self->getToken(); $self->assertTokenType($enumNameToken, IdentifierToken); - $enum->name(identifierRemoveNullablePrefix($enumNameToken->value())); + my $name = identifierRemoveNullablePrefix($enumNameToken->value()); + $enum->name($name); + $enum->type(makeSimpleType($name)); $self->assertTokenValue($self->getToken(), "{", __LINE__); push(@{$enum->values}, @{$self->parseEnumValueList()}); $self->assertTokenValue($self->getToken(), "}", __LINE__); $self->assertTokenValue($self->getToken(), ";", __LINE__); + $enum->extendedAttributes($extendedAttributeList); return $enum; } $self->assertUnexpectedToken($next->value(), __LINE__); @@ -818,14 +981,30 @@ sub parseCallbackRest my $next = $self->nextToken(); if ($next->type() == IdentifierToken) { - $self->assertTokenType($self->getToken(), IdentifierToken); + my $callback = IDLCallbackFunction->new(); + + my $nameToken = $self->getToken(); + $self->assertTokenType($nameToken, IdentifierToken); + + $callback->type(makeSimpleType($nameToken->value())); + $self->assertTokenValue($self->getToken(), "=", __LINE__); - $self->parseReturnType(); + + my $operation = IDLOperation->new(); + $operation->type($self->parseReturnType()); + $operation->extendedAttributes($extendedAttributeList); + $self->assertTokenValue($self->getToken(), "(", __LINE__); - $self->parseArgumentList(); + + push(@{$operation->arguments}, @{$self->parseArgumentList()}); + $self->assertTokenValue($self->getToken(), ")", __LINE__); $self->assertTokenValue($self->getToken(), ";", __LINE__); - return; + + $callback->operation($operation); + $callback->extendedAttributes($extendedAttributeList); + + return $callback; } $self->assertUnexpectedToken($next->value(), __LINE__); } @@ -839,14 +1018,16 @@ sub parseTypedef my $next = $self->nextToken(); if ($next->value() eq "typedef") { $self->assertTokenValue($self->getToken(), "typedef", __LINE__); - my $typedef = Typedef->new(); - $typedef->extendedAttributes($self->parseExtendedAttributeListAllowEmpty()); - $typedef->type($self->parseType()); + my $typedef = IDLTypedef->new(); + + my $type = $self->parseType(); + $typedef->type($type); + my $nameToken = $self->getToken(); $self->assertTokenType($nameToken, IdentifierToken); $self->assertTokenValue($self->getToken(), ";", __LINE__); my $name = $nameToken->value(); - die "typedef redefinition for " . $name . " at " . $self->{Line} if (exists $typedefs{$name} && $typedef->type ne $typedefs{$name}->type); + die "typedef redefinition for " . $name . " at " . $self->{Line} if (exists $typedefs{$name} && $typedef->type->name ne $typedefs{$name}->type->name); $typedefs{$name} = $typedef; return; } @@ -860,9 +1041,9 @@ sub parseImplementsStatement my $next = $self->nextToken(); if ($next->type() == IdentifierToken) { - $self->parseScopedName(); + $self->parseName(); $self->assertTokenValue($self->getToken(), "implements", __LINE__); - $self->parseScopedName(); + $self->parseName(); $self->assertTokenValue($self->getToken(), ";", __LINE__); return; } @@ -876,9 +1057,10 @@ sub parseConst my $next = $self->nextToken(); if ($next->value() eq "const") { - my $newDataNode = domConstant->new(); + my $newDataNode = IDLConstant->new(); $self->assertTokenValue($self->getToken(), "const", __LINE__); - $newDataNode->type($self->parseConstType()); + my $type = $self->parseConstType(); + $newDataNode->type($type); my $constNameToken = $self->getToken(); $self->assertTokenType($constNameToken, IdentifierToken); $newDataNode->name(identifierRemoveNullablePrefix($constNameToken->value())); @@ -905,10 +1087,6 @@ sub parseConstValue if ($next->type() == FloatToken || $next->value() =~ /$nextConstValue_2/) { return $self->parseFloatLiteral(); } - # backward compatibility - if ($next->type() == StringToken) { - return $self->getToken()->value(); - } if ($next->type() == IntegerToken) { return $self->getToken()->value(); } @@ -962,18 +1140,19 @@ sub parseAttributeOrOperationOrIterator if ($next->value() eq "serializer") { return $self->parseSerializer($extendedAttributeList); } - if ($next->value() =~ /$nextAttributeOrOperationOrIterator_1/) { + if ($next->value() =~ /$nextAttributeOrOperation_1/) { my $qualifier = $self->parseQualifier(); my $newDataNode = $self->parseAttributeOrOperationRest($extendedAttributeList); - if (defined($newDataNode) && $qualifier eq "static") { - $newDataNode->isStatic(1); + if (defined($newDataNode)) { + $newDataNode->isStatic(1) if $qualifier eq "static"; + $newDataNode->isStringifier(1) if $qualifier eq "stringifier"; } return $newDataNode; } if ($next->value() =~ /$nextAttribute_1/) { return $self->parseAttribute($extendedAttributeList); } - if ($next->type() == IdentifierToken || $next->value() =~ /$nextAttributeOrOperationOrIterator_2/) { + if ($next->type() == IdentifierToken || $next->value() =~ /$nextAttributeOrOperation_2/) { return $self->parseOperationOrIterator($extendedAttributeList); } $self->assertUnexpectedToken($next->value(), __LINE__); @@ -987,7 +1166,22 @@ sub parseSerializer my $next = $self->nextToken(); if ($next->value() eq "serializer") { $self->assertTokenValue($self->getToken(), "serializer", __LINE__); - return $self->parseSerializerRest($extendedAttributeList); + my $next = $self->nextToken(); + my $newDataNode; + if ($next->value() ne ";") { + $newDataNode = $self->parseSerializerRest($extendedAttributeList); + my $next = $self->nextToken(); + } else { + $newDataNode = IDLSerializable->new(); + } + + my $toJSONFunction = IDLOperation->new(); + $toJSONFunction->name("toJSON"); + $toJSONFunction->extendedAttributes($extendedAttributeList); + push(@{$newDataNode->functions}, $toJSONFunction); + + $self->assertTokenValue($self->getToken(), ";", __LINE__); + return $newDataNode; } $self->assertUnexpectedToken($next->value(), __LINE__); } @@ -1000,7 +1194,9 @@ sub parseSerializerRest my $next = $self->nextToken(); if ($next->value() eq "=") { $self->assertTokenValue($self->getToken(), "=", __LINE__); - return $self->parseSerializationPattern($extendedAttributeList); + + return $self->parseSerializationPattern(); + } if ($next->type() == IdentifierToken || $next->value() eq "(") { return $self->parseOperationRest($extendedAttributeList); @@ -1010,59 +1206,74 @@ sub parseSerializerRest sub parseSerializationPattern { my $self = shift; - my $extendedAttributeList = shift; my $next = $self->nextToken(); if ($next->value() eq "{") { $self->assertTokenValue($self->getToken(), "{", __LINE__); - $self->parseSerializationPatternMap(); + my $newDataNode = IDLSerializable->new(); + $self->parseSerializationAttributes($newDataNode); $self->assertTokenValue($self->getToken(), "}", __LINE__); - return; + return $newDataNode; } if ($next->value() eq "[") { - $self->assertTokenValue($self->getToken(), "[", __LINE__); - $self->parseSerializationPatternList(); - $self->assertTokenValue($self->getToken(), "]", __LINE__); - return; + die "Serialization of lists pattern is not currently supported."; } if ($next->type() == IdentifierToken) { - $self->assertTokenType($self->getToken(), IdentifierToken); - return; + my @attributes = (); + my $token = $self->getToken(); + $self->assertTokenType($token, IdentifierToken); + push(@attributes, $token->value()); + + my $newDataNode = IDLSerializable->new(); + $newDataNode->attributes(\@attributes); + + return $newDataNode; } $self->assertUnexpectedToken($next->value(), __LINE__); } -sub parseSerializationPatternMap +sub parseSerializationAttributes { my $self = shift; - my $next = $self->nextToken(); - if ($next->value() eq "getter") { - $self->assertTokenValue($self->getToken(), "getter", __LINE__); - return; - } - if ($next->value() eq "inherit") { - $self->assertTokenValue($self->getToken(), "inherit", __LINE__); - $self->parseIdentifiers(); - return; - } - if ($next->type() == IdentifierToken) { - $self->assertTokenType($self->getToken(), IdentifierToken); - $self->parseIdentifiers(); + my $serializable = shift; + + my @attributes = (); + my @identifiers = $self->parseIdentifierList(); + + for my $identifier (@identifiers) { + if ($identifier eq "getter") { + $serializable->hasGetter(1); + die "Serializer getter keyword is not currently supported."; + } + + if ($identifier eq "inherit") { + $serializable->hasInherit(1); + next; + } + + if ($identifier eq "attribute") { + $serializable->hasAttribute(1); + # Attributes will be filled in via applyMemberList() + next; + } + + push(@attributes, $identifier); } + + $serializable->attributes(\@attributes); } -sub parseSerializationPatternList +sub parseIdentifierList { my $self = shift; my $next = $self->nextToken(); - if ($next->value() eq "getter") { - $self->assertTokenValue($self->getToken(), "getter", __LINE__); - return; - } - if ($next->type() == IdentifierToken) { - $self->assertTokenType($self->getToken(), IdentifierToken); - $self->parseIdentifiers(); + + my @identifiers = (); + if ($next->type == IdentifierToken) { + push(@identifiers, $self->getToken()->value()); + push(@identifiers, @{$self->parseIdentifiers()}); } + return @identifiers; } sub parseIdentifiers @@ -1115,11 +1326,11 @@ sub parseAttributeOrOperationRest } if ($next->type() == IdentifierToken || $next->value() =~ /$nextAttributeOrOperationRest_1/) { my $returnType = $self->parseReturnType(); - my $interface = $self->parseOperationRest($extendedAttributeList); - if (defined ($interface)) { - $interface->signature->type($returnType); + my $operation = $self->parseOperationRest($extendedAttributeList); + if (defined ($operation)) { + $operation->type($returnType); } - return $interface; + return $operation; } $self->assertUnexpectedToken($next->value(), __LINE__); } @@ -1144,33 +1355,25 @@ sub parseAttributeRest my $next = $self->nextToken(); if ($next->value() =~ /$nextAttributeRest_1/) { - my $newDataNode = domAttribute->new(); - if ($self->parseReadOnly()) { - $newDataNode->type("attribute"); - $newDataNode->isReadOnly(1); - } else { - $newDataNode->type("attribute"); - } + my $newDataNode = IDLAttribute->new(); + $newDataNode->isReadOnly($self->parseReadOnly()); + $self->assertTokenValue($self->getToken(), "attribute", __LINE__); - $newDataNode->signature(domSignature->new()); + my $type = $self->parseType(); - if (typeHasNullableSuffix($type)) { - $newDataNode->signature->isNullable(1); - } else { - $newDataNode->signature->isNullable(0); - } - # Remove all "?" in the type declaration, e.g. "double?" -> "double". - $newDataNode->signature->type(typeRemoveNullableSuffix($type)); + $newDataNode->type($type); + my $token = $self->getToken(); $self->assertTokenType($token, IdentifierToken); - $newDataNode->signature->name(identifierRemoveNullablePrefix($token->value())); + $newDataNode->name(identifierRemoveNullablePrefix($token->value())); $self->assertTokenValue($self->getToken(), ";", __LINE__); + # CustomConstructor may also be used on attributes. if (defined $extendedAttributeList->{"CustomConstructors"}) { delete $extendedAttributeList->{"CustomConstructors"}; $extendedAttributeList->{"CustomConstructor"} = "VALUE_IS_MISSING"; } - $newDataNode->signature->extendedAttributes($extendedAttributeList); + $newDataNode->extendedAttributes($extendedAttributeList); return $newDataNode; } $self->assertUnexpectedToken($next->value(), __LINE__); @@ -1207,13 +1410,17 @@ sub parseOperationOrIterator if ($next->value() =~ /$nextSpecials_1/) { return $self->parseSpecialOperation($extendedAttributeList); } + if ($next->value() eq "iterable") { + return $self->parseIterableRest($extendedAttributeList); + } if ($next->type() == IdentifierToken || $next->value() =~ /$nextAttributeOrOperationRest_1/) { my $returnType = $self->parseReturnType(); - my $interface = $self->parseOperationOrIteratorRest($extendedAttributeList); - if (defined ($interface)) { - $interface->signature->type($returnType); + my $next = $self->nextToken(); + if ($next->type() == IdentifierToken || $next->value() eq "(") { + my $operation = $self->parseOperationRest($extendedAttributeList); + $operation->type($returnType); + return $operation; } - return $interface; } $self->assertUnexpectedToken($next->value(), __LINE__); } @@ -1228,12 +1435,12 @@ sub parseSpecialOperation my @specials = (); push(@specials, @{$self->parseSpecials()}); my $returnType = $self->parseReturnType(); - my $interface = $self->parseOperationRest($extendedAttributeList); - if (defined ($interface)) { - $interface->signature->type($returnType); - $interface->signature->specials(\@specials); + my $operation = $self->parseOperationRest($extendedAttributeList); + if (defined ($operation)) { + $operation->type($returnType); + $operation->specials(\@specials); } - return $interface; + return $operation; } $self->assertUnexpectedToken($next->value(), __LINE__); } @@ -1281,62 +1488,75 @@ sub parseSpecial $self->assertUnexpectedToken($next->value(), __LINE__); } -sub parseOperationOrIteratorRest +sub parseIterableRest { my $self = shift; my $extendedAttributeList = shift; my $next = $self->nextToken(); - if ($next->value() eq "iterator") { - return $self->parseIteratorRest($extendedAttributeList); - } - if ($next->type() == IdentifierToken || $next->value() eq "(") { - return $self->parseOperationRest($extendedAttributeList); - } - $self->assertUnexpectedToken($next->value(), __LINE__); -} - -sub parseIteratorRest -{ - my $self = shift; - my $extendedAttributeList = shift; - - my $next = $self->nextToken(); - if ($next->value() eq "iterator") { - $self->assertTokenValue($self->getToken(), "iterator", __LINE__); - $self->parseOptionalIteratorInterfaceOrObject($extendedAttributeList); + if ($next->value() eq "iterable") { + $self->assertTokenValue($self->getToken(), "iterable", __LINE__); + my $iterableNode = $self->parseOptionalIterableInterface($extendedAttributeList); $self->assertTokenValue($self->getToken(), ";", __LINE__); - return; + return $iterableNode; } $self->assertUnexpectedToken($next->value(), __LINE__); } -sub parseOptionalIteratorInterfaceOrObject +sub parseOptionalIterableInterface { my $self = shift; my $extendedAttributeList = shift; - my $next = $self->nextToken(); - if ($next->value() =~ /$nextOptionalIteratorInterfaceOrObject_1/) { - return $self->parseOptionalIteratorInterface($extendedAttributeList); - } - if ($next->value() eq "object") { - $self->assertTokenValue($self->getToken(), "object", __LINE__); - return; - } - $self->assertUnexpectedToken($next->value(), __LINE__); -} - -sub parseOptionalIteratorInterface -{ - my $self = shift; - my $extendedAttributeList = shift; + my $symbolIteratorFunction = IDLOperation->new(); + $symbolIteratorFunction->name("[Symbol.Iterator]"); + $symbolIteratorFunction->extendedAttributes($extendedAttributeList); + + my $entriesFunction = IDLOperation->new(); + $entriesFunction->name("entries"); + $entriesFunction->extendedAttributes($extendedAttributeList); + + my $keysFunction = IDLOperation->new(); + $keysFunction->name("keys"); + $keysFunction->extendedAttributes($extendedAttributeList); + + my $valuesFunction = IDLOperation->new(); + $valuesFunction->name("values"); + $valuesFunction->extendedAttributes($extendedAttributeList); + + my $forEachFunction = IDLOperation->new(); + $forEachFunction->name("forEach"); + $forEachFunction->extendedAttributes($extendedAttributeList); + my $forEachArgument = IDLArgument->new(); + $forEachArgument->name("callback"); + $forEachArgument->type(makeSimpleType("any")); + push(@{$forEachFunction->arguments}, ($forEachArgument)); + + my $newDataNode = IDLIterable->new(); + $newDataNode->extendedAttributes($extendedAttributeList); + push(@{$newDataNode->functions}, $symbolIteratorFunction); + push(@{$newDataNode->functions}, $entriesFunction); + push(@{$newDataNode->functions}, $keysFunction); + push(@{$newDataNode->functions}, $valuesFunction); + push(@{$newDataNode->functions}, $forEachFunction); + + $self->assertTokenValue($self->getToken(), "<", __LINE__); + my $type1 = $self->parseType(); + + if ($self->nextToken()->value() eq ",") { + $self->assertTokenValue($self->getToken(), ",", __LINE__); - my $next = $self->nextToken(); - if ($next->value() eq "=") { - $self->assertTokenValue($self->getToken(), "=", __LINE__); - $self->assertTokenType($self->getToken(), IdentifierToken); + my $type2 = $self->parseType(); + $newDataNode->isKeyValue(1); + $newDataNode->keyType($type1); + $newDataNode->valueType($type2); + } else { + $newDataNode->isKeyValue(0); + $newDataNode->valueType($type1); } + $self->assertTokenValue($self->getToken(), ">", __LINE__); + + return $newDataNode; } sub parseOperationRest @@ -1346,15 +1566,19 @@ sub parseOperationRest my $next = $self->nextToken(); if ($next->type() == IdentifierToken || $next->value() eq "(") { - my $newDataNode = domFunction->new(); - $newDataNode->signature(domSignature->new()); + my $newDataNode = IDLOperation->new(); + my $name = $self->parseOptionalIdentifier(); - $newDataNode->signature->name(identifierRemoveNullablePrefix($name)); + $newDataNode->name(identifierRemoveNullablePrefix($name)); + $self->assertTokenValue($self->getToken(), "(", $name, __LINE__); - push(@{$newDataNode->parameters}, @{$self->parseArgumentList()}); + + push(@{$newDataNode->arguments}, @{$self->parseArgumentList()}); + $self->assertTokenValue($self->getToken(), ")", __LINE__); $self->assertTokenValue($self->getToken(), ";", __LINE__); - $newDataNode->signature->extendedAttributes($extendedAttributeList); + + $newDataNode->extendedAttributes($extendedAttributeList); return $newDataNode; } $self->assertUnexpectedToken($next->value(), __LINE__); @@ -1406,10 +1630,8 @@ sub parseArgument my $self = shift; my $next = $self->nextToken(); if ($next->type() == IdentifierToken || $next->value() =~ /$nextArgumentList_1/) { - my $in = $self->parseIn(); my $extendedAttributeList = $self->parseExtendedAttributeListAllowEmpty(); my $argument = $self->parseOptionalOrRequiredArgument($extendedAttributeList); - $argument->direction($self->parseIn()); return $argument; } $self->assertUnexpectedToken($next->value(), __LINE__); @@ -1420,36 +1642,23 @@ sub parseOptionalOrRequiredArgument my $self = shift; my $extendedAttributeList = shift; - my $paramDataNode = domSignature->new(); + my $paramDataNode = IDLArgument->new(); $paramDataNode->extendedAttributes($extendedAttributeList); my $next = $self->nextToken(); if ($next->value() eq "optional") { $self->assertTokenValue($self->getToken(), "optional", __LINE__); + my $type = $self->parseType(); - # domDataNode can only consider last "?". - if (typeHasNullableSuffix($type)) { - $paramDataNode->isNullable(1); - } else { - $paramDataNode->isNullable(0); - } - # Remove all "?" if exists, e.g. "object?[]?" -> "object[]". - $paramDataNode->type(identifierRemoveNullablePrefix(typeRemoveNullableSuffix($type))); + $paramDataNode->type($type); $paramDataNode->isOptional(1); $paramDataNode->name($self->parseArgumentName()); - $self->parseDefault(); + $paramDataNode->default($self->parseDefault()); return $paramDataNode; } if ($next->type() == IdentifierToken || $next->value() =~ /$nextExceptionField_1/) { my $type = $self->parseType(); - # domDataNode can only consider last "?". - if (typeHasNullableSuffix($type)) { - $paramDataNode->isNullable(1); - } else { - $paramDataNode->isNullable(0); - } - # Remove all "?" if exists, e.g. "object?[]?" -> "object[]". - $paramDataNode->type(typeRemoveNullableSuffix($type)); + $paramDataNode->type($type); $paramDataNode->isOptional(0); $paramDataNode->isVariadic($self->parseEllipsis()); $paramDataNode->name($self->parseArgumentName()); @@ -1504,16 +1713,17 @@ sub parseExceptionField my $next = $self->nextToken(); if ($next->type() == IdentifierToken || $next->value() =~ /$nextExceptionField_1/) { - my $newDataNode = domAttribute->new(); - $newDataNode->type("attribute"); + my $newDataNode = IDLAttribute->new(); $newDataNode->isReadOnly(1); - $newDataNode->signature(domSignature->new()); - $newDataNode->signature->type($self->parseType()); + + my $type = $self->parseType(); + $newDataNode->type($type); + my $token = $self->getToken(); $self->assertTokenType($token, IdentifierToken); - $newDataNode->signature->name(identifierRemoveNullablePrefix($token->value())); + $newDataNode->name(identifierRemoveNullablePrefix($token->value())); $self->assertTokenValue($self->getToken(), ";", __LINE__); - $newDataNode->signature->extendedAttributes($extendedAttributeList); + $newDataNode->extendedAttributes($extendedAttributeList); return $newDataNode; } $self->assertUnexpectedToken($next->value(), __LINE__); @@ -1594,9 +1804,9 @@ sub parseExtendedAttribute { my $self = shift; my $next = $self->nextToken(); - if ($next->type() == IdentifierToken || $next->value() eq "::") { - my $scopedName = $self->parseScopedName(); - return $self->parseExtendedAttributeRest($scopedName); + if ($next->type() == IdentifierToken) { + my $name = $self->parseName(); + return $self->parseExtendedAttributeRest($name); } # backward compatibility. Spec doesn' allow "[]". But WebKit requires. if ($next->value() eq ']') { @@ -1609,9 +1819,9 @@ sub parseExtendedAttribute2 { my $self = shift; my $next = $self->nextToken(); - if ($next->type() == IdentifierToken || $next->value() eq "::") { - my $scopedName = $self->parseScopedName(); - return $self->parseExtendedAttributeRest($scopedName); + if ($next->type() == IdentifierToken) { + my $name = $self->parseName(); + return $self->parseExtendedAttributeRest($name); } return {}; } @@ -1647,9 +1857,15 @@ sub parseExtendedAttributeRest2 { my $self = shift; my $next = $self->nextToken(); - if ($next->type() == IdentifierToken || $next->value() eq "::") { - my $scopedName = $self->parseScopedName(); - return $self->parseExtendedAttributeRest3($scopedName); + if ($next->value() eq "(") { + $self->assertTokenValue($self->getToken(), "(", __LINE__); + my @arguments = $self->parseIdentifierList(); + $self->assertTokenValue($self->getToken(), ")", __LINE__); + return @arguments; + } + if ($next->type() == IdentifierToken) { + my $name = $self->parseName(); + return $self->parseExtendedAttributeRest3($name); } if ($next->type() == IntegerToken) { my $token = $self->getToken(); @@ -1666,12 +1882,12 @@ sub parseExtendedAttributeRest3 my $next = $self->nextToken(); if ($next->value() eq "&") { $self->assertTokenValue($self->getToken(), "&", __LINE__); - my $rightValue = $self->parseScopedName(); + my $rightValue = $self->parseName(); return $name . "&" . $rightValue; } if ($next->value() eq "|") { $self->assertTokenValue($self->getToken(), "|", __LINE__); - my $rightValue = $self->parseScopedName(); + my $rightValue = $self->parseName(); return $name . "|" . $rightValue; } if ($next->value() eq "(") { @@ -1682,30 +1898,12 @@ sub parseExtendedAttributeRest3 return $attr; } if ($next->type() == IdentifierToken || $next->value() =~ /$nextExtendedAttributeRest3_1/) { - my @names = (); - push(@names, $name); - push(@names, @{$self->parseScopedNameListNoComma()}); - return join(' ', @names); + $self->parseNameNoComma(); + return $name; } $self->assertUnexpectedToken($next->value()); } -sub parseScopedNameListNoComma -{ - my $self = shift; - my @names = (); - - while (1) { - my $next = $self->nextToken(); - if ($next->type() == IdentifierToken || $next->value() eq "::") { - push(@names, $self->parseScopedName()); - } else { - last; - } - } - return \@names; -} - sub parseArgumentNameKeyword { my $self = shift; @@ -1778,9 +1976,9 @@ sub parseType my $self = shift; my $next = $self->nextToken(); if ($next->value() eq "(") { - $self->parseUnionType(); - $self->parseTypeSuffix(); - return; + my $unionType = $self->parseUnionType(); + $unionType->isNullable($self->parseNull()); + return $unionType; } if ($next->type() == IdentifierToken || $next->value() =~ /$nextType_1/) { return $self->parseSingleType(); @@ -1794,10 +1992,15 @@ sub parseSingleType my $next = $self->nextToken(); if ($next->value() eq "any") { $self->assertTokenValue($self->getToken(), "any", __LINE__); - return "any" . $self->parseTypeSuffixStartingWithArray(); + + my $anyType = IDLType->new(); + $anyType->name("any"); + return $anyType; } if ($next->type() == IdentifierToken || $next->value() =~ /$nextSingleType_1/) { - return $self->parseNonAnyType(); + my $nonAnyType = $self->parseNonAnyType(); + $nonAnyType->isNullable($self->parseNull()); + return $nonAnyType; } $self->assertUnexpectedToken($next->value(), __LINE__); } @@ -1806,14 +2009,20 @@ sub parseUnionType { my $self = shift; my $next = $self->nextToken(); + + my $unionType = IDLType->new(); + $unionType->name("UNION"); + $unionType->isUnion(1); + if ($next->value() eq "(") { $self->assertTokenValue($self->getToken(), "(", __LINE__); - $self->parseUnionMemberType(); - $self->assertTokenValue($self->getToken(), "or", __LINE__); - $self->parseUnionMemberType(); - $self->parseUnionMemberTypes(); + + push(@{$unionType->subtypes}, $self->parseUnionMemberType()); + push(@{$unionType->subtypes}, $self->parseUnionMemberTypes()); + $self->assertTokenValue($self->getToken(), ")", __LINE__); - return; + + return $unionType; } $self->assertUnexpectedToken($next->value(), __LINE__); } @@ -1823,20 +2032,14 @@ sub parseUnionMemberType my $self = shift; my $next = $self->nextToken(); if ($next->value() eq "(") { - $self->parseUnionType(); - $self->parseTypeSuffix(); - return; - } - if ($next->value() eq "any") { - $self->assertTokenValue($self->getToken(), "any", __LINE__); - $self->assertTokenValue($self->getToken(), "[", __LINE__); - $self->assertTokenValue($self->getToken(), "]", __LINE__); - $self->parseTypeSuffix(); - return; + my $unionType = $self->parseUnionType(); + $unionType->isNullable($self->parseNull()); + return $unionType; } if ($next->type() == IdentifierToken || $next->value() =~ /$nextSingleType_1/) { - $self->parseNonAnyType(); - return; + my $nonAnyType = $self->parseNonAnyType(); + $nonAnyType->isNullable($self->parseNull()); + return $nonAnyType; } $self->assertUnexpectedToken($next->value(), __LINE__); } @@ -1845,46 +2048,126 @@ sub parseUnionMemberTypes { my $self = shift; my $next = $self->nextToken(); + + my @subtypes = (); + if ($next->value() eq "or") { $self->assertTokenValue($self->getToken(), "or", __LINE__); - $self->parseUnionMemberType(); - $self->parseUnionMemberTypes(); + push(@subtypes, $self->parseUnionMemberType()); + push(@subtypes, $self->parseUnionMemberTypes()); } + + return @subtypes; } sub parseNonAnyType { my $self = shift; my $next = $self->nextToken(); + + my $type = IDLType->new(); + if ($next->value() =~ /$nextNonAnyType_1/) { - return $self->parsePrimitiveType() . $self->parseTypeSuffix(); + $type->name($self->parsePrimitiveType()); + return $type; } - if ($next->value() eq "ByteString") { - $self->assertTokenValue($self->getToken(), "ByteString", __LINE__); - return "ByteString" . $self->parseTypeSuffix(); + if ($next->value() =~ /$nextStringType_1/) { + $type->name($self->parseStringType()); + return $type; } - if ($next->value() eq "DOMString") { - $self->assertTokenValue($self->getToken(), "DOMString", __LINE__); - return "DOMString" . $self->parseTypeSuffix(); + if ($next->value() eq "object") { + $self->assertTokenValue($self->getToken(), "object", __LINE__); + + $type->name("object"); + return $type; + } + if ($next->value() eq "RegExp") { + $self->assertTokenValue($self->getToken(), "RegExp", __LINE__); + + $type->name("RegExp"); + return $type; + } + if ($next->value() eq "Error") { + $self->assertTokenValue($self->getToken(), "Error", __LINE__); + + $type->name("Error"); + return $type; + } + if ($next->value() eq "DOMException") { + $self->assertTokenValue($self->getToken(), "DOMException", __LINE__); + + $type->name("DOMException"); + return $type; + } + if ($next->value() eq "Date") { + $self->assertTokenValue($self->getToken(), "Date", __LINE__); + + $type->name("Date"); + return $type; } if ($next->value() eq "sequence") { $self->assertTokenValue($self->getToken(), "sequence", __LINE__); $self->assertTokenValue($self->getToken(), "<", __LINE__); - my $type = $self->parseType(); + + my $subtype = $self->parseType(); + $self->assertTokenValue($self->getToken(), ">", __LINE__); - return "sequence<" . $type . ">" . $self->parseNull(); + + $type->name("sequence"); + push(@{$type->subtypes}, $subtype); + + return $type; } - if ($next->value() eq "object") { - $self->assertTokenValue($self->getToken(), "object", __LINE__); - return "object" . $self->parseTypeSuffix(); + if ($next->value() eq "FrozenArray") { + $self->assertTokenValue($self->getToken(), "FrozenArray", __LINE__); + $self->assertTokenValue($self->getToken(), "<", __LINE__); + + my $subtype = $self->parseType(); + + $self->assertTokenValue($self->getToken(), ">", __LINE__); + + $type->name("FrozenArray"); + push(@{$type->subtypes}, $subtype); + + return $type; } - if ($next->value() eq "Date") { - $self->assertTokenValue($self->getToken(), "Date", __LINE__); - return "Date" . $self->parseTypeSuffix(); + if ($next->value() eq "Promise") { + $self->assertTokenValue($self->getToken(), "Promise", __LINE__); + $self->assertTokenValue($self->getToken(), "<", __LINE__); + + my $subtype = $self->parseReturnType(); + + $self->assertTokenValue($self->getToken(), ">", __LINE__); + + $type->name("Promise"); + push(@{$type->subtypes}, $subtype); + + return $type; } - if ($next->type() == IdentifierToken || $next->value() eq "::") { - my $name = identifierRemoveNullablePrefix($self->parseScopedName()); - return $name . $self->parseTypeSuffix(); + if ($next->value() eq "record") { + $self->assertTokenValue($self->getToken(), "record", __LINE__); + $self->assertTokenValue($self->getToken(), "<", __LINE__); + + my $keyType = IDLType->new(); + $keyType->name($self->parseStringType()); + + $self->assertTokenValue($self->getToken(), ",", __LINE__); + + my $valueType = $self->parseType(); + + $self->assertTokenValue($self->getToken(), ">", __LINE__); + + $type->name("record"); + push(@{$type->subtypes}, $keyType); + push(@{$type->subtypes}, $valueType); + + return $type; + } + if ($next->type() == IdentifierToken) { + my $identifier = $self->getToken(); + + $type->name(identifierRemoveNullablePrefix($identifier->value())); + return $type; } $self->assertUnexpectedToken($next->value(), __LINE__); } @@ -1893,12 +2176,40 @@ sub parseConstType { my $self = shift; my $next = $self->nextToken(); + + my $type = IDLType->new(); + if ($next->value() =~ /$nextNonAnyType_1/) { - return $self->parsePrimitiveType() . $self->parseNull(); + $type->name($self->parsePrimitiveType()); + $type->isNullable($self->parseNull()); + return $type; } if ($next->type() == IdentifierToken) { - my $token = $self->getToken(); - return $token->value() . $self->parseNull(); + my $identifier = $self->getToken(); + + $type->name($identifier->value()); + $type->isNullable($self->parseNull()); + + return $type; + } + $self->assertUnexpectedToken($next->value(), __LINE__); +} + +sub parseStringType +{ + my $self = shift; + my $next = $self->nextToken(); + if ($next->value() eq "ByteString") { + $self->assertTokenValue($self->getToken(), "ByteString", __LINE__); + return "ByteString"; + } + if ($next->value() eq "DOMString") { + $self->assertTokenValue($self->getToken(), "DOMString", __LINE__); + return "DOMString"; + } + if ($next->value() eq "USVString") { + $self->assertTokenValue($self->getToken(), "USVString", __LINE__); + return "USVString"; } $self->assertUnexpectedToken($next->value(), __LINE__); } @@ -2004,43 +2315,15 @@ sub parseOptionalLong return 0; } -sub parseTypeSuffix -{ - my $self = shift; - my $next = $self->nextToken(); - if ($next->value() eq "[") { - $self->assertTokenValue($self->getToken(), "[", __LINE__); - $self->assertTokenValue($self->getToken(), "]", __LINE__); - return "[]" . $self->parseTypeSuffix(); - } - if ($next->value() eq "?") { - $self->assertTokenValue($self->getToken(), "?", __LINE__); - return "?" . $self->parseTypeSuffixStartingWithArray(); - } - return ""; -} - -sub parseTypeSuffixStartingWithArray -{ - my $self = shift; - my $next = $self->nextToken(); - if ($next->value() eq "[") { - $self->assertTokenValue($self->getToken(), "[", __LINE__); - $self->assertTokenValue($self->getToken(), "]", __LINE__); - return "[]" . $self->parseTypeSuffix(); - } - return ""; -} - sub parseNull { my $self = shift; my $next = $self->nextToken(); if ($next->value() eq "?") { $self->assertTokenValue($self->getToken(), "?", __LINE__); - return "?"; + return 1; } - return ""; + return 0; } sub parseReturnType @@ -2049,7 +2332,10 @@ sub parseReturnType my $next = $self->nextToken(); if ($next->value() eq "void") { $self->assertTokenValue($self->getToken(), "void", __LINE__); - return "void"; + + my $voidType = IDLType->new(); + $voidType->name("void"); + return $voidType; } if ($next->type() == IdentifierToken || $next->value() =~ /$nextExceptionField_1/) { return $self->parseType(); @@ -2057,17 +2343,6 @@ sub parseReturnType $self->assertUnexpectedToken($next->value(), __LINE__); } -sub parseIn -{ - my $self = shift; - my $next = $self->nextToken(); - if ($next->value() eq "in") { - $self->assertTokenValue($self->getToken(), "in", __LINE__); - return "in"; - } - return ""; -} - sub parseOptionalSemicolon { my $self = shift; @@ -2077,115 +2352,72 @@ sub parseOptionalSemicolon } } -sub parseScopedName +sub parseNameNoComma { my $self = shift; my $next = $self->nextToken(); - if ($next->value() eq "::") { - return $self->parseAbsoluteScopedName(); - } if ($next->type() == IdentifierToken) { - return $self->parseRelativeScopedName(); + my $identifier = $self->getToken(); + return ($identifier->value()); } - $self->assertUnexpectedToken($next->value()); -} -sub parseAbsoluteScopedName -{ - my $self = shift; - my $next = $self->nextToken(); - if ($next->value() eq "::") { - $self->assertTokenValue($self->getToken(), "::"); - my $token = $self->getToken(); - $self->assertTokenType($token, IdentifierToken); - return "::" . $token->value() . $self->parseScopedNameParts(); - } - $self->assertUnexpectedToken($next->value()); + return (); } -sub parseRelativeScopedName +sub parseName { my $self = shift; my $next = $self->nextToken(); if ($next->type() == IdentifierToken) { - my $token = $self->getToken(); - return $token->value() . $self->parseScopedNameParts(); + my $identifier = $self->getToken(); + return $identifier->value(); } $self->assertUnexpectedToken($next->value()); } -sub parseScopedNameParts -{ - my $self = shift; - my @names = (); - - while (1) { - my $next = $self->nextToken(); - if ($next->value() eq "::") { - $self->assertTokenValue($self->getToken(), "::"); - push(@names, "::"); - my $token = $self->getToken(); - $self->assertTokenType($token, IdentifierToken); - push(@names, $token->value()); - } else { - last; - } - } - return join("", @names); -} - -sub parseScopedNameList -{ - my $self = shift; - my $next = $self->nextToken(); - if ($next->type() == IdentifierToken || $next->value() eq "::") { - my @names = (); - push(@names, $self->parseScopedName()); - push(@names, @{$self->parseScopedNames()}); - return \@names; - } - $self->assertUnexpectedToken($next->value(), __LINE__); -} - -sub parseScopedNames -{ - my $self = shift; - my @names = (); - - while (1) { - my $next = $self->nextToken(); - if ($next->value() eq ",") { - $self->assertTokenValue($self->getToken(), ","); - push(@names, $self->parseScopedName()); - } else { - last; - } - } - return \@names; -} - sub applyMemberList { my $interface = shift; my $members = shift; for my $item (@{$members}) { - if (ref($item) eq "domAttribute") { + if (ref($item) eq "IDLAttribute") { push(@{$interface->attributes}, $item); next; } - if (ref($item) eq "domConstant") { + if (ref($item) eq "IDLConstant") { push(@{$interface->constants}, $item); next; } - if (ref($item) eq "domFunction") { - if ($item->signature->name eq "") { + if (ref($item) eq "IDLIterable") { + $interface->iterable($item); + next; + } + if (ref($item) eq "IDLOperation") { + if ($item->name eq "") { push(@{$interface->anonymousFunctions}, $item); } else { push(@{$interface->functions}, $item); } next; } + if (ref($item) eq "IDLSerializable") { + $interface->serializable($item); + next; + } + } + + if ($interface->serializable) { + my $numSerializerAttributes = @{$interface->serializable->attributes}; + if ($interface->serializable->hasAttribute) { + foreach my $attribute (@{$interface->attributes}) { + push(@{$interface->serializable->attributes}, $attribute->name); + } + } elsif ($numSerializerAttributes == 0) { + foreach my $attribute (@{$interface->attributes}) { + push(@{$interface->serializable->attributes}, $attribute->name); + } + } } } @@ -2198,25 +2430,22 @@ sub applyExtendedAttributeList my @constructorParams = @{$extendedAttributeList->{"Constructors"}}; my $index = (@constructorParams == 1) ? 0 : 1; foreach my $param (@constructorParams) { - my $constructor = domFunction->new(); - $constructor->signature(domSignature->new()); - $constructor->signature->name("Constructor"); - $constructor->signature->extendedAttributes($extendedAttributeList); - $constructor->parameters($param); - $constructor->{overloadedIndex} = $index++; + my $constructor = IDLOperation->new(); + $constructor->name("Constructor"); + $constructor->extendedAttributes($extendedAttributeList); + $constructor->arguments($param); push(@{$interface->constructors}, $constructor); } delete $extendedAttributeList->{"Constructors"}; $extendedAttributeList->{"Constructor"} = "VALUE_IS_MISSING"; } elsif (defined $extendedAttributeList->{"NamedConstructor"}) { - my $newDataNode = domFunction->new(); - $newDataNode->signature(domSignature->new()); - $newDataNode->signature->name("NamedConstructor"); - $newDataNode->signature->extendedAttributes($extendedAttributeList); + my $newDataNode = IDLOperation->new(); + $newDataNode->name("NamedConstructor"); + $newDataNode->extendedAttributes($extendedAttributeList); my %attributes = %{$extendedAttributeList->{"NamedConstructor"}}; my @attributeKeys = keys (%attributes); my $constructorName = $attributeKeys[0]; - push(@{$newDataNode->parameters}, @{$attributes{$constructorName}}); + push(@{$newDataNode->arguments}, @{$attributes{$constructorName}}); $extendedAttributeList->{"NamedConstructor"} = $constructorName; push(@{$interface->constructors}, $newDataNode); } @@ -2224,12 +2453,10 @@ sub applyExtendedAttributeList my @customConstructorParams = @{$extendedAttributeList->{"CustomConstructors"}}; my $index = (@customConstructorParams == 1) ? 0 : 1; foreach my $param (@customConstructorParams) { - my $customConstructor = domFunction->new(); - $customConstructor->signature(domSignature->new()); - $customConstructor->signature->name("CustomConstructor"); - $customConstructor->signature->extendedAttributes($extendedAttributeList); - $customConstructor->parameters($param); - $customConstructor->{overloadedIndex} = $index++; + my $customConstructor = IDLOperation->new(); + $customConstructor->name("CustomConstructor"); + $customConstructor->extendedAttributes($extendedAttributeList); + $customConstructor->arguments($param); push(@{$interface->customConstructors}, $customConstructor); } delete $extendedAttributeList->{"CustomConstructors"}; diff --git a/Source/WebCore/bindings/scripts/InFilesCompiler.pm b/Source/WebCore/bindings/scripts/InFilesCompiler.pm index 297568c97..0780d9456 100644 --- a/Source/WebCore/bindings/scripts/InFilesCompiler.pm +++ b/Source/WebCore/bindings/scripts/InFilesCompiler.pm @@ -14,7 +14,7 @@ # THIS SOFTWARE IS PROVIDED BY GOOGLE, INC. ``AS IS'' AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -37,7 +37,7 @@ require Config; package InFilesCompiler; -my $inputFile = ""; +my @inputFiles; my $outputDir = "."; my $defaultItemFactory; @@ -82,11 +82,11 @@ sub new() sub initializeFromCommandLine() { ::GetOptions( - 'input=s' => \$inputFile, + 'input=s' => \@inputFiles, 'outputDir=s' => \$outputDir, ); - die "You must specify --input <file>" unless length($inputFile); + die "You must specify at least one --input <file>" unless scalar(@inputFiles); ::mkpath($outputDir); @@ -99,14 +99,17 @@ sub compile() my $object = shift; my $generateCode = shift; - my $file = new IO::File; - open($file, $inputFile) or die "Failed to open file: $!"; - my $InParser = InFilesParser->new(); - $InParser->parse($file, \¶meterHandler, \&itemHandler); - close($file); - die "Failed to read from file: $inputFile" if (keys %parsedItems == 0); + foreach my $inputFile (@inputFiles) { + my $file = new IO::File; + open($file, $inputFile) or die "Failed to open file: $!"; + + $InParser->parse($file, \¶meterHandler, \&itemHandler); + + close($file); + die "Failed to read from file: $inputFile" if (keys %parsedItems == 0); + } &$generateCode(\%parsedParameters, \%parsedItems); } @@ -130,7 +133,7 @@ sub license() * THIS SOFTWARE IS PROVIDED BY GOOGLE, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR diff --git a/Source/WebCore/bindings/scripts/InFilesParser.pm b/Source/WebCore/bindings/scripts/InFilesParser.pm index 65393807f..75db11999 100644 --- a/Source/WebCore/bindings/scripts/InFilesParser.pm +++ b/Source/WebCore/bindings/scripts/InFilesParser.pm @@ -11,10 +11,10 @@ # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # -# THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY +# THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR diff --git a/Source/WebCore/bindings/scripts/StaticString.pm b/Source/WebCore/bindings/scripts/StaticString.pm index b8feaa57e..9ba0c70a8 100644 --- a/Source/WebCore/bindings/scripts/StaticString.pm +++ b/Source/WebCore/bindings/scripts/StaticString.pm @@ -33,27 +33,26 @@ sub GenerateStrings($) my @result = (); - while ( my ($name, $value) = each %strings ) { - push(@result, "static const LChar ${name}String8[] = \"${value}\";\n"); - } - - push(@result, "\n"); + push(@result, <<END); +#if COMPILER(MSVC) +#pragma warning(push) +#pragma warning(disable: 4307) +#endif - while ( my ($name, $value) = each %strings ) { - my $length = length($value); - my $hash = Hasher::GenerateHashValue($value); - push(@result, <<END); -static StringImpl::StaticASCIILiteral ${name}Data = { - StringImpl::StaticASCIILiteral::s_initialRefCount, - $length, - ${name}String8, - 0, - StringImpl::StaticASCIILiteral::s_initialFlags | (${hash} << StringImpl::StaticASCIILiteral::s_hashShift) -}; END + + for my $name (sort keys %strings) { + my $value = $strings{$name}; + push(@result, "static StringImpl::StaticStringImpl ${name}Data(\"${value}\");\n"); } - push(@result, "\n"); + push(@result, <<END); + +#if COMPILER(MSVC) +#pragma warning(pop) +#endif + +END return join "", @result; } @@ -67,7 +66,7 @@ sub GenerateStringAsserts($) push(@result, "#ifndef NDEBUG\n"); - while ( my ($name, $value) = each %strings ) { + for my $name (sort keys %strings) { push(@result, " reinterpret_cast<StringImpl*>(&${name}Data)->assertHashIsCorrect();\n"); } diff --git a/Source/WebCore/bindings/scripts/generate-bindings-all.pl b/Source/WebCore/bindings/scripts/generate-bindings-all.pl new file mode 100755 index 000000000..968ea113e --- /dev/null +++ b/Source/WebCore/bindings/scripts/generate-bindings-all.pl @@ -0,0 +1,245 @@ +#!/usr/bin/perl +# +# Copyright (C) 2016 Sony Interactive Entertainment Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +use strict; +use warnings; +use FindBin; +use lib $FindBin::Bin; + +use File::Basename; +use File::Spec; +use File::Find; +use Getopt::Long; + +my $perl = $^X; +my $scriptDir = $FindBin::Bin; +my @idlDirectories; +my $outputDirectory; +my $idlFilesList; +my $generator; +my @generatorDependency; +my $defines; +my $preprocessor; +my $supplementalDependencyFile; +my @ppExtraOutput; +my @ppExtraArgs; +my $numOfJobs = 1; +my $idlAttributesFile; +my $showProgress; + +GetOptions('include=s@' => \@idlDirectories, + 'outputDir=s' => \$outputDirectory, + 'idlFilesList=s' => \$idlFilesList, + 'generator=s' => \$generator, + 'generatorDependency=s@' => \@generatorDependency, + 'defines=s' => \$defines, + 'preprocessor=s' => \$preprocessor, + 'supplementalDependencyFile=s' => \$supplementalDependencyFile, + 'ppExtraOutput=s@' => \@ppExtraOutput, + 'ppExtraArgs=s@' => \@ppExtraArgs, + 'idlAttributesFile=s' => \$idlAttributesFile, + 'numOfJobs=i' => \$numOfJobs, + 'showProgress' => \$showProgress); + +$| = 1; +my @idlFiles; +open(my $fh, '<', $idlFilesList) or die "Cannot open $idlFilesList"; +@idlFiles = map { CygwinPathIfNeeded(s/\r?\n?$//r) } <$fh>; +close($fh) or die; + +my %oldSupplements; +my %newSupplements; +if ($supplementalDependencyFile) { + my @output = ($supplementalDependencyFile, @ppExtraOutput); + my @deps = ($idlFilesList, @idlFiles, @generatorDependency); + if (needsUpdate(\@output, \@deps)) { + readSupplementalDependencyFile($supplementalDependencyFile, \%oldSupplements) if -e $supplementalDependencyFile; + my @args = (File::Spec->catfile($scriptDir, 'preprocess-idls.pl'), + '--defines', $defines, + '--idlFilesList', $idlFilesList, + '--supplementalDependencyFile', $supplementalDependencyFile, + @ppExtraArgs); + printProgress("Preprocess IDL"); + executeCommand($perl, @args) == 0 or die; + } + readSupplementalDependencyFile($supplementalDependencyFile, \%newSupplements); +} + +my @args = (File::Spec->catfile($scriptDir, 'generate-bindings.pl'), + '--defines', $defines, + '--generator', $generator, + '--outputDir', $outputDirectory, + '--preprocessor', $preprocessor, + '--idlAttributesFile', $idlAttributesFile, + '--write-dependencies'); +push @args, map { ('--include', $_) } @idlDirectories; +push @args, '--supplementalDependencyFile', $supplementalDependencyFile if $supplementalDependencyFile; + +my %directoryCache; +buildDirectoryCache(); + +my @idlFilesToUpdate = grep &{sub { + if (defined($oldSupplements{$_}) + && @{$oldSupplements{$_}} ne @{$newSupplements{$_} or []}) { + # Re-process the IDL file if its supplemental dependencies were added or removed + return 1; + } + my ($filename, $dirs, $suffix) = fileparse($_, '.idl'); + my $sourceFile = File::Spec->catfile($outputDirectory, "JS$filename.cpp"); + my $headerFile = File::Spec->catfile($outputDirectory, "JS$filename.h"); + my $depFile = File::Spec->catfile($outputDirectory, "JS$filename.dep"); + my @output = ($sourceFile, $headerFile); + my @deps = ($_, + $idlAttributesFile, + @generatorDependency, + @{$newSupplements{$_} or []}, + implicitDependencies($depFile)); + needsUpdate(\@output, \@deps); +}}, @idlFiles; + +my $abort = 0; +my $totalCount = @idlFilesToUpdate; +my $currentCount = 0; + +spawnGenerateBindingsIfNeeded() for (1 .. $numOfJobs); +while (waitpid(-1, 0) != -1) { + if ($?) { + $abort = 1; + } + spawnGenerateBindingsIfNeeded(); +} +exit $abort; + +sub needsUpdate +{ + my ($objects, $depends) = @_; + my $oldestObjectTime; + for (@$objects) { + return 1 if !-f; + my $m = mtime($_); + if (!defined $oldestObjectTime || $m < $oldestObjectTime) { + $oldestObjectTime = $m; + } + } + for (@$depends) { + die "Missing required dependency: $_" if !-f; + my $m = mtime($_); + if ($oldestObjectTime < $m) { + return 1; + } + } + return 0; +} + +sub mtime +{ + my ($file) = @_; + return (stat $file)[9]; +} + +sub spawnGenerateBindingsIfNeeded +{ + return if $abort; + return unless @idlFilesToUpdate; + my $file = shift @idlFilesToUpdate; + $currentCount++; + my $basename = basename($file); + printProgress("[$currentCount/$totalCount] $basename"); + my $pid = spawnCommand($perl, @args, $file); + $abort = 1 unless defined $pid; +} + +sub buildDirectoryCache +{ + my $wanted = sub { + $directoryCache{$_} = $File::Find::name; + $File::Find::prune = 1 unless ~/\./; + }; + find($wanted, @idlDirectories); +} + +sub implicitDependencies +{ + my ($depFile) = @_; + return () unless -f $depFile; + open(my $fh, '<', $depFile) or die "Cannot open $depFile"; + my $firstLine = <$fh>; + close($fh) or die; + my (undef, $deps) = split(/ : /, $firstLine); + my @deps = split(/\s+/, $deps); + return map { $directoryCache{$_} or () } @deps; +} + +sub executeCommand +{ + if ($^O eq 'MSWin32') { + return system(quoteCommand(@_)); + } + return system(@_); +} + +sub spawnCommand +{ + my $pid = fork(); + if ($pid == 0) { + @_ = quoteCommand(@_) if ($^O eq 'MSWin32'); + exec(@_); + die "Cannot exec"; + } + return $pid; +} + +sub quoteCommand +{ + return map { + '"' . s/([\\\"])/\\$1/gr . '"'; + } @_; +} + +sub CygwinPathIfNeeded +{ + my $path = shift; + return Cygwin::win_to_posix_path($path) if ($^O eq 'cygwin'); + return $path; +} + +sub readSupplementalDependencyFile +{ + my $filename = shift; + my $supplements = shift; + open(my $fh, '<', $filename) or die "Cannot open $filename"; + while (<$fh>) { + my ($idlFile, @followingIdlFiles) = split(/\s+/); + $supplements->{$idlFile} = [sort @followingIdlFiles]; + } + close($fh) or die; +} + +sub printProgress +{ + return unless $showProgress; + my $msg = shift; + print "$msg\n"; +} diff --git a/Source/WebCore/bindings/scripts/generate-bindings.pl b/Source/WebCore/bindings/scripts/generate-bindings.pl index f93434e40..1048a512e 100755 --- a/Source/WebCore/bindings/scripts/generate-bindings.pl +++ b/Source/WebCore/bindings/scripts/generate-bindings.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# Copyright (C) 2005 Apple Computer, Inc. +# Copyright (C) 2005 Apple Inc. # Copyright (C) 2006 Anders Carlsson <andersca@mac.com> # # This file is part of WebKit @@ -29,6 +29,8 @@ # <rdar://problems/4251781&4251785> use strict; +use FindBin; +use lib '.', $FindBin::Bin; use File::Path; use File::Basename; @@ -80,7 +82,7 @@ $targetIdlFile = Cwd::realpath($targetIdlFile); if ($verbose) { print "$generator: $targetIdlFile\n"; } -my $targetInterfaceName = fileparse(basename($targetIdlFile), ".idl"); +my $targetInterfaceName = fileparse($targetIdlFile, ".idl"); my $idlFound = 0; my @supplementedIdlFiles; @@ -98,7 +100,7 @@ if ($supplementalDependencyFile) { open FH, "< $supplementalDependencyFile" or die "Cannot open $supplementalDependencyFile\n"; while (my $line = <FH>) { my ($idlFile, @followingIdlFiles) = split(/\s+/, $line); - if ($idlFile and basename($idlFile) eq basename($targetIdlFile)) { + if ($idlFile and fileparse($idlFile) eq fileparse($targetIdlFile)) { $idlFound = 1; @supplementedIdlFiles = sort @followingIdlFiles; } @@ -110,11 +112,11 @@ if ($supplementalDependencyFile) { # dependency file) but should generate .h and .cpp files. if (!$idlFound and $additionalIdlFiles) { my @idlFiles = shellwords($additionalIdlFiles); - $idlFound = grep { $_ and basename($_) eq basename($targetIdlFile) } @idlFiles; + $idlFound = grep { $_ and fileparse($_) eq fileparse($targetIdlFile) } @idlFiles; } if (!$idlFound) { - my $codeGen = CodeGenerator->new(\@idlDirectories, $generator, $outputDirectory, $outputHeadersDirectory, 0, $preprocessor, $writeDependencies, $verbose); + my $codeGen = CodeGenerator->new(\@idlDirectories, $generator, $outputDirectory, $outputHeadersDirectory, $preprocessor, $writeDependencies, $verbose); # We generate empty .h and .cpp files just to tell build scripts that .h and .cpp files are created. generateEmptyHeaderAndCpp($codeGen->FileNamePrefix(), $targetInterfaceName, $outputHeadersDirectory, $outputDirectory); @@ -128,22 +130,26 @@ my $targetDocument = $targetParser->Parse($targetIdlFile, $defines, $preprocesso if ($idlAttributesFile) { my $idlAttributes = loadIDLAttributes($idlAttributesFile); - checkIDLAttributes($idlAttributes, $targetDocument, basename($targetIdlFile)); + checkIDLAttributes($idlAttributes, $targetDocument, fileparse($targetIdlFile)); } foreach my $idlFile (@supplementedIdlFiles) { next if $idlFile eq $targetIdlFile; - my $interfaceName = fileparse(basename($idlFile), ".idl"); + my $interfaceName = fileparse($idlFile, ".idl"); my $parser = IDLParser->new(!$verbose); my $document = $parser->Parse($idlFile, $defines, $preprocessor); foreach my $interface (@{$document->interfaces}) { - if (!$interface->isPartial || $interface->name eq $targetInterfaceName) { + if (!$interface->isPartial || $interface->type->name eq $targetInterfaceName) { my $targetDataNode; + my @targetGlobalContexts; foreach my $interface (@{$targetDocument->interfaces}) { - if ($interface->name eq $targetInterfaceName) { + if ($interface->type->name eq $targetInterfaceName) { $targetDataNode = $interface; + my $exposedAttribute = $targetDataNode->extendedAttributes->{"Exposed"} || "Window"; + $exposedAttribute = substr($exposedAttribute, 1, -1) if substr($exposedAttribute, 0, 1) eq "("; + @targetGlobalContexts = split(",", $exposedAttribute); last; } } @@ -151,30 +157,36 @@ foreach my $idlFile (@supplementedIdlFiles) { # Support for attributes of partial interfaces. foreach my $attribute (@{$interface->attributes}) { + next unless shouldPropertyBeExposed($attribute, \@targetGlobalContexts); + # Record that this attribute is implemented by $interfaceName. - $attribute->signature->extendedAttributes->{"ImplementedBy"} = $interfaceName if $interface->isPartial; + $attribute->extendedAttributes->{"ImplementedBy"} = $interfaceName if $interface->isPartial; # Add interface-wide extended attributes to each attribute. foreach my $extendedAttributeName (keys %{$interface->extendedAttributes}) { - $attribute->signature->extendedAttributes->{$extendedAttributeName} = $interface->extendedAttributes->{$extendedAttributeName}; + $attribute->extendedAttributes->{$extendedAttributeName} = $interface->extendedAttributes->{$extendedAttributeName}; } push(@{$targetDataNode->attributes}, $attribute); } # Support for methods of partial interfaces. foreach my $function (@{$interface->functions}) { + next unless shouldPropertyBeExposed($function, \@targetGlobalContexts); + # Record that this method is implemented by $interfaceName. - $function->signature->extendedAttributes->{"ImplementedBy"} = $interfaceName if $interface->isPartial; + $function->extendedAttributes->{"ImplementedBy"} = $interfaceName if $interface->isPartial; # Add interface-wide extended attributes to each method. foreach my $extendedAttributeName (keys %{$interface->extendedAttributes}) { - $function->signature->extendedAttributes->{$extendedAttributeName} = $interface->extendedAttributes->{$extendedAttributeName}; + $function->extendedAttributes->{$extendedAttributeName} = $interface->extendedAttributes->{$extendedAttributeName}; } push(@{$targetDataNode->functions}, $function); } # Support for constants of partial interfaces. foreach my $constant (@{$interface->constants}) { + next unless shouldPropertyBeExposed($constant, \@targetGlobalContexts); + # Record that this constant is implemented by $interfaceName. $constant->extendedAttributes->{"ImplementedBy"} = $interfaceName if $interface->isPartial; @@ -191,9 +203,28 @@ foreach my $idlFile (@supplementedIdlFiles) { } # Generate desired output for the target IDL file. -my $codeGen = CodeGenerator->new(\@idlDirectories, $generator, $outputDirectory, $outputHeadersDirectory, 0, $preprocessor, $writeDependencies, $verbose, $targetIdlFile); +my $codeGen = CodeGenerator->new(\@idlDirectories, $generator, $outputDirectory, $outputHeadersDirectory, $preprocessor, $writeDependencies, $verbose, $targetIdlFile); $codeGen->ProcessDocument($targetDocument, $defines); +# Attributes / Operations / Constants of supplemental interfaces can have an [Exposed=XX] attribute which restricts +# on which global contexts they should be exposed. +sub shouldPropertyBeExposed +{ + my ($context, $targetGlobalContexts) = @_; + + my $exposed = $context->extendedAttributes->{Exposed}; + + return 1 unless $exposed; + + $exposed = substr($exposed, 1, -1) if substr($exposed, 0, 1) eq "("; + my @sourceGlobalContexts = split(",", $exposed); + + for my $targetGlobalContext (@$targetGlobalContexts) { + return 1 if grep(/^$targetGlobalContext$/, @sourceGlobalContexts); + } + return 0; +} + sub generateEmptyHeaderAndCpp { my ($prefix, $targetInterfaceName, $outputHeadersDirectory, $outputDirectory) = @_; @@ -240,7 +271,7 @@ sub loadIDLAttributes $idlAttributes{$name}{"VALUE_IS_MISSING"} = 1; } } else { - die "The format of " . basename($idlAttributesFile) . " is wrong: line $.\n"; + die "The format of " . fileparse($idlAttributesFile) . " is wrong: line $.\n"; } } close FH; @@ -258,13 +289,13 @@ sub checkIDLAttributes checkIfIDLAttributesExists($idlAttributes, $interface->extendedAttributes, $idlFile); foreach my $attribute (@{$interface->attributes}) { - checkIfIDLAttributesExists($idlAttributes, $attribute->signature->extendedAttributes, $idlFile); + checkIfIDLAttributesExists($idlAttributes, $attribute->extendedAttributes, $idlFile); } foreach my $function (@{$interface->functions}) { - checkIfIDLAttributesExists($idlAttributes, $function->signature->extendedAttributes, $idlFile); - foreach my $parameter (@{$function->parameters}) { - checkIfIDLAttributesExists($idlAttributes, $parameter->extendedAttributes, $idlFile); + checkIfIDLAttributesExists($idlAttributes, $function->extendedAttributes, $idlFile); + foreach my $argument (@{$function->arguments}) { + checkIfIDLAttributesExists($idlAttributes, $argument->extendedAttributes, $idlFile); } } } diff --git a/Source/WebCore/bindings/scripts/gobject-generate-headers.pl b/Source/WebCore/bindings/scripts/gobject-generate-headers.pl deleted file mode 100644 index b39cf253a..000000000 --- a/Source/WebCore/bindings/scripts/gobject-generate-headers.pl +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/perl -w -# -# Copyright (C) 2009 Adam Dingle <adam@yorba.org> -# -# This file is part of WebKit -# -# 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. -# - -my $classlist = <STDIN>; -chomp($classlist); -my @classes = split / /, $classlist; -@classes = sort @classes; - -print <<EOF; -/* This file is part of the WebKit open source project. - This file has been generated by gobject-generate-headers.pl. DO NOT MODIFY! - - 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 - along 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. -*/ - -EOF - -my $outType = $ARGV[0]; -my $header; -if ($outType eq "defines") { - $header = "webkitdomdefines_h"; -} elsif ($outType eq "gdom") { - $header = "webkitdom_h"; -} else { - die "unknown output type"; -} - -print "#ifndef ${header}\n"; -print "#define ${header}\n"; -print "\n"; - -if ($outType eq "defines") { - print "#include <glib.h>\n\n"; - print "#ifdef G_OS_WIN32\n"; - print " #ifdef BUILDING_WEBKIT\n"; - print " #define WEBKIT_API __declspec(dllexport)\n"; - print " #else\n"; - print " #define WEBKIT_API __declspec(dllimport)\n"; - print " #endif\n"; - print "#else\n"; - print " #define WEBKIT_API __attribute__((visibility(\"default\")))\n"; - print "#endif\n\n"; - print "#define WEBKIT_DEPRECATED WEBKIT_API G_DEPRECATED\n"; - print "#define WEBKIT_DEPRECATED_FOR(f) WEBKIT_API G_DEPRECATED_FOR(f)\n"; - print "\n"; - print "#ifndef WEBKIT_API\n"; - print " #define WEBKIT_API\n"; - print "#endif\n"; - - foreach my $class (@classes) { - if ($class ne "Deprecated" && $class ne "Custom") { - print "typedef struct _WebKitDOM${class} WebKitDOM${class};\n"; - print "typedef struct _WebKitDOM${class}Class WebKitDOM${class}Class;\n"; - print "\n"; - } - } -} elsif ($outType eq "gdom") { - print "#define __WEBKITDOM_H_INSIDE__\n\n"; - foreach my $class (@classes) { - print "#include <webkitdom/WebKitDOM${class}.h>\n"; - } - print "\n#undef __WEBKITDOM_H_INSIDE__\n"; -} - -print "\n"; -print "#endif\n"; diff --git a/Source/WebCore/bindings/scripts/preprocess-idls.pl b/Source/WebCore/bindings/scripts/preprocess-idls.pl index d65df33cf..a984ab7c5 100644 --- a/Source/WebCore/bindings/scripts/preprocess-idls.pl +++ b/Source/WebCore/bindings/scripts/preprocess-idls.pl @@ -19,10 +19,13 @@ # use strict; +use FindBin; +use lib $FindBin::Bin; use File::Basename; use Getopt::Long; use Cwd; +use Config; my $defines; my $preprocessor; @@ -30,7 +33,6 @@ my $idlFilesList; my $supplementalDependencyFile; my $windowConstructorsFile; my $workerGlobalScopeConstructorsFile; -my $sharedWorkerGlobalScopeConstructorsFile; my $dedicatedWorkerGlobalScopeConstructorsFile; my $supplementalMakefileDeps; @@ -40,7 +42,6 @@ GetOptions('defines=s' => \$defines, 'supplementalDependencyFile=s' => \$supplementalDependencyFile, 'windowConstructorsFile=s' => \$windowConstructorsFile, 'workerGlobalScopeConstructorsFile=s' => \$workerGlobalScopeConstructorsFile, - 'sharedWorkerGlobalScopeConstructorsFile=s' => \$sharedWorkerGlobalScopeConstructorsFile, 'dedicatedWorkerGlobalScopeConstructorsFile=s' => \$dedicatedWorkerGlobalScopeConstructorsFile, 'supplementalMakefileDeps=s' => \$supplementalMakefileDeps); @@ -48,13 +49,22 @@ die('Must specify #define macros using --defines.') unless defined($defines); die('Must specify an output file using --supplementalDependencyFile.') unless defined($supplementalDependencyFile); die('Must specify an output file using --windowConstructorsFile.') unless defined($windowConstructorsFile); die('Must specify an output file using --workerGlobalScopeConstructorsFile.') unless defined($workerGlobalScopeConstructorsFile); -die('Must specify an output file using --sharedWorkerGlobalScopeConstructorsFile.') unless defined($sharedWorkerGlobalScopeConstructorsFile); die('Must specify an output file using --dedicatedWorkerGlobalScopeConstructorsFile.') unless defined($dedicatedWorkerGlobalScopeConstructorsFile); die('Must specify the file listing all IDLs using --idlFilesList.') unless defined($idlFilesList); +$supplementalDependencyFile = CygwinPathIfNeeded($supplementalDependencyFile); +$windowConstructorsFile = CygwinPathIfNeeded($windowConstructorsFile); +$workerGlobalScopeConstructorsFile = CygwinPathIfNeeded($workerGlobalScopeConstructorsFile); +$dedicatedWorkerGlobalScopeConstructorsFile = CygwinPathIfNeeded($dedicatedWorkerGlobalScopeConstructorsFile); +$supplementalMakefileDeps = CygwinPathIfNeeded($supplementalMakefileDeps); + open FH, "< $idlFilesList" or die "Cannot open $idlFilesList\n"; -my @idlFiles = <FH>; -chomp(@idlFiles); +my @idlFilesIn = <FH>; +chomp(@idlFilesIn); +my @idlFiles = (); +foreach (@idlFilesIn) { + push @idlFiles, CygwinPathIfNeeded($_); +} close FH; my %interfaceNameToIdlFile; @@ -63,14 +73,13 @@ my %supplementalDependencies; my %supplementals; my $windowConstructorsCode = ""; my $workerGlobalScopeConstructorsCode = ""; -my $sharedWorkerGlobalScopeConstructorsCode = ""; my $dedicatedWorkerGlobalScopeConstructorsCode = ""; # Get rid of duplicates in idlFiles array. my %idlFileHash = map { $_, 1 } @idlFiles; # Populate $idlFileToInterfaceName and $interfaceNameToIdlFile. -foreach my $idlFile (keys %idlFileHash) { +foreach my $idlFile (sort keys %idlFileHash) { my $fullPath = Cwd::realpath($idlFile); my $interfaceName = fileparse(basename($idlFile), ".idl"); $idlFileToInterfaceName{$fullPath} = $interfaceName; @@ -87,6 +96,13 @@ foreach my $idlFile (sort keys %idlFileHash) { $supplementalDependencies{$fullPath} = [$partialInterfaceName]; next; } + + $supplementals{$fullPath} = []; + + # Skip if the IDL file does not contain an interface, a callback interface or an exception. + # The IDL may contain a dictionary. + next unless containsInterfaceOrExceptionFromIDL($idlFileContents); + my $interfaceName = fileparse(basename($idlFile), ".idl"); # Handle implements statements. my $implementedInterfaces = getImplementedInterfacesFromIDL($idlFileContents, $interfaceName); @@ -99,29 +115,41 @@ foreach my $idlFile (sort keys %idlFileHash) { $supplementalDependencies{$implementedIdlFile} = [$interfaceName]; } } - # Handle [NoInterfaceObject]. - unless (isCallbackInterfaceFromIDL($idlFileContents)) { - my $extendedAttributes = getInterfaceExtendedAttributesFromIDL($idlFileContents); - unless ($extendedAttributes->{"NoInterfaceObject"}) { - my @globalContexts = split("&", $extendedAttributes->{"GlobalContext"} || "DOMWindow"); + + # For every interface that is exposed in a given ECMAScript global environment and: + # - is a callback interface that has constants declared on it, or + # - is a non-callback interface that is not declared with the [NoInterfaceObject] extended attribute, a corresponding + # property must exist on the ECMAScript environment's global object. + # See https://heycam.github.io/webidl/#es-interfaces + my $extendedAttributes = getInterfaceExtendedAttributesFromIDL($idlFileContents); + unless ($extendedAttributes->{"NoInterfaceObject"}) { + if (!isCallbackInterfaceFromIDL($idlFileContents) || interfaceHasConstantAttribute($idlFileContents)) { + my $exposedAttribute = $extendedAttributes->{"Exposed"} || "Window"; + $exposedAttribute = substr($exposedAttribute, 1, -1) if substr($exposedAttribute, 0, 1) eq "("; + my @globalContexts = split(",", $exposedAttribute); my $attributeCode = GenerateConstructorAttribute($interfaceName, $extendedAttributes); - $windowConstructorsCode .= $attributeCode if grep(/^DOMWindow$/, @globalContexts); - $workerGlobalScopeConstructorsCode .= $attributeCode if grep(/^WorkerGlobalScope$/, @globalContexts); - $sharedWorkerGlobalScopeConstructorsCode .= $attributeCode if grep(/^SharedWorkerGlobalScope$/, @globalContexts); - $dedicatedWorkerGlobalScopeConstructorsCode .= $attributeCode if grep(/^DedicatedWorkerGlobalScope$/, @globalContexts); + foreach my $globalContext (@globalContexts) { + if ($globalContext eq "Window") { + $windowConstructorsCode .= $attributeCode; + } elsif ($globalContext eq "Worker") { + $workerGlobalScopeConstructorsCode .= $attributeCode; + } elsif ($globalContext eq "DedicatedWorker") { + $dedicatedWorkerGlobalScopeConstructorsCode .= $attributeCode; + } else { + die "Unsupported global context '$globalContext' used in [Exposed] at $idlFile"; + } + } } } - $supplementals{$fullPath} = []; } # Generate partial interfaces for Constructors. GeneratePartialInterface("DOMWindow", $windowConstructorsCode, $windowConstructorsFile); GeneratePartialInterface("WorkerGlobalScope", $workerGlobalScopeConstructorsCode, $workerGlobalScopeConstructorsFile); -GeneratePartialInterface("SharedWorkerGlobalScope", $sharedWorkerGlobalScopeConstructorsCode, $sharedWorkerGlobalScopeConstructorsFile); GeneratePartialInterface("DedicatedWorkerGlobalScope", $dedicatedWorkerGlobalScopeConstructorsCode, $dedicatedWorkerGlobalScopeConstructorsFile); # Resolves partial interfaces and implements dependencies. -foreach my $idlFile (keys %supplementalDependencies) { +foreach my $idlFile (sort keys %supplementalDependencies) { my $baseFiles = $supplementalDependencies{$idlFile}; foreach my $baseFile (@{$baseFiles}) { my $targetIdlFile = $interfaceNameToIdlFile{$baseFile}; @@ -165,6 +193,21 @@ if ($supplementalMakefileDeps) { WriteFileIfChanged($supplementalMakefileDeps, $makefileDeps); } +my $cygwinPathAdded; +sub CygwinPathIfNeeded +{ + my $path = shift; + if ($path && $Config{osname} eq "cygwin") { + if (not $cygwinPathAdded) { + $ENV{PATH} = "$ENV{PATH}:/cygdrive/c/cygwin/bin"; + $cygwinPathAdded = 1; + } + chomp($path = `cygpath -u '$path'`); + $path =~ s/[\r\n]//; + } + return $path; +} + sub WriteFileIfChanged { my $fileName = shift; @@ -202,8 +245,8 @@ sub GenerateConstructorAttribute my $code = " "; my @extendedAttributesList; - foreach my $attributeName (keys %{$extendedAttributes}) { - next unless ($attributeName eq "Conditional" || $attributeName eq "EnabledAtRuntime" || $attributeName eq "EnabledBySetting"); + foreach my $attributeName (sort keys %{$extendedAttributes}) { + next unless ($attributeName eq "Conditional" || $attributeName eq "EnabledAtRuntime" || $attributeName eq "EnabledForWorld" || $attributeName eq "EnabledBySetting" || $attributeName eq "PrivateIdentifier" || $attributeName eq "PublicIdentifier"); my $extendedAttribute = $attributeName; $extendedAttribute .= "=" . $extendedAttributes->{$attributeName} unless $extendedAttributes->{$attributeName} eq "VALUE_IS_MISSING"; push(@extendedAttributesList, $extendedAttribute); @@ -270,6 +313,16 @@ sub isCallbackInterfaceFromIDL return ($fileContents =~ /callback\s+interface\s+\w+/gs); } +sub containsInterfaceOrExceptionFromIDL +{ + my $fileContents = shift; + + return 1 if $fileContents =~ /\bcallback\s+interface\s+\w+/gs; + return 1 if $fileContents =~ /\binterface\s+\w+/gs; + return 1 if $fileContents =~ /\bexception\s+\w+/gs; + return 0; +} + sub trim { my $string = shift; @@ -283,8 +336,13 @@ sub getInterfaceExtendedAttributesFromIDL my $extendedAttributes = {}; - if ($fileContents =~ /\[(.*)\]\s+(interface|exception)\s+(\w+)/gs) { - my @parts = split(',', $1); + # Remove comments from fileContents before processing. + # FIX: Preference to use Regex::Common::comment, however it is not available on + # all build systems. + $fileContents =~ s/(?:(?:(?:\/\/)(?:[^\n]*)(?:\n))|(?:(?:\/\*)(?:(?:[^\*]+|\*(?!\/))*)(?:\*\/)))//g; + + if ($fileContents =~ /\[(.*)\]\s+(callback interface|interface|exception)\s+(\w+)/gs) { + my @parts = split(m/,(?![^()]*\))/, $1); foreach my $part (@parts) { my @keyValue = split('=', $part); my $key = trim($keyValue[0]); @@ -297,3 +355,10 @@ sub getInterfaceExtendedAttributesFromIDL return $extendedAttributes; } + +sub interfaceHasConstantAttribute +{ + my $fileContents = shift; + + return $fileContents =~ /\s+const[\s\w]+=\s+[\w]+;/gs; +} diff --git a/Source/WebCore/bindings/scripts/preprocessor.pm b/Source/WebCore/bindings/scripts/preprocessor.pm index d92590d77..2321d5191 100644 --- a/Source/WebCore/bindings/scripts/preprocessor.pm +++ b/Source/WebCore/bindings/scripts/preprocessor.pm @@ -51,18 +51,20 @@ sub applyPreprocessor $preprocessor = "/usr/sfw/bin/gcc"; } elsif (-x "/usr/bin/clang") { $preprocessor = "/usr/bin/clang"; - } elsif ($Config{osname} eq 'msys') { - $preprocessor = "gcc"; } else { $preprocessor = "/usr/bin/gcc"; } - push(@args, qw(-E -P -x c++)); + if ($Config::Config{"osname"} eq "MSWin32") { + push(@args, qw(/EP)); + } else { + push(@args, qw(-E -P -x c++)); + } } if ($Config::Config{"osname"} eq "darwin") { push(@args, "-I" . $ENV{BUILT_PRODUCTS_DIR} . "/usr/local/include") if $ENV{BUILT_PRODUCTS_DIR}; push(@args, "-isysroot", $ENV{SDKROOT}) if $ENV{SDKROOT}; - $defines .= " WTF_PLATFORM_IOS" if defined $ENV{PLATFORM_NAME} && $ENV{PLATFORM_NAME} =~ /iphone(os|simulator)/; + $defines .= " WTF_PLATFORM_IOS" if defined $ENV{PLATFORM_NAME} && $ENV{PLATFORM_NAME} !~ /macosx/; } # Remove double quotations from $defines and extract macros. @@ -73,19 +75,34 @@ sub applyPreprocessor @macros = map { "-D$_" } @macros; my $pid = 0; - if ($Config{osname} eq "cygwin" || $Config{osname} eq 'MSWin32' || $Config{osname} eq 'msys') { + if ($Config{osname} eq "cygwin") { + $ENV{PATH} = "$ENV{PATH}:/cygdrive/c/cygwin/bin"; + my @preprocessorAndFlags; + if ($preprocessor eq "/usr/bin/gcc") { + @preprocessorAndFlags = split(' ', $preprocessor); + } else { + $preprocessor =~ /"(.*)"/; + chomp(my $preprocessor = `cygpath -u '$1'`) if (defined $1); + chomp($fileName = `cygpath -w '$fileName'`); + @preprocessorAndFlags = ($preprocessor, "/nologo", "/EP"); + } # This call can fail if Windows rebases cygwin, so retry a few times until it succeeds. for (my $tries = 0; !$pid && ($tries < 20); $tries++) { eval { # Suppress STDERR so that if we're using cl.exe, the output # name isn't needlessly echoed. use Symbol 'gensym'; my $err = gensym; - $pid = open3(\*PP_IN, \*PP_OUT, $err, split(' ', $preprocessor), @args, @macros, $fileName); + $pid = open3(\*PP_IN, \*PP_OUT, $err, @preprocessorAndFlags, @args, @macros, $fileName); 1; } or do { sleep 1; } }; + } elsif ($Config::Config{"osname"} eq "MSWin32") { + # Suppress STDERR so that if we're using cl.exe, the output + # name isn't needlessly echoed. + use Symbol 'gensym'; my $err = gensym; + $pid = open3(\*PP_IN, \*PP_OUT, $err, $preprocessor, @args, @macros, $fileName); } else { $pid = open2(\*PP_OUT, \*PP_IN, split(' ', $preprocessor), @args, @macros, $fileName); } |