Rename AtomicString to AtomString
[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-2019 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, "propertyNameToAtomString(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->{EnabledForContext}
1709         || $context->extendedAttributes->{EnabledForWorld}
1710         || $context->extendedAttributes->{EnabledBySetting}
1711         || $context->extendedAttributes->{DisabledByQuirk}
1712         || $context->extendedAttributes->{SecureContext}
1713         || $context->extendedAttributes->{ContextHasServiceWorkerScheme}
1714         || $context->extendedAttributes->{CustomEnabled};
1715 }
1716
1717 # https://heycam.github.io/webidl/#es-operations
1718 sub OperationShouldBeOnInstance
1719 {
1720     my ($interface, $operation) = @_;
1721
1722     return 1 if IsGlobalOrPrimaryGlobalInterface($interface);
1723
1724     # [Unforgeable] operations should be on the instance. https://heycam.github.io/webidl/#Unforgeable
1725     if (IsUnforgeable($interface, $operation)) {
1726         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);
1727         return 1;
1728     }
1729
1730     return 0;
1731 }
1732
1733 sub OperationHasForcedReturnValue
1734 {
1735     my ($operation) = @_;
1736
1737     foreach my $argument (@{$operation->arguments}) {
1738         return 1 if $argument->extendedAttributes->{ReturnValue};
1739     }
1740     return 0;
1741 }
1742
1743 sub IsAcceleratedDOMAttribute
1744 {
1745     my ($interface, $attribute) = @_;
1746
1747     # If we use CustomGetterSetter in IDL code generator we cannot skip type check.
1748     return 0 if NeedsRuntimeCheck($interface, $attribute) and AttributeShouldBeOnInstance($interface, $attribute);
1749     return 0 if $attribute->extendedAttributes->{PrivateIdentifier} and AttributeShouldBeOnInstance($interface, $attribute);
1750
1751     # If the interface has special logic for casting we cannot hoist type check to JSC.
1752     return 0 if $interface->extendedAttributes->{ImplicitThis};
1753     return 0 if $interface->extendedAttributes->{CustomProxyToJSObject};
1754
1755     return 0 if $attribute->isStatic;
1756     return 0 if $attribute->isMapLike;
1757     return 0 if $codeGenerator->IsConstructorType($attribute->type);
1758     return 0 if IsJSBuiltin($interface, $attribute);
1759     return 0 if $attribute->extendedAttributes->{LenientThis};
1760     return 0 if $codeGenerator->IsPromiseType($attribute->type);
1761     return 0 if $attribute->extendedAttributes->{DOMJIT};
1762     return 1;
1763 }
1764
1765 sub GetJSCAttributesForAttribute
1766 {
1767     my $interface = shift;
1768     my $attribute = shift;
1769
1770     my @specials = ();
1771     push(@specials, "JSC::PropertyAttribute::DontDelete") if IsUnforgeable($interface, $attribute);
1772
1773     # As per Web IDL specification, constructor properties on the ECMAScript global object should not be enumerable.
1774     my $isGlobalConstructor = $codeGenerator->IsConstructorType($attribute->type);
1775     push(@specials, "JSC::PropertyAttribute::DontEnum") if ($attribute->extendedAttributes->{NotEnumerable} || $isGlobalConstructor);
1776     push(@specials, "JSC::PropertyAttribute::ReadOnly") if IsReadonly($attribute);
1777     push(@specials, "JSC::PropertyAttribute::CustomAccessor") unless $isGlobalConstructor or IsJSBuiltin($interface, $attribute);
1778     push(@specials, "JSC::PropertyAttribute::DOMAttribute") if IsAcceleratedDOMAttribute($interface, $attribute);
1779     push(@specials, "JSC::PropertyAttribute::DOMJITAttribute") if $attribute->extendedAttributes->{DOMJIT};
1780     push(@specials, "JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin") if  IsJSBuiltin($interface, $attribute);
1781     return "static_cast<unsigned>(" . ((@specials > 0) ? join(" | ", @specials) : "0") . ")";
1782 }
1783
1784 sub GetIndexedGetterOperation
1785 {
1786     my $interface = shift;
1787     return GetSpecialAccessorOperationForType($interface, "getter", "unsigned long", 1);
1788 }
1789
1790 sub GetIndexedSetterOperation
1791 {
1792     my $interface = shift;
1793     return GetSpecialAccessorOperationForType($interface, "setter", "unsigned long", 2);
1794 }
1795
1796 sub GetNamedGetterOperation
1797 {
1798     my $interface = shift;
1799     return GetSpecialAccessorOperationForType($interface, "getter", "DOMString", 1);
1800 }
1801
1802 sub GetNamedSetterOperation
1803 {
1804     my $interface = shift;
1805     return GetSpecialAccessorOperationForType($interface, "setter", "DOMString", 2);
1806 }
1807
1808 sub GetNamedDeleterOperation
1809 {
1810     my $interface = shift;
1811     return GetSpecialAccessorOperationForType($interface, "deleter", "DOMString", 1);
1812 }
1813
1814 sub InstanceOperationCount
1815 {
1816     my $interface = shift;
1817     my $count = 0;
1818
1819     foreach my $operation (@{$interface->operations}) {
1820         $count++ if OperationShouldBeOnInstance($interface, $operation);
1821     }
1822
1823     return $count;
1824 }
1825
1826 sub PrototypeOperationCount
1827 {
1828     my $interface = shift;
1829     my $count = 0;
1830
1831     foreach my $operation (@{$interface->operations}) {
1832         $count++ if !$operation->isStatic && !OperationShouldBeOnInstance($interface, $operation);
1833     }
1834
1835     $count += scalar @{$interface->iterable->operations} if $interface->iterable;
1836     $count += scalar @{$interface->mapLike->operations} if $interface->mapLike;
1837     $count += scalar @{$interface->serializable->operations} if $interface->serializable;
1838
1839     return $count;
1840 }
1841
1842 sub InstancePropertyCount
1843 {
1844     my $interface = shift;
1845     my $count = 0;
1846     foreach my $attribute (@{$interface->attributes}) {
1847         $count++ if AttributeShouldBeOnInstance($interface, $attribute);
1848     }
1849     $count += InstanceOperationCount($interface);
1850     return $count;
1851 }
1852
1853 sub PrototypePropertyCount
1854 {
1855     my $interface = shift;
1856     my $count = 0;
1857     foreach my $attribute (@{$interface->attributes}) {
1858         $count++ if !AttributeShouldBeOnInstance($interface, $attribute);
1859     }
1860     $count += PrototypeOperationCount($interface);
1861     $count++ if NeedsConstructorProperty($interface);
1862     return $count;
1863 }
1864
1865 sub InstanceOverridesGetOwnPropertySlot
1866 {
1867     my $interface = shift;
1868     return $interface->extendedAttributes->{CustomGetOwnPropertySlot}
1869         || $interface->extendedAttributes->{Plugin}
1870         || GetIndexedGetterOperation($interface)
1871         || GetNamedGetterOperation($interface);
1872 }
1873
1874 sub InstanceOverridesGetOwnPropertyNames
1875 {
1876     my $interface = shift;
1877     return $interface->extendedAttributes->{CustomGetOwnPropertyNames}
1878         || GetIndexedGetterOperation($interface)
1879         || GetNamedGetterOperation($interface);
1880 }
1881
1882 sub InstanceOverridesPut
1883 {
1884     my $interface = shift;
1885     return $interface->extendedAttributes->{CustomPut}
1886         || $interface->extendedAttributes->{Plugin}
1887         || GetIndexedSetterOperation($interface)
1888         || GetNamedSetterOperation($interface);
1889 }
1890
1891 sub InstanceOverridesDefineOwnProperty
1892 {
1893     my $interface = shift;
1894
1895     return 0 if $interface->extendedAttributes->{DefaultDefineOwnProperty};
1896
1897     return $interface->extendedAttributes->{CustomDefineOwnProperty}
1898         || GetIndexedSetterOperation($interface)
1899         || GetNamedSetterOperation($interface);
1900 }
1901
1902 sub InstanceOverridesDeleteProperty
1903 {
1904     my $interface = shift;
1905     return $interface->extendedAttributes->{CustomDeleteProperty}
1906         || GetNamedDeleterOperation($interface);
1907 }
1908
1909 sub PrototypeHasStaticPropertyTable
1910 {
1911     my $interface = shift;
1912     my $numConstants = @{$interface->constants};
1913     return $numConstants > 0 || PrototypePropertyCount($interface) > 0;
1914 }
1915
1916 sub InstanceNeedsVisitChildren
1917 {
1918     my $interface = shift;
1919     
1920     foreach my $attribute (@{$interface->attributes}) {
1921         return 1 if $attribute->extendedAttributes->{CachedAttribute};
1922     }
1923
1924     return 1 if $interface->extendedAttributes->{JSCustomMarkFunction};
1925     return 1 if $interface->extendedAttributes->{ReportExtraMemoryCost};
1926     return 0;
1927 }
1928
1929 sub InstanceNeedsEstimatedSize
1930 {
1931     my $interface = shift;
1932     return $interface->extendedAttributes->{ReportExtraMemoryCost};
1933 }
1934
1935 sub GetImplClassName
1936 {
1937     my $interface = shift;
1938
1939     return $interface->type->name;
1940 }
1941
1942 sub IsClassNameWordBoundary
1943 {
1944     my ($name, $i) = @_;
1945
1946     # Interpret negative numbers as distance from end of string, just as the substr function does.
1947     $i += length($name) if $i < 0;
1948
1949     return 0 if $i < 0;
1950     return 1 if $i == 0;
1951     return 1 if $i == length($name);
1952     return 0 if $i > length($name);
1953
1954     my $checkString = substr($name, $i - 1);
1955     return $checkString =~ /^[^A-Z][A-Z]/ || $checkString =~ /^[A-Z][A-Z][^A-Z]/;
1956 }
1957
1958 sub IsPrefixRemovable
1959 {
1960     my ($class, $name, $i) = @_;
1961
1962     return IsClassNameWordBoundary($name, $i)
1963         && (IsClassNameWordBoundary($class, $i) && substr($class, 0, $i) eq substr($name, 0, $i)
1964             || IsClassNameWordBoundary($class, -$i) && substr($class, -$i) eq substr($name, 0, $i));
1965 }
1966
1967 sub GetNestedClassName
1968 {
1969     my ($interface, $name) = @_;
1970
1971     my $class = GetImplClassName($interface);
1972     my $member = $codeGenerator->WK_ucfirst($name);
1973
1974     # Since the enumeration name will be nested in the class name's namespace, remove any words
1975     # that happen to match the start or end of the class name. If an enumeration is named TrackType or
1976     # TextTrackType, and the class is named TextTrack, then we will get a name like TextTrack::Type.
1977     my $memberLength = length($member);
1978     my $longestPrefixLength = 0;
1979     if ($member =~ /^[A-Z]./) {
1980         for (my $i = 2; $i < $memberLength - 1; $i++) {
1981             $longestPrefixLength = $i if IsPrefixRemovable($class, $member, $i);
1982         }
1983     }
1984     $member = substr($member, $longestPrefixLength);
1985
1986     return "${class}::$member";
1987 }
1988
1989 sub GetEnumerationClassName
1990 {
1991     my ($type, $interface) = @_;
1992
1993     assert("Not a type") if ref($type) ne "IDLType";
1994
1995     if ($codeGenerator->HasEnumImplementationNameOverride($type)) {
1996         return $codeGenerator->GetEnumImplementationNameOverride($type);
1997     }
1998
1999     my $name = $type->name;
2000
2001     return $name if $codeGenerator->IsExternalEnumType($type);
2002     return $name unless defined($interface);
2003
2004     return GetNestedClassName($interface, $name);
2005 }
2006
2007 sub GetEnumerationValueName
2008 {
2009     my ($name) = @_;
2010
2011     return "EmptyString" if $name eq "";
2012     $name = join("", map { $codeGenerator->WK_ucfirst($_) } split("-", $name));
2013     $name = "_$name" if $name =~ /^\d/;
2014     return $name;
2015 }
2016
2017 sub GenerateEnumerationHeader
2018 {
2019     my ($object, $enumeration, $className) = @_;
2020  
2021     # - Add default header template and header protection.
2022     push(@headerContentHeader, GenerateHeaderContentHeader($enumeration));
2023
2024     $headerIncludes{"${className}.h"} = 1;
2025
2026     push(@headerContent, "\nnamespace WebCore {\n\n");
2027     push(@headerContent, GenerateEnumerationHeaderContent($enumeration, $className));
2028     push(@headerContent, "} // namespace WebCore\n");
2029      
2030     my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2031     push(@headerContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
2032 }
2033  
2034 sub GenerateEnumerationImplementation
2035 {
2036     my ($object, $enumeration, $className) = @_;
2037  
2038     # - Add default header template
2039     push(@implContentHeader, GenerateImplementationContentHeader($enumeration));
2040
2041     push(@implContent, "\n\nnamespace WebCore {\n");
2042     push(@implContent, "using namespace JSC;\n\n");
2043     push(@implContent, GenerateEnumerationImplementationContent($enumeration, $className));
2044     push(@implContent, "} // namespace WebCore\n");
2045      
2046     my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2047     push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
2048 }
2049
2050 sub GenerateEnumerationImplementationContent
2051 {
2052     my ($enumeration, $className, $interface, $conditionalString) = @_;
2053
2054     # FIXME: A little ugly to have this be a side effect instead of a return value.
2055     AddToImplIncludes("<JavaScriptCore/JSString.h>");
2056     AddToImplIncludes("<JavaScriptCore/JSCInlines.h>");
2057     AddToImplIncludes("JSDOMConvertEnumeration.h");
2058
2059     my $result = "";
2060     $result .= "#if ${conditionalString}\n\n" if $conditionalString;
2061
2062
2063     $result .= "String convertEnumerationToString($className enumerationValue)\n";
2064     $result .= "{\n";
2065     AddToImplIncludes("<wtf/NeverDestroyed.h>");
2066     $result .= "    static const NeverDestroyed<String> values[] = {\n";
2067     foreach my $value (@{$enumeration->values}) {
2068         if ($value eq "") {
2069             $result .= "        emptyString(),\n";
2070         } else {
2071             $result .= "        MAKE_STATIC_STRING_IMPL(\"$value\"),\n";
2072         }
2073     }
2074     $result .= "    };\n";
2075     my $index = 0;
2076     foreach my $value (@{$enumeration->values}) {
2077         my $enumerationValueName = GetEnumerationValueName($value);
2078         $result .= "    static_assert(static_cast<size_t>(${className}::$enumerationValueName) == $index, \"${className}::$enumerationValueName is not $index as expected\");\n";
2079         $index++;
2080     }
2081     $result .= "    ASSERT(static_cast<size_t>(enumerationValue) < WTF_ARRAY_LENGTH(values));\n";
2082     $result .= "    return values[static_cast<size_t>(enumerationValue)];\n";
2083     $result .= "}\n\n";
2084
2085
2086     # FIXME: Change to take VM& instead of ExecState*.
2087     $result .= "template<> JSString* convertEnumerationToJS(ExecState& state, $className enumerationValue)\n";
2088     $result .= "{\n";
2089     $result .= "    return jsStringWithCache(&state, convertEnumerationToString(enumerationValue));\n";
2090     $result .= "}\n\n";
2091
2092     # FIXME: Change to take VM& instead of ExecState&.
2093     # FIXME: Consider using toStringOrNull to make exception checking faster.
2094     # FIXME: Consider finding a more efficient way to match against all the strings quickly.
2095     $result .= "template<> Optional<$className> parseEnumeration<$className>(ExecState& state, JSValue value)\n";
2096     $result .= "{\n";
2097     $result .= "    auto stringValue = value.toWTFString(&state);\n";
2098     foreach my $value (@{$enumeration->values}) {
2099         my $enumerationValueName = GetEnumerationValueName($value);
2100         if ($value eq "") {
2101             $result .= "    if (stringValue.isEmpty())\n";
2102         } else {
2103             $result .= "    if (stringValue == \"$value\")\n";
2104         }
2105         $result .= "        return ${className}::${enumerationValueName};\n";
2106     }
2107     $result .= "    return WTF::nullopt;\n";
2108     $result .= "}\n\n";
2109
2110     $result .= "template<> const char* expectedEnumerationValues<$className>()\n";
2111     $result .= "{\n";
2112     $result .= "    return \"\\\"" . join ("\\\", \\\"", @{$enumeration->values}) . "\\\"\";\n";
2113     $result .= "}\n\n";
2114
2115     $result .= "#endif\n\n" if $conditionalString;
2116
2117     return $result;
2118 }
2119
2120 sub GenerateEnumerationsImplementationContent
2121 {
2122     my ($interface, $enumerations) = @_;
2123
2124     return "" unless @$enumerations;
2125
2126     my $result = "";
2127     foreach my $enumeration (@$enumerations) {
2128         my $className = GetEnumerationClassName($enumeration->type, $interface);
2129         my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2130         $result .= GenerateEnumerationImplementationContent($enumeration, $className, $interface, $conditionalString);
2131     }
2132     return $result;
2133 }
2134
2135 sub GenerateEnumerationHeaderContent
2136 {
2137     my ($enumeration, $className, $conditionalString) = @_;
2138
2139     $headerIncludes{"JSDOMConvertEnumeration.h"} = 1;
2140
2141     my $result = "";
2142     $result .= "#if ${conditionalString}\n\n" if $conditionalString;
2143
2144     my $exportMacro = GetExportMacroForJSClass($enumeration);
2145
2146     $result .= "${exportMacro}String convertEnumerationToString($className);\n";
2147     $result .= "template<> ${exportMacro}JSC::JSString* convertEnumerationToJS(JSC::ExecState&, $className);\n\n";
2148     $result .= "template<> ${exportMacro}Optional<$className> parseEnumeration<$className>(JSC::ExecState&, JSC::JSValue);\n";
2149     $result .= "template<> ${exportMacro}const char* expectedEnumerationValues<$className>();\n\n";
2150     $result .= "#endif\n\n" if $conditionalString;
2151     
2152     return $result;
2153 }
2154
2155 sub GenerateEnumerationsHeaderContent
2156 {
2157     my ($interface, $enumerations) = @_;
2158
2159     return "" unless @$enumerations;
2160
2161     # FIXME: Could optimize this to only generate the parts of each enumeration that are actually
2162     # used, which would require iterating over everything in the interface.
2163
2164     my $result = "";
2165     foreach my $enumeration (@$enumerations) {
2166         my $className = GetEnumerationClassName($enumeration->type, $interface);
2167         my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2168         $result .= GenerateEnumerationHeaderContent($enumeration, $className, $conditionalString);
2169     }
2170     return $result;
2171 }
2172
2173 sub GetDictionaryClassName
2174 {
2175     my ($type, $interface) = @_;
2176
2177     if ($codeGenerator->HasDictionaryImplementationNameOverride($type)) {
2178         return $codeGenerator->GetDictionaryImplementationNameOverride($type);
2179     }
2180
2181     my $name = $type->name;
2182     return $name if $codeGenerator->IsExternalDictionaryType($type);
2183     return $name unless defined($interface);
2184     return GetNestedClassName($interface, $name);
2185 }
2186
2187 sub GenerateDefaultValue
2188 {
2189     my ($typeScope, $context, $type, $defaultValue) = @_;
2190
2191     if ($codeGenerator->IsStringType($type)) {
2192         my $useAtomString = $type->extendedAttributes->{AtomString};
2193         if ($defaultValue eq "null") {
2194             return $useAtomString ? "nullAtom()" : "String()";
2195         } elsif ($defaultValue eq "\"\"") {
2196             return $useAtomString ? "emptyAtom()" : "emptyString()";
2197         } else {
2198             return $useAtomString ? "AtomString(${defaultValue}, AtomString::ConstructFromLiteral)" : "${defaultValue}_s";
2199         }
2200     }
2201
2202     if ($codeGenerator->IsEnumType($type)) {
2203         # FIXME: Would be nice to report an error if the value does not have quote marks around it.
2204         # FIXME: Would be nice to report an error if the value is not one of the enumeration values.
2205         if ($defaultValue eq "null") {
2206             die if !$type->isNullable;
2207             return "WTF::nullopt";
2208         }
2209         my $className = GetEnumerationClassName($type, $typeScope);
2210         my $enumerationValueName = GetEnumerationValueName(substr($defaultValue, 1, -1));
2211         return $className . "::" . $enumerationValueName;
2212     }
2213     if ($defaultValue eq "null") {
2214         if ($type->isUnion) {
2215             return "WTF::nullopt" if $type->isNullable;
2216
2217             my $IDLType = GetIDLType($typeScope, $type);
2218             return "convert<${IDLType}>(state, jsNull());";
2219         }
2220
2221         return "jsNull()" if $type->name eq "any";
2222         return "nullptr" if $codeGenerator->IsWrapperType($type) || $codeGenerator->IsBufferSourceType($type);
2223         return "String()" if $codeGenerator->IsStringType($type);
2224         return "WTF::nullopt";
2225     }
2226
2227     if ($defaultValue eq "[]") {
2228         my $IDLType = GetIDLType($typeScope, $type);
2229         return "Converter<${IDLType}>::ReturnType{ }";
2230     }
2231
2232     return "jsUndefined()" if $defaultValue eq "undefined";
2233     return "PNaN" if $defaultValue eq "NaN";
2234
2235     return $defaultValue;
2236 }
2237
2238 sub GenerateDictionaryHeaderContent
2239 {
2240     my ($dictionary, $className, $conditionalString) = @_;
2241
2242     $headerIncludes{"JSDOMConvertDictionary.h"} = 1;
2243
2244     my $exportMacro = GetExportMacroForJSClass($dictionary);
2245
2246     my $result = "";
2247     $result .= "#if ${conditionalString}\n\n" if $conditionalString;
2248     $result .= "template<> ${exportMacro}${className} convertDictionary<${className}>(JSC::ExecState&, JSC::JSValue);\n\n";
2249
2250     if ($dictionary->extendedAttributes->{JSGenerateToJSObject}) {
2251         $result .= "${exportMacro}JSC::JSObject* convertDictionaryToJS(JSC::ExecState&, JSDOMGlobalObject&, const ${className}&);\n\n";
2252     }
2253
2254     $result .= "#endif\n\n" if $conditionalString;
2255     return $result;
2256 }
2257
2258 sub GenerateDictionariesHeaderContent
2259 {
2260     my ($typeScope, $allDictionaries) = @_;
2261
2262     return "" unless @$allDictionaries;
2263
2264     my $result = "";
2265     foreach my $dictionary (@$allDictionaries) {
2266         $headerIncludes{$typeScope->type->name . ".h"} = 1 if $typeScope;
2267         my $className = GetDictionaryClassName($dictionary->type, $typeScope);
2268         my $conditionalString = $codeGenerator->GenerateConditionalString($dictionary);
2269         $result .= GenerateDictionaryHeaderContent($dictionary, $className, $conditionalString);
2270     }
2271     return $result;
2272 }
2273
2274 sub GenerateDictionaryImplementationContent
2275 {
2276     my ($dictionary, $className, $interface) = @_;
2277
2278     my $result = "";
2279
2280     my $name = $dictionary->type->name;
2281     my $typeScope = $interface || $dictionary;
2282
2283     my $conditional = $dictionary->extendedAttributes->{Conditional};
2284     if ($conditional) {
2285         my $conditionalString = $codeGenerator->GenerateConditionalStringFromAttributeValue($conditional);
2286         $result .= "#if ${conditionalString}\n\n";
2287     }
2288
2289     # FIXME: A little ugly to have this be a side effect instead of a return value.
2290     AddToImplIncludes("<JavaScriptCore/JSCInlines.h>");
2291     AddToImplIncludes("JSDOMConvertDictionary.h");
2292
2293     # https://heycam.github.io/webidl/#es-dictionary
2294     $result .= "template<> $className convertDictionary<$className>(ExecState& state, JSValue value)\n";
2295     $result .= "{\n";
2296     $result .= "    VM& vm = state.vm();\n";
2297     $result .= "    auto throwScope = DECLARE_THROW_SCOPE(vm);\n";
2298     $result .= "    bool isNullOrUndefined = value.isUndefinedOrNull();\n";
2299     $result .= "    auto* object = isNullOrUndefined ? nullptr : value.getObject();\n";
2300
2301     # 1. If Type(V) is not Undefined, Null or Object, then throw a TypeError.
2302     $result .= "    if (UNLIKELY(!isNullOrUndefined && !object)) {\n";
2303     $result .= "        throwTypeError(&state, throwScope);\n";
2304     $result .= "        return { };\n";
2305     $result .= "    }\n";
2306
2307     # 2. Let dict be an empty dictionary value of type D; every dictionary member is initially considered to be not present.
2308
2309     # 3. Let dictionaries be a list consisting of D and all of D’s inherited dictionaries, in order from least to most derived.
2310     my @dictionaries;
2311     push(@dictionaries, $dictionary);
2312     my $parentType = $dictionary->parentType;
2313     while (defined($parentType)) {
2314         my $parentDictionary = $codeGenerator->GetDictionaryByType($parentType);
2315         assert("Unable to find definition for dictionary named '" . $parentType->name . "'!") unless defined($parentDictionary);
2316         unshift(@dictionaries, $parentDictionary);
2317         $parentType = $parentDictionary->parentType;
2318     }
2319
2320     my $arguments = "";
2321     my $comma = "";
2322
2323     $result .= "    $className result;\n";
2324
2325     # 4. For each dictionary dictionary in dictionaries, in order:
2326     foreach my $dictionary (@dictionaries) {
2327         # For each dictionary member member declared on dictionary, in lexicographical order:
2328         my @sortedMembers = sort { $a->name cmp $b->name } @{$dictionary->members};
2329         foreach my $member (@sortedMembers) {
2330             $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.
2331
2332             my $type = $member->type;
2333             AddToImplIncludesForIDLType($type);
2334
2335             # 4.1. Let key be the identifier of member.
2336             my $key = $member->name;
2337             my $implementedAsKey = $member->extendedAttributes->{ImplementedAs} || $key;
2338
2339             # 4.2. Let value be an ECMAScript value, depending on Type(V):
2340             $result .= "    JSValue ${key}Value;\n";
2341             $result .= "    if (isNullOrUndefined)\n";
2342             $result .= "        ${key}Value = jsUndefined();\n";
2343             $result .= "    else {\n";
2344             $result .= "        ${key}Value = object->get(&state, Identifier::fromString(&state, \"${key}\"));\n";
2345             $result .= "        RETURN_IF_EXCEPTION(throwScope, { });\n";
2346             $result .= "    }\n";
2347
2348             my $IDLType = GetIDLType($typeScope, $type);
2349
2350             # 4.3. If value is not undefined, then:
2351             $result .= "    if (!${key}Value.isUndefined()) {\n";
2352
2353             my $nativeValue = JSValueToNative($typeScope, $member, "${key}Value", $member->extendedAttributes->{Conditional}, "&state", "state", "", "*jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject())");
2354             $result .= "        result.$implementedAsKey = $nativeValue;\n";
2355             $result .= "        RETURN_IF_EXCEPTION(throwScope, { });\n";
2356
2357             # Value is undefined.
2358             # 4.4. Otherwise, if value is undefined but the dictionary member has a default value, then:
2359             if (!$member->isRequired && defined $member->default) {
2360                 $result .= "    } else\n";
2361                 $result .= "        result.$implementedAsKey = " . GenerateDefaultValue($typeScope, $member, $member->type, $member->default) . ";\n";
2362             } elsif ($member->isRequired) {
2363                 # 4.5. Otherwise, if value is undefined and the dictionary member is a required dictionary member, then throw a TypeError.
2364                 $result .= "    } else {\n";
2365                 $result .= "        throwRequiredMemberTypeError(state, throwScope, \"". $member->name ."\", \"$name\", \"". GetTypeNameForDisplayInException($type) ."\");\n";
2366                 $result .= "        return { };\n";
2367                 $result .= "    }\n";
2368             } else {
2369                 $result .= "    }\n";
2370             }
2371         }
2372     }
2373
2374     # 5. Return dict.
2375     $result .= "    return result;\n";
2376     $result .= "}\n\n";
2377
2378     if ($dictionary->extendedAttributes->{JSGenerateToJSObject}) {
2379         AddToImplIncludes("JSDOMGlobalObject.h");
2380         AddToImplIncludes("<JavaScriptCore/ObjectConstructor.h>");
2381
2382         $result .= "JSC::JSObject* convertDictionaryToJS(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const ${className}& dictionary)\n";
2383         $result .= "{\n";
2384         $result .= "    auto& vm = state.vm();\n\n";
2385
2386         # 1. Let O be ! ObjectCreate(%ObjectPrototype%).
2387         $result .= "    auto result = constructEmptyObject(&state, globalObject.objectPrototype());\n\n";
2388
2389         # 2. Let dictionaries be a list consisting of D and all of D’s inherited dictionaries,
2390         #    in order from least to most derived.
2391         #    NOTE: This was done above.
2392
2393         # 3. For each dictionary dictionary in dictionaries, in order:
2394         foreach my $dictionary (@dictionaries) {
2395             # 3.1. For each dictionary member member declared on dictionary, in lexicographical order:
2396             my @sortedMembers = sort { $a->name cmp $b->name } @{$dictionary->members};
2397             foreach my $member (@sortedMembers) {
2398                 my $key = $member->name;
2399                 my $implementedAsKey = $member->extendedAttributes->{ImplementedAs} || $key;
2400                 my $valueExpression = "dictionary.${implementedAsKey}";
2401
2402                 # 1. Let key be the identifier of member.
2403                 # 2. If the dictionary member named key is present in V, then:
2404                     # 1. Let idlValue be the value of member on V.
2405                     # 2. Let value be the result of converting idlValue to an ECMAScript value.
2406                     # 3. Perform ! CreateDataProperty(O, key, value).
2407
2408                 my $needsRuntimeCheck = NeedsRuntimeCheck($dictionary, $member);
2409                 my $indent = "";
2410                 if ($needsRuntimeCheck) {
2411                     my $runtimeEnableConditionalString = GenerateRuntimeEnableConditionalString($dictionary, $member, "true");
2412                     $result .= "    if (${runtimeEnableConditionalString}) {\n";
2413                     $indent = "    ";
2414                 }
2415
2416                 if (!$member->isRequired && not defined $member->default) {
2417                     my $IDLType = GetIDLType($typeScope, $member->type);
2418                     my $conversionExpression = NativeToJSValueUsingReferences($member, $typeScope, "${IDLType}::extractValueFromNullable(${valueExpression})", "globalObject");
2419
2420                     $result .= "${indent}    if (!${IDLType}::isNullValue(${valueExpression})) {\n";
2421                     $result .= "${indent}        auto ${key}Value = ${conversionExpression};\n";
2422                     $result .= "${indent}        result->putDirect(vm, JSC::Identifier::fromString(&vm, \"${key}\"), ${key}Value);\n";
2423                     $result .= "${indent}    }\n";
2424                 } else {
2425                     my $conversionExpression = NativeToJSValueUsingReferences($member, $typeScope, $valueExpression, "globalObject");
2426
2427                     $result .= "${indent}    auto ${key}Value = ${conversionExpression};\n";
2428                     $result .= "${indent}    result->putDirect(vm, JSC::Identifier::fromString(&vm, \"${key}\"), ${key}Value);\n";
2429                 }
2430                 if ($needsRuntimeCheck) {
2431                     $result .= "    }\n";
2432                 }
2433             }
2434         }
2435
2436         $result .= "    return result;\n";
2437         $result .= "}\n\n";
2438     }
2439
2440     $result .= "#endif\n\n" if $conditional;
2441
2442     return $result;
2443 }
2444
2445 sub GenerateDictionariesImplementationContent
2446 {
2447     my ($typeScope, $allDictionaries) = @_;
2448
2449     my $result = "";
2450     foreach my $dictionary (@$allDictionaries) {
2451         my $className = GetDictionaryClassName($dictionary->type, $typeScope);
2452         $result .= GenerateDictionaryImplementationContent($dictionary, $className, $typeScope);
2453     }
2454     return $result;
2455 }
2456
2457 sub GetJSTypeForNode
2458 {
2459     my ($interface) = @_;
2460
2461     if ($codeGenerator->InheritsInterface($interface, "Document")) {
2462         return "JSDocumentWrapperType";
2463     }
2464     if ($codeGenerator->InheritsInterface($interface, "DocumentFragment")) {
2465         return "JSDocumentFragmentNodeType";
2466     }
2467     if ($codeGenerator->InheritsInterface($interface, "DocumentType")) {
2468         return "JSDocumentTypeNodeType";
2469     }
2470     if ($codeGenerator->InheritsInterface($interface, "ProcessingInstruction")) {
2471         return "JSProcessingInstructionNodeType";
2472     }
2473     if ($codeGenerator->InheritsInterface($interface, "CDATASection")) {
2474         return "JSCDATASectionNodeType";
2475     }
2476     if ($codeGenerator->InheritsInterface($interface, "Attr")) {
2477         return "JSAttrNodeType";
2478     }
2479     if ($codeGenerator->InheritsInterface($interface, "Comment")) {
2480         return "JSCommentNodeType";
2481     }
2482     if ($codeGenerator->InheritsInterface($interface, "Text")) {
2483         return "JSTextNodeType";
2484     }
2485     if ($codeGenerator->InheritsInterface($interface, "Element")) {
2486         return "JSElementType";
2487     }
2488     return "JSNodeType";
2489 }
2490
2491 sub GenerateHeader
2492 {
2493     my ($object, $interface, $enumerations, $dictionaries) = @_;
2494
2495     my $interfaceName = $interface->type->name;
2496     my $className = "JS$interfaceName";
2497     my %structureFlags = ();
2498
2499     my $hasParent = $interface->parentType || $interface->extendedAttributes->{JSLegacyParent};
2500     my $parentClassName = GetParentClassName($interface);
2501     my $needsVisitChildren = InstanceNeedsVisitChildren($interface);
2502
2503     # - Add default header template and header protection
2504     push(@headerContentHeader, GenerateHeaderContentHeader($interface));
2505
2506     if ($hasParent) {
2507         $headerIncludes{"$parentClassName.h"} = 1;
2508     } else {
2509         $headerIncludes{"JSDOMWrapper.h"} = 1;
2510         if ($interface->isException) {
2511             $headerIncludes{"<JavaScriptCore/ErrorPrototype.h>"} = 1;
2512         }
2513     }
2514
2515     $headerIncludes{"SVGElement.h"} = 1 if $className =~ /^JSSVG/;
2516
2517     my $implType = GetImplClassName($interface);
2518
2519     my $numConstants = @{$interface->constants};
2520     my $numAttributes = @{$interface->attributes};
2521     my $numOperations = @{$interface->operations};
2522
2523     push(@headerContent, "\nnamespace WebCore {\n\n");
2524
2525     if ($codeGenerator->IsSVGAnimatedType($interface->type)) {
2526         $headerIncludes{"SVGAnimatedPropertyImpl.h"} = 1;
2527     } elsif ($codeGenerator->IsSVGPathSegType($interface->type)) {
2528         $headerIncludes{"SVGPathSegImpl.h"} = 1;
2529     } else {
2530         $headerIncludes{"$interfaceName.h"} = 1 if $hasParent && $interface->extendedAttributes->{JSGenerateToNativeObject};
2531         # Implementation class forward declaration
2532         if (IsDOMGlobalObject($interface)) {
2533             AddClassForwardIfNeeded($interface->type);
2534         }
2535     }
2536
2537     push(@headerContent, "class JSWindowProxy;\n\n") if $interfaceName eq "DOMWindow" or $interfaceName eq "RemoteDOMWindow";
2538
2539     my $exportMacro = GetExportMacroForJSClass($interface);
2540
2541     # Class declaration
2542     push(@headerContent, "class $exportMacro$className : public $parentClassName {\n");
2543
2544     # Static create methods
2545     push(@headerContent, "public:\n");
2546     push(@headerContent, "    using Base = $parentClassName;\n");
2547     push(@headerContent, "    using DOMWrapped = $implType;\n") if $hasParent;
2548
2549     if ($interfaceName eq "DOMWindow" || $interfaceName eq "RemoteDOMWindow") {
2550         push(@headerContent, "    static $className* create(JSC::VM& vm, JSC::Structure* structure, Ref<$implType>&& impl, JSWindowProxy* proxy)\n");
2551         push(@headerContent, "    {\n");
2552         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(vm.heap)) ${className}(vm, structure, WTFMove(impl), proxy);\n");
2553         push(@headerContent, "        ptr->finishCreation(vm, proxy);\n");
2554         push(@headerContent, "        return ptr;\n");
2555         push(@headerContent, "    }\n\n");
2556     } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope") || $codeGenerator->InheritsInterface($interface, "WorkletGlobalScope")) {
2557         push(@headerContent, "    static $className* create(JSC::VM& vm, JSC::Structure* structure, Ref<$implType>&& impl, JSC::JSProxy* proxy)\n");
2558         push(@headerContent, "    {\n");
2559         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(vm.heap)) ${className}(vm, structure, WTFMove(impl));\n");
2560         push(@headerContent, "        ptr->finishCreation(vm, proxy);\n");
2561         push(@headerContent, "        return ptr;\n");
2562         push(@headerContent, "    }\n\n");
2563     } elsif ($interface->extendedAttributes->{MasqueradesAsUndefined}) {
2564         AddIncludesForImplementationTypeInHeader($implType);
2565         push(@headerContent, "    static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<$implType>&& impl)\n");
2566         push(@headerContent, "    {\n");
2567         push(@headerContent, "        globalObject->masqueradesAsUndefinedWatchpoint()->fireAll(globalObject->vm(), \"Allocated masquerading object\");\n");
2568         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject, WTFMove(impl));\n");
2569         push(@headerContent, "        ptr->finishCreation(globalObject->vm());\n");
2570         push(@headerContent, "        return ptr;\n");
2571         push(@headerContent, "    }\n\n");
2572     } elsif (!NeedsImplementationClass($interface)) {
2573         push(@headerContent, "    static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject)\n");
2574         push(@headerContent, "    {\n");
2575         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject);\n");
2576         push(@headerContent, "        ptr->finishCreation(globalObject->vm());\n");
2577         push(@headerContent, "        return ptr;\n");
2578         push(@headerContent, "    }\n\n");  
2579     } else {
2580         if (!$codeGenerator->IsSVGAnimatedType($interface->type) && !$codeGenerator->IsSVGPathSegType($interface->type)) {
2581             AddIncludesForImplementationTypeInHeader($implType);
2582         }
2583         push(@headerContent, "    static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<$implType>&& impl)\n");
2584         push(@headerContent, "    {\n");
2585         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject, WTFMove(impl));\n");
2586         push(@headerContent, "        ptr->finishCreation(globalObject->vm());\n");
2587         push(@headerContent, "        return ptr;\n");
2588         push(@headerContent, "    }\n\n");
2589     }
2590
2591     push(@headerContent, "    static const bool needsDestruction = false;\n\n") if IsDOMGlobalObject($interface);
2592
2593     $structureFlags{"JSC::HasStaticPropertyTable"} = 1 if InstancePropertyCount($interface) > 0;
2594     $structureFlags{"JSC::NewImpurePropertyFiresWatchpoints"} = 1 if $interface->extendedAttributes->{NewImpurePropertyFiresWatchpoints};
2595     $structureFlags{"JSC::IsImmutablePrototypeExoticObject"} = 1 if $interface->extendedAttributes->{IsImmutablePrototypeExoticObject};
2596     $structureFlags{"JSC::MasqueradesAsUndefined"} = 1 if $interface->extendedAttributes->{MasqueradesAsUndefined};
2597     $structureFlags{"JSC::ImplementsHasInstance | JSC::ImplementsDefaultHasInstance"} = 1 if $interfaceName eq "DOMWindow";
2598         
2599     # Prototype
2600     unless (ShouldUseGlobalObjectPrototype($interface)) {
2601         push(@headerContent, "    static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&);\n");
2602         push(@headerContent, "    static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&);\n");
2603     }
2604
2605     # JSValue to implementation type
2606     if (ShouldGenerateToWrapped($hasParent, $interface)) {
2607         # FIXME: Add extended attribute for this.
2608         my @toWrappedArguments = ();
2609         push(@toWrappedArguments, "JSC::VM&");
2610         push(@toWrappedArguments, "JSC::ExecState&") if $interface->type->name eq "XPathNSResolver";
2611         push(@toWrappedArguments, "JSC::JSValue");
2612
2613         my $toWrappedType = $interface->type->name eq "XPathNSResolver" ? "RefPtr<${implType}>" : "${implType}*";
2614
2615         my $export = "";
2616         $export = "WEBCORE_EXPORT " if $interface->extendedAttributes->{ExportToWrappedFunction};
2617         push(@headerContent, "    static ${export}${toWrappedType} toWrapped(" . join(", ", @toWrappedArguments) . ");\n");
2618     }
2619
2620     $headerTrailingIncludes{"${className}Custom.h"} = 1 if $interface->extendedAttributes->{JSCustomHeader};
2621
2622     my $namedGetterOperation = GetNamedGetterOperation($interface);
2623     my $indexedGetterOperation = GetIndexedGetterOperation($interface);
2624
2625     # FIXME: Why doesn't this also include Indexed Getters and [CustomGetOwnPropertySlot]
2626     if ($namedGetterOperation) {
2627         if ($codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins")) {
2628             $structureFlags{"JSC::GetOwnPropertySlotIsImpure"} = 1;
2629         } else {
2630             $structureFlags{"JSC::GetOwnPropertySlotIsImpureForPropertyAbsence"} = 1;
2631         }
2632     }
2633     
2634     # ClassInfo MethodTable declarations.
2635     
2636     if (InstanceOverridesGetOwnPropertySlot($interface)) {
2637         push(@headerContent, "    static bool getOwnPropertySlot(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&);\n");
2638         $structureFlags{"JSC::OverridesGetOwnPropertySlot"} = 1;
2639         push(@headerContent, "    static bool getOwnPropertySlotByIndex(JSC::JSObject*, JSC::ExecState*, unsigned propertyName, JSC::PropertySlot&);\n");
2640         $structureFlags{"JSC::InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero"} = 1;
2641     }
2642     
2643     if (InstanceOverridesGetOwnPropertyNames($interface)) {
2644         push(@headerContent, "    static void getOwnPropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode = JSC::EnumerationMode());\n");
2645         $structureFlags{"JSC::OverridesGetPropertyNames"} = 1;
2646     }
2647     
2648     if (InstanceOverridesPut($interface)) {
2649         push(@headerContent, "    static bool put(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&);\n");
2650         push(@headerContent, "    static bool putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName, JSC::JSValue, bool shouldThrow);\n");
2651     }
2652     
2653     if (InstanceOverridesDefineOwnProperty($interface)) {
2654         push(@headerContent, "    static bool defineOwnProperty(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, const JSC::PropertyDescriptor&, bool shouldThrow);\n");
2655     }
2656
2657     if (InstanceOverridesDeleteProperty($interface)) {
2658         push(@headerContent, "    static bool deleteProperty(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName);\n");
2659         push(@headerContent, "    static bool deletePropertyByIndex(JSC::JSCell*, JSC::ExecState*, unsigned);\n");
2660     }
2661
2662     if (InstanceOverridesGetCallData($interface)) {
2663         push(@headerContent, "    static JSC::CallType getCallData(JSC::JSCell*, JSC::CallData&);\n\n");
2664         $headerIncludes{"<JavaScriptCore/CallData.h>"} = 1;
2665         $structureFlags{"JSC::OverridesGetCallData"} = 1;
2666     }
2667     
2668     if ($interface->extendedAttributes->{CustomGetPrototype}) {
2669         push(@headerContent, "    static JSC::JSValue getPrototype(JSC::JSObject*, JSC::ExecState*);\n");
2670     }
2671     
2672     if ($interface->extendedAttributes->{CustomToStringName}) {
2673         push(@headerContent, "    static String toStringName(const JSC::JSObject*, JSC::ExecState*);\n");
2674     }
2675     
2676     if ($interface->extendedAttributes->{CustomPreventExtensions}) {
2677         push(@headerContent, "    static bool preventExtensions(JSC::JSObject*, JSC::ExecState*);\n");
2678     }
2679
2680     if (InstanceNeedsEstimatedSize($interface)) {
2681         push(@headerContent, "    static size_t estimatedSize(JSCell*, JSC::VM&);\n");
2682     }
2683     
2684     if (!$hasParent) {
2685         push(@headerContent, "    static void destroy(JSC::JSCell*);\n");
2686     }
2687
2688     # Class info
2689     if ($interfaceName eq "Node") {
2690         push(@headerContent, "\n");
2691         push(@headerContent, "protected:\n");
2692         push(@headerContent, "    static const JSC::ClassInfo s_info;\n");
2693         push(@headerContent, "public:\n");
2694         push(@headerContent, "    static constexpr const JSC::ClassInfo* info() { return &s_info; }\n\n");
2695     } else {
2696         push(@headerContent, "\n");
2697         push(@headerContent, "    DECLARE_INFO;\n\n");
2698     }
2699
2700     # Structure ID
2701     push(@headerContent, "    static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)\n");
2702     push(@headerContent, "    {\n");
2703     if (IsDOMGlobalObject($interface)) {
2704         push(@headerContent, "        return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::GlobalObjectType, StructureFlags), info());\n");
2705     } elsif ($codeGenerator->InheritsInterface($interface, "Node")) {
2706         my $type = GetJSTypeForNode($interface);
2707         push(@headerContent, "        return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSType($type), StructureFlags), info());\n");
2708     } elsif ($codeGenerator->InheritsInterface($interface, "Event")) {
2709         push(@headerContent, "        return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSType(JSEventType), StructureFlags), info());\n");
2710     } else {
2711         push(@headerContent, "        return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());\n");
2712     }
2713     push(@headerContent, "    }\n\n");
2714
2715     # Custom pushEventHandlerScope function
2716     if ($interface->extendedAttributes->{CustomPushEventHandlerScope}) {
2717         push(@headerContent, "    JSC::JSScope* pushEventHandlerScope(JSC::ExecState*, JSC::JSScope*) const;\n\n");
2718     }
2719     
2720     # Constructor object getter
2721     unless ($interface->extendedAttributes->{NoInterfaceObject}) {
2722         push(@headerContent, "    static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*);\n");
2723         push(@headerContent, "    static JSC::JSValue getNamedConstructor(JSC::VM&, JSC::JSGlobalObject*);\n") if $interface->extendedAttributes->{NamedConstructor};
2724     }
2725
2726     # Serializer function.
2727     if ($interface->serializable) {
2728         push(@headerContent, "    static JSC::JSObject* serialize(JSC::ExecState&, ${className}& thisObject, JSDOMGlobalObject&, JSC::ThrowScope&);\n");
2729     }
2730     
2731     my $numCustomOperations = 0;
2732     my $numCustomAttributes = 0;
2733
2734     my $hasForwardDeclaringOperations = 0;
2735     my $hasForwardDeclaringAttributes = 0;
2736
2737     my $hasDOMJITAttributes = 0;
2738
2739     # Attribute and function enums
2740     if ($numAttributes > 0) {
2741         foreach my $attribute (@{$interface->attributes}) {
2742             $numCustomAttributes++ if HasCustomGetter($attribute);
2743             $numCustomAttributes++ if HasCustomSetter($attribute);
2744             if ($attribute->extendedAttributes->{CachedAttribute}) {
2745                 my $conditionalString = $codeGenerator->GenerateConditionalString($attribute);
2746                 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2747                 push(@headerContent, "    mutable JSC::WriteBarrier<JSC::Unknown> m_" . $attribute->name . ";\n");
2748                 $numCachedAttributes++;
2749                 push(@headerContent, "#endif\n") if $conditionalString;
2750             }
2751             $hasDOMJITAttributes = 1 if $attribute->extendedAttributes->{DOMJIT};
2752
2753             $hasForwardDeclaringAttributes = 1 if $attribute->extendedAttributes->{ForwardDeclareInHeader};
2754         }
2755     }
2756
2757     # visit function
2758     if ($needsVisitChildren) {
2759         push(@headerContent, "    static void visitChildren(JSCell*, JSC::SlotVisitor&);\n");
2760         push(@headerContent, "    void visitAdditionalChildren(JSC::SlotVisitor&);\n") if $interface->extendedAttributes->{JSCustomMarkFunction};
2761         push(@headerContent, "\n");
2762
2763         if ($interface->extendedAttributes->{JSCustomMarkFunction}) {
2764             # We assume that the logic in visitAdditionalChildren is highly volatile, and during a
2765             # concurrent GC or in between eden GCs something may happen that would lead to this
2766             # logic behaving differently. Since this could mark objects or add opaque roots, this
2767             # means that after any increment of mutator resumption in a concurrent GC and at least
2768             # once during any eden GC we need to re-execute visitAdditionalChildren on any objects
2769             # that we had executed it on before. We do this using the DOM's own MarkingConstraint,
2770             # which will call visitOutputConstraints on all objects in the DOM's own
2771             # outputConstraintSubspace. visitOutputConstraints is the name JSC uses for the method
2772             # that the GC calls to ask an object is it would like to mark anything else after the
2773             # program resumed since the last call to visitChildren or visitOutputConstraints. Since
2774             # this just calls visitAdditionalChildren, you usually don't have to worry about this.
2775             push(@headerContent, "    static void visitOutputConstraints(JSCell*, JSC::SlotVisitor&);\n");
2776             my $subspaceFunc = IsDOMGlobalObject($interface) ? "globalObjectOutputConstraintSubspaceFor" : "outputConstraintSubspaceFor";
2777             push(@headerContent, "    template<typename, JSC::SubspaceAccess> static JSC::CompleteSubspace* subspaceFor(JSC::VM& vm) { return $subspaceFunc(vm); }\n");
2778         }
2779     }
2780
2781     if (NeedsImplementationClass($interface)) {
2782         push(@headerContent, "    static void heapSnapshot(JSCell*, JSC::HeapSnapshotBuilder&);\n");
2783     }
2784     
2785     if ($numCustomAttributes > 0) {
2786         push(@headerContent, "\n    // Custom attributes\n");
2787
2788         foreach my $attribute (@{$interface->attributes}) {
2789             my $conditionalString = $codeGenerator->GenerateConditionalString($attribute);
2790             if (HasCustomGetter($attribute)) {
2791                 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2792                 my $methodName = $codeGenerator->WK_lcfirst($attribute->name);
2793                 push(@headerContent, "    JSC::JSValue " . $methodName . "(JSC::ExecState&) const;\n");
2794                 push(@headerContent, "#endif\n") if $conditionalString;
2795             }
2796             if (HasCustomSetter($attribute) && !IsReadonly($attribute)) {
2797                 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2798                 push(@headerContent, "    void set" . $codeGenerator->WK_ucfirst($attribute->name) . "(JSC::ExecState&, JSC::JSValue);\n");
2799                 push(@headerContent, "#endif\n") if $conditionalString;
2800             }
2801         }
2802     }
2803
2804     foreach my $operation (@{$interface->operations}) {
2805         $numCustomOperations++ if HasCustomMethod($operation);
2806         $hasForwardDeclaringOperations = 1 if $operation->extendedAttributes->{ForwardDeclareInHeader};
2807     }
2808
2809     if ($numCustomOperations > 0) {
2810         my $inAppleCopyright = 0;
2811         push(@headerContent, "\n    // Custom functions\n");
2812         foreach my $operation (@{$interface->operations}) {
2813             next unless HasCustomMethod($operation);
2814             next if $operation->{overloads} && $operation->{overloadIndex} != 1;
2815
2816             if ($operation->extendedAttributes->{AppleCopyright}) {
2817                 if (!$inAppleCopyright) {
2818                     push(@headerContent, $beginAppleCopyrightForHeaderFiles);
2819                     $inAppleCopyright = 1;
2820                 }
2821             } elsif ($inAppleCopyright) {
2822                 push(@headerContent, $endAppleCopyright);
2823                 $inAppleCopyright = 0;
2824             }
2825
2826             my $conditionalString = $codeGenerator->GenerateConditionalString($operation);
2827             push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2828
2829             my $functionImplementationName = $operation->extendedAttributes->{ImplementedAs} || $codeGenerator->WK_lcfirst($operation->name);
2830
2831             my @functionArguments = ();
2832             push(@functionArguments, "JSC::ExecState&");
2833             push(@functionArguments, "Ref<DeferredPromise>&&") if $codeGenerator->IsPromiseType($operation->type) && !$operation->extendedAttributes->{ReturnsOwnPromise};
2834
2835             push(@headerContent, "    " . ($operation->isStatic ? "static " : "") . "JSC::JSValue " . $functionImplementationName . "(" . join(", ", @functionArguments) . ");\n");
2836
2837             push(@headerContent, "#endif\n") if $conditionalString;
2838         }
2839         push(@headerContent, $endAppleCopyright) if $inAppleCopyright;
2840     }
2841
2842     if (NeedsImplementationClass($interface)) {
2843         if ($hasParent) {
2844             push(@headerContent, "    $interfaceName& wrapped() const\n");
2845             push(@headerContent, "    {\n");
2846             push(@headerContent, "        return static_cast<$interfaceName&>(Base::wrapped());\n");
2847             push(@headerContent, "    }\n");
2848         }
2849     }
2850
2851     # structure flags
2852     if (%structureFlags) {
2853         push(@headerContent, "public:\n");
2854         push(@headerContent, "    static const unsigned StructureFlags = Base::StructureFlags");
2855         foreach my $structureFlag (sort (keys %structureFlags)) {
2856             push(@headerContent, " | " . $structureFlag);
2857         }
2858         push(@headerContent, ";\n");
2859     }
2860
2861     push(@headerContent, "protected:\n");
2862
2863     # Constructor
2864     if ($interfaceName eq "DOMWindow" || $interfaceName eq "RemoteDOMWindow") {
2865         push(@headerContent, "    $className(JSC::VM&, JSC::Structure*, Ref<$implType>&&, JSWindowProxy*);\n");
2866     } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope") || $codeGenerator->InheritsInterface($interface, "WorkletGlobalScope")) {
2867         push(@headerContent, "    $className(JSC::VM&, JSC::Structure*, Ref<$implType>&&);\n");
2868     } elsif (!NeedsImplementationClass($interface)) {
2869         push(@headerContent, "    $className(JSC::Structure*, JSDOMGlobalObject&);\n\n");
2870      } else {
2871         push(@headerContent, "    $className(JSC::Structure*, JSDOMGlobalObject&, Ref<$implType>&&);\n\n");
2872     }
2873
2874     if ($interfaceName eq "DOMWindow" || $interfaceName eq "RemoteDOMWindow") {
2875         push(@headerContent, "    void finishCreation(JSC::VM&, JSWindowProxy*);\n");
2876     } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope") || $codeGenerator->InheritsInterface($interface, "WorkletGlobalScope")) {
2877         push(@headerContent, "    void finishCreation(JSC::VM&, JSC::JSProxy*);\n");
2878     } else {
2879         push(@headerContent, "    void finishCreation(JSC::VM&);\n");
2880     }
2881
2882     push(@headerContent, "};\n\n");
2883
2884     if (ShouldGenerateWrapperOwnerCode($hasParent, $interface)) {
2885         if ($interfaceName ne "Node" && $codeGenerator->InheritsInterface($interface, "Node")) {
2886             $headerIncludes{"JSNode.h"} = 1;
2887             push(@headerContent, "class JS${interfaceName}Owner : public JSNodeOwner {\n");
2888         } else {
2889             push(@headerContent, "class JS${interfaceName}Owner : public JSC::WeakHandleOwner {\n");
2890         }
2891         $headerIncludes{"<wtf/NeverDestroyed.h>"} = 1;
2892         push(@headerContent, "public:\n");
2893         push(@headerContent, "    virtual bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor&, const char**);\n");
2894         push(@headerContent, "    virtual void finalize(JSC::Handle<JSC::Unknown>, void* context);\n");
2895         push(@headerContent, "};\n");
2896         push(@headerContent, "\n");
2897         push(@headerContent, "inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld&, $implType*)\n");
2898         push(@headerContent, "{\n");
2899         push(@headerContent, "    static NeverDestroyed<JS${interfaceName}Owner> owner;\n");
2900         push(@headerContent, "    return &owner.get();\n");
2901         push(@headerContent, "}\n");
2902         push(@headerContent, "\n");
2903         push(@headerContent, "inline void* wrapperKey($implType* wrappableObject)\n");
2904         push(@headerContent, "{\n");
2905         push(@headerContent, "    return wrappableObject;\n");
2906         push(@headerContent, "}\n");
2907         push(@headerContent, "\n");
2908     }
2909     if (ShouldGenerateToJSDeclaration($hasParent, $interface)) {
2910         # Node and NodeList have custom inline implementations which thus cannot be exported.
2911         # FIXME: The special case for Node and NodeList should probably be implemented via an IDL attribute.
2912         if ($implType eq "Node" or $implType eq "NodeList") {
2913             push(@headerContent, "JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, $implType&);\n");
2914         } else {
2915             push(@headerContent, $exportMacro."JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, $implType&);\n");
2916         }
2917         push(@headerContent, "inline JSC::JSValue toJS(JSC::ExecState* state, JSDOMGlobalObject* globalObject, $implType* impl) { return impl ? toJS(state, globalObject, *impl) : JSC::jsNull(); }\n");
2918
2919         push(@headerContent, "JSC::JSValue toJSNewlyCreated(JSC::ExecState*, JSDOMGlobalObject*, Ref<$implType>&&);\n");
2920         push(@headerContent, "inline JSC::JSValue toJSNewlyCreated(JSC::ExecState* state, JSDOMGlobalObject* globalObject, RefPtr<$implType>&& impl) { return impl ? toJSNewlyCreated(state, globalObject, impl.releaseNonNull()) : JSC::jsNull(); }\n");
2921    }
2922
2923     push(@headerContent, "\n");
2924
2925     GeneratePrototypeDeclaration(\@headerContent, $className, $interface) if HeaderNeedsPrototypeDeclaration($interface);
2926
2927     if ($hasForwardDeclaringOperations) {
2928         my $inAppleCopyright = 0;
2929         push(@headerContent,"// Functions\n\n");
2930         foreach my $operation (@{$interface->operations}) {
2931             next if $operation->{overloadIndex} && $operation->{overloadIndex} > 1;
2932             next unless $operation->extendedAttributes->{ForwardDeclareInHeader};
2933
2934             if ($operation->extendedAttributes->{AppleCopyright}) {
2935                 if (!$inAppleCopyright) {
2936                     push(@headerContent, $beginAppleCopyrightForHeaderFiles);
2937                     $inAppleCopyright = 1;
2938                 }
2939             } elsif ($inAppleCopyright) {
2940                 push(@headerContent, $endAppleCopyright);
2941                 $inAppleCopyright = 0;
2942             }
2943
2944             my $conditionalAttribute = GetConditionalForOperationConsideringOverloads($operation);
2945             my $conditionalString = $conditionalAttribute ? $codeGenerator->GenerateConditionalStringFromAttributeValue($conditionalAttribute) : undef;
2946             push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2947             my $functionName = GetFunctionName($interface, $className, $operation);
2948             push(@headerContent, "JSC::EncodedJSValue JSC_HOST_CALL ${functionName}(JSC::ExecState*);\n");
2949             push(@headerContent, "#endif\n") if $conditionalString;
2950         }
2951
2952         push(@headerContent, $endAppleCopyright) if $inAppleCopyright;
2953         push(@headerContent,"\n");
2954     }
2955
2956     if ($hasForwardDeclaringAttributes) {
2957         push(@headerContent,"// Attributes\n\n");
2958         foreach my $attribute (@{$interface->attributes}) {
2959             next unless $attribute->extendedAttributes->{ForwardDeclareInHeader};
2960
2961             my $conditionalString = $codeGenerator->GenerateConditionalString($attribute);
2962             push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2963             my $getter = GetAttributeGetterName($interface, $className, $attribute);
2964             push(@headerContent, "JSC::EncodedJSValue ${getter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::PropertyName);\n");
2965             if (!IsReadonly($attribute)) {
2966                 my $setter = GetAttributeSetterName($interface, $className, $attribute);
2967                 push(@headerContent, "bool ${setter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::EncodedJSValue);\n");
2968             }
2969             push(@headerContent, "#endif\n") if $conditionalString;
2970         }
2971     }
2972
2973     # CheckSubClass Snippet function.
2974     if ($interface->extendedAttributes->{DOMJIT}) {
2975         $headerIncludes{"<JavaScriptCore/Snippet.h>"} = 1;
2976         push(@headerContent, "#if ENABLE(JIT)\n");
2977         push(@headerContent, "Ref<JSC::Snippet> checkSubClassSnippetFor${className}();\n");
2978         push(@headerContent, "#endif\n");
2979     }
2980
2981     if ($hasDOMJITAttributes) {
2982         $headerIncludes{"<JavaScriptCore/DOMJITGetterSetter.h>"} = 1;
2983         push(@headerContent,"// DOM JIT Attributes\n\n");
2984         foreach my $attribute (@{$interface->attributes}) {
2985             next unless $attribute->extendedAttributes->{DOMJIT};
2986             assert("Only DOMJIT=Getter is supported for attributes") unless $codeGenerator->ExtendedAttributeContains($attribute->extendedAttributes->{DOMJIT}, "Getter");
2987
2988             my $interfaceName = $interface->type->name;
2989             my $className = $interfaceName . $codeGenerator->WK_ucfirst($attribute->name);
2990             my $domJITClassName = $className . "Attribute";
2991
2992             push(@headerContent, "#if ENABLE(JIT)\n");
2993             push(@headerContent, "Ref<JSC::DOMJIT::CallDOMGetterSnippet> compile${domJITClassName}();\n");
2994             push(@headerContent, "#endif\n\n");
2995         }
2996     }
2997
2998     if (HasCustomConstructor($interface)) {
2999         push(@headerContent, "// Custom constructor\n");
3000         push(@headerContent, "JSC::EncodedJSValue JSC_HOST_CALL construct${className}(JSC::ExecState&);\n\n");
3001     }
3002
3003     if (NeedsImplementationClass($interface)) {
3004         my $toWrappedType = $interface->type->name eq "XPathNSResolver" ? "RefPtr<${implType}>" : "${implType}*";
3005         $headerIncludes{"JSDOMWrapper.h"} = 1;
3006
3007         push(@headerContent, "template<> struct JSDOMWrapperConverterTraits<${implType}> {\n");
3008         push(@headerContent, "    using WrapperClass = ${className};\n");
3009         push(@headerContent, "    using ToWrappedReturnType = ${toWrappedType};\n");
3010         push(@headerContent, "};\n");
3011     }
3012
3013     push(@headerContent, GenerateEnumerationsHeaderContent($interface, $enumerations));
3014     push(@headerContent, GenerateDictionariesHeaderContent($interface, $dictionaries));
3015
3016     my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
3017     push(@headerContent, "\n} // namespace WebCore\n");
3018     push(@headerContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
3019
3020     if ($interface->extendedAttributes->{AppleCopyright}) {
3021         push(@headerContent, "\n");
3022         push(@headerContent, split("\r", $endAppleCopyright));
3023     }
3024
3025     # - Generate dependencies.
3026     if ($writeDependencies) {
3027         my @ancestors;
3028         $codeGenerator->ForAllParents($interface, sub {
3029             my $currentInterface = shift;
3030             push(@ancestors, $currentInterface->type->name);
3031         }, 0);
3032         for my $dictionary (@$dictionaries) {
3033             my $parentType = $dictionary->parentType;
3034             while (defined($parentType)) {
3035                 push(@ancestors, $parentType->name) if $codeGenerator->IsExternalDictionaryType($parentType);
3036                 my $parentDictionary = $codeGenerator->GetDictionaryByType($parentType);
3037                 assert("Unable to find definition for dictionary named '" . $parentType->name . "'!") unless defined($parentDictionary);
3038                 $parentType = $parentDictionary->parentType;
3039             }
3040         }
3041         push(@depsContent, "$className.h : ", join(" ", map { "$_.idl" } @ancestors), "\n");
3042         push(@depsContent, map { "$_.idl :\n" } @ancestors);
3043     }
3044 }
3045
3046 sub GeneratePropertiesHashTable
3047 {
3048     my ($object, $interface, $isInstance, $hashKeys, $hashSpecials, $hashValue1, $hashValue2, $conditionals, $readWriteConditionals, $runtimeEnabledOperations, $runtimeEnabledAttributes) = @_;
3049
3050     # FIXME: These should be functions on $interface.
3051     my $interfaceName = $interface->type->name;
3052     my $className = "JS$interfaceName";
3053     
3054     # - Add all properties in a hashtable definition
3055     my $propertyCount = $isInstance ? InstancePropertyCount($interface) : PrototypePropertyCount($interface);
3056
3057     if (!$isInstance && NeedsConstructorProperty($interface)) {
3058         die if !$propertyCount;
3059         push(@$hashKeys, "constructor");
3060         my $getter = "js" . $interfaceName . "Constructor";
3061         push(@$hashValue1, $getter);
3062
3063         my $setter = "setJS" . $interfaceName . "Constructor";
3064         push(@$hashValue2, $setter);
3065         push(@$hashSpecials, "static_cast<unsigned>(JSC::PropertyAttribute::DontEnum)");
3066     }
3067
3068     return 0 if !$propertyCount;
3069
3070     my @attributes = @{$interface->attributes};
3071     push(@attributes, @{$interface->mapLike->attributes}) if $interface->mapLike;
3072
3073     foreach my $attribute (@attributes) {
3074         next if ($attribute->isStatic);
3075         next if AttributeShouldBeOnInstance($interface, $attribute) != $isInstance;
3076         next if ($attribute->extendedAttributes->{PrivateIdentifier} and not $attribute->extendedAttributes->{PublicIdentifier});
3077
3078         # Global objects add RuntimeEnabled attributes after creation so do not add them to the static table.
3079         if ($isInstance && NeedsRuntimeCheck($interface, $attribute)) {
3080             $propertyCount -= 1;
3081             next;
3082         }
3083
3084         my $name = $attribute->name;
3085         push(@$hashKeys, $name);
3086
3087         my $special = GetJSCAttributesForAttribute($interface, $attribute);
3088         push(@$hashSpecials, $special);
3089
3090         if ($attribute->extendedAttributes->{DOMJIT}) {
3091             push(@$hashValue1, "&DOMJITAttributeFor" . $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name));
3092         } else {
3093             my $getter = GetAttributeGetterName($interface, $className, $attribute);
3094             push(@$hashValue1, $getter);
3095         }
3096
3097         if (IsReadonly($attribute)) {
3098             push(@$hashValue2, "0");
3099         } else {
3100             my $setter = GetAttributeSetterName($interface, $className, $attribute);
3101             push(@$hashValue2, $setter);
3102         }
3103
3104         my $conditional = $attribute->extendedAttributes->{Conditional};
3105         $conditionals->{$name} = $conditional if $conditional;
3106         my $readWriteConditional = $attribute->extendedAttributes->{ConditionallyReadWrite};
3107         $readWriteConditionals->{$name} = $readWriteConditional if $readWriteConditional;
3108
3109         if (NeedsRuntimeCheck($interface, $attribute)) {
3110             push(@$runtimeEnabledAttributes, $attribute);
3111         }
3112     }
3113
3114     my @operations = @{$interface->operations};
3115     push(@operations, @{$interface->iterable->operations}) if IsKeyValueIterableInterface($interface);
3116     push(@operations, @{$interface->mapLike->operations}) if $interface->mapLike;
3117     push(@operations, @{$interface->serializable->operations}) if $interface->serializable;
3118     foreach my $operation (@operations) {
3119         next if ($operation->extendedAttributes->{PrivateIdentifier} and not $operation->extendedAttributes->{PublicIdentifier});
3120         next if ($operation->isStatic);
3121         next if $operation->{overloadIndex} && $operation->{overloadIndex} > 1;
3122         next if OperationShouldBeOnInstance($interface, $operation) != $isInstance;
3123         next if $operation->name eq "[Symbol.Iterator]";
3124
3125         # Global objects add RuntimeEnabled operations after creation so do not add them to the static table.
3126         if ($isInstance && NeedsRuntimeCheck($interface, $operation)) {
3127             $propertyCount -= 1;
3128             next;
3129         }
3130
3131         my $name = $operation->name;
3132         push(@$hashKeys, $name);
3133
3134         my $functionName = GetFunctionName($interface, $className, $operation);
3135         push(@$hashValue1, $functionName);
3136
3137         my $functionLength = GetFunctionLength($operation);
3138
3139         if ($operation->extendedAttributes->{DOMJIT}) {
3140             push(@$hashValue2, "&DOMJITSignatureFor" . $interface->type->name . $codeGenerator->WK_ucfirst($operation->name));
3141         } else {
3142             push(@$hashValue2, $functionLength);
3143         }
3144
3145         push(@$hashSpecials, ComputeFunctionSpecial($interface, $operation));
3146
3147         my $conditional = GetConditionalForOperationConsideringOverloads($operation);
3148         $conditionals->{$name} = $conditional if $conditional;