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