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