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