66ef5179c823c1558132ec9c4e3c347306a39394
[WebKit-https.git] / Source / WebCore / bindings / scripts / CodeGeneratorJS.pm
1 #
2 # Copyright (C) 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 # Copyright (C) 2006 Anders Carlsson <andersca@mac.com>
4 # Copyright (C) 2006, 2007 Samuel Weinig <sam@webkit.org>
5 # Copyright (C) 2006 Alexey Proskuryakov <ap@webkit.org>
6 # Copyright (C) 2006-2017 Apple Inc. All rights reserved.
7 # Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au>
8 # Copyright (C) Research In Motion Limited 2010. All rights reserved.
9 # Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
10 # Copyright (C) 2011 Patrick Gansterer <paroga@webkit.org>
11 # Copyright (C) 2012 Ericsson AB. All rights reserved.
12 # Copyright (C) 2007, 2008, 2009, 2012 Google Inc.
13 # Copyright (C) 2013 Samsung Electronics. All rights reserved.
14 # Copyright (C) 2015, 2016 Canon Inc. All rights reserved.
15 #
16 # This library is free software; you can redistribute it and/or
17 # modify it under the terms of the GNU Library General Public
18 # License as published by the Free Software Foundation; either
19 # version 2 of the License, or (at your option) any later version.
20 #
21 # This library is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24 # Library General Public License for more details.
25 #
26 # You should have received a copy of the GNU Library General Public License
27 # along with this library; see the file COPYING.LIB.  If not, write to
28 # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29 # Boston, MA 02110-1301, USA.
30
31
32 package CodeGeneratorJS;
33
34 use strict;
35 use constant FileNamePrefix => "JS";
36 use Carp qw<longmess>;
37 use Data::Dumper;
38 use Hasher;
39
40 my $codeGenerator;
41 my $writeDependencies;
42
43 my @headerContentHeader = ();
44 my @headerContent = ();
45 my %headerIncludes = ();
46 my %headerTrailingIncludes = ();
47
48 my @implContentHeader = ();
49 my @implContent = ();
50 my %implIncludes = ();
51 my @depsContent = ();
52 my $numCachedAttributes = 0;
53
54 my $beginAppleCopyrightForHeaderFiles = <<END;
55 // ------- Begin Apple Copyright -------
56 /*
57  * Copyright (C) 2008 Apple Inc. All rights reserved.
58  *
59  * Permission is granted by Apple to use this file to the extent
60  * necessary to relink with LGPL WebKit files.
61  *
62  * No license or rights are granted by Apple expressly or by
63  * implication, estoppel, or otherwise, to Apple patents and
64  * trademarks. For the sake of clarity, no license or rights are
65  * granted by Apple expressly or by implication, estoppel, or otherwise,
66  * under any Apple patents, copyrights and trademarks to underlying
67  * implementations of any application programming interfaces (APIs)
68  * or to any functionality that is invoked by calling any API.
69  */
70
71 END
72 my $beginAppleCopyrightForSourceFiles = <<END;
73 // ------- Begin Apple Copyright -------
74 /*
75  * Copyright (C) 2008 Apple Inc. All rights reserved.
76  *
77  * No license or rights are granted by Apple expressly or by implication,
78  * estoppel, or otherwise, to Apple copyrights, patents, trademarks, trade
79  * secrets or other rights.
80  */
81
82 END
83 my $endAppleCopyright   = <<END;
84 // ------- End Apple Copyright   -------
85
86 END
87
88 # Default .h template
89 my $headerTemplate = << "EOF";
90 /*
91     This file is part of the WebKit open source project.
92     This file has been generated by generate-bindings.pl. DO NOT MODIFY!
93
94     This library is free software; you can redistribute it and/or
95     modify it under the terms of the GNU Library General Public
96     License as published by the Free Software Foundation; either
97     version 2 of the License, or (at your option) any later version.
98
99     This library is distributed in the hope that it will be useful,
100     but WITHOUT ANY WARRANTY; without even the implied warranty of
101     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
102     Library General Public License for more details.
103
104     You should have received a copy of the GNU Library General Public License
105     along with this library; see the file COPYING.LIB.  If not, write to
106     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
107     Boston, MA 02110-1301, USA.
108 */
109 EOF
110
111 sub assert
112 {
113     my $message = shift;
114
115     my $mess = longmess();
116     print Dumper($mess);
117
118     die $message;
119 }
120
121 # Default constructor
122 sub new
123 {
124     my $object = shift;
125     my $reference = { };
126
127     $codeGenerator = shift;
128     $writeDependencies = shift;
129
130     bless($reference, $object);
131     return $reference;
132 }
133
134 sub GenerateEnumeration
135 {
136     my ($object, $enumeration) = @_;
137
138     my $className = GetEnumerationClassName($enumeration->type);
139     $object->GenerateEnumerationHeader($enumeration, $className);
140     $object->GenerateEnumerationImplementation($enumeration, $className);
141 }
142
143 sub GenerateDictionary
144 {
145     my ($object, $dictionary, $enumerations, $otherDictionaries) = @_;
146
147     my $className = GetDictionaryClassName($dictionary->type);
148     $object->GenerateDictionaryHeader($dictionary, $className, $enumerations, $otherDictionaries);
149     $object->GenerateDictionaryImplementation($dictionary, $className, $enumerations, $otherDictionaries);
150 }
151
152 sub GenerateCallbackFunction
153 {
154     my ($object, $callbackFunction, $enumerations, $dictionaries) = @_;
155
156     $object->GenerateCallbackFunctionHeader($callbackFunction, $enumerations, $dictionaries);
157     $object->GenerateCallbackFunctionImplementation($callbackFunction, $enumerations, $dictionaries);
158 }
159
160 sub GenerateInterface
161 {
162     my ($object, $interface, $defines, $enumerations, $dictionaries) = @_;
163
164     $codeGenerator->LinkOverloadedOperations($interface);
165
166     AddStringifierOperationIfNeeded($interface);
167     AddLegacyCallerOperationIfNeeded($interface);
168
169     if ($interface->isCallback) {
170         $object->GenerateCallbackInterfaceHeader($interface, $enumerations, $dictionaries);
171         $object->GenerateCallbackInterfaceImplementation($interface, $enumerations, $dictionaries);
172     } else {
173         $object->GenerateHeader($interface, $enumerations, $dictionaries);
174         $object->GenerateImplementation($interface, $enumerations, $dictionaries);
175     }
176 }
177
178 sub AddStringifierOperationIfNeeded
179 {
180     my $interface = shift;
181
182     foreach my $property (@{$interface->attributes}, @{$interface->operations}, @{$interface->anonymousOperations}) {
183         next unless $property->isStringifier;
184
185         if (ref($property) eq "IDLAttribute") {
186             assert("stringifier can only be used on attributes with type DOMString or USVString") unless $property->type->name eq "DOMString" || $property->type->name eq "USVString";
187         }
188
189         if (ref($property) eq "IDLOperation") {
190             assert("stringifier can only be used on operations with a return type of DOMString") unless $property->type->name eq "DOMString";
191             assert("stringifier can only be used on operations with zero arguments") unless scalar(@{$property->arguments}) == 0;
192
193             # Don't duplicate the operation if it was declared with the name 'toString'.
194             return if $property->name eq "toString";
195         }
196
197         my $stringifier = IDLOperation->new();
198         $stringifier->name("toString");
199         $stringifier->type(IDLParser::cloneType($property->type));
200         $stringifier->isStringifier(1);
201
202         IDLParser::copyExtendedAttributes($stringifier->extendedAttributes, $property->extendedAttributes);
203
204         if ($property->name && !$stringifier->extendedAttributes->{ImplementedAs}) {
205             $stringifier->extendedAttributes->{ImplementedAs} = $property->name;
206         }
207
208         # If the stringifier was declared as read-write attribute and had [CEReactions], we need to remove
209         # it from the operation, as the operation should act like attribute getter, which doesn't respect
210         # [CEReactions].
211         if (ref($property) eq "IDLAttribute" && !$property->isReadOnly && $stringifier->extendedAttributes->{CEReactions}) {
212              delete $stringifier->extendedAttributes->{CEReactions};
213         }
214
215         push(@{$interface->operations}, $stringifier);
216         return;
217     }
218 }
219
220 sub AddLegacyCallerOperationIfNeeded
221 {
222     my $interface = shift;
223
224     foreach my $operation (@{$interface->operations}, @{$interface->anonymousOperations}) {
225         my $isLegacyCaller = grep { $_ eq "legacycaller" } @{$operation->specials};
226         if ($isLegacyCaller) {
227             $interface->{LegacyCallers} = [] if !exists $interface->{LegacyCallers};
228
229             my $clonedOperation = IDLParser::cloneOperation($operation);
230             push(@{$interface->{LegacyCallers}}, $clonedOperation);
231     
232             $clonedOperation->{overloads} = $interface->{LegacyCallers};
233             $clonedOperation->{overloadIndex} = @{$interface->{LegacyCallers}};
234         }
235     }
236 }
237
238 sub EventHandlerAttributeEventName
239 {
240     my $attribute = shift;
241     my $eventType = $attribute->extendedAttributes->{ImplementedAs} || $attribute->name;
242
243     # Remove the "on" prefix.
244     $eventType = substr($eventType, 2);
245
246     return "eventNames().${eventType}Event";
247 }
248
249 sub GetParentClassName
250 {
251     my $interface = shift;
252
253     return $interface->extendedAttributes->{JSLegacyParent} if $interface->extendedAttributes->{JSLegacyParent};
254     return "JSDOMObject" unless NeedsImplementationClass($interface);
255     return "JSDOMWrapper<" . GetImplClassName($interface) . ">" unless $interface->parentType;
256     return "JS" . $interface->parentType->name;
257 }
258
259 sub GetCallbackClassName
260 {
261     my $className = shift;
262
263     return "JS$className";
264 }
265
266 sub 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("<runtime/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" || $codeGenerator->InheritsInterface($interface, "WorkerGlobalScope") || $interface->type->name eq "TestGlobalObject";
501 }
502
503 sub ShouldUseGlobalObjectPrototype
504 {
505     my $interface = shift;
506
507     # For workers, the global object is a DedicatedWorkerGlobalScope.
508     return 0 if $interface->type->name eq "WorkerGlobalScope";
509
510     return IsDOMGlobalObject($interface);
511 }
512
513 sub GenerateIndexedGetter
514 {
515     my ($interface, $indexedGetterOperation, $indexExpression) = @_;
516     
517     # NOTE: This abstractly implements steps 1.2.1 - 1.2.8 of the LegacyPlatformObjectGetOwnProperty
518     #       algorithm. Returing the conversion expression and attributes expression for use
519     #       by the caller.
520     
521     # 1.2.1 Let operation be the operation used to declare the indexed property getter.
522     # 1.2.2 Let value be an uninitialized variable.
523     # 1.2.3 If operation was defined without an identifier, then set value to the result
524     #       of performing the steps listed in the interface description to determine the
525     #       value of an indexed property with index as the index.
526     # 1.2.4 Otherwise, operation was defined with an identifier. Set value to the result
527     #       of performing the steps listed in the description of operation with index as
528     #       the only argument value.
529     # 1.2.5 Let desc be a newly created Property Descriptor with no fields.
530     # 1.2.6 Set desc.[[Value]] to the result of converting value to an ECMAScript value.
531     # 1.2.7 If O implements an interface with an indexed property setter, then set
532     #       desc.[[Writable]] to true, otherwise set it to false.
533     # 1.2.8 Return desc.
534     
535     my @attributes = ();
536     push(@attributes, "ReadOnly") if !GetIndexedSetterOperation($interface) && !$interface->extendedAttributes->{Plugin};
537     
538     my $attributeString = ((@attributes > 0) ? join(" | ", @attributes) : "0");
539
540     my $indexedGetterFunctionName = $indexedGetterOperation->extendedAttributes->{ImplementedAs} || $indexedGetterOperation->name || "item";
541     my $nativeToJSConversion = NativeToJSValueUsingPointers($indexedGetterOperation, $interface, "thisObject->wrapped().${indexedGetterFunctionName}(${indexExpression})", "*thisObject->globalObject()");
542     
543     return ($nativeToJSConversion, $attributeString);
544 }
545
546 sub GenerateNamedGetter
547 {
548     my ($interface, $namedGetterOperation, $namedPropertyExpression) = @_;
549     
550     # NOTE: This abstractly implements steps 2.1 - 2.10 of the LegacyPlatformObjectGetOwnProperty
551     #       algorithm. Returing the conversion expression and attributes expression for use
552     #       by the caller.
553     
554     # 2.1  Let operation be the operation used to declare the named property getter.
555     # 2.2  Let value be an uninitialized variable.
556     # 2.3  If operation was defined without an identifier, then set value to the result
557     #      of performing the steps listed in the interface description to determine the
558     #      value of a named property with P as the name.
559     # 2.4  Otherwise, operation was defined with an identifier. Set value to the result
560     #      of performing the steps listed in the description of operation with P as the
561     #      only argument value..
562     # 2.5  Let desc be a newly created Property Descriptor with no fields.
563     # 2.6  Set desc.[[Value]] to the result of converting value to an ECMAScript value.
564     # 2.7  If O implements an interface with a named property setter, then set desc.[[Writable]]
565     #      to true, otherwise set it to false.
566     # 2.8  If O implements an interface with the [LegacyUnenumerableNamedProperties]
567     #      extended attribute, then set desc.[[Enumerable]] to false, otherwise set it
568     #      to true.
569     # 2.9  Set desc.[[Configurable]] to true.
570     # 2.10 Return desc.
571     
572     my @attributes = ();
573     push(@attributes, "ReadOnly") if !GetNamedSetterOperation($interface) && !$interface->extendedAttributes->{Plugin};
574     push(@attributes, "DontEnum") if $interface->extendedAttributes->{LegacyUnenumerableNamedProperties};
575     
576     my $attributeString = ((@attributes > 0) ? join(" | ", @attributes) : "0");
577     my $nativeToJSConversion = NativeToJSValueUsingPointers($namedGetterOperation, $interface, $namedPropertyExpression, "*thisObject->globalObject()");
578     
579     return ($nativeToJSConversion, $attributeString);
580 }
581
582 sub GenerateNamedGetterLambda
583 {
584     my ($outputArray, $interface, $namedGetterOperation, $namedGetterFunctionName, $IDLType) = @_;
585     
586     # NOTE: Named getters are little odd. To avoid doing duplicate lookups (once when checking if
587     #       the property name is a 'supported property name' and once to get the value) we signal
588     #       that a property is supported by whether or not it is 'null' (where what null means is
589     #       dependant on the IDL type). This is based on the assumption that no named getter will
590     #       ever actually want to return null as an actual return value, which seems like an ok
591     #       assumption to make (should it turn out this doesn't hold in the future, we have lots
592     #       of options; do two lookups, add an extra layer of std::optional, etc.).
593     
594     my $resultType = "typename ${IDLType}::ImplementationType";
595     $resultType = "ExceptionOr<" . $resultType . ">" if $namedGetterOperation->extendedAttributes->{MayThrowException};
596     my $returnType = "std::optional<" . $resultType . ">";
597
598     push(@$outputArray, "    auto getterFunctor = [] (auto& thisObject, auto propertyName) -> ${returnType} {\n");
599
600     my @arguments = GenerateCallWithUsingReferences($namedGetterOperation->extendedAttributes->{CallWith}, $outputArray, "std::nullopt", "thisObject", "        ");
601     push(@arguments, "propertyNameToAtomicString(propertyName)");
602
603     push(@$outputArray, "        auto result = thisObject.wrapped().${namedGetterFunctionName}(" . join(", ", @arguments) . ");\n");
604     
605     if ($namedGetterOperation->extendedAttributes->{MayThrowException}) {
606         push(@$outputArray, "        if (result.hasException())\n");
607         push(@$outputArray, "            return ${resultType} { result.releaseException() };\n");
608         push(@$outputArray, "        if (!${IDLType}::isNullValue(result.returnValue()))\n");
609         push(@$outputArray, "            return ${resultType} { ${IDLType}::extractValueFromNullable(result.releaseReturnValue()) };\n");
610         push(@$outputArray, "        return std::nullopt;\n");
611     } else {
612         push(@$outputArray, "        if (!${IDLType}::isNullValue(result))\n");
613         push(@$outputArray, "            return ${resultType} { ${IDLType}::extractValueFromNullable(result) };\n");
614         push(@$outputArray, "        return std::nullopt;\n");
615     }
616     push(@$outputArray, "    };\n");
617 }
618
619 # https://heycam.github.io/webidl/#legacy-platform-object-getownproperty
620 sub GenerateGetOwnPropertySlot
621 {
622     my ($outputArray, $interface, $className) = @_;
623     
624     return if $interface->extendedAttributes->{CustomGetOwnPropertySlot};
625     
626     push(@$outputArray, "bool ${className}::getOwnPropertySlot(JSObject* object, ExecState* state, PropertyName propertyName, PropertySlot& slot)\n");
627     push(@$outputArray, "{\n");
628     push(@$outputArray, "    auto* thisObject = jsCast<${className}*>(object);\n");
629     push(@$outputArray, "    ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n");
630     
631     my $namedGetterOperation = GetNamedGetterOperation($interface);
632     my $indexedGetterOperation = GetIndexedGetterOperation($interface);
633     
634     if (($namedGetterOperation && $namedGetterOperation->extendedAttributes->{MayThrowException}) || ($indexedGetterOperation && $indexedGetterOperation->extendedAttributes->{MayThrowException})) {
635         push(@$outputArray, "    auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
636     }
637     
638     # NOTE: The alogithm for [[GetOwnProperty]] contains only the following step:
639     # 1. Return LegacyPlatformObjectGetOwnProperty(O, P, false).
640     
641     # Therefore, the following steps are from the LegacyPlatformObjectGetOwnProperty algorithm
642     # https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
643     
644     # 1. If O supports indexed properties and P is an array index property name, then:
645     if ($indexedGetterOperation) {
646         # 1.1. Let index be the result of calling ToUint32(P).
647         push(@$outputArray, "    if (auto index = parseIndex(propertyName)) {\n");
648         
649         # 1.2. If index is a supported property index, then:
650         # FIXME: This should support non-contiguous indices.
651         push(@$outputArray, "        if (index.value() < thisObject->wrapped().length()) {\n");
652         
653         # NOTE: GenerateIndexedGetter implements steps 1.2.1 - 1.2.8.
654         
655         my ($nativeToJSConversion, $attributeString) = GenerateIndexedGetter($interface, $indexedGetterOperation, "index.value()");
656         
657         push(@$outputArray, "            auto value = ${nativeToJSConversion};\n");
658         push(@$outputArray, "            RETURN_IF_EXCEPTION(throwScope, false);\n") if $indexedGetterOperation->extendedAttributes->{MayThrowException};
659         
660         push(@$outputArray, "            slot.setValue(thisObject, ${attributeString}, value);\n");
661         push(@$outputArray, "            return true;\n");
662         
663         push(@$outputArray, "        }\n");
664         
665         # 1.3. Set ignoreNamedProps to true.
666         # NOTE: Setting ignoreNamedProps has the effect of skipping step 2, so we can early return here
667         #       rather than going through the paces of having an actual ignoreNamedProps update.
668         if ($namedGetterOperation || $interface->extendedAttributes->{Plugin}) {
669             push(@$outputArray, "        return JSObject::getOwnPropertySlot(object, state, propertyName, slot);\n");
670         }
671         push(@$outputArray, "    }\n");
672     }
673     
674     # 2. If O supports named properties, the result of running the named property visibility
675     #    algorithm with property name P and object O is true, and ignoreNamedProps is false, then:
676     if ($namedGetterOperation) {
677         # NOTE: ignoreNamedProps is guarenteed to be false here, as it is initially false, and never set
678         #       to true, due to the early return in step 1.3
679         AddToImplIncludes("JSDOMAbstractOperations.h");
680                 
681         my $namedGetterFunctionName = $namedGetterOperation->extendedAttributes->{ImplementedAs} || $namedGetterOperation->name || "namedItem";
682         my $IDLType = GetIDLTypeExcludingNullability($interface, $namedGetterOperation->type);
683         
684         push(@$outputArray, "    using GetterIDLType = ${IDLType};\n");
685         
686         GenerateNamedGetterLambda($outputArray, $interface, $namedGetterOperation, $namedGetterFunctionName, "GetterIDLType");
687         
688         my $overrideBuiltin = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins") ? "OverrideBuiltins::Yes" : "OverrideBuiltins::No";
689         push(@$outputArray, "    if (auto namedProperty = accessVisibleNamedProperty<${overrideBuiltin}>(*state, *thisObject, propertyName, getterFunctor)) {\n");
690         
691         # NOTE: GenerateNamedGetter implements steps 2.1 - 2.10.
692         
693         my ($nativeToJSConversion, $attributeString) = GenerateNamedGetter($interface, $namedGetterOperation, "WTFMove(namedProperty.value())");
694         
695         push(@$outputArray, "        auto value = ${nativeToJSConversion};\n");
696         push(@$outputArray, "        RETURN_IF_EXCEPTION(throwScope, false);\n") if $namedGetterOperation->extendedAttributes->{MayThrowException};
697         
698         push(@$outputArray, "        slot.setValue(thisObject, ${attributeString}, value);\n");
699         push(@$outputArray, "        return true;\n");
700         push(@$outputArray, "    }\n");
701     }
702
703     if ($interface->extendedAttributes->{Plugin}) {
704         AddToImplIncludes("JSPluginElementFunctions.h");
705         push(@$outputArray, "    if (pluginElementCustomGetOwnPropertySlot(thisObject, state, propertyName, slot))\n");
706         push(@$outputArray, "        return true;\n");
707     }
708
709     # 3. Return OrdinaryGetOwnProperty(O, P).
710     push(@$outputArray, "    return JSObject::getOwnPropertySlot(object, state, propertyName, slot);\n");
711     
712     push(@$outputArray, "}\n\n");
713 }
714
715 # https://heycam.github.io/webidl/#legacy-platform-object-getownproperty
716 sub GenerateGetOwnPropertySlotByIndex
717 {
718     my ($outputArray, $interface, $className) = @_;
719     
720     return if $interface->extendedAttributes->{CustomGetOwnPropertySlot};
721
722     # Sink the int-to-string conversion that happens when we create a PropertyName
723     # to the point where we actually need it.
724     my $didGeneratePropertyName = 0;
725     my $propertyNameGeneration = sub {
726         return if $didGeneratePropertyName;
727         
728         push(@$outputArray, "    auto propertyName = Identifier::from(state, index);\n");
729         $didGeneratePropertyName = 1;
730     };
731     
732     push(@$outputArray, "bool ${className}::getOwnPropertySlotByIndex(JSObject* object, ExecState* state, unsigned index, PropertySlot& slot)\n");
733     push(@$outputArray, "{\n");
734     push(@$outputArray, "    auto* thisObject = jsCast<${className}*>(object);\n");
735     push(@$outputArray, "    ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n");
736     
737     my $namedGetterOperation = GetNamedGetterOperation($interface);
738     my $indexedGetterOperation = GetIndexedGetterOperation($interface);
739     
740     if (($namedGetterOperation && $namedGetterOperation->extendedAttributes->{MayThrowException}) || ($indexedGetterOperation && $indexedGetterOperation->extendedAttributes->{MayThrowException})) {
741         push(@$outputArray, "    auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
742     }
743     
744     # NOTE: The alogithm for [[GetOwnProperty]] contains only the following step:
745     # 1. Return LegacyPlatformObjectGetOwnProperty(O, P, false).
746     
747     # Therefore, the following steps are from the LegacyPlatformObjectGetOwnProperty algorithm
748     # https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
749     
750     # 1. If O supports indexed properties and P is an array index property name, then:
751     if ($indexedGetterOperation) {
752         # 1.1. Let index be the result of calling ToUint32(P).
753         push(@$outputArray, "    if (LIKELY(index <= MAX_ARRAY_INDEX)) {\n");
754         
755         # 1.2. If index is a supported property index, then:
756         # FIXME: This should support non-contiguous indices.
757         push(@$outputArray, "        if (index < thisObject->wrapped().length()) {\n");
758         
759         # NOTE: GenerateIndexedGetter implements steps 1.2.1 - 1.2.8.
760         
761         my ($nativeToJSConversion, $attributeString) = GenerateIndexedGetter($interface, $indexedGetterOperation, "index");
762         
763         push(@$outputArray, "            auto value = ${nativeToJSConversion};\n");
764         push(@$outputArray, "            RETURN_IF_EXCEPTION(throwScope, false);\n") if $indexedGetterOperation->extendedAttributes->{MayThrowException};
765         
766         push(@$outputArray, "            slot.setValue(thisObject, ${attributeString}, value);\n");
767         push(@$outputArray, "            return true;\n");
768         
769         push(@$outputArray, "        }\n");
770         
771         # 1.3. Set ignoreNamedProps to true.
772         # NOTE: Setting ignoreNamedProps has the effect of skipping step 2, so we can early return here
773         #       rather than going through the paces of having an actual ignoreNamedProps update.
774         if ($namedGetterOperation || $interface->extendedAttributes->{Plugin}) {
775             push(@$outputArray, "        return JSObject::getOwnPropertySlotByIndex(object, state, index, slot);\n");
776         }
777         push(@$outputArray, "    }\n");
778     }
779     
780     # 2. If O supports named properties, the result of running the named property visibility
781     #    algorithm with property name P and object O is true, and ignoreNamedProps is false, then:
782     if ($namedGetterOperation) {
783         # NOTE: ignoreNamedProps is guarenteed to be false here, as it is initially false, and never set
784         #       to true, due to the early return in step 1.3
785         AddToImplIncludes("JSDOMAbstractOperations.h");
786                 
787         &$propertyNameGeneration();
788         
789         my $namedGetterFunctionName = $namedGetterOperation->extendedAttributes->{ImplementedAs} || $namedGetterOperation->name || "namedItem";
790         my $IDLType = GetIDLTypeExcludingNullability($interface, $namedGetterOperation->type);
791         
792         push(@$outputArray, "    using GetterIDLType = ${IDLType};\n");
793         
794         GenerateNamedGetterLambda($outputArray, $interface, $namedGetterOperation, $namedGetterFunctionName, "GetterIDLType");
795         
796         my $overrideBuiltin = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins") ? "OverrideBuiltins::Yes" : "OverrideBuiltins::No";
797         push(@$outputArray, "    if (auto namedProperty = accessVisibleNamedProperty<${overrideBuiltin}>(*state, *thisObject, propertyName, getterFunctor)) {\n");
798         
799         # NOTE: GenerateNamedGetter implements steps 2.1 - 2.10.
800         
801         my ($nativeToJSConversion, $attributeString) = GenerateNamedGetter($interface, $namedGetterOperation, "WTFMove(namedProperty.value())");
802
803         push(@$outputArray, "        auto value = ${nativeToJSConversion};\n");
804         push(@$outputArray, "        RETURN_IF_EXCEPTION(throwScope, false);\n") if $namedGetterOperation->extendedAttributes->{MayThrowException};
805         
806         push(@$outputArray, "        slot.setValue(thisObject, ${attributeString}, value);\n");
807         push(@$outputArray, "        return true;\n");
808         push(@$outputArray, "    }\n");
809     }
810     
811     if ($interface->extendedAttributes->{Plugin}) {
812         &$propertyNameGeneration();
813
814         AddToImplIncludes("JSPluginElementFunctions.h");
815         push(@$outputArray, "    if (pluginElementCustomGetOwnPropertySlot(thisObject, state, propertyName, slot))\n");
816         push(@$outputArray, "        return true;\n");
817     }
818
819     # 3. Return OrdinaryGetOwnProperty(O, P).
820     push(@$outputArray, "    return JSObject::getOwnPropertySlotByIndex(object, state, index, slot);\n");
821     
822     push(@$outputArray, "}\n\n");
823 }
824
825 # https://heycam.github.io/webidl/#legacy-platform-object-property-enumeration
826 sub GenerateGetOwnPropertyNames
827 {
828     my ($outputArray, $interface, $className) = @_;
829     
830     return if $interface->extendedAttributes->{CustomGetOwnPropertyNames};
831     
832     my $namedGetterOperation = GetNamedGetterOperation($interface);
833     my $indexedGetterOperation = GetIndexedGetterOperation($interface);
834     
835     push(@$outputArray, "void ${className}::getOwnPropertyNames(JSObject* object, ExecState* state, PropertyNameArray& propertyNames, EnumerationMode mode)\n");
836     push(@$outputArray, "{\n");
837     push(@$outputArray, "    auto* thisObject = jsCast<${className}*>(object);\n");
838     push(@$outputArray, "    ASSERT_GC_OBJECT_INHERITS(object, info());\n");
839     
840     # 1. If the object supports indexed properties, then the object’s supported
841     #    property indices are enumerated first, in numerical order.
842     # FIXME: This should support non-contiguous indices.
843     if ($indexedGetterOperation) {
844         push(@$outputArray, "    for (unsigned i = 0, count = thisObject->wrapped().length(); i < count; ++i)\n");
845         push(@$outputArray, "        propertyNames.add(Identifier::from(state, i));\n");
846     }
847
848     # 2. If the object supports named properties and doesn’t implement an interface
849     #    with the [LegacyUnenumerableNamedProperties] extended attribute, then the
850     #    object’s supported property names that are visible according to the named
851     #    property visibility algorithm are enumerated next, in the order given in
852     #    the definition of the set of supported property names.
853     if ($namedGetterOperation) {
854         if (!$interface->extendedAttributes->{LegacyUnenumerableNamedProperties}) {
855             push(@$outputArray, "    for (auto& propertyName : thisObject->wrapped().supportedPropertyNames())\n");
856             push(@$outputArray, "        propertyNames.add(Identifier::fromString(state, propertyName));\n");
857         } else {
858             push(@$outputArray, "    if (mode.includeDontEnumProperties()) {\n");
859             push(@$outputArray, "        for (auto& propertyName : thisObject->wrapped().supportedPropertyNames())\n");
860             push(@$outputArray, "            propertyNames.add(Identifier::fromString(state, propertyName));\n");
861             push(@$outputArray, "    }\n");
862         }
863     }
864     
865     # 3. Finally, any enumerable own properties or properties from the object’s
866     #    prototype chain are then enumerated, in no defined order.
867     push(@$outputArray, "    JSObject::getOwnPropertyNames(object, state, propertyNames, mode);\n");
868     push(@$outputArray, "}\n\n");
869 }
870
871 # https://heycam.github.io/webidl/#invoke-indexed-setter
872 sub GenerateInvokeIndexedPropertySetter
873 {
874     my ($outputArray, $indent, $interface, $indexedSetterOperation, $indexExpression, $value) = @_;
875     
876     # The second argument of the indexed setter operation is the argument being converted.
877     my $argument = @{$indexedSetterOperation->arguments}[1];
878     my $nativeValue = JSValueToNative($interface, $argument, $value, $indexedSetterOperation->extendedAttributes->{Conditional}, "state", "*state", "thisObject", "", "");
879     
880     push(@$outputArray, $indent . "auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
881     push(@$outputArray, $indent . "auto nativeValue = ${nativeValue};\n");
882     push(@$outputArray, $indent . "RETURN_IF_EXCEPTION(throwScope, true);\n");
883     
884     my $indexedSetterFunctionName = $indexedSetterOperation->name || "setItem";
885     my $nativeValuePassExpression = PassArgumentExpression("nativeValue", $argument);
886     my $functionString = "thisObject->wrapped().${indexedSetterFunctionName}(${indexExpression}, ${nativeValuePassExpression})";
887     $functionString = "propagateException(*state, throwScope, ${functionString})" if NeedsExplicitPropagateExceptionCall($indexedSetterOperation);
888     
889     push(@$outputArray, $indent . $functionString . ";\n");
890 }
891
892 # https://heycam.github.io/webidl/#invoke-named-setter
893 sub GenerateInvokeNamedPropertySetter
894 {
895     my ($outputArray, $indent, $interface, $namedSetterOperation, $value) = @_;
896     
897     my $argument = @{$namedSetterOperation->arguments}[1];
898     my $nativeValue = JSValueToNative($interface, $argument, $value, $namedSetterOperation->extendedAttributes->{Conditional}, "state", "*state", "thisObject", "", "");
899     
900     push(@$outputArray, $indent . "auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
901     push(@$outputArray, $indent . "auto nativeValue = ${nativeValue};\n");
902     push(@$outputArray, $indent . "RETURN_IF_EXCEPTION(throwScope, true);\n");
903
904     push(@$outputArray, $indent . "bool isPropertySupported = true;\n") if $namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties};
905
906     my $namedSetterFunctionName = $namedSetterOperation->name || "setNamedItem";
907     my $nativeValuePassExpression = PassArgumentExpression("nativeValue", $argument);
908
909     my @arguments = ();
910     push(@arguments, "propertyNameToString(propertyName)");
911     push(@arguments, $nativeValuePassExpression);
912     push(@arguments, "isPropertySupported") if $namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties};
913
914     my $functionString = "thisObject->wrapped().${namedSetterFunctionName}(" . join(", ", @arguments) . ")";
915     $functionString = "propagateException(*state, throwScope, ${functionString})" if NeedsExplicitPropagateExceptionCall($namedSetterOperation);
916
917     push(@$outputArray, $indent . $functionString . ";\n");
918 }
919
920 sub GeneratePut
921 {
922     my ($outputArray, $interface, $className) = @_;
923     
924     return if $interface->extendedAttributes->{CustomPut};
925     
926     my $namedSetterOperation = GetNamedSetterOperation($interface);
927     my $indexedSetterOperation = GetIndexedSetterOperation($interface);
928     
929     push(@$outputArray, "bool ${className}::put(JSCell* cell, ExecState* state, PropertyName propertyName, JSValue value, PutPropertySlot& putPropertySlot)\n");
930     push(@$outputArray, "{\n");
931     push(@$outputArray, "    auto* thisObject = jsCast<${className}*>(cell);\n");
932     push(@$outputArray, "    ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n\n");
933     
934     if (($namedSetterOperation && $namedSetterOperation->extendedAttributes->{CEReactions}) || ($indexedSetterOperation && $indexedSetterOperation->extendedAttributes->{CEReactions})) {
935         push(@$outputArray, "    CustomElementReactionStack customElementReactionStack;\n\n");
936         AddToImplIncludes("CustomElementReactionQueue.h");
937     }
938     
939     if ($indexedSetterOperation) {
940         push(@$outputArray, "    if (auto index = parseIndex(propertyName)) {\n");
941         
942         GenerateInvokeIndexedPropertySetter($outputArray, "        ", $interface, $indexedSetterOperation, "index.value()", "value");
943         
944         push(@$outputArray, "        return true;\n");
945         push(@$outputArray, "    }\n\n");
946     }
947     
948     if ($namedSetterOperation) {
949         # FIMXE: We need a more comprehensive story for Symbols.
950         push(@$outputArray, "    if (!propertyName.isSymbol()) {\n");
951         
952         my $additionalIndent = "";
953         
954         my $overrideBuiltins = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins");
955         if (!$overrideBuiltins) {
956             push(@$outputArray, "        PropertySlot slot { thisObject, PropertySlot::InternalMethodType::VMInquiry };\n");
957             push(@$outputArray, "        JSValue prototype = thisObject->getPrototypeDirect();\n");
958             push(@$outputArray, "        if (!(prototype.isObject() && asObject(prototype)->getPropertySlot(state, propertyName, slot))) {\n");
959             $additionalIndent .= "    ";
960         }
961
962         GenerateInvokeNamedPropertySetter($outputArray, $additionalIndent . "        ", $interface, $namedSetterOperation, "value");
963         if ($namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties}) {
964             push(@$outputArray, $additionalIndent . "        if (!isPropertySupported)\n");
965             push(@$outputArray, $additionalIndent . "            return JSObject::put(thisObject, state, propertyName, value, putPropertySlot);\n");
966         }
967         push(@$outputArray, $additionalIndent . "        return true;\n");
968
969         if (!$overrideBuiltins) {
970             push(@$outputArray, "        }\n");
971         }
972         
973         push(@$outputArray, "    }\n\n");
974     }
975     
976     assert("Using both a named property setter and [Plugin] together is not supported.") if $namedSetterOperation && $interface->extendedAttributes->{Plugin};
977     if ($interface->extendedAttributes->{Plugin}) {
978         AddToImplIncludes("JSPluginElementFunctions.h");
979
980         push(@$outputArray, "    bool putResult = false;\n");
981         push(@$outputArray, "    if (pluginElementCustomPut(thisObject, state, propertyName, value, putPropertySlot, putResult))\n");
982         push(@$outputArray, "        return putResult;\n\n");
983     }
984
985     push(@$outputArray, "    return JSObject::put(thisObject, state, propertyName, value, putPropertySlot);\n");
986     push(@$outputArray, "}\n\n");
987 }
988
989 sub GeneratePutByIndex
990 {
991     my ($outputArray, $interface, $className) = @_;
992     
993     return if $interface->extendedAttributes->{CustomPut};
994
995     my $namedSetterOperation = GetNamedSetterOperation($interface);
996     my $indexedSetterOperation = GetIndexedSetterOperation($interface);
997     
998     my $overrideBuiltins = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins");
999     my $ellidesCallsToBase = ($namedSetterOperation && $overrideBuiltins) && !$interface->extendedAttributes->{Plugin} && !$namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties};
1000     
1001     push(@$outputArray, "bool ${className}::putByIndex(JSCell* cell, ExecState* state, unsigned index, JSValue value, bool" . (!$ellidesCallsToBase ? " shouldThrow" : "") . ")\n");
1002     push(@$outputArray, "{\n");
1003     push(@$outputArray, "    auto* thisObject = jsCast<${className}*>(cell);\n");
1004     push(@$outputArray, "    ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n\n");
1005     
1006     if (($namedSetterOperation && $namedSetterOperation->extendedAttributes->{CEReactions}) || ($indexedSetterOperation && $indexedSetterOperation->extendedAttributes->{CEReactions})) {
1007         push(@$outputArray, "    CustomElementReactionStack customElementReactionStack;\n\n");
1008         AddToImplIncludes("CustomElementReactionQueue.h");
1009     }
1010     
1011     if ($indexedSetterOperation ) {
1012         push(@$outputArray, "    if (LIKELY(index <= MAX_ARRAY_INDEX)) {\n");
1013         
1014         GenerateInvokeIndexedPropertySetter($outputArray, "        ", $interface, $indexedSetterOperation, "index", "value");
1015         
1016         push(@$outputArray, "        return true;\n");
1017         push(@$outputArray, "    }\n\n");
1018     }
1019     
1020     if ($namedSetterOperation) {
1021         push(@$outputArray, "    auto propertyName = Identifier::from(state, index);\n");
1022                 
1023         my $additionalIndent = "";
1024         if (!$overrideBuiltins) {
1025             push(@$outputArray, "    PropertySlot slot { thisObject, PropertySlot::InternalMethodType::VMInquiry };\n");
1026             push(@$outputArray, "    JSValue prototype = thisObject->getPrototypeDirect();\n");
1027             push(@$outputArray, "    if (!(prototype.isObject() && asObject(prototype)->getPropertySlot(state, propertyName, slot))) {\n");
1028             $additionalIndent .= "    ";
1029         }
1030         
1031         GenerateInvokeNamedPropertySetter($outputArray, $additionalIndent . "    ", $interface, $namedSetterOperation, "value");
1032         if ($namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties}) {
1033             push(@$outputArray, $additionalIndent . "    if (!isPropertySupported)\n");
1034             push(@$outputArray, $additionalIndent . "        return JSObject::putByIndex(cell, state, index, value, shouldThrow);\n");
1035         }
1036         push(@$outputArray, $additionalIndent . "    return true;\n");
1037         
1038         if (!$overrideBuiltins) {
1039             push(@$outputArray, "    }\n\n");
1040         }
1041     }
1042
1043     assert("Using both a named property setter and [Plugin] together is not supported.") if $namedSetterOperation && $interface->extendedAttributes->{Plugin};
1044     if ($interface->extendedAttributes->{Plugin}) {
1045         AddToImplIncludes("JSPluginElementFunctions.h");
1046         push(@$outputArray, "    auto propertyName = Identifier::from(state, index);\n");
1047         push(@$outputArray, "    PutPropertySlot putPropertySlot(thisObject, shouldThrow);\n");
1048         push(@$outputArray, "    bool putResult = false;\n");
1049         push(@$outputArray, "    if (pluginElementCustomPut(thisObject, state, propertyName, value, putPropertySlot, putResult))\n");
1050         push(@$outputArray, "        return putResult;\n\n");
1051     }
1052
1053     if (!$ellidesCallsToBase) {
1054         push(@$outputArray, "    return JSObject::putByIndex(cell, state, index, value, shouldThrow);\n");
1055     }
1056     
1057     push(@$outputArray, "}\n\n");
1058 }
1059
1060 sub GenerateIsUnforgeablePropertyName
1061 {
1062     my ($outputArray, $interface) = @_;
1063     
1064     my @unforgeablePropertyNames = ();
1065     foreach my $property (@{$interface->attributes}, @{$interface->operations}) {
1066         next if $property->isStatic;
1067         
1068         if (IsUnforgeable($interface, $property)) {
1069             push(@unforgeablePropertyNames, $property->name);
1070         }
1071     }
1072     
1073     return 0 if (scalar(@unforgeablePropertyNames) == 0);
1074     
1075     my $condition = join(" || ", map { "propertyName == \"" . $_ . "\"" } @unforgeablePropertyNames);
1076     
1077     push(@$outputArray, "static bool isUnforgeablePropertyName(PropertyName propertyName)\n");
1078     push(@$outputArray, "{\n");
1079     push(@$outputArray, "    return ${condition};\n");
1080     push(@$outputArray, "}\n\n");
1081     
1082     return 1;
1083 }
1084
1085 # https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
1086 sub GenerateDefineOwnProperty
1087 {
1088     my ($outputArray, $interface, $className) = @_;
1089     
1090     return if $interface->extendedAttributes->{CustomDefineOwnProperty};
1091     
1092     my $namedSetterOperation = GetNamedSetterOperation($interface);
1093     my $indexedSetterOperation = GetIndexedSetterOperation($interface);
1094     
1095     return if !$namedSetterOperation && !$indexedSetterOperation;
1096     
1097     push(@$outputArray, "bool ${className}::defineOwnProperty(JSObject* object, ExecState* state, PropertyName propertyName, const PropertyDescriptor& propertyDescriptor, bool shouldThrow)\n");
1098     push(@$outputArray, "{\n");
1099     push(@$outputArray, "    auto* thisObject = jsCast<${className}*>(object);\n");
1100     push(@$outputArray, "    ASSERT_GC_OBJECT_INHERITS(thisObject, info());\n\n");
1101     
1102     if (($namedSetterOperation && $namedSetterOperation->extendedAttributes->{CEReactions}) || ($indexedSetterOperation && $indexedSetterOperation->extendedAttributes->{CEReactions})) {
1103         push(@$outputArray, "    CustomElementReactionStack customElementReactionStack;\n\n");
1104         AddToImplIncludes("CustomElementReactionQueue.h");
1105     }
1106     
1107     # 1. If O supports indexed properties and P is an array index property name, then:
1108     if (GetIndexedGetterOperation($interface)) {
1109         # NOTE: The numbers are out of order because there is no reason doing steps 1, 3, and 4 if there
1110         # is no indexed property setter.
1111
1112         if (!$indexedSetterOperation) {
1113             # 2. If O does not implement an interface with an indexed property setter, then return false.
1114             push(@$outputArray, "    if (parseIndex(propertyName))\n");
1115             push(@$outputArray, "        return false;\n\n");
1116         } else {
1117             push(@$outputArray, "    if (auto index = parseIndex(propertyName)) {\n");
1118
1119             # 1. If the result of calling IsDataDescriptor(Desc) is false, then return false.
1120             push(@$outputArray, "        if (!propertyDescriptor.isDataDescriptor())\n");
1121             push(@$outputArray, "            return false;\n");
1122             
1123             # 3. Invoke the indexed property setter with P and Desc.[[Value]].
1124             GenerateInvokeIndexedPropertySetter($outputArray, "        ", $interface, $indexedSetterOperation, "index.value()", "propertyDescriptor.value()");
1125             
1126             # 4. Return true.
1127             push(@$outputArray, "        return true;\n");
1128             push(@$outputArray, "    }\n\n");
1129         }
1130     }
1131     
1132     # 2. If O supports named properties, O does not implement an interface with the [Global] or [PrimaryGlobal]
1133     #    extended attribute and P is not an unforgeable property name of O, then:
1134     if (GetNamedGetterOperation($interface) && !IsGlobalOrPrimaryGlobalInterface($interface)) {
1135         # FIMXE: We need a more comprehensive story for Symbols.
1136         push(@$outputArray, "    if (!propertyName.isSymbol()) {\n");
1137         
1138         my $additionalIndent = "";
1139         
1140         my $hasUnforgableProperties = GenerateIsUnforgeablePropertyName($outputArray, $interface);
1141         if ($hasUnforgableProperties) {
1142             push(@$outputArray, "        if (!isUnforgeablePropertyName(propertyName)) {\n");
1143             $additionalIndent .= "    ";
1144         }
1145         
1146         # 1. Let creating be true if P is not a supported property name, and false otherwise.
1147         # NOTE: This step is strength reduced into the only use of 'creating' in step 2.2.1
1148         
1149         # 2. If O implements an interface with the [OverrideBuiltins] extended attribute or O
1150         #    does not have an own property named P, then:
1151         my $overrideBuiltins = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins");
1152         if (!$overrideBuiltins) {
1153             # FIXME: Is JSObject::getOwnPropertySlot the right function to call? Is there a function that will
1154             #        only look at the actual properties, and not call into our implementation of the
1155             #        [[GetOwnProperty]] hook?
1156             push(@$outputArray, $additionalIndent. "        PropertySlot slot { thisObject, PropertySlot::InternalMethodType::VMInquiry };\n");
1157             push(@$outputArray, $additionalIndent. "        if (!JSObject::getOwnPropertySlot(thisObject, state, propertyName, slot)) {\n");
1158             $additionalIndent .= "    ";
1159         }
1160         if (!$namedSetterOperation) {
1161             # 2.1. If creating is false and O does not implement an interface with a named property setter, then return false.
1162             push(@$outputArray, $additionalIndent . "        if (thisObject->wrapped().isSupportedPropertyName(propertyNameToString(propertyName)))\n");
1163             push(@$outputArray, $additionalIndent . "            return false;\n");
1164         } else {
1165             # 2.2. If O implements an interface with a named property setter, then:
1166             
1167             # 2.2.1. If the result of calling IsDataDescriptor(Desc) is false, then return false.
1168             push(@$outputArray, $additionalIndent . "        if (!propertyDescriptor.isDataDescriptor())\n");
1169             push(@$outputArray, $additionalIndent . "            return false;\n");
1170             
1171             # 2.2.2. Invoke the named property setter with P and Desc.[[Value]].
1172             GenerateInvokeNamedPropertySetter($outputArray, $additionalIndent . "        ", $interface, $namedSetterOperation, "propertyDescriptor.value()");
1173             if ($namedSetterOperation->extendedAttributes->{CallNamedSetterOnlyForSupportedProperties}) {
1174                 push(@$outputArray, $additionalIndent . "    if (!isPropertySupported)\n");
1175                 push(@$outputArray, $additionalIndent . "        return JSObject::defineOwnProperty(object, state, propertyName, propertyDescriptor, shouldThrow);\n");
1176             }
1177             # 2.2.3. Return true.
1178             push(@$outputArray, $additionalIndent . "        return true;\n");
1179         }
1180         
1181         if (!$overrideBuiltins) {
1182             push(@$outputArray, $additionalIndent . "    }\n");
1183         }
1184         
1185         if ($hasUnforgableProperties) {
1186             push(@$outputArray, "        }\n");
1187         }
1188         
1189         # Close the !propertyName.isSymbol() condition.
1190         push(@$outputArray, "    }\n\n");
1191     }
1192     
1193     push(@$outputArray, "    PropertyDescriptor newPropertyDescriptor = propertyDescriptor;\n");
1194         
1195     # 3. If O does not implement an interface with the [Global] or [PrimaryGlobal] extended attribute,
1196     #    then set Desc.[[Configurable]] to true.
1197     if (!IsGlobalOrPrimaryGlobalInterface($interface)) {
1198         push(@$outputArray, "    newPropertyDescriptor.setConfigurable(true);\n");
1199     }
1200     
1201     # 4. Return OrdinaryDefineOwnProperty(O, P, Desc).
1202     # FIXME: Does this do the same thing?
1203     push(@$outputArray, "    return JSObject::defineOwnProperty(object, state, propertyName, newPropertyDescriptor, shouldThrow);\n");
1204     
1205     push(@$outputArray, "}\n\n");
1206 }
1207
1208 sub GenerateDeletePropertyCommon
1209 {
1210     my ($outputArray, $interface, $className, $operation, $conditional) = @_;
1211     
1212     # This implements step 2 of https://heycam.github.io/webidl/#legacy-platform-object-delete
1213     # so it can be shared between the generation of deleteProperty and deletePropertyByIndex.
1214
1215     # 2. If O supports named properties, O does not implement an interface with the
1216     #    [Global] or [PrimaryGlobal] extended attribute and the result of calling the
1217     #    named property visibility algorithm with property name P and object O is true,
1218     #    then:
1219     assert("Named property deleters are not allowed without a corresponding named property getter.") if !GetNamedGetterOperation($interface);
1220     assert("Named property deleters are not allowed on global object interfaces.") if IsGlobalOrPrimaryGlobalInterface($interface);
1221
1222     AddToImplIncludes("JSDOMAbstractOperations.h", $conditional);
1223     my $overrideBuiltin = $codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins") ? "OverrideBuiltins::Yes" : "OverrideBuiltins::No";
1224     push(@$outputArray, "    if (isVisibleNamedProperty<${overrideBuiltin}>(*state, thisObject, propertyName)) {\n");
1225
1226     if ($operation->extendedAttributes->{CEReactions}) {
1227         push(@$outputArray, "        CustomElementReactionStack customElementReactionStack;\n");
1228         AddToImplIncludes("CustomElementReactionQueue.h", $conditional);
1229     }
1230
1231     # 2.1. If O does not implement an interface with a named property deleter, then return false.
1232     # 2.2. Let operation be the operation used to declare the named property deleter.
1233     # NOTE: We only add a deleteProperty implementation of we have a named property deleter.
1234
1235     # 2.3. If operation was defined without an identifier, then:
1236     #      1. Perform the steps listed in the interface description to delete an existing named
1237     #         property with P as the name.
1238     #      2. If the steps indicated that the deletion failed, then return false.
1239     # 2.4. Otherwise, operation was defined with an identifier:
1240     #      1. Perform the steps listed in the description of operation with P as the only argument
1241     #         value.
1242     #      2. If operation was declared with a return type of boolean and the steps returned false,
1243     #         then return false.
1244
1245     my $functionImplementationName = $operation->extendedAttributes->{ImplementedAs} || $codeGenerator->WK_lcfirst($operation->name) || "deleteNamedProperty";
1246     my $functionCall = "impl." . $functionImplementationName . "(propertyNameToString(propertyName))";
1247
1248     # NOTE: We expect the implementation function of named deleters without an identifier to
1249     #       return either bool or ExceptionOr<bool>. the implementation function of named deleters
1250     #       with an identifier have no restriction, but if the return value of the operation is
1251     #       boolean, we return that value, otherwise it is ignored (as per section 4.2).
1252
1253     if ($operation->extendedAttributes->{MayThrowException}) {
1254         push(@$outputArray, "        auto result = ${functionCall};\n");
1255         push(@$outputArray, "        if (result.hasException()) {\n");
1256         push(@$outputArray, "            auto throwScope = DECLARE_THROW_SCOPE(state->vm());\n");
1257         push(@$outputArray, "            propagateException(*state, throwScope, result.releaseException());\n");
1258         push(@$outputArray, "            return true;\n");
1259         push(@$outputArray, "        }\n\n");
1260
1261         if (!$operation->name || $operation->name && $operation->type->name eq "boolean") {
1262             push(@$outputArray, "        return result.releaseReturnValue();\n");
1263         } else {
1264             push(@$outputArray, "        return true;\n");
1265         }
1266     } else {
1267         if (!$operation->name || $operation->name && $operation->type->name eq "boolean") {
1268             push(@$outputArray, "        return ${functionCall};\n");
1269         } else {
1270             push(@$outputArray, "        ${functionCall};\n");
1271             push(@$outputArray, "        return true;\n");
1272         }
1273     }
1274
1275     push(@$outputArray, "    }\n");
1276 }
1277
1278 sub GenerateDeleteProperty
1279 {
1280     my ($outputArray, $interface, $className, $operation, $conditional) = @_;
1281
1282     # This implements https://heycam.github.io/webidl/#legacy-platform-object-delete for the
1283     # for the deleteProperty override hook.
1284
1285     push(@$outputArray, "bool ${className}::deleteProperty(JSCell* cell, ExecState* state, PropertyName propertyName)\n");
1286     push(@$outputArray, "{\n");
1287
1288     push(@$outputArray, "    auto& thisObject = *jsCast<${className}*>(cell);\n");
1289     push(@$outputArray, "    auto& impl = thisObject.wrapped();\n");
1290
1291     # 1. If O supports indexed properties and P is an array index property name, then:
1292     #    1. Let index be the result of calling ToUint32(P).
1293     #    2. If index is not a supported property index, then return true.
1294     #    3. Return false.
1295     if (GetIndexedGetterOperation($interface)) {
1296         push(@$outputArray, "    if (auto index = parseIndex(propertyName))\n");
1297         push(@$outputArray, "        return !impl.isSupportedPropertyIndex(index.value());\n");
1298     }
1299
1300     # GenerateDeletePropertyCommon implements step 2.
1301     GenerateDeletePropertyCommon($outputArray, $interface, $className, $operation, $conditional);
1302
1303     # FIXME: Instead of calling down JSObject::deleteProperty, perhaps we should implement
1304     # the remained of the algorithm ourselves.
1305     push(@$outputArray, "    return JSObject::deleteProperty(cell, state, propertyName);\n");
1306     push(@$outputArray, "}\n\n");
1307 }
1308
1309 sub GenerateDeletePropertyByIndex
1310 {
1311     my ($outputArray, $interface, $className, $operation, $conditional) = @_;
1312
1313     # This implements https://heycam.github.io/webidl/#legacy-platform-object-delete for the
1314     # for the deletePropertyByIndex override hook.
1315
1316     push(@$outputArray, "bool ${className}::deletePropertyByIndex(JSCell* cell, ExecState* state, unsigned index)\n");
1317     push(@$outputArray, "{\n");
1318
1319     push(@$outputArray, "    auto& thisObject = *jsCast<${className}*>(cell);\n");
1320     push(@$outputArray, "    auto& impl = thisObject.wrapped();\n");
1321
1322     # 1. If O supports indexed properties and P is an array index property name, then:
1323     #    1. Let index be the result of calling ToUint32(P).
1324     #    2. If index is not a supported property index, then return true.
1325     #    3. Return false.
1326
1327     # NOTE: For deletePropertyByIndex, if there is an indexed getter, checking isSupportedPropertyIndex()
1328     #       is all that needs to be done, no need to generate the .
1329
1330     if (GetIndexedGetterOperation($interface)) {
1331         push(@$outputArray, "    return !impl.isSupportedPropertyIndex(index);\n");
1332     } else {
1333         push(@$outputArray, "    auto propertyName = Identifier::from(state, index);\n");
1334
1335         # GenerateDeletePropertyCommon implements step 2.
1336         GenerateDeletePropertyCommon($outputArray, $interface, $className, $operation, $conditional);
1337
1338         # FIXME: Instead of calling down JSObject::deletePropertyByIndex, perhaps we should implement
1339         # the remaineder of the algoritm (steps 3 and 4) ourselves.
1340         
1341         # 3. If O has an own property with name P, then:
1342         #    1. If the property is not configurable, then return false.
1343         #    2. Otherwise, remove the property from O.
1344         # 3. Return true.
1345         
1346         push(@$outputArray, "    return JSObject::deletePropertyByIndex(cell, state, index);\n");
1347     }
1348
1349     push(@$outputArray, "}\n\n");
1350 }
1351
1352
1353 sub GenerateNamedDeleterDefinition
1354 {
1355     my ($outputArray, $interface, $className) = @_;
1356     
1357     return if $interface->extendedAttributes->{CustomDeleteProperty};
1358
1359     my $namedDeleterOperation = GetNamedDeleterOperation($interface);
1360     
1361     # This implements https://heycam.github.io/webidl/#legacy-platform-object-delete using
1362     # the deleteProperty and deletePropertyByIndex override hooks.
1363
1364     assert("Named property deleters are not allowed without a corresponding named property getter.") if !GetNamedGetterOperation($interface);
1365     assert("Named property deleters are not allowed on global object interfaces.") if IsGlobalOrPrimaryGlobalInterface($interface);
1366
1367     my $conditional = $namedDeleterOperation->extendedAttributes->{Conditional};
1368     if ($conditional) {
1369         my $conditionalString = $codeGenerator->GenerateConditionalStringFromAttributeValue($conditional);
1370         push(@$outputArray, "#if ${conditionalString}\n\n");;
1371     }
1372
1373     GenerateDeleteProperty($outputArray, $interface, $className, $namedDeleterOperation, $conditional);
1374     GenerateDeletePropertyByIndex($outputArray, $interface, $className, $namedDeleterOperation, $conditional);
1375
1376     push(@implContent, "#endif\n\n") if $conditional;
1377 }
1378
1379 sub GenerateHeaderContentHeader
1380 {
1381     my $interface = shift;
1382     my $className = "JS" . $interface->type->name;
1383
1384     my @headerContentHeader;
1385     if ($interface->extendedAttributes->{AppleCopyright}) {
1386         @headerContentHeader = split("\r", $beginAppleCopyrightForHeaderFiles);
1387     } else {
1388         @headerContentHeader = split("\r", $headerTemplate);
1389     }
1390
1391     push(@headerContentHeader, "\n#pragma once\n\n");
1392
1393     my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
1394     push(@headerContentHeader, "#if ${conditionalString}\n\n") if $conditionalString;
1395     return @headerContentHeader;
1396 }
1397
1398 sub GenerateImplementationContentHeader
1399 {
1400     my $interface = shift;
1401     my $className = "JS" . $interface->type->name;
1402
1403     my @implContentHeader;
1404     if ($interface->extendedAttributes->{AppleCopyright}) {
1405         @implContentHeader = split("\r", $beginAppleCopyrightForSourceFiles);
1406     } else {
1407         @implContentHeader = split("\r", $headerTemplate);
1408     }
1409
1410     push(@implContentHeader, "\n#include \"config.h\"\n");
1411     my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
1412     push(@implContentHeader, "\n#if ${conditionalString}\n\n") if $conditionalString;
1413     push(@implContentHeader, "#include \"$className.h\"\n\n");
1414     return @implContentHeader;
1415 }
1416
1417 sub NeedsImplementationClass
1418 {
1419     my ($interface) = @_;
1420
1421     return 0 if $interface->extendedAttributes->{JSBuiltin};
1422     return 1;
1423 }
1424
1425 sub ShouldGenerateToWrapped
1426 {
1427     my ($hasParent, $interface) = @_;
1428
1429     return 0 if not NeedsImplementationClass($interface);
1430     return 1 if !$hasParent or $interface->extendedAttributes->{JSGenerateToNativeObject};
1431     return 1 if $interface->parentType && $interface->parentType->name eq "EventTarget";
1432     return 0;
1433 }
1434
1435 sub ShouldGenerateWrapperOwnerCode
1436 {
1437     my ($hasParent, $interface) = @_;
1438
1439     return 0 if not NeedsImplementationClass($interface);
1440     return 1 if !$hasParent;
1441     return 1 if GetGenerateIsReachable($interface);
1442     return 1 if GetCustomIsReachable($interface);
1443     return 1 if $interface->extendedAttributes->{JSCustomFinalize};
1444     return 1 if $codeGenerator->InheritsExtendedAttribute($interface, "ActiveDOMObject");
1445     return 0;
1446 }
1447
1448 sub ShouldGenerateToJSDeclaration
1449 {
1450     my ($hasParent, $interface) = @_;
1451
1452     return 0 if ($interface->extendedAttributes->{SuppressToJSObject});
1453     return 0 if not NeedsImplementationClass($interface);
1454     return 0 if $interface->extendedAttributes->{CustomProxyToJSObject};
1455     return 1 if (!$hasParent or $interface->extendedAttributes->{JSGenerateToJSObject} or $interface->extendedAttributes->{CustomToJSObject});
1456     return 1 if $interface->parentType && $interface->parentType->name eq "EventTarget";
1457     return 1 if $interface->extendedAttributes->{Constructor} or $interface->extendedAttributes->{NamedConstructor};
1458     return 0;
1459 }
1460
1461 sub ShouldGenerateToJSImplementation
1462 {
1463     my ($hasParent, $interface) = @_;
1464
1465     return 0 if not ShouldGenerateToJSDeclaration($hasParent, $interface);
1466     return 1 if not $interface->extendedAttributes->{CustomToJSObject};
1467     return 0;
1468 }
1469
1470 sub GetTypeNameForDisplayInException
1471 {
1472     my ($type) = @_;
1473
1474     # FIXME: Add more type specializations.
1475     return "(" . join(" or ", map { $_->name } GetFlattenedMemberTypes($type)) . ")" if $type->isUnion;
1476     return $type->name;
1477 }
1478
1479 sub GetArgumentExceptionFunction
1480 {
1481     my ($interface, $argument, $argumentIndex, $quotedFunctionName) = @_;
1482
1483     my $name = $argument->name;
1484     my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($interface);
1485     my $typeName = GetTypeNameForDisplayInException($argument->type);
1486
1487     if ($codeGenerator->IsCallbackInterface($argument->type) || $codeGenerator->IsCallbackFunction($argument->type)) {
1488         # FIXME: We should have specialized messages for callback interfaces vs. callback functions.
1489         return "throwArgumentMustBeFunctionError(state, scope, ${argumentIndex}, \"${name}\", \"${visibleInterfaceName}\", ${quotedFunctionName});";
1490     }
1491
1492     if ($codeGenerator->IsWrapperType($argument->type) || $codeGenerator->IsBufferSourceType($argument->type)) {
1493         return "throwArgumentTypeError(state, scope, ${argumentIndex}, \"${name}\", \"${visibleInterfaceName}\", ${quotedFunctionName}, \"${typeName}\");";
1494     }
1495
1496     if ($codeGenerator->IsEnumType($argument->type)) {
1497         my $className = GetEnumerationClassName($argument->type, $interface);
1498         return "throwArgumentMustBeEnumError(state, scope, ${argumentIndex}, \"${name}\", \"${visibleInterfaceName}\", ${quotedFunctionName}, expectedEnumerationValues<${className}>());";
1499     }
1500
1501     return undef;
1502 }
1503
1504 sub GetArgumentExceptionThrower
1505 {
1506     my ($interface, $argument, $argumentIndex, $quotedFunctionName) = @_;
1507
1508     my $functionCall = GetArgumentExceptionFunction($interface, $argument, $argumentIndex, $quotedFunctionName);
1509     return "[](JSC::ExecState& state, JSC::ThrowScope& scope) { " . $functionCall . " }" if $functionCall;
1510 }
1511
1512 sub GetAttributeExceptionFunction
1513 {
1514     my ($interface, $attribute) = @_;
1515     
1516     my $name = $attribute->name;
1517     my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($interface);
1518     my $typeName = GetTypeNameForDisplayInException($attribute->type);
1519
1520     if ($codeGenerator->IsWrapperType($attribute->type) || $codeGenerator->IsBufferSourceType($attribute->type)) {
1521         return "throwAttributeTypeError(state, scope, \"${visibleInterfaceName}\", \"${name}\", \"${typeName}\");";
1522     }
1523 }
1524
1525 sub GetAttributeExceptionThrower
1526 {
1527     my ($interface, $attribute) = @_;
1528
1529     my $functionCall = GetAttributeExceptionFunction($interface, $attribute);
1530     return "[](JSC::ExecState& state, JSC::ThrowScope& scope) { " . $functionCall . " }" if $functionCall;
1531
1532 }
1533
1534 sub PassArgumentExpression
1535 {
1536     my ($name, $context) = @_;
1537
1538     my $type = $context->type;
1539
1540     return "WTFMove(${name})" if $type->isNullable;
1541     
1542     if ($codeGenerator->IsBufferSourceType($type)) {
1543         return "*${name}" if $type->name eq "ArrayBuffer";
1544         return "${name}.releaseNonNull()";
1545     }
1546
1547     return "${name}.releaseNonNull()" if $codeGenerator->IsCallbackInterface($type) || $codeGenerator->IsCallbackFunction($type);
1548     return "*${name}" if $codeGenerator->IsWrapperType($type);
1549     return "WTFMove(${name})";
1550 }
1551
1552 sub GetAttributeGetterName
1553 {
1554     my ($interface, $className, $attribute) = @_;
1555
1556     return $codeGenerator->WK_lcfirst($className) . "Constructor" . $codeGenerator->WK_ucfirst($attribute->name) if $attribute->isStatic;
1557     return GetJSBuiltinFunctionName($className, $attribute) if IsJSBuiltin($interface, $attribute);
1558     return "js" . $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name) . ($codeGenerator->IsConstructorType($attribute->type) ? "Constructor" : "");
1559 }
1560
1561 sub GetAttributeSetterName
1562 {
1563     my ($interface, $className, $attribute) = @_;
1564
1565     return "set" . $codeGenerator->WK_ucfirst($className) . "Constructor" . $codeGenerator->WK_ucfirst($attribute->name) if $attribute->isStatic;
1566     return "set" . $codeGenerator->WK_ucfirst(GetJSBuiltinFunctionName($className, $attribute)) if IsJSBuiltin($interface, $attribute);
1567     return "setJS" . $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name) . ($codeGenerator->IsConstructorType($attribute->type) ? "Constructor" : "");
1568 }
1569
1570 sub GetFunctionName
1571 {
1572     my ($interface, $className, $operation) = @_;
1573
1574     return GetJSBuiltinFunctionName($className, $operation) if IsJSBuiltin($interface, $operation);
1575
1576     my $functionName = $operation->name;
1577     $functionName = "SymbolIterator" if $functionName eq "[Symbol.Iterator]";
1578
1579     my $kind = $operation->isStatic ? "Constructor" : (OperationShouldBeOnInstance($interface, $operation) ? "Instance" : "Prototype");
1580     return $codeGenerator->WK_lcfirst($className) . $kind . "Function" . $codeGenerator->WK_ucfirst($functionName);
1581 }
1582
1583 sub GetFullyQualifiedImplementationCallName
1584 {
1585     my ($interface, $property, $implementationName, $implExpression, $conditional) = @_;
1586     
1587     my $implementedBy = $property->extendedAttributes->{ImplementedBy};
1588     if ($implementedBy) {
1589         AddToImplIncludes("${implementedBy}.h", $conditional);
1590         return "WebCore::${implementedBy}::${implementationName}";
1591     }
1592     
1593     if ($property->isStatic || $property->extendedAttributes->{Constructor} || $property->extendedAttributes->{NamedConstructor}) {
1594         return $interface->type->name . "::${implementationName}";
1595     }
1596     
1597     if ($property->isMapLike) {
1598         return "forward" . $codeGenerator->WK_ucfirst($property->name) . "ToMapLike";
1599     }
1600     
1601     return "${implExpression}.${implementationName}";
1602 }
1603
1604 sub AddAdditionalArgumentsForImplementationCall
1605 {
1606     my ($arguments, $interface, $property, $implExpression, $stateExpression, $thisObjectExpression) = @_;
1607     
1608     if ($property->extendedAttributes->{ImplementedBy} && !$property->isStatic) {
1609         unshift(@$arguments, $implExpression);
1610     }
1611     
1612     if ($property->isMapLike) {
1613         push(@$arguments, $stateExpression);
1614         push(@$arguments, $thisObjectExpression);
1615     }
1616 }
1617
1618 sub GetSpecialAccessorOperationForType
1619 {
1620     my ($interface, $special, $firstParameterType, $numberOfParameters) = @_;
1621
1622     foreach my $operation (@{$interface->operations}, @{$interface->anonymousOperations}) {
1623         my $specials = $operation->specials;
1624         my $specialExists = grep { $_ eq $special } @$specials;
1625         my $arguments = $operation->arguments;
1626         if ($specialExists and scalar(@$arguments) == $numberOfParameters and $arguments->[0]->type->name eq $firstParameterType) {
1627             return $operation;
1628         }
1629     }
1630
1631     return 0;
1632 }
1633
1634 sub IsGlobalOrPrimaryGlobalInterface
1635 {
1636     my $interface = shift;
1637
1638     return $interface->extendedAttributes->{Global} || $interface->extendedAttributes->{PrimaryGlobal};
1639 }
1640
1641 sub AttributeShouldBeOnInstance
1642 {
1643     my $interface = shift;
1644     my $attribute = shift;
1645
1646     return 1 if IsGlobalOrPrimaryGlobalInterface($interface);
1647     return 1 if $codeGenerator->IsConstructorType($attribute->type);
1648
1649     # [Unforgeable] attributes should be on the instance.
1650     # https://heycam.github.io/webidl/#Unforgeable
1651     return 1 if IsUnforgeable($interface, $attribute);
1652
1653     if ($interface->extendedAttributes->{CheckSecurity}) {
1654         return 0 if $attribute->extendedAttributes->{DoNotCheckSecurity};
1655         return 0 if $attribute->extendedAttributes->{DoNotCheckSecurityOnGetter};
1656         return 1;
1657     }
1658
1659     return 0;
1660 }
1661
1662 sub NeedsRuntimeCheck
1663 {
1664     my $interface = shift;
1665     return $interface->extendedAttributes->{EnabledAtRuntime}
1666         || $interface->extendedAttributes->{EnabledForWorld}
1667         || $interface->extendedAttributes->{SecureContext};
1668 }
1669
1670 # https://heycam.github.io/webidl/#es-operations
1671 sub OperationShouldBeOnInstance
1672 {
1673     my ($interface, $operation) = @_;
1674
1675     return 1 if IsGlobalOrPrimaryGlobalInterface($interface);
1676
1677     # FIXME: The bindings generator does not support putting runtime-enabled operations on the instance yet (except for global objects).
1678     return 0 if NeedsRuntimeCheck($operation);
1679
1680     # [Unforgeable] operations should be on the instance. https://heycam.github.io/webidl/#Unforgeable
1681     return 1 if IsUnforgeable($interface, $operation);
1682
1683     return 0;
1684 }
1685
1686 sub OperationHasForcedReturnValue
1687 {
1688     my ($operation) = @_;
1689
1690     foreach my $argument (@{$operation->arguments}) {
1691         return 1 if $argument->extendedAttributes->{ReturnValue};
1692     }
1693     return 0;
1694 }
1695
1696 sub IsAcceleratedDOMAttribute
1697 {
1698     my ($interface, $attribute) = @_;
1699
1700     # If we use CustomGetterSetter in IDL code generator we cannot skip type check.
1701     return 0 if NeedsRuntimeCheck($attribute) and AttributeShouldBeOnInstance($interface, $attribute);
1702     return 0 if $attribute->extendedAttributes->{PrivateIdentifier} and AttributeShouldBeOnInstance($interface, $attribute);
1703
1704     # If the interface has special logic for casting we cannot hoist type check to JSC.
1705     return 0 if $interface->extendedAttributes->{ImplicitThis};
1706     return 0 if $interface->extendedAttributes->{CustomProxyToJSObject};
1707
1708     return 0 if $attribute->isStatic;
1709     return 0 if $attribute->isMapLike;
1710     return 0 if $codeGenerator->IsConstructorType($attribute->type);
1711     return 0 if IsJSBuiltin($interface, $attribute);
1712     return 0 if $attribute->extendedAttributes->{LenientThis};
1713     return 0 if $codeGenerator->IsPromiseType($attribute->type);
1714     return 0 if $attribute->extendedAttributes->{DOMJIT};
1715     return 1;
1716 }
1717
1718 sub GetJSCAttributesForAttribute
1719 {
1720     my $interface = shift;
1721     my $attribute = shift;
1722
1723     my @specials = ();
1724     push(@specials, "DontDelete") if IsUnforgeable($interface, $attribute);
1725
1726     # As per Web IDL specification, constructor properties on the ECMAScript global object should not be enumerable.
1727     my $isGlobalConstructor = $codeGenerator->IsConstructorType($attribute->type);
1728     push(@specials, "DontEnum") if ($attribute->extendedAttributes->{NotEnumerable} || $isGlobalConstructor);
1729     push(@specials, "ReadOnly") if IsReadonly($attribute);
1730     push(@specials, "CustomAccessor") unless $isGlobalConstructor or IsJSBuiltin($interface, $attribute);
1731     push(@specials, "DOMAttribute") if IsAcceleratedDOMAttribute($interface, $attribute);
1732     push(@specials, "DOMJITAttribute") if $attribute->extendedAttributes->{DOMJIT};
1733     push(@specials, "Accessor | Builtin") if  IsJSBuiltin($interface, $attribute);
1734     return (@specials > 0) ? join(" | ", @specials) : "0";
1735 }
1736
1737 sub GetIndexedGetterOperation
1738 {
1739     my $interface = shift;
1740     return GetSpecialAccessorOperationForType($interface, "getter", "unsigned long", 1);
1741 }
1742
1743 sub GetIndexedSetterOperation
1744 {
1745     my $interface = shift;
1746     return GetSpecialAccessorOperationForType($interface, "setter", "unsigned long", 2);
1747 }
1748
1749 sub GetNamedGetterOperation
1750 {
1751     my $interface = shift;
1752     return GetSpecialAccessorOperationForType($interface, "getter", "DOMString", 1);
1753 }
1754
1755 sub GetNamedSetterOperation
1756 {
1757     my $interface = shift;
1758     return GetSpecialAccessorOperationForType($interface, "setter", "DOMString", 2);
1759 }
1760
1761 sub GetNamedDeleterOperation
1762 {
1763     my $interface = shift;
1764     return GetSpecialAccessorOperationForType($interface, "deleter", "DOMString", 1);
1765 }
1766
1767 sub InstanceOperationCount
1768 {
1769     my $interface = shift;
1770     my $count = 0;
1771
1772     foreach my $operation (@{$interface->operations}) {
1773         $count++ if OperationShouldBeOnInstance($interface, $operation);
1774     }
1775
1776     return $count;
1777 }
1778
1779 sub PrototypeOperationCount
1780 {
1781     my $interface = shift;
1782     my $count = 0;
1783
1784     foreach my $operation (@{$interface->operations}) {
1785         $count++ if !$operation->isStatic && !OperationShouldBeOnInstance($interface, $operation);
1786     }
1787
1788     $count += scalar @{$interface->iterable->operations} if $interface->iterable;
1789     $count += scalar @{$interface->mapLike->operations} if $interface->mapLike;
1790     $count += scalar @{$interface->serializable->operations} if $interface->serializable;
1791
1792     return $count;
1793 }
1794
1795 sub InstancePropertyCount
1796 {
1797     my $interface = shift;
1798     my $count = 0;
1799     foreach my $attribute (@{$interface->attributes}) {
1800         $count++ if AttributeShouldBeOnInstance($interface, $attribute);
1801     }
1802     $count += InstanceOperationCount($interface);
1803     return $count;
1804 }
1805
1806 sub PrototypePropertyCount
1807 {
1808     my $interface = shift;
1809     my $count = 0;
1810     foreach my $attribute (@{$interface->attributes}) {
1811         $count++ if !AttributeShouldBeOnInstance($interface, $attribute);
1812     }
1813     $count += PrototypeOperationCount($interface);
1814     $count++ if NeedsConstructorProperty($interface);
1815     return $count;
1816 }
1817
1818 sub InstanceOverridesGetOwnPropertySlot
1819 {
1820     my $interface = shift;
1821     return $interface->extendedAttributes->{CustomGetOwnPropertySlot}
1822         || $interface->extendedAttributes->{Plugin}
1823         || GetIndexedGetterOperation($interface)
1824         || GetNamedGetterOperation($interface);
1825 }
1826
1827 sub InstanceOverridesGetOwnPropertyNames
1828 {
1829     my $interface = shift;
1830     return $interface->extendedAttributes->{CustomGetOwnPropertyNames}
1831         || GetIndexedGetterOperation($interface)
1832         || GetNamedGetterOperation($interface);
1833 }
1834
1835 sub InstanceOverridesPut
1836 {
1837     my $interface = shift;
1838     return $interface->extendedAttributes->{CustomPut}
1839         || $interface->extendedAttributes->{Plugin}
1840         || GetIndexedSetterOperation($interface)
1841         || GetNamedSetterOperation($interface);
1842 }
1843
1844 sub InstanceOverridesDefineOwnProperty
1845 {
1846     my $interface = shift;
1847
1848     return 0 if $interface->extendedAttributes->{DefaultDefineOwnProperty};
1849
1850     return $interface->extendedAttributes->{CustomDefineOwnProperty}
1851         || GetIndexedSetterOperation($interface)
1852         || GetNamedSetterOperation($interface);
1853 }
1854
1855 sub InstanceOverridesDeleteProperty
1856 {
1857     my $interface = shift;
1858     return $interface->extendedAttributes->{CustomDeleteProperty}
1859         || GetNamedDeleterOperation($interface);
1860 }
1861
1862 sub PrototypeHasStaticPropertyTable
1863 {
1864     my $interface = shift;
1865     my $numConstants = @{$interface->constants};
1866     return $numConstants > 0 || PrototypePropertyCount($interface) > 0;
1867 }
1868
1869 sub InstanceNeedsVisitChildren
1870 {
1871     my $interface = shift;
1872     
1873     foreach my $attribute (@{$interface->attributes}) {
1874         return 1 if $attribute->extendedAttributes->{CachedAttribute};
1875     }
1876
1877     return 1 if $interface->extendedAttributes->{JSCustomMarkFunction};
1878     return 1 if $interface->extendedAttributes->{ReportExtraMemoryCost};
1879     return 0;
1880 }
1881
1882 sub InstanceNeedsEstimatedSize
1883 {
1884     my $interface = shift;
1885     return $interface->extendedAttributes->{ReportExtraMemoryCost};
1886 }
1887
1888 sub GetImplClassName
1889 {
1890     my $interface = shift;
1891
1892     return $interface->type->name;
1893 }
1894
1895 sub IsClassNameWordBoundary
1896 {
1897     my ($name, $i) = @_;
1898
1899     # Interpret negative numbers as distance from end of string, just as the substr function does.
1900     $i += length($name) if $i < 0;
1901
1902     return 0 if $i < 0;
1903     return 1 if $i == 0;
1904     return 1 if $i == length($name);
1905     return 0 if $i > length($name);
1906
1907     my $checkString = substr($name, $i - 1);
1908     return $checkString =~ /^[^A-Z][A-Z]/ || $checkString =~ /^[A-Z][A-Z][^A-Z]/;
1909 }
1910
1911 sub IsPrefixRemovable
1912 {
1913     my ($class, $name, $i) = @_;
1914
1915     return IsClassNameWordBoundary($name, $i)
1916         && (IsClassNameWordBoundary($class, $i) && substr($class, 0, $i) eq substr($name, 0, $i)
1917             || IsClassNameWordBoundary($class, -$i) && substr($class, -$i) eq substr($name, 0, $i));
1918 }
1919
1920 sub GetNestedClassName
1921 {
1922     my ($interface, $name) = @_;
1923
1924     my $class = GetImplClassName($interface);
1925     my $member = $codeGenerator->WK_ucfirst($name);
1926
1927     # Since the enumeration name will be nested in the class name's namespace, remove any words
1928     # that happen to match the start or end of the class name. If an enumeration is named TrackType or
1929     # TextTrackType, and the class is named TextTrack, then we will get a name like TextTrack::Type.
1930     my $memberLength = length($member);
1931     my $longestPrefixLength = 0;
1932     if ($member =~ /^[A-Z]./) {
1933         for (my $i = 2; $i < $memberLength - 1; $i++) {
1934             $longestPrefixLength = $i if IsPrefixRemovable($class, $member, $i);
1935         }
1936     }
1937     $member = substr($member, $longestPrefixLength);
1938
1939     return "${class}::$member";
1940 }
1941
1942 sub GetEnumerationClassName
1943 {
1944     my ($type, $interface) = @_;
1945
1946     assert("Not a type") if ref($type) ne "IDLType";
1947
1948     if ($codeGenerator->HasEnumImplementationNameOverride($type)) {
1949         return $codeGenerator->GetEnumImplementationNameOverride($type);
1950     }
1951
1952     my $name = $type->name;
1953
1954     return $name if $codeGenerator->IsExternalEnumType($type);
1955     return $name unless defined($interface);
1956
1957     return GetNestedClassName($interface, $name);
1958 }
1959
1960 sub GetEnumerationValueName
1961 {
1962     my ($name) = @_;
1963
1964     return "EmptyString" if $name eq "";
1965     $name = join("", map { $codeGenerator->WK_ucfirst($_) } split("-", $name));
1966     $name = "_$name" if $name =~ /^\d/;
1967     return $name;
1968 }
1969
1970 sub GenerateEnumerationHeader
1971 {
1972     my ($object, $enumeration, $className) = @_;
1973  
1974     # - Add default header template and header protection.
1975     push(@headerContentHeader, GenerateHeaderContentHeader($enumeration));
1976
1977     $headerIncludes{"${className}.h"} = 1;
1978
1979     push(@headerContent, "\nnamespace WebCore {\n\n");
1980     push(@headerContent, GenerateEnumerationHeaderContent($enumeration, $className));
1981     push(@headerContent, "} // namespace WebCore\n");
1982      
1983     my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
1984     push(@headerContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
1985 }
1986  
1987 sub GenerateEnumerationImplementation
1988 {
1989     my ($object, $enumeration, $className) = @_;
1990  
1991     # - Add default header template
1992     push(@implContentHeader, GenerateImplementationContentHeader($enumeration));
1993
1994     push(@implContent, "\nusing namespace JSC;\n\n");
1995     push(@implContent, "namespace WebCore {\n\n");
1996     push(@implContent, GenerateEnumerationImplementationContent($enumeration, $className));
1997     push(@implContent, "} // namespace WebCore\n");
1998      
1999     my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2000     push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
2001 }
2002
2003 sub GenerateEnumerationImplementationContent
2004 {
2005     my ($enumeration, $className, $interface, $conditionalString) = @_;
2006
2007     # FIXME: A little ugly to have this be a side effect instead of a return value.
2008     AddToImplIncludes("<runtime/JSString.h>");
2009     AddToImplIncludes("<runtime/JSCInlines.h>");
2010     AddToImplIncludes("JSDOMConvertEnumeration.h");
2011
2012     my $result = "";
2013     $result .= "#if ${conditionalString}\n\n" if $conditionalString;
2014
2015     # FIXME: Change to take VM& instead of ExecState*.
2016     $result .= "template<> JSString* convertEnumerationToJS(ExecState& state, $className enumerationValue)\n";
2017     $result .= "{\n";
2018     AddToImplIncludes("<wtf/NeverDestroyed.h>");
2019     $result .= "    static const NeverDestroyed<String> values[] = {\n";
2020     foreach my $value (@{$enumeration->values}) {
2021         if ($value eq "") {
2022             $result .= "        emptyString(),\n";
2023         } else {
2024             $result .= "        MAKE_STATIC_STRING_IMPL(\"$value\"),\n";
2025         }
2026     }
2027     $result .= "    };\n";
2028     my $index = 0;
2029     foreach my $value (@{$enumeration->values}) {
2030         my $enumerationValueName = GetEnumerationValueName($value);
2031         $result .= "    static_assert(static_cast<size_t>(${className}::$enumerationValueName) == $index, \"${className}::$enumerationValueName is not $index as expected\");\n";
2032         $index++;
2033     }
2034     $result .= "    ASSERT(static_cast<size_t>(enumerationValue) < WTF_ARRAY_LENGTH(values));\n";
2035     $result .= "    return jsStringWithCache(&state, values[static_cast<size_t>(enumerationValue)]);\n";
2036     $result .= "}\n\n";
2037
2038     # FIXME: Change to take VM& instead of ExecState&.
2039     # FIXME: Consider using toStringOrNull to make exception checking faster.
2040     # FIXME: Consider finding a more efficient way to match against all the strings quickly.
2041     $result .= "template<> std::optional<$className> parseEnumeration<$className>(ExecState& state, JSValue value)\n";
2042     $result .= "{\n";
2043     $result .= "    auto stringValue = value.toWTFString(&state);\n";
2044     foreach my $value (@{$enumeration->values}) {
2045         my $enumerationValueName = GetEnumerationValueName($value);
2046         if ($value eq "") {
2047             $result .= "    if (stringValue.isEmpty())\n";
2048         } else {
2049             $result .= "    if (stringValue == \"$value\")\n";
2050         }
2051         $result .= "        return ${className}::${enumerationValueName};\n";
2052     }
2053     $result .= "    return std::nullopt;\n";
2054     $result .= "}\n\n";
2055
2056     $result .= "template<> const char* expectedEnumerationValues<$className>()\n";
2057     $result .= "{\n";
2058     $result .= "    return \"\\\"" . join ("\\\", \\\"", @{$enumeration->values}) . "\\\"\";\n";
2059     $result .= "}\n\n";
2060
2061     $result .= "#endif\n\n" if $conditionalString;
2062
2063     return $result;
2064 }
2065
2066 sub GenerateEnumerationsImplementationContent
2067 {
2068     my ($interface, $enumerations) = @_;
2069
2070     return "" unless @$enumerations;
2071
2072     my $result = "";
2073     foreach my $enumeration (@$enumerations) {
2074         my $className = GetEnumerationClassName($enumeration->type, $interface);
2075         my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2076         $result .= GenerateEnumerationImplementationContent($enumeration, $className, $interface, $conditionalString);
2077     }
2078     return $result;
2079 }
2080
2081 sub GenerateEnumerationHeaderContent
2082 {
2083     my ($enumeration, $className, $conditionalString) = @_;
2084
2085     $headerIncludes{"JSDOMConvertEnumeration.h"} = 1;
2086
2087     my $result = "";
2088     $result .= "#if ${conditionalString}\n\n" if $conditionalString;
2089
2090     my $exportMacro = GetExportMacroForJSClass($enumeration);
2091
2092     $result .= "template<> ${exportMacro}JSC::JSString* convertEnumerationToJS(JSC::ExecState&, $className);\n\n";
2093     $result .= "template<> ${exportMacro}std::optional<$className> parseEnumeration<$className>(JSC::ExecState&, JSC::JSValue);\n";
2094     $result .= "template<> ${exportMacro}const char* expectedEnumerationValues<$className>();\n\n";
2095     $result .= "#endif\n\n" if $conditionalString;
2096     
2097     return $result;
2098 }
2099
2100 sub GenerateEnumerationsHeaderContent
2101 {
2102     my ($interface, $enumerations) = @_;
2103
2104     return "" unless @$enumerations;
2105
2106     # FIXME: Could optimize this to only generate the parts of each enumeration that are actually
2107     # used, which would require iterating over everything in the interface.
2108
2109     my $result = "";
2110     foreach my $enumeration (@$enumerations) {
2111         my $className = GetEnumerationClassName($enumeration->type, $interface);
2112         my $conditionalString = $codeGenerator->GenerateConditionalString($enumeration);
2113         $result .= GenerateEnumerationHeaderContent($enumeration, $className, $conditionalString);
2114     }
2115     return $result;
2116 }
2117
2118 sub GetDictionaryClassName
2119 {
2120     my ($type, $interface) = @_;
2121
2122     if ($codeGenerator->HasDictionaryImplementationNameOverride($type)) {
2123         return $codeGenerator->GetDictionaryImplementationNameOverride($type);
2124     }
2125
2126     my $name = $type->name;
2127     return $name if $codeGenerator->IsExternalDictionaryType($type);
2128     return $name unless defined($interface);
2129     return GetNestedClassName($interface, $name);
2130 }
2131
2132 sub GenerateDefaultValue
2133 {
2134     my ($typeScope, $context, $type, $defaultValue) = @_;
2135
2136     if ($codeGenerator->IsStringType($type)) {
2137         my $useAtomicString = $type->extendedAttributes->{AtomicString};
2138         if ($defaultValue eq "null") {
2139             return $useAtomicString ? "nullAtom()" : "String()";
2140         } elsif ($defaultValue eq "\"\"") {
2141             return $useAtomicString ? "emptyAtom()" : "emptyString()";
2142         } else {
2143             return $useAtomicString ? "AtomicString(${defaultValue}, AtomicString::ConstructFromLiteral)" : "ASCIILiteral(${defaultValue})";
2144         }
2145     }
2146
2147     if ($codeGenerator->IsEnumType($type)) {
2148         # FIXME: Would be nice to report an error if the value does not have quote marks around it.
2149         # FIXME: Would be nice to report an error if the value is not one of the enumeration values.
2150         my $className = GetEnumerationClassName($type, $typeScope);
2151         my $enumerationValueName = GetEnumerationValueName(substr($defaultValue, 1, -1));
2152         return $className . "::" . $enumerationValueName;
2153     }
2154     if ($defaultValue eq "null") {
2155         if ($type->isUnion) {
2156             return "std::nullopt" if $type->isNullable;
2157
2158             my $IDLType = GetIDLType($typeScope, $type);
2159             return "convert<${IDLType}>(state, jsNull());";
2160         }
2161
2162         return "jsNull()" if $type->name eq "any";
2163         return "nullptr" if $codeGenerator->IsWrapperType($type) || $codeGenerator->IsBufferSourceType($type);
2164         return "String()" if $codeGenerator->IsStringType($type);
2165         return "std::nullopt";
2166     }
2167
2168     if ($defaultValue eq "[]") {
2169         my $IDLType = GetIDLType($typeScope, $type);
2170         return "Converter<${IDLType}>::ReturnType{ }";
2171     }
2172
2173     return "jsUndefined()" if $defaultValue eq "undefined";
2174     return "PNaN" if $defaultValue eq "NaN";
2175
2176     return $defaultValue;
2177 }
2178
2179 sub GenerateDictionaryHeaderContent
2180 {
2181     my ($dictionary, $className, $conditionalString) = @_;
2182
2183     $headerIncludes{"JSDOMConvertDictionary.h"} = 1;
2184
2185     my $result = "";
2186     $result .= "#if ${conditionalString}\n\n" if $conditionalString;
2187     $result .= "template<> ${className} convertDictionary<${className}>(JSC::ExecState&, JSC::JSValue);\n\n";
2188
2189     if ($dictionary->extendedAttributes->{JSGenerateToJSObject}) {
2190         $result .= "JSC::JSObject* convertDictionaryToJS(JSC::ExecState&, JSDOMGlobalObject&, const ${className}&);\n\n";
2191     }
2192
2193     $result .= "#endif\n\n" if $conditionalString;
2194     return $result;
2195 }
2196
2197 sub GenerateDictionariesHeaderContent
2198 {
2199     my ($typeScope, $allDictionaries) = @_;
2200
2201     return "" unless @$allDictionaries;
2202
2203     my $result = "";
2204     foreach my $dictionary (@$allDictionaries) {
2205         $headerIncludes{$typeScope->type->name . ".h"} = 1 if $typeScope;
2206         my $className = GetDictionaryClassName($dictionary->type, $typeScope);
2207         my $conditionalString = $codeGenerator->GenerateConditionalString($dictionary);
2208         $result .= GenerateDictionaryHeaderContent($dictionary, $className, $conditionalString);
2209     }
2210     return $result;
2211 }
2212
2213 sub GenerateDictionaryImplementationContent
2214 {
2215     my ($dictionary, $className, $interface) = @_;
2216
2217     my $result = "";
2218
2219     my $name = $dictionary->type->name;
2220     my $typeScope = $interface || $dictionary;
2221
2222     my $conditional = $dictionary->extendedAttributes->{Conditional};
2223     if ($conditional) {
2224         my $conditionalString = $codeGenerator->GenerateConditionalStringFromAttributeValue($conditional);
2225         $result .= "#if ${conditionalString}\n\n";
2226     }
2227
2228     # FIXME: A little ugly to have this be a side effect instead of a return value.
2229     AddToImplIncludes("<runtime/JSCInlines.h>");
2230     AddToImplIncludes("JSDOMConvertDictionary.h");
2231
2232     # https://heycam.github.io/webidl/#es-dictionary
2233     $result .= "template<> $className convertDictionary<$className>(ExecState& state, JSValue value)\n";
2234     $result .= "{\n";
2235     $result .= "    VM& vm = state.vm();\n";
2236     $result .= "    auto throwScope = DECLARE_THROW_SCOPE(vm);\n";
2237     $result .= "    bool isNullOrUndefined = value.isUndefinedOrNull();\n";
2238     $result .= "    auto* object = isNullOrUndefined ? nullptr : value.getObject();\n";
2239
2240     # 1. If Type(V) is not Undefined, Null or Object, then throw a TypeError.
2241     $result .= "    if (UNLIKELY(!isNullOrUndefined && !object)) {\n";
2242     $result .= "        throwTypeError(&state, throwScope);\n";
2243     $result .= "        return { };\n";
2244     $result .= "    }\n";
2245
2246     # 2. Let dict be an empty dictionary value of type D; every dictionary member is initially considered to be not present.
2247
2248     # 3. Let dictionaries be a list consisting of D and all of D’s inherited dictionaries, in order from least to most derived.
2249     my @dictionaries;
2250     push(@dictionaries, $dictionary);
2251     my $parentType = $dictionary->parentType;
2252     while (defined($parentType)) {
2253         my $parentDictionary = $codeGenerator->GetDictionaryByType($parentType);
2254         assert("Unable to find definition for dictionary named '" . $parentType->name . "'!") unless defined($parentDictionary);
2255         unshift(@dictionaries, $parentDictionary);
2256         $parentType = $parentDictionary->parentType;
2257     }
2258
2259     my $arguments = "";
2260     my $comma = "";
2261
2262     $result .= "    $className result;\n";
2263
2264     # 4. For each dictionary dictionary in dictionaries, in order:
2265     foreach my $dictionary (@dictionaries) {
2266         # For each dictionary member member declared on dictionary, in lexicographical order:
2267         my @sortedMembers = sort { $a->name cmp $b->name } @{$dictionary->members};
2268         foreach my $member (@sortedMembers) {
2269             $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.
2270
2271             my $type = $member->type;
2272             AddToImplIncludesForIDLType($type);
2273
2274             # 4.1. Let key be the identifier of member.
2275             my $key = $member->name;
2276             my $implementedAsKey = $member->extendedAttributes->{ImplementedAs} || $key;
2277
2278             # 4.2. Let value be an ECMAScript value, depending on Type(V):
2279             $result .= "    JSValue ${key}Value = isNullOrUndefined ? jsUndefined() : object->get(&state, Identifier::fromString(&state, \"${key}\"));\n";
2280
2281             my $IDLType = GetIDLType($typeScope, $type);
2282
2283             # 4.3. If value is not undefined, then:
2284             $result .= "    if (!${key}Value.isUndefined()) {\n";
2285
2286             my $nativeValue = JSValueToNative($typeScope, $member, "${key}Value", $member->extendedAttributes->{Conditional}, "&state", "state");
2287             $result .= "        result.$implementedAsKey = $nativeValue;\n";
2288             $result .= "        RETURN_IF_EXCEPTION(throwScope, { });\n";
2289
2290             # Value is undefined.
2291             # 4.4. Otherwise, if value is undefined but the dictionary member has a default value, then:
2292             if (!$member->isRequired && defined $member->default) {
2293                 $result .= "    } else\n";
2294                 $result .= "        result.$implementedAsKey = " . GenerateDefaultValue($typeScope, $member, $member->type, $member->default) . ";\n";
2295             } elsif ($member->isRequired) {
2296                 # 4.5. Otherwise, if value is undefined and the dictionary member is a required dictionary member, then throw a TypeError.
2297                 $result .= "    } else {\n";
2298                 $result .= "        throwRequiredMemberTypeError(state, throwScope, \"". $member->name ."\", \"$name\", \"". GetTypeNameForDisplayInException($type) ."\");\n";
2299                 $result .= "        return { };\n";
2300                 $result .= "    }\n";
2301             } else {
2302                 $result .= "    }\n";
2303             }
2304         }
2305     }
2306
2307     # 5. Return dict.
2308     $result .= "    return result;\n";
2309     $result .= "}\n\n";
2310
2311     if ($dictionary->extendedAttributes->{JSGenerateToJSObject}) {
2312         AddToImplIncludes("JSDOMGlobalObject.h");
2313         AddToImplIncludes("<runtime/ObjectConstructor.h>");
2314
2315         $result .= "JSC::JSObject* convertDictionaryToJS(JSC::ExecState& state, JSDOMGlobalObject& globalObject, const ${className}& dictionary)\n";
2316         $result .= "{\n";
2317         $result .= "    auto& vm = state.vm();\n\n";
2318
2319         # 1. Let O be ! ObjectCreate(%ObjectPrototype%).
2320         $result .= "    auto result = constructEmptyObject(&state, globalObject.objectPrototype());\n\n";
2321
2322         # 2. Let dictionaries be a list consisting of D and all of D’s inherited dictionaries,
2323         #    in order from least to most derived.
2324         #    NOTE: This was done above.
2325
2326         # 3. For each dictionary dictionary in dictionaries, in order:
2327         foreach my $dictionary (@dictionaries) {
2328             # 3.1. For each dictionary member member declared on dictionary, in lexicographical order:
2329             my @sortedMembers = sort { $a->name cmp $b->name } @{$dictionary->members};
2330             foreach my $member (@sortedMembers) {
2331                 my $key = $member->name;
2332                 my $implementedAsKey = $member->extendedAttributes->{ImplementedAs} || $key;
2333                 my $valueExpression = "dictionary.${implementedAsKey}";
2334
2335                 # 1. Let key be the identifier of member.
2336                 # 2. If the dictionary member named key is present in V, then:
2337                     # 1. Let idlValue be the value of member on V.
2338                     # 2. Let value be the result of converting idlValue to an ECMAScript value.
2339                     # 3. Perform ! CreateDataProperty(O, key, value).
2340
2341                 if (!$member->isRequired && not defined $member->default) {
2342                     my $IDLType = GetIDLType($typeScope, $member->type);
2343                     my $conversionExpression = NativeToJSValueUsingReferences($member, $typeScope, "${IDLType}::extractValueFromNullable(${valueExpression})", "globalObject");
2344
2345                     $result .= "    if (!${IDLType}::isNullValue(${valueExpression})) {\n";
2346                     $result .= "        auto ${key}Value = ${conversionExpression};\n";
2347                     $result .= "        result->putDirect(vm, JSC::Identifier::fromString(&vm, \"${key}\"), ${key}Value);\n";
2348                     $result .= "    }\n";
2349                 } else {
2350                     my $conversionExpression = NativeToJSValueUsingReferences($member, $typeScope, $valueExpression, "globalObject");
2351
2352                     $result .= "    auto ${key}Value = ${conversionExpression};\n";
2353                     $result .= "    result->putDirect(vm, JSC::Identifier::fromString(&vm, \"${key}\"), ${key}Value);\n";
2354                 }
2355             }
2356         }
2357
2358         $result .= "    return result;\n";
2359         $result .= "}\n\n";
2360     }
2361
2362     $result .= "#endif\n\n" if $conditional;
2363
2364     return $result;
2365 }
2366
2367 sub GenerateDictionariesImplementationContent
2368 {
2369     my ($typeScope, $allDictionaries) = @_;
2370
2371     my $result = "";
2372     foreach my $dictionary (@$allDictionaries) {
2373         my $className = GetDictionaryClassName($dictionary->type, $typeScope);
2374         $result .= GenerateDictionaryImplementationContent($dictionary, $className, $typeScope);
2375     }
2376     return $result;
2377 }
2378
2379 sub GetJSTypeForNode
2380 {
2381     my ($interface) = @_;
2382
2383     if ($codeGenerator->InheritsInterface($interface, "Document")) {
2384         return "JSDocumentWrapperType";
2385     }
2386     if ($codeGenerator->InheritsInterface($interface, "DocumentFragment")) {
2387         return "JSDocumentFragmentNodeType";
2388     }
2389     if ($codeGenerator->InheritsInterface($interface, "DocumentType")) {
2390         return "JSDocumentTypeNodeType";
2391     }
2392     if ($codeGenerator->InheritsInterface($interface, "ProcessingInstruction")) {
2393         return "JSProcessingInstructionNodeType";
2394     }
2395     if ($codeGenerator->InheritsInterface($interface, "CDATASection")) {
2396         return "JSCDATASectionNodeType";
2397     }
2398     if ($codeGenerator->InheritsInterface($interface, "Attr")) {
2399         return "JSAttrNodeType";
2400     }
2401     if ($codeGenerator->InheritsInterface($interface, "Comment")) {
2402         return "JSCommentNodeType";
2403     }
2404     if ($codeGenerator->InheritsInterface($interface, "Text")) {
2405         return "JSTextNodeType";
2406     }
2407     if ($codeGenerator->InheritsInterface($interface, "Element")) {
2408         return "JSElementType";
2409     }
2410     return "JSNodeType";
2411 }
2412
2413 sub GenerateHeader
2414 {
2415     my ($object, $interface, $enumerations, $dictionaries) = @_;
2416
2417     my $interfaceName = $interface->type->name;
2418     my $className = "JS$interfaceName";
2419     my %structureFlags = ();
2420
2421     my $hasParent = $interface->parentType || $interface->extendedAttributes->{JSLegacyParent};
2422     my $parentClassName = GetParentClassName($interface);
2423     my $needsVisitChildren = InstanceNeedsVisitChildren($interface);
2424
2425     # - Add default header template and header protection
2426     push(@headerContentHeader, GenerateHeaderContentHeader($interface));
2427
2428     if ($hasParent) {
2429         $headerIncludes{"$parentClassName.h"} = 1;
2430     } else {
2431         $headerIncludes{"JSDOMWrapper.h"} = 1;
2432         if ($interface->isException) {
2433             $headerIncludes{"<runtime/ErrorPrototype.h>"} = 1;
2434         }
2435     }
2436
2437     $headerIncludes{"$interfaceName.h"} = 1 if $hasParent && $interface->extendedAttributes->{JSGenerateToNativeObject};
2438
2439     $headerIncludes{"SVGElement.h"} = 1 if $className =~ /^JSSVG/;
2440
2441     my $implType = GetImplClassName($interface);
2442
2443     my $numConstants = @{$interface->constants};
2444     my $numAttributes = @{$interface->attributes};
2445     my $numOperations = @{$interface->operations};
2446
2447     push(@headerContent, "\nnamespace WebCore {\n\n");
2448
2449     if ($codeGenerator->IsSVGAnimatedType($interface->type)) {
2450         $headerIncludes{"$interfaceName.h"} = 1;
2451     } else {
2452         # Implementation class forward declaration
2453         if (IsDOMGlobalObject($interface)) {
2454             AddClassForwardIfNeeded($interface->type);
2455         }
2456     }
2457
2458     push(@headerContent, "class JSDOMWindowProxy;\n\n") if $interfaceName eq "DOMWindow";
2459
2460     my $exportMacro = GetExportMacroForJSClass($interface);
2461
2462     # Class declaration
2463     push(@headerContent, "class $exportMacro$className : public $parentClassName {\n");
2464
2465     # Static create methods
2466     push(@headerContent, "public:\n");
2467     push(@headerContent, "    using Base = $parentClassName;\n");
2468     push(@headerContent, "    using DOMWrapped = $implType;\n") if $hasParent;
2469
2470     if ($interfaceName eq "DOMWindow") {
2471         push(@headerContent, "    static $className* create(JSC::VM& vm, JSC::Structure* structure, Ref<$implType>&& impl, JSDOMWindowProxy* proxy)\n");
2472         push(@headerContent, "    {\n");
2473         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(vm.heap)) ${className}(vm, structure, WTFMove(impl), proxy);\n");
2474         push(@headerContent, "        ptr->finishCreation(vm, proxy);\n");
2475         push(@headerContent, "        return ptr;\n");
2476         push(@headerContent, "    }\n\n");
2477     } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope")) {
2478         push(@headerContent, "    static $className* create(JSC::VM& vm, JSC::Structure* structure, Ref<$implType>&& impl, JSC::JSProxy* proxy)\n");
2479         push(@headerContent, "    {\n");
2480         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(vm.heap)) ${className}(vm, structure, WTFMove(impl));\n");
2481         push(@headerContent, "        ptr->finishCreation(vm, proxy);\n");
2482         push(@headerContent, "        return ptr;\n");
2483         push(@headerContent, "    }\n\n");
2484     } elsif ($interface->extendedAttributes->{MasqueradesAsUndefined}) {
2485         AddIncludesForImplementationTypeInHeader($implType);
2486         push(@headerContent, "    static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<$implType>&& impl)\n");
2487         push(@headerContent, "    {\n");
2488         push(@headerContent, "        globalObject->masqueradesAsUndefinedWatchpoint()->fireAll(globalObject->vm(), \"Allocated masquerading object\");\n");
2489         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject, WTFMove(impl));\n");
2490         push(@headerContent, "        ptr->finishCreation(globalObject->vm());\n");
2491         push(@headerContent, "        return ptr;\n");
2492         push(@headerContent, "    }\n\n");
2493     } elsif (!NeedsImplementationClass($interface)) {
2494         push(@headerContent, "    static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject)\n");
2495         push(@headerContent, "    {\n");
2496         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject);\n");
2497         push(@headerContent, "        ptr->finishCreation(globalObject->vm());\n");
2498         push(@headerContent, "        return ptr;\n");
2499         push(@headerContent, "    }\n\n");  
2500     } else {
2501         AddIncludesForImplementationTypeInHeader($implType);
2502         push(@headerContent, "    static $className* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<$implType>&& impl)\n");
2503         push(@headerContent, "    {\n");
2504         push(@headerContent, "        $className* ptr = new (NotNull, JSC::allocateCell<$className>(globalObject->vm().heap)) $className(structure, *globalObject, WTFMove(impl));\n");
2505         push(@headerContent, "        ptr->finishCreation(globalObject->vm());\n");
2506         push(@headerContent, "        return ptr;\n");
2507         push(@headerContent, "    }\n\n");
2508     }
2509
2510     push(@headerContent, "    static const bool needsDestruction = false;\n\n") if IsDOMGlobalObject($interface);
2511
2512     $structureFlags{"JSC::HasStaticPropertyTable"} = 1 if InstancePropertyCount($interface) > 0;
2513     $structureFlags{"JSC::NewImpurePropertyFiresWatchpoints"} = 1 if $interface->extendedAttributes->{NewImpurePropertyFiresWatchpoints};
2514     $structureFlags{"JSC::IsImmutablePrototypeExoticObject"} = 1 if $interface->extendedAttributes->{IsImmutablePrototypeExoticObject};
2515     $structureFlags{"JSC::MasqueradesAsUndefined"} = 1 if $interface->extendedAttributes->{MasqueradesAsUndefined};
2516     $structureFlags{"JSC::ImplementsHasInstance | JSC::ImplementsDefaultHasInstance"} = 1 if $interfaceName eq "DOMWindow";
2517         
2518     # Prototype
2519     unless (ShouldUseGlobalObjectPrototype($interface)) {
2520         push(@headerContent, "    static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&);\n");
2521         push(@headerContent, "    static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&);\n");
2522     }
2523
2524     # JSValue to implementation type
2525     if (ShouldGenerateToWrapped($hasParent, $interface)) {
2526         # FIXME: Add extended attribute for this.
2527         my @toWrappedArguments = ();
2528         push(@toWrappedArguments, "JSC::VM&");
2529         push(@toWrappedArguments, "JSC::ExecState&") if $interface->type->name eq "XPathNSResolver";
2530         push(@toWrappedArguments, "JSC::JSValue");
2531
2532         my $toWrappedType = $interface->type->name eq "XPathNSResolver" ? "RefPtr<${implType}>" : "${implType}*";
2533
2534         my $export = "";
2535         $export = "WEBCORE_EXPORT " if $interface->extendedAttributes->{ExportToWrappedFunction};
2536         push(@headerContent, "    static ${export}${toWrappedType} toWrapped(" . join(", ", @toWrappedArguments) . ");\n");
2537     }
2538
2539     $headerTrailingIncludes{"${className}Custom.h"} = 1 if $interface->extendedAttributes->{JSCustomHeader};
2540
2541     my $namedGetterOperation = GetNamedGetterOperation($interface);
2542     my $indexedGetterOperation = GetIndexedGetterOperation($interface);
2543
2544     # FIXME: Why doesn't this also include Indexed Getters and [CustomGetOwnPropertySlot]
2545     if ($namedGetterOperation) {
2546         if ($codeGenerator->InheritsExtendedAttribute($interface, "OverrideBuiltins")) {
2547             $structureFlags{"JSC::GetOwnPropertySlotIsImpure"} = 1;
2548         } else {
2549             $structureFlags{"JSC::GetOwnPropertySlotIsImpureForPropertyAbsence"} = 1;
2550         }
2551     }
2552     
2553     # ClassInfo MethodTable declarations.
2554     
2555     if (InstanceOverridesGetOwnPropertySlot($interface)) {
2556         push(@headerContent, "    static bool getOwnPropertySlot(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&);\n");
2557         $structureFlags{"JSC::OverridesGetOwnPropertySlot"} = 1;
2558         push(@headerContent, "    static bool getOwnPropertySlotByIndex(JSC::JSObject*, JSC::ExecState*, unsigned propertyName, JSC::PropertySlot&);\n");
2559         $structureFlags{"JSC::InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero"} = 1;
2560     }
2561     
2562     if (InstanceOverridesGetOwnPropertyNames($interface)) {
2563         push(@headerContent, "    static void getOwnPropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode = JSC::EnumerationMode());\n");
2564         $structureFlags{"JSC::OverridesGetPropertyNames"} = 1;
2565     }
2566     
2567     if (InstanceOverridesPut($interface)) {
2568         push(@headerContent, "    static bool put(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&);\n");
2569         push(@headerContent, "    static bool putByIndex(JSC::JSCell*, JSC::ExecState*, unsigned propertyName, JSC::JSValue, bool shouldThrow);\n");
2570     }
2571     
2572     if (InstanceOverridesDefineOwnProperty($interface)) {
2573         push(@headerContent, "    static bool defineOwnProperty(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, const JSC::PropertyDescriptor&, bool shouldThrow);\n");
2574     }
2575
2576     if (InstanceOverridesDeleteProperty($interface)) {
2577         push(@headerContent, "    static bool deleteProperty(JSC::JSCell*, JSC::ExecState*, JSC::PropertyName);\n");
2578         push(@headerContent, "    static bool deletePropertyByIndex(JSC::JSCell*, JSC::ExecState*, unsigned);\n");
2579     }
2580
2581     if (InstanceOverridesGetCallData($interface)) {
2582         push(@headerContent, "    static JSC::CallType getCallData(JSC::JSCell*, JSC::CallData&);\n\n");
2583         $headerIncludes{"<runtime/CallData.h>"} = 1;
2584         $structureFlags{"JSC::TypeOfShouldCallGetCallData"} = 1;
2585     }
2586     
2587     if ($interface->extendedAttributes->{CustomGetPrototype}) {
2588         push(@headerContent, "    static JSC::JSValue getPrototype(JSC::JSObject*, JSC::ExecState*);\n");
2589     }
2590     
2591     if ($interface->extendedAttributes->{CustomToStringName}) {
2592         push(@headerContent, "    static String toStringName(const JSC::JSObject*, JSC::ExecState*);\n");
2593     }
2594     
2595     if ($interface->extendedAttributes->{CustomPreventExtensions}) {
2596         push(@headerContent, "    static bool preventExtensions(JSC::JSObject*, JSC::ExecState*);\n");
2597     }
2598     
2599     if (InstanceNeedsEstimatedSize($interface)) {
2600         push(@headerContent, "    static size_t estimatedSize(JSCell*);\n");
2601     }
2602     
2603     if (!$hasParent) {
2604         push(@headerContent, "    static void destroy(JSC::JSCell*);\n");
2605     }
2606
2607     # Class info
2608     if ($interfaceName eq "Node") {
2609         push(@headerContent, "\n");
2610         push(@headerContent, "protected:\n");
2611         push(@headerContent, "    static const JSC::ClassInfo s_info;\n");
2612         push(@headerContent, "public:\n");
2613         push(@headerContent, "    static constexpr const JSC::ClassInfo* info() { return &s_info; }\n\n");
2614     } else {
2615         push(@headerContent, "\n");
2616         push(@headerContent, "    DECLARE_INFO;\n\n");
2617     }
2618
2619     # Structure ID
2620     push(@headerContent, "    static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)\n");
2621     push(@headerContent, "    {\n");
2622     if (IsDOMGlobalObject($interface)) {
2623         push(@headerContent, "        return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::GlobalObjectType, StructureFlags), info());\n");
2624     } elsif ($codeGenerator->InheritsInterface($interface, "Node")) {
2625         my $type = GetJSTypeForNode($interface);
2626         push(@headerContent, "        return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSType($type), StructureFlags), info());\n");
2627     } elsif ($codeGenerator->InheritsInterface($interface, "Event")) {
2628         push(@headerContent, "        return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSType(JSEventType), StructureFlags), info());\n");
2629     } else {
2630         push(@headerContent, "        return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());\n");
2631     }
2632     push(@headerContent, "    }\n\n");
2633
2634     # Custom pushEventHandlerScope function
2635     if ($interface->extendedAttributes->{CustomPushEventHandlerScope}) {
2636         push(@headerContent, "    JSC::JSScope* pushEventHandlerScope(JSC::ExecState*, JSC::JSScope*) const;\n\n");
2637     }
2638     
2639     # Constructor object getter
2640     unless ($interface->extendedAttributes->{NoInterfaceObject}) {
2641         push(@headerContent, "    static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*);\n");
2642         push(@headerContent, "    static JSC::JSValue getNamedConstructor(JSC::VM&, JSC::JSGlobalObject*);\n") if $interface->extendedAttributes->{NamedConstructor};
2643     }
2644
2645     # Serializer function.
2646     if ($interface->serializable) {
2647         push(@headerContent, "    static JSC::JSObject* serialize(JSC::ExecState&, ${className}& thisObject, JSDOMGlobalObject&, JSC::ThrowScope&);\n");
2648     }
2649     
2650     my $numCustomOperations = 0;
2651     my $numCustomAttributes = 0;
2652
2653     my $hasForwardDeclaringOperations = 0;
2654     my $hasForwardDeclaringAttributes = 0;
2655
2656     my $hasDOMJITAttributes = 0;
2657
2658     # Attribute and function enums
2659     if ($numAttributes > 0) {
2660         foreach my $attribute (@{$interface->attributes}) {
2661             $numCustomAttributes++ if HasCustomGetter($attribute);
2662             $numCustomAttributes++ if HasCustomSetter($attribute);
2663             if ($attribute->extendedAttributes->{CachedAttribute}) {
2664                 my $conditionalString = $codeGenerator->GenerateConditionalString($attribute);
2665                 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2666                 push(@headerContent, "    mutable JSC::WriteBarrier<JSC::Unknown> m_" . $attribute->name . ";\n");
2667                 $numCachedAttributes++;
2668                 push(@headerContent, "#endif\n") if $conditionalString;
2669             }
2670             $hasDOMJITAttributes = 1 if $attribute->extendedAttributes->{DOMJIT};
2671
2672             $hasForwardDeclaringAttributes = 1 if $attribute->extendedAttributes->{ForwardDeclareInHeader};
2673         }
2674     }
2675
2676     # visit function
2677     if ($needsVisitChildren) {
2678         push(@headerContent, "    static void visitChildren(JSCell*, JSC::SlotVisitor&);\n");
2679         push(@headerContent, "    void visitAdditionalChildren(JSC::SlotVisitor&);\n") if $interface->extendedAttributes->{JSCustomMarkFunction};
2680         push(@headerContent, "\n");
2681
2682         if ($interface->extendedAttributes->{JSCustomMarkFunction}) {
2683             # We assume that the logic in visitAdditionalChildren is highly volatile, and during a
2684             # concurrent GC or in between eden GCs something may happen that would lead to this
2685             # logic behaving differently. Since this could mark objects or add opaque roots, this
2686             # means that after any increment of mutator resumption in a concurrent GC and at least
2687             # once during any eden GC we need to re-execute visitAdditionalChildren on any objects
2688             # that we had executed it on before. We do this using the DOM's own MarkingConstraint,
2689             # which will call visitOutputConstraints on all objects in the DOM's own
2690             # outputConstraintSubspace. visitOutputConstraints is the name JSC uses for the method
2691             # that the GC calls to ask an object is it would like to mark anything else after the
2692             # program resumed since the last call to visitChildren or visitOutputConstraints. Since
2693             # this just calls visitAdditionalChildren, you usually don't have to worry about this.
2694             push(@headerContent, "    static void visitOutputConstraints(JSCell*, JSC::SlotVisitor&);\n");
2695             my $subspaceFunc = IsDOMGlobalObject($interface) ? "globalObjectOutputConstraintSubspaceFor" : "outputConstraintSubspaceFor";
2696             push(@headerContent, "    template<typename> static JSC::Subspace* subspaceFor(JSC::VM& vm) { return $subspaceFunc(vm); }\n");
2697         }
2698     }
2699     
2700     if ($numCustomAttributes > 0) {
2701         push(@headerContent, "\n    // Custom attributes\n");
2702
2703         foreach my $attribute (@{$interface->attributes}) {
2704             my $conditionalString = $codeGenerator->GenerateConditionalString($attribute);
2705             if (HasCustomGetter($attribute)) {
2706                 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2707                 my $methodName = $codeGenerator->WK_lcfirst($attribute->name);
2708                 push(@headerContent, "    JSC::JSValue " . $methodName . "(JSC::ExecState&) const;\n");
2709                 push(@headerContent, "#endif\n") if $conditionalString;
2710             }
2711             if (HasCustomSetter($attribute) && !IsReadonly($attribute)) {
2712                 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2713                 push(@headerContent, "    void set" . $codeGenerator->WK_ucfirst($attribute->name) . "(JSC::ExecState&, JSC::JSValue);\n");
2714                 push(@headerContent, "#endif\n") if $conditionalString;
2715             }
2716         }
2717     }
2718
2719     foreach my $operation (@{$interface->operations}) {
2720         $numCustomOperations++ if HasCustomMethod($operation);
2721         $hasForwardDeclaringOperations = 1 if $operation->extendedAttributes->{ForwardDeclareInHeader};
2722     }
2723
2724     if ($numCustomOperations > 0) {
2725         my $inAppleCopyright = 0;
2726         push(@headerContent, "\n    // Custom functions\n");
2727         foreach my $operation (@{$interface->operations}) {
2728             next unless HasCustomMethod($operation);
2729             next if $operation->{overloads} && $operation->{overloadIndex} != 1;
2730
2731             if ($operation->extendedAttributes->{AppleCopyright}) {
2732                 if (!$inAppleCopyright) {
2733                     push(@headerContent, $beginAppleCopyrightForHeaderFiles);
2734                     $inAppleCopyright = 1;
2735                 }
2736             } elsif ($inAppleCopyright) {
2737                 push(@headerContent, $endAppleCopyright);
2738                 $inAppleCopyright = 0;
2739             }
2740
2741             my $conditionalString = $codeGenerator->GenerateConditionalString($operation);
2742             push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2743
2744             my $functionImplementationName = $operation->extendedAttributes->{ImplementedAs} || $codeGenerator->WK_lcfirst($operation->name);
2745
2746             my @functionArguments = ();
2747             push(@functionArguments, "JSC::ExecState&");
2748             push(@functionArguments, "Ref<DeferredPromise>&&") if $codeGenerator->IsPromiseType($operation->type) && !$operation->extendedAttributes->{ReturnsOwnPromise};
2749
2750             push(@headerContent, "    " . ($operation->isStatic ? "static " : "") . "JSC::JSValue " . $functionImplementationName . "(" . join(", ", @functionArguments) . ");\n");
2751
2752             push(@headerContent, "#endif\n") if $conditionalString;
2753         }
2754         push(@headerContent, $endAppleCopyright) if $inAppleCopyright;
2755     }
2756
2757     if (NeedsImplementationClass($interface)) {
2758         if ($hasParent) {
2759             push(@headerContent, "    $interfaceName& wrapped() const\n");
2760             push(@headerContent, "    {\n");
2761             push(@headerContent, "        return static_cast<$interfaceName&>(Base::wrapped());\n");
2762             push(@headerContent, "    }\n");
2763         }
2764     }
2765
2766     # structure flags
2767     if (%structureFlags) {
2768         push(@headerContent, "public:\n");
2769         push(@headerContent, "    static const unsigned StructureFlags = ");
2770         foreach my $structureFlag (sort (keys %structureFlags)) {
2771             push(@headerContent, $structureFlag . " | ");
2772         }
2773         push(@headerContent, "Base::StructureFlags;\n");
2774     }
2775
2776     push(@headerContent, "protected:\n");
2777
2778     # Constructor
2779     if ($interfaceName eq "DOMWindow") {
2780         push(@headerContent, "    $className(JSC::VM&, JSC::Structure*, Ref<$implType>&&, JSDOMWindowProxy*);\n");
2781     } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope")) {
2782         push(@headerContent, "    $className(JSC::VM&, JSC::Structure*, Ref<$implType>&&);\n");
2783     } elsif (!NeedsImplementationClass($interface)) {
2784         push(@headerContent, "    $className(JSC::Structure*, JSDOMGlobalObject&);\n\n");
2785      } else {
2786         push(@headerContent, "    $className(JSC::Structure*, JSDOMGlobalObject&, Ref<$implType>&&);\n\n");
2787     }
2788
2789     if ($interfaceName eq "DOMWindow") {
2790         push(@headerContent, "    void finishCreation(JSC::VM&, JSDOMWindowProxy*);\n");
2791     } elsif ($codeGenerator->InheritsInterface($interface, "WorkerGlobalScope")) {
2792         push(@headerContent, "    void finishCreation(JSC::VM&, JSC::JSProxy*);\n");
2793     } else {
2794         push(@headerContent, "    void finishCreation(JSC::VM&);\n");
2795     }
2796
2797     push(@headerContent, "};\n\n");
2798
2799     if (ShouldGenerateWrapperOwnerCode($hasParent, $interface)) {
2800         if ($interfaceName ne "Node" && $codeGenerator->InheritsInterface($interface, "Node")) {
2801             $headerIncludes{"JSNode.h"} = 1;
2802             push(@headerContent, "class JS${interfaceName}Owner : public JSNodeOwner {\n");
2803         } else {
2804             push(@headerContent, "class JS${interfaceName}Owner : public JSC::WeakHandleOwner {\n");
2805         }
2806         $headerIncludes{"<wtf/NeverDestroyed.h>"} = 1;
2807         push(@headerContent, "public:\n");
2808         push(@headerContent, "    virtual bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor&);\n");
2809         push(@headerContent, "    virtual void finalize(JSC::Handle<JSC::Unknown>, void* context);\n");
2810         push(@headerContent, "};\n");
2811         push(@headerContent, "\n");
2812         push(@headerContent, "inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld&, $implType*)\n");
2813         push(@headerContent, "{\n");
2814         push(@headerContent, "    static NeverDestroyed<JS${interfaceName}Owner> owner;\n");
2815         push(@headerContent, "    return &owner.get();\n");
2816         push(@headerContent, "}\n");
2817         push(@headerContent, "\n");
2818         push(@headerContent, "inline void* wrapperKey($implType* wrappableObject)\n");
2819         push(@headerContent, "{\n");
2820         push(@headerContent, "    return wrappableObject;\n");
2821         push(@headerContent, "}\n");
2822         push(@headerContent, "\n");
2823     }
2824     if (ShouldGenerateToJSDeclaration($hasParent, $interface)) {
2825         # Node and NodeList have custom inline implementations which thus cannot be exported.
2826         # FIXME: The special case for Node and NodeList should probably be implemented via an IDL attribute.
2827         if ($implType eq "Node" or $implType eq "NodeList") {
2828             push(@headerContent, "JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, $implType&);\n");
2829         } else {
2830             push(@headerContent, $exportMacro."JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, $implType&);\n");
2831         }
2832         push(@headerContent, "inline JSC::JSValue toJS(JSC::ExecState* state, JSDOMGlobalObject* globalObject, $implType* impl) { return impl ? toJS(state, globalObject, *impl) : JSC::jsNull(); }\n");
2833
2834         push(@headerContent, "JSC::JSValue toJSNewlyCreated(JSC::ExecState*, JSDOMGlobalObject*, Ref<$implType>&&);\n");
2835         push(@headerContent, "inline JSC::JSValue toJSNewlyCreated(JSC::ExecState* state, JSDOMGlobalObject* globalObject, RefPtr<$implType>&& impl) { return impl ? toJSNewlyCreated(state, globalObject, impl.releaseNonNull()) : JSC::jsNull(); }\n");
2836    }
2837
2838     push(@headerContent, "\n");
2839
2840     GeneratePrototypeDeclaration(\@headerContent, $className, $interface) if HeaderNeedsPrototypeDeclaration($interface);
2841
2842     if ($hasForwardDeclaringOperations) {
2843         my $inAppleCopyright = 0;
2844         push(@headerContent,"// Functions\n\n");
2845         foreach my $operation (@{$interface->operations}) {
2846             next if $operation->{overloadIndex} && $operation->{overloadIndex} > 1;
2847             next unless $operation->extendedAttributes->{ForwardDeclareInHeader};
2848
2849             if ($operation->extendedAttributes->{AppleCopyright}) {
2850                 if (!$inAppleCopyright) {
2851                     push(@headerContent, $beginAppleCopyrightForHeaderFiles);
2852                     $inAppleCopyright = 1;
2853                 }
2854             } elsif ($inAppleCopyright) {
2855                 push(@headerContent, $endAppleCopyright);
2856                 $inAppleCopyright = 0;
2857             }
2858
2859             my $conditionalAttribute = GetConditionalForOperationConsideringOverloads($operation);
2860             my $conditionalString = $conditionalAttribute ? $codeGenerator->GenerateConditionalStringFromAttributeValue($conditionalAttribute) : undef;
2861             push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2862             my $functionName = GetFunctionName($interface, $className, $operation);
2863             push(@headerContent, "JSC::EncodedJSValue JSC_HOST_CALL ${functionName}(JSC::ExecState*);\n");
2864             push(@headerContent, "#endif\n") if $conditionalString;
2865         }
2866
2867         push(@headerContent, $endAppleCopyright) if $inAppleCopyright;
2868         push(@headerContent,"\n");
2869     }
2870
2871     if ($hasForwardDeclaringAttributes) {
2872         push(@headerContent,"// Attributes\n\n");
2873         foreach my $attribute (@{$interface->attributes}) {
2874             next unless $attribute->extendedAttributes->{ForwardDeclareInHeader};
2875
2876             my $conditionalString = $codeGenerator->GenerateConditionalString($attribute);
2877             push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
2878             my $getter = GetAttributeGetterName($interface, $className, $attribute);
2879             push(@headerContent, "JSC::EncodedJSValue ${getter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::PropertyName);\n");
2880             if (!IsReadonly($attribute)) {
2881                 my $setter = GetAttributeSetterName($interface, $className, $attribute);
2882                 push(@headerContent, "bool ${setter}(JSC::ExecState*, JSC::EncodedJSValue, JSC::EncodedJSValue);\n");
2883             }
2884             push(@headerContent, "#endif\n") if $conditionalString;
2885         }
2886     }
2887
2888     # CheckSubClass Snippet function.
2889     if ($interface->extendedAttributes->{DOMJIT}) {
2890         $headerIncludes{"<jit/Snippet.h>"} = 1;
2891         push(@headerContent, "#if ENABLE(JIT)\n");
2892         push(@headerContent, "Ref<JSC::Snippet> checkSubClassSnippetFor${className}();\n");
2893         push(@headerContent, "#endif\n");
2894     }
2895
2896     if ($hasDOMJITAttributes) {
2897         $headerIncludes{"<domjit/DOMJITGetterSetter.h>"} = 1;
2898         push(@headerContent,"// DOM JIT Attributes\n\n");
2899         foreach my $attribute (@{$interface->attributes}) {
2900             next unless $attribute->extendedAttributes->{DOMJIT};
2901             assert("Only DOMJIT=Getter is supported for attributes") unless $codeGenerator->ExtendedAttributeContains($attribute->extendedAttributes->{DOMJIT}, "Getter");
2902
2903             my $interfaceName = $interface->type->name;
2904             my $className = $interfaceName . $codeGenerator->WK_ucfirst($attribute->name);
2905             my $domJITClassName = $className . "Attribute";
2906
2907             push(@headerContent, "#if ENABLE(JIT)\n");
2908             push(@headerContent, "Ref<JSC::DOMJIT::CallDOMGetterSnippet> compile${domJITClassName}();\n");
2909             push(@headerContent, "#endif\n\n");
2910         }
2911     }
2912
2913     if (HasCustomConstructor($interface)) {
2914         push(@headerContent, "// Custom constructor\n");
2915         push(@headerContent, "JSC::EncodedJSValue JSC_HOST_CALL construct${className}(JSC::ExecState&);\n\n");
2916     }
2917
2918     if (NeedsImplementationClass($interface)) {
2919         my $toWrappedType = $interface->type->name eq "XPathNSResolver" ? "RefPtr<${implType}>" : "${implType}*";
2920     
2921         push(@headerContent, "template<> struct JSDOMWrapperConverterTraits<${implType}> {\n");
2922         push(@headerContent, "    using WrapperClass = ${className};\n");
2923         push(@headerContent, "    using ToWrappedReturnType = ${toWrappedType};\n");
2924         push(@headerContent, "};\n");
2925     }
2926
2927     push(@headerContent, GenerateEnumerationsHeaderContent($interface, $enumerations));
2928     push(@headerContent, GenerateDictionariesHeaderContent($interface, $dictionaries));
2929
2930     my $conditionalString = $codeGenerator->GenerateConditionalString($interface);
2931     push(@headerContent, "\n} // namespace WebCore\n");
2932     push(@headerContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
2933
2934     if ($interface->extendedAttributes->{AppleCopyright}) {
2935         push(@headerContent, "\n");
2936         push(@headerContent, split("\r", $endAppleCopyright));
2937     }
2938
2939     # - Generate dependencies.
2940     if ($writeDependencies) {
2941         my @ancestors;
2942         $codeGenerator->ForAllParents($interface, sub {
2943             my $currentInterface = shift;
2944             push(@ancestors, $currentInterface->type->name);
2945         }, 0);
2946         for my $dictionary (@$dictionaries) {
2947             my $parentType = $dictionary->parentType;
2948             while (defined($parentType)) {
2949                 push(@ancestors, $parentType->name) if $codeGenerator->IsExternalDictionaryType($parentType);
2950                 my $parentDictionary = $codeGenerator->GetDictionaryByType($parentType);
2951                 assert("Unable to find definition for dictionary named '" . $parentType->name . "'!") unless defined($parentDictionary);
2952                 $parentType = $parentDictionary->parentType;
2953             }
2954         }
2955         push(@depsContent, "$className.h : ", join(" ", map { "$_.idl" } @ancestors), "\n");
2956         push(@depsContent, map { "$_.idl :\n" } @ancestors);
2957     }
2958 }
2959
2960 sub GeneratePropertiesHashTable
2961 {
2962     my ($object, $interface, $isInstance, $hashKeys, $hashSpecials, $hashValue1, $hashValue2, $conditionals, $readWriteConditionals, $runtimeEnabledOperations, $runtimeEnabledAttributes, $settingsEnabledOperations, $settingsEnabledAttributes) = @_;
2963
2964     # FIXME: These should be functions on $interface.
2965     my $interfaceName = $interface->type->name;
2966     my $className = "JS$interfaceName";
2967     
2968     # - Add all properties in a hashtable definition
2969     my $propertyCount = $isInstance ? InstancePropertyCount($interface) : PrototypePropertyCount($interface);
2970
2971     if (!$isInstance && NeedsConstructorProperty($interface)) {
2972         die if !$propertyCount;
2973         push(@$hashKeys, "constructor");
2974         my $getter = "js" . $interfaceName . "Constructor";
2975         push(@$hashValue1, $getter);
2976
2977         my $setter = "setJS" . $interfaceName . "Constructor";
2978         push(@$hashValue2, $setter);
2979         push(@$hashSpecials, "DontEnum");
2980     }
2981
2982     return 0 if !$propertyCount;
2983
2984     my @attributes = @{$interface->attributes};
2985     push(@attributes, @{$interface->mapLike->attributes}) if $interface->mapLike;
2986
2987     foreach my $attribute (@attributes) {
2988         next if ($attribute->isStatic);
2989         next if AttributeShouldBeOnInstance($interface, $attribute) != $isInstance;
2990
2991         # Global objects add RuntimeEnabled attributes after creation so do not add them to the static table.
2992         if ($isInstance && NeedsRuntimeCheck($attribute)) {
2993             $propertyCount -= 1;
2994             next;
2995         }
2996
2997         my $name = $attribute->name;
2998         push(@$hashKeys, $name);
2999
3000         my $special = GetJSCAttributesForAttribute($interface, $attribute);
3001         push(@$hashSpecials, $special);
3002
3003         if ($attribute->extendedAttributes->{DOMJIT}) {
3004             push(@$hashValue1, "&DOMJITAttributeFor" . $interface->type->name . $codeGenerator->WK_ucfirst($attribute->name));
3005         } else {
3006             my $getter = GetAttributeGetterName($interface, $className, $attribute);
3007             push(@$hashValue1, $getter);
3008         }
3009
3010         if (IsReadonly($attribute)) {
3011             push(@$hashValue2, "0");
3012         } else {
3013             my $setter = GetAttributeSetterName($interface, $className, $attribute);
3014             push(@$hashValue2, $setter);
3015         }
3016
3017         my $conditional = $attribute->extendedAttributes->{Conditional};
3018         $conditionals->{$name} = $conditional if $conditional;
3019         my $readWriteConditional = $attribute->extendedAttributes->{ConditionallyReadWrite};
3020         $readWriteConditionals->{$name} = $readWriteConditional if $readWriteConditional;
3021
3022         if (NeedsRuntimeCheck($attribute)) {
3023             push(@$runtimeEnabledAttributes, $attribute);
3024         }
3025
3026         if ($attribute->extendedAttributes->{EnabledBySetting}) {
3027             push(@$settingsEnabledAttributes, $attribute);
3028         }
3029     }
3030
3031     my @operations = @{$interface->operations};
3032     push(@operations, @{$interface->iterable->operations}) if IsKeyValueIterableInterface($interface);
3033     push(@operations, @{$interface->mapLike->operations}) if $interface->mapLike;
3034     push(@operations, @{$interface->serializable->operations}) if $interface->serializable;
3035     foreach my $operation (@operations) {
3036         next if ($operation->extendedAttributes->{PrivateIdentifier} and not $operation->extendedAttributes->{PublicIdentifier});
3037         next if ($operation->isStatic);
3038         next if $operation->{overloadIndex} && $operation->{overloadIndex} > 1;
3039         next if OperationShouldBeOnInstance($interface, $operation) != $isInstance;
3040         next if $operation->name eq "[Symbol.Iterator]";
3041
3042         # Global objects add RuntimeEnabled operations after creation so do not add them to the static table.
3043         if ($isInstance && NeedsRuntimeCheck($operation)) {
3044             $propertyCount -= 1;
3045             next;
3046         }
3047
3048         my $name = $operation->name;
3049         push(@$hashKeys, $name);
3050
3051         my $functionName = GetFunctionName($interface, $className, $operation);
3052         push(@$hashValue1, $functionName);
3053
3054         my $functionLength = GetFunctionLength($operation);
3055
3056         if ($operation->extendedAttributes->{DOMJIT}) {
3057             push(@$hashValue2, "&DOMJITSignatureFor" . $interface->type->name . $codeGenerator->WK_ucfirst($operation->name));
3058         } else {
3059             push(@$hashValue2, $functionLength);
3060         }
3061
3062         push(@$hashSpecials, ComputeFunctionSpecial($interface, $operation));
3063
3064         my $conditional = GetConditionalForOperationConsideringOverloads($operation);
3065         $conditionals->{$name} = $conditional if $conditional;
3066
3067         if (NeedsRuntimeCheck($operation)) {
3068             push(@$runtimeEnabledOperations, $operation);
3069         }
3070
3071         if ($operation->extendedAttributes->{EnabledBySetting}) {
3072             push(@$settingsEnabledOperations, $operation);
3073         }
3074     }
3075
3076     return $propertyCount;
3077 }
3078
3079 # This computes an effective overload set for a given operation / constructor,
3080 # which represents the allowable invocations.This set is used as input for
3081 # the Web IDL overload resolution algorithm.
3082 # http://heycam.github.io/webidl/#dfn-effective-overload-set
3083 sub ComputeEffectiveOverloadSet
3084 {
3085     my ($overloads) = @_;
3086
3087     my %allSets;
3088     my $addTuple = sub {
3089         my $tuple = shift;
3090         # The Web IDL specification uses a flat set of tuples but we use a hash where the key is the
3091         # number of parameters and the value is the set of tuples for the given number of parameters.
3092         my $length = scalar(@{@$tuple[1]});
3093         if (!exists($allSets{$length})) {
3094             $allSets{$length} = [ $tuple ];
3095         } else {
3096             push(@{$allSets{$length}}, $tuple);
3097         }
3098     };
3099
3100     my $m = LengthOfLongestOperationParameterList($overloads);
3101     foreach my $overload (@{$overloads}) {
3102         my $n = @{$overload->arguments};
3103         my @t;
3104         my @o;
3105         my $isVariadic = 0;
3106         foreach my $argument (@{$overload->arguments}) {
3107             push(@t, $argument->type);
3108             if ($argument->isOptional) {
3109                 push(@o, "optional");
3110             } elsif ($argument->isVariadic) {
3111                 push(@o, "variadic");
3112                 $isVariadic = 1;
3113             } else {
3114                 push(@o, "required");
3115             }
3116         }
3117         &$addTuple([$overload, [@t], [@o]]);
3118         if ($isVariadic) {
3119             my @newT = @t;
3120             my @newO = @o;
3121             for (my $i = $n; $i < $m; $i++) {
3122                 push(@newT, $t[-1]);
3123                 push(@newO, "variadic");
3124                 &$addTuple([$overload, [@newT], [@newO]]);
3125             }
3126         }
3127         for (my $i = $n - 1; $i >= 0; $i--) {
3128             my $argument = @{$overload->arguments}[$i];
3129             last unless ($argument->isOptional || $argument->isVariadic);
3130             pop(@t);
3131             pop(@o);
3132             &$addTuple([$overload, [@t], [@o]]);
3133         }
3134     }
3135     return %allSets;
3136 }
3137
3138 sub IsIDLTypeDistinguishableWithUnionForOverloadResolution
3139 {
3140     my ($type, $unionSubtypes) = @_;
3141
3142     assert("First type should not be a union") if $type->isUnion;
3143     for my $unionSubType (@$unionSubtypes) {
3144         return 0 unless AreTypesDistinguishableForOverloadResolution($type, $unionSubType);
3145     }
3146     return 1;
3147 }
3148
3149 # Determines if two types are distinguishable in the context of overload resolution,
3150 # according to the Web IDL specification:
3151 # http://heycam.github.io/webidl/#dfn-distinguishable
3152 sub AreTypesDistinguishableForOverloadResolution
3153 {
3154     my ($typeA, $typeB) = @_;
3155
3156     my $isCallbackFunctionOrDictionary = sub {
3157         my $type = shift;
3158         return $codeGenerator->IsCallbackFunction($type) || $codeGenerator->IsDictionaryType($type);
3159     };
3160
3161     # Two types are distinguishable for overload resolution if at most one of the two includes a nullable type.
3162     return 0 if $typeA->isNullable && $typeB->isNullable;
3163
3164     # Union types: typeA and typeB  are distinguishable if:
3165     # - Both types are either a union type or nullable union type, and each member type of the one is
3166     #   distinguishable with each member type of the other.
3167     # - One type is a union type or nullable union type, the other is neither a union type nor a nullable
3168     #   union type, and each member type of the first is distinguishable with the second.
3169     if ($typeA->isUnion && $typeB->isUnion) {
3170         for my $unionASubType (@{$typeA->subtypes}) {
3171             return 0 unless IsIDLTypeDistinguishableWithUnionForOverloadResolution($unionASubType, $typeB->subtypes);
3172         }
3173         return 1;
3174     } elsif ($typeA->isUnion) {
3175         return IsIDLTypeDistinguishableWithUnionForOverloadResolution($typeB, $typeA->subtypes);
3176     } elsif ($typeB->isUnion) {
3177         return IsIDLTypeDistinguishableWithUnionForOverloadResolution($typeA, $typeB->subtypes);
3178     }
3179
3180     return 0 if $typeA->name eq $typeB->name;
3181     return 0 if $typeA->name eq "object" or $typeB->name eq "object";
3182     return 0 if $codeGenerator->IsNumericType($typeA) && $codeGenerator->IsNumericType($typeB);
3183     return 0 if $codeGenerator->IsStringOrEnumType($typeA) && $codeGenerator->IsStringOrEnumType($typeB);
3184     return 0 if $codeGenerator->IsDictionaryType($typeA) && $codeGenerator->IsDictionaryType($typeB);
3185     return 0 if $codeGenerator->IsCallbackInterface($typeA) && $codeGenerator->IsCallbackInterface($typeB);
3186     return 0 if &$isCallbackFunctionOrDictionary($typeA) && &$isCallbackFunctionOrDictionary($typeB);
3187     return 0 if $codeGenerator->IsSequenceOrFrozenArrayType($typeA) && $codeGenerator->IsSequenceOrFrozenArrayType($typeB);
3188     # FIXME: return 0 if $typeA and $typeB are both exception types.
3189     return 1;
3190 }
3191
3192 # If there is more than one entry in an effective overload set that has a given type list length,
3193 # then for those entries there must be an index i such that for each pair of entries the types
3194 # at index i are distinguishable. The lowest such index is termed the distinguishing argument index.
3195 # http://heycam.github.io/webidl/#dfn-distinguishing-argument-index
3196 sub GetDistinguishingArgumentIndex
3197 {
3198     my ($operation, $S) = @_;
3199
3200     # FIXME: Consider all the tuples, not just the 2 first ones?
3201     my $firstTupleTypes = @{@{$S}[0]}[1];
3202     my $secondTupleTypes = @{@{$S}[1]}[1];
3203     for (my $index = 0; $index < scalar(@$firstTupleTypes); $index++) {
3204         return $index if AreTypesDistinguishableForOverloadResolution(@{$firstTupleTypes}[$index], @{$secondTupleTypes}[$index]);
3205     }
3206     die "Undistinguishable overloads for operation " . $operation->name . " with length: " . scalar(@$firstTupleTypes);
3207 }
3208
3209 sub GetOverloadThatMatches
3210 {
3211     my ($S, $parameterIndex, $matches) = @_;
3212
3213     for my $tuple (@{$S}) {
3214         my $type = @{@{$tuple}[1]}[$parameterIndex];
3215         my $optionality = @{@{$tuple}[2]}[$parameterIndex];
3216         if ($type->isUnion) {
3217             for my $subtype (GetFlattenedMemberTypes($type)) {
3218                 return @{$tuple}[0] if $matches->($subtype, $optionality);
3219             }
3220         } else {
3221             return @{$tuple}[0] if $matches->($type, $optionality);
3222         }
3223     }
3224 }
3225
3226 sub GetOverloadThatMatchesIgnoringUnionSubtypes
3227 {
3228     my ($S, $parameterIndex, $matches) = @_;
3229
3230     for my $tuple (@{$S}) {
3231         my $type = @{@{$tuple}[1]}[$parameterIndex];
3232         my $optionality = @{@{$tuple}[2]}[$parameterIndex];
3233         return @{$tuple}[0] if $matches->($type, $optionality);
3234     }
3235 }
3236
3237 sub GetConditionalForOperationConsideringOverloads
3238 {
3239     my $operation = shift;
3240
3241     return $operation->extendedAttributes->{Conditional} unless $operation->{overloads};
3242
3243     my %conditions;
3244     foreach my $overload (@{$operation->{overloads}}) {
3245         my $conditional = $overload->extendedAttributes->{Conditional};
3246         return unless $conditional;
3247         $conditions{$conditional} = 1;
3248     }
3249     return join("|", keys %conditions);
3250 }
3251
3252 # Implements the overload resolution algorithm, as defined in the Web IDL specification:
3253 # http://heycam.github.io/webidl/#es-overloads
3254 sub GenerateOverloadDispatcher
3255 {
3256     my ($operation, $interface, $overloadFunctionPrefix, $overloadFunctionSuffix, $parametersToForward) = @_;
3257     
3258     my %allSets = ComputeEffectiveOverloadSet($operation->{overloads});
3259
3260     my $generateOverloadCallIfNecessary = sub {
3261         my ($overload, $condition, $include) = @_;
3262         return unless $overload;
3263         my $conditionalString = $codeGenerator->GenerateConditionalString($overload);
3264         push(@implContent, "#if ${conditionalString}\n") if $conditionalString;
3265         push(@implContent, "        if ($condition)\n    ") if $condition;
3266         push(@implContent, "        return " . $overloadFunctionPrefix . $overload->{overloadIndex} . $overloadFunctionSuffix . "(${parametersToForward});\n");
3267         push(@implContent, "#endif\n") if $conditionalString;
3268         AddToImplIncludes($include, $overload->extendedAttributes->{Conditional}) if $include;
3269     };
3270     my $isOptionalParameter = sub {
3271         my ($type, $optionality) = @_;
3272         return $optionality eq "optional";
3273     };
3274     my $isDictionaryOrRecordParameter = sub {
3275         my ($type, $optionality) = @_;
3276         return $codeGenerator->IsDictionaryType($type) || $codeGenerator->IsRecordType($type);
3277     };
3278     my $isNullableOrDictionaryOrRecordOrUnionContainingOne = sub {
3279         my ($type, $optionality) = @_;
3280         return 1 if $type->isNullable;
3281         if ($type->isUnion) {
3282             for my $subtype (GetFlattenedMemberTypes($type)) {
3283                 return 1 if $type->isNullable || &$isDictionaryOrRecordParameter($subtype, $optionality);
3284             }
3285             return 0;
3286         } else {
3287             return &$isDictionaryOrRecordParameter($type, $optionality);
3288         }
3289     };
3290     my $isObjectOrErrorParameter = sub {
3291         my ($type, $optionality) = @_;
3292         return $type->name eq "object" || $type->name eq "Error";
3293     };
3294     my $isObjectOrErrorOrDOMExceptionParameter = sub {
3295         my ($type, $optionality) = @_;
3296         return 1 if &$isObjectOrErrorParameter($type, $optionality);
3297         return $type->name eq "DOMException";
3298     };
3299     my $isObjectOrCallbackFunctionParameter = sub {
3300         my ($type, $optionality) = @_;
3301         return $type->name eq "object" || $codeGenerator->IsCallbackFunction($type);
3302     };
3303     my $isSequenceOrFrozenArrayParameter = sub {
3304         my ($type, $optionality) = @_;
3305         return $codeGenerator->IsSequenceOrFrozenArrayType($type);
3306     };
3307     my $isDictionaryOrRecordOrObjectOrCallbackInterfaceParameter = sub {
3308         my ($type, $optionality) = @_;
3309         return 1 if &$isDictionaryOrRecordParameter($type, $optionality);
3310         return 1 if $type->name eq "object";
3311         return 1 if $codeGenerator->IsCallbackInterface($type) && !$codeGenerator->IsCallbackFunction($type);
3312         return 0;
3313     };
3314     my $isBooleanParameter = sub {
3315         my ($type, $optionality) = @_;
3316         return $type->name eq "boolean";
3317     };
3318     my $isNumericParameter = sub {
3319         my ($type, $optionality) = @_;
3320         return $codeGenerator->IsNumericType($type);
3321     };
3322     my $isStringOrEnumParameter = sub {
3323         my ($type, $optionality) = @_;
3324         return $codeGenerator->IsStringOrEnumType($type);
3325     };
3326     my $isAnyParameter = sub {
3327         my ($type, $optionality) = @_;
3328         return $type->name eq "any";
3329     };
3330
3331     my $maxArgCount = LengthOfLongestOperationParameterList($operation->{overloads});
3332
3333     push(@implContent, "    size_t argsCount = std::min<size_t>(${maxArgCount}, state->argumentCount());\n");
3334
3335     for my $length ( sort keys %allSets ) {
3336         push(@implContent, "    if (argsCount == ${length}) {\n");
3337
3338         my $S = $allSets{$length};
3339         if (scalar(@$S) > 1) {
3340             my $d = GetDistinguishingArgumentIndex($operation, $S);
3341             push(@implContent, "        JSValue distinguishingArg = state->uncheckedArgument($d);\n");
3342
3343             my $overload = GetOverloadThatMatchesIgnoringUnionSubtypes($S, $d, \&$isOptionalParameter);
3344             &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isUndefined()");
3345
3346             $overload = GetOverloadThatMatchesIgnoringUnionSubtypes($S, $d, \&$isNullableOrDictionaryOrRecordOrUnionContainingOne);
3347             &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isUndefinedOrNull()");
3348
3349             for my $tuple (@{$S}) {
3350                 my $overload = @{$tuple}[0];
3351                 my $type = @{@{$tuple}[1]}[$d];
3352
3353                 my @subtypes = $type->isUnion ? GetFlattenedMemberTypes($type) : ( $type );
3354                 for my $subtype (@subtypes) {
3355                     if ($codeGenerator->IsWrapperType($subtype) || $codeGenerator->IsBufferSourceType($subtype)) {
3356                         if ($subtype->name eq "DOMWindow") {
3357                             AddToImplIncludes("JSDOMWindowProxy.h");
3358                             &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isObject() && (asObject(distinguishingArg)->inherits(vm, JSDOMWindowProxy::info()) || asObject(distinguishingArg)->inherits(vm, JSDOMWindow::info()))");
3359                         } else {
3360                             &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isObject() && asObject(distinguishingArg)->inherits(vm, JS" . $subtype->name . "::info())");
3361                         }
3362                     }
3363                 }
3364             }
3365
3366             $overload = GetOverloadThatMatches($S, $d, \&$isObjectOrErrorOrDOMExceptionParameter);
3367             &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isObject() && asObject(distinguishingArg)->inherits(vm, JSDOMException::info())");
3368
3369             $overload = GetOverloadThatMatches($S, $d, \&$isObjectOrErrorParameter);
3370             &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isObject() && asObject(distinguishingArg)->type() == ErrorInstanceType");
3371
3372             $overload = GetOverloadThatMatches($S, $d, \&$isObjectOrCallbackFunctionParameter);
3373             &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isFunction()");
3374
3375             # FIXME: Avoid invoking GetMethod(object, Symbol.iterator) again in convert<IDLSequence<T>>(...).
3376             $overload = GetOverloadThatMatches($S, $d, \&$isSequenceOrFrozenArrayParameter);
3377             &$generateOverloadCallIfNecessary($overload, "hasIteratorMethod(*state, distinguishingArg)", "<runtime/IteratorOperations.h>");
3378
3379             $overload = GetOverloadThatMatches($S, $d, \&$isDictionaryOrRecordOrObjectOrCallbackInterfaceParameter);
3380             &$generateOverloadCallIfNecessary($overload, "distinguishingArg.isObject()");
3381
3382             my $booleanOverload = GetOverloadThatMatches($S, $d, \&$isBooleanParameter);
3383             &$generateOverloadCallIfNecessary($booleanOverload, "distinguishingArg.isBoolean()");
3384
3385             my $numericOverload = GetOverloadThatMatches($S, $d, \&$isNumericParameter);
3386             &$generateOverloadCallIfNecessary($numericOverload, "distinguishingArg.isNumber()");
3387
3388             # Fallbacks.
3389             $overload = GetOverloadThatMatches($S, $d, \&$isStringOrEnumParameter);
3390             if ($overload) {
3391                 &$generateOverloadCallIfNecessary($overload);
3392             } elsif ($numericOverload) {
3393                 &$generateOverloadCallIfNecessary($numericOverload);
3394             } elsif ($booleanOverload) {
3395                 &$generateOverloadCallIfNecessary($booleanOverload);
3396             } else {
3397                 $overload = GetOverloadThatMatches($S, $d, \&$isAnyParameter);
3398                 &$generateOverloadCallIfNecessary($overload);
3399             }
3400         } else {
3401             # Only 1 overload with this number of parameters.
3402             my $overload = @{@{$S}[0]}[0];
3403             &$generateOverloadCallIfNecessary($overload);
3404         }
3405         push(@implContent, <<END);
3406     }
3407 END
3408     }
3409     my $minArgCount = GetFunctionLength($operation);
3410     if ($minArgCount > 0) {
3411         push(@implContent, "    return argsCount < $minArgCount ? throwVMError(state, throwScope, createNotEnoughArgumentsError(state)) : throwVMTypeError(state, throwScope);\n")
3412     } else {
3413         push(@implContent, "    return throwVMTypeError(state, throwScope);\n")
3414     }
3415 }
3416
3417 # As per Web IDL specification, the length of a function Object is its number of mandatory parameters.
3418 sub GetFunctionLength
3419 {
3420     my $operation = shift;
3421
3422     my $getOverloadLength = sub {
3423         my $operation = shift;
3424
3425         my $length = 0;
3426         foreach my $argument (@{$operation->arguments}) {
3427             last if $argument->isOptional || $argument->isVariadic;
3428             $length++;
3429         }
3430         return $length;
3431     };
3432
3433     my $length = &$getOverloadLength($operation);
3434     foreach my $overload (@{$operation->{overloads}}) {
3435         my $newLength = &$getOverloadLength($overload);
3436         $length = $newLength if $newLength < $length;
3437     }
3438     return $length;
3439 }
3440
3441 sub LengthOfLongestOperationParameterList
3442 {
3443     my ($overloads) = @_;
3444     my $result = 0;
3445     foreach my $overload (@{$overloads}) {
3446         my @arguments = @{$overload->arguments};
3447         $result = @arguments if $result < @arguments;
3448     }
3449     return $result;
3450 }
3451
3452 # See http://refspecs.linux-foundation.org/cxxabi-1.83.html.
3453 sub GetGnuVTableRefForInterface
3454 {
3455     my $interface = shift;
3456     my $vtableName = GetGnuVTableNameForInterface($interface);
3457     if (!$vtableName) {
3458         return "0";
3459     }
3460     my $typename = $interface->type->name;
3461     my $offset = GetGnuVTableOffsetForType($typename);
3462     return "&" . $vtableName . "[" . $offset . "]";
3463 }
3464
3465 sub GetGnuVTableNameForInterface
3466 {
3467     my $interface = shift;
3468     my $typename = $interface->type->name;
3469     my $templatePosition = index($typename, "<");
3470     return "" if $templatePosition != -1;
3471     return "" if GetImplementationLacksVTableForInterface($interface);
3472     return "" if GetSkipVTableValidationForInterface($interface);
3473     return "_ZTV" . GetGnuMangledNameForInterface($interface);
3474 }
3475
3476 sub GetGnuMangledNameForInterface
3477 {
3478     my $interface = shift;
3479     my $typename = $interface->type->name;
3480     my $templatePosition = index($typename, "<");
3481     if ($templatePosition != -1) {
3482         return "";
3483     }
3484     my $mangledType = length($typename) . $typename;
3485     my $namespace = "WebCore";
3486     my $mangledNamespace =  "N" . length($namespace) . $namespace;
3487     return $mangledNamespace . $mangledType . "E";
3488 }
3489
3490 sub GetGnuVTableOffsetForType
3491 {
3492     my $typename = shift;
3493     if ($typename eq "SVGAElement"
3494         || $typename eq "SVGCircleElement"
3495         || $typename eq "SVGClipPathElement"
3496         || $typename eq "SVGDefsElement"
3497         || $typename eq "SVGEllipseElement"
3498         || $typename eq "SVGForeignObjectElement"
3499         || $typename eq "SVGGElement"
3500         || $typename eq "SVGImageElement"
3501         || $typename eq "SVGLineElement"
3502         || $typename eq "SVGPathElement"
3503         || $typename eq "SVGPolyElement"
3504         || $typename eq "SVGPolygonElement"
3505         || $typename eq "SVGPolylineElement"
3506         || $typename eq "SVGRectElement"
3507         || $typename eq "SVGSVGElement"
3508         || $typename eq "SVGGraphicsElement"
3509         || $typename eq "SVGSwitchElement"
3510         || $typename eq "SVGTextElement"
3511         || $typename eq "SVGUseElement") {
3512         return "3";
3513     }
3514     return "2";
3515 }
3516
3517 # See http://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B_Name_Mangling.
3518 sub GetWinVTableRefForInterface
3519 {
3520     my $interface = shift;
3521     my $vtableName = GetWinVTableNameForInterface($interface);
3522     return 0 if !$vtableName;
3523     return "__identifier(\"" . $vtableName . "\")";
3524 }
3525
3526 sub GetWinVTableNameForInterface
3527 {
3528     my $interface = shift;
3529     my $typename = $interface->type->name;
3530     my $templatePosition = index($typename, "<");
3531     return "" if $templatePosition != -1;
3532     return "" if GetImplementationLacksVTableForInterface($interface);
3533     return "" if GetSkipVTableValidationForInterface($interface);
3534     return "??_7" . GetWinMangledNameForInterface($interface) . "6B@";
3535 }
3536
3537 sub GetWinMangledNameForInterface
3538 {
3539     my $interface = shift;
3540     my $typename = $interface->type->name;
3541     my $namespace = "WebCore";
3542     return $typename . "@" . $namespace . "@@";
3543 }
3544
3545 sub GetImplementationLacksVTableForInterface
3546 {
3547     my $interface = shift;
3548     return $interface->extendedAttributes->{ImplementationLacksVTable};
3549 }
3550
3551 sub GetSkipVTableValidationForInterface
3552 {
3553     my $interface = shift;
3554     return $interface->extendedAttributes->{SkipVTableValidation};
3555 }
3556
3557 # URL becomes url, but SetURL becomes setURL.
3558 sub ToMethodName
3559 {
3560     my $param = shift;
3561     my $ret = lcfirst($param);
3562     $ret =~ s/cSS/css/ if $ret =~ /^cSS/;
3563     $ret =~ s/dOM/dom/ if $ret =~ /^dOM/;
3564     $ret =~ s/hTML/html/ if $ret =~ /^hTML/;
3565     $ret =~ s/jS/js/ if $ret =~ /^jS/;
3566     $ret =~ s/uRL/url/ if $ret =~ /^uRL/;
3567     $ret =~ s/xML/xml/ if $ret =~ /^xML/;
3568     $ret =~ s/xSLT/xslt/ if $ret =~ /^xSLT/;
3569
3570     # For HTML5 FileSystem API Flags attributes.
3571     # (create is widely used to instantiate an object and must be avoided.)
3572     $ret =~ s/^create/isCreate/ if $ret =~ /^create$/;
3573     $ret =~ s/^exclusive/isExclusive/ if $ret =~ /^exclusive$/;
3574
3575     return $ret;
3576 }
3577
3578 # Returns the conditional string that determines whether a method/attribute is enabled at runtime.
3579 # A method/attribute is enabled at runtime if either its RuntimeEnabledFeatures function returns
3580 # true or its EnabledForWorld function returns true (or both).
3581 # NOTE: Parameter passed in must have an 'extendedAttributes' property.
3582 # (e.g. DOMInterface, DOMAttribute, DOMOperation, DOMIterable, etc.)
3583 sub GenerateRuntimeEnableConditionalString
3584 {
3585     my $context = shift;
3586
3587     AddToImplIncludes("RuntimeEnabledFeatures.h");
3588     AddToImplIncludes("ScriptExecutionContext.h");
3589
3590     my @conjuncts;
3591     push @conjuncts, "jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->isSecureContext()" if $context->extendedAttributes->{SecureContext};
3592
3593     if ($context->extendedAttributes->{EnabledForWorld}) {
3594         assert("Must specify value for EnabledForWorld.") if $context->extendedAttributes->{EnabledForWorld} eq "VALUE_IS_MISSING";
3595         push @conjuncts, "worldForDOMObject(this)." . ToMethodName($context->extendedAttributes->{EnabledForWorld}) . "()";
3596     }
3597
3598     if ($context->extendedAttributes->{EnabledAtRuntime}) {
3599         assert("Must specify value for EnabledAtRuntime.") if $context->extendedAttributes->{EnabledAtRuntime} eq "VALUE_IS_MISSING";
3600         my @flags = split /&/, $context->extendedAttributes->{EnabledAtRuntime};
3601         foreach my $flag (@flags) {
3602             push @conjuncts, "RuntimeEnabledFeatures::sharedFeatures()." . ToMethodName($flag) . "Enabled()";
3603         }
3604     }
3605
3606     my $result = join(" && ", @conjuncts);
3607     $result = "($result)" if @conjuncts > 1;
3608     return $result;
3609 }
3610
3611 sub GetCastingHelperForThisObject
3612 {
3613     my $interface = shift;
3614     my $interfaceName = $interface->type->name;
3615     return "jsDynamicDowncast<JS$interfaceName*>";
3616 }
3617
3618 # http://heycam.github.io/webidl/#Unscopable
3619 sub addUnscopableProperties
3620 {
3621     my $interface = shift;
3622
3623     my @unscopables;
3624     foreach my $operationOrAttribute (@{$interface->operations}, @{$interface->attributes}) {
3625         push(@unscopables, $operationOrAttribute->name) if $operationOrAttribute->extendedAttributes->{Unscopable};
3626     }
3627     return if scalar(@unscopables) == 0;
3628
3629     AddToImplIncludes("<runtime/ObjectConstructor.h>");
3630     push(@implContent, "    JSObject& unscopables = *constructEmptyObject(globalObject()->globalExec(), globalObject()->nullPrototypeObjectStructure());\n");
3631     foreach my $unscopable (@unscopables) {
3632         push(@implContent, "    unscopables.putDirect(vm, Identifier::fromString(&vm, \"$unscopable\"), jsBoolean(true));\n");
3633     }
3634     push(@implContent, "    putDirectWithoutTransition(vm, vm.propertyNames->unscopablesSymbol, &unscopables, DontEnum | ReadOnly);\n");
3635 }
3636
3637 sub GetUnsafeArgumentType
3638 {
3639     my ($interface, $type) = @_;
3640
3641     my $IDLType = GetIDLType($interface, $type);
3642     return "DOMJIT::IDLJSArgumentType<${IDLType}>";
3643 }
3644
3645 sub GetArgumentTypeFilter
3646 {
3647     my ($interface, $type) = @_;
3648
3649     my $IDLType = GetIDLType($interface, $type);
3650     return "DOMJIT::IDLArgumentTypeFilter<${IDLType}>::value";
3651 }
3652
3653 sub GetResultTypeFilter
3654 {
3655     my ($interface, $type) = @_;
3656
3657     my $IDLT