2 # Copyright (C) 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 # Copyright (C) 2006 Anders Carlsson <andersca@mac.com>
4 # Copyright (C) 2006, 2007 Samuel Weinig <sam@webkit.org>
5 # Copyright (C) 2006 Alexey Proskuryakov <ap@webkit.org>
6 # Copyright (C) 2006-2018 Apple Inc. All rights reserved.
7 # Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au>
8 # Copyright (C) Research In Motion Limited 2010. All rights reserved.
9 # Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
10 # Copyright (C) 2011 Patrick Gansterer <paroga@webkit.org>
11 # Copyright (C) 2012 Ericsson AB. All rights reserved.
12 # Copyright (C) 2007, 2008, 2009, 2012 Google Inc.
13 # Copyright (C) 2013 Samsung Electronics. All rights reserved.
14 # Copyright (C) 2015, 2016 Canon Inc. All rights reserved.
16 # This library is free software; you can redistribute it and/or
17 # modify it under the terms of the GNU Library General Public
18 # License as published by the Free Software Foundation; either
19 # version 2 of the License, or (at your option) any later version.
21 # This library is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 # Library General Public License for more details.
26 # You should have received a copy of the GNU Library General Public License
27 # along with this library; see the file COPYING.LIB. If not, write to
28 # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29 # Boston, MA 02110-1301, USA.
32 package CodeGeneratorJS;
35 use constant FileNamePrefix => "JS";
36 use Carp qw<longmess>;
41 my $writeDependencies;
43 my @headerContentHeader = ();
44 my @headerContent = ();
45 my %headerIncludes = ();
46 my %headerTrailingIncludes = ();
48 my @implContentHeader = ();
50 my %implIncludes = ();
52 my $numCachedAttributes = 0;
54 my $beginAppleCopyrightForHeaderFiles = <<END;
55 // ------- Begin Apple Copyright -------
57 * Copyright (C) 2008 Apple Inc. All rights reserved.
59 * Permission is granted by Apple to use this file to the extent
60 * necessary to relink with LGPL WebKit files.
62 * No license or rights are granted by Apple expressly or by
63 * implication, estoppel, or otherwise, to Apple patents and
64 * trademarks. For the sake of clarity, no license or rights are
65 * granted by Apple expressly or by implication, estoppel, or otherwise,
66 * under any Apple patents, copyrights and trademarks to underlying
67 * implementations of any application programming interfaces (APIs)
68 * or to any functionality that is invoked by calling any API.
72 my $beginAppleCopyrightForSourceFiles = <<END;
73 // ------- Begin Apple Copyright -------
75 * Copyright (C) 2008 Apple Inc. All rights reserved.
77 * No license or rights are granted by Apple expressly or by implication,
78 * estoppel, or otherwise, to Apple copyrights, patents, trademarks, trade
79 * secrets or other rights.
83 my $endAppleCopyright = <<END;
84 // ------- End Apple Copyright -------
89 my $headerTemplate = << "EOF";
91 This file is part of the WebKit open source project.
92 This file has been generated by generate-bindings.pl. DO NOT MODIFY!
94 This library is free software; you can redistribute it and/or
95 modify it under the terms of the GNU Library General Public
96 License as published by the Free Software Foundation; either
97 version 2 of the License, or (at your option) any later version.
99 This library is distributed in the hope that it will be useful,
100 but WITHOUT ANY WARRANTY; without even the implied warranty of
101 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
102 Library General Public License for more details.
104 You should have received a copy of the GNU Library General Public License
105 along with this library; see the file COPYING.LIB. If not, write to
106 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
107 Boston, MA 02110-1301, USA.
115 my $mess = longmess();
121 # Default constructor
127 $codeGenerator = shift;
128 $writeDependencies = shift;
130 bless($reference, $object);
134 sub GenerateEnumeration
136 my ($object, $enumeration) = @_;
138 my $className = GetEnumerationClassName($enumeration->type);
139 $object->GenerateEnumerationHeader($enumeration, $className);
140 $object->GenerateEnumerationImplementation($enumeration, $className);
143 sub GenerateDictionary
145 my ($object, $dictionary, $enumerations, $otherDictionaries) = @_;
147 my $className = GetDictionaryClassName($dictionary->type);
148 $object->GenerateDictionaryHeader($dictionary, $className, $enumerations, $otherDictionaries);
149 $object->GenerateDictionaryImplementation($dictionary, $className, $enumerations, $otherDictionaries);
152 sub GenerateCallbackFunction
154 my ($object, $callbackFunction, $enumerations, $dictionaries) = @_;
156 $object->GenerateCallbackFunctionHeader($callbackFunction, $enumerations, $dictionaries);
157 $object->GenerateCallbackFunctionImplementation($callbackFunction, $enumerations, $dictionaries);
160 sub GenerateInterface
162 my ($object, $interface, $defines, $enumerations, $dictionaries) = @_;
164 $codeGenerator->LinkOverloadedOperations($interface);
166 AddStringifierOperationIfNeeded($interface);
167 AddLegacyCallerOperationIfNeeded($interface);
169 if ($interface->isCallback) {
170 $object->GenerateCallbackInterfaceHeader($interface, $enumerations, $dictionaries);
171 $object->GenerateCallbackInterfaceImplementation($interface, $enumerations, $dictionaries);
173 $object->GenerateHeader($interface, $enumerations, $dictionaries);
174 $object->GenerateImplementation($interface, $enumerations, $dictionaries);
178 sub AddStringifierOperationIfNeeded
180 my $interface = shift;
182 foreach my $property (@{$interface->attributes}, @{$interface->operations}, @{$interface->anonymousOperations}) {
183 next unless $property->isStringifier;
185 if (ref($property) eq "IDLAttribute") {
186 assert("stringifier can only be used on attributes with type DOMString or USVString") unless $property->type->name eq "DOMString" || $property->type->name eq "USVString";
189 if (ref($property) eq "IDLOperation") {
190 assert("stringifier can only be used on operations with a return type of DOMString") unless $property->type->name eq "DOMString";
191 assert("stringifier can only be used on operations with zero arguments") unless scalar(@{$property->arguments}) == 0;
193 # Don't duplicate the operation if it was declared with the name 'toString'.
194 return if $property->name eq "toString";
197 my $stringifier = IDLOperation->new();
198 $stringifier->name("toString");
199 $stringifier->type(IDLParser::cloneType($property->type));
200 $stringifier->isStringifier(1);
202 IDLParser::copyExtendedAttributes($stringifier->extendedAttributes, $property->extendedAttributes);
204 if ($property->name && !$stringifier->extendedAttributes->{ImplementedAs}) {
205 $stringifier->extendedAttributes->{ImplementedAs} = $property->name;
208 # If the stringifier was declared as read-write attribute and had [CEReactions], we need to remove
209 # it from the operation, as the operation should act like attribute getter, which doesn't respect
211 if (ref($property) eq "IDLAttribute" && !$property->isReadOnly && $stringifier->extendedAttributes->{CEReactions}) {
212 delete $stringifier->extendedAttributes->{CEReactions};
215 push(@{$interface->operations}, $stringifier);
220 sub AddLegacyCallerOperationIfNeeded
222 my $interface = shift;
224 foreach my $operation (@{$interface->operations}, @{$interface->anonymousOperations}) {
225 my $isLegacyCaller = grep { $_ eq "legacycaller" } @{$operation->specials};
226 if ($isLegacyCaller) {
227 $interface->{LegacyCallers} = [] if !exists $interface->{LegacyCallers};
229 my $clonedOperation = IDLParser::cloneOperation($operation);
230 push(@{$interface->{LegacyCallers}}, $clonedOperation);
232 $clonedOperation->{overloads} = $interface->{LegacyCallers};
233 $clonedOperation->{overloadIndex} = @{$interface->{LegacyCallers}};
238 sub EventHandlerAttributeEventName
240 my $attribute = shift;
241 my $eventType = $attribute->extendedAttributes->{ImplementedAs} || $attribute->name;
243 # Remove the "on" prefix.
244 $eventType = substr($eventType, 2);
246 return "eventNames().${eventType}Event";
249 sub GetParentClassName
251 my $interface = shift;
253 return $interface->extendedAttributes->{JSLegacyParent} if $interface->extendedAttributes->{JSLegacyParent};
254 return "JSDOMObject" unless NeedsImplementationClass($interface);
255 return "JSDOMWrapper<" . GetImplClassName($interface) . ">" unless $interface->parentType;
256 return "JS" . $interface->parentType->name;
259 sub GetCallbackClassName
261 my $className = shift;
263 return "JS$className";
266 sub GetExportMacroForJSClass
268 my $interface = shift;
270 return $interface->extendedAttributes->{ExportMacro} . " " if $interface->extendedAttributes->{ExportMacro};
274 sub AddIncludesForImplementationTypeInImpl
276 my $implementationType = shift;
278 AddIncludesForImplementationType($implementationType, \%implIncludes);
281 sub AddIncludesForImplementationTypeInHeader
283 my $implementationType = shift;
285 AddIncludesForImplementationType($implementationType, \%headerIncludes);
288 sub AddIncludesForImplementationType
290 my ($implementationType, $includesRef) = @_;
292 $includesRef->{"${implementationType}.h"} = 1;
295 sub AddToImplIncludesForIDLType
297 my ($type, $conditional) = @_;
299 return AddToIncludesForIDLType($type, \%implIncludes, $conditional)
302 sub AddToIncludesForIDLType
304 my ($type, $includesRef, $conditional) = @_;
306 if ($type->isNullable) {
307 AddToIncludes("JSDOMConvertNullable.h", $includesRef, $conditional);
310 if ($type->extendedAttributes->{OverrideIDLType}) {
311 my $overrideTypeName = $type->extendedAttributes->{OverrideIDLType};
312 if ($overrideTypeName eq "IDLIDBKey") {
313 AddToIncludes("JSDOMConvertIndexedDB.h", $includesRef, $conditional);
317 if ($overrideTypeName eq "IDLWebGLAny" || $overrideTypeName eq "IDLWebGLExtension") {
318 AddToIncludes("JSDOMConvertWebGL.h", $includesRef, $conditional);
323 if ($type->name eq "any") {
324 AddToIncludes("JSDOMConvertAny.h", $includesRef, $conditional);
328 if ($type->name eq "boolean") {
329 AddToIncludes("JSDOMConvertBoolean.h", $includesRef, $conditional);
333 if ($codeGenerator->IsBufferSourceType($type)) {
334 AddToIncludes("JSDOMConvertBufferSource.h", $includesRef, $conditional);
338 if ($codeGenerator->IsCallbackFunction($type) || $codeGenerator->IsCallbackInterface($type)) {
339 AddToIncludes("JS" . $type->name . ".h", $includesRef, $conditional);
340 AddToIncludes("JSDOMConvertCallbacks.h", $includesRef, $conditional);
344 if ($type->name eq "Date") {
345 AddToIncludes("JSDOMConvertDate.h", $includesRef, $conditional);
349 if ($codeGenerator->IsExternalDictionaryType($type)) {
350 AddToIncludes("JS" . $type->name . ".h", $includesRef, $conditional);
351 AddToIncludes("JSDOMConvertDictionary.h", $includesRef, $conditional);
355 if ($codeGenerator->IsExternalEnumType($type)) {
356 AddToIncludes("JS" . $type->name . ".h", $includesRef, $conditional);
357 AddToIncludes("JSDOMConvertEnumeration.h", $includesRef, $conditional);
361 if ($type->name eq "EventListener") {
362 AddToIncludes("JSEventListener.h", $includesRef, $conditional);
363 AddToIncludes("JSDOMConvertEventListener.h", $includesRef, $conditional);
367 if ($codeGenerator->IsInterfaceType($type)) {
368 AddToIncludes("JS" . $type->name . ".h", $includesRef, $conditional);
369 AddToIncludes("JSDOMConvertInterface.h", $includesRef, $conditional);
373 if ($type->name eq "JSON") {
374 AddToIncludes("JSDOMConvertJSON.h", $includesRef, $conditional);
378 if ($codeGenerator->IsNumericType($type)) {
379 AddToIncludes("JSDOMConvertNumbers.h", $includesRef, $conditional);
383 if ($type->name eq "object") {
384 AddToIncludes("JSDOMConvertObject.h", $includesRef, $conditional);
388 if ($codeGenerator->IsPromiseType($type)) {
389 AddToIncludes("DOMPromiseProxy.h", $includesRef, $conditional);
390 AddToIncludes("JSDOMConvertPromise.h", $includesRef, $conditional);
392 AddToIncludesForIDLType(@{$type->subtypes}[0], $includesRef, $conditional);
396 if ($codeGenerator->IsRecordType($type)) {
397 AddToIncludes("<wtf/Vector.h>", $includesRef, $conditional);
398 AddToIncludes("JSDOMConvertRecord.h", $includesRef, $conditional);
400 AddToIncludesForIDLType(@{$type->subtypes}[0], $includesRef, $conditional);
401 AddToIncludesForIDLType(@{$type->subtypes}[1], $includesRef, $conditional);
405 if ($codeGenerator->IsSequenceOrFrozenArrayType($type)) {
406 AddToIncludes("<JavaScriptCore/JSArray.h>", $includesRef, $conditional);
407 AddToIncludes("JSDOMConvertSequences.h", $includesRef, $conditional);
409 AddToIncludesForIDLType(@{$type->subtypes}[0], $includesRef, $conditional);
413 if ($type->name eq "ScheduledAction") {
414 AddToIncludes("JSDOMConvertScheduledAction.h", $includesRef, $conditional);
418 if ($type->name eq "SerializedScriptValue") {
419 AddToIncludes("SerializedScriptValue.h", $includesRef, $conditional);
420 AddToIncludes("JSDOMConvertSerializedScriptValue.h", $includesRef, $conditional);
424 if ($codeGenerator->IsStringType($type)) {
425 AddToIncludes("JSDOMConvertStrings.h", $includesRef, $conditional);
429 if ($type->isUnion) {
430 AddToIncludes("<wtf/Variant.h>", $includesRef, $conditional);
431 AddToIncludes("JSDOMConvertUnion.h", $includesRef, $conditional);
433 foreach my $memberType (@{$type->subtypes}) {
434 AddToIncludesForIDLType($memberType, $includesRef, $conditional);
440 if ($type->name eq "XPathNSResolver") {
441 AddToIncludes("JSXPathNSResolver.h", $includesRef, $conditional);
442 AddToIncludes("JSDOMConvertXPathNSResolver.h", $includesRef, $conditional);
447 sub AddToImplIncludes
449 my ($header, $conditional) = @_;
451 AddToIncludes($header, \%implIncludes, $conditional);
456 my ($header, $includesRef, $conditional) = @_;
458 if (not $conditional) {
459 $includesRef->{$header} = 1;
460 } elsif (not exists($includesRef->{$header})) {
461 $includesRef->{$header} = $conditional;
463 my $oldValue = $includesRef->{$header};
464 $includesRef->{$header} = "$oldValue|$conditional" if $oldValue ne 1;
470 my $attribute = shift;
471 return $attribute->isReadOnly && !$attribute->extendedAttributes->{Replaceable} && !$attribute->extendedAttributes->{PutForwards};
474 sub AddClassForwardIfNeeded
478 # SVGAnimatedLength/Number/etc. are not classes so they can't be forward declared as classes.
479 return if $codeGenerator->IsSVGAnimatedType($type);
480 return if $codeGenerator->IsBufferSourceType($type);
482 push(@headerContent, "class " . $type->name . ";\n\n");
485 sub GetGenerateIsReachable
487 my $interface = shift;
488 return $interface->extendedAttributes->{GenerateIsReachable};
491 sub GetCustomIsReachable
493 my $interface = shift;
494 return $interface->extendedAttributes->{CustomIsReachable};
497 sub IsDOMGlobalObject
499 my $interface = shift;
500 return $interface->type->name eq "DOMWindow" || $interface->type->name eq "RemoteDOMWindow" || $codeGenerator->InheritsInterface($interface, "WorkerGlobalScope") || $codeGenerator->InheritsInterface($interface, "WorkletGlobalScope") || $interface->type->name eq "TestGlobalObject";
503 sub ShouldUseGlobalObjectPrototype
505 my $interface = shift;
507 # For workers, the global object is a DedicatedWorkerGlobalScope.
508 return 0 if $interface->type->name eq "WorkerGlobalScope";
509 # For worklets, the global object is a PaintWorkletGlobalScope.
510 return 0 if $interface->type->name eq "WorkletGlobalScope";
512 return IsDOMGlobalObject($interface);
515 sub GenerateIndexedGetter
517 my ($interface, $indexedGetterOperation, $indexExpression) = @_;
519 # NOTE: This abstractly implements steps 1.2.1 - 1.2.8 of the LegacyPlatformObjectGetOwnProperty
520 # algorithm. Returing the conversion expression and attributes expression for use
523 # 1.2.1 Let operation be the operation used to declare the indexed property getter.
524 # 1.2.2 Let value be an uninitialized variable.
525 # 1.2.3 If operation was defined without an identifier, then set value to the result
526 # of performing the steps listed in the interface description to determine the
527 # value of an indexed property with index as the index.
528 # 1.2.4 Otherwise, operation was defined with an identifier. Set value to the result
529 # of performing the steps listed in the description of operation with index as
530 # the only argument value.
531 # 1.2.5 Let desc be a newly created Property Descriptor with no fields.
532 # 1.2.6 Set desc.[[Value]] to the result of converting value to an ECMAScript value.
533 # 1.2.7 If O implements an interface with an indexed property setter, then set
534 # desc.[[Writable]] to true, otherwise set it to false.
538 push(@attributes, "JSC::PropertyAttribute::ReadOnly") if !GetIndexedSetterOperation($interface) && !$interface->extendedAttributes->{Plugin};
540 my $attributeString = "static_cast<unsigned>(" . ((@attributes > 0) ? join(" | ", @attributes) : "0") . ")";
542 my $indexedGetterFunctionName = $indexedGetterOperation->extendedAttributes->{ImplementedAs} || $indexedGetterOperation->name || "item";
543 my $nativeToJSConversion = NativeToJSValueUsingPointers($indexedGetterOperation, $interface, "thisObject->wrapped().${indexedGetterFunctionName}(${indexExpression})", "*thisObject->globalObject()");
545 return ($nativeToJSConversion, $attributeString);
548 sub GenerateNamedGetter
550 my ($interface, $namedGetterOperation, $namedPropertyExpression) = @_;
552 # NOTE: This abstractly implements steps 2.1 - 2.10 of the LegacyPlatformObjectGetOwnProperty
553 # algorithm. Returing the conversion expression and attributes expression for use
556 # 2.1 Let operation be the operation used to declare the named property getter.
557 # 2.2 Let value be an uninitialized variable.
558 # 2.3 If operation was defined without an identifier, then set value to the result
559 # of performing the steps listed in the interface description to determine the
560 # value of a named property with P as the name.
561 # 2.4 Otherwise, operation was defined with an identifier. Set value to the result
562 # of performing the steps listed in the description of operation with P as the
563 # only argument value..
564 # 2.5 Let desc be a newly created Property Descriptor with no fields.
565 # 2.6 Set desc.[[Value]] to the result of converting value to an ECMAScript value.
566 # 2.7 If O implements an interface with a named property setter, then set desc.[[Writable]]
567 # to true, otherwise set it to false.
568 # 2.8 If O implements an interface with the [LegacyUnenumerableNamedProperties]
569 # extended attribute, then set desc.[[Enumerable]] to false, otherwise set it
571 # 2.9 Set desc.[[Configurable]] to true.
575 push(@attributes, "JSC::PropertyAttribute::ReadOnly") if !GetNamedSetterOperation($interface) && !$interface->extendedAttributes->{Plugin};
576 push(@attributes, "JSC::PropertyAttribute::DontEnum") if $interface->extendedAttributes->{LegacyUnenumerableNamedProperties};
578 my $attributeString = "static_cast<unsigned>(" . ((@attributes > 0) ? join(" | ", @attributes) : "0") . ")";
579 my $nativeToJSConversion = NativeToJSValueUsingPointers($namedGetterOperation, $interface, $namedPropertyExpression, "*thisObject->globalObject()");
581 return ($nativeToJSConversion, $attributeString);
584 sub GenerateNamedGetterLambda
586 my ($outputArray, $interface, $namedGetterOperation, $namedGetterFunctionName, $IDLType) = @_;
588 # NOTE: Named getters are little odd. To avoid doing duplicate lookups (once when checking if
589 # the property name is a 'supported property name' and once to get the value) we signal
590 # that a property is supported by whether or not it is 'null' (where what null means is
591 # dependant on the IDL type). This is based on the assumption that no named getter will
592 # ever actually want to return null as an actual return value, which seems like an ok
593 # assumption to make (should it turn out this doesn't hold in the future, we have lots
594 # of options; do two lookups, add an extra layer of std::optional, etc.).
596 my $resultType = "typename ${IDLType}::ImplementationType";
597 $resultType = "ExceptionOr<" . $resultType . ">" if $namedGetterOperation->extendedAttributes->{MayThrowException};
598 my $returnType = "std::optional<" . $resultType . ">";
600 push(@$outputArray, " auto getterFunctor = [] (auto& thisObject, auto propertyName) -> ${returnType} {\n");
602 my @arguments = GenerateCallWithUsingReferences($namedGetterOperation->extendedAttributes->{CallWith}, $outputArray, "std::nullopt", "thisObject", " ");
603 push(@arguments, "propertyNameToAtomicString(propertyName)");
605 push(@$outputArray, " auto result = thisObject.wrapped().${namedGetterFunctionName}(" . join(", ", @arguments) . ");\n");
607 if ($namedGetterOperation->extendedAttributes->{MayThrowException}) {
608 push(@$outputArray, " if (result.hasException())\n");
609 push(@$outputArray, " return ${resultType} { result.releaseException() };\n");
610 push(@$outputArray, " if (!${IDLType}::isNullValue(result.returnValue()))\n");
611 push(@$outputArray, " return ${resultType} { ${IDLType}::extractValueFromNullable(result.releaseReturnValue()) };\n");
612 push(@$outputArray, " return std::nullopt;\n");
614 push(@$outputArray, " if (!${IDLType}::isNullValue(result))\n");
615 push(@$outputArray, " return ${resultType} { ${IDLType}::extractValueFromNullable(result) };\n");
616 push(@$outputArray, " return std::nullopt;\n");
618 push(@$outputArray, " };\n");
621 # https://heycam.github.io/webidl/#legacy-platform-object-getownproperty
622 sub GenerateGetOwnPropertySlot
624 my ($outputArray, $interface, $className) = @_;
626 return if $interface->extendedAttributes->{CustomGetOwnPropertySlot};
628 push(@$outputArray, "bool ${className}::getOwnPropertySlot(JSObject* object, ExecState* state, PropertyName propertyName, PropertySlot& slot)\n");
629 push(@$outputArray, "{\n");
630 push(@$outputArray, " auto* thisObject = jsCast<${className}*>(object);\n");
631 push(@$outputArray, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n");
633 my $namedGetterOperation = GetNamedGetterOperation($interface);
634 my $indexedGetterOperation = GetIndexedGetterOperation($interface);
636 if (($namedGetterOperation && $namedGetterOperation->extendedAttributes->{MayThrowException}) || ($indexedGetterOperation && $indexedGetterOperation->extendedAttributes->{MayThrowException})) {
637 push(@$outputArray, " auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
640 # NOTE: The alogithm for [[GetOwnProperty]] contains only the following step:
641 # 1. Return LegacyPlatformObjectGetOwnProperty(O, P, false).
643 # Therefore, the following steps are from the LegacyPlatformObjectGetOwnProperty algorithm
644 # https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
646 # 1. If O supports indexed properties and P is an array index property name, then:
647 if ($indexedGetterOperation) {
648 # 1.1. Let index be the result of calling ToUint32(P).
649 push(@$outputArray, " if (auto index = parseIndex(propertyName)) {\n");
651 # 1.2. If index is a supported property index, then:
652 # FIXME: This should support non-contiguous indices.
653 push(@$outputArray, " if (index.value() < thisObject->wrapped().length()) {\n");
655 # NOTE: GenerateIndexedGetter implements steps 1.2.1 - 1.2.8.
657 my ($nativeToJSConversion, $attributeString) = GenerateIndexedGetter($interface, $indexedGetterOperation, "index.value()");
659 push(@$outputArray, " auto value = ${nativeToJSConversion};\n");
660 push(@$outputArray, " RETURN_IF_EXCEPTION(throwScope, false);\n") if $indexedGetterOperation->extendedAttributes->{MayThrowException};
662 push(@$outputArray, " slot.setValue(thisObject, ${attributeString}, value);\n");
663 push(@$outputArray, " return true;\n");
665 push(@$outputArray, " }\n");
667 # 1.3. Set ignoreNamedProps to true.
668 # NOTE: Setting ignoreNamedProps has the effect of skipping step 2, so we can early return here
669 # rather than going through the paces of having an actual ignoreNamedProps update.
670 if ($namedGetterOperation || $interface->extendedAttributes->{Plugin}) {
671 push(@$outputArray, " return JSObject::getOwnPropertySlot(object, state, propertyName, slot);\n");
673 push(@$outputArray, " }\n");
676 # 2. If O supports named properties, the result of running the named property visibility
677 # algorithm with property name P and object O is true, and ignoreNamedProps is false, then:
678 if ($namedGetterOperation) {
679 # NOTE: ignoreNamedProps is guarenteed to be false here, as it is initially false, and never set
680 # to true, due to the early return in step 1.3
681 AddToImplIncludes("JSDOMAbstractOperations.h");
683 my $namedGetterFunctionName = $namedGetterOperation->extendedAttributes->{ImplementedAs} || $namedGetterOperation->name || "namedItem";
684 my $IDLType = GetIDLTypeExcludingNullability($interface, $namedGetterOperation->type);
686 push(@$outputArray, " using GetterIDLType = ${IDLType};\n");
688 GenerateNamedGetterLambda($outputArray, $interface, $namedGetterOperation, $namedGetterFunctionName, "GetterIDLType");
690 my $overrideBuiltin = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins") ? "OverrideBuiltins::Yes" : "OverrideBuiltins::No";
691 push(@$outputArray, " if (auto namedProperty = accessVisibleNamedProperty<${overrideBuiltin}>(*state, *thisObject, propertyName, getterFunctor)) {\n");
693 # NOTE: GenerateNamedGetter implements steps 2.1 - 2.10.
695 my ($nativeToJSConversion, $attributeString) = GenerateNamedGetter($interface, $namedGetterOperation, "WTFMove(namedProperty.value())");
697 push(@$outputArray, " auto value = ${nativeToJSConversion};\n");
698 push(@$outputArray, " RETURN_IF_EXCEPTION(throwScope, false);\n") if $namedGetterOperation->extendedAttributes->{MayThrowException};
700 push(@$outputArray, " slot.setValue(thisObject, ${attributeString}, value);\n");
701 push(@$outputArray, " return true;\n");
702 push(@$outputArray, " }\n");
705 if ($interface->extendedAttributes->{Plugin}) {
706 AddToImplIncludes("JSPluginElementFunctions.h");
707 push(@$outputArray, " if (pluginElementCustomGetOwnPropertySlot(thisObject, state, propertyName, slot))\n");
708 push(@$outputArray, " return true;\n");
711 # 3. Return OrdinaryGetOwnProperty(O, P).
712 push(@$outputArray, " return JSObject::getOwnPropertySlot(object, state, propertyName, slot);\n");
714 push(@$outputArray, "}\n\n");
717 # https://heycam.github.io/webidl/#legacy-platform-object-getownproperty
718 sub GenerateGetOwnPropertySlotByIndex
720 my ($outputArray, $interface, $className) = @_;
722 return if $interface->extendedAttributes->{CustomGetOwnPropertySlot};
724 # Sink the int-to-string conversion that happens when we create a PropertyName
725 # to the point where we actually need it.
726 my $didGeneratePropertyName = 0;
727 my $propertyNameGeneration = sub {
728 return if $didGeneratePropertyName;
730 push(@$outputArray, " auto propertyName = Identifier::from(state, index);\n");
731 $didGeneratePropertyName = 1;
734 push(@$outputArray, "bool ${className}::getOwnPropertySlotByIndex(JSObject* object, ExecState* state, unsigned index, PropertySlot& slot)\n");
735 push(@$outputArray, "{\n");
736 push(@$outputArray, " auto* thisObject = jsCast<${className}*>(object);\n");
737 push(@$outputArray, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n");
739 my $namedGetterOperation = GetNamedGetterOperation($interface);
740 my $indexedGetterOperation = GetIndexedGetterOperation($interface);
742 if (($namedGetterOperation && $namedGetterOperation->extendedAttributes->{MayThrowException}) || ($indexedGetterOperation && $indexedGetterOperation->extendedAttributes->{MayThrowException})) {
743 push(@$outputArray, " auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
746 # NOTE: The alogithm for [[GetOwnProperty]] contains only the following step:
747 # 1. Return LegacyPlatformObjectGetOwnProperty(O, P, false).
749 # Therefore, the following steps are from the LegacyPlatformObjectGetOwnProperty algorithm
750 # https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
752 # 1. If O supports indexed properties and P is an array index property name, then:
753 if ($indexedGetterOperation) {
754 # 1.1. Let index be the result of calling ToUint32(P).
755 push(@$outputArray, " if (LIKELY(index <= MAX_ARRAY_INDEX)) {\n");
757 # 1.2. If index is a supported property index, then:
758 # FIXME: This should support non-contiguous indices.
759 push(@$outputArray, " if (index < thisObject->wrapped().length()) {\n");
761 # NOTE: GenerateIndexedGetter implements steps 1.2.1 - 1.2.8.
763 my ($nativeToJSConversion, $attributeString) = GenerateIndexedGetter($interface, $indexedGetterOperation, "index");
765 push(@$outputArray, " auto value = ${nativeToJSConversion};\n");
766 push(@$outputArray, " RETURN_IF_EXCEPTION(throwScope, false);\n") if $indexedGetterOperation->extendedAttributes->{MayThrowException};
768 push(@$outputArray, " slot.setValue(thisObject, ${attributeString}, value);\n");
769 push(@$outputArray, " return true;\n");
771 push(@$outputArray, " }\n");
773 # 1.3. Set ignoreNamedProps to true.
774 # NOTE: Setting ignoreNamedProps has the effect of skipping step 2, so we can early return here
775 # rather than going through the paces of having an actual ignoreNamedProps update.
776 if ($namedGetterOperation || $interface->extendedAttributes->{Plugin}) {
777 push(@$outputArray, " return JSObject::getOwnPropertySlotByIndex(object, state, index, slot);\n");
779 push(@$outputArray, " }\n");
782 # 2. If O supports named properties, the result of running the named property visibility
783 # algorithm with property name P and object O is true, and ignoreNamedProps is false, then:
784 if ($namedGetterOperation) {
785 # NOTE: ignoreNamedProps is guarenteed to be false here, as it is initially false, and never set
786 # to true, due to the early return in step 1.3
787 AddToImplIncludes("JSDOMAbstractOperations.h");
789 &$propertyNameGeneration();
791 my $namedGetterFunctionName = $namedGetterOperation->extendedAttributes->{ImplementedAs} || $namedGetterOperation->name || "namedItem";
792 my $IDLType = GetIDLTypeExcludingNullability($interface, $namedGetterOperation->type);
794 push(@$outputArray, " using GetterIDLType = ${IDLType};\n");
796 GenerateNamedGetterLambda($outputArray, $interface, $namedGetterOperation, $namedGetterFunctionName, "GetterIDLType");
798 my $overrideBuiltin = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins") ? "OverrideBuiltins::Yes" : "OverrideBuiltins::No";
799 push(@$outputArray, " if (auto namedProperty = accessVisibleNamedProperty<${overrideBuiltin}>(*state, *thisObject, propertyName, getterFunctor)) {\n");
801 # NOTE: GenerateNamedGetter implements steps 2.1 - 2.10.
803 my ($nativeToJSConversion, $attributeString) = GenerateNamedGetter($interface, $namedGetterOperation, "WTFMove(namedProperty.value())");
805 push(@$outputArray, " auto value = ${nativeToJSConversion};\n");
806 push(@$outputArray, " RETURN_IF_EXCEPTION(throwScope, false);\n") if $namedGetterOperation->extendedAttributes->{MayThrowException};
808 push(@$outputArray, " slot.setValue(thisObject, ${attributeString}, value);\n");
809 push(@$outputArray, " return true;\n");
810 push(@$outputArray, " }\n");
813 if ($interface->extendedAttributes->{Plugin}) {
814 &$propertyNameGeneration();
816 AddToImplIncludes("JSPluginElementFunctions.h");
817 push(@$outputArray, " if (pluginElementCustomGetOwnPropertySlot(thisObject, state, propertyName, slot))\n");
818 push(@$outputArray, " return true;\n");
821 # 3. Return OrdinaryGetOwnProperty(O, P).
822 push(@$outputArray, " return JSObject::getOwnPropertySlotByIndex(object, state, index, slot);\n");
824 push(@$outputArray, "}\n\n");
827 # https://heycam.github.io/webidl/#legacy-platform-object-property-enumeration
828 sub GenerateGetOwnPropertyNames
830 my ($outputArray, $interface, $className) = @_;
832 return if $interface->extendedAttributes->{CustomGetOwnPropertyNames};
834 my $namedGetterOperation = GetNamedGetterOperation($interface);
835 my $indexedGetterOperation = GetIndexedGetterOperation($interface);
837 push(@$outputArray, "void ${className}::getOwnPropertyNames(JSObject* object, ExecState* state, PropertyNameArray& propertyNames, EnumerationMode mode)\n");
838 push(@$outputArray, "{\n");
839 push(@$outputArray, " auto* thisObject = jsCast<${className}*>(object);\n");
840 push(@$outputArray, " ASSERT_GC_OBJECT_INHERITS(object, info());\n");
842 # 1. If the object supports indexed properties, then the object’s supported
843 # property indices are enumerated first, in numerical order.
844 # FIXME: This should support non-contiguous indices.
845 if ($indexedGetterOperation) {
846 push(@$outputArray, " for (unsigned i = 0, count = thisObject->wrapped().length(); i < count; ++i)\n");
847 push(@$outputArray, " propertyNames.add(Identifier::from(state, i));\n");
850 # 2. If the object supports named properties and doesn’t implement an interface
851 # with the [LegacyUnenumerableNamedProperties] extended attribute, then the
852 # object’s supported property names that are visible according to the named
853 # property visibility algorithm are enumerated next, in the order given in
854 # the definition of the set of supported property names.
855 if ($namedGetterOperation) {
856 if (!$interface->extendedAttributes->{LegacyUnenumerableNamedProperties}) {
857 push(@$outputArray, " for (auto& propertyName : thisObject->wrapped().supportedPropertyNames())\n");
858 push(@$outputArray, " propertyNames.add(Identifier::fromString(state, propertyName));\n");
860 push(@$outputArray, " if (mode.includeDontEnumProperties()) {\n");
861 push(@$outputArray, " for (auto& propertyName : thisObject->wrapped().supportedPropertyNames())\n");
862 push(@$outputArray, " propertyNames.add(Identifier::fromString(state, propertyName));\n");
863 push(@$outputArray, " }\n");
867 # 3. Finally, any enumerable own properties or properties from the object’s
868 # prototype chain are then enumerated, in no defined order.
869 push(@$outputArray, " JSObject::getOwnPropertyNames(object, state, propertyNames, mode);\n");
870 push(@$outputArray, "}\n\n");
873 # https://heycam.github.io/webidl/#invoke-indexed-setter
874 sub GenerateInvokeIndexedPropertySetter
876 my ($outputArray, $indent, $interface, $indexedSetterOperation, $indexExpression, $value) = @_;
878 # The second argument of the indexed setter operation is the argument being converted.
879 my $argument = @{$indexedSetterOperation->arguments}[1];
880 my $nativeValue = JSValueToNative($interface, $argument, $value, $indexedSetterOperation->extendedAttributes->{Conditional}, "state", "*state", "thisObject", "", "");
882 push(@$outputArray, $indent . "auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
883 push(@$outputArray, $indent . "auto nativeValue = ${nativeValue};\n");
884 push(@$outputArray, $indent . "RETURN_IF_EXCEPTION(throwScope, true);\n");
886 my $indexedSetterFunctionName = $indexedSetterOperation->name || "setItem";
887 my $nativeValuePassExpression = PassArgumentExpression("nativeValue", $argument);
888 my $functionString = "thisObject->wrapped().${indexedSetterFunctionName}(${indexExpression}, ${nativeValuePassExpression})";
889 $functionString = "propagateException(*state, throwScope, ${functionString})" if NeedsExplicitPropagateExceptionCall($indexedSetterOperation);
891 push(@$outputArray, $indent . $functionString . ";\n");
894 # https://heycam.github.io/webidl/#invoke-named-setter
895 sub GenerateInvokeNamedPropertySetter
897 my ($outputArray, $indent, $interface, $namedSetterOperation, $value) = @_;
899 my $argument = @{$namedSetterOperation->arguments}[1];
900 my $nativeValue = JSValueToNative($interface, $argument, $value, $namedSetterOperation->extendedAttributes->{Conditional}, "state", "*state", "thisObject", "", "");
902 push(@$outputArray, $indent . "auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
903 push(@$outputArray, $indent . "auto nativeValue = ${nativeValue};\n");
904 push(@$outputArray, $indent . "RETURN_IF_EXCEPTION(throwScope, true);\n");
906 push(@$outputArray, $indent . "bool isPropertySupported = true;\n") if $namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties};
908 my $namedSetterFunctionName = $namedSetterOperation->name || "setNamedItem";
909 my $nativeValuePassExpression = PassArgumentExpression("nativeValue", $argument);
912 push(@arguments, "propertyNameToString(propertyName)");
913 push(@arguments, $nativeValuePassExpression);
914 push(@arguments, "isPropertySupported") if $namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties};
916 my $functionString = "thisObject->wrapped().${namedSetterFunctionName}(" . join(", ", @arguments) . ")";
917 $functionString = "propagateException(*state, throwScope, ${functionString})" if NeedsExplicitPropagateExceptionCall($namedSetterOperation);
919 push(@$outputArray, $indent . $functionString . ";\n");
924 my ($outputArray, $interface, $className) = @_;
926 return if $interface->extendedAttributes->{CustomPut};
928 my $namedSetterOperation = GetNamedSetterOperation($interface);
929 my $indexedSetterOperation = GetIndexedSetterOperation($interface);
931 push(@$outputArray, "bool ${className}::put(JSCell* cell, ExecState* state, PropertyName propertyName, JSValue value, PutPropertySlot& putPropertySlot)\n");
932 push(@$outputArray, "{\n");
933 push(@$outputArray, " auto* thisObject = jsCast<${className}*>(cell);\n");
934 push(@$outputArray, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n\n");
936 assert("CEReactions is not supported on having both named setters and indexed setters") if $namedSetterOperation && $namedSetterOperation->extendedAttributes->{CEReactions}
937 && $indexedSetterOperation && $indexedSetterOperation->extendedAttributes->{CEReactions};
938 if ($namedSetterOperation) {
939 GenerateCustomElementReactionsStackIfNeeded($outputArray, $namedSetterOperation, "*state");
941 if ($indexedSetterOperation) {
942 GenerateCustomElementReactionsStackIfNeeded($outputArray, $indexedSetterOperation, "*state");
945 if ($indexedSetterOperation) {
946 push(@$outputArray, " if (auto index = parseIndex(propertyName)) {\n");
948 GenerateInvokeIndexedPropertySetter($outputArray, " ", $interface, $indexedSetterOperation, "index.value()", "value");
950 push(@$outputArray, " return true;\n");
951 push(@$outputArray, " }\n\n");
954 if ($namedSetterOperation) {
955 # FIMXE: We need a more comprehensive story for Symbols.
956 push(@$outputArray, " if (!propertyName.isSymbol()) {\n");
958 my $additionalIndent = "";
960 my $overrideBuiltins = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins");
961 if (!$overrideBuiltins) {
962 push(@$outputArray, " PropertySlot slot { thisObject, PropertySlot::InternalMethodType::VMInquiry };\n");
963 push(@$outputArray, " JSValue prototype = thisObject->getPrototypeDirect(state->vm());\n");
964 push(@$outputArray, " if (!(prototype.isObject() && asObject(prototype)->getPropertySlot(state, propertyName, slot))) {\n");
965 $additionalIndent .= " ";
968 GenerateInvokeNamedPropertySetter($outputArray, $additionalIndent . " ", $interface, $namedSetterOperation, "value");
969 if ($namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties}) {
970 push(@$outputArray, $additionalIndent . " if (!isPropertySupported)\n");
971 push(@$outputArray, $additionalIndent . " return JSObject::put(thisObject, state, propertyName, value, putPropertySlot);\n");
973 push(@$outputArray, $additionalIndent . " return true;\n");
975 if (!$overrideBuiltins) {
976 push(@$outputArray, " }\n");
979 push(@$outputArray, " }\n\n");
982 assert("Using both a named property setter and [Plugin] together is not supported.") if $namedSetterOperation && $interface->extendedAttributes->{Plugin};
983 if ($interface->extendedAttributes->{Plugin}) {
984 AddToImplIncludes("JSPluginElementFunctions.h");
986 push(@$outputArray, " bool putResult = false;\n");
987 push(@$outputArray, " if (pluginElementCustomPut(thisObject, state, propertyName, value, putPropertySlot, putResult))\n");
988 push(@$outputArray, " return putResult;\n\n");
991 push(@$outputArray, " return JSObject::put(thisObject, state, propertyName, value, putPropertySlot);\n");
992 push(@$outputArray, "}\n\n");
995 sub GeneratePutByIndex
997 my ($outputArray, $interface, $className) = @_;
999 return if $interface->extendedAttributes->{CustomPut};
1001 my $namedSetterOperation = GetNamedSetterOperation($interface);
1002 my $indexedSetterOperation = GetIndexedSetterOperation($interface);
1004 my $overrideBuiltins = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins");
1005 my $ellidesCallsToBase = ($namedSetterOperation && $overrideBuiltins) && !$interface->extendedAttributes->{Plugin} && !$namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties};
1007 push(@$outputArray, "bool ${className}::putByIndex(JSCell* cell, ExecState* state, unsigned index, JSValue value, bool" . (!$ellidesCallsToBase ? " shouldThrow" : "") . ")\n");
1008 push(@$outputArray, "{\n");
1009 push(@$outputArray, " auto* thisObject = jsCast<${className}*>(cell);\n");
1010 push(@$outputArray, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n\n");
1012 assert("CEReactions is not supported on having both named setters and indexed setters") if $namedSetterOperation && $namedSetterOperation->extendedAttributes->{CEReactions}
1013 && $indexedSetterOperation && $indexedSetterOperation->extendedAttributes->{CEReactions};
1014 if ($namedSetterOperation) {
1015 GenerateCustomElementReactionsStackIfNeeded($outputArray, $namedSetterOperation, "*state");
1017 if ($indexedSetterOperation) {
1018 GenerateCustomElementReactionsStackIfNeeded($outputArray, $indexedSetterOperation, "*state");
1021 if ($indexedSetterOperation ) {
1022 push(@$outputArray, " if (LIKELY(index <= MAX_ARRAY_INDEX)) {\n");
1024 GenerateInvokeIndexedPropertySetter($outputArray, " ", $interface, $indexedSetterOperation, "index", "value");
1026 push(@$outputArray, " return true;\n");
1027 push(@$outputArray, " }\n\n");
1030 if ($namedSetterOperation) {
1031 push(@$outputArray, " auto propertyName = Identifier::from(state, index);\n");
1033 my $additionalIndent = "";
1034 if (!$overrideBuiltins) {
1035 push(@$outputArray, " PropertySlot slot { thisObject, PropertySlot::InternalMethodType::VMInquiry };\n");
1036 push(@$outputArray, " JSValue prototype = thisObject->getPrototypeDirect(state->vm());\n");
1037 push(@$outputArray, " if (!(prototype.isObject() && asObject(prototype)->getPropertySlot(state, propertyName, slot))) {\n");
1038 $additionalIndent .= " ";
1041 GenerateInvokeNamedPropertySetter($outputArray, $additionalIndent . " ", $interface, $namedSetterOperation, "value");
1042 if ($namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties}) {
1043 push(@$outputArray, $additionalIndent . " if (!isPropertySupported)\n");
1044 push(@$outputArray, $additionalIndent . " return JSObject::putByIndex(cell, state, index, value, shouldThrow);\n");
1046 push(@$outputArray, $additionalIndent . " return true;\n");
1048 if (!$overrideBuiltins) {
1049 push(@$outputArray, " }\n\n");
1053 assert("Using both a named property setter and [Plugin] together is not supported.") if $namedSetterOperation && $interface->extendedAttributes->{Plugin};
1054 if ($interface->extendedAttributes->{Plugin}) {
1055 AddToImplIncludes("JSPluginElementFunctions.h");
1056 push(@$outputArray, " auto propertyName = Identifier::from(state, index);\n");
1057 push(@$outputArray, " PutPropertySlot putPropertySlot(thisObject, shouldThrow);\n");
1058 push(@$outputArray, " bool putResult = false;\n");
1059 push(@$outputArray, " if (pluginElementCustomPut(thisObject, state, propertyName, value, putPropertySlot, putResult))\n");
1060 push(@$outputArray, " return putResult;\n\n");
1063 if (!$ellidesCallsToBase) {
1064 push(@$outputArray, " return JSObject::putByIndex(cell, state, index, value, shouldThrow);\n");
1067 push(@$outputArray, "}\n\n");
1070 sub GenerateIsUnforgeablePropertyName
1072 my ($outputArray, $interface) = @_;
1074 my @unforgeablePropertyNames = ();
1075 foreach my $property (@{$interface->attributes}, @{$interface->operations}) {
1076 next if $property->isStatic;
1078 if (IsUnforgeable($interface, $property)) {
1079 push(@unforgeablePropertyNames, $property->name);
1083 return 0 if (scalar(@unforgeablePropertyNames) == 0);
1085 my $condition = join(" || ", map { "propertyName == \"" . $_ . "\"" } @unforgeablePropertyNames);
1087 push(@$outputArray, "static bool isUnforgeablePropertyName(PropertyName propertyName)\n");
1088 push(@$outputArray, "{\n");
1089 push(@$outputArray, " return ${condition};\n");
1090 push(@$outputArray, "}\n\n");
1095 # https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
1096 sub GenerateDefineOwnProperty
1098 my ($outputArray, $interface, $className) = @_;
1100 return if $interface->extendedAttributes->{CustomDefineOwnProperty};
1102 my $namedSetterOperation = GetNamedSetterOperation($interface);
1103 my $indexedSetterOperation = GetIndexedSetterOperation($interface);
1105 return if !$namedSetterOperation && !$indexedSetterOperation;
1107 push(@$outputArray, "bool ${className}::defineOwnProperty(JSObject* object, ExecState* state, PropertyName propertyName, const PropertyDescriptor& propertyDescriptor, bool shouldThrow)\n");
1108 push(@$outputArray, "{\n");
1109 push(@$outputArray, " auto* thisObject = jsCast<${className}*>(object);\n");
1110 push(@$outputArray, " ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n\n");
1112 assert("CEReactions is not supported on having both named setters and indexed setters") if $namedSetterOperation && $namedSetterOperation->extendedAttributes->{CEReactions}
1113 && $indexedSetterOperation && $indexedSetterOperation->extendedAttributes->{CEReactions};
1114 if ($namedSetterOperation) {
1115 GenerateCustomElementReactionsStackIfNeeded($outputArray, $namedSetterOperation, "*state");
1117 if ($indexedSetterOperation) {
1118 GenerateCustomElementReactionsStackIfNeeded($outputArray, $indexedSetterOperation, "*state");
1121 # 1. If O supports indexed properties and P is an array index property name, then:
1122 if (GetIndexedGetterOperation($interface)) {
1123 # NOTE: The numbers are out of order because there is no reason doing steps 1, 3, and 4 if there
1124 # is no indexed property setter.
1126 if (!$indexedSetterOperation) {
1127 # 2. If O does not implement an interface with an indexed property setter, then return false.
1128 push(@$outputArray, " if (parseIndex(propertyName))\n");
1129 push(@$outputArray, " return false;\n\n");
1131 push(@$outputArray, " if (auto index = parseIndex(propertyName)) {\n");
1133 # 1. If the result of calling IsDataDescriptor(Desc) is false, then return false.
1134 push(@$outputArray, " if (!propertyDescriptor.isDataDescriptor())\n");
1135 push(@$outputArray, " return false;\n");
1137 # 3. Invoke the indexed property setter with P and Desc.[[Value]].
1138 GenerateInvokeIndexedPropertySetter($outputArray, " ", $interface, $indexedSetterOperation, "index.value()", "propertyDescriptor.value()");
1141 push(@$outputArray, " return true;\n");
1142 push(@$outputArray, " }\n\n");
1146 # 2. If O supports named properties, O does not implement an interface with the [Global] or [PrimaryGlobal]
1147 # extended attribute and P is not an unforgeable property name of O, then:
1148 if (GetNamedGetterOperation($interface) && !IsGlobalOrPrimaryGlobalInterface($interface)) {
1149 # FIMXE: We need a more comprehensive story for Symbols.
1150 push(@$outputArray, " if (!propertyName.isSymbol()) {\n");
1152 my $additionalIndent = "";
1154 my $hasUnforgableProperties = GenerateIsUnforgeablePropertyName($outputArray, $interface);
1155 if ($hasUnforgableProperties) {
1156 push(@$outputArray, " if (!isUnforgeablePropertyName(propertyName)) {\n");
1157 $additionalIndent .= " ";
1160 # 1. Let creating be true if P is not a supported property name, and false otherwise.
1161 # NOTE: This step is strength reduced into the only use of 'creating' in step 2.2.1
1163 # 2. If O implements an interface with the [OverrideBuiltins] extended attribute or O
1164 # does not have an own property named P, then:
1165 my $overrideBuiltins = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins");
1166 if (!$overrideBuiltins) {
1167 # FIXME: Is JSObject::getOwnPropertySlot the right function to call? Is there a function that will
1168 # only look at the actual properties, and not call into our implementation of the
1169 # [[GetOwnProperty]] hook?
1170 push(@$outputArray, $additionalIndent. " PropertySlot slot { thisObject, PropertySlot::InternalMethodType::VMInquiry };\n");
1171 push(@$outputArray, $additionalIndent. " if (!JSObject::getOwnPropertySlot(thisObject, state, propertyName, slot)) {\n");
1172 $additionalIndent .= " ";
1174 if (!$namedSetterOperation) {
1175 # 2.1. If creating is false and O does not implement an interface with a named property setter, then return false.
1176 push(@$outputArray, $additionalIndent . " if (thisObject->wrapped().isSupportedPropertyName(propertyNameToString(propertyName)))\n");
1177 push(@$outputArray, $additionalIndent . " return false;\n");
1179 # 2.2. If O implements an interface with a named property setter, then:
1181 # 2.2.1. If the result of calling IsDataDescriptor(Desc) is false, then return false.
1182 push(@$outputArray, $additionalIndent . " if (!propertyDescriptor.isDataDescriptor())\n");
1183 push(@$outputArray, $additionalIndent . " return false;\n");
1185 # 2.2.2. Invoke the named property setter with P and Desc.[[Value]].
1186 GenerateInvokeNamedPropertySetter($outputArray, $additionalIndent . " ", $interface, $namedSetterOperation, "propertyDescriptor.value()");
1187 if ($namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties}) {
1188 push(@$outputArray, $additionalIndent . " if (!isPropertySupported)\n");
1189 push(@$outputArray, $additionalIndent . " return JSObject::defineOwnProperty(object, state, propertyName, propertyDescriptor, shouldThrow);\n");
1191 # 2.2.3. Return true.
1192 push(@$outputArray, $additionalIndent . " return true;\n");
1195 if (!$overrideBuiltins) {
1196 push(@$outputArray, $additionalIndent . " }\n");
1199 if ($hasUnforgableProperties) {
1200 push(@$outputArray, " }\n");
1203 # Close the !propertyName.isSymbol() condition.
1204 push(@$outputArray, " }\n\n");
1207 push(@$outputArray, " PropertyDescriptor newPropertyDescriptor = propertyDescriptor;\n");
1209 # 3. If O does not implement an interface with the [Global] or [PrimaryGlobal] extended attribute,
1210 # then set Desc.[[Configurable]] to true.
1211 if (!IsGlobalOrPrimaryGlobalInterface($interface)) {
1212 push(@$outputArray, " newPropertyDescriptor.setConfigurable(true);\n");
1215 # 4. Return OrdinaryDefineOwnProperty(O, P, Desc).
1216 # FIXME: Does this do the same thing?
1217 push(@$outputArray, " return JSObject::defineOwnProperty(object, state, propertyName, newPropertyDescriptor, shouldThrow);\n");
1219 push(@$outputArray, "}\n\n");
1222 sub GenerateDeletePropertyCommon
1224 my ($outputArray, $interface, $className, $operation, $conditional) = @_;
1226 # This implements step 2 of https://heycam.github.io/webidl/#legacy-platform-object-delete
1227 # so it can be shared between the generation of deleteProperty and deletePropertyByIndex.
1229 # 2. If O supports named properties, O does not implement an interface with the
1230 # [Global] or [PrimaryGlobal] extended attribute and the result of calling the
1231 # named property visibility algorithm with property name P and object O is true,
1233 assert("Named property deleters are not allowed without a corresponding named property getter.") if !GetNamedGetterOperation($interface);
1234 assert("Named property deleters are not allowed on global object interfaces.") if IsGlobalOrPrimaryGlobalInterface($interface);
1236 AddToImplIncludes("JSDOMAbstractOperations.h", $conditional);
1237 my $overrideBuiltin = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins") ? "OverrideBuiltins::Yes" : "OverrideBuiltins::No";
1238 push(@$outputArray, " if (isVisibleNamedProperty<${overrideBuiltin}>(*state, thisObject, propertyName)) {\n");
1240 GenerateCustomElementReactionsStackIfNeeded($outputArray, $operation, "*state");
1242 # 2.1. If O does not implement an interface with a named property deleter, then return false.
1243 # 2.2. Let operation be the operation used to declare the named property deleter.
1244 # NOTE: We only add a deleteProperty implementation of we have a named property deleter.
1246 # 2.3. If operation was defined without an identifier, then:
1247 # 1. Perform the steps listed in the interface description to delete an existing named
1248 # property with P as the name.
1249 # 2. If the steps indicated that the deletion failed, then return false.
1250 # 2.4. Otherwise, operation was defined with an identifier:
1251 # 1. Perform the steps listed in the description of operation with P as the only argument
1253 # 2. If operation was declared with a return type of boolean and the steps returned false,
1254 # then return false.
1256 my $functionImplementationName = $operation->extendedAttributes->{ImplementedAs} || $codeGenerator->WK_lcfirst($operation->name) || "deleteNamedProperty";
1257 my $functionCall = "impl." . $functionImplementationName . "(propertyNameToString(propertyName))";
1259 # NOTE: We expect the implementation function of named deleters without an identifier to
1260 # return either bool or ExceptionOr<bool>. the implementation function of named deleters
1261 # with an identifier have no restriction, but if the return value of the operation is
1262 # boolean, we return that value, otherwise it is ignored (as per section 4.2).
1264 if ($operation->extendedAttributes->{MayThrowException}) {
1265 push(@$outputArray, " auto result = ${functionCall};\n");
1266 push(@$outputArray, " if (result.hasException()) {\n");
1267 push(@$outputArray, " auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
1268 push(@$outputArray, " propagateException(*state, throwScope, result.releaseException());\n");
1269 push(@$outputArray, " return true;\n");
1270 push(@$outputArray, " }\n\n");
1272 if (!$operation->name || $operation->name && $operation->type->name eq "boolean") {
1273 push(@$outputArray, " return result.releaseReturnValue();\n");
1275 push(@$outputArray, " return true;\n");
1278 if (!$operation->name || $operation->name && $operation->type->name eq "boolean") {
1279 push(@$outputArray, " return ${functionCall};\n");
1281 push(@$outputArray, " ${functionCall};\n");
1282 push(@$outputArray, " return true;\n");
1286 push(@$outputArray, " }\n");
1289 sub GenerateDeleteProperty
1291 my ($outputArray, $interface, $className, $operation, $conditional) = @_;
1293 # This implements https://heycam.github.io/webidl/#legacy-platform-object-delete for the
1294 # for the deleteProperty override hook.
1296 push(@$outputArray, "bool ${className}::deleteProperty(JSCell* cell, ExecState* state, PropertyName propertyName)\n");
1297 push(@$outputArray, "{\n");
1299 push(@$outputArray, " auto& thisObject = *jsCast<${className}*>(cell);\n");
1300 push(@$outputArray, " auto& impl = thisObject.wrapped();\n");
1302 # 1. If O supports indexed properties and P is an array index property name, then:
1303 # 1. Let index be the result of calling ToUint32(P).
1304 # 2. If index is not a supported property index, then return true.
1306 if (GetIndexedGetterOperation($interface)) {
1307 push(@$outputArray, " if (auto index = parseIndex(propertyName))\n");
1308 push(@$outputArray, " return !impl.isSupportedPropertyIndex(index.value());\n");
1311 # GenerateDeletePropertyCommon implements step 2.
1312 GenerateDeletePropertyCommon($outputArray, $interface, $className, $operation, $conditional);
1314 # FIXME: Instead of calling down JSObject::deleteProperty, perhaps we should implement
1315 # the remained of the algorithm ourselves.
1316 push(@$outputArray, " return JSObject::deleteProperty(cell, state, propertyName);\n");
1317 push(@$outputArray, "}\n\n");
1320 sub GenerateDeletePropertyByIndex
1322 my ($outputArray, $interface, $className, $operation, $conditional) = @_;
1324 # This implements https://heycam.github.io/webidl/#legacy-platform-object-delete for the
1325 # for the deletePropertyByIndex override hook.
1327 push(@$outputArray, "bool ${className}::deletePropertyByIndex(JSCell* cell, ExecState* state, unsigned index)\n");
1328 push(@$outputArray, "{\n");
1330 push(@$outputArray, " auto& thisObject = *jsCast<${className}*>(cell);\n");
1331 push(@$outputArray, " auto& impl = thisObject.wrapped();\n");
1333 # 1. If O supports indexed properties and P is an array index property name, then:
1334 # 1. Let index be the result of calling ToUint32(P).
1335 # 2. If index is not a supported property index, then return true.
1338 # NOTE: For deletePropertyByIndex, if there is an indexed getter, checking isSupportedPropertyIndex()
1339 # is all that needs to be done, no need to generate the .
1341 if (GetIndexedGetterOperation($interface)) {
1342 push(@$outputArray, " return !impl.isSupportedPropertyIndex(index);\n");
1344 push(@$outputArray, " auto propertyName = Identifier::from(state, index);\n");
1346 # GenerateDeletePropertyCommon implements step 2.
1347 GenerateDeletePropertyCommon($outputArray, $interface, $className, $operation, $conditional);
1349 # FIXME: Instead of calling down JSObject::deletePropertyByIndex, perhaps we should implement
1350 # the remaineder of the algoritm (steps 3 and 4) ourselves.
1352 # 3. If O has an own property with name P, then:
1353 # 1. If the property is not configurable, then return false.
1354 # 2. Otherwise, remove the property from O.
1357 push(@$outputArray, " return JSObject::deletePropertyByIndex(cell, state, index);\n");
1360 push(@$outputArray, "}\n\n");
1364 sub GenerateNamedDeleterDefinition
1366 my ($outputArray, $interface, $className) = @_;
1368 return if $interface->extendedAttributes->{CustomDeleteProperty};
1370 my $namedDeleterOperation = GetNamedDeleterOperation($interface);
1372 # This implements https://heycam.github.io/webidl/#legacy-platform-object-delete using
1373 # the deleteProperty and deletePropertyByIndex override hooks.
1375 assert("Named property deleters are not allowed without a corresponding named property getter.") if !GetNamedGetterOperation($interface);
1376 assert("Named property deleters are not allowed on global object interfaces.") if IsGlobalOrPrimaryGlobalInterface($interface);
1378 my $conditional = $namedDeleterOperation->extendedAttributes->{Conditional};
1380 my $conditionalString = $codeGenerator->GenerateConditionalStringFromAttributeValue($conditional);
1381 push(@$outputArray, "#if ${conditionalString}\n\n");;
1384 GenerateDeleteProperty($outputArray, $interface, $className, $namedDeleterOperation, $conditional);
1385 GenerateDeletePropertyByIndex($outputArray, $interface, $className, $namedDeleterOperation, $conditional);
1387 push(@implContent, "#endif\n\n") if $conditional;
1390 sub GenerateHeaderContentHeader
1392 my $interface = shift;
1393 my $className = "JS" . $interface->type->name;
1395 my @headerContentHeader;
1396 if ($interface->extendedAttributes->{AppleCopyright}) {
1397 @headerContentHeader = split("\r", $beginAppleCopyrightForHeaderFiles);
1399 @headerContentHeader = split("\r", $headerTemplate);
1402 push(@headerContentHeader, "\n#pragma once\n\n");
1404 my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
1405 push(@headerContentHeader, "#if ${conditionalString}\n\n") if $conditionalString;
1406 return @headerContentHeader;
1409 sub GenerateImplementationContentHeader
1411 my $interface = shift;
1412 my $className = "JS" . $interface->type->name;
1414 my @implContentHeader;
1415 if ($interface->extendedAttributes->{AppleCopyright}) {
1416 @implContentHeader = split("\r", $beginAppleCopyrightForSourceFiles);
1418 @implContentHeader = split("\r", $headerTemplate);
1421 push(@implContentHeader, "\n#include \"config.h\"\n");
1422 my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
1423 push(@implContentHeader, "\n#if ${conditionalString}\n\n") if $conditionalString;
1424 push(@implContentHeader, "#include \"$className.h\"\n\n");
1425 return @implContentHeader;
1428 sub NeedsImplementationClass
1430 my ($interface) = @_;
1432 return 0 if $interface->extendedAttributes->{JSBuiltin};
1436 sub ShouldGenerateToWrapped
1438 my ($hasParent, $interface) = @_;
1440 return 0 if not NeedsImplementationClass($interface);
1441 return 1 if !$hasParent or $interface->extendedAttributes->{JSGenerateToNativeObject};
1442 return 1 if $interface->parentType && $interface->parentType->name eq "EventTarget";
1446 sub ShouldGenerateWrapperOwnerCode
1448 my ($hasParent, $interface) = @_;
1450 return 0 if not NeedsImplementationClass($interface);
1451 return 1 if !$hasParent;
1452 return 1 if GetGenerateIsReachable($interface);
1453 return 1 if GetCustomIsReachable($interface);
1454 return 1 if $interface->extendedAttributes->{JSCustomFinalize};
1455 return 1 if $codeGenerator->InheritsExtendedAttribute($interface, "ActiveDOMObject");
1459 sub ShouldGenerateToJSDeclaration
1461 my ($hasParent, $interface) = @_;
1463 return 0 if ($interface->extendedAttributes->{SuppressToJSObject});
1464 return 0 if not NeedsImplementationClass($interface);
1465 return 0 if $interface->extendedAttributes->{CustomProxyToJSObject};
1466 return 1 if (!$hasParent or $interface->extendedAttributes->{JSGenerateToJSObject} or $interface->extendedAttributes->{CustomToJSObject});
1467 return 1 if $interface->parentType && $interface->parentType->name eq "EventTarget";
1468 return 1 if $interface->extendedAttributes->{Constructor} or $interface->extendedAttributes->{NamedConstructor};
1472 sub ShouldGenerateToJSImplementation
1474 my ($hasParent, $interface) = @_;
1476 return 0 if not ShouldGenerateToJSDeclaration($hasParent, $interface);
1477 return 1 if not $interface->extendedAttributes->{CustomToJSObject};
1481 sub GetTypeNameForDisplayInException
1485 # FIXME: Add more type specializations.
1486 return "(" . join(" or ", map { $_->name } GetFlattenedMemberTypes($type)) . ")" if $type->isUnion;
1490 sub GetArgumentExceptionFunction
1492 my ($interface, $argument, $argumentIndex, $quotedFunctionName) = @_;
1494 my $name = $argument->name;
1495 my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($interface);
1496 my $typeName = GetTypeNameForDisplayInException($argument->type);
1498 if ($codeGenerator->IsCallbackInterface($argument->type) || $codeGenerator->IsCallbackFunction($argument->type)) {
1499 # FIXME: We should have specialized messages for callback interfaces vs. callback functions.
1500 return "throwArgumentMustBeFunctionError(state, scope, ${argumentIndex}, \"${name}\", \"${visibleInterfaceName}\", ${quotedFunctionName});";
1503 if ($codeGenerator->IsWrapperType($argument->type) || $codeGenerator->IsBufferSourceType($argument->type)) {
1504 return "throwArgumentTypeError(state, scope, ${argumentIndex}, \"${name}\", \"${visibleInterfaceName}\", ${quotedFunctionName}, \"${typeName}\");";
1507 if ($codeGenerator->IsEnumType($argument->type)) {
1508 my $className = GetEnumerationClassName($argument->type, $interface);
1509 return "throwArgumentMustBeEnumError(state, scope, ${argumentIndex}, \"${name}\", \"${visibleInterfaceName}\", ${quotedFunctionName}, expectedEnumerationValues<${className}>());";
1515 sub GetArgumentExceptionThrower
1517 my ($interface, $argument, $argumentIndex, $quotedFunctionName) = @_;
1519 my $functionCall = GetArgumentExceptionFunction($interface, $argument, $argumentIndex, $quotedFunctionName);
1520 return "[](JSC::ExecState& state, JSC::ThrowScope& scope) { " . $functionCall . " }" if $functionCall;
1523 sub GetAttributeExceptionFunction
1525 my ($interface, $attribute) = @_;
1527 my $name = $attribute->name;
1528 my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($interface);
1529 my $typeName = GetTypeNameForDisplayInException($attribute->type);
1531 if ($codeGenerator->IsWrapperType($attribute->type) || $codeGenerator->IsBufferSourceType($attribute->type)) {
1532 return "throwAttributeTypeError(state, scope, \"${visibleInterfaceName}\", \"${name}\", \"${typeName}\");";
1536 sub GetAttributeExceptionThrower
1538 my ($interface, $attribute) = @_;
1540 my $functionCall = GetAttributeExceptionFunction($interface, $attribute);
1541 return "[](JSC::ExecState& state, JSC::ThrowScope& scope) { " . $functionCall . " }" if $functionCall;
1545 sub PassArgumentExpression
1547 my ($name, $context) = @_;
1549 my $type = $context->type;
1551 return "WTFMove(${name})" if $type->isNullable;
1553 if ($codeGenerator->IsBufferSourceType($type)) {
1554 return "*${name}" if $type->name eq "ArrayBuffer";
1555 return "${name}.releaseNonNull()";
1558 return "${name}.releaseNonNull()" if $codeGenerator->IsCallbackInterface($type) || $codeGenerator->IsCallbackFunction($type) || ($codeGenerator->IsPromiseType($type) && (ref($context) ne "IDLArgument" || !$context->isOptional));
1559 return "*${name}" if $codeGenerator->IsWrapperType($type);
1560 return "WTFMove(${name})";
1563 sub GetAttributeGetterName
1565 my ($interface, $className, $attribute) = @_;
1567 return $codeGenerator->WK_lcfirst($className) . "Constructor" . $codeGenerator->WK_ucfirst($attribute->name) if $attribute->isStatic;
1568 return GetJSBuiltinFunctionName($className, $attribute) if IsJSBuiltin($interface, $attribute);
1569 return "js" . $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name) . ($codeGenerator->IsConstructorType($attribute->type) ? "Constructor" : "");
1572 sub GetAttributeSetterName
1574 my ($interface, $className, $attribute) = @_;
1576 return "set" . $codeGenerator->WK_ucfirst($className) . "Constructor" . $codeGenerator->WK_ucfirst($attribute->name) if $attribute->isStatic;
1577 return "set" . $codeGenerator->WK_ucfirst(GetJSBuiltinFunctionName($className, $attribute)) if IsJSBuiltin($interface, $attribute);
1578 return "setJS" . $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name) . ($codeGenerator->IsConstructorType($attribute->type) ? "Constructor" : "");
1583 my ($interface, $className, $operation) = @_;
1585 return GetJSBuiltinFunctionName($className, $operation) if IsJSBuiltin($interface, $operation);
1587 my $functionName = $operation->name;
1588 $functionName = "SymbolIterator" if $functionName eq "[Symbol.Iterator]";
1590 my $kind = $operation->isStatic ? "Constructor" : (OperationShouldBeOnInstance($interface, $operation) ? "Instance" : "Prototype");
1591 return $codeGenerator->WK_lcfirst($className) . $kind . "Function" . $codeGenerator->WK_ucfirst($functionName);
1594 sub GetFullyQualifiedImplementationCallName
1596 my ($interface, $property, $implementationName, $implExpression, $conditional) = @_;
1598 my $implementedBy = $property->extendedAttributes->{ImplementedBy};
1599 if ($implementedBy) {
1600 AddToImplIncludes("${implementedBy}.h", $conditional);
1601 return "WebCore::${implementedBy}::${implementationName}";
1604 if ($property->isStatic || $property->extendedAttributes->{Constructor} || $property->extendedAttributes->{NamedConstructor}) {
1605 return $interface->type->name . "::${implementationName}";
1608 if ($property->isMapLike) {
1609 return "forward" . $codeGenerator->WK_ucfirst($property->name) . "ToMapLike";
1612 return "${implExpression}.${implementationName}";
1615 sub AddAdditionalArgumentsForImplementationCall
1617 my ($arguments, $interface, $property, $implExpression, $stateExpression, $thisObjectExpression) = @_;
1619 if ($property->extendedAttributes->{ImplementedBy} && !$property->isStatic) {
1620 unshift(@$arguments, $implExpression);
1623 if ($property->isMapLike) {
1624 push(@$arguments, $stateExpression);
1625 push(@$arguments, $thisObjectExpression);
1629 sub GetSpecialAccessorOperationForType
1631 my ($interface, $special, $firstParameterType, $numberOfParameters) = @_;
1633 foreach my $operation (@{$interface->operations}, @{$interface->anonymousOperations}) {
1634 my $specials = $operation->specials;
1635 my $specialExists = grep { $_ eq $special } @$specials;
1636 my $arguments = $operation->arguments;
1637 if ($specialExists and scalar(@$arguments) == $numberOfParameters and $arguments->[0]->type->name eq $firstParameterType) {
1645 sub IsGlobalOrPrimaryGlobalInterface
1647 my $interface = shift;
1649 return $interface->extendedAttributes->{Global} || $interface->extendedAttributes->{PrimaryGlobal};
1652 sub AttributeShouldBeOnInstance
1654 my $interface = shift;
1655 my $attribute = shift;
1657 return 1 if IsGlobalOrPrimaryGlobalInterface($interface);
1658 return 1 if $codeGenerator->IsConstructorType($attribute->type);
1660 # [Unforgeable] attributes should be on the instance.
1661 # https://heycam.github.io/webidl/#Unforgeable
1662 return 1 if IsUnforgeable($interface, $attribute);
1664 if ($interface->extendedAttributes->{CheckSecurity}) {
1665 return 0 if $attribute->extendedAttributes->{DoNotCheckSecurity};
1666 return 0 if $attribute->extendedAttributes->{DoNotCheckSecurityOnGetter};
1673 sub IsAlwaysExposedOnInterface
1675 my ($interfaceExposures, $contextExposures) = @_;
1677 my %contextExposureSet = ();
1679 if (ref($contextExposures) eq "ARRAY") {
1680 foreach my $contextExposure (@$contextExposures) {
1681 $contextExposureSet{$contextExposure} = 1;
1684 $contextExposureSet{$contextExposures} = 1;
1687 if (ref($interfaceExposures) ne "ARRAY") {
1688 $interfaceExposures = [$interfaceExposures];
1691 foreach my $interfaceExposure (@$interfaceExposures) {
1692 return 0 unless exists $contextExposureSet{$interfaceExposure};
1698 sub NeedsRuntimeCheck
1700 my ($interface, $context) = @_;
1702 if ($context->extendedAttributes->{Exposed}) {
1703 my $interfaceExposures = $interface->extendedAttributes->{Exposed} || "Window";
1704 return 1 if !IsAlwaysExposedOnInterface($interfaceExposures, $context->extendedAttributes->{Exposed});
1707 return $context->extendedAttributes->{EnabledAtRuntime}
1708 || $context->extendedAttributes->{EnabledForWorld}
1709 || $context->extendedAttributes->{EnabledBySetting}
1710 || $context->extendedAttributes->{DisabledByQuirk}
1711 || $context->extendedAttributes->{SecureContext}
1712 || $context->extendedAttributes->{ContextHasServiceWorkerScheme};
1715 # https://heycam.github.io/webidl/#es-operations
1716 sub OperationShouldBeOnInstance
1718 my ($interface, $operation) = @_;
1720 return 1 if IsGlobalOrPrimaryGlobalInterface($interface);
1722 # [Unforgeable] operations should be on the instance. https://heycam.github.io/webidl/#Unforgeable
1723 if (IsUnforgeable($interface, $operation)) {
1724 assert("The bindings generator does not support putting runtime-enabled operations on the instance yet (except for global objects):[" . $interface->type->name . "::" . $operation->name . "]") if NeedsRuntimeCheck($interface, $operation);
1731 sub OperationHasForcedReturnValue
1733 my ($operation) = @_;
1735 foreach my $argument (@{$operation->arguments}) {
1736 return 1 if $argument->extendedAttributes->{ReturnValue};
1741 sub IsAcceleratedDOMAttribute
1743 my ($interface, $attribute) = @_;
1745 # If we use CustomGetterSetter in IDL code generator we cannot skip type check.
1746 return 0 if NeedsRuntimeCheck($interface, $attribute) and AttributeShouldBeOnInstance($interface, $attribute);
1747 return 0 if $attribute->extendedAttributes->{PrivateIdentifier} and AttributeShouldBeOnInstance($interface, $attribute);
1749 # If the interface has special logic for casting we cannot hoist type check to JSC.
1750 return 0 if $interface->extendedAttributes->{ImplicitThis};
1751 return 0 if $interface->extendedAttributes->{CustomProxyToJSObject};
1753 return 0 if $attribute->isStatic;
1754 return 0 if $attribute->isMapLike;
1755 return 0 if $codeGenerator->IsConstructorType($attribute->type);
1756 return 0 if IsJSBuiltin($interface, $attribute);
1757 return 0 if $attribute->extendedAttributes->{LenientThis};
1758 return 0 if $codeGenerator->IsPromiseType($attribute->type);
1759 return 0 if $attribute->extendedAttributes->{DOMJIT};
1763 sub GetJSCAttributesForAttribute
1765 my $interface = shift;
1766 my $attribute = shift;
1769 push(@specials, "JSC::PropertyAttribute::DontDelete") if IsUnforgeable($interface, $attribute);
1771 # As per Web IDL specification, constructor properties on the ECMAScript global object should not be enumerable.
1772 my $isGlobalConstructor = $codeGenerator->IsConstructorType($attribute->type);
1773 push(@specials, "JSC::PropertyAttribute::DontEnum") if ($attribute->extendedAttributes->{NotEnumerable} || $isGlobalConstructor);
1774 push(@specials, "JSC::PropertyAttribute::ReadOnly") if IsReadonly($attribute);
1775 push(@specials, "JSC::PropertyAttribute::CustomAccessor") unless $isGlobalConstructor or IsJSBuiltin($interface, $attribute);
1776 push(@specials, "JSC::PropertyAttribute::DOMAttribute") if IsAcceleratedDOMAttribute($interface, $attribute);
1777 push(@specials, "JSC::PropertyAttribute::DOMJITAttribute") if $attribute->extendedAttributes->{DOMJIT};
1778 push(@specials, "JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin") if IsJSBuiltin($interface, $attribute);
1779 return "static_cast<unsigned>(" . ((@specials > 0) ? join(" | ", @specials) : "0") . ")";
1782 sub GetIndexedGetterOperation
1784 my $interface = shift;
1785 return GetSpecialAccessorOperationForType($interface, "getter", "unsigned long", 1);
1788 sub GetIndexedSetterOperation
1790 my $interface = shift;
1791 return GetSpecialAccessorOperationForType($interface, "setter", "unsigned long", 2);
1794 sub GetNamedGetterOperation
1796 my $interface = shift;
1797 return GetSpecialAccessorOperationForType($interface, "getter", "DOMString", 1);
1800 sub GetNamedSetterOperation
1802 my $interface = shift;
1803 return GetSpecialAccessorOperationForType($interface, "setter", "DOMString", 2);
1806 sub GetNamedDeleterOperation
1808 my $interface = shift;
1809 return GetSpecialAccessorOperationForType($interface, "deleter", "DOMString", 1);
1812 sub InstanceOperationCount
1814 my $interface = shift;
1817 foreach my $operation (@{$interface->operations}) {
1818 $count++ if OperationShouldBeOnInstance($interface, $operation);
1824 sub PrototypeOperationCount
1826 my $interface = shift;
1829 foreach my $operation (@{$interface->operations}) {
1830 $count++ if !$operation->isStatic && !OperationShouldBeOnInstance($interface, $operation);
1833 $count += scalar @{$interface->iterable->operations} if $interface->iterable;
1834 $count += scalar @{$interface->mapLike->operations} if $interface->mapLike;
1835 $count += scalar @{$interface->serializable->operations} if $interface->serializable;
1840 sub InstancePropertyCount
1842 my $interface = shift;
1844 foreach my $attribute (@{$interface->attributes}) {
1845 $count++ if AttributeShouldBeOnInstance($interface, $attribute);
1847 $count += InstanceOperationCount($interface);
1851 sub PrototypePropertyCount
1853 my $interface = shift;
1855 foreach my $attribute (@{$interface->attributes}) {
1856 $count++ if !AttributeShouldBeOnInstance($interface, $attribute);
1858 $count += PrototypeOperationCount($interface);
1859 $count++ if NeedsConstructorProperty($interface);
1863 sub InstanceOverridesGetOwnPropertySlot
1865 my $interface = shift;
1866 return $interface->extendedAttributes->{CustomGetOwnPropertySlot}
1867 || $interface->extendedAttributes->{Plugin}
1868 || GetIndexedGetterOperation($interface)
1869 || GetNamedGetterOperation($interface);
1872 sub InstanceOverridesGetOwnPropertyNames
1874 my $interface = shift;
1875 return $interface->extendedAttributes->{CustomGetOwnPropertyNames}
1876 || GetIndexedGetterOperation($interface)
1877 || GetNamedGetterOperation($interface);
1880 sub InstanceOverridesPut
1882 my $interface = shift;
1883 return $interface->extendedAttributes->{CustomPut}
1884 || $interface->extendedAttributes->{Plugin}
1885 || GetIndexedSetterOperation($interface)
1886 || GetNamedSetterOperation($interface);
1889 sub InstanceOverridesDefineOwnProperty
1891 my $interface = shift;
1893 return 0 if $interface->extendedAttributes->{DefaultDefineOwnProperty};
1895 return $interface->extendedAttributes->{CustomDefineOwnProperty}
1896 || GetIndexedSetterOperation($interface)
1897 || GetNamedSetterOperation($interface);
1900 sub InstanceOverridesDeleteProperty
1902 my $interface = shift;
1903 return $interface->extendedAttributes->{CustomDeleteProperty}
1904 || GetNamedDeleterOperation($interface);
1907 sub PrototypeHasStaticPropertyTable
1909 my $interface = shift;
1910 my $numConstants = @{$interface->constants};
1911 return $numConstants > 0 || PrototypePropertyCount($interface) > 0;
1914 sub InstanceNeedsVisitChildren
1916 my $interface = shift;
1918 foreach my $attribute (@{$interface->attributes}) {
1919 return 1 if $attribute->extendedAttributes->{CachedAttribute};
1922 return 1 if $interface->extendedAttributes->{JSCustomMarkFunction};
1923 return 1 if $interface->extendedAttributes->{ReportExtraMemoryCost};
1927 sub InstanceNeedsEstimatedSize
1929 my $interface = shift;
1930 return $interface->extendedAttributes->{ReportExtraMemoryCost};
1933 sub GetImplClassName
1935 my $interface = shift;
1937 return $interface->type->name;
1940 sub IsClassNameWordBoundary
1942 my ($name, $i) = @_;
1944 # Interpret negative numbers as distance from end of string, just as the substr function does.
1945 $i += length($name) if $i < 0;
1948 return 1 if $i == 0;
1949 return 1 if $i == length($name);
1950 return 0 if $i > length($name);
1952 my $checkString = substr($name, $i - 1);
1953 return $checkString =~ /^[^A-Z][A-Z]/ || $checkString =~ /^[A-Z][A-Z][^A-Z]/;
1956 sub IsPrefixRemovable
1958 my ($class, $name, $i) = @_;
1960 return IsClassNameWordBoundary($name, $i)
1961 && (IsClassNameWordBoundary($class, $i) && substr($class, 0, $i) eq substr($name, 0, $i)
1962 || IsClassNameWordBoundary($class, -$i) && substr($class, -$i) eq substr($name, 0, $i));
1965 sub GetNestedClassName
1967 my ($interface, $name) = @_;
1969 my $class = GetImplClassName($interface);
1970 my $member = $codeGenerator->WK_ucfirst($name);
1972 # Since the enumeration name will be nested in the class name's namespace, remove any words
1973 # that happen to match the start or end of the class name. If an enumeration is named TrackType or
1974 # TextTrackType, and the class is named TextTrack, then we will get a name like TextTrack::Type.
1975 my $memberLength = length($member);
1976 my $longestPrefixLength = 0;
1977 if ($member =~ /^[A-Z]./) {
1978 for (my $i = 2; $i < $memberLength - 1; $i++) {
1979 $longestPrefixLength = $i if IsPrefixRemovable($class, $member, $i);
1982 $member = substr($member, $longestPrefixLength);
1984 return "${class}::$member";
1987 sub GetEnumerationClassName
1989 my ($type, $interface) = @_;
1991 assert("Not a type") if ref($type) ne "IDLType";
1993 if ($codeGenerator->HasEnumImplementationNameOverride($type)) {
1994 return $codeGenerator->GetEnumImplementationNameOverride($type);
1997 my $name = $type->name;
1999 return $name if $codeGenerator->IsExternalEnumType($type);
2000 return $name unless defined($interface);
2002 return GetNestedClassName($interface, $name);
2005 sub GetEnumerationValueName
2009 return "EmptyString" if $name eq "";
2010 $name = join("", map { $codeGenerator->WK_ucfirst($_) } split("-", $name));
2011 $name = "_$name" if $name =~ /^\d/;
2015 sub GenerateEnumerationHeader
2017 my ($object, $enumeration, $className) = @_;
2019 # - Add default header template and header protection.
2020 push(@headerContentHeader, GenerateHeaderContentHeader($enumeration));
2022 $headerIncludes{"${className}.h"} = 1;
2024 push(@headerContent, "\nnamespace WebCore {\n\n");
2025 push(@headerContent, GenerateEnumerationHeaderContent($enumeration, $className));
2026 push(@headerContent, "} // namespace WebCore\n");
2028 my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2029 push(@headerContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
2032 sub GenerateEnumerationImplementation
2034 my ($object, $enumeration, $className) = @_;
2036 # - Add default header template
2037 push(@implContentHeader, GenerateImplementationContentHeader($enumeration));
2039 push(@implContent, "\n\nnamespace WebCore {\n");
2040 push(@implContent, "using namespace JSC;\n\n");
2041 push(@implContent, GenerateEnumerationImplementationContent($enumeration, $className));
2042 push(@implContent, "} // namespace WebCore\n");
2044 my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2045 push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
2048 sub GenerateEnumerationImplementationContent
2050 my ($enumeration, $className, $interface, $conditionalString) = @_;
2052 # FIXME: A little ugly to have this be a side effect instead of a return value.
2053 AddToImplIncludes("<JavaScriptCore/JSString.h>");
2054 AddToImplIncludes("<JavaScriptCore/JSCInlines.h>");
2055 AddToImplIncludes("JSDOMConvertEnumeration.h");
2058 $result .= "#if ${conditionalString}\n\n" if $conditionalString;
2061 $result .= "String convertEnumerationToString($className enumerationValue)\n";
2063 AddToImplIncludes("<wtf/NeverDestroyed.h>");
2064 $result .= " static const NeverDestroyed<String> values[] = {\n";
2065 foreach my $value (@{$enumeration->values}) {
2067 $result .= " emptyString(),\n";
2069 $result .= " MAKE_STATIC_STRING_IMPL(\"$value\"),\n";
2074 foreach my $value (@{$enumeration->values}) {
2075 my $enumerationValueName = GetEnumerationValueName($value);
2076 $result .= " static_assert(static_cast<size_t>(${className}::$enumerationValueName) == $index, \"${className}::$enumerationValueName is not $index as expected\");\n";
2079 $result .= " ASSERT(static_cast<size_t>(enumerationValue) < WTF_ARRAY_LENGTH(values));\n";
2080 $result .= " return values[static_cast<size_t>(enumerationValue)];\n";
2084 # FIXME: Change to take VM& instead of ExecState*.
2085 $result .= "template<> JSString* convertEnumerationToJS(ExecState& state, $className enumerationValue)\n";
2087 $result .= " return jsStringWithCache(&state, convertEnumerationToString(enumerationValue));\n";
2090 # FIXME: Change to take VM& instead of ExecState&.
2091 # FIXME: Consider using toStringOrNull to make exception checking faster.
2092 # FIXME: Consider finding a more efficient way to match against all the strings quickly.
2093 $result .= "template<> std::optional<$className> parseEnumeration<$className>(ExecState& state, JSValue value)\n";
2095 $result .= " auto stringValue = value.toWTFString(&state);\n";
2096 foreach my $value (@{$enumeration->values}) {
2097 my $enumerationValueName = GetEnumerationValueName($value);
2099 $result .= " if (stringValue.isEmpty())\n";
2101 $result .= " if (stringValue == \"$value\")\n";
2103 $result .= " return ${className}::${enumerationValueName};\n";
2105 $result .= " return std::nullopt;\n";
2108 $result .= "template<> const char* expectedEnumerationValues<$className>()\n";
2110 $result .= " return \"\\\"" . join ("\\\", \\\"", @{$enumeration->values}) . "\\\"\";\n";
2113 $result .= "#endif\n\n" if $conditionalString;
2118 sub GenerateEnumerationsImplementationContent
2120 my ($interface, $enumerations) = @_;
2122 return "" unless @$enumerations;
2125 foreach my $enumeration (@$enumerations) {
2126 my $className = GetEnumerationClassName($enumeration->type, $interface);
2127 my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2128 $result .= GenerateEnumerationImplementationContent($enumeration, $className, $interface, $conditionalString);
2133 sub GenerateEnumerationHeaderContent
2135 my ($enumeration, $className, $conditionalString) = @_;
2137 $headerIncludes{"JSDOMConvertEnumeration.h"} = 1;
2140 $result .= "#if ${conditionalString}\n\n" if $conditionalString;
2142 my $exportMacro = GetExportMacroForJSClass($enumeration);
2144 $result .= "${exportMacro}String convertEnumerationToString($className);\n";
2145 $result .= "template<> ${exportMacro}JSC::JSString* convertEnumerationToJS(JSC::ExecState&, $className);\n\n";
2146 $result .= "template<> ${exportMacro}std::optional<$className> parseEnumeration<$className>(JSC::ExecState&, JSC::JSValue);\n";
2147 $result .= "template<> ${exportMacro}const char* expectedEnumerationValues<$className>();\n\n";
2148 $result .= "#endif\n\n" if $conditionalString;
2153 sub GenerateEnumerationsHeaderContent
2155 my ($interface, $enumerations) = @_;
2157 return "" unless @$enumerations;
2159 # FIXME: Could optimize this to only generate the parts of each enumeration that are actually
2160 # used, which would require iterating over everything in the interface.
2163 foreach my $enumeration (@$enumerations) {
2164 my $className = GetEnumerationClassName($enumeration->type, $interface);
2165 my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2166 $result .= GenerateEnumerationHeaderContent($enumeration, $className, $conditionalString);
2171 sub GetDictionaryClassName
2173 my ($type, $interface) = @_;
2175 if ($codeGenerator->HasDictionaryImplementationNameOverride($type)) {
2176 return $codeGenerator->GetDictionaryImplementationNameOverride($type);
2179 my $name = $type->name;
2180 return $name if $codeGenerator->IsExternalDictionaryType($type);
2181 return $name unless defined($interface);
2182 return GetNestedClassName($interface, $name);
2185 sub GenerateDefaultValue
2187 my ($typeScope, $context, $type, $defaultValue) = @_;
2189 if ($codeGenerator->IsStringType($type)) {
2190 my $useAtomicString = $type->extendedAttributes->{AtomicString};
2191 if ($defaultValue eq "null") {
2192 return $useAtomicString ? "nullAtom()" : "String()";
2193 } elsif ($defaultValue eq "\"\"") {
2194 return $useAtomicString ? "emptyAtom()" : "emptyString()";
2196 return $useAtomicString ? "AtomicString(${defaultValue}, AtomicString::ConstructFromLiteral)" : "${defaultValue}_s";
2200 if ($codeGenerator->IsEnumType($type)) {
2201 # FIXME: Would be nice to report an error if the value does not have quote marks around it.
2202 # FIXME: Would be nice to report an error if the value is not one of the enumeration values.
2203 if ($defaultValue eq "null") {
2204 die if !$type->isNullable;
2205 return "std::nullopt";
2207 my $className = GetEnumerationClassName($type, $typeScope);
2208 my $enumerationValueName = GetEnumerationValueName(substr($defaultValue, 1, -1));
2209 return $className . "::" . $enumerationValueName;
2211 if ($defaultValue eq "null") {
2212 if ($type->isUnion) {
2213 return "std::nullopt" if $type->isNullable;
2215 my $IDLType = GetIDLType($typeScope, $type);
2216 return "convert<${IDLType}>(state, jsNull());";
2219 return "jsNull()" if $type->name eq "any";
2220 return "nullptr" if $codeGenerator->IsWrapperType($type) || $codeGenerator->IsBufferSourceType($type);
2221 return "String()" if $codeGenerator->IsStringType($type);
2222 return "std::nullopt";
2225 if ($defaultValue eq "[]") {
2226 my $IDLType = GetIDLType($typeScope, $type);
2227 return "Converter<${IDLType}>::ReturnType{ }";
2230 return "jsUndefined()" if $defaultValue eq "undefined";
2231 return "PNaN" if $defaultValue eq "NaN";
2233 return $defaultValue;
2236 sub GenerateDictionaryHeaderContent
2238 my ($dictionary, $className, $conditionalString) = @_;
2240 $headerIncludes{"JSDOMConvertDictionary.h"} = 1;
2242 my $exportMacro = GetExportMacroForJSClass($dictionary);
2245 $result .= "#if ${conditionalString}\n\n" if $conditionalString;
2246 $result .= "template<> ${exportMacro}${className} convertDictionary<${className}>(JSC::ExecState&, JSC::JSValue);\n\n";
2248 if ($dictionary->extendedAttributes->{JSGenerateToJSObject}) {
2249 $result .= "${exportMacro}JSC::JSObject* convertDictionaryToJS(JSC::ExecState&, JSDOMGlobalObject&, const ${className}&);\n\n";
2252 $result .= "#endif\n\n" if $conditionalString;
2256 sub GenerateDictionariesHeaderContent
2258 my ($typeScope, $allDictionaries) = @_;
2260 return "" unless @$allDictionaries;
2263 foreach my $dictionary (@$allDictionaries) {
2264 $headerIncludes{$typeScope->type->name . ".h"} = 1 if $typeScope;
2265 my $className = GetDictionaryClassName($dictionary->type, $typeScope);
2266 my $conditionalString = $codeGenerator->GenerateConditionalString($dictionary);
2267 $result .= GenerateDictionaryHeaderContent($dictionary, $className, $conditionalString);
2272 sub GenerateDictionaryImplementationContent
2274 my ($dictionary, $className, $interface) = @_;
2278 my $name = $dictionary->type->name;
2279 my $typeScope = $interface || $dictionary;
2281 my $conditional = $dictionary->extendedAttributes->{Conditional};
2283 my $conditionalString = $codeGenerator->GenerateConditionalStringFromAttributeValue($conditional);
2284 $result .= "#if ${conditionalString}\n\n";
2287 # FIXME: A little ugly to have this be a side effect instead of a return value.
2288 AddToImplIncludes("<JavaScriptCore/JSCInlines.h>");
2289 AddToImplIncludes("JSDOMConvertDictionary.h");
2291 # https://heycam.github.io/webidl/#es-dictionary
2292 $result .= "template<> $className convertDictionary<$className>(ExecState& state, JSValue value)\n";
2294 $result .= " VM& vm = state.vm();\n";
2295 $result .= " auto throwScope = DECLARE_THROW_SCOPE(vm);\n";
2296 $result .= " bool isNullOrUndefined = value.isUndefinedOrNull();\n";
2297 $result .= " auto* object = isNullOrUndefined ? nullptr : value.getObject();\n";
2299 # 1. If Type(V) is not Undefined, Null or Object, then throw a TypeError.
2300 $result .= " if (UNLIKELY(!isNullOrUndefined && !object)) {\n";
2301 $result .= " throwTypeError(&state, throwScope);\n";
2302 $result .= " return { };\n";
2305 # 2. Let dict be an empty dictionary value of type D; every dictionary member is initially considered to be not present.
2307 # 3. Let dictionaries be a list consisting of D and all of D’s inherited dictionaries, in order from least to most derived.
2309 push(@dictionaries, $dictionary);
2310 my $parentType = $dictionary->parentType;
2311 while (defined($parentType)) {
2312 my $parentDictionary = $codeGenerator->GetDictionaryByType($parentType);
2313 assert("Unable to find definition for dictionary named '" . $parentType->name . "'!") unless defined($parentDictionary);
2314 unshift(@dictionaries, $parentDictionary);
2315 $parentType = $parentDictionary->parentType;
2321 $result .= " $className result;\n";
2323 # 4. For each dictionary dictionary in dictionaries, in order:
2324 foreach my $dictionary (@dictionaries) {
2325 # For each dictionary member member declared on dictionary, in lexicographical order:
2326 my @sortedMembers = sort { $a->name cmp $b->name } @{$dictionary->members};
2327 foreach my $member (@sortedMembers) {
2328 $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.
2330 my $type = $member->type;
2331 AddToImplIncludesForIDLType($type);
2333 # 4.1. Let key be the identifier of member.
2334 my $key = $member->name;
2335 my $implementedAsKey = $member->extendedAttributes->{ImplementedAs} || $key;
2337 # 4.2. Let value be an ECMAScript value, depending on Type(V):
2338 $result .= " JSValue ${key}Value;\n";
2339 $result .= " if (isNullOrUndefined)\n";
2340 $result .= " ${key}Value = jsUndefined();\n";
2341 $result .= " else {\n";
2342 $result .= " ${key}Value = object->get(&state, Identifier::fromString(&state, \"${key}\"));\n";
2343 $result .= " RETURN_IF_EXCEPTION(throwScope, { });\n";
2346 my $IDLType = GetIDLType($typeScope, $type);
2348 # 4.3. If value is not undefined, then:
2349 $result .= " if (!${key}Value.isUndefined()) {\n";
2351 my $nativeValue = JSValueToNative($typeScope, $member, "${key}Value", $member->extendedAttributes->{Conditional}, "&state", "state");
2352 $result .= " result.$implementedAsKey = $nativeValue;\n";
2353 $result .= " RETURN_IF_EXCEPTION(throwScope, { });\n";
2355 # Value is undefined.
2356 # 4.4. Otherwise, if value is undefined but the dictionary member has a default value, then:
2357 if (!$member->isRequired && defined $member->default) {
2358 $result .= " } else\n";
2359 $result .= " result.$implementedAsKey = " . GenerateDefaultValue($typeScope, $member, $member->type, $member->default) . ";\n";
2360 } elsif ($member->isRequired) {
2361 # 4.5. Otherwise, if value is undefined and the dictionary member is a required dictionary member, then throw a TypeError.
2362 $result .= " } else {\n";
2363 $result .= " throwRequiredMemberTypeError(state, throwScope, \"". $member->name ."\", \"$name\", \"". GetTypeNameForDisplayInException($type) ."\");\n";
2364 $result .= " return { };\n";
2373 $result .= " return result;\n";
2376 if ($dictionary->extendedAttributes->{JSGenerateToJSObject}) {
2377 AddToImplIncludes("JSDOMGlobalObject.h");
2378 AddToImplIncludes("<JavaScriptCore/ObjectConstructor.h>");
2380 $result .= "JSC::JSObject* convertDictionaryToJS(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const ${className}& dictionary)\n";
2382 $result .= " auto& vm = state.vm();\n\n";
2384 # 1. Let O be ! ObjectCreate(%ObjectPrototype%).
2385 $result .= " auto result = constructEmptyObject(&state, globalObject.objectPrototype());\n\n";
2387 # 2. Let dictionaries be a list consisting of D and all of D’s inherited dictionaries,
2388 # in order from least to most derived.
2389 # NOTE: This was done above.
2391 # 3. For each dictionary dictionary in dictionaries, in order:
2392 foreach my $dictionary (@dictionaries) {
2393 # 3.1. For each dictionary member member declared on dictionary, in lexicographical order:
2394 my @sortedMembers = sort { $a->name cmp $b->name } @{$dictionary->members};
2395 foreach my $member (@sortedMembers) {
2396 my $key = $member->name;
2397 my $implementedAsKey = $member->extendedAttributes->{ImplementedAs} || $key;
2398 my $valueExpression = "dictionary.${implementedAsKey}";
2400 # 1. Let key be the identifier of member.
2401 # 2. If the dictionary member named key is present in V, then:
2402 # 1. Let idlValue be the value of member on V.
2403 # 2. Let value be the result of converting idlValue to an ECMAScript value.
2404 # 3. Perform ! CreateDataProperty(O, key, value).
2406 if (!$member->isRequired && not defined $member->default) {
2407 my $IDLType = GetIDLType($typeScope, $member->type);
2408 my $conversionExpression = NativeToJSValueUsingReferences($member, $typeScope, "${IDLType}::extractValueFromNullable(${valueExpression})", "globalObject");
2410 $result .= " if (!${IDLType}::isNullValue(${valueExpression})) {\n";
2411 $result .= " auto ${key}Value = ${conversionExpression};\n";
2412 $result .= " result->putDirect(vm, JSC::Identifier::fromString(&vm, \"${key}\"), ${key}Value);\n";
2415 my $conversionExpression = NativeToJSValueUsingReferences($member, $typeScope, $valueExpression, "globalObject");
2417 $result .= " auto ${key}Value = ${conversionExpression};\n";
2418 $result .= " result->putDirect(vm, JSC::Identifier::fromString(&vm, \"${key}\"), ${key}Value);\n";
2423 $result .= " return result;\n";
2427 $result .= "#endif\n\n" if $conditional;
2432 sub GenerateDictionariesImplementationContent
2434 my ($typeScope, $allDictionaries) = @_;
2437 foreach my $dictionary (@$allDictionaries) {
2438 my $className = GetDictionaryClassName($dictionary->type, $typeScope);
2439 $result .= GenerateDictionaryImplementationContent($dictionary, $className, $typeScope);
2444 sub GetJSTypeForNode
2446 my ($interface) = @_;
2448 if ($codeGenerator->InheritsInterface($interface, "Document")) {
2449 return "JSDocumentWrapperType";
2451 if ($codeGenerator->InheritsInterface($interface, "DocumentFragment")) {
2452 return "JSDocumentFragmentNodeType";
2454 if ($codeGenerator->InheritsInterface($interface, "DocumentType")) {
2455 return "JSDocumentTypeNodeType";
2457 if ($codeGenerator->InheritsInterface($interface, "ProcessingInstruction")) {
2458 return "JSProcessingInstructionNodeType";
2460 if ($codeGenerator->InheritsInterface($interface, "CDATASection")) {
2461 return "JSCDATASectionNodeType";
2463 if ($codeGenerator->InheritsInterface($interface, "Attr")) {
2464 return "JSAttrNodeType";
2466 if ($codeGenerator->InheritsInterface($interface, "Comment")) {
2467 return "JSCommentNodeType";
2469 if ($codeGenerator->InheritsInterface($interface, "Text")) {
2470 return "JSTextNodeType";
2472 if ($codeGenerator->InheritsInterface($interface, "Element")) {
2473 return "JSElementType";
2475 return "JSNodeType";
2480 my ($object, $interface, $enumerations, $dictionaries) = @_;
2482 my $interfaceName = $interface->type->name;
2483 my $className = "JS$interfaceName";
2484 my %structureFlags = ();
2486 my $hasParent = $interface->parentType || $interface->extendedAttributes->{JSLegacyParent};
2487 my $parentClassName = GetParentClassName($interface);
2488 my $needsVisitChildren = InstanceNeedsVisitChildren($interface);
2490 # - Add default header template and header protection
2491 push(@headerContentHeader, GenerateHeaderContentHeader($interface));
2494 $headerIncludes{"$parentClassName.h"} = 1;
2496 $headerIncludes{"JSDOMWrapper.h"} = 1;
2497 if ($interface->isException) {
2498 $headerIncludes{"<JavaScriptCore/ErrorPrototype.h>"} = 1;
2502 $headerIncludes{"$interfaceName.h"} = 1 if $hasParent && $interface->extendedAttributes->{JSGenerateToNativeObject};
2504 $headerIncludes{"SVGElement.h"} = 1 if $className =~ /^JSSVG/;
2506 my $implType = GetImplClassName($interface);
2508 my $numConstants = @{$interface->constants};
2509 my $numAttributes = @{$interface->attributes};
2510 my $numOperations = @{$interface->operations};
2512 push(@headerContent, "\nnamespace WebCore {\n\n");
2514 if ($codeGenerator->IsSVGAnimatedType($interface->type)) {
2515 $headerIncludes{"$interfaceName.h"} = 1;
2517 # Implementation class forward declaration
2518 if (IsDOMGlobalObject($interface)) {
2519 AddClassForwardIfNeeded($interface->type);
2523 push(@headerContent, "class JSWindowProxy;\n\n") if $interfaceName eq "DOMWindow" or $interfaceName eq "RemoteDOMWindow";
2525 my $exportMacro = GetExportMacroForJSClass($interface);
2528 push(@headerContent, "class $exportMacro$className : public $parentClassName {\n");
2530 # Static create methods
2531 push(@headerContent, "public:\n");
2532 push(@headerContent, " using Base = $parentClassName;\n");
2533 push(@headerContent, " using DOMWrapped = $implType;\n") if $hasParent;
2535 if ($interfaceName eq "DOMWindow" || $interfaceName eq "RemoteDOMWindow") {
2536 push(@headerContent, " static $className* create(JSC::VM& vm, JSC::Structure* structure, Ref<$implType>&& impl, JSWindowProxy* proxy)\n");
2537 push(@headerContent, " {\n");
2538 push(@headerContent, " $className* ptr = new (NotNull, JSC::allocateCell<$className>(vm.heap)) ${className}(vm, structure, WTFMove(impl), proxy);\n");
2539 push(@headerContent, " ptr->finishCreation(vm, proxy);\n");
2540 push(@headerContent, " return ptr;\n");
2541 push(@headerContent, " }\n\n");
2542 } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope") || $codeGenerator->InheritsInterface($interface, "WorkletGlobalScope")) {
2543 push(@headerContent, " static $className* create(JSC::VM& vm, JSC::Structure* structure, Ref<$implType>&& impl, JSC::JSProxy* proxy)\n");
2544 push(@headerContent, " {\n");
2545 push(@headerContent, " $className* ptr = new (NotNull, JSC::allocateCell<$className>(vm.heap)) ${className}(vm, structure, WTFMove(impl));\n");
2546 push(@headerContent, " ptr->finishCreation(vm, proxy);\n");
2547 push(@headerContent, " return ptr;\n");
2548 push(@headerContent, " }\n\n");
2549 } elsif ($interface->extendedAttributes->{MasqueradesAsUndefined}) {
2550 AddIncludesForImplementationTypeInHeader($implType);
2551 push(@headerContent, " static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<$implType>&& impl)\n");
2552 push(@headerContent, " {\n");
2553 push(@headerContent, " globalObject->masqueradesAsUndefinedWatchpoint()->fireAll(globalObject->vm(), \"Allocated masquerading object\");\n");
2554 push(@headerContent, " $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject, WTFMove(impl));\n");
2555 push(@headerContent, " ptr->finishCreation(globalObject->vm());\n");
2556 push(@headerContent, " return ptr;\n");
2557 push(@headerContent, " }\n\n");
2558 } elsif (!NeedsImplementationClass($interface)) {
2559 push(@headerContent, " static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject)\n");
2560 push(@headerContent, " {\n");
2561 push(@headerContent, " $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject);\n");
2562 push(@headerContent, " ptr->finishCreation(globalObject->vm());\n");
2563 push(@headerContent, " return ptr;\n");
2564 push(@headerContent, " }\n\n");
2566 AddIncludesForImplementationTypeInHeader($implType);
2567 push(@headerContent, " static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<$implType>&& impl)\n");
2568 push(@headerContent, " {\n");
2569 push(@headerContent, " $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject, WTFMove(impl));\n");
2570 push(@headerContent, " ptr->finishCreation(globalObject->vm());\n");
2571 push(@headerContent, " return ptr;\n");
2572 push(@headerContent, " }\n\n");
2575 push(@headerContent, " static const bool needsDestruction = false;\n\n") if IsDOMGlobalObject($interface);
2577 $structureFlags{"JSC::HasStaticPropertyTable"} = 1 if InstancePropertyCount($interface) > 0;
2578 $structureFlags{"JSC::NewImpurePropertyFiresWatchpoints"} = 1 if $interface->extendedAttributes->{NewImpurePropertyFiresWatchpoints};
2579 $structureFlags{"JSC::IsImmutablePrototypeExoticObject"} = 1 if $interface->extendedAttributes->{IsImmutablePrototypeExoticObject};
2580 $structureFlags{"JSC::MasqueradesAsUndefined"} = 1 if $interface->extendedAttributes->{MasqueradesAsUndefined};
2581 $structureFlags{"JSC::ImplementsHasInstance | JSC::ImplementsDefaultHasInstance"} = 1 if $interfaceName eq "DOMWindow";
2584 unless (ShouldUseGlobalObjectPrototype($interface)) {
2585 push(@headerContent, " static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&);\n");
2586 push(@headerContent, " static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&);\n");
2589 # JSValue to implementation type
2590 if (ShouldGenerateToWrapped($hasParent, $interface)) {
2591 # FIXME: Add extended attribute for this.
2592 my @toWrappedArguments = ();
2593 push(@toWrappedArguments, "JSC::VM&");
2594 push(@toWrappedArguments, "JSC::ExecState&") if $interface->type->name eq "XPathNSResolver";
2595 push(@toWrappedArguments, "JSC::JSValue");
2597 my $toWrappedType = $interface->type->name eq "XPathNSResolver" ? "RefPtr<${implType}>" : "${implType}*";
2600 $export = "WEBCORE_EXPORT " if $interface->extendedAttributes->{ExportToWrappedFunction};
2601 push(@headerContent, " static ${export}${toWrappedType} toWrapped(" . join(", ", @toWrappedArguments) . ");\n");
2604 $headerTrailingIncludes{"${className}Custom.h"} = 1 if $interface->extendedAttributes->{JSCustomHeader};
2606 my $namedGetterOperation = GetNamedGetterOperation($interface);
2607 my $indexedGetterOperation = GetIndexedGetterOperation($interface);
2609 # FIXME: Why doesn't this also include Indexed Getters and [CustomGetOwnPropertySlot]
2610 if ($namedGetterOperation) {
2611 if ($codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins")) {
2612 $structureFlags{"JSC::GetOwnPropertySlotIsImpure"} = 1;
2614 $structureFlags{"JSC::GetOwnPropertySlotIsImpureForPropertyAbsence"} = 1;
2618 # ClassInfo MethodTable declarations.
2620 if (InstanceOverridesGetOwnPropertySlot($interface)) {
2621 push(@headerContent, " static bool getOwnPropertySlot(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&);\n");
2622 $structureFlags{"JSC::OverridesGetOwnPropertySlot"} = 1;
2623 push(@headerContent, " static bool getOwnPropertySlotByIndex(JSC::JSObject*, JSC::ExecState*, unsigned propertyName, JSC::PropertySlot&);\n");
2624 $structureFlags{"JSC::InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero"} = 1;
2627 if (InstanceOverridesGetOwnPropertyNames($interface)) {
2628 push(@headerContent, " static void getOwnPropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode = JSC::EnumerationMode());\n");
2629 $structureFlags{"JSC::OverridesGetPropertyNames"} = 1;
2632 if (InstanceOverridesPut($interface)) {
2633 push(@headerContent, " static bool put(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&);\n");
2634 push(@headerContent, " static bool putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName, JSC::JSValue, bool shouldThrow);\n");
2637 if (InstanceOverridesDefineOwnProperty($interface)) {
2638 push(@headerContent, " static bool defineOwnProperty(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, const JSC::PropertyDescriptor&, bool shouldThrow);\n");
2641 if (InstanceOverridesDeleteProperty($interface)) {
2642 push(@headerContent, " static bool deleteProperty(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName);\n");
2643 push(@headerContent, " static bool deletePropertyByIndex(JSC::JSCell*, JSC::ExecState*, unsigned);\n");
2646 if (InstanceOverridesGetCallData($interface)) {
2647 push(@headerContent, " static JSC::CallType getCallData(JSC::JSCell*, JSC::CallData&);\n\n");
2648 $headerIncludes{"<JavaScriptCore/CallData.h>"} = 1;
2649 $structureFlags{"JSC::OverridesGetCallData"} = 1;
2652 if ($interface->extendedAttributes->{CustomGetPrototype}) {
2653 push(@headerContent, " static JSC::JSValue getPrototype(JSC::JSObject*, JSC::ExecState*);\n");
2656 if ($interface->extendedAttributes->{CustomToStringName}) {
2657 push(@headerContent, " static String toStringName(const JSC::JSObject*, JSC::ExecState*);\n");
2660 if ($interface->extendedAttributes->{CustomPreventExtensions}) {
2661 push(@headerContent, " static bool preventExtensions(JSC::JSObject*, JSC::ExecState*);\n");
2664 if (InstanceNeedsEstimatedSize($interface)) {
2665 push(@headerContent, " static size_t estimatedSize(JSCell*, JSC::VM&);\n");
2669 push(@headerContent, " static void destroy(JSC::JSCell*);\n");
2673 if ($interfaceName eq "Node") {
2674 push(@headerContent, "\n");
2675 push(@headerContent, "protected:\n");
2676 push(@headerContent, " static const JSC::ClassInfo s_info;\n");
2677 push(@headerContent, "public:\n");
2678 push(@headerContent, " static constexpr const JSC::ClassInfo* info() { return &s_info; }\n\n");
2680 push(@headerContent, "\n");
2681 push(@headerContent, " DECLARE_INFO;\n\n");
2685 push(@headerContent, " static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)\n");
2686 push(@headerContent, " {\n");
2687 if (IsDOMGlobalObject($interface)) {
2688 push(@headerContent, " return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::GlobalObjectType, StructureFlags), info());\n");
2689 } elsif ($codeGenerator->InheritsInterface($interface, "Node")) {
2690 my $type = GetJSTypeForNode($interface);
2691 push(@headerContent, " return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSType($type), StructureFlags), info());\n");
2692 } elsif ($codeGenerator->InheritsInterface($interface, "Event")) {
2693 push(@headerContent, " return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSType(JSEventType), StructureFlags), info());\n");
2695 push(@headerContent, " return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());\n");
2697 push(@headerContent, " }\n\n");
2699 # Custom pushEventHandlerScope function
2700 if ($interface->extendedAttributes->{CustomPushEventHandlerScope}) {
2701 push(@headerContent, " JSC::JSScope* pushEventHandlerScope(JSC::ExecState*, JSC::JSScope*) const;\n\n");
2704 # Constructor object getter
2705 unless ($interface->extendedAttributes->{NoInterfaceObject}) {
2706 push(@headerContent, " static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*);\n");
2707 push(@headerContent, " static JSC::JSValue getNamedConstructor(JSC::VM&, JSC::JSGlobalObject*);\n") if $interface->extendedAttributes->{NamedConstructor};
2710 # Serializer function.
2711 if ($interface->serializable) {
2712 push(@headerContent, " static JSC::JSObject* serialize(JSC::ExecState&, ${className}& thisObject, JSDOMGlobalObject&, JSC::ThrowScope&);\n");
2715 my $numCustomOperations = 0;
2716 my $numCustomAttributes = 0;
2718 my $hasForwardDeclaringOperations = 0;
2719 my $hasForwardDeclaringAttributes = 0;
2721 my $hasDOMJITAttributes = 0;
2723 # Attribute and function enums
2724 if ($numAttributes > 0) {
2725 foreach my $attribute (@{$interface->attributes}) {
2726 $numCustomAttributes++ if HasCustomGetter($attribute);
2727 $numCustomAttributes++ if HasCustomSetter($attribute);
2728 if ($attribute->extendedAttributes->{CachedAttribute}) {
2729 my $conditionalString = $codeGenerator->GenerateConditionalString($attribute);
2730 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2731 push(@headerContent, " mutable JSC::WriteBarrier<JSC::Unknown> m_" . $attribute->name . ";\n");
2732 $numCachedAttributes++;
2733 push(@headerContent, "#endif\n") if $conditionalString;
2735 $hasDOMJITAttributes = 1 if $attribute->extendedAttributes->{DOMJIT};
2737 $hasForwardDeclaringAttributes = 1 if $attribute->extendedAttributes->{ForwardDeclareInHeader};
2742 if ($needsVisitChildren) {
2743 push(@headerContent, " static void visitChildren(JSCell*, JSC::SlotVisitor&);\n");
2744 push(@headerContent, " void visitAdditionalChildren(JSC::SlotVisitor&);\n") if $interface->extendedAttributes->{JSCustomMarkFunction};
2745 push(@headerContent, "\n");
2747 if ($interface->extendedAttributes->{JSCustomMarkFunction}) {
2748 # We assume that the logic in visitAdditionalChildren is highly volatile, and during a
2749 # concurrent GC or in between eden GCs something may happen that would lead to this
2750 # logic behaving differently. Since this could mark objects or add opaque roots, this
2751 # means that after any increment of mutator resumption in a concurrent GC and at least
2752 # once during any eden GC we need to re-execute visitAdditionalChildren on any objects
2753 # that we had executed it on before. We do this using the DOM's own MarkingConstraint,
2754 # which will call visitOutputConstraints on all objects in the DOM's own
2755 # outputConstraintSubspace. visitOutputConstraints is the name JSC uses for the method
2756 # that the GC calls to ask an object is it would like to mark anything else after the
2757 # program resumed since the last call to visitChildren or visitOutputConstraints. Since
2758 # this just calls visitAdditionalChildren, you usually don't have to worry about this.
2759 push(@headerContent, " static void visitOutputConstraints(JSCell*, JSC::SlotVisitor&);\n");
2760 my $subspaceFunc = IsDOMGlobalObject($interface) ? "globalObjectOutputConstraintSubspaceFor" : "outputConstraintSubspaceFor";
2761 push(@headerContent, " template<typename> static JSC::CompleteSubspace* subspaceFor(JSC::VM& vm) { return $subspaceFunc(vm); }\n");
2765 if (NeedsImplementationClass($interface)) {
2766 push(@headerContent, " static void heapSnapshot(JSCell*, JSC::HeapSnapshotBuilder&);\n");
2769 if ($numCustomAttributes > 0) {
2770 push(@headerContent, "\n // Custom attributes\n");
2772 foreach my $attribute (@{$interface->attributes}) {
2773 my $conditionalString = $codeGenerator->GenerateConditionalString($attribute);
2774 if (HasCustomGetter($attribute)) {
2775 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2776 my $methodName = $codeGenerator->WK_lcfirst($attribute->name);
2777 push(@headerContent, " JSC::JSValue " . $methodName . "(JSC::ExecState&) const;\n");
2778 push(@headerContent, "#endif\n") if $conditionalString;
2780 if (HasCustomSetter($attribute) && !IsReadonly($attribute)) {
2781 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2782 push(@headerContent, " void set" . $codeGenerator->WK_ucfirst($attribute->name) . "(JSC::ExecState&, JSC::JSValue);\n");
2783 push(@headerContent, "#endif\n") if $conditionalString;
2788 foreach my $operation (@{$interface->operations}) {
2789 $numCustomOperations++ if HasCustomMethod($operation);
2790 $hasForwardDeclaringOperations = 1 if $operation->extendedAttributes->{ForwardDeclareInHeader};
2793 if ($numCustomOperations > 0) {
2794 my $inAppleCopyright = 0;
2795 push(@headerContent, "\n // Custom functions\n");
2796 foreach my $operation (@{$interface->operations}) {
2797 next unless HasCustomMethod($operation);
2798 next if $operation->{overloads} && $operation->{overloadIndex} != 1;
2800 if ($operation->extendedAttributes->{AppleCopyright}) {
2801 if (!$inAppleCopyright) {
2802 push(@headerContent, $beginAppleCopyrightForHeaderFiles);
2803 $inAppleCopyright = 1;
2805 } elsif ($inAppleCopyright) {
2806 push(@headerContent, $endAppleCopyright);
2807 $inAppleCopyright = 0;
2810 my $conditionalString = $codeGenerator->GenerateConditionalString($operation);
2811 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2813 my $functionImplementationName = $operation->extendedAttributes->{ImplementedAs} || $codeGenerator->WK_lcfirst($operation->name);
2815 my @functionArguments = ();
2816 push(@functionArguments, "JSC::ExecState&");
2817 push(@functionArguments, "Ref<DeferredPromise>&&") if $codeGenerator->IsPromiseType($operation->type) && !$operation->extendedAttributes->{ReturnsOwnPromise};
2819 push(@headerContent, " " . ($operation->isStatic ? "static " : "") . "JSC::JSValue " . $functionImplementationName . "(" . join(", ", @functionArguments) . ");\n");
2821 push(@headerContent, "#endif\n") if $conditionalString;
2823 push(@headerContent, $endAppleCopyright) if $inAppleCopyright;
2826 if (NeedsImplementationClass($interface)) {
2828 push(@headerContent, " $interfaceName& wrapped() const\n");
2829 push(@headerContent, " {\n");
2830 push(@headerContent, " return static_cast<$interfaceName&>(Base::wrapped());\n");
2831 push(@headerContent, " }\n");
2836 if (%structureFlags) {
2837 push(@headerContent, "public:\n");
2838 push(@headerContent, " static const unsigned StructureFlags = ");
2839 foreach my $structureFlag (sort (keys %structureFlags)) {
2840 push(@headerContent, $structureFlag . " | ");
2842 push(@headerContent, "Base::StructureFlags;\n");
2845 push(@headerContent, "protected:\n");
2848 if ($interfaceName eq "DOMWindow" || $interfaceName eq "RemoteDOMWindow") {
2849 push(@headerContent, " $className(JSC::VM&, JSC::Structure*, Ref<$implType>&&, JSWindowProxy*);\n");
2850 } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope") || $codeGenerator->InheritsInterface($interface, "WorkletGlobalScope")) {
2851 push(@headerContent, " $className(JSC::VM&, JSC::Structure*, Ref<$implType>&&);\n");
2852 } elsif (!NeedsImplementationClass($interface)) {
2853 push(@headerContent, " $className(JSC::Structure*, JSDOMGlobalObject&);\n\n");
2855 push(@headerContent, " $className(JSC::Structure*, JSDOMGlobalObject&, Ref<$implType>&&);\n\n");
2858 if ($interfaceName eq "DOMWindow" || $interfaceName eq "RemoteDOMWindow") {
2859 push(@headerContent, " void finishCreation(JSC::VM&, JSWindowProxy*);\n");
2860 } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope") || $codeGenerator->InheritsInterface($interface, "WorkletGlobalScope")) {
2861 push(@headerContent, " void finishCreation(JSC::VM&, JSC::JSProxy*);\n");
2863 push(@headerContent, " void finishCreation(JSC::VM&);\n");
2866 push(@headerContent, "};\n\n");
2868 if (ShouldGenerateWrapperOwnerCode($hasParent, $interface)) {
2869 if ($interfaceName ne "Node" && $codeGenerator->InheritsInterface($interface, "Node")) {
2870 $headerIncludes{"JSNode.h"} = 1;
2871 push(@headerContent, "class JS${interfaceName}Owner : public JSNodeOwner {\n");
2873 push(@headerContent, "class JS${interfaceName}Owner : public JSC::WeakHandleOwner {\n");
2875 $headerIncludes{"<wtf/NeverDestroyed.h>"} = 1;
2876 push(@headerContent, "public:\n");
2877 push(@headerContent, " virtual bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor&, const char**);\n");
2878 push(@headerContent, " virtual void finalize(JSC::Handle<JSC::Unknown>, void* context);\n");
2879 push(@headerContent, "};\n");
2880 push(@headerContent, "\n");
2881 push(@headerContent, "inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld&, $implType*)\n");
2882 push(@headerContent, "{\n");
2883 push(@headerContent, " static NeverDestroyed<JS${interfaceName}Owner> owner;\n");
2884 push(@headerContent, " return &owner.get();\n");
2885 push(@headerContent, "}\n");
2886 push(@headerContent, "\n");
2887 push(@headerContent, "inline void* wrapperKey($implType* wrappableObject)\n");
2888 push(@headerContent, "{\n");
2889 push(@headerContent, " return wrappableObject;\n");
2890 push(@headerContent, "}\n");
2891 push(@headerContent, "\n");
2893 if (ShouldGenerateToJSDeclaration($hasParent, $interface)) {
2894 # Node and NodeList have custom inline implementations which thus cannot be exported.
2895 # FIXME: The special case for Node and NodeList should probably be implemented via an IDL attribute.
2896 if ($implType eq "Node" or $implType eq "NodeList") {
2897 push(@headerContent, "JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, $implType&);\n");
2899 push(@headerContent, $exportMacro."JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, $implType&);\n");
2901 push(@headerContent, "inline JSC::JSValue toJS(JSC::ExecState* state, JSDOMGlobalObject* globalObject, $implType* impl) { return impl ? toJS(state, globalObject, *impl) : JSC::jsNull(); }\n");
2903 push(@headerContent, "JSC::JSValue toJSNewlyCreated(JSC::ExecState*, JSDOMGlobalObject*, Ref<$implType>&&);\n");
2904 push(@headerContent, "inline JSC::JSValue toJSNewlyCreated(JSC::ExecState* state, JSDOMGlobalObject* globalObject, RefPtr<$implType>&& impl) { return impl ? toJSNewlyCreated(state, globalObject, impl.releaseNonNull()) : JSC::jsNull(); }\n");
2907 push(@headerContent, "\n");
2909 GeneratePrototypeDeclaration(\@headerContent, $className, $interface) if HeaderNeedsPrototypeDeclaration($interface);
2911 if ($hasForwardDeclaringOperations) {
2912 my $inAppleCopyright = 0;
2913 push(@headerContent,"// Functions\n\n");
2914 foreach my $operation (@{$interface->operations}) {
2915 next if $operation->{overloadIndex} && $operation->{overloadIndex} > 1;
2916 next unless $operation->extendedAttributes->{ForwardDeclareInHeader};
2918 if ($operation->extendedAttributes->{AppleCopyright}) {
2919 if (!$inAppleCopyright) {
2920 push(@headerContent, $beginAppleCopyrightForHeaderFiles);
2921 $inAppleCopyright = 1;
2923 } elsif ($inAppleCopyright) {
2924 push(@headerContent, $endAppleCopyright);
2925 $inAppleCopyright = 0;
2928 my $conditionalAttribute = GetConditionalForOperationConsideringOverloads($operation);
2929 my $conditionalString = $conditionalAttribute ? $codeGenerator->GenerateConditionalStringFromAttributeValue($conditionalAttribute) : undef;
2930 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2931 my $functionName = GetFunctionName($interface, $className, $operation);
2932 push(@headerContent, "JSC::EncodedJSValue JSC_HOST_CALL ${functionName}(JSC::ExecState*);\n");
2933 push(@headerContent, "#endif\n") if $conditionalString;
2936 push(@headerContent, $endAppleCopyright) if $inAppleCopyright;
2937 push(@headerContent,"\n");
2940 if ($hasForwardDeclaringAttributes) {
2941 push(@headerContent,"// Attributes\n\n");
2942 foreach my $attribute (@{$interface->attributes}) {
2943 next unless $attribute->extendedAttributes->{ForwardDeclareInHeader};
2945 my $conditionalString = $codeGenerator->GenerateConditionalString($attribute);
2946 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2947 my $getter = GetAttributeGetterName($interface, $className, $attribute);
2948 push(@headerContent, "JSC::EncodedJSValue ${getter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::PropertyName);\n");
2949 if (!IsReadonly($attribute)) {
2950 my $setter = GetAttributeSetterName($interface, $className, $attribute);
2951 push(@headerContent, "bool ${setter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::EncodedJSValue);\n");
2953 push(@headerContent, "#endif\n") if $conditionalString;
2957 # CheckSubClass Snippet function.
2958 if ($interface->extendedAttributes->{DOMJIT}) {
2959 $headerIncludes{"<JavaScriptCore/Snippet.h>"} = 1;
2960 push(@headerContent, "#if ENABLE(JIT)\n");
2961 push(@headerContent, "Ref<JSC::Snippet> checkSubClassSnippetFor${className}();\n");
2962 push(@headerContent, "#endif\n");
2965 if ($hasDOMJITAttributes) {
2966 $headerIncludes{"<JavaScriptCore/DOMJITGetterSetter.h>"} = 1;
2967 push(@headerContent,"// DOM JIT Attributes\n\n");
2968 foreach my $attribute (@{$interface->attributes}) {
2969 next unless $attribute->extendedAttributes->{DOMJIT};
2970 assert("Only DOMJIT=Getter is supported for attributes") unless $codeGenerator->ExtendedAttributeContains($attribute->extendedAttributes->{DOMJIT}, "Getter");
2972 my $interfaceName = $interface->type->name;
2973 my $className = $interfaceName . $codeGenerator->WK_ucfirst($attribute->name);
2974 my $domJITClassName = $className . "Attribute";
2976 push(@headerContent, "#if ENABLE(JIT)\n");
2977 push(@headerContent, "Ref<JSC::DOMJIT::CallDOMGetterSnippet> compile${domJITClassName}();\n");
2978 push(@headerContent, "#endif\n\n");
2982 if (HasCustomConstructor($interface)) {
2983 push(@headerContent, "// Custom constructor\n");
2984 push(@headerContent, "JSC::EncodedJSValue JSC_HOST_CALL construct${className}(JSC::ExecState&);\n\n");
2987 if (NeedsImplementationClass($interface)) {
2988 my $toWrappedType = $interface->type->name eq "XPathNSResolver" ? "RefPtr<${implType}>" : "${implType}*";
2989 $headerIncludes{"JSDOMWrapper.h"} = 1;
2991 push(@headerContent, "template<> struct JSDOMWrapperConverterTraits<${implType}> {\n");
2992 push(@headerContent, " using WrapperClass = ${className};\n");
2993 push(@headerContent, " using ToWrappedReturnType = ${toWrappedType};\n");
2994 push(@headerContent, "};\n");
2997 push(@headerContent, GenerateEnumerationsHeaderContent($interface, $enumerations));
2998 push(@headerContent, GenerateDictionariesHeaderContent($interface, $dictionaries));
3000 my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
3001 push(@headerContent, "\n} // namespace WebCore\n");
3002 push(@headerContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
3004 if ($interface->extendedAttributes->{AppleCopyright}) {
3005 push(@headerContent, "\n");
3006 push(@headerContent, split("\r", $endAppleCopyright));
3009 # - Generate dependencies.
3010 if ($writeDependencies) {
3012 $codeGenerator->ForAllParents($interface, sub {
3013 my $currentInterface = shift;
3014 push(@ancestors, $currentInterface->type->name);
3016 for my $dictionary (@$dictionaries) {
3017 my $parentType = $dictionary->parentType;
3018 while (defined($parentType)) {
3019 push(@ancestors, $parentType->name) if $codeGenerator->IsExternalDictionaryType($parentType);
3020 my $parentDictionary = $codeGenerator->GetDictionaryByType($parentType);
3021 assert("Unable to find definition for dictionary named '" . $parentType->name . "'!") unless defined($parentDictionary);
3022 $parentType = $parentDictionary->parentType;
3025 push(@depsContent, "$className.h : ", join(" ", map { "$_.idl" } @ancestors), "\n");
3026 push(@depsContent, map { "$_.idl :\n" } @ancestors);
3030 sub GeneratePropertiesHashTable
3032 my ($object, $interface, $isInstance, $hashKeys, $hashSpecials, $hashValue1, $hashValue2, $conditionals, $readWriteConditionals, $runtimeEnabledOperations, $runtimeEnabledAttributes) = @_;
3034 # FIXME: These should be functions on $interface.
3035 my $interfaceName = $interface->type->name;
3036 my $className = "JS$interfaceName";
3038 # - Add all properties in a hashtable definition
3039 my $propertyCount = $isInstance ? InstancePropertyCount($interface) : PrototypePropertyCount($interface);
3041 if (!$isInstance && NeedsConstructorProperty($interface)) {
3042 die if !$propertyCount;
3043 push(@$hashKeys, "constructor");
3044 my $getter = "js" . $interfaceName . "Constructor";
3045 push(@$hashValue1, $getter);
3047 my $setter = "setJS" . $interfaceName . "Constructor";
3048 push(@$hashValue2, $setter);
3049 push(@$hashSpecials, "static_cast<unsigned>(JSC::PropertyAttribute::DontEnum)");
3052 return 0 if !$propertyCount;
3054 my @attributes = @{$interface->attributes};
3055 push(@attributes, @{$interface->mapLike->attributes}) if $interface->mapLike;
3057 foreach my $attribute (@attributes) {
3058 next if ($attribute->isStatic);
3059 next if AttributeShouldBeOnInstance($interface, $attribute) != $isInstance;
3061 # Global objects add RuntimeEnabled attributes after creation so do not add them to the static table.
3062 if ($isInstance && NeedsRuntimeCheck($interface, $attribute)) {
3063 $propertyCount -= 1;
3067 my $name = $attribute->name;
3068 push(@$hashKeys, $name);
3070 my $special = GetJSCAttributesForAttribute($interface, $attribute);
3071 push(@$hashSpecials, $special);
3073 if ($attribute->extendedAttributes->{DOMJIT}) {
3074 push(@$hashValue1, "&DOMJITAttributeFor" . $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name));
3076 my $getter = GetAttributeGetterName($interface, $className, $attribute);
3077 push(@$hashValue1, $getter);
3080 if (IsReadonly($attribute)) {
3081 push(@$hashValue2, "0");
3083 my $setter = GetAttributeSetterName($interface, $className, $attribute);
3084 push(@$hashValue2, $setter);
3087 my $conditional = $attribute->extendedAttributes->{Conditional};
3088 $conditionals->{$name} = $conditional if $conditional;
3089 my $readWriteConditional = $attribute->extendedAttributes->{ConditionallyReadWrite};
3090 $readWriteConditionals->{$name} = $readWriteConditional if $readWriteConditional;
3092 if (NeedsRuntimeCheck($interface, $attribute)) {
3093 push(@$runtimeEnabledAttributes, $attribute);
3097 my @operations = @{$interface->operations};
3098 push(@operations, @{$interface->iterable->operations}) if IsKeyValueIterableInterface($interface);
3099 push(@operations, @{$interface->mapLike->operations}) if $interface->mapLike;
3100 push(@operations, @{$interface->serializable->operations}) if $interface->serializable;
3101 foreach my $operation (@operations) {
3102 next if ($operation->extendedAttributes->{PrivateIdentifier} and not $operation->extendedAttributes->{PublicIdentifier});
3103 next if ($operation->isStatic);
3104 next if $operation->{overloadIndex} && $operation->{overloadIndex} > 1;
3105 next if OperationShouldBeOnInstance($interface, $operation) != $isInstance;
3106 next if $operation->name eq "[Symbol.Iterator]";
3108 # Global objects add RuntimeEnabled operations after creation so do not add them to the static table.
3109 if ($isInstance && NeedsRuntimeCheck($interface, $operation)) {
3110 $propertyCount -= 1;
3114 my $name = $operation->name;
3115 push(@$hashKeys, $name);
3117 my $functionName = GetFunctionName($interface, $className, $operation);
3118 push(@$hashValue1, $functionName);
3120 my $functionLength = GetFunctionLength($operation);
3122 if ($operation->extendedAttributes->{DOMJIT}) {
3123 push(@$hashValue2, "&DOMJITSignatureFor" . $interface->type->name . $codeGenerator->WK_ucfirst($operation->name));
3125 push(@$hashValue2, $functionLength);
3128 push(@$hashSpecials, ComputeFunctionSpecial($interface, $operation));
3130 my $conditional = GetConditionalForOperationConsideringOverloads($operation);
3131 $conditionals->{$name} = $conditional if $conditional;
3133 if (NeedsRuntimeCheck($interface, $operation)) {
3134 push(@$runtimeEnabledOperations, $operation);
3138 return $propertyCount;
3141 # This computes an effective overload set for a given operation / constructor,
3142 # which represents the allowable invocations.This set is used as input for
3143 # the Web IDL overload resolution algorithm.
3144 # http://heycam.github.io/webidl/#dfn-effective-overload-set
3145 sub ComputeEffectiveOverloadSet
3147 my ($overloads) = @_;
3150 my $addTuple = sub {
3152 # The Web IDL specification uses a flat set of tuples but we use a hash where the key is the
3153 # number of parameters and the value is the set of tuples for the given number of parameters.