Clear m_pendingTargets in MutationObserver::takeRecords
[WebKit-https.git] / Source / WebCore / bindings / scripts / CodeGeneratorJS.pm
1 #
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.
15 #
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.
20 #
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.
25 #
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.
30
31
32 package CodeGeneratorJS;
33
34 use strict;
35 use constant FileNamePrefix => "JS";
36 use Carp qw<longmess>;
37 use Data::Dumper;
38 use Hasher;
39
40 my $codeGenerator;
41 my $writeDependencies;
42
43 my @headerContentHeader = ();
44 my @headerContent = ();
45 my %headerIncludes = ();
46 my %headerTrailingIncludes = ();
47
48 my @implContentHeader = ();
49 my @implContent = ();
50 my %implIncludes = ();
51 my @depsContent = ();
52 my $numCachedAttributes = 0;
53
54 my $beginAppleCopyrightForHeaderFiles = <<END;
55 // ------- Begin Apple Copyright -------
56 /*
57  * Copyright (C) 2008 Apple Inc. All rights reserved.
58  *
59  * Permission is granted by Apple to use this file to the extent
60  * necessary to relink with LGPL WebKit files.
61  *
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.
69  */
70
71 END
72 my $beginAppleCopyrightForSourceFiles = <<END;
73 // ------- Begin Apple Copyright -------
74 /*
75  * Copyright (C) 2008 Apple Inc. All rights reserved.
76  *
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.
80  */
81
82 END
83 my $endAppleCopyright   = <<END;
84 // ------- End Apple Copyright   -------
85
86 END
87
88 # Default .h template
89 my $headerTemplate = << "EOF";
90 /*
91     This file is part of the WebKit open source project.
92     This file has been generated by generate-bindings.pl. DO NOT MODIFY!
93
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.
98
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.
103
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.
108 */
109 EOF
110
111 sub assert
112 {
113     my $message = shift;
114
115     my $mess = longmess();
116     print Dumper($mess);
117
118     die $message;
119 }
120
121 # Default constructor
122 sub new
123 {
124     my $object = shift;
125     my $reference = { };
126
127     $codeGenerator = shift;
128     $writeDependencies = shift;
129
130     bless($reference, $object);
131     return $reference;
132 }
133
134 sub GenerateEnumeration
135 {
136     my ($object, $enumeration) = @_;
137
138     my $className = GetEnumerationClassName($enumeration->type);
139     $object->GenerateEnumerationHeader($enumeration, $className);
140     $object->GenerateEnumerationImplementation($enumeration, $className);
141 }
142
143 sub GenerateDictionary
144 {
145     my ($object, $dictionary, $enumerations, $otherDictionaries) = @_;
146
147     my $className = GetDictionaryClassName($dictionary->type);
148     $object->GenerateDictionaryHeader($dictionary, $className, $enumerations, $otherDictionaries);
149     $object->GenerateDictionaryImplementation($dictionary, $className, $enumerations, $otherDictionaries);
150 }
151
152 sub GenerateCallbackFunction
153 {
154     my ($object, $callbackFunction, $enumerations, $dictionaries) = @_;
155
156     $object->GenerateCallbackFunctionHeader($callbackFunction, $enumerations, $dictionaries);
157     $object->GenerateCallbackFunctionImplementation($callbackFunction, $enumerations, $dictionaries);
158 }
159
160 sub GenerateInterface
161 {
162     my ($object, $interface, $defines, $enumerations, $dictionaries) = @_;
163
164     $codeGenerator->LinkOverloadedOperations($interface);
165
166     AddStringifierOperationIfNeeded($interface);
167     AddLegacyCallerOperationIfNeeded($interface);
168
169     if ($interface->isCallback) {
170         $object->GenerateCallbackInterfaceHeader($interface, $enumerations, $dictionaries);
171         $object->GenerateCallbackInterfaceImplementation($interface, $enumerations, $dictionaries);
172     } else {
173         $object->GenerateHeader($interface, $enumerations, $dictionaries);
174         $object->GenerateImplementation($interface, $enumerations, $dictionaries);
175     }
176 }
177
178 sub AddStringifierOperationIfNeeded
179 {
180     my $interface = shift;
181
182     foreach my $property (@{$interface->attributes}, @{$interface->operations}, @{$interface->anonymousOperations}) {
183         next unless $property->isStringifier;
184
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";
187         }
188
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;
192
193             # Don't duplicate the operation if it was declared with the name 'toString'.
194             return if $property->name eq "toString";
195         }
196
197         my $stringifier = IDLOperation->new();
198         $stringifier->name("toString");
199         $stringifier->type(IDLParser::cloneType($property->type));
200         $stringifier->isStringifier(1);
201
202         IDLParser::copyExtendedAttributes($stringifier->extendedAttributes, $property->extendedAttributes);
203
204         if ($property->name && !$stringifier->extendedAttributes->{ImplementedAs}) {
205             $stringifier->extendedAttributes->{ImplementedAs} = $property->name;
206         }
207
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
210         # [CEReactions].
211         if (ref($property) eq "IDLAttribute" && !$property->isReadOnly && $stringifier->extendedAttributes->{CEReactions}) {
212              delete $stringifier->extendedAttributes->{CEReactions};
213         }
214
215         push(@{$interface->operations}, $stringifier);
216         return;
217     }
218 }
219
220 sub AddLegacyCallerOperationIfNeeded
221 {
222     my $interface = shift;
223
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};
228
229             my $clonedOperation = IDLParser::cloneOperation($operation);
230             push(@{$interface->{LegacyCallers}}, $clonedOperation);
231     
232             $clonedOperation->{overloads} = $interface->{LegacyCallers};
233             $clonedOperation->{overloadIndex} = @{$interface->{LegacyCallers}};
234         }
235     }
236 }
237
238 sub EventHandlerAttributeEventName
239 {
240     my $attribute = shift;
241     my $eventType = $attribute->extendedAttributes->{ImplementedAs} || $attribute->name;
242
243     # Remove the "on" prefix.
244     $eventType = substr($eventType, 2);
245
246     return "eventNames().${eventType}Event";
247 }
248
249 sub GetParentClassName
250 {
251     my $interface = shift;
252
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;
257 }
258
259 sub GetCallbackClassName
260 {
261     my $className = shift;
262
263     return "JS$className";
264 }
265
266 sub GetExportMacroForJSClass
267 {
268     my $interface = shift;
269
270     return $interface->extendedAttributes->{ExportMacro} . " " if $interface->extendedAttributes->{ExportMacro};
271     return "";
272 }
273
274 sub AddIncludesForImplementationTypeInImpl
275 {
276     my $implementationType = shift;
277     
278     AddIncludesForImplementationType($implementationType, \%implIncludes);
279 }
280
281 sub AddIncludesForImplementationTypeInHeader
282 {
283     my $implementationType = shift;
284     
285     AddIncludesForImplementationType($implementationType, \%headerIncludes);
286 }
287
288 sub AddIncludesForImplementationType
289 {
290     my ($implementationType, $includesRef) = @_;
291
292     $includesRef->{"${implementationType}.h"} = 1;
293 }
294
295 sub AddToImplIncludesForIDLType
296 {
297     my ($type, $conditional) = @_;
298
299     return AddToIncludesForIDLType($type, \%implIncludes, $conditional)
300 }
301
302 sub AddToIncludesForIDLType
303 {
304     my ($type, $includesRef, $conditional) = @_;
305
306     if ($type->isNullable) {
307         AddToIncludes("JSDOMConvertNullable.h", $includesRef, $conditional);
308     }
309
310     if ($type->extendedAttributes->{OverrideIDLType}) {
311         my $overrideTypeName = $type->extendedAttributes->{OverrideIDLType};
312         if ($overrideTypeName eq "IDLIDBKey") {
313             AddToIncludes("JSDOMConvertIndexedDB.h", $includesRef, $conditional);
314             return;
315         }
316
317         if ($overrideTypeName eq "IDLWebGLAny" || $overrideTypeName eq "IDLWebGLExtension") {
318             AddToIncludes("JSDOMConvertWebGL.h", $includesRef, $conditional);
319             return;
320         }
321     }
322
323     if ($type->name eq "any") {
324         AddToIncludes("JSDOMConvertAny.h", $includesRef, $conditional);
325         return;
326     }
327
328     if ($type->name eq "boolean") {
329         AddToIncludes("JSDOMConvertBoolean.h", $includesRef, $conditional);
330         return;
331     }
332
333     if ($codeGenerator->IsBufferSourceType($type)) {
334         AddToIncludes("JSDOMConvertBufferSource.h", $includesRef, $conditional);
335         return;
336     }
337
338     if ($codeGenerator->IsCallbackFunction($type) || $codeGenerator->IsCallbackInterface($type)) {
339         AddToIncludes("JS" . $type->name . ".h", $includesRef, $conditional);
340         AddToIncludes("JSDOMConvertCallbacks.h", $includesRef, $conditional);
341         return;
342     }
343
344     if ($type->name eq "Date") {
345         AddToIncludes("JSDOMConvertDate.h", $includesRef, $conditional);
346         return;
347     }
348
349     if ($codeGenerator->IsExternalDictionaryType($type)) {
350         AddToIncludes("JS" . $type->name . ".h", $includesRef, $conditional);
351         AddToIncludes("JSDOMConvertDictionary.h", $includesRef, $conditional);
352         return;
353     }
354
355     if ($codeGenerator->IsExternalEnumType($type)) {
356         AddToIncludes("JS" . $type->name . ".h", $includesRef, $conditional);
357         AddToIncludes("JSDOMConvertEnumeration.h", $includesRef, $conditional);
358         return;
359     }
360
361     if ($type->name eq "EventListener") {
362         AddToIncludes("JSEventListener.h", $includesRef, $conditional);
363         AddToIncludes("JSDOMConvertEventListener.h", $includesRef, $conditional);
364         return;
365     }
366
367     if ($codeGenerator->IsInterfaceType($type)) {
368         AddToIncludes("JS" . $type->name . ".h", $includesRef, $conditional);
369         AddToIncludes("JSDOMConvertInterface.h", $includesRef, $conditional);
370         return;
371     }
372
373     if ($type->name eq "JSON") {
374         AddToIncludes("JSDOMConvertJSON.h", $includesRef, $conditional);
375         return;
376     }
377
378     if ($codeGenerator->IsNumericType($type)) {
379         AddToIncludes("JSDOMConvertNumbers.h", $includesRef, $conditional);
380         return;
381     }
382
383     if ($type->name eq "object") {
384         AddToIncludes("JSDOMConvertObject.h", $includesRef, $conditional);
385         return;
386     }
387
388     if ($codeGenerator->IsPromiseType($type)) {
389         AddToIncludes("DOMPromiseProxy.h", $includesRef, $conditional);
390         AddToIncludes("JSDOMConvertPromise.h", $includesRef, $conditional);
391
392         AddToIncludesForIDLType(@{$type->subtypes}[0], $includesRef, $conditional);
393         return;
394     }
395
396     if ($codeGenerator->IsRecordType($type)) {
397         AddToIncludes("<wtf/Vector.h>", $includesRef, $conditional);
398         AddToIncludes("JSDOMConvertRecord.h", $includesRef, $conditional);
399
400         AddToIncludesForIDLType(@{$type->subtypes}[0], $includesRef, $conditional);
401         AddToIncludesForIDLType(@{$type->subtypes}[1], $includesRef, $conditional);
402         return;
403     }
404
405     if ($codeGenerator->IsSequenceOrFrozenArrayType($type)) {
406         AddToIncludes("<JavaScriptCore/JSArray.h>", $includesRef, $conditional);
407         AddToIncludes("JSDOMConvertSequences.h", $includesRef, $conditional);
408
409         AddToIncludesForIDLType(@{$type->subtypes}[0], $includesRef, $conditional);
410         return;
411     }
412
413     if ($type->name eq "ScheduledAction") {
414         AddToIncludes("JSDOMConvertScheduledAction.h", $includesRef, $conditional);
415         return;
416     }
417
418     if ($type->name eq "SerializedScriptValue") {
419         AddToIncludes("SerializedScriptValue.h", $includesRef, $conditional);
420         AddToIncludes("JSDOMConvertSerializedScriptValue.h", $includesRef, $conditional);
421         return;
422     }
423
424     if ($codeGenerator->IsStringType($type)) {
425         AddToIncludes("JSDOMConvertStrings.h", $includesRef, $conditional);
426         return;
427     }
428
429     if ($type->isUnion) {
430         AddToIncludes("<wtf/Variant.h>", $includesRef, $conditional);
431         AddToIncludes("JSDOMConvertUnion.h", $includesRef, $conditional);
432
433         foreach my $memberType (@{$type->subtypes}) {
434             AddToIncludesForIDLType($memberType, $includesRef, $conditional);
435         }
436
437         return;
438     }
439
440     if ($type->name eq "XPathNSResolver") {
441         AddToIncludes("JSXPathNSResolver.h", $includesRef, $conditional);
442         AddToIncludes("JSDOMConvertXPathNSResolver.h", $includesRef, $conditional);
443         return;
444     }
445 }
446
447 sub AddToImplIncludes
448 {
449     my ($header, $conditional) = @_;
450
451     AddToIncludes($header, \%implIncludes, $conditional);
452 }
453
454 sub AddToIncludes
455 {
456     my ($header, $includesRef, $conditional) = @_;
457
458     if (not $conditional) {
459         $includesRef->{$header} = 1;
460     } elsif (not exists($includesRef->{$header})) {
461         $includesRef->{$header} = $conditional;
462     } else {
463         my $oldValue = $includesRef->{$header};
464         $includesRef->{$header} = "$oldValue|$conditional" if $oldValue ne 1;
465     }
466 }
467
468 sub IsReadonly
469 {
470     my $attribute = shift;
471     return $attribute->isReadOnly && !$attribute->extendedAttributes->{Replaceable} && !$attribute->extendedAttributes->{PutForwards};
472 }
473
474 sub AddClassForwardIfNeeded
475 {
476     my $type = shift;
477
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);
481
482     push(@headerContent, "class " . $type->name . ";\n\n");
483 }
484
485 sub GetGenerateIsReachable
486 {
487     my $interface = shift;
488     return $interface->extendedAttributes->{GenerateIsReachable};
489 }
490
491 sub GetCustomIsReachable
492 {
493     my $interface = shift;
494     return $interface->extendedAttributes->{CustomIsReachable};
495 }
496
497 sub IsDOMGlobalObject
498 {
499     my $interface = shift;
500     return $interface->type->name eq "DOMWindow" || $interface->type->name eq "RemoteDOMWindow" || $codeGenerator->InheritsInterface($interface, "WorkerGlobalScope") || $interface->type->name eq "TestGlobalObject";
501 }
502
503 sub ShouldUseGlobalObjectPrototype
504 {
505     my $interface = shift;
506
507     # For workers, the global object is a DedicatedWorkerGlobalScope.
508     return 0 if $interface->type->name eq "WorkerGlobalScope";
509
510     return IsDOMGlobalObject($interface);
511 }
512
513 sub GenerateIndexedGetter
514 {
515     my ($interface, $indexedGetterOperation, $indexExpression) = @_;
516     
517     # NOTE: This abstractly implements steps 1.2.1 - 1.2.8 of the LegacyPlatformObjectGetOwnProperty
518     #       algorithm. Returing the conversion expression and attributes expression for use
519     #       by the caller.
520     
521     # 1.2.1 Let operation be the operation used to declare the indexed property getter.
522     # 1.2.2 Let value be an uninitialized variable.
523     # 1.2.3 If operation was defined without an identifier, then set value to the result
524     #       of performing the steps listed in the interface description to determine the
525     #       value of an indexed property with index as the index.
526     # 1.2.4 Otherwise, operation was defined with an identifier. Set value to the result
527     #       of performing the steps listed in the description of operation with index as
528     #       the only argument value.
529     # 1.2.5 Let desc be a newly created Property Descriptor with no fields.
530     # 1.2.6 Set desc.[[Value]] to the result of converting value to an ECMAScript value.
531     # 1.2.7 If O implements an interface with an indexed property setter, then set
532     #       desc.[[Writable]] to true, otherwise set it to false.
533     # 1.2.8 Return desc.
534     
535     my @attributes = ();
536     push(@attributes, "JSC::PropertyAttribute::ReadOnly") if !GetIndexedSetterOperation($interface) && !$interface->extendedAttributes->{Plugin};
537     
538     my $attributeString = "static_cast<unsigned>(" . ((@attributes > 0) ? join(" | ", @attributes) : "0") . ")";
539
540     my $indexedGetterFunctionName = $indexedGetterOperation->extendedAttributes->{ImplementedAs} || $indexedGetterOperation->name || "item";
541     my $nativeToJSConversion = NativeToJSValueUsingPointers($indexedGetterOperation, $interface, "thisObject->wrapped().${indexedGetterFunctionName}(${indexExpression})", "*thisObject->globalObject()");
542     
543     return ($nativeToJSConversion, $attributeString);
544 }
545
546 sub GenerateNamedGetter
547 {
548     my ($interface, $namedGetterOperation, $namedPropertyExpression) = @_;
549     
550     # NOTE: This abstractly implements steps 2.1 - 2.10 of the LegacyPlatformObjectGetOwnProperty
551     #       algorithm. Returing the conversion expression and attributes expression for use
552     #       by the caller.
553     
554     # 2.1  Let operation be the operation used to declare the named property getter.
555     # 2.2  Let value be an uninitialized variable.
556     # 2.3  If operation was defined without an identifier, then set value to the result
557     #      of performing the steps listed in the interface description to determine the
558     #      value of a named property with P as the name.
559     # 2.4  Otherwise, operation was defined with an identifier. Set value to the result
560     #      of performing the steps listed in the description of operation with P as the
561     #      only argument value..
562     # 2.5  Let desc be a newly created Property Descriptor with no fields.
563     # 2.6  Set desc.[[Value]] to the result of converting value to an ECMAScript value.
564     # 2.7  If O implements an interface with a named property setter, then set desc.[[Writable]]
565     #      to true, otherwise set it to false.
566     # 2.8  If O implements an interface with the [LegacyUnenumerableNamedProperties]
567     #      extended attribute, then set desc.[[Enumerable]] to false, otherwise set it
568     #      to true.
569     # 2.9  Set desc.[[Configurable]] to true.
570     # 2.10 Return desc.
571     
572     my @attributes = ();
573     push(@attributes, "JSC::PropertyAttribute::ReadOnly") if !GetNamedSetterOperation($interface) && !$interface->extendedAttributes->{Plugin};
574     push(@attributes, "JSC::PropertyAttribute::DontEnum") if $interface->extendedAttributes->{LegacyUnenumerableNamedProperties};
575     
576     my $attributeString = "static_cast<unsigned>(" . ((@attributes > 0) ? join(" | ", @attributes) : "0") . ")";
577     my $nativeToJSConversion = NativeToJSValueUsingPointers($namedGetterOperation, $interface, $namedPropertyExpression, "*thisObject->globalObject()");
578     
579     return ($nativeToJSConversion, $attributeString);
580 }
581
582 sub GenerateNamedGetterLambda
583 {
584     my ($outputArray, $interface, $namedGetterOperation, $namedGetterFunctionName, $IDLType) = @_;
585     
586     # NOTE: Named getters are little odd. To avoid doing duplicate lookups (once when checking if
587     #       the property name is a 'supported property name' and once to get the value) we signal
588     #       that a property is supported by whether or not it is 'null' (where what null means is
589     #       dependant on the IDL type). This is based on the assumption that no named getter will
590     #       ever actually want to return null as an actual return value, which seems like an ok
591     #       assumption to make (should it turn out this doesn't hold in the future, we have lots
592     #       of options; do two lookups, add an extra layer of std::optional, etc.).
593     
594     my $resultType = "typename ${IDLType}::ImplementationType";
595     $resultType = "ExceptionOr<" . $resultType . ">" if $namedGetterOperation->extendedAttributes->{MayThrowException};
596     my $returnType = "std::optional<" . $resultType . ">";
597
598     push(@$outputArray, "    auto getterFunctor = [] (auto& thisObject, auto propertyName) -> ${returnType} {\n");
599
600     my @arguments = GenerateCallWithUsingReferences($namedGetterOperation->extendedAttributes->{CallWith}, $outputArray, "std::nullopt", "thisObject", "        ");
601     push(@arguments, "propertyNameToAtomicString(propertyName)");
602
603     push(@$outputArray, "        auto result = thisObject.wrapped().${namedGetterFunctionName}(" . join(", ", @arguments) . ");\n");
604     
605     if ($namedGetterOperation->extendedAttributes->{MayThrowException}) {
606         push(@$outputArray, "        if (result.hasException())\n");
607         push(@$outputArray, "            return ${resultType} { result.releaseException() };\n");
608         push(@$outputArray, "        if (!${IDLType}::isNullValue(result.returnValue()))\n");
609         push(@$outputArray, "            return ${resultType} { ${IDLType}::extractValueFromNullable(result.releaseReturnValue()) };\n");
610         push(@$outputArray, "        return std::nullopt;\n");
611     } else {
612         push(@$outputArray, "        if (!${IDLType}::isNullValue(result))\n");
613         push(@$outputArray, "            return ${resultType} { ${IDLType}::extractValueFromNullable(result) };\n");
614         push(@$outputArray, "        return std::nullopt;\n");
615     }
616     push(@$outputArray, "    };\n");
617 }
618
619 # https://heycam.github.io/webidl/#legacy-platform-object-getownproperty
620 sub GenerateGetOwnPropertySlot
621 {
622     my ($outputArray, $interface, $className) = @_;
623     
624     return if $interface->extendedAttributes->{CustomGetOwnPropertySlot};
625     
626     push(@$outputArray, "bool ${className}::getOwnPropertySlot(JSObject* object, ExecState* state, PropertyName propertyName, PropertySlot& slot)\n");
627     push(@$outputArray, "{\n");
628     push(@$outputArray, "    auto* thisObject = jsCast<${className}*>(object);\n");
629     push(@$outputArray, "    ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n");
630     
631     my $namedGetterOperation = GetNamedGetterOperation($interface);
632     my $indexedGetterOperation = GetIndexedGetterOperation($interface);
633     
634     if (($namedGetterOperation && $namedGetterOperation->extendedAttributes->{MayThrowException}) || ($indexedGetterOperation && $indexedGetterOperation->extendedAttributes->{MayThrowException})) {
635         push(@$outputArray, "    auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
636     }
637     
638     # NOTE: The alogithm for [[GetOwnProperty]] contains only the following step:
639     # 1. Return LegacyPlatformObjectGetOwnProperty(O, P, false).
640     
641     # Therefore, the following steps are from the LegacyPlatformObjectGetOwnProperty algorithm
642     # https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
643     
644     # 1. If O supports indexed properties and P is an array index property name, then:
645     if ($indexedGetterOperation) {
646         # 1.1. Let index be the result of calling ToUint32(P).
647         push(@$outputArray, "    if (auto index = parseIndex(propertyName)) {\n");
648         
649         # 1.2. If index is a supported property index, then:
650         # FIXME: This should support non-contiguous indices.
651         push(@$outputArray, "        if (index.value() < thisObject->wrapped().length()) {\n");
652         
653         # NOTE: GenerateIndexedGetter implements steps 1.2.1 - 1.2.8.
654         
655         my ($nativeToJSConversion, $attributeString) = GenerateIndexedGetter($interface, $indexedGetterOperation, "index.value()");
656         
657         push(@$outputArray, "            auto value = ${nativeToJSConversion};\n");
658         push(@$outputArray, "            RETURN_IF_EXCEPTION(throwScope, false);\n") if $indexedGetterOperation->extendedAttributes->{MayThrowException};
659         
660         push(@$outputArray, "            slot.setValue(thisObject, ${attributeString}, value);\n");
661         push(@$outputArray, "            return true;\n");
662         
663         push(@$outputArray, "        }\n");
664         
665         # 1.3. Set ignoreNamedProps to true.
666         # NOTE: Setting ignoreNamedProps has the effect of skipping step 2, so we can early return here
667         #       rather than going through the paces of having an actual ignoreNamedProps update.
668         if ($namedGetterOperation || $interface->extendedAttributes->{Plugin}) {
669             push(@$outputArray, "        return JSObject::getOwnPropertySlot(object, state, propertyName, slot);\n");
670         }
671         push(@$outputArray, "    }\n");
672     }
673     
674     # 2. If O supports named properties, the result of running the named property visibility
675     #    algorithm with property name P and object O is true, and ignoreNamedProps is false, then:
676     if ($namedGetterOperation) {
677         # NOTE: ignoreNamedProps is guarenteed to be false here, as it is initially false, and never set
678         #       to true, due to the early return in step 1.3
679         AddToImplIncludes("JSDOMAbstractOperations.h");
680                 
681         my $namedGetterFunctionName = $namedGetterOperation->extendedAttributes->{ImplementedAs} || $namedGetterOperation->name || "namedItem";
682         my $IDLType = GetIDLTypeExcludingNullability($interface, $namedGetterOperation->type);
683         
684         push(@$outputArray, "    using GetterIDLType = ${IDLType};\n");
685         
686         GenerateNamedGetterLambda($outputArray, $interface, $namedGetterOperation, $namedGetterFunctionName, "GetterIDLType");
687         
688         my $overrideBuiltin = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins") ? "OverrideBuiltins::Yes" : "OverrideBuiltins::No";
689         push(@$outputArray, "    if (auto namedProperty = accessVisibleNamedProperty<${overrideBuiltin}>(*state, *thisObject, propertyName, getterFunctor)) {\n");
690         
691         # NOTE: GenerateNamedGetter implements steps 2.1 - 2.10.
692         
693         my ($nativeToJSConversion, $attributeString) = GenerateNamedGetter($interface, $namedGetterOperation, "WTFMove(namedProperty.value())");
694         
695         push(@$outputArray, "        auto value = ${nativeToJSConversion};\n");
696         push(@$outputArray, "        RETURN_IF_EXCEPTION(throwScope, false);\n") if $namedGetterOperation->extendedAttributes->{MayThrowException};
697         
698         push(@$outputArray, "        slot.setValue(thisObject, ${attributeString}, value);\n");
699         push(@$outputArray, "        return true;\n");
700         push(@$outputArray, "    }\n");
701     }
702
703     if ($interface->extendedAttributes->{Plugin}) {
704         AddToImplIncludes("JSPluginElementFunctions.h");
705         push(@$outputArray, "    if (pluginElementCustomGetOwnPropertySlot(thisObject, state, propertyName, slot))\n");
706         push(@$outputArray, "        return true;\n");
707     }
708
709     # 3. Return OrdinaryGetOwnProperty(O, P).
710     push(@$outputArray, "    return JSObject::getOwnPropertySlot(object, state, propertyName, slot);\n");
711     
712     push(@$outputArray, "}\n\n");
713 }
714
715 # https://heycam.github.io/webidl/#legacy-platform-object-getownproperty
716 sub GenerateGetOwnPropertySlotByIndex
717 {
718     my ($outputArray, $interface, $className) = @_;
719     
720     return if $interface->extendedAttributes->{CustomGetOwnPropertySlot};
721
722     # Sink the int-to-string conversion that happens when we create a PropertyName
723     # to the point where we actually need it.
724     my $didGeneratePropertyName = 0;
725     my $propertyNameGeneration = sub {
726         return if $didGeneratePropertyName;
727         
728         push(@$outputArray, "    auto propertyName = Identifier::from(state, index);\n");
729         $didGeneratePropertyName = 1;
730     };
731     
732     push(@$outputArray, "bool ${className}::getOwnPropertySlotByIndex(JSObject* object, ExecState* state, unsigned index, PropertySlot& slot)\n");
733     push(@$outputArray, "{\n");
734     push(@$outputArray, "    auto* thisObject = jsCast<${className}*>(object);\n");
735     push(@$outputArray, "    ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n");
736     
737     my $namedGetterOperation = GetNamedGetterOperation($interface);
738     my $indexedGetterOperation = GetIndexedGetterOperation($interface);
739     
740     if (($namedGetterOperation && $namedGetterOperation->extendedAttributes->{MayThrowException}) || ($indexedGetterOperation && $indexedGetterOperation->extendedAttributes->{MayThrowException})) {
741         push(@$outputArray, "    auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
742     }
743     
744     # NOTE: The alogithm for [[GetOwnProperty]] contains only the following step:
745     # 1. Return LegacyPlatformObjectGetOwnProperty(O, P, false).
746     
747     # Therefore, the following steps are from the LegacyPlatformObjectGetOwnProperty algorithm
748     # https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
749     
750     # 1. If O supports indexed properties and P is an array index property name, then:
751     if ($indexedGetterOperation) {
752         # 1.1. Let index be the result of calling ToUint32(P).
753         push(@$outputArray, "    if (LIKELY(index <= MAX_ARRAY_INDEX)) {\n");
754         
755         # 1.2. If index is a supported property index, then:
756         # FIXME: This should support non-contiguous indices.
757         push(@$outputArray, "        if (index < thisObject->wrapped().length()) {\n");
758         
759         # NOTE: GenerateIndexedGetter implements steps 1.2.1 - 1.2.8.
760         
761         my ($nativeToJSConversion, $attributeString) = GenerateIndexedGetter($interface, $indexedGetterOperation, "index");
762         
763         push(@$outputArray, "            auto value = ${nativeToJSConversion};\n");
764         push(@$outputArray, "            RETURN_IF_EXCEPTION(throwScope, false);\n") if $indexedGetterOperation->extendedAttributes->{MayThrowException};
765         
766         push(@$outputArray, "            slot.setValue(thisObject, ${attributeString}, value);\n");
767         push(@$outputArray, "            return true;\n");
768         
769         push(@$outputArray, "        }\n");
770         
771         # 1.3. Set ignoreNamedProps to true.
772         # NOTE: Setting ignoreNamedProps has the effect of skipping step 2, so we can early return here
773         #       rather than going through the paces of having an actual ignoreNamedProps update.
774         if ($namedGetterOperation || $interface->extendedAttributes->{Plugin}) {
775             push(@$outputArray, "        return JSObject::getOwnPropertySlotByIndex(object, state, index, slot);\n");
776         }
777         push(@$outputArray, "    }\n");
778     }
779     
780     # 2. If O supports named properties, the result of running the named property visibility
781     #    algorithm with property name P and object O is true, and ignoreNamedProps is false, then:
782     if ($namedGetterOperation) {
783         # NOTE: ignoreNamedProps is guarenteed to be false here, as it is initially false, and never set
784         #       to true, due to the early return in step 1.3
785         AddToImplIncludes("JSDOMAbstractOperations.h");
786                 
787         &$propertyNameGeneration();
788         
789         my $namedGetterFunctionName = $namedGetterOperation->extendedAttributes->{ImplementedAs} || $namedGetterOperation->name || "namedItem";
790         my $IDLType = GetIDLTypeExcludingNullability($interface, $namedGetterOperation->type);
791         
792         push(@$outputArray, "    using GetterIDLType = ${IDLType};\n");
793         
794         GenerateNamedGetterLambda($outputArray, $interface, $namedGetterOperation, $namedGetterFunctionName, "GetterIDLType");
795         
796         my $overrideBuiltin = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins") ? "OverrideBuiltins::Yes" : "OverrideBuiltins::No";
797         push(@$outputArray, "    if (auto namedProperty = accessVisibleNamedProperty<${overrideBuiltin}>(*state, *thisObject, propertyName, getterFunctor)) {\n");
798         
799         # NOTE: GenerateNamedGetter implements steps 2.1 - 2.10.
800         
801         my ($nativeToJSConversion, $attributeString) = GenerateNamedGetter($interface, $namedGetterOperation, "WTFMove(namedProperty.value())");
802
803         push(@$outputArray, "        auto value = ${nativeToJSConversion};\n");
804         push(@$outputArray, "        RETURN_IF_EXCEPTION(throwScope, false);\n") if $namedGetterOperation->extendedAttributes->{MayThrowException};
805         
806         push(@$outputArray, "        slot.setValue(thisObject, ${attributeString}, value);\n");
807         push(@$outputArray, "        return true;\n");
808         push(@$outputArray, "    }\n");
809     }
810     
811     if ($interface->extendedAttributes->{Plugin}) {
812         &$propertyNameGeneration();
813
814         AddToImplIncludes("JSPluginElementFunctions.h");
815         push(@$outputArray, "    if (pluginElementCustomGetOwnPropertySlot(thisObject, state, propertyName, slot))\n");
816         push(@$outputArray, "        return true;\n");
817     }
818
819     # 3. Return OrdinaryGetOwnProperty(O, P).
820     push(@$outputArray, "    return JSObject::getOwnPropertySlotByIndex(object, state, index, slot);\n");
821     
822     push(@$outputArray, "}\n\n");
823 }
824
825 # https://heycam.github.io/webidl/#legacy-platform-object-property-enumeration
826 sub GenerateGetOwnPropertyNames
827 {
828     my ($outputArray, $interface, $className) = @_;
829     
830     return if $interface->extendedAttributes->{CustomGetOwnPropertyNames};
831     
832     my $namedGetterOperation = GetNamedGetterOperation($interface);
833     my $indexedGetterOperation = GetIndexedGetterOperation($interface);
834     
835     push(@$outputArray, "void ${className}::getOwnPropertyNames(JSObject* object, ExecState* state, PropertyNameArray& propertyNames, EnumerationMode mode)\n");
836     push(@$outputArray, "{\n");
837     push(@$outputArray, "    auto* thisObject = jsCast<${className}*>(object);\n");
838     push(@$outputArray, "    ASSERT_GC_OBJECT_INHERITS(object, info());\n");
839     
840     # 1. If the object supports indexed properties, then the object’s supported
841     #    property indices are enumerated first, in numerical order.
842     # FIXME: This should support non-contiguous indices.
843     if ($indexedGetterOperation) {
844         push(@$outputArray, "    for (unsigned i = 0, count = thisObject->wrapped().length(); i < count; ++i)\n");
845         push(@$outputArray, "        propertyNames.add(Identifier::from(state, i));\n");
846     }
847
848     # 2. If the object supports named properties and doesn’t implement an interface
849     #    with the [LegacyUnenumerableNamedProperties] extended attribute, then the
850     #    object’s supported property names that are visible according to the named
851     #    property visibility algorithm are enumerated next, in the order given in
852     #    the definition of the set of supported property names.
853     if ($namedGetterOperation) {
854         if (!$interface->extendedAttributes->{LegacyUnenumerableNamedProperties}) {
855             push(@$outputArray, "    for (auto& propertyName : thisObject->wrapped().supportedPropertyNames())\n");
856             push(@$outputArray, "        propertyNames.add(Identifier::fromString(state, propertyName));\n");
857         } else {
858             push(@$outputArray, "    if (mode.includeDontEnumProperties()) {\n");
859             push(@$outputArray, "        for (auto& propertyName : thisObject->wrapped().supportedPropertyNames())\n");
860             push(@$outputArray, "            propertyNames.add(Identifier::fromString(state, propertyName));\n");
861             push(@$outputArray, "    }\n");
862         }
863     }
864     
865     # 3. Finally, any enumerable own properties or properties from the object’s
866     #    prototype chain are then enumerated, in no defined order.
867     push(@$outputArray, "    JSObject::getOwnPropertyNames(object, state, propertyNames, mode);\n");
868     push(@$outputArray, "}\n\n");
869 }
870
871 # https://heycam.github.io/webidl/#invoke-indexed-setter
872 sub GenerateInvokeIndexedPropertySetter
873 {
874     my ($outputArray, $indent, $interface, $indexedSetterOperation, $indexExpression, $value) = @_;
875     
876     # The second argument of the indexed setter operation is the argument being converted.
877     my $argument = @{$indexedSetterOperation->arguments}[1];
878     my $nativeValue = JSValueToNative($interface, $argument, $value, $indexedSetterOperation->extendedAttributes->{Conditional}, "state", "*state", "thisObject", "", "");
879     
880     push(@$outputArray, $indent . "auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
881     push(@$outputArray, $indent . "auto nativeValue = ${nativeValue};\n");
882     push(@$outputArray, $indent . "RETURN_IF_EXCEPTION(throwScope, true);\n");
883     
884     my $indexedSetterFunctionName = $indexedSetterOperation->name || "setItem";
885     my $nativeValuePassExpression = PassArgumentExpression("nativeValue", $argument);
886     my $functionString = "thisObject->wrapped().${indexedSetterFunctionName}(${indexExpression}, ${nativeValuePassExpression})";
887     $functionString = "propagateException(*state, throwScope, ${functionString})" if NeedsExplicitPropagateExceptionCall($indexedSetterOperation);
888     
889     push(@$outputArray, $indent . $functionString . ";\n");
890 }
891
892 # https://heycam.github.io/webidl/#invoke-named-setter
893 sub GenerateInvokeNamedPropertySetter
894 {
895     my ($outputArray, $indent, $interface, $namedSetterOperation, $value) = @_;
896     
897     my $argument = @{$namedSetterOperation->arguments}[1];
898     my $nativeValue = JSValueToNative($interface, $argument, $value, $namedSetterOperation->extendedAttributes->{Conditional}, "state", "*state", "thisObject", "", "");
899     
900     push(@$outputArray, $indent . "auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
901     push(@$outputArray, $indent . "auto nativeValue = ${nativeValue};\n");
902     push(@$outputArray, $indent . "RETURN_IF_EXCEPTION(throwScope, true);\n");
903
904     push(@$outputArray, $indent . "bool isPropertySupported = true;\n") if $namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties};
905
906     my $namedSetterFunctionName = $namedSetterOperation->name || "setNamedItem";
907     my $nativeValuePassExpression = PassArgumentExpression("nativeValue", $argument);
908
909     my @arguments = ();
910     push(@arguments, "propertyNameToString(propertyName)");
911     push(@arguments, $nativeValuePassExpression);
912     push(@arguments, "isPropertySupported") if $namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties};
913
914     my $functionString = "thisObject->wrapped().${namedSetterFunctionName}(" . join(", ", @arguments) . ")";
915     $functionString = "propagateException(*state, throwScope, ${functionString})" if NeedsExplicitPropagateExceptionCall($namedSetterOperation);
916
917     push(@$outputArray, $indent . $functionString . ";\n");
918 }
919
920 sub GeneratePut
921 {
922     my ($outputArray, $interface, $className) = @_;
923     
924     return if $interface->extendedAttributes->{CustomPut};
925     
926     my $namedSetterOperation = GetNamedSetterOperation($interface);
927     my $indexedSetterOperation = GetIndexedSetterOperation($interface);
928     
929     push(@$outputArray, "bool ${className}::put(JSCell* cell, ExecState* state, PropertyName propertyName, JSValue value, PutPropertySlot& putPropertySlot)\n");
930     push(@$outputArray, "{\n");
931     push(@$outputArray, "    auto* thisObject = jsCast<${className}*>(cell);\n");
932     push(@$outputArray, "    ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n\n");
933
934     assert("CEReactions is not supported on having both named setters and indexed setters") if $namedSetterOperation && $namedSetterOperation->extendedAttributes->{CEReactions}
935         && $indexedSetterOperation && $indexedSetterOperation->extendedAttributes->{CEReactions};
936     if ($namedSetterOperation) {
937         GenerateCustomElementReactionsStackIfNeeded($outputArray, $namedSetterOperation, "*state");
938     }
939     if ($indexedSetterOperation) {
940         GenerateCustomElementReactionsStackIfNeeded($outputArray, $indexedSetterOperation, "*state");
941     }
942     
943     if ($indexedSetterOperation) {
944         push(@$outputArray, "    if (auto index = parseIndex(propertyName)) {\n");
945         
946         GenerateInvokeIndexedPropertySetter($outputArray, "        ", $interface, $indexedSetterOperation, "index.value()", "value");
947         
948         push(@$outputArray, "        return true;\n");
949         push(@$outputArray, "    }\n\n");
950     }
951     
952     if ($namedSetterOperation) {
953         # FIMXE: We need a more comprehensive story for Symbols.
954         push(@$outputArray, "    if (!propertyName.isSymbol()) {\n");
955         
956         my $additionalIndent = "";
957         
958         my $overrideBuiltins = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins");
959         if (!$overrideBuiltins) {
960             push(@$outputArray, "        PropertySlot slot { thisObject, PropertySlot::InternalMethodType::VMInquiry };\n");
961             push(@$outputArray, "        JSValue prototype = thisObject->getPrototypeDirect(state->vm());\n");
962             push(@$outputArray, "        if (!(prototype.isObject() && asObject(prototype)->getPropertySlot(state, propertyName, slot))) {\n");
963             $additionalIndent .= "    ";
964         }
965
966         GenerateInvokeNamedPropertySetter($outputArray, $additionalIndent . "        ", $interface, $namedSetterOperation, "value");
967         if ($namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties}) {
968             push(@$outputArray, $additionalIndent . "        if (!isPropertySupported)\n");
969             push(@$outputArray, $additionalIndent . "            return JSObject::put(thisObject, state, propertyName, value, putPropertySlot);\n");
970         }
971         push(@$outputArray, $additionalIndent . "        return true;\n");
972
973         if (!$overrideBuiltins) {
974             push(@$outputArray, "        }\n");
975         }
976         
977         push(@$outputArray, "    }\n\n");
978     }
979     
980     assert("Using both a named property setter and [Plugin] together is not supported.") if $namedSetterOperation && $interface->extendedAttributes->{Plugin};
981     if ($interface->extendedAttributes->{Plugin}) {
982         AddToImplIncludes("JSPluginElementFunctions.h");
983
984         push(@$outputArray, "    bool putResult = false;\n");
985         push(@$outputArray, "    if (pluginElementCustomPut(thisObject, state, propertyName, value, putPropertySlot, putResult))\n");
986         push(@$outputArray, "        return putResult;\n\n");
987     }
988
989     push(@$outputArray, "    return JSObject::put(thisObject, state, propertyName, value, putPropertySlot);\n");
990     push(@$outputArray, "}\n\n");
991 }
992
993 sub GeneratePutByIndex
994 {
995     my ($outputArray, $interface, $className) = @_;
996     
997     return if $interface->extendedAttributes->{CustomPut};
998
999     my $namedSetterOperation = GetNamedSetterOperation($interface);
1000     my $indexedSetterOperation = GetIndexedSetterOperation($interface);
1001     
1002     my $overrideBuiltins = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins");
1003     my $ellidesCallsToBase = ($namedSetterOperation && $overrideBuiltins) && !$interface->extendedAttributes->{Plugin} && !$namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties};
1004     
1005     push(@$outputArray, "bool ${className}::putByIndex(JSCell* cell, ExecState* state, unsigned index, JSValue value, bool" . (!$ellidesCallsToBase ? " shouldThrow" : "") . ")\n");
1006     push(@$outputArray, "{\n");
1007     push(@$outputArray, "    auto* thisObject = jsCast<${className}*>(cell);\n");
1008     push(@$outputArray, "    ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n\n");
1009
1010     assert("CEReactions is not supported on having both named setters and indexed setters") if $namedSetterOperation && $namedSetterOperation->extendedAttributes->{CEReactions}
1011         && $indexedSetterOperation && $indexedSetterOperation->extendedAttributes->{CEReactions};
1012     if ($namedSetterOperation) {
1013         GenerateCustomElementReactionsStackIfNeeded($outputArray, $namedSetterOperation, "*state");
1014     }
1015     if ($indexedSetterOperation) {
1016         GenerateCustomElementReactionsStackIfNeeded($outputArray, $indexedSetterOperation, "*state");
1017     }
1018     
1019     if ($indexedSetterOperation ) {
1020         push(@$outputArray, "    if (LIKELY(index <= MAX_ARRAY_INDEX)) {\n");
1021         
1022         GenerateInvokeIndexedPropertySetter($outputArray, "        ", $interface, $indexedSetterOperation, "index", "value");
1023         
1024         push(@$outputArray, "        return true;\n");
1025         push(@$outputArray, "    }\n\n");
1026     }
1027     
1028     if ($namedSetterOperation) {
1029         push(@$outputArray, "    auto propertyName = Identifier::from(state, index);\n");
1030                 
1031         my $additionalIndent = "";
1032         if (!$overrideBuiltins) {
1033             push(@$outputArray, "    PropertySlot slot { thisObject, PropertySlot::InternalMethodType::VMInquiry };\n");
1034             push(@$outputArray, "    JSValue prototype = thisObject->getPrototypeDirect(state->vm());\n");
1035             push(@$outputArray, "    if (!(prototype.isObject() && asObject(prototype)->getPropertySlot(state, propertyName, slot))) {\n");
1036             $additionalIndent .= "    ";
1037         }
1038         
1039         GenerateInvokeNamedPropertySetter($outputArray, $additionalIndent . "    ", $interface, $namedSetterOperation, "value");
1040         if ($namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties}) {
1041             push(@$outputArray, $additionalIndent . "    if (!isPropertySupported)\n");
1042             push(@$outputArray, $additionalIndent . "        return JSObject::putByIndex(cell, state, index, value, shouldThrow);\n");
1043         }
1044         push(@$outputArray, $additionalIndent . "    return true;\n");
1045         
1046         if (!$overrideBuiltins) {
1047             push(@$outputArray, "    }\n\n");
1048         }
1049     }
1050
1051     assert("Using both a named property setter and [Plugin] together is not supported.") if $namedSetterOperation && $interface->extendedAttributes->{Plugin};
1052     if ($interface->extendedAttributes->{Plugin}) {
1053         AddToImplIncludes("JSPluginElementFunctions.h");
1054         push(@$outputArray, "    auto propertyName = Identifier::from(state, index);\n");
1055         push(@$outputArray, "    PutPropertySlot putPropertySlot(thisObject, shouldThrow);\n");
1056         push(@$outputArray, "    bool putResult = false;\n");
1057         push(@$outputArray, "    if (pluginElementCustomPut(thisObject, state, propertyName, value, putPropertySlot, putResult))\n");
1058         push(@$outputArray, "        return putResult;\n\n");
1059     }
1060
1061     if (!$ellidesCallsToBase) {
1062         push(@$outputArray, "    return JSObject::putByIndex(cell, state, index, value, shouldThrow);\n");
1063     }
1064     
1065     push(@$outputArray, "}\n\n");
1066 }
1067
1068 sub GenerateIsUnforgeablePropertyName
1069 {
1070     my ($outputArray, $interface) = @_;
1071     
1072     my @unforgeablePropertyNames = ();
1073     foreach my $property (@{$interface->attributes}, @{$interface->operations}) {
1074         next if $property->isStatic;
1075         
1076         if (IsUnforgeable($interface, $property)) {
1077             push(@unforgeablePropertyNames, $property->name);
1078         }
1079     }
1080     
1081     return 0 if (scalar(@unforgeablePropertyNames) == 0);
1082     
1083     my $condition = join(" || ", map { "propertyName == \"" . $_ . "\"" } @unforgeablePropertyNames);
1084     
1085     push(@$outputArray, "static bool isUnforgeablePropertyName(PropertyName propertyName)\n");
1086     push(@$outputArray, "{\n");
1087     push(@$outputArray, "    return ${condition};\n");
1088     push(@$outputArray, "}\n\n");
1089     
1090     return 1;
1091 }
1092
1093 # https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
1094 sub GenerateDefineOwnProperty
1095 {
1096     my ($outputArray, $interface, $className) = @_;
1097     
1098     return if $interface->extendedAttributes->{CustomDefineOwnProperty};
1099     
1100     my $namedSetterOperation = GetNamedSetterOperation($interface);
1101     my $indexedSetterOperation = GetIndexedSetterOperation($interface);
1102     
1103     return if !$namedSetterOperation && !$indexedSetterOperation;
1104     
1105     push(@$outputArray, "bool ${className}::defineOwnProperty(JSObject* object, ExecState* state, PropertyName propertyName, const PropertyDescriptor& propertyDescriptor, bool shouldThrow)\n");
1106     push(@$outputArray, "{\n");
1107     push(@$outputArray, "    auto* thisObject = jsCast<${className}*>(object);\n");
1108     push(@$outputArray, "    ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n\n");
1109
1110     assert("CEReactions is not supported on having both named setters and indexed setters") if $namedSetterOperation && $namedSetterOperation->extendedAttributes->{CEReactions}
1111         && $indexedSetterOperation && $indexedSetterOperation->extendedAttributes->{CEReactions};
1112     if ($namedSetterOperation) {
1113         GenerateCustomElementReactionsStackIfNeeded($outputArray, $namedSetterOperation, "*state");
1114     }
1115     if ($indexedSetterOperation) {
1116         GenerateCustomElementReactionsStackIfNeeded($outputArray, $indexedSetterOperation, "*state");
1117     }
1118     
1119     # 1. If O supports indexed properties and P is an array index property name, then:
1120     if (GetIndexedGetterOperation($interface)) {
1121         # NOTE: The numbers are out of order because there is no reason doing steps 1, 3, and 4 if there
1122         # is no indexed property setter.
1123
1124         if (!$indexedSetterOperation) {
1125             # 2. If O does not implement an interface with an indexed property setter, then return false.
1126             push(@$outputArray, "    if (parseIndex(propertyName))\n");
1127             push(@$outputArray, "        return false;\n\n");
1128         } else {
1129             push(@$outputArray, "    if (auto index = parseIndex(propertyName)) {\n");
1130
1131             # 1. If the result of calling IsDataDescriptor(Desc) is false, then return false.
1132             push(@$outputArray, "        if (!propertyDescriptor.isDataDescriptor())\n");
1133             push(@$outputArray, "            return false;\n");
1134             
1135             # 3. Invoke the indexed property setter with P and Desc.[[Value]].
1136             GenerateInvokeIndexedPropertySetter($outputArray, "        ", $interface, $indexedSetterOperation, "index.value()", "propertyDescriptor.value()");
1137             
1138             # 4. Return true.
1139             push(@$outputArray, "        return true;\n");
1140             push(@$outputArray, "    }\n\n");
1141         }
1142     }
1143     
1144     # 2. If O supports named properties, O does not implement an interface with the [Global] or [PrimaryGlobal]
1145     #    extended attribute and P is not an unforgeable property name of O, then:
1146     if (GetNamedGetterOperation($interface) && !IsGlobalOrPrimaryGlobalInterface($interface)) {
1147         # FIMXE: We need a more comprehensive story for Symbols.
1148         push(@$outputArray, "    if (!propertyName.isSymbol()) {\n");
1149         
1150         my $additionalIndent = "";
1151         
1152         my $hasUnforgableProperties = GenerateIsUnforgeablePropertyName($outputArray, $interface);
1153         if ($hasUnforgableProperties) {
1154             push(@$outputArray, "        if (!isUnforgeablePropertyName(propertyName)) {\n");
1155             $additionalIndent .= "    ";
1156         }
1157         
1158         # 1. Let creating be true if P is not a supported property name, and false otherwise.
1159         # NOTE: This step is strength reduced into the only use of 'creating' in step 2.2.1
1160         
1161         # 2. If O implements an interface with the [OverrideBuiltins] extended attribute or O
1162         #    does not have an own property named P, then:
1163         my $overrideBuiltins = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins");
1164         if (!$overrideBuiltins) {
1165             # FIXME: Is JSObject::getOwnPropertySlot the right function to call? Is there a function that will
1166             #        only look at the actual properties, and not call into our implementation of the
1167             #        [[GetOwnProperty]] hook?
1168             push(@$outputArray, $additionalIndent. "        PropertySlot slot { thisObject, PropertySlot::InternalMethodType::VMInquiry };\n");
1169             push(@$outputArray, $additionalIndent. "        if (!JSObject::getOwnPropertySlot(thisObject, state, propertyName, slot)) {\n");
1170             $additionalIndent .= "    ";
1171         }
1172         if (!$namedSetterOperation) {
1173             # 2.1. If creating is false and O does not implement an interface with a named property setter, then return false.
1174             push(@$outputArray, $additionalIndent . "        if (thisObject->wrapped().isSupportedPropertyName(propertyNameToString(propertyName)))\n");
1175             push(@$outputArray, $additionalIndent . "            return false;\n");
1176         } else {
1177             # 2.2. If O implements an interface with a named property setter, then:
1178             
1179             # 2.2.1. If the result of calling IsDataDescriptor(Desc) is false, then return false.
1180             push(@$outputArray, $additionalIndent . "        if (!propertyDescriptor.isDataDescriptor())\n");
1181             push(@$outputArray, $additionalIndent . "            return false;\n");
1182             
1183             # 2.2.2. Invoke the named property setter with P and Desc.[[Value]].
1184             GenerateInvokeNamedPropertySetter($outputArray, $additionalIndent . "        ", $interface, $namedSetterOperation, "propertyDescriptor.value()");
1185             if ($namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties}) {
1186                 push(@$outputArray, $additionalIndent . "    if (!isPropertySupported)\n");
1187                 push(@$outputArray, $additionalIndent . "        return JSObject::defineOwnProperty(object, state, propertyName, propertyDescriptor, shouldThrow);\n");
1188             }
1189             # 2.2.3. Return true.
1190             push(@$outputArray, $additionalIndent . "        return true;\n");
1191         }
1192         
1193         if (!$overrideBuiltins) {
1194             push(@$outputArray, $additionalIndent . "    }\n");
1195         }
1196         
1197         if ($hasUnforgableProperties) {
1198             push(@$outputArray, "        }\n");
1199         }
1200         
1201         # Close the !propertyName.isSymbol() condition.
1202         push(@$outputArray, "    }\n\n");
1203     }
1204     
1205     push(@$outputArray, "    PropertyDescriptor newPropertyDescriptor = propertyDescriptor;\n");
1206         
1207     # 3. If O does not implement an interface with the [Global] or [PrimaryGlobal] extended attribute,
1208     #    then set Desc.[[Configurable]] to true.
1209     if (!IsGlobalOrPrimaryGlobalInterface($interface)) {
1210         push(@$outputArray, "    newPropertyDescriptor.setConfigurable(true);\n");
1211     }
1212     
1213     # 4. Return OrdinaryDefineOwnProperty(O, P, Desc).
1214     # FIXME: Does this do the same thing?
1215     push(@$outputArray, "    return JSObject::defineOwnProperty(object, state, propertyName, newPropertyDescriptor, shouldThrow);\n");
1216     
1217     push(@$outputArray, "}\n\n");
1218 }
1219
1220 sub GenerateDeletePropertyCommon
1221 {
1222     my ($outputArray, $interface, $className, $operation, $conditional) = @_;
1223     
1224     # This implements step 2 of https://heycam.github.io/webidl/#legacy-platform-object-delete
1225     # so it can be shared between the generation of deleteProperty and deletePropertyByIndex.
1226
1227     # 2. If O supports named properties, O does not implement an interface with the
1228     #    [Global] or [PrimaryGlobal] extended attribute and the result of calling the
1229     #    named property visibility algorithm with property name P and object O is true,
1230     #    then:
1231     assert("Named property deleters are not allowed without a corresponding named property getter.") if !GetNamedGetterOperation($interface);
1232     assert("Named property deleters are not allowed on global object interfaces.") if IsGlobalOrPrimaryGlobalInterface($interface);
1233
1234     AddToImplIncludes("JSDOMAbstractOperations.h", $conditional);
1235     my $overrideBuiltin = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins") ? "OverrideBuiltins::Yes" : "OverrideBuiltins::No";
1236     push(@$outputArray, "    if (isVisibleNamedProperty<${overrideBuiltin}>(*state, thisObject, propertyName)) {\n");
1237
1238     GenerateCustomElementReactionsStackIfNeeded($outputArray, $operation, "*state");
1239
1240     # 2.1. If O does not implement an interface with a named property deleter, then return false.
1241     # 2.2. Let operation be the operation used to declare the named property deleter.
1242     # NOTE: We only add a deleteProperty implementation of we have a named property deleter.
1243
1244     # 2.3. If operation was defined without an identifier, then:
1245     #      1. Perform the steps listed in the interface description to delete an existing named
1246     #         property with P as the name.
1247     #      2. If the steps indicated that the deletion failed, then return false.
1248     # 2.4. Otherwise, operation was defined with an identifier:
1249     #      1. Perform the steps listed in the description of operation with P as the only argument
1250     #         value.
1251     #      2. If operation was declared with a return type of boolean and the steps returned false,
1252     #         then return false.
1253
1254     my $functionImplementationName = $operation->extendedAttributes->{ImplementedAs} || $codeGenerator->WK_lcfirst($operation->name) || "deleteNamedProperty";
1255     my $functionCall = "impl." . $functionImplementationName . "(propertyNameToString(propertyName))";
1256
1257     # NOTE: We expect the implementation function of named deleters without an identifier to
1258     #       return either bool or ExceptionOr<bool>. the implementation function of named deleters
1259     #       with an identifier have no restriction, but if the return value of the operation is
1260     #       boolean, we return that value, otherwise it is ignored (as per section 4.2).
1261
1262     if ($operation->extendedAttributes->{MayThrowException}) {
1263         push(@$outputArray, "        auto result = ${functionCall};\n");
1264         push(@$outputArray, "        if (result.hasException()) {\n");
1265         push(@$outputArray, "            auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
1266         push(@$outputArray, "            propagateException(*state, throwScope, result.releaseException());\n");
1267         push(@$outputArray, "            return true;\n");
1268         push(@$outputArray, "        }\n\n");
1269
1270         if (!$operation->name || $operation->name && $operation->type->name eq "boolean") {
1271             push(@$outputArray, "        return result.releaseReturnValue();\n");
1272         } else {
1273             push(@$outputArray, "        return true;\n");
1274         }
1275     } else {
1276         if (!$operation->name || $operation->name && $operation->type->name eq "boolean") {
1277             push(@$outputArray, "        return ${functionCall};\n");
1278         } else {
1279             push(@$outputArray, "        ${functionCall};\n");
1280             push(@$outputArray, "        return true;\n");
1281         }
1282     }
1283
1284     push(@$outputArray, "    }\n");
1285 }
1286
1287 sub GenerateDeleteProperty
1288 {
1289     my ($outputArray, $interface, $className, $operation, $conditional) = @_;
1290
1291     # This implements https://heycam.github.io/webidl/#legacy-platform-object-delete for the
1292     # for the deleteProperty override hook.
1293
1294     push(@$outputArray, "bool ${className}::deleteProperty(JSCell* cell, ExecState* state, PropertyName propertyName)\n");
1295     push(@$outputArray, "{\n");
1296
1297     push(@$outputArray, "    auto& thisObject = *jsCast<${className}*>(cell);\n");
1298     push(@$outputArray, "    auto& impl = thisObject.wrapped();\n");
1299
1300     # 1. If O supports indexed properties and P is an array index property name, then:
1301     #    1. Let index be the result of calling ToUint32(P).
1302     #    2. If index is not a supported property index, then return true.
1303     #    3. Return false.
1304     if (GetIndexedGetterOperation($interface)) {
1305         push(@$outputArray, "    if (auto index = parseIndex(propertyName))\n");
1306         push(@$outputArray, "        return !impl.isSupportedPropertyIndex(index.value());\n");
1307     }
1308
1309     # GenerateDeletePropertyCommon implements step 2.
1310     GenerateDeletePropertyCommon($outputArray, $interface, $className, $operation, $conditional);
1311
1312     # FIXME: Instead of calling down JSObject::deleteProperty, perhaps we should implement
1313     # the remained of the algorithm ourselves.
1314     push(@$outputArray, "    return JSObject::deleteProperty(cell, state, propertyName);\n");
1315     push(@$outputArray, "}\n\n");
1316 }
1317
1318 sub GenerateDeletePropertyByIndex
1319 {
1320     my ($outputArray, $interface, $className, $operation, $conditional) = @_;
1321
1322     # This implements https://heycam.github.io/webidl/#legacy-platform-object-delete for the
1323     # for the deletePropertyByIndex override hook.
1324
1325     push(@$outputArray, "bool ${className}::deletePropertyByIndex(JSCell* cell, ExecState* state, unsigned index)\n");
1326     push(@$outputArray, "{\n");
1327
1328     push(@$outputArray, "    auto& thisObject = *jsCast<${className}*>(cell);\n");
1329     push(@$outputArray, "    auto& impl = thisObject.wrapped();\n");
1330
1331     # 1. If O supports indexed properties and P is an array index property name, then:
1332     #    1. Let index be the result of calling ToUint32(P).
1333     #    2. If index is not a supported property index, then return true.
1334     #    3. Return false.
1335
1336     # NOTE: For deletePropertyByIndex, if there is an indexed getter, checking isSupportedPropertyIndex()
1337     #       is all that needs to be done, no need to generate the .
1338
1339     if (GetIndexedGetterOperation($interface)) {
1340         push(@$outputArray, "    return !impl.isSupportedPropertyIndex(index);\n");
1341     } else {
1342         push(@$outputArray, "    auto propertyName = Identifier::from(state, index);\n");
1343
1344         # GenerateDeletePropertyCommon implements step 2.
1345         GenerateDeletePropertyCommon($outputArray, $interface, $className, $operation, $conditional);
1346
1347         # FIXME: Instead of calling down JSObject::deletePropertyByIndex, perhaps we should implement
1348         # the remaineder of the algoritm (steps 3 and 4) ourselves.
1349         
1350         # 3. If O has an own property with name P, then:
1351         #    1. If the property is not configurable, then return false.
1352         #    2. Otherwise, remove the property from O.
1353         # 3. Return true.
1354         
1355         push(@$outputArray, "    return JSObject::deletePropertyByIndex(cell, state, index);\n");
1356     }
1357
1358     push(@$outputArray, "}\n\n");
1359 }
1360
1361
1362 sub GenerateNamedDeleterDefinition
1363 {
1364     my ($outputArray, $interface, $className) = @_;
1365     
1366     return if $interface->extendedAttributes->{CustomDeleteProperty};
1367
1368     my $namedDeleterOperation = GetNamedDeleterOperation($interface);
1369     
1370     # This implements https://heycam.github.io/webidl/#legacy-platform-object-delete using
1371     # the deleteProperty and deletePropertyByIndex override hooks.
1372
1373     assert("Named property deleters are not allowed without a corresponding named property getter.") if !GetNamedGetterOperation($interface);
1374     assert("Named property deleters are not allowed on global object interfaces.") if IsGlobalOrPrimaryGlobalInterface($interface);
1375
1376     my $conditional = $namedDeleterOperation->extendedAttributes->{Conditional};
1377     if ($conditional) {
1378         my $conditionalString = $codeGenerator->GenerateConditionalStringFromAttributeValue($conditional);
1379         push(@$outputArray, "#if ${conditionalString}\n\n");;
1380     }
1381
1382     GenerateDeleteProperty($outputArray, $interface, $className, $namedDeleterOperation, $conditional);
1383     GenerateDeletePropertyByIndex($outputArray, $interface, $className, $namedDeleterOperation, $conditional);
1384
1385     push(@implContent, "#endif\n\n") if $conditional;
1386 }
1387
1388 sub GenerateHeaderContentHeader
1389 {
1390     my $interface = shift;
1391     my $className = "JS" . $interface->type->name;
1392
1393     my @headerContentHeader;
1394     if ($interface->extendedAttributes->{AppleCopyright}) {
1395         @headerContentHeader = split("\r", $beginAppleCopyrightForHeaderFiles);
1396     } else {
1397         @headerContentHeader = split("\r", $headerTemplate);
1398     }
1399
1400     push(@headerContentHeader, "\n#pragma once\n\n");
1401
1402     my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
1403     push(@headerContentHeader, "#if ${conditionalString}\n\n") if $conditionalString;
1404     return @headerContentHeader;
1405 }
1406
1407 sub GenerateImplementationContentHeader
1408 {
1409     my $interface = shift;
1410     my $className = "JS" . $interface->type->name;
1411
1412     my @implContentHeader;
1413     if ($interface->extendedAttributes->{AppleCopyright}) {
1414         @implContentHeader = split("\r", $beginAppleCopyrightForSourceFiles);
1415     } else {
1416         @implContentHeader = split("\r", $headerTemplate);
1417     }
1418
1419     push(@implContentHeader, "\n#include \"config.h\"\n");
1420     my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
1421     push(@implContentHeader, "\n#if ${conditionalString}\n\n") if $conditionalString;
1422     push(@implContentHeader, "#include \"$className.h\"\n\n");
1423     return @implContentHeader;
1424 }
1425
1426 sub NeedsImplementationClass
1427 {
1428     my ($interface) = @_;
1429
1430     return 0 if $interface->extendedAttributes->{JSBuiltin};
1431     return 1;
1432 }
1433
1434 sub ShouldGenerateToWrapped
1435 {
1436     my ($hasParent, $interface) = @_;
1437
1438     return 0 if not NeedsImplementationClass($interface);
1439     return 1 if !$hasParent or $interface->extendedAttributes->{JSGenerateToNativeObject};
1440     return 1 if $interface->parentType && $interface->parentType->name eq "EventTarget";
1441     return 0;
1442 }
1443
1444 sub ShouldGenerateWrapperOwnerCode
1445 {
1446     my ($hasParent, $interface) = @_;
1447
1448     return 0 if not NeedsImplementationClass($interface);
1449     return 1 if !$hasParent;
1450     return 1 if GetGenerateIsReachable($interface);
1451     return 1 if GetCustomIsReachable($interface);
1452     return 1 if $interface->extendedAttributes->{JSCustomFinalize};
1453     return 1 if $codeGenerator->InheritsExtendedAttribute($interface, "ActiveDOMObject");
1454     return 0;
1455 }
1456
1457 sub ShouldGenerateToJSDeclaration
1458 {
1459     my ($hasParent, $interface) = @_;
1460
1461     return 0 if ($interface->extendedAttributes->{SuppressToJSObject});
1462     return 0 if not NeedsImplementationClass($interface);
1463     return 0 if $interface->extendedAttributes->{CustomProxyToJSObject};
1464     return 1 if (!$hasParent or $interface->extendedAttributes->{JSGenerateToJSObject} or $interface->extendedAttributes->{CustomToJSObject});
1465     return 1 if $interface->parentType && $interface->parentType->name eq "EventTarget";
1466     return 1 if $interface->extendedAttributes->{Constructor} or $interface->extendedAttributes->{NamedConstructor};
1467     return 0;
1468 }
1469
1470 sub ShouldGenerateToJSImplementation
1471 {
1472     my ($hasParent, $interface) = @_;
1473
1474     return 0 if not ShouldGenerateToJSDeclaration($hasParent, $interface);
1475     return 1 if not $interface->extendedAttributes->{CustomToJSObject};
1476     return 0;
1477 }
1478
1479 sub GetTypeNameForDisplayInException
1480 {
1481     my ($type) = @_;
1482
1483     # FIXME: Add more type specializations.
1484     return "(" . join(" or ", map { $_->name } GetFlattenedMemberTypes($type)) . ")" if $type->isUnion;
1485     return $type->name;
1486 }
1487
1488 sub GetArgumentExceptionFunction
1489 {
1490     my ($interface, $argument, $argumentIndex, $quotedFunctionName) = @_;
1491
1492     my $name = $argument->name;
1493     my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($interface);
1494     my $typeName = GetTypeNameForDisplayInException($argument->type);
1495
1496     if ($codeGenerator->IsCallbackInterface($argument->type) || $codeGenerator->IsCallbackFunction($argument->type)) {
1497         # FIXME: We should have specialized messages for callback interfaces vs. callback functions.
1498         return "throwArgumentMustBeFunctionError(state, scope, ${argumentIndex}, \"${name}\", \"${visibleInterfaceName}\", ${quotedFunctionName});";
1499     }
1500
1501     if ($codeGenerator->IsWrapperType($argument->type) || $codeGenerator->IsBufferSourceType($argument->type)) {
1502         return "throwArgumentTypeError(state, scope, ${argumentIndex}, \"${name}\", \"${visibleInterfaceName}\", ${quotedFunctionName}, \"${typeName}\");";
1503     }
1504
1505     if ($codeGenerator->IsEnumType($argument->type)) {
1506         my $className = GetEnumerationClassName($argument->type, $interface);
1507         return "throwArgumentMustBeEnumError(state, scope, ${argumentIndex}, \"${name}\", \"${visibleInterfaceName}\", ${quotedFunctionName}, expectedEnumerationValues<${className}>());";
1508     }
1509
1510     return undef;
1511 }
1512
1513 sub GetArgumentExceptionThrower
1514 {
1515     my ($interface, $argument, $argumentIndex, $quotedFunctionName) = @_;
1516
1517     my $functionCall = GetArgumentExceptionFunction($interface, $argument, $argumentIndex, $quotedFunctionName);
1518     return "[](JSC::ExecState& state, JSC::ThrowScope& scope) { " . $functionCall . " }" if $functionCall;
1519 }
1520
1521 sub GetAttributeExceptionFunction
1522 {
1523     my ($interface, $attribute) = @_;
1524     
1525     my $name = $attribute->name;
1526     my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($interface);
1527     my $typeName = GetTypeNameForDisplayInException($attribute->type);
1528
1529     if ($codeGenerator->IsWrapperType($attribute->type) || $codeGenerator->IsBufferSourceType($attribute->type)) {
1530         return "throwAttributeTypeError(state, scope, \"${visibleInterfaceName}\", \"${name}\", \"${typeName}\");";
1531     }
1532 }
1533
1534 sub GetAttributeExceptionThrower
1535 {
1536     my ($interface, $attribute) = @_;
1537
1538     my $functionCall = GetAttributeExceptionFunction($interface, $attribute);
1539     return "[](JSC::ExecState& state, JSC::ThrowScope& scope) { " . $functionCall . " }" if $functionCall;
1540
1541 }
1542
1543 sub PassArgumentExpression
1544 {
1545     my ($name, $context) = @_;
1546
1547     my $type = $context->type;
1548
1549     return "WTFMove(${name})" if $type->isNullable;
1550
1551     if ($codeGenerator->IsBufferSourceType($type)) {
1552         return "*${name}" if $type->name eq "ArrayBuffer";
1553         return "${name}.releaseNonNull()";
1554     }
1555
1556     return "${name}.releaseNonNull()" if $codeGenerator->IsCallbackInterface($type) || $codeGenerator->IsCallbackFunction($type) || ($codeGenerator->IsPromiseType($type) && (ref($context) ne "IDLArgument" || !$context->isOptional));
1557     return "*${name}" if $codeGenerator->IsWrapperType($type);
1558     return "WTFMove(${name})";
1559 }
1560
1561 sub GetAttributeGetterName
1562 {
1563     my ($interface, $className, $attribute) = @_;
1564
1565     return $codeGenerator->WK_lcfirst($className) . "Constructor" . $codeGenerator->WK_ucfirst($attribute->name) if $attribute->isStatic;
1566     return GetJSBuiltinFunctionName($className, $attribute) if IsJSBuiltin($interface, $attribute);
1567     return "js" . $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name) . ($codeGenerator->IsConstructorType($attribute->type) ? "Constructor" : "");
1568 }
1569
1570 sub GetAttributeSetterName
1571 {
1572     my ($interface, $className, $attribute) = @_;
1573
1574     return "set" . $codeGenerator->WK_ucfirst($className) . "Constructor" . $codeGenerator->WK_ucfirst($attribute->name) if $attribute->isStatic;
1575     return "set" . $codeGenerator->WK_ucfirst(GetJSBuiltinFunctionName($className, $attribute)) if IsJSBuiltin($interface, $attribute);
1576     return "setJS" . $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name) . ($codeGenerator->IsConstructorType($attribute->type) ? "Constructor" : "");
1577 }
1578
1579 sub GetFunctionName
1580 {
1581     my ($interface, $className, $operation) = @_;
1582
1583     return GetJSBuiltinFunctionName($className, $operation) if IsJSBuiltin($interface, $operation);
1584
1585     my $functionName = $operation->name;
1586     $functionName = "SymbolIterator" if $functionName eq "[Symbol.Iterator]";
1587
1588     my $kind = $operation->isStatic ? "Constructor" : (OperationShouldBeOnInstance($interface, $operation) ? "Instance" : "Prototype");
1589     return $codeGenerator->WK_lcfirst($className) . $kind . "Function" . $codeGenerator->WK_ucfirst($functionName);
1590 }
1591
1592 sub GetFullyQualifiedImplementationCallName
1593 {
1594     my ($interface, $property, $implementationName, $implExpression, $conditional) = @_;
1595     
1596     my $implementedBy = $property->extendedAttributes->{ImplementedBy};
1597     if ($implementedBy) {
1598         AddToImplIncludes("${implementedBy}.h", $conditional);
1599         return "WebCore::${implementedBy}::${implementationName}";
1600     }
1601     
1602     if ($property->isStatic || $property->extendedAttributes->{Constructor} || $property->extendedAttributes->{NamedConstructor}) {
1603         return $interface->type->name . "::${implementationName}";
1604     }
1605     
1606     if ($property->isMapLike) {
1607         return "forward" . $codeGenerator->WK_ucfirst($property->name) . "ToMapLike";
1608     }
1609     
1610     return "${implExpression}.${implementationName}";
1611 }
1612
1613 sub AddAdditionalArgumentsForImplementationCall
1614 {
1615     my ($arguments, $interface, $property, $implExpression, $stateExpression, $thisObjectExpression) = @_;
1616     
1617     if ($property->extendedAttributes->{ImplementedBy} && !$property->isStatic) {
1618         unshift(@$arguments, $implExpression);
1619     }
1620     
1621     if ($property->isMapLike) {
1622         push(@$arguments, $stateExpression);
1623         push(@$arguments, $thisObjectExpression);
1624     }
1625 }
1626
1627 sub GetSpecialAccessorOperationForType
1628 {
1629     my ($interface, $special, $firstParameterType, $numberOfParameters) = @_;
1630
1631     foreach my $operation (@{$interface->operations}, @{$interface->anonymousOperations}) {
1632         my $specials = $operation->specials;
1633         my $specialExists = grep { $_ eq $special } @$specials;
1634         my $arguments = $operation->arguments;
1635         if ($specialExists and scalar(@$arguments) == $numberOfParameters and $arguments->[0]->type->name eq $firstParameterType) {
1636             return $operation;
1637         }
1638     }
1639
1640     return 0;
1641 }
1642
1643 sub IsGlobalOrPrimaryGlobalInterface
1644 {
1645     my $interface = shift;
1646
1647     return $interface->extendedAttributes->{Global} || $interface->extendedAttributes->{PrimaryGlobal};
1648 }
1649
1650 sub AttributeShouldBeOnInstance
1651 {
1652     my $interface = shift;
1653     my $attribute = shift;
1654
1655     return 1 if IsGlobalOrPrimaryGlobalInterface($interface);
1656     return 1 if $codeGenerator->IsConstructorType($attribute->type);
1657
1658     # [Unforgeable] attributes should be on the instance.
1659     # https://heycam.github.io/webidl/#Unforgeable
1660     return 1 if IsUnforgeable($interface, $attribute);
1661
1662     if ($interface->extendedAttributes->{CheckSecurity}) {
1663         return 0 if $attribute->extendedAttributes->{DoNotCheckSecurity};
1664         return 0 if $attribute->extendedAttributes->{DoNotCheckSecurityOnGetter};
1665         return 1;
1666     }
1667
1668     return 0;
1669 }
1670
1671 sub IsAlwaysExposedOnInterface
1672 {
1673     my ($interfaceExposures, $contextExposures) = @_;
1674
1675     my %contextExposureSet = ();
1676
1677     if (ref($contextExposures) eq "ARRAY") {
1678         foreach my $contextExposure (@$contextExposures) {
1679             $contextExposureSet{$contextExposure} = 1;
1680         }
1681     } else {
1682         $contextExposureSet{$contextExposures} = 1;
1683     }
1684
1685     if (ref($interfaceExposures) ne "ARRAY") {
1686         $interfaceExposures = [$interfaceExposures];
1687     }
1688
1689     foreach my $interfaceExposure (@$interfaceExposures) {
1690         return 0 unless exists $contextExposureSet{$interfaceExposure};
1691     }
1692
1693     return 1;
1694 }
1695
1696 sub NeedsRuntimeCheck
1697 {
1698     my ($interface, $context) = @_;
1699
1700     if ($context->extendedAttributes->{Exposed}) {
1701         my $interfaceExposures = $interface->extendedAttributes->{Exposed} || "Window";
1702         return 1 if !IsAlwaysExposedOnInterface($interfaceExposures, $context->extendedAttributes->{Exposed});
1703     }
1704
1705     return $context->extendedAttributes->{EnabledAtRuntime}
1706         || $context->extendedAttributes->{EnabledForWorld}
1707         || $context->extendedAttributes->{EnabledBySetting}
1708         || $context->extendedAttributes->{DisabledByQuirk}
1709         || $context->extendedAttributes->{SecureContext}
1710         || $context->extendedAttributes->{ContextHasServiceWorkerScheme};
1711 }
1712
1713 # https://heycam.github.io/webidl/#es-operations
1714 sub OperationShouldBeOnInstance
1715 {
1716     my ($interface, $operation) = @_;
1717
1718     return 1 if IsGlobalOrPrimaryGlobalInterface($interface);
1719
1720     # [Unforgeable] operations should be on the instance. https://heycam.github.io/webidl/#Unforgeable
1721     if (IsUnforgeable($interface, $operation)) {
1722         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);
1723         return 1;
1724     }
1725
1726     return 0;
1727 }
1728
1729 sub OperationHasForcedReturnValue
1730 {
1731     my ($operation) = @_;
1732
1733     foreach my $argument (@{$operation->arguments}) {
1734         return 1 if $argument->extendedAttributes->{ReturnValue};
1735     }
1736     return 0;
1737 }
1738
1739 sub IsAcceleratedDOMAttribute
1740 {
1741     my ($interface, $attribute) = @_;
1742
1743     # If we use CustomGetterSetter in IDL code generator we cannot skip type check.
1744     return 0 if NeedsRuntimeCheck($interface, $attribute) and AttributeShouldBeOnInstance($interface, $attribute);
1745     return 0 if $attribute->extendedAttributes->{PrivateIdentifier} and AttributeShouldBeOnInstance($interface, $attribute);
1746
1747     # If the interface has special logic for casting we cannot hoist type check to JSC.
1748     return 0 if $interface->extendedAttributes->{ImplicitThis};
1749     return 0 if $interface->extendedAttributes->{CustomProxyToJSObject};
1750
1751     return 0 if $attribute->isStatic;
1752     return 0 if $attribute->isMapLike;
1753     return 0 if $codeGenerator->IsConstructorType($attribute->type);
1754     return 0 if IsJSBuiltin($interface, $attribute);
1755     return 0 if $attribute->extendedAttributes->{LenientThis};
1756     return 0 if $codeGenerator->IsPromiseType($attribute->type);
1757     return 0 if $attribute->extendedAttributes->{DOMJIT};
1758     return 1;
1759 }
1760
1761 sub GetJSCAttributesForAttribute
1762 {
1763     my $interface = shift;
1764     my $attribute = shift;
1765
1766     my @specials = ();
1767     push(@specials, "JSC::PropertyAttribute::DontDelete") if IsUnforgeable($interface, $attribute);
1768
1769     # As per Web IDL specification, constructor properties on the ECMAScript global object should not be enumerable.
1770     my $isGlobalConstructor = $codeGenerator->IsConstructorType($attribute->type);
1771     push(@specials, "JSC::PropertyAttribute::DontEnum") if ($attribute->extendedAttributes->{NotEnumerable} || $isGlobalConstructor);
1772     push(@specials, "JSC::PropertyAttribute::ReadOnly") if IsReadonly($attribute);
1773     push(@specials, "JSC::PropertyAttribute::CustomAccessor") unless $isGlobalConstructor or IsJSBuiltin($interface, $attribute);
1774     push(@specials, "JSC::PropertyAttribute::DOMAttribute") if IsAcceleratedDOMAttribute($interface, $attribute);
1775     push(@specials, "JSC::PropertyAttribute::DOMJITAttribute") if $attribute->extendedAttributes->{DOMJIT};
1776     push(@specials, "JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin") if  IsJSBuiltin($interface, $attribute);
1777     return "static_cast<unsigned>(" . ((@specials > 0) ? join(" | ", @specials) : "0") . ")";
1778 }
1779
1780 sub GetIndexedGetterOperation
1781 {
1782     my $interface = shift;
1783     return GetSpecialAccessorOperationForType($interface, "getter", "unsigned long", 1);
1784 }
1785
1786 sub GetIndexedSetterOperation
1787 {
1788     my $interface = shift;
1789     return GetSpecialAccessorOperationForType($interface, "setter", "unsigned long", 2);
1790 }
1791
1792 sub GetNamedGetterOperation
1793 {
1794     my $interface = shift;
1795     return GetSpecialAccessorOperationForType($interface, "getter", "DOMString", 1);
1796 }
1797
1798 sub GetNamedSetterOperation
1799 {
1800     my $interface = shift;
1801     return GetSpecialAccessorOperationForType($interface, "setter", "DOMString", 2);
1802 }
1803
1804 sub GetNamedDeleterOperation
1805 {
1806     my $interface = shift;
1807     return GetSpecialAccessorOperationForType($interface, "deleter", "DOMString", 1);
1808 }
1809
1810 sub InstanceOperationCount
1811 {
1812     my $interface = shift;
1813     my $count = 0;
1814
1815     foreach my $operation (@{$interface->operations}) {
1816         $count++ if OperationShouldBeOnInstance($interface, $operation);
1817     }
1818
1819     return $count;
1820 }
1821
1822 sub PrototypeOperationCount
1823 {
1824     my $interface = shift;
1825     my $count = 0;
1826
1827     foreach my $operation (@{$interface->operations}) {
1828         $count++ if !$operation->isStatic && !OperationShouldBeOnInstance($interface, $operation);
1829     }
1830
1831     $count += scalar @{$interface->iterable->operations} if $interface->iterable;
1832     $count += scalar @{$interface->mapLike->operations} if $interface->mapLike;
1833     $count += scalar @{$interface->serializable->operations} if $interface->serializable;
1834
1835     return $count;
1836 }
1837
1838 sub InstancePropertyCount
1839 {
1840     my $interface = shift;
1841     my $count = 0;
1842     foreach my $attribute (@{$interface->attributes}) {
1843         $count++ if AttributeShouldBeOnInstance($interface, $attribute);
1844     }
1845     $count += InstanceOperationCount($interface);
1846     return $count;
1847 }
1848
1849 sub PrototypePropertyCount
1850 {
1851     my $interface = shift;
1852     my $count = 0;
1853     foreach my $attribute (@{$interface->attributes}) {
1854         $count++ if !AttributeShouldBeOnInstance($interface, $attribute);
1855     }
1856     $count += PrototypeOperationCount($interface);
1857     $count++ if NeedsConstructorProperty($interface);
1858     return $count;
1859 }
1860
1861 sub InstanceOverridesGetOwnPropertySlot
1862 {
1863     my $interface = shift;
1864     return $interface->extendedAttributes->{CustomGetOwnPropertySlot}
1865         || $interface->extendedAttributes->{Plugin}
1866         || GetIndexedGetterOperation($interface)
1867         || GetNamedGetterOperation($interface);
1868 }
1869
1870 sub InstanceOverridesGetOwnPropertyNames
1871 {
1872     my $interface = shift;
1873     return $interface->extendedAttributes->{CustomGetOwnPropertyNames}
1874         || GetIndexedGetterOperation($interface)
1875         || GetNamedGetterOperation($interface);
1876 }
1877
1878 sub InstanceOverridesPut
1879 {
1880     my $interface = shift;
1881     return $interface->extendedAttributes->{CustomPut}
1882         || $interface->extendedAttributes->{Plugin}
1883         || GetIndexedSetterOperation($interface)
1884         || GetNamedSetterOperation($interface);
1885 }
1886
1887 sub InstanceOverridesDefineOwnProperty
1888 {
1889     my $interface = shift;
1890
1891     return 0 if $interface->extendedAttributes->{DefaultDefineOwnProperty};
1892
1893     return $interface->extendedAttributes->{CustomDefineOwnProperty}
1894         || GetIndexedSetterOperation($interface)
1895         || GetNamedSetterOperation($interface);
1896 }
1897
1898 sub InstanceOverridesDeleteProperty
1899 {
1900     my $interface = shift;
1901     return $interface->extendedAttributes->{CustomDeleteProperty}
1902         || GetNamedDeleterOperation($interface);
1903 }
1904
1905 sub PrototypeHasStaticPropertyTable
1906 {
1907     my $interface = shift;
1908     my $numConstants = @{$interface->constants};
1909     return $numConstants > 0 || PrototypePropertyCount($interface) > 0;
1910 }
1911
1912 sub InstanceNeedsVisitChildren
1913 {
1914     my $interface = shift;
1915     
1916     foreach my $attribute (@{$interface->attributes}) {
1917         return 1 if $attribute->extendedAttributes->{CachedAttribute};
1918     }
1919
1920     return 1 if $interface->extendedAttributes->{JSCustomMarkFunction};
1921     return 1 if $interface->extendedAttributes->{ReportExtraMemoryCost};
1922     return 0;
1923 }
1924
1925 sub InstanceNeedsEstimatedSize
1926 {
1927     my $interface = shift;
1928     return $interface->extendedAttributes->{ReportExtraMemoryCost};
1929 }
1930
1931 sub GetImplClassName
1932 {
1933     my $interface = shift;
1934
1935     return $interface->type->name;
1936 }
1937
1938 sub IsClassNameWordBoundary
1939 {
1940     my ($name, $i) = @_;
1941
1942     # Interpret negative numbers as distance from end of string, just as the substr function does.
1943     $i += length($name) if $i < 0;
1944
1945     return 0 if $i < 0;
1946     return 1 if $i == 0;
1947     return 1 if $i == length($name);
1948     return 0 if $i > length($name);
1949
1950     my $checkString = substr($name, $i - 1);
1951     return $checkString =~ /^[^A-Z][A-Z]/ || $checkString =~ /^[A-Z][A-Z][^A-Z]/;
1952 }
1953
1954 sub IsPrefixRemovable
1955 {
1956     my ($class, $name, $i) = @_;
1957
1958     return IsClassNameWordBoundary($name, $i)
1959         && (IsClassNameWordBoundary($class, $i) && substr($class, 0, $i) eq substr($name, 0, $i)
1960             || IsClassNameWordBoundary($class, -$i) && substr($class, -$i) eq substr($name, 0, $i));
1961 }
1962
1963 sub GetNestedClassName
1964 {
1965     my ($interface, $name) = @_;
1966
1967     my $class = GetImplClassName($interface);
1968     my $member = $codeGenerator->WK_ucfirst($name);
1969
1970     # Since the enumeration name will be nested in the class name's namespace, remove any words
1971     # that happen to match the start or end of the class name. If an enumeration is named TrackType or
1972     # TextTrackType, and the class is named TextTrack, then we will get a name like TextTrack::Type.
1973     my $memberLength = length($member);
1974     my $longestPrefixLength = 0;
1975     if ($member =~ /^[A-Z]./) {
1976         for (my $i = 2; $i < $memberLength - 1; $i++) {
1977             $longestPrefixLength = $i if IsPrefixRemovable($class, $member, $i);
1978         }
1979     }
1980     $member = substr($member, $longestPrefixLength);
1981
1982     return "${class}::$member";
1983 }
1984
1985 sub GetEnumerationClassName
1986 {
1987     my ($type, $interface) = @_;
1988
1989     assert("Not a type") if ref($type) ne "IDLType";
1990
1991     if ($codeGenerator->HasEnumImplementationNameOverride($type)) {
1992         return $codeGenerator->GetEnumImplementationNameOverride($type);
1993     }
1994
1995     my $name = $type->name;
1996
1997     return $name if $codeGenerator->IsExternalEnumType($type);
1998     return $name unless defined($interface);
1999
2000     return GetNestedClassName($interface, $name);
2001 }
2002
2003 sub GetEnumerationValueName
2004 {
2005     my ($name) = @_;
2006
2007     return "EmptyString" if $name eq "";
2008     $name = join("", map { $codeGenerator->WK_ucfirst($_) } split("-", $name));
2009     $name = "_$name" if $name =~ /^\d/;
2010     return $name;
2011 }
2012
2013 sub GenerateEnumerationHeader
2014 {
2015     my ($object, $enumeration, $className) = @_;
2016  
2017     # - Add default header template and header protection.
2018     push(@headerContentHeader, GenerateHeaderContentHeader($enumeration));
2019
2020     $headerIncludes{"${className}.h"} = 1;
2021
2022     push(@headerContent, "\nnamespace WebCore {\n\n");
2023     push(@headerContent, GenerateEnumerationHeaderContent($enumeration, $className));
2024     push(@headerContent, "} // namespace WebCore\n");
2025      
2026     my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2027     push(@headerContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
2028 }
2029  
2030 sub GenerateEnumerationImplementation
2031 {
2032     my ($object, $enumeration, $className) = @_;
2033  
2034     # - Add default header template
2035     push(@implContentHeader, GenerateImplementationContentHeader($enumeration));
2036
2037     push(@implContent, "\n\nnamespace WebCore {\n");
2038     push(@implContent, "using namespace JSC;\n\n");
2039     push(@implContent, GenerateEnumerationImplementationContent($enumeration, $className));
2040     push(@implContent, "} // namespace WebCore\n");
2041      
2042     my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2043     push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
2044 }
2045
2046 sub GenerateEnumerationImplementationContent
2047 {
2048     my ($enumeration, $className, $interface, $conditionalString) = @_;
2049
2050     # FIXME: A little ugly to have this be a side effect instead of a return value.
2051     AddToImplIncludes("<JavaScriptCore/JSString.h>");
2052     AddToImplIncludes("<JavaScriptCore/JSCInlines.h>");
2053     AddToImplIncludes("JSDOMConvertEnumeration.h");
2054
2055     my $result = "";
2056     $result .= "#if ${conditionalString}\n\n" if $conditionalString;
2057
2058
2059     $result .= "String convertEnumerationToString($className enumerationValue)\n";
2060     $result .= "{\n";
2061     AddToImplIncludes("<wtf/NeverDestroyed.h>");
2062     $result .= "    static const NeverDestroyed<String> values[] = {\n";
2063     foreach my $value (@{$enumeration->values}) {
2064         if ($value eq "") {
2065             $result .= "        emptyString(),\n";
2066         } else {
2067             $result .= "        MAKE_STATIC_STRING_IMPL(\"$value\"),\n";
2068         }
2069     }
2070     $result .= "    };\n";
2071     my $index = 0;
2072     foreach my $value (@{$enumeration->values}) {
2073         my $enumerationValueName = GetEnumerationValueName($value);
2074         $result .= "    static_assert(static_cast<size_t>(${className}::$enumerationValueName) == $index, \"${className}::$enumerationValueName is not $index as expected\");\n";
2075         $index++;
2076     }
2077     $result .= "    ASSERT(static_cast<size_t>(enumerationValue) < WTF_ARRAY_LENGTH(values));\n";
2078     $result .= "    return values[static_cast<size_t>(enumerationValue)];\n";
2079     $result .= "}\n\n";
2080
2081
2082     # FIXME: Change to take VM& instead of ExecState*.
2083     $result .= "template<> JSString* convertEnumerationToJS(ExecState& state, $className enumerationValue)\n";
2084     $result .= "{\n";
2085     $result .= "    return jsStringWithCache(&state, convertEnumerationToString(enumerationValue));\n";
2086     $result .= "}\n\n";
2087
2088     # FIXME: Change to take VM& instead of ExecState&.
2089     # FIXME: Consider using toStringOrNull to make exception checking faster.
2090     # FIXME: Consider finding a more efficient way to match against all the strings quickly.
2091     $result .= "template<> std::optional<$className> parseEnumeration<$className>(ExecState& state, JSValue value)\n";
2092     $result .= "{\n";
2093     $result .= "    auto stringValue = value.toWTFString(&state);\n";
2094     foreach my $value (@{$enumeration->values}) {
2095         my $enumerationValueName = GetEnumerationValueName($value);
2096         if ($value eq "") {
2097             $result .= "    if (stringValue.isEmpty())\n";
2098         } else {
2099             $result .= "    if (stringValue == \"$value\")\n";
2100         }
2101         $result .= "        return ${className}::${enumerationValueName};\n";
2102     }
2103     $result .= "    return std::nullopt;\n";
2104     $result .= "}\n\n";
2105
2106     $result .= "template<> const char* expectedEnumerationValues<$className>()\n";
2107     $result .= "{\n";
2108     $result .= "    return \"\\\"" . join ("\\\", \\\"", @{$enumeration->values}) . "\\\"\";\n";
2109     $result .= "}\n\n";
2110
2111     $result .= "#endif\n\n" if $conditionalString;
2112
2113     return $result;
2114 }
2115
2116 sub GenerateEnumerationsImplementationContent
2117 {
2118     my ($interface, $enumerations) = @_;
2119
2120     return "" unless @$enumerations;
2121
2122     my $result = "";
2123     foreach my $enumeration (@$enumerations) {
2124         my $className = GetEnumerationClassName($enumeration->type, $interface);
2125         my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2126         $result .= GenerateEnumerationImplementationContent($enumeration, $className, $interface, $conditionalString);
2127     }
2128     return $result;
2129 }
2130
2131 sub GenerateEnumerationHeaderContent
2132 {
2133     my ($enumeration, $className, $conditionalString) = @_;
2134
2135     $headerIncludes{"JSDOMConvertEnumeration.h"} = 1;
2136
2137     my $result = "";
2138     $result .= "#if ${conditionalString}\n\n" if $conditionalString;
2139
2140     my $exportMacro = GetExportMacroForJSClass($enumeration);
2141
2142     $result .= "${exportMacro}String convertEnumerationToString($className);\n";
2143     $result .= "template<> ${exportMacro}JSC::JSString* convertEnumerationToJS(JSC::ExecState&, $className);\n\n";
2144     $result .= "template<> ${exportMacro}std::optional<$className> parseEnumeration<$className>(JSC::ExecState&, JSC::JSValue);\n";
2145     $result .= "template<> ${exportMacro}const char* expectedEnumerationValues<$className>();\n\n";
2146     $result .= "#endif\n\n" if $conditionalString;
2147     
2148     return $result;
2149 }
2150
2151 sub GenerateEnumerationsHeaderContent
2152 {
2153     my ($interface, $enumerations) = @_;
2154
2155     return "" unless @$enumerations;
2156
2157     # FIXME: Could optimize this to only generate the parts of each enumeration that are actually
2158     # used, which would require iterating over everything in the interface.
2159
2160     my $result = "";
2161     foreach my $enumeration (@$enumerations) {
2162         my $className = GetEnumerationClassName($enumeration->type, $interface);
2163         my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2164         $result .= GenerateEnumerationHeaderContent($enumeration, $className, $conditionalString);
2165     }
2166     return $result;
2167 }
2168
2169 sub GetDictionaryClassName
2170 {
2171     my ($type, $interface) = @_;
2172
2173     if ($codeGenerator->HasDictionaryImplementationNameOverride($type)) {
2174         return $codeGenerator->GetDictionaryImplementationNameOverride($type);
2175     }
2176
2177     my $name = $type->name;
2178     return $name if $codeGenerator->IsExternalDictionaryType($type);
2179     return $name unless defined($interface);
2180     return GetNestedClassName($interface, $name);
2181 }
2182
2183 sub GenerateDefaultValue
2184 {
2185     my ($typeScope, $context, $type, $defaultValue) = @_;
2186
2187     if ($codeGenerator->IsStringType($type)) {
2188         my $useAtomicString = $type->extendedAttributes->{AtomicString};
2189         if ($defaultValue eq "null") {
2190             return $useAtomicString ? "nullAtom()" : "String()";
2191         } elsif ($defaultValue eq "\"\"") {
2192             return $useAtomicString ? "emptyAtom()" : "emptyString()";
2193         } else {
2194             return $useAtomicString ? "AtomicString(${defaultValue}, AtomicString::ConstructFromLiteral)" : "${defaultValue}_s";
2195         }
2196     }
2197
2198     if ($codeGenerator->IsEnumType($type)) {
2199         # FIXME: Would be nice to report an error if the value does not have quote marks around it.
2200         # FIXME: Would be nice to report an error if the value is not one of the enumeration values.
2201         if ($defaultValue eq "null") {
2202             die if !$type->isNullable;
2203             return "std::nullopt";
2204         }
2205         my $className = GetEnumerationClassName($type, $typeScope);
2206         my $enumerationValueName = GetEnumerationValueName(substr($defaultValue, 1, -1));
2207         return $className . "::" . $enumerationValueName;
2208     }
2209     if ($defaultValue eq "null") {
2210         if ($type->isUnion) {
2211             return "std::nullopt" if $type->isNullable;
2212
2213             my $IDLType = GetIDLType($typeScope, $type);
2214             return "convert<${IDLType}>(state, jsNull());";
2215         }
2216
2217         return "jsNull()" if $type->name eq "any";
2218         return "nullptr" if $codeGenerator->IsWrapperType($type) || $codeGenerator->IsBufferSourceType($type);
2219         return "String()" if $codeGenerator->IsStringType($type);
2220         return "std::nullopt";
2221     }
2222
2223     if ($defaultValue eq "[]") {
2224         my $IDLType = GetIDLType($typeScope, $type);
2225         return "Converter<${IDLType}>::ReturnType{ }";
2226     }
2227
2228     return "jsUndefined()" if $defaultValue eq "undefined";
2229     return "PNaN" if $defaultValue eq "NaN";
2230
2231     return $defaultValue;
2232 }
2233
2234 sub GenerateDictionaryHeaderContent
2235 {
2236     my ($dictionary, $className, $conditionalString) = @_;
2237
2238     $headerIncludes{"JSDOMConvertDictionary.h"} = 1;
2239
2240     my $exportMacro = GetExportMacroForJSClass($dictionary);
2241
2242     my $result = "";
2243     $result .= "#if ${conditionalString}\n\n" if $conditionalString;
2244     $result .= "template<> ${exportMacro}${className} convertDictionary<${className}>(JSC::ExecState&, JSC::JSValue);\n\n";
2245
2246     if ($dictionary->extendedAttributes->{JSGenerateToJSObject}) {
2247         $result .= "${exportMacro}JSC::JSObject* convertDictionaryToJS(JSC::ExecState&, JSDOMGlobalObject&, const ${className}&);\n\n";
2248     }
2249
2250     $result .= "#endif\n\n" if $conditionalString;
2251     return $result;
2252 }
2253
2254 sub GenerateDictionariesHeaderContent
2255 {
2256     my ($typeScope, $allDictionaries) = @_;
2257
2258     return "" unless @$allDictionaries;
2259
2260     my $result = "";
2261     foreach my $dictionary (@$allDictionaries) {
2262         $headerIncludes{$typeScope->type->name . ".h"} = 1 if $typeScope;
2263         my $className = GetDictionaryClassName($dictionary->type, $typeScope);
2264         my $conditionalString = $codeGenerator->GenerateConditionalString($dictionary);
2265         $result .= GenerateDictionaryHeaderContent($dictionary, $className, $conditionalString);
2266     }
2267     return $result;
2268 }
2269
2270 sub GenerateDictionaryImplementationContent
2271 {
2272     my ($dictionary, $className, $interface) = @_;
2273
2274     my $result = "";
2275
2276     my $name = $dictionary->type->name;
2277     my $typeScope = $interface || $dictionary;
2278
2279     my $conditional = $dictionary->extendedAttributes->{Conditional};
2280     if ($conditional) {
2281         my $conditionalString = $codeGenerator->GenerateConditionalStringFromAttributeValue($conditional);
2282         $result .= "#if ${conditionalString}\n\n";
2283     }
2284
2285     # FIXME: A little ugly to have this be a side effect instead of a return value.
2286     AddToImplIncludes("<JavaScriptCore/JSCInlines.h>");
2287     AddToImplIncludes("JSDOMConvertDictionary.h");
2288
2289     # https://heycam.github.io/webidl/#es-dictionary
2290     $result .= "template<> $className convertDictionary<$className>(ExecState& state, JSValue value)\n";
2291     $result .= "{\n";
2292     $result .= "    VM& vm = state.vm();\n";
2293     $result .= "    auto throwScope = DECLARE_THROW_SCOPE(vm);\n";
2294     $result .= "    bool isNullOrUndefined = value.isUndefinedOrNull();\n";
2295     $result .= "    auto* object = isNullOrUndefined ? nullptr : value.getObject();\n";
2296
2297     # 1. If Type(V) is not Undefined, Null or Object, then throw a TypeError.
2298     $result .= "    if (UNLIKELY(!isNullOrUndefined && !object)) {\n";
2299     $result .= "        throwTypeError(&state, throwScope);\n";
2300     $result .= "        return { };\n";
2301     $result .= "    }\n";
2302
2303     # 2. Let dict be an empty dictionary value of type D; every dictionary member is initially considered to be not present.
2304
2305     # 3. Let dictionaries be a list consisting of D and all of D’s inherited dictionaries, in order from least to most derived.
2306     my @dictionaries;
2307     push(@dictionaries, $dictionary);
2308     my $parentType = $dictionary->parentType;
2309     while (defined($parentType)) {
2310         my $parentDictionary = $codeGenerator->GetDictionaryByType($parentType);
2311         assert("Unable to find definition for dictionary named '" . $parentType->name . "'!") unless defined($parentDictionary);
2312         unshift(@dictionaries, $parentDictionary);
2313         $parentType = $parentDictionary->parentType;
2314     }
2315
2316     my $arguments = "";
2317     my $comma = "";
2318
2319     $result .= "    $className result;\n";
2320
2321     # 4. For each dictionary dictionary in dictionaries, in order:
2322     foreach my $dictionary (@dictionaries) {
2323         # For each dictionary member member declared on dictionary, in lexicographical order:
2324         my @sortedMembers = sort { $a->name cmp $b->name } @{$dictionary->members};
2325         foreach my $member (@sortedMembers) {
2326             $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.
2327
2328             my $type = $member->type;
2329             AddToImplIncludesForIDLType($type);
2330
2331             # 4.1. Let key be the identifier of member.
2332             my $key = $member->name;
2333             my $implementedAsKey = $member->extendedAttributes->{ImplementedAs} || $key;
2334
2335             # 4.2. Let value be an ECMAScript value, depending on Type(V):
2336             $result .= "    JSValue ${key}Value;\n";
2337             $result .= "    if (isNullOrUndefined)\n";
2338             $result .= "        ${key}Value = jsUndefined();\n";
2339             $result .= "    else {\n";
2340             $result .= "        ${key}Value = object->get(&state, Identifier::fromString(&state, \"${key}\"));\n";
2341             $result .= "        RETURN_IF_EXCEPTION(throwScope, { });\n";
2342             $result .= "    }\n";
2343
2344             my $IDLType = GetIDLType($typeScope, $type);
2345
2346             # 4.3. If value is not undefined, then:
2347             $result .= "    if (!${key}Value.isUndefined()) {\n";
2348
2349             my $nativeValue = JSValueToNative($typeScope, $member, "${key}Value", $member->extendedAttributes->{Conditional}, "&state", "state");
2350             $result .= "        result.$implementedAsKey = $nativeValue;\n";
2351             $result .= "        RETURN_IF_EXCEPTION(throwScope, { });\n";
2352
2353             # Value is undefined.
2354             # 4.4. Otherwise, if value is undefined but the dictionary member has a default value, then:
2355             if (!$member->isRequired && defined $member->default) {
2356                 $result .= "    } else\n";
2357                 $result .= "        result.$implementedAsKey = " . GenerateDefaultValue($typeScope, $member, $member->type, $member->default) . ";\n";
2358             } elsif ($member->isRequired) {
2359                 # 4.5. Otherwise, if value is undefined and the dictionary member is a required dictionary member, then throw a TypeError.
2360                 $result .= "    } else {\n";
2361                 $result .= "        throwRequiredMemberTypeError(state, throwScope, \"". $member->name ."\", \"$name\", \"". GetTypeNameForDisplayInException($type) ."\");\n";
2362                 $result .= "        return { };\n";
2363                 $result .= "    }\n";
2364             } else {
2365                 $result .= "    }\n";
2366             }
2367         }
2368     }
2369
2370     # 5. Return dict.
2371     $result .= "    return result;\n";
2372     $result .= "}\n\n";
2373
2374     if ($dictionary->extendedAttributes->{JSGenerateToJSObject}) {
2375         AddToImplIncludes("JSDOMGlobalObject.h");
2376         AddToImplIncludes("<JavaScriptCore/ObjectConstructor.h>");
2377
2378         $result .= "JSC::JSObject* convertDictionaryToJS(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const ${className}& dictionary)\n";
2379         $result .= "{\n";
2380         $result .= "    auto& vm = state.vm();\n\n";
2381
2382         # 1. Let O be ! ObjectCreate(%ObjectPrototype%).
2383         $result .= "    auto result = constructEmptyObject(&state, globalObject.objectPrototype());\n\n";
2384
2385         # 2. Let dictionaries be a list consisting of D and all of D’s inherited dictionaries,
2386         #    in order from least to most derived.
2387         #    NOTE: This was done above.
2388
2389         # 3. For each dictionary dictionary in dictionaries, in order:
2390         foreach my $dictionary (@dictionaries) {
2391             # 3.1. For each dictionary member member declared on dictionary, in lexicographical order:
2392             my @sortedMembers = sort { $a->name cmp $b->name } @{$dictionary->members};
2393             foreach my $member (@sortedMembers) {
2394                 my $key = $member->name;
2395                 my $implementedAsKey = $member->extendedAttributes->{ImplementedAs} || $key;
2396                 my $valueExpression = "dictionary.${implementedAsKey}";
2397
2398                 # 1. Let key be the identifier of member.
2399                 # 2. If the dictionary member named key is present in V, then:
2400                     # 1. Let idlValue be the value of member on V.
2401                     # 2. Let value be the result of converting idlValue to an ECMAScript value.
2402                     # 3. Perform ! CreateDataProperty(O, key, value).
2403
2404                 if (!$member->isRequired && not defined $member->default) {
2405                     my $IDLType = GetIDLType($typeScope, $member->type);
2406                     my $conversionExpression = NativeToJSValueUsingReferences($member, $typeScope, "${IDLType}::extractValueFromNullable(${valueExpression})", "globalObject");
2407
2408                     $result .= "    if (!${IDLType}::isNullValue(${valueExpression})) {\n";
2409                     $result .= "        auto ${key}Value = ${conversionExpression};\n";
2410                     $result .= "        result->putDirect(vm, JSC::Identifier::fromString(&vm, \"${key}\"), ${key}Value);\n";
2411                     $result .= "    }\n";
2412                 } else {
2413                     my $conversionExpression = NativeToJSValueUsingReferences($member, $typeScope, $valueExpression, "globalObject");
2414
2415                     $result .= "    auto ${key}Value = ${conversionExpression};\n";
2416                     $result .= "    result->putDirect(vm, JSC::Identifier::fromString(&vm, \"${key}\"), ${key}Value);\n";
2417                 }
2418             }
2419         }
2420
2421         $result .= "    return result;\n";
2422         $result .= "}\n\n";
2423     }
2424
2425     $result .= "#endif\n\n" if $conditional;
2426
2427     return $result;
2428 }
2429
2430 sub GenerateDictionariesImplementationContent
2431 {
2432     my ($typeScope, $allDictionaries) = @_;
2433
2434     my $result = "";
2435     foreach my $dictionary (@$allDictionaries) {
2436         my $className = GetDictionaryClassName($dictionary->type, $typeScope);
2437         $result .= GenerateDictionaryImplementationContent($dictionary, $className, $typeScope);
2438     }
2439     return $result;
2440 }
2441
2442 sub GetJSTypeForNode
2443 {
2444     my ($interface) = @_;
2445
2446     if ($codeGenerator->InheritsInterface($interface, "Document")) {
2447         return "JSDocumentWrapperType";
2448     }
2449     if ($codeGenerator->InheritsInterface($interface, "DocumentFragment")) {
2450         return "JSDocumentFragmentNodeType";
2451     }
2452     if ($codeGenerator->InheritsInterface($interface, "DocumentType")) {
2453         return "JSDocumentTypeNodeType";
2454     }
2455     if ($codeGenerator->InheritsInterface($interface, "ProcessingInstruction")) {
2456         return "JSProcessingInstructionNodeType";
2457     }
2458     if ($codeGenerator->InheritsInterface($interface, "CDATASection")) {
2459         return "JSCDATASectionNodeType";
2460     }
2461     if ($codeGenerator->InheritsInterface($interface, "Attr")) {
2462         return "JSAttrNodeType";
2463     }
2464     if ($codeGenerator->InheritsInterface($interface, "Comment")) {
2465         return "JSCommentNodeType";
2466     }
2467     if ($codeGenerator->InheritsInterface($interface, "Text")) {
2468         return "JSTextNodeType";
2469     }
2470     if ($codeGenerator->InheritsInterface($interface, "Element")) {
2471         return "JSElementType";
2472     }
2473     return "JSNodeType";
2474 }
2475
2476 sub GenerateHeader
2477 {
2478     my ($object, $interface, $enumerations, $dictionaries) = @_;
2479
2480     my $interfaceName = $interface->type->name;
2481     my $className = "JS$interfaceName";
2482     my %structureFlags = ();
2483
2484     my $hasParent = $interface->parentType || $interface->extendedAttributes->{JSLegacyParent};
2485     my $parentClassName = GetParentClassName($interface);
2486     my $needsVisitChildren = InstanceNeedsVisitChildren($interface);
2487
2488     # - Add default header template and header protection
2489     push(@headerContentHeader, GenerateHeaderContentHeader($interface));
2490
2491     if ($hasParent) {
2492         $headerIncludes{"$parentClassName.h"} = 1;
2493     } else {
2494         $headerIncludes{"JSDOMWrapper.h"} = 1;
2495         if ($interface->isException) {
2496             $headerIncludes{"<JavaScriptCore/ErrorPrototype.h>"} = 1;
2497         }
2498     }
2499
2500     $headerIncludes{"$interfaceName.h"} = 1 if $hasParent && $interface->extendedAttributes->{JSGenerateToNativeObject};
2501
2502     $headerIncludes{"SVGElement.h"} = 1 if $className =~ /^JSSVG/;
2503
2504     my $implType = GetImplClassName($interface);
2505
2506     my $numConstants = @{$interface->constants};
2507     my $numAttributes = @{$interface->attributes};
2508     my $numOperations = @{$interface->operations};
2509
2510     push(@headerContent, "\nnamespace WebCore {\n\n");
2511
2512     if ($codeGenerator->IsSVGAnimatedType($interface->type)) {
2513         $headerIncludes{"$interfaceName.h"} = 1;
2514     } else {
2515         # Implementation class forward declaration
2516         if (IsDOMGlobalObject($interface)) {
2517             AddClassForwardIfNeeded($interface->type);
2518         }
2519     }
2520
2521     push(@headerContent, "class JSWindowProxy;\n\n") if $interfaceName eq "DOMWindow" or $interfaceName eq "RemoteDOMWindow";
2522
2523     my $exportMacro = GetExportMacroForJSClass($interface);
2524
2525     # Class declaration
2526     push(@headerContent, "class $exportMacro$className : public $parentClassName {\n");
2527
2528     # Static create methods
2529     push(@headerContent, "public:\n");
2530     push(@headerContent, "    using Base = $parentClassName;\n");
2531     push(@headerContent, "    using DOMWrapped = $implType;\n") if $hasParent;
2532
2533     if ($interfaceName eq "DOMWindow" || $interfaceName eq "RemoteDOMWindow") {
2534         push(@headerContent, "    static $className* create(JSC::VM& vm, JSC::Structure* structure, Ref<$implType>&& impl, JSWindowProxy* proxy)\n");
2535         push(@headerContent, "    {\n");
2536         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(vm.heap)) ${className}(vm, structure, WTFMove(impl), proxy);\n");
2537         push(@headerContent, "        ptr->finishCreation(vm, proxy);\n");
2538         push(@headerContent, "        return ptr;\n");
2539         push(@headerContent, "    }\n\n");
2540     } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope")) {
2541         push(@headerContent, "    static $className* create(JSC::VM& vm, JSC::Structure* structure, Ref<$implType>&& impl, JSC::JSProxy* proxy)\n");
2542         push(@headerContent, "    {\n");
2543         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(vm.heap)) ${className}(vm, structure, WTFMove(impl));\n");
2544         push(@headerContent, "        ptr->finishCreation(vm, proxy);\n");
2545         push(@headerContent, "        return ptr;\n");
2546         push(@headerContent, "    }\n\n");
2547     } elsif ($interface->extendedAttributes->{MasqueradesAsUndefined}) {
2548         AddIncludesForImplementationTypeInHeader($implType);
2549         push(@headerContent, "    static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<$implType>&& impl)\n");
2550         push(@headerContent, "    {\n");
2551         push(@headerContent, "        globalObject->masqueradesAsUndefinedWatchpoint()->fireAll(globalObject->vm(), \"Allocated masquerading object\");\n");
2552         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject, WTFMove(impl));\n");
2553         push(@headerContent, "        ptr->finishCreation(globalObject->vm());\n");
2554         push(@headerContent, "        return ptr;\n");
2555         push(@headerContent, "    }\n\n");
2556     } elsif (!NeedsImplementationClass($interface)) {
2557         push(@headerContent, "    static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject)\n");
2558         push(@headerContent, "    {\n");
2559         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject);\n");
2560         push(@headerContent, "        ptr->finishCreation(globalObject->vm());\n");
2561         push(@headerContent, "        return ptr;\n");
2562         push(@headerContent, "    }\n\n");  
2563     } else {
2564         AddIncludesForImplementationTypeInHeader($implType);
2565         push(@headerContent, "    static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<$implType>&& impl)\n");
2566         push(@headerContent, "    {\n");
2567         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject, WTFMove(impl));\n");
2568         push(@headerContent, "        ptr->finishCreation(globalObject->vm());\n");
2569         push(@headerContent, "        return ptr;\n");
2570         push(@headerContent, "    }\n\n");
2571     }
2572
2573     push(@headerContent, "    static const bool needsDestruction = false;\n\n") if IsDOMGlobalObject($interface);
2574
2575     $structureFlags{"JSC::HasStaticPropertyTable"} = 1 if InstancePropertyCount($interface) > 0;
2576     $structureFlags{"JSC::NewImpurePropertyFiresWatchpoints"} = 1 if $interface->extendedAttributes->{NewImpurePropertyFiresWatchpoints};
2577     $structureFlags{"JSC::IsImmutablePrototypeExoticObject"} = 1 if $interface->extendedAttributes->{IsImmutablePrototypeExoticObject};
2578     $structureFlags{"JSC::MasqueradesAsUndefined"} = 1 if $interface->extendedAttributes->{MasqueradesAsUndefined};
2579     $structureFlags{"JSC::ImplementsHasInstance | JSC::ImplementsDefaultHasInstance"} = 1 if $interfaceName eq "DOMWindow";
2580         
2581     # Prototype
2582     unless (ShouldUseGlobalObjectPrototype($interface)) {
2583         push(@headerContent, "    static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&);\n");
2584         push(@headerContent, "    static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&);\n");
2585     }
2586
2587     # JSValue to implementation type
2588     if (ShouldGenerateToWrapped($hasParent, $interface)) {
2589         # FIXME: Add extended attribute for this.
2590         my @toWrappedArguments = ();
2591         push(@toWrappedArguments, "JSC::VM&");
2592         push(@toWrappedArguments, "JSC::ExecState&") if $interface->type->name eq "XPathNSResolver";
2593         push(@toWrappedArguments, "JSC::JSValue");
2594
2595         my $toWrappedType = $interface->type->name eq "XPathNSResolver" ? "RefPtr<${implType}>" : "${implType}*";
2596
2597         my $export = "";
2598         $export = "WEBCORE_EXPORT " if $interface->extendedAttributes->{ExportToWrappedFunction};
2599         push(@headerContent, "    static ${export}${toWrappedType} toWrapped(" . join(", ", @toWrappedArguments) . ");\n");
2600     }
2601
2602     $headerTrailingIncludes{"${className}Custom.h"} = 1 if $interface->extendedAttributes->{JSCustomHeader};
2603
2604     my $namedGetterOperation = GetNamedGetterOperation($interface);
2605     my $indexedGetterOperation = GetIndexedGetterOperation($interface);
2606
2607     # FIXME: Why doesn't this also include Indexed Getters and [CustomGetOwnPropertySlot]
2608     if ($namedGetterOperation) {
2609         if ($codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins")) {
2610             $structureFlags{"JSC::GetOwnPropertySlotIsImpure"} = 1;
2611         } else {
2612             $structureFlags{"JSC::GetOwnPropertySlotIsImpureForPropertyAbsence"} = 1;
2613         }
2614     }
2615     
2616     # ClassInfo MethodTable declarations.
2617     
2618     if (InstanceOverridesGetOwnPropertySlot($interface)) {
2619         push(@headerContent, "    static bool getOwnPropertySlot(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&);\n");
2620         $structureFlags{"JSC::OverridesGetOwnPropertySlot"} = 1;
2621         push(@headerContent, "    static bool getOwnPropertySlotByIndex(JSC::JSObject*, JSC::ExecState*, unsigned propertyName, JSC::PropertySlot&);\n");
2622         $structureFlags{"JSC::InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero"} = 1;
2623     }
2624     
2625     if (InstanceOverridesGetOwnPropertyNames($interface)) {
2626         push(@headerContent, "    static void getOwnPropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode = JSC::EnumerationMode());\n");
2627         $structureFlags{"JSC::OverridesGetPropertyNames"} = 1;
2628     }
2629     
2630     if (InstanceOverridesPut($interface)) {
2631         push(@headerContent, "    static bool put(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&);\n");
2632         push(@headerContent, "    static bool putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName, JSC::JSValue, bool shouldThrow);\n");
2633     }
2634     
2635     if (InstanceOverridesDefineOwnProperty($interface)) {
2636         push(@headerContent, "    static bool defineOwnProperty(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, const JSC::PropertyDescriptor&, bool shouldThrow);\n");
2637     }
2638
2639     if (InstanceOverridesDeleteProperty($interface)) {
2640         push(@headerContent, "    static bool deleteProperty(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName);\n");
2641         push(@headerContent, "    static bool deletePropertyByIndex(JSC::JSCell*, JSC::ExecState*, unsigned);\n");
2642     }
2643
2644     if (InstanceOverridesGetCallData($interface)) {
2645         push(@headerContent, "    static JSC::CallType getCallData(JSC::JSCell*, JSC::CallData&);\n\n");
2646         $headerIncludes{"<JavaScriptCore/CallData.h>"} = 1;
2647         $structureFlags{"JSC::OverridesGetCallData"} = 1;
2648     }
2649     
2650     if ($interface->extendedAttributes->{CustomGetPrototype}) {
2651         push(@headerContent, "    static JSC::JSValue getPrototype(JSC::JSObject*, JSC::ExecState*);\n");
2652     }
2653     
2654     if ($interface->extendedAttributes->{CustomToStringName}) {
2655         push(@headerContent, "    static String toStringName(const JSC::JSObject*, JSC::ExecState*);\n");
2656     }
2657     
2658     if ($interface->extendedAttributes->{CustomPreventExtensions}) {
2659         push(@headerContent, "    static bool preventExtensions(JSC::JSObject*, JSC::ExecState*);\n");
2660     }
2661
2662     if (InstanceNeedsEstimatedSize($interface)) {
2663         push(@headerContent, "    static size_t estimatedSize(JSCell*, JSC::VM&);\n");
2664     }
2665     
2666     if (!$hasParent) {
2667         push(@headerContent, "    static void destroy(JSC::JSCell*);\n");
2668     }
2669
2670     # Class info
2671     if ($interfaceName eq "Node") {
2672         push(@headerContent, "\n");
2673         push(@headerContent, "protected:\n");
2674         push(@headerContent, "    static const JSC::ClassInfo s_info;\n");
2675         push(@headerContent, "public:\n");
2676         push(@headerContent, "    static constexpr const JSC::ClassInfo* info() { return &s_info; }\n\n");
2677     } else {
2678         push(@headerContent, "\n");
2679         push(@headerContent, "    DECLARE_INFO;\n\n");
2680     }
2681
2682     # Structure ID
2683     push(@headerContent, "    static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)\n");
2684     push(@headerContent, "    {\n");
2685     if (IsDOMGlobalObject($interface)) {
2686         push(@headerContent, "        return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::GlobalObjectType, StructureFlags), info());\n");
2687     } elsif ($codeGenerator->InheritsInterface($interface, "Node")) {
2688         my $type = GetJSTypeForNode($interface);
2689         push(@headerContent, "        return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSType($type), StructureFlags), info());\n");
2690     } elsif ($codeGenerator->InheritsInterface($interface, "Event")) {
2691         push(@headerContent, "        return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSType(JSEventType), StructureFlags), info());\n");
2692     } else {
2693         push(@headerContent, "        return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());\n");
2694     }
2695     push(@headerContent, "    }\n\n");
2696
2697     # Custom pushEventHandlerScope function
2698     if ($interface->extendedAttributes->{CustomPushEventHandlerScope}) {
2699         push(@headerContent, "    JSC::JSScope* pushEventHandlerScope(JSC::ExecState*, JSC::JSScope*) const;\n\n");
2700     }
2701     
2702     # Constructor object getter
2703     unless ($interface->extendedAttributes->{NoInterfaceObject}) {
2704         push(@headerContent, "    static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*);\n");
2705         push(@headerContent, "    static JSC::JSValue getNamedConstructor(JSC::VM&, JSC::JSGlobalObject*);\n") if $interface->extendedAttributes->{NamedConstructor};
2706     }
2707
2708     # Serializer function.
2709     if ($interface->serializable) {
2710         push(@headerContent, "    static JSC::JSObject* serialize(JSC::ExecState&, ${className}& thisObject, JSDOMGlobalObject&, JSC::ThrowScope&);\n");
2711     }
2712     
2713     my $numCustomOperations = 0;
2714     my $numCustomAttributes = 0;
2715
2716     my $hasForwardDeclaringOperations = 0;
2717     my $hasForwardDeclaringAttributes = 0;
2718
2719     my $hasDOMJITAttributes = 0;
2720
2721     # Attribute and function enums
2722     if ($numAttributes > 0) {
2723         foreach my $attribute (@{$interface->attributes}) {
2724             $numCustomAttributes++ if HasCustomGetter($attribute);
2725             $numCustomAttributes++ if HasCustomSetter($attribute);
2726             if ($attribute->extendedAttributes->{CachedAttribute}) {
2727                 my $conditionalString = $codeGenerator->GenerateConditionalString($attribute);
2728                 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2729                 push(@headerContent, "    mutable JSC::WriteBarrier<JSC::Unknown> m_" . $attribute->name . ";\n");
2730                 $numCachedAttributes++;
2731                 push(@headerContent, "#endif\n") if $conditionalString;
2732             }
2733             $hasDOMJITAttributes = 1 if $attribute->extendedAttributes->{DOMJIT};
2734
2735             $hasForwardDeclaringAttributes = 1 if $attribute->extendedAttributes->{ForwardDeclareInHeader};
2736         }
2737     }
2738
2739     # visit function
2740     if ($needsVisitChildren) {
2741         push(@headerContent, "    static void visitChildren(JSCell*, JSC::SlotVisitor&);\n");
2742         push(@headerContent, "    void visitAdditionalChildren(JSC::SlotVisitor&);\n") if $interface->extendedAttributes->{JSCustomMarkFunction};
2743         push(@headerContent, "\n");
2744
2745         if ($interface->extendedAttributes->{JSCustomMarkFunction}) {
2746             # We assume that the logic in visitAdditionalChildren is highly volatile, and during a
2747             # concurrent GC or in between eden GCs something may happen that would lead to this
2748             # logic behaving differently. Since this could mark objects or add opaque roots, this
2749             # means that after any increment of mutator resumption in a concurrent GC and at least
2750             # once during any eden GC we need to re-execute visitAdditionalChildren on any objects
2751             # that we had executed it on before. We do this using the DOM's own MarkingConstraint,
2752             # which will call visitOutputConstraints on all objects in the DOM's own
2753             # outputConstraintSubspace. visitOutputConstraints is the name JSC uses for the method
2754             # that the GC calls to ask an object is it would like to mark anything else after the
2755             # program resumed since the last call to visitChildren or visitOutputConstraints. Since
2756             # this just calls visitAdditionalChildren, you usually don't have to worry about this.
2757             push(@headerContent, "    static void visitOutputConstraints(JSCell*, JSC::SlotVisitor&);\n");
2758             my $subspaceFunc = IsDOMGlobalObject($interface) ? "globalObjectOutputConstraintSubspaceFor" : "outputConstraintSubspaceFor";
2759             push(@headerContent, "    template<typename> static JSC::CompleteSubspace* subspaceFor(JSC::VM& vm) { return $subspaceFunc(vm); }\n");
2760         }
2761     }
2762
2763     if (NeedsImplementationClass($interface)) {
2764         push(@headerContent, "    static void heapSnapshot(JSCell*, JSC::HeapSnapshotBuilder&);\n");
2765     }
2766     
2767     if ($numCustomAttributes > 0) {
2768         push(@headerContent, "\n    // Custom attributes\n");
2769
2770         foreach my $attribute (@{$interface->attributes}) {
2771             my $conditionalString = $codeGenerator->GenerateConditionalString($attribute);
2772             if (HasCustomGetter($attribute)) {
2773                 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2774                 my $methodName = $codeGenerator->WK_lcfirst($attribute->name);
2775                 push(@headerContent, "    JSC::JSValue " . $methodName . "(JSC::ExecState&) const;\n");
2776                 push(@headerContent, "#endif\n") if $conditionalString;
2777             }
2778             if (HasCustomSetter($attribute) && !IsReadonly($attribute)) {
2779                 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2780                 push(@headerContent, "    void set" . $codeGenerator->WK_ucfirst($attribute->name) . "(JSC::ExecState&, JSC::JSValue);\n");
2781                 push(@headerContent, "#endif\n") if $conditionalString;
2782             }
2783         }
2784     }
2785
2786     foreach my $operation (@{$interface->operations}) {
2787         $numCustomOperations++ if HasCustomMethod($operation);
2788         $hasForwardDeclaringOperations = 1 if $operation->extendedAttributes->{ForwardDeclareInHeader};
2789     }
2790
2791     if ($numCustomOperations > 0) {
2792         my $inAppleCopyright = 0;
2793         push(@headerContent, "\n    // Custom functions\n");
2794         foreach my $operation (@{$interface->operations}) {
2795             next unless HasCustomMethod($operation);
2796             next if $operation->{overloads} && $operation->{overloadIndex} != 1;
2797
2798             if ($operation->extendedAttributes->{AppleCopyright}) {
2799                 if (!$inAppleCopyright) {
2800                     push(@headerContent, $beginAppleCopyrightForHeaderFiles);
2801                     $inAppleCopyright = 1;
2802                 }
2803             } elsif ($inAppleCopyright) {
2804                 push(@headerContent, $endAppleCopyright);
2805                 $inAppleCopyright = 0;
2806             }
2807
2808             my $conditionalString = $codeGenerator->GenerateConditionalString($operation);
2809             push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2810
2811             my $functionImplementationName = $operation->extendedAttributes->{ImplementedAs} || $codeGenerator->WK_lcfirst($operation->name);
2812
2813             my @functionArguments = ();
2814             push(@functionArguments, "JSC::ExecState&");
2815             push(@functionArguments, "Ref<DeferredPromise>&&") if $codeGenerator->IsPromiseType($operation->type) && !$operation->extendedAttributes->{ReturnsOwnPromise};
2816
2817             push(@headerContent, "    " . ($operation->isStatic ? "static " : "") . "JSC::JSValue " . $functionImplementationName . "(" . join(", ", @functionArguments) . ");\n");
2818
2819             push(@headerContent, "#endif\n") if $conditionalString;
2820         }
2821         push(@headerContent, $endAppleCopyright) if $inAppleCopyright;
2822     }
2823
2824     if (NeedsImplementationClass($interface)) {
2825         if ($hasParent) {
2826             push(@headerContent, "    $interfaceName& wrapped() const\n");
2827             push(@headerContent, "    {\n");
2828             push(@headerContent, "        return static_cast<$interfaceName&>(Base::wrapped());\n");
2829             push(@headerContent, "    }\n");
2830         }
2831     }
2832
2833     # structure flags
2834     if (%structureFlags) {
2835         push(@headerContent, "public:\n");
2836         push(@headerContent, "    static const unsigned StructureFlags = ");
2837         foreach my $structureFlag (sort (keys %structureFlags)) {
2838             push(@headerContent, $structureFlag . " | ");
2839         }
2840         push(@headerContent, "Base::StructureFlags;\n");
2841     }
2842
2843     push(@headerContent, "protected:\n");
2844
2845     # Constructor
2846     if ($interfaceName eq "DOMWindow" || $interfaceName eq "RemoteDOMWindow") {
2847         push(@headerContent, "    $className(JSC::VM&, JSC::Structure*, Ref<$implType>&&, JSWindowProxy*);\n");
2848     } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope")) {
2849         push(@headerContent, "    $className(JSC::VM&, JSC::Structure*, Ref<$implType>&&);\n");
2850     } elsif (!NeedsImplementationClass($interface)) {
2851         push(@headerContent, "    $className(JSC::Structure*, JSDOMGlobalObject&);\n\n");
2852      } else {
2853         push(@headerContent, "    $className(JSC::Structure*, JSDOMGlobalObject&, Ref<$implType>&&);\n\n");
2854     }
2855
2856     if ($interfaceName eq "DOMWindow" || $interfaceName eq "RemoteDOMWindow") {
2857         push(@headerContent, "    void finishCreation(JSC::VM&, JSWindowProxy*);\n");
2858     } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope")) {
2859         push(@headerContent, "    void finishCreation(JSC::VM&, JSC::JSProxy*);\n");
2860     } else {
2861         push(@headerContent, "    void finishCreation(JSC::VM&);\n");
2862     }
2863
2864     push(@headerContent, "};\n\n");
2865
2866     if (ShouldGenerateWrapperOwnerCode($hasParent, $interface)) {
2867         if ($interfaceName ne "Node" && $codeGenerator->InheritsInterface($interface, "Node")) {
2868             $headerIncludes{"JSNode.h"} = 1;
2869             push(@headerContent, "class JS${interfaceName}Owner : public JSNodeOwner {\n");
2870         } else {
2871             push(@headerContent, "class JS${interfaceName}Owner : public JSC::WeakHandleOwner {\n");
2872         }
2873         $headerIncludes{"<wtf/NeverDestroyed.h>"} = 1;
2874         push(@headerContent, "public:\n");
2875         push(@headerContent, "    virtual bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor&, const char**);\n");
2876         push(@headerContent, "    virtual void finalize(JSC::Handle<JSC::Unknown>, void* context);\n");
2877         push(@headerContent, "};\n");
2878         push(@headerContent, "\n");
2879         push(@headerContent, "inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld&, $implType*)\n");
2880         push(@headerContent, "{\n");
2881         push(@headerContent, "    static NeverDestroyed<JS${interfaceName}Owner> owner;\n");
2882         push(@headerContent, "    return &owner.get();\n");
2883         push(@headerContent, "}\n");
2884         push(@headerContent, "\n");
2885         push(@headerContent, "inline void* wrapperKey($implType* wrappableObject)\n");
2886         push(@headerContent, "{\n");
2887         push(@headerContent, "    return wrappableObject;\n");
2888         push(@headerContent, "}\n");
2889         push(@headerContent, "\n");
2890     }
2891     if (ShouldGenerateToJSDeclaration($hasParent, $interface)) {
2892         # Node and NodeList have custom inline implementations which thus cannot be exported.
2893         # FIXME: The special case for Node and NodeList should probably be implemented via an IDL attribute.
2894         if ($implType eq "Node" or $implType eq "NodeList") {
2895             push(@headerContent, "JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, $implType&);\n");
2896         } else {
2897             push(@headerContent, $exportMacro."JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, $implType&);\n");
2898         }
2899         push(@headerContent, "inline JSC::JSValue toJS(JSC::ExecState* state, JSDOMGlobalObject* globalObject, $implType* impl) { return impl ? toJS(state, globalObject, *impl) : JSC::jsNull(); }\n");
2900
2901         push(@headerContent, "JSC::JSValue toJSNewlyCreated(JSC::ExecState*, JSDOMGlobalObject*, Ref<$implType>&&);\n");
2902         push(@headerContent, "inline JSC::JSValue toJSNewlyCreated(JSC::ExecState* state, JSDOMGlobalObject* globalObject, RefPtr<$implType>&& impl) { return impl ? toJSNewlyCreated(state, globalObject, impl.releaseNonNull()) : JSC::jsNull(); }\n");
2903    }
2904
2905     push(@headerContent, "\n");
2906
2907     GeneratePrototypeDeclaration(\@headerContent, $className, $interface) if HeaderNeedsPrototypeDeclaration($interface);
2908
2909     if ($hasForwardDeclaringOperations) {
2910         my $inAppleCopyright = 0;
2911         push(@headerContent,"// Functions\n\n");
2912         foreach my $operation (@{$interface->operations}) {
2913             next if $operation->{overloadIndex} && $operation->{overloadIndex} > 1;
2914             next unless $operation->extendedAttributes->{ForwardDeclareInHeader};
2915
2916             if ($operation->extendedAttributes->{AppleCopyright}) {
2917                 if (!$inAppleCopyright) {
2918                     push(@headerContent, $beginAppleCopyrightForHeaderFiles);
2919                     $inAppleCopyright = 1;
2920                 }
2921             } elsif ($inAppleCopyright) {
2922                 push(@headerContent, $endAppleCopyright);
2923                 $inAppleCopyright = 0;
2924             }
2925
2926             my $conditionalAttribute = GetConditionalForOperationConsideringOverloads($operation);
2927             my $conditionalString = $conditionalAttribute ? $codeGenerator->GenerateConditionalStringFromAttributeValue($conditionalAttribute) : undef;
2928             push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2929             my $functionName = GetFunctionName($interface, $className, $operation);
2930             push(@headerContent, "JSC::EncodedJSValue JSC_HOST_CALL ${functionName}(JSC::ExecState*);\n");
2931             push(@headerContent, "#endif\n") if $conditionalString;
2932         }
2933
2934         push(@headerContent, $endAppleCopyright) if $inAppleCopyright;
2935         push(@headerContent,"\n");
2936     }
2937
2938     if ($hasForwardDeclaringAttributes) {
2939         push(@headerContent,"// Attributes\n\n");
2940         foreach my $attribute (@{$interface->attributes}) {
2941             next unless $attribute->extendedAttributes->{ForwardDeclareInHeader};
2942
2943             my $conditionalString = $codeGenerator->GenerateConditionalString($attribute);
2944             push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2945             my $getter = GetAttributeGetterName($interface, $className, $attribute);
2946             push(@headerContent, "JSC::EncodedJSValue ${getter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::PropertyName);\n");
2947             if (!IsReadonly($attribute)) {
2948                 my $setter = GetAttributeSetterName($interface, $className, $attribute);
2949                 push(@headerContent, "bool ${setter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::EncodedJSValue);\n");
2950             }
2951             push(@headerContent, "#endif\n") if $conditionalString;
2952         }
2953     }
2954
2955     # CheckSubClass Snippet function.
2956     if ($interface->extendedAttributes->{DOMJIT}) {
2957         $headerIncludes{"<JavaScriptCore/Snippet.h>"} = 1;
2958         push(@headerContent, "#if ENABLE(JIT)\n");
2959         push(@headerContent, "Ref<JSC::Snippet> checkSubClassSnippetFor${className}();\n");
2960         push(@headerContent, "#endif\n");
2961     }
2962
2963     if ($hasDOMJITAttributes) {
2964         $headerIncludes{"<JavaScriptCore/DOMJITGetterSetter.h>"} = 1;
2965         push(@headerContent,"// DOM JIT Attributes\n\n");
2966         foreach my $attribute (@{$interface->attributes}) {
2967             next unless $attribute->extendedAttributes->{DOMJIT};
2968             assert("Only DOMJIT=Getter is supported for attributes") unless $codeGenerator->ExtendedAttributeContains($attribute->extendedAttributes->{DOMJIT}, "Getter");
2969
2970             my $interfaceName = $interface->type->name;
2971             my $className = $interfaceName . $codeGenerator->WK_ucfirst($attribute->name);
2972             my $domJITClassName = $className . "Attribute";
2973
2974             push(@headerContent, "#if ENABLE(JIT)\n");
2975             push(@headerContent, "Ref<JSC::DOMJIT::CallDOMGetterSnippet> compile${domJITClassName}();\n");
2976             push(@headerContent, "#endif\n\n");
2977         }
2978     }
2979
2980     if (HasCustomConstructor($interface)) {
2981         push(@headerContent, "// Custom constructor\n");
2982         push(@headerContent, "JSC::EncodedJSValue JSC_HOST_CALL construct${className}(JSC::ExecState&);\n\n");
2983     }
2984
2985     if (NeedsImplementationClass($interface)) {
2986         my $toWrappedType = $interface->type->name eq "XPathNSResolver" ? "RefPtr<${implType}>" : "${implType}*";
2987         $headerIncludes{"JSDOMWrapper.h"} = 1;
2988
2989         push(@headerContent, "template<> struct JSDOMWrapperConverterTraits<${implType}> {\n");
2990         push(@headerContent, "    using WrapperClass = ${className};\n");
2991         push(@headerContent, "    using ToWrappedReturnType = ${toWrappedType};\n");
2992         push(@headerContent, "};\n");
2993     }
2994
2995     push(@headerContent, GenerateEnumerationsHeaderContent($interface, $enumerations));
2996     push(@headerContent, GenerateDictionariesHeaderContent($interface, $dictionaries));
2997
2998     my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
2999     push(@headerContent, "\n} // namespace WebCore\n");
3000     push(@headerContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
3001
3002     if ($interface->extendedAttributes->{AppleCopyright}) {
3003         push(@headerContent, "\n");
3004         push(@headerContent, split("\r", $endAppleCopyright));
3005     }
3006
3007     # - Generate dependencies.
3008     if ($writeDependencies) {
3009         my @ancestors;
3010         $codeGenerator->ForAllParents($interface, sub {
3011             my $currentInterface = shift;
3012             push(@ancestors, $currentInterface->type->name);
3013         }, 0);
3014         for my $dictionary (@$dictionaries) {
3015             my $parentType = $dictionary->parentType;
3016             while (defined($parentType)) {
3017                 push(@ancestors, $parentType->name) if $codeGenerator->IsExternalDictionaryType($parentType);
3018                 my $parentDictionary = $codeGenerator->GetDictionaryByType($parentType);
3019                 assert("Unable to find definition for dictionary named '" . $parentType->name . "'!") unless defined($parentDictionary);
3020                 $parentType = $parentDictionary->parentType;
3021             }
3022         }
3023         push(@depsContent, "$className.h : ", join(" ", map { "$_.idl" } @ancestors), "\n");
3024         push(@depsContent, map { "$_.idl :\n" } @ancestors);
3025     }
3026 }
3027
3028 sub GeneratePropertiesHashTable
3029 {
3030     my ($object, $interface, $isInstance, $hashKeys, $hashSpecials, $hashValue1, $hashValue2, $conditionals, $readWriteConditionals, $runtimeEnabledOperations, $runtimeEnabledAttributes) = @_;
3031
3032     # FIXME: These should be functions on $interface.
3033     my $interfaceName = $interface->type->name;
3034     my $className = "JS$interfaceName";
3035     
3036     # - Add all properties in a hashtable definition
3037     my $propertyCount = $isInstance ? InstancePropertyCount($interface) : PrototypePropertyCount($interface);
3038
3039     if (!$isInstance && NeedsConstructorProperty($interface)) {
3040         die if !$propertyCount;
3041         push(@$hashKeys, "constructor");
3042         my $getter = "js" . $interfaceName . "Constructor";
3043         push(@$hashValue1, $getter);
3044
3045         my $setter = "setJS" . $interfaceName . "Constructor";
3046         push(@$hashValue2, $setter);
3047         push(@$hashSpecials, "static_cast<unsigned>(JSC::PropertyAttribute::DontEnum)");
3048     }
3049
3050     return 0 if !$propertyCount;
3051
3052     my @attributes = @{$interface->attributes};
3053     push(@attributes, @{$interface->mapLike->attributes}) if $interface->mapLike;
3054
3055     foreach my $attribute (@attributes) {
3056         next if ($attribute->isStatic);
3057         next if AttributeShouldBeOnInstance($interface, $attribute) != $isInstance;
3058
3059         # Global objects add RuntimeEnabled attributes after creation so do not add them to the static table.
3060         if ($isInstance && NeedsRuntimeCheck($interface, $attribute)) {
3061             $propertyCount -= 1;
3062             next;
3063         }
3064
3065         my $name = $attribute->name;
3066         push(@$hashKeys, $name);
3067
3068         my $special = GetJSCAttributesForAttribute($interface, $attribute);
3069         push(@$hashSpecials, $special);
3070
3071         if ($attribute->extendedAttributes->{DOMJIT}) {
3072             push(@$hashValue1, "&DOMJITAttributeFor" . $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name));
3073         } else {
3074             my $getter = GetAttributeGetterName($interface, $className, $attribute);
3075             push(@$hashValue1, $getter);
3076         }
3077
3078         if (IsReadonly($attribute)) {
3079             push(@$hashValue2, "0");
3080         } else {
3081             my $setter = GetAttributeSetterName($interface, $className, $attribute);
3082             push(@$hashValue2, $setter);
3083         }
3084
3085         my $conditional = $attribute->extendedAttributes->{Conditional};
3086         $conditionals->{$name} = $conditional if $conditional;
3087         my $readWriteConditional = $attribute->extendedAttributes->{ConditionallyReadWrite};
3088         $readWriteConditionals->{$name} = $readWriteConditional if $readWriteConditional;
3089
3090         if (NeedsRuntimeCheck($interface, $attribute)) {
3091             push(@$runtimeEnabledAttributes, $attribute);
3092         }
3093     }
3094
3095     my @operations = @{$interface->operations};
3096     push(@operations, @{$interface->iterable->operations}) if IsKeyValueIterableInterface($interface);
3097     push(@operations, @{$interface->mapLike->operations}) if $interface->mapLike;
3098     push(@operations, @{$interface->serializable->operations}) if $interface->serializable;
3099     foreach my $operation (@operations) {
3100         next if ($operation->extendedAttributes->{PrivateIdentifier} and not $operation->extendedAttributes->{PublicIdentifier});
3101         next if ($operation->isStatic);
3102         next if $operation->{overloadIndex} && $operation->{overloadIndex} > 1;
3103         next if OperationShouldBeOnInstance($interface, $operation) != $isInstance;
3104         next if $operation->name eq "[Symbol.Iterator]";
3105
3106         # Global objects add RuntimeEnabled operations after creation so do not add them to the static table.
3107         if ($isInstance && NeedsRuntimeCheck($interface, $operation)) {
3108             $propertyCount -= 1;
3109             next;
3110         }
3111
3112         my $name = $operation->name;
3113         push(@$hashKeys, $name);
3114
3115         my $functionName = GetFunctionName($interface, $className, $operation);
3116         push(@$hashValue1, $functionName);
3117
3118         my $functionLength = GetFunctionLength($operation);
3119
3120         if ($operation->extendedAttributes->{DOMJIT}) {
3121             push(@$hashValue2, "&DOMJITSignatureFor" . $interface->type->name . $codeGenerator->WK_ucfirst($operation->name));
3122         } else {
3123             push(@$hashValue2, $functionLength);
3124         }
3125
3126         push(@$hashSpecials, ComputeFunctionSpecial($interface, $operation));
3127
3128         my $conditional = GetConditionalForOperationConsideringOverloads($operation);
3129         $conditionals->{$name} = $conditional if $conditional;
3130
3131         if (NeedsRuntimeCheck($interface, $operation)) {
3132             push(@$runtimeEnabledOperations, $operation);
3133         }
3134     }
3135
3136     return $propertyCount;
3137 }
3138
3139 # This computes an effective overload set for a given operation / constructor,
3140 # which represents the allowable invocations.This set is used as input for
3141 # the Web IDL overload resolution algorithm.
3142 # http://heycam.github.io/webidl/#dfn-effective-overload-set
3143 sub ComputeEffectiveOverloadSet
3144 {
3145     my ($overloads) = @_;
3146
3147     my %allSets;
3148     my $addTuple = sub {
3149         my $tuple = shift;
3150         # The Web IDL specification uses a flat set of tuples but we use a hash where the key is the
3151         # number of parameters and the value is the set of tuples for the given number of parameters.
3152         my $length = scalar(@{@$tuple[1]});
3153         if (!exists($allSets{$length})) {
3154             $allSets{$length} = [ $tuple ];