diff options
Diffstat (limited to 'Source/WebCore/bindings/scripts/CodeGeneratorJS.pm')
-rw-r--r-- | Source/WebCore/bindings/scripts/CodeGeneratorJS.pm | 7226 |
1 files changed, 4629 insertions, 2597 deletions
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; |