summaryrefslogtreecommitdiff
path: root/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/bindings/scripts/CodeGeneratorJS.pm')
-rw-r--r--Source/WebCore/bindings/scripts/CodeGeneratorJS.pm7226
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;