25fb0ba894f19ab85418d9c5ec4c2fbefa489158
[WebKit-https.git] / WebCore / bindings / scripts / CodeGeneratorJS.pm
1 #
2 # KDOM IDL parser
3 #
4 # Copyright (C) 2005 Nikolas Zimmermann <wildfox@kde.org>
5 # Copyright (C) 2006 Anders Carlsson <andersca@mac.com>
6 # Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com>
7 # Copyright (C) 2006 Apple Computer, Inc.
8 #
9 # This file is part of the KDE project
10 #
11 # This library is free software; you can redistribute it and/or
12 # modify it under the terms of the GNU Library General Public
13 # License as published by the Free Software Foundation; either
14 # version 2 of the License, or (at your option) any later version.
15 #
16 # This library is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 # Library General Public License for more details.
20 #
21 # You should have received a copy of the GNU Library General Public License
22 # aint with this library; see the file COPYING.LIB.  If not, write to
23 # the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 # Boston, MA 02111-1307, USA.
25 #
26
27 package CodeGeneratorJS;
28
29 use File::stat;
30
31 my $module = "";
32 my $outputDir = "";
33
34 my @headerContent = ();
35 my @implContentHeader = ();
36 my @implContent = ();
37 my %implIncludes = ();
38
39 # Default .h template
40 my $headerTemplate = << "EOF";
41 /*
42     This file is part of the WebKit open source project.
43     This file has been generated by generate-bindings.pl. DO NOT MODIFY!
44
45     This library is free software; you can redistribute it and/or
46     modify it under the terms of the GNU Library General Public
47     License as published by the Free Software Foundation; either
48     version 2 of the License, or (at your option) any later version.
49
50     This library is distributed in the hope that it will be useful,
51     but WITHOUT ANY WARRANTY; without even the implied warranty of
52     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
53     Library General Public License for more details.
54
55     You should have received a copy of the GNU Library General Public License
56     along with this library; see the file COPYING.LIB.  If not, write to
57     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
58     Boston, MA 02111-1307, USA.
59 */
60 EOF
61
62 # Default constructor
63 sub new
64 {
65     my $object = shift;
66     my $reference = { };
67
68     $codeGenerator = shift;
69     $outputDir = shift;
70
71     bless($reference, $object);
72     return $reference;
73 }
74
75 sub finish
76 {
77     my $object = shift;
78
79     # Commit changes!
80     $object->WriteData();
81 }
82
83 sub leftShift($$) {
84     my ($value, $distance) = @_;
85     return (($value << $distance) & 0xFFFFFFFF);
86 }
87
88 # Params: 'domClass' struct
89 sub GenerateInterface
90 {
91     my $object = shift;
92     my $dataNode = shift;
93     my $defines = shift;
94
95     # Start actual generation
96     $object->GenerateHeader($dataNode);
97     $object->GenerateImplementation($dataNode);
98
99     my $name = $dataNode->name;
100
101     # Open files for writing
102     my $headerFileName = "$outputDir/JS$name.h";
103     my $implFileName = "$outputDir/JS$name.cpp";
104
105     open($IMPL, ">$implFileName") || die "Couldn't open file $implFileName";
106     open($HEADER, ">$headerFileName") || die "Couldn't open file $headerFileName";
107 }
108
109 # Params: 'idlDocument' struct
110 sub GenerateModule
111 {
112     my $object = shift;
113     my $dataNode = shift;
114
115     $module = $dataNode->module;
116 }
117
118 sub GetParentClassName
119 {
120     my $dataNode = shift;
121
122     return $dataNode->extendedAttributes->{"LegacyParent"} if $dataNode->extendedAttributes->{"LegacyParent"};
123     return "KJS::DOMObject" if @{$dataNode->parents} eq 0;
124     return "JS" . $codeGenerator->StripModule($dataNode->parents(0));
125 }
126
127 sub GetLegacyHeaderIncludes
128 {
129     my $legacyParent = shift;
130
131     return "#include \"JSHTMLInputElementBase.h\"\n\n" if $legacyParent eq "JSHTMLInputElementBase";
132     return "#include \"kjs_window.h\"\n\n" if $legacyParent eq "KJS::Window";
133     return "#include \"kjs_domnode.h\"\n\n" if $legacyParent eq "KJS::DOMNode";
134     return "#include \"kjs_events.h\"\n\n" if $module eq "events";
135     return "#include \"kjs_dom.h\"\n\n" if $module eq "core";
136     return "#include \"kjs_css.h\"\n\n" if $module eq "css";
137     return "#include \"kjs_html.h\"\n\n" if $module eq "html";
138     return "#include \"kjs_traversal.h\"\n\n" if $module eq "traversal";
139
140     die "Don't know what headers to include for module $module";
141 }
142
143 sub AvoidInclusionOfType
144 {
145     my $type = shift;
146
147     # Special case: SVGRect.h / SVGPoint.h / SVGNumber.h do not exist.
148     return 1 if $type eq "SVGRect" or $type eq "SVGPoint" or $type eq "SVGNumber";
149     return 0;
150 }
151
152 sub AddIncludesForType
153 {
154     my $type = $codeGenerator->StripModule(shift);
155
156     # When we're finished with the one-file-per-class
157     # reorganization, we won't need these special cases.
158     if ($codeGenerator->IsPrimitiveType($type) or AvoidInclusionOfType($type)
159         or $type eq "DOMString" or $type eq "DOMObject" or $type eq "RGBColor" or $type eq "Rect") {
160     } elsif ($type =~ /SVGPathSeg/) {
161         $joinedName = $type;
162         $joinedName =~ s/Abs|Rel//;
163         $implIncludes{"${joinedName}.h"} = 1;
164     } else {
165         # default, include the same named file
166         $implIncludes{"${type}.h"} = 1;
167     }
168
169     # additional includes (things needed to compile the bindings but not the header)
170
171     if ($type eq "CanvasRenderingContext2D") {
172         $implIncludes{"CanvasGradient.h"} = 1;
173         $implIncludes{"CanvasPattern.h"} = 1;
174         $implIncludes{"CanvasStyle.h"} = 1;
175     }
176
177     if ($type eq "CanvasGradient" or $type eq "XPathNSResolver") {
178         $implIncludes{"PlatformString.h"} = 1;
179     }
180 }
181
182 sub AddIncludesForSVGAnimatedType
183 {
184     my $type = shift;
185     $type =~ s/SVGAnimated//;
186
187     if ($type eq "SVGRect" or $type eq "SVGPoint" or $type eq "SVGNumber") {
188         $implIncludes{"JSSVG$type.h"} = 1;
189     } elsif ($type eq "String") {
190         $implIncludes{"PlatformString.h"} = 1;
191     }
192 }
193
194 sub AddClassForwardIfNeeded
195 {
196     my $implClassName = shift;
197
198     # SVGAnimatedLength/Number/etc.. are typedefs to SVGAnimtatedTemplate, so don't use class forwards for them!
199     push(@headerContent, "class $implClassName;\n\n") unless $codeGenerator->IsSVGAnimatedType($implClassName);
200 }
201
202 sub HashValueForClassAndName
203 {
204     my $class = shift;
205     my $name = shift;
206
207     # SVG Filter enums live in WebCore namespace (platform/graphics/)
208     if ($class =~ /^SVGFE*/ or $class =~ /^SVGComponentTransferFunctionElement$/) {
209         return "WebCore::$name";
210     }
211
212     return "${class}::$name";
213 }
214
215 sub GenerateHeader
216 {
217     my $object = shift;
218     my $dataNode = shift;
219
220     my $interfaceName = $dataNode->name;
221     my $className = "JS$interfaceName";
222     my $implClassName = $interfaceName;
223
224     # We only support multiple parents with SVG (for now).
225     if (@{$dataNode->parents} > 1) {
226         die "A class can't have more than one parent" unless $interfaceName =~ /SVG/;
227         $codeGenerator->AddMethodsConstantsAndAttributesFromParentClasses($dataNode);
228     }
229
230     my $hasLegacyParent = $dataNode->extendedAttributes->{"LegacyParent"};
231     my $hasRealParent = @{$dataNode->parents} > 0;
232     my $hasParent = $hasLegacyParent || $hasRealParent;
233     my $parentClassName = GetParentClassName($dataNode);
234     my $conditional = $dataNode->extendedAttributes->{"Conditional"};
235
236     # - Add default header template
237     @headerContent = split("\r", $headerTemplate);
238
239     # - Add header protection
240     push(@headerContent, "\n#ifndef $className" . "_H");
241     push(@headerContent, "\n#define $className" . "_H\n\n");
242
243     push(@headerContent, "#ifdef ${conditional}_SUPPORT\n\n") if $conditional;
244
245     if (exists $dataNode->extendedAttributes->{"LegacyParent"}) {
246         push(@headerContent, GetLegacyHeaderIncludes($dataNode->extendedAttributes->{"LegacyParent"}));
247     } else {
248         if ($hasParent) {
249             push(@headerContent, "#include \"$parentClassName.h\"\n");
250         } else {
251             push(@headerContent, "#include \"kjs_binding.h\"\n");
252         }
253     }
254
255     my $numConstants = @{$dataNode->constants};
256     my $numAttributes = @{$dataNode->attributes};
257     my $numFunctions = @{$dataNode->functions};
258
259     push(@headerContent, "\nnamespace WebCore {\n\n");
260
261     # Implementation class forward declaration
262     AddClassForwardIfNeeded($implClassName);
263
264     # Class declaration
265     push(@headerContent, "class $className : public $parentClassName {\n");
266     push(@headerContent, "public:\n");
267
268     # Constructor
269     if ($dataNode->extendedAttributes->{"DoNotCache"}) {
270         push(@headerContent, "    $className($implClassName*);\n");
271     } else {
272         push(@headerContent, "    $className(KJS::ExecState*, $implClassName*);\n");
273     }
274
275     # Destructor
276     if (!$hasParent or $interfaceName eq "Document") {
277         push(@headerContent, "    virtual ~$className();\n");
278     }
279
280     # Getters
281     if ($numAttributes > 0) {
282         push(@headerContent, "    virtual bool getOwnPropertySlot(KJS::ExecState*, const KJS::Identifier&, KJS::PropertySlot&);\n");
283         push(@headerContent, "    KJS::JSValue* getValueProperty(KJS::ExecState*, int token) const;\n");
284     }
285
286     # Check if we have any writable properties
287     my $hasReadWriteProperties = 0;
288     foreach (@{$dataNode->attributes}) {
289         if ($_->type !~ /^readonly\ attribute$/) {
290             $hasReadWriteProperties = 1;
291         }
292     }
293
294     if ($hasReadWriteProperties) {
295         push(@headerContent, "    virtual void put(KJS::ExecState*, const KJS::Identifier&, KJS::JSValue*, int attr = KJS::None);\n");
296         push(@headerContent, "    void putValueProperty(KJS::ExecState*, int, KJS::JSValue*, int attr);\n");
297     }
298
299     # Class info
300     push(@headerContent, "    virtual const KJS::ClassInfo* classInfo() const { return &info; }\n");
301     push(@headerContent, "    static const KJS::ClassInfo info;\n");
302
303     # Custom mark function
304     if ($dataNode->extendedAttributes->{"CustomMarkFunction"}) {
305         push(@headerContent, "\n    virtual void mark();\n\n");
306     }
307
308     # Constructor object getter
309     if ($dataNode->extendedAttributes->{"GenerateConstructor"}) {
310         push(@headerContent, "    static KJS::JSValue* getConstructor(KJS::ExecState*);\n")
311     }
312
313     my $numCustomFunctions = 0;
314     my $numCustomAttributes = 0;
315
316     # Attribute and function enums
317     if ($numAttributes + $numFunctions > 0) {
318         push(@headerContent, "    enum {\n")
319     }
320
321     if ($numAttributes > 0) {
322         push(@headerContent, "        // Attributes\n        ");
323
324         my $i = -1;
325         foreach (@{$dataNode->attributes}) {
326             my $attribute = $_;
327
328             $numCustomAttributes++ if $attribute->signature->extendedAttributes->{"Custom"};
329
330             $i++;
331             if ((($i % 4) eq 0) and ($i ne 0)) {
332                 push(@headerContent, "\n        ");
333             }
334
335             my $value = $attribute->signature->type =~ /Constructor$/
336                       ? $attribute->signature->name . "ConstructorAttrNum"
337                       : ucfirst($attribute->signature->name) . "AttrNum";
338             $value .= ", " if (($i < $numAttributes - 1));
339             $value .= ", " if (($i eq $numAttributes - 1) and ($numFunctions ne 0));
340             push(@headerContent, $value);
341         }
342     }
343
344     if ($numFunctions > 0) {
345         push(@headerContent, "\n\n") if $numAttributes > 0;
346         push(@headerContent,"        // Functions\n        ");
347
348         $i = -1;
349         foreach my $function (@{$dataNode->functions}) {
350             $i++;
351
352             push(@headerContent, "\n        ") if ((($i % 4) eq 0) and ($i ne 0));
353
354             $numCustomFunctions++ if $function->signature->extendedAttributes->{"Custom"};
355
356             my $value = ucfirst($function->signature->name) . "FuncNum";
357             $value .= ", " if ($i < $numFunctions - 1);
358             push(@headerContent, $value);
359         }
360     }
361
362     push(@headerContent, "\n    };\n") if ($numAttributes + $numFunctions > 0);
363
364     if ($numCustomAttributes > 0) {
365         push(@headerContent, "\n    // Custom attributes\n");
366
367         foreach my $attribute (@{$dataNode->attributes}) {
368             if ($attribute->signature->extendedAttributes->{"Custom"}) {
369                 push(@headerContent, "    KJS::JSValue* " . $attribute->signature->name . "(KJS::ExecState*) const;\n");
370                 if ($attribute->type !~ /^readonly/) {
371                     push(@headerContent, "    void set" . ucfirst($attribute->signature->name) . "(KJS::ExecState*, KJS::JSValue*);\n");
372                 }
373             }
374         }
375     }
376
377     if ($numCustomFunctions > 0) {
378         push(@headerContent, "\n    // Custom functions\n");
379         foreach my $function (@{$dataNode->functions}) {
380             if ($function->signature->extendedAttributes->{"Custom"}) {
381                 push(@headerContent, "    KJS::JSValue* " . $function->signature->name . "(KJS::ExecState*, const KJS::List&);\n");
382             }
383         }
384     }
385
386     # Index setter
387     if ($dataNode->extendedAttributes->{"HasCustomIndexSetter"}) {
388         push(@headerContent, "    void indexSetter(KJS::ExecState*, const KJS::Identifier &propertyName, KJS::JSValue*, int attr);\n");
389     }
390
391     if (!$hasParent) {
392         push(@headerContent, "    $implClassName* impl() const { return m_impl.get(); }\n");
393         push(@headerContent, "private:\n");
394         push(@headerContent, "    RefPtr<$implClassName> m_impl;\n");
395     } elsif ($dataNode->extendedAttributes->{"GenerateNativeConverter"}) {
396         push(@headerContent, "    $implClassName* impl() const;\n");
397     }
398
399     # Index getter
400     if ($dataNode->extendedAttributes->{"HasIndexGetter"}) {
401         push(@headerContent, "private:\n");
402         push(@headerContent, "    static KJS::JSValue* indexGetter(KJS::ExecState*, KJS::JSObject*, const KJS::Identifier&, const KJS::PropertySlot&);\n");
403     }
404     # Name getter
405     if ($dataNode->extendedAttributes->{"HasNameGetter"} || $dataNode->extendedAttributes->{"HasOverridingNameGetter"}) {
406         push(@headerContent, "private:\n");
407         push(@headerContent, "    static KJS::JSValue* nameGetter(KJS::ExecState*, KJS::JSObject*, const KJS::Identifier&, const KJS::PropertySlot&);\n");
408         push(@headerContent, "    static bool canGetItemsForName(KJS::ExecState*, $implClassName*, const AtomicString&);\n")
409     }
410
411     push(@headerContent, "};\n\n");
412
413     if (!$hasParent) {
414         push(@headerContent, "KJS::JSValue* toJS(KJS::ExecState*, $implClassName*);\n");
415     }
416     if (!$hasParent || $dataNode->extendedAttributes->{"GenerateNativeConverter"}) {
417         push(@headerContent, "$implClassName* to${interfaceName}(KJS::JSValue*);\n");
418     }
419     push(@headerContent, "\n");
420
421     # Add prototype declaration -- code adopted from the KJS_DEFINE_PROTOTYPE and KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE macros
422     push(@headerContent, "class ${className}Proto : public KJS::JSObject {\n");
423     push(@headerContent, "public:\n");
424     if ($dataNode->extendedAttributes->{"DoNotCache"}) {
425         push(@headerContent, "    static KJS::JSObject* self();\n");
426     } else {
427         push(@headerContent, "    static KJS::JSObject* self(KJS::ExecState* exec);\n");
428     }
429     push(@headerContent, "    virtual const KJS::ClassInfo* classInfo() const { return &info; }\n");
430     push(@headerContent, "    static const KJS::ClassInfo info;\n");
431     if ($numFunctions > 0 || $numConstants > 0) {
432         push(@headerContent, "    bool getOwnPropertySlot(KJS::ExecState*, const KJS::Identifier&, KJS::PropertySlot&);\n");
433     }
434     if ($numConstants ne 0) {
435         push(@headerContent, "    KJS::JSValue* getValueProperty(KJS::ExecState*, int token) const;\n");
436     }
437     if ($dataNode->extendedAttributes->{"DoNotCache"}) {
438         push(@headerContent, "    ${className}Proto() { }\n");
439     } else {
440         push(@headerContent, "    ${className}Proto(KJS::ExecState* exec)\n");
441         if ($hasParent && $parentClassName ne "KJS::DOMCSSRule" && $parentClassName ne "KJS::DOMNodeFilter") {
442             push(@headerContent, "        : KJS::JSObject(${parentClassName}Proto::self(exec)) { }\n");
443         } else {
444             push(@headerContent, "        : KJS::JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()) { }\n");
445         }
446     }
447
448     push(@headerContent, "};\n\n");
449
450     push(@headerContent, "}\n\n");
451
452     push(@headerContent, "#endif // ${conditional}_SUPPORT\n\n") if $conditional;
453
454     push(@headerContent, "#endif\n");
455 }
456
457 sub GenerateImplementation
458 {
459     my $object = shift;
460     my $dataNode = shift;
461
462     my $interfaceName = $dataNode->name;
463     my $className = "JS$interfaceName";
464     my $implClassName = $interfaceName;
465
466     my $hasLegacyParent = $dataNode->extendedAttributes->{"LegacyParent"};
467     my $hasRealParent = @{$dataNode->parents} > 0;
468     my $hasParent = $hasLegacyParent || $hasRealParent;
469     my $parentClassName = GetParentClassName($dataNode);
470     my $conditional = $dataNode->extendedAttributes->{"Conditional"};
471
472     # - Add default header template
473     @implContentHeader = split("\r", $headerTemplate);
474     push(@implContentHeader, "\n#include \"config.h\"\n\n");
475
476     push(@implContentHeader, "#ifdef ${conditional}_SUPPORT\n\n") if $conditional;
477
478     if ($className =~ /^JSSVGAnimated/) {
479         AddIncludesForSVGAnimatedType($interfaceName);
480     }
481
482     push(@implContentHeader, "#include \"SVGAnimatedTemplate.h\"\n") if ($className =~ /SVG/);
483     push(@implContentHeader, "#include \"$className.h\"\n\n");
484     push(@implContentHeader, "#include <wtf/GetPtr.h>\n\n");
485
486     AddIncludesForType($interfaceName);
487
488     @implContent = ();
489
490     push(@implContent, "\nusing namespace KJS;\n\n");
491     push(@implContent, "namespace WebCore {\n\n");
492
493     # - Add all attributes in a hashtable definition
494     my $numAttributes = @{$dataNode->attributes};
495     if ($numAttributes > 0) {
496         my $hashSize = $numAttributes;
497         my $hashName = $className . "Table";
498
499         my @hashKeys = ();      # ie. 'insertBefore'
500         my @hashValues = ();    # ie. 'JSNode::InsertBefore'
501         my @hashSpecials = ();    # ie. 'DontDelete|Function'
502         my @hashParameters = ();  # ie. '2'
503
504         foreach my $attribute (@{$dataNode->attributes}) {
505             my $name = $attribute->signature->name;
506             push(@hashKeys, $name);
507
508             my $value = $className . "::" . ($attribute->signature->type =~ /Constructor$/
509                                        ? $attribute->signature->name . "ConstructorAttrNum"
510                                        : ucfirst($attribute->signature->name) . "AttrNum");
511             push(@hashValues, $value);
512
513             my $special = "DontDelete";
514             $special .= "|ReadOnly" if ($attribute->type =~ /readonly/);
515             push(@hashSpecials, $special);
516
517             my $numParameters = "0";
518             push(@hashParameters, $numParameters);
519         }
520
521         $object->GenerateHashTable($hashName, $hashSize,
522                                    \@hashKeys, \@hashValues,
523                                    \@hashSpecials, \@hashParameters);
524     }
525
526     my $numConstants = @{$dataNode->constants};
527     my $numFunctions = @{$dataNode->functions};
528
529     # - Add all constants
530     if ($dataNode->extendedAttributes->{"GenerateConstructor"}) {
531         $hashSize = $numConstants;
532         $hashName = $className . "ConstructorTable";
533
534         @hashKeys = ();
535         @hashValues = ();
536         @hashSpecials = ();
537         @hashParameters = ();
538
539         foreach my $constant (@{$dataNode->constants}) {
540             my $name = $constant->name;
541             push(@hashKeys, $name);
542
543             my $value = HashValueForClassAndName($implClassName, $name);
544             push(@hashValues, $value);
545
546             my $special = "DontDelete|ReadOnly";
547             push(@hashSpecials, $special);
548
549             my $numParameters = 0;
550             push(@hashParameters, $numParameters);
551         }
552
553         $object->GenerateHashTable($hashName, $hashSize,
554                                    \@hashKeys, \@hashValues,
555                                    \@hashSpecials, \@hashParameters);
556
557         my $protoClassName;
558         $protoClassName = "${className}Proto";
559
560         push(@implContent, constructorFor($className, $protoClassName, $interfaceName, $dataNode->extendedAttributes->{"CanBeConstructed"}));
561     }
562
563     # - Add functions and constants to a hashtable definition
564     $hashSize = $numFunctions + $numConstants;
565     $hashName = $className . "ProtoTable";
566
567     @hashKeys = ();
568     @hashValues = ();
569     @hashSpecials = ();
570     @hashParameters = ();
571
572     foreach my $constant (@{$dataNode->constants}) {
573         my $name = $constant->name;
574         push(@hashKeys, $name);
575
576         my $value = HashValueForClassAndName($implClassName, $name);
577         push(@hashValues, $value);
578
579         my $special = "DontDelete|ReadOnly";
580         push(@hashSpecials, $special);
581
582         my $numParameters = 0;
583         push(@hashParameters, $numParameters);
584     }
585
586     foreach my $function (@{$dataNode->functions}) {
587         my $name = $function->signature->name;
588         push(@hashKeys, $name);
589
590         my $value = $className . "::" . ucfirst($name) . "FuncNum";
591         push(@hashValues, $value);
592
593         my $special = "DontDelete|Function";
594         push(@hashSpecials, $special);
595
596         my $numParameters = @{$function->parameters};
597         push(@hashParameters, $numParameters);
598     }
599
600     $object->GenerateHashTable($hashName, $hashSize,
601                                \@hashKeys, \@hashValues,
602                                \@hashSpecials, \@hashParameters);
603
604     if ($numFunctions > 0) {
605         push(@implContent, protoFuncFor($className));
606     }
607
608     push(@implContent, "const ClassInfo ${className}Proto::info = { \"$interfaceName\", 0, &${className}ProtoTable, 0 };\n\n");
609     if ($dataNode->extendedAttributes->{"DoNotCache"}) {
610         push(@implContent, "JSObject* ${className}Proto::self()\n");
611         push(@implContent, "{\n");
612         push(@implContent, "    return new ${className}Proto();\n");
613         push(@implContent, "}\n\n");
614     } else {
615         push(@implContent, "JSObject* ${className}Proto::self(ExecState* exec)\n");
616         push(@implContent, "{\n");
617         push(@implContent, "    return KJS::cacheGlobalObject<${className}Proto>(exec, \"[[${className}.prototype]]\");\n");
618         push(@implContent, "}\n\n");
619     }
620     if ($numConstants > 0 || $numFunctions > 0) {
621         push(@implContent, "bool ${className}Proto::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)\n");
622         push(@implContent, "{\n");
623         if ($numConstants eq 0) {
624             push(@implContent, "    return getStaticFunctionSlot<${className}ProtoFunc, JSObject>(exec, &${className}ProtoTable, this, propertyName, slot);\n");
625         } elsif ($numFunctions eq 0) {
626             push(@implContent, "    return getStaticValueSlot<${className}Proto, JSObject>(exec, &${className}ProtoTable, this, propertyName, slot);\n");
627         } else {
628             push(@implContent, "    return getStaticPropertySlot<${className}ProtoFunc, ${className}Proto, JSObject>(exec, &${className}ProtoTable, this, propertyName, slot);\n");
629         }
630         push(@implContent, "}\n\n");
631     }
632     if ($numConstants ne 0) {
633         push(@implContent, "JSValue* ${className}Proto::getValueProperty(ExecState*, int token) const\n{\n");
634         push(@implContent, "    // The token is the numeric value of its associated constant\n");
635         push(@implContent, "    return jsNumber(token);\n}\n\n");
636     }
637
638     # - Initialize static ClassInfo object
639     push(@implContent, "const ClassInfo $className" . "::info = { \"$interfaceName\", ");
640     if ($hasParent) {
641         push(@implContent, "&" .$parentClassName . "::info, ");
642     } else {
643         push(@implContent, "0, ");
644     }
645
646     if ($numAttributes > 0) {
647         push(@implContent, "&${className}Table, ");
648     } else {
649         push(@implContent, "0, ")
650     }
651     push(@implContent, "0 };\n\n");
652
653     # Constructor
654     if ($dataNode->extendedAttributes->{"DoNotCache"}) {
655         push(@implContent, "${className}::$className($implClassName* impl)\n");
656         push(@implContent, "    : $parentClassName(impl)\n");
657     } else {
658         push(@implContent, "${className}::$className(ExecState* exec, $implClassName* impl)\n");
659         if ($hasParent) {
660             push(@implContent, "    : $parentClassName(exec, impl)\n");
661         } else {
662             push(@implContent, "    : m_impl(impl)\n");
663         }
664     }
665
666     if ($dataNode->extendedAttributes->{"DoNotCache"}) {
667         push(@implContent, "{\n    setPrototype(${className}Proto::self());\n}\n\n");
668     } else {
669         push(@implContent, "{\n    setPrototype(${className}Proto::self(exec));\n}\n\n");
670     }
671
672     # Destructor
673     if (!$hasParent) {
674         push(@implContent, "${className}::~$className()\n");
675         push(@implContent, "{\n    ScriptInterpreter::forgetDOMObject(m_impl.get());\n}\n\n");
676     }
677
678     # Document needs a special destructor because it's a special case for caching. It needs
679     # its own special handling rather than relying on the caching that Node normally does.
680     if ($interfaceName eq "Document") {
681         push(@implContent, "${className}::~$className()\n");
682         push(@implContent, "{\n    ScriptInterpreter::forgetDOMObject(static_cast<${implClassName}*>(m_impl.get()));\n}\n\n");
683     }
684
685     # Attributes
686     if ($numAttributes ne 0) {
687         push(@implContent, "bool ${className}::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)\n");
688         push(@implContent, "{\n");
689         # FIXME: We need to provide scalable hooks/attributes for this kind of extension
690         if ($interfaceName eq "DOMWindow") {
691             push(@implContent, "    if (getOverridePropertySlot(exec, propertyName, slot))\n");
692             push(@implContent, "        return true;\n");
693         }
694
695         my $hasNameGetterGeneration = sub {
696             push(@implContent, "    if (canGetItemsForName(exec, static_cast<$implClassName*>(impl()), propertyName)) {\n");
697             push(@implContent, "        slot.setCustom(this, nameGetter);\n");
698             push(@implContent, "        return true;\n");
699             push(@implContent, "    }\n");
700         };
701
702         if ($dataNode->extendedAttributes->{"HasOverridingNameGetter"}) {
703             &$hasNameGetterGeneration();
704         }
705
706         my $requiresManualLookup = $dataNode->extendedAttributes->{"HasIndexGetter"} || $dataNode->extendedAttributes->{"HasNameGetter"};
707         if ($requiresManualLookup) {
708             push(@implContent, "    const HashEntry* entry = Lookup::findEntry(&${className}Table, propertyName);\n");
709             push(@implContent, "    if (entry) {\n");
710             push(@implContent, "        slot.setStaticEntry(this, entry, staticValueGetter<$className>);\n");
711             push(@implContent, "        return true;\n");
712             push(@implContent, "    }\n");
713         }
714
715         if ($dataNode->extendedAttributes->{"HasNameGetter"} || $dataNode->extendedAttributes->{"HasOverridingNameGetter"}) {
716             # if it has a prototype, we need to check that first too
717             push(@implContent, "    if (prototype()->isObject() && static_cast<JSObject*>(prototype())->hasProperty(exec, propertyName))\n");
718             push(@implContent, "        return false;\n");
719         }
720
721         if ($dataNode->extendedAttributes->{"HasIndexGetter"}) {
722             push(@implContent, "    bool ok;\n");
723             push(@implContent, "    unsigned u = propertyName.toUInt32(&ok);\n");
724             push(@implContent, "    if (ok && u < static_cast<$implClassName*>(impl())->length()) {\n");
725             push(@implContent, "        slot.setCustomIndex(this, u, indexGetter);\n");
726             push(@implContent, "        return true;\n");
727             push(@implContent, "    }\n");
728         }
729
730         if ($dataNode->extendedAttributes->{"HasNameGetter"}) {
731             &$hasNameGetterGeneration();
732         }
733
734         if ($requiresManualLookup) {
735             push(@implContent, "    return ${parentClassName}::getOwnPropertySlot(exec, propertyName, slot);\n");
736         } else {
737             push(@implContent, "    return getStaticValueSlot<$className, $parentClassName>(exec, &${className}Table, this, propertyName, slot);\n");
738         }
739         push(@implContent, "}\n\n");
740
741         push(@implContent, "JSValue* ${className}::getValueProperty(ExecState* exec, int token) const\n{\n");
742         push(@implContent, "    $implClassName* imp = static_cast<$implClassName*>(impl());\n\n");
743         push(@implContent, "    switch (token) {\n");
744
745         foreach my $attribute (@{$dataNode->attributes}) {
746             my $name = $attribute->signature->name;
747
748             if ($attribute->signature->extendedAttributes->{"Custom"}) {
749                 push(@implContent, "    case " . ucfirst($name) . "AttrNum:\n");
750                 push(@implContent, "        return $name(exec);\n");
751             } elsif ($attribute->signature->type =~ /Constructor$/) {
752                 my $constructorType = $codeGenerator->StripModule($attribute->signature->type);
753                 $constructorType =~ s/Constructor$//;
754
755                 push(@implContent, "    case " . $name . "ConstructorAttrNum:\n");
756                 push(@implContent, "        return JS" . $constructorType . "::getConstructor(exec);\n");
757             } elsif (!@{$attribute->getterExceptions}) {
758                 push(@implContent, "    case " . ucfirst($name) . "AttrNum:\n");
759                 push(@implContent, "        return " . NativeToJSValue($attribute->signature, "imp->$name()") . ";\n");
760             } else {
761                 push(@implContent, "    case " . ucfirst($name) . "AttrNum: {\n");
762                 push(@implContent, "        ExceptionCode ec = 0;\n");
763                 push(@implContent, "        KJS::JSValue* result = " . NativeToJSValue($attribute->signature, "imp->$name(ec)") . ";\n");
764                 push(@implContent, "        setDOMException(exec, ec);\n");
765                 push(@implContent, "        return result;\n");
766                 push(@implContent, "    }\n");
767             }
768         }
769
770         push(@implContent, "    }\n    return 0;\n}\n\n");
771
772         # Check if we have any writable attributes
773         my $hasReadWriteProperties = 0;
774         foreach my $attribute (@{$dataNode->attributes}) {
775             $hasReadWriteProperties = 1 if $attribute->type !~ /^readonly/;
776         }
777         if ($hasReadWriteProperties) {
778             push(@implContent, "void ${className}::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)\n");
779             push(@implContent, "{\n");
780             if ($dataNode->extendedAttributes->{"HasCustomIndexSetter"}) {
781                 push(@implContent, "    if (!lookupPut<$className>(exec, propertyName, value, attr, &${className}Table, this))\n");
782                 push(@implContent, "        indexSetter(exec, propertyName, value, attr);\n");
783             } else {
784                 push(@implContent, "    lookupPut<$className, $parentClassName>(exec, propertyName, value, attr, &${className}Table, this);\n");
785             }
786             push(@implContent, "}\n\n");
787
788             push(@implContent, "void ${className}::putValueProperty(ExecState* exec, int token, JSValue* value, int /*attr*/)\n");
789             push(@implContent, "{\n");
790             push(@implContent, "    $implClassName* imp = static_cast<$implClassName*>(impl());\n\n");
791             push(@implContent, "    switch (token) {\n");
792
793             foreach my $attribute (@{$dataNode->attributes}) {
794                 if ($attribute->type !~ /^readonly/) {
795                     my $name = $attribute->signature->name;
796
797                     if ($attribute->signature->extendedAttributes->{"Custom"}) {
798                         push(@implContent, "    case " . ucfirst($name) . "AttrNum: {\n");
799                         push(@implContent, "        set" . ucfirst($name) . "(exec, value);\n");
800                     } elsif ($attribute->signature->type =~ /Constructor$/) {
801                         my $constructorType = $attribute->signature->type;
802                         $constructorType =~ s/Constructor$//;
803
804                         $implIncludes{"JS" . $constructorType . ".h"} = 1;
805                         push(@implContent, "    case " . $name ."ConstructorAttrNum: {\n");
806                         push(@implContent, "        // Shadowing a built-in constructor\n");
807
808                         # FIXME: We need to provide scalable hooks/attributes for this kind of extension
809                         push(@implContent, "        if (isSafeScript(exec))\n");
810                         push(@implContent, "            JSObject::put(exec, \"$name\", value);\n");
811                     } else {
812                         push(@implContent, "    case " . ucfirst($name) ."AttrNum: {\n");
813                         push(@implContent, "        ExceptionCode ec = 0;\n") if @{$attribute->setterExceptions};
814                         push(@implContent, "        imp->set" . ucfirst($name) . "(" . JSValueToNative($attribute->signature, "value"));
815                         push(@implContent, ", ec") if @{$attribute->setterExceptions};
816                         push(@implContent, ");\n");
817                         push(@implContent, "        setDOMException(exec, ec);\n") if @{$attribute->setterExceptions};
818                     }
819                     push(@implContent, "        break;\n");
820                     push(@implContent, "    }\n");
821                 }
822             }
823             push(@implContent, "    }\n"); # end switch
824
825             if ($interfaceName eq "DOMWindow") {
826                 push(@implContent, "    // FIXME: Hack to prevent unused variable warning -- remove once DOMWindow includes a settable property\n");
827                 push(@implContent, "    (void)imp;\n");
828             }
829             push(@implContent, "}\n\n"); # end function
830         }
831     }
832
833     if ($dataNode->extendedAttributes->{"GenerateConstructor"}) {
834         push(@implContent, "JSValue* ${className}::getConstructor(ExecState* exec)\n{\n");
835         push(@implContent, "    return KJS::cacheGlobalObject<${className}Constructor>(exec, \"[[${interfaceName}.constructor]]\");\n");
836         push(@implContent, "}\n");
837     }
838
839     # Functions
840     if ($numFunctions ne 0) {
841         push(@implContent, "JSValue* ${className}ProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)\n{\n");
842         push(@implContent, "    if (!thisObj->inherits(&${className}::info))\n");
843         push(@implContent, "      return throwError(exec, TypeError);\n\n");
844
845         push(@implContent, "    $implClassName* imp = static_cast<$implClassName*>(static_cast<$className*>(thisObj)->impl());\n\n");
846
847         push(@implContent, "    switch (id) {\n");
848         foreach my $function (@{$dataNode->functions}) {
849             push(@implContent, "    case ${className}::" . ucfirst($function->signature->name) . "FuncNum: {\n");
850
851             if ($function->signature->extendedAttributes->{"Custom"}) {
852                 push(@implContent, "        return static_cast<${className}*>(thisObj)->" . $function->signature->name . "(exec, args);\n    }\n");
853                 next;
854             }
855
856             AddIncludesForType($function->signature->type);
857
858             if (@{$function->raisesExceptions}) {
859                 push(@implContent, "        ExceptionCode ec = 0;\n");
860             }
861
862             my $paramIndex = 0;
863             my $functionString = "imp->" . $function->signature->name . "(";
864             my $numParameters = @{$function->parameters};
865             my $hasOptionalArguments = 0;
866
867             foreach my $parameter (@{$function->parameters}) {
868                 if (!$hasOptionalArguments && $parameter->extendedAttributes->{"Optional"}) {
869                     push(@implContent, "\n        int argsCount = args.size();\n");
870                     $hasOptionalArguments = 1;
871                 }
872
873                 if ($hasOptionalArguments) {
874                     push(@implContent, "        if (argsCount < " . ($paramIndex + 1) . ") {\n");
875                     GenerateImplementationFunctionCall($function, $functionString, $paramIndex, "    " x 3);
876                     push(@implContent, "        }\n\n");
877                 }
878
879                 my $name = $parameter->name;
880                 push(@implContent, "        bool ${name}Ok;\n") if TypeCanFailConversion($parameter);
881                 push(@implContent, "        " . GetNativeType($parameter) . " $name = " . JSValueToNative($parameter, "args[$paramIndex]", TypeCanFailConversion($parameter) ? "${name}Ok" : undef) . ";\n");
882                 if (TypeCanFailConversion($parameter)) {
883                     push(@implContent, "        if (!${name}Ok) {\n");
884                     push(@implContent, "            setDOMException(exec, TYPE_MISMATCH_ERR);\n");
885                     push(@implContent, "            return jsUndefined();\n        }\n");
886                 }
887
888                 # If a parameter is "an index", it should throw an INDEX_SIZE_ERR
889                 # exception
890                 if ($parameter->extendedAttributes->{"IsIndex"}) {
891                     $implIncludes{"ExceptionCode.h"} = 1;
892                     push(@implContent, "        if ($name < 0) {\n");
893                     push(@implContent, "            setDOMException(exec, INDEX_SIZE_ERR);\n");
894                     push(@implContent, "            return jsUndefined();\n        }\n");
895                 }
896
897                 $functionString .= ", " if $paramIndex;
898                 $functionString .= $name;
899
900                 $paramIndex++;
901             }
902
903             push(@implContent, "\n");
904             GenerateImplementationFunctionCall($function, $functionString, $paramIndex, "    " x 2);
905
906             push(@implContent, "    }\n"); # end case
907         }
908         push(@implContent, "    }\n"); # end switch
909         push(@implContent, "    return 0;\n");
910         push(@implContent, "}\n");
911     }
912
913     if ($dataNode->extendedAttributes->{"HasIndexGetter"}) {
914         push(@implContent, "\nJSValue* ${className}::indexGetter(ExecState* exec, JSObject* originalObject, const Identifier& propertyName, const PropertySlot& slot)\n");
915         push(@implContent, "{\n");
916         push(@implContent, "    ${className}* thisObj = static_cast<$className*>(slot.slotBase());\n");
917         push(@implContent, "    return toJS(exec, static_cast<$implClassName*>(thisObj->impl())->item(slot.index()));\n");
918         push(@implContent, "}\n");
919     }
920
921     if (!$hasParent) {
922         push(@implContent, "KJS::JSValue* toJS(KJS::ExecState* exec, $implClassName* obj)\n");
923         push(@implContent, "{\n");
924         push(@implContent, "    return KJS::cacheDOMObject<$implClassName, $className>(exec, obj);\n");
925         push(@implContent, "}\n");
926     }
927
928     if (!$hasParent || $dataNode->extendedAttributes->{"GenerateNativeConverter"}) {
929         push(@implContent, "$implClassName* to${interfaceName}(KJS::JSValue* val)\n");
930         push(@implContent, "{\n");
931         push(@implContent, "    return val->isObject(&${className}::info) ? static_cast<$className*>(val)->impl() : 0;\n");
932         push(@implContent, "}\n");
933     }
934
935     if ($dataNode->extendedAttributes->{"GenerateNativeConverter"} && $hasParent) {
936         push(@implContent, "\n$implClassName* ${className}::impl() const\n");
937         push(@implContent, "{\n");
938         push(@implContent, "    return static_cast<$implClassName*>(${parentClassName}::impl());\n");
939         push(@implContent, "}\n");
940     }
941
942     push(@implContent, "\n}\n");
943
944     push(@implContent, "\n#endif // ${conditional}_SUPPORT\n") if $conditional;
945 }
946
947 sub GenerateImplementationFunctionCall()
948 {
949     my $function = shift;
950     my $functionString = shift;
951     my $paramIndex = shift;
952     my $indent = shift;
953
954     if (@{$function->raisesExceptions}) {
955         $functionString .= ", " if $paramIndex;
956         $functionString .= "ec";
957     }
958     $functionString .= ")";
959
960     if ($function->signature->type eq "void") {
961         push(@implContent, $indent . "$functionString;\n");
962         push(@implContent, $indent . "setDOMException(exec, ec);\n") if @{$function->raisesExceptions};
963         push(@implContent, $indent . "return jsUndefined();\n");
964     } else {
965         push(@implContent, "\n" . $indent . "KJS::JSValue* result = " . NativeToJSValue($function->signature, $functionString) . ";\n");
966         push(@implContent, $indent . "setDOMException(exec, ec);\n") if @{$function->raisesExceptions};
967         push(@implContent, $indent . "return result;\n");
968     }
969 }
970
971 sub GetNativeType
972 {
973     my $signature = shift;
974
975     my $type = $codeGenerator->StripModule($signature->type);
976
977     if ($type eq "unsigned long") {
978         # Special-case index arguments because we need to check that they aren't < 0.
979         return "int" if $signature->extendedAttributes->{"IsIndex"};
980         return "unsigned";
981     }
982
983     return $type if $type eq "unsigned short" or $type eq "float" or $type eq "AtomicString";
984     return "bool" if $type eq "boolean";
985     return "int" if $type eq "long";
986     return "String" if $type eq "DOMString";
987     return "PassRefPtr<${type}>" if $type eq "NodeFilter";
988     return "Range::CompareHow" if $type eq "CompareHow";
989     return "EventTargetNode*" if $type eq "EventTarget";
990     return "FloatRect" if $type eq "SVGRect";
991     return "FloatPoint" if $type eq "SVGPoint";
992     return "double" if $type eq "SVGNumber";
993     return "SVGPaint::SVGPaintType" if $type eq "SVGPaintType";
994
995     # Default, assume native type is a pointer with same type name as idl type
996     return "${type}*";
997 }
998
999 sub TypeCanFailConversion
1000 {
1001     my $signature = shift;
1002
1003     my $type = $codeGenerator->StripModule($signature->type);
1004
1005     # FIXME: convert to use a hash
1006
1007     return 0 if $type eq "boolean" or
1008                 $type eq "float" or
1009                 $type eq "AtomicString" or
1010                 $type eq "DOMString" or
1011                 $type eq "Node" or
1012                 $type eq "Element" or
1013                 $type eq "DocumentType" or
1014                 $type eq "EventTarget" or
1015                 $type eq "Range" or
1016                 $type eq "NodeFilter" or
1017                 $type eq "DOMWindow" or
1018                 $type eq "XPathEvaluator" or
1019                 $type eq "XPathNSResolver" or
1020                 $type eq "XPathResult" or
1021                 $type eq "SVGAngle" or
1022                 $type eq "SVGLength" or
1023                 $type eq "SVGNumber" or
1024                 $type eq "SVGPoint" or
1025                 $type eq "SVGTransform" or
1026                 $type eq "SVGPathSeg" or
1027                 $type eq "SVGMatrix" or
1028                 $type eq "SVGRect" or
1029                 $type eq "SVGElement" or
1030                 $type eq "HTMLOptionElement" or
1031                 $type eq "unsigned short" or # or can it?
1032                 $type eq "CompareHow" or # or can it?
1033                 $type eq "SVGPaintType"; # or can it?
1034
1035     if ($type eq "unsigned long" or $type eq "long" or $type eq "Attr") {
1036         $implIncludes{"ExceptionCode.h"} = 1;
1037         return 1;
1038     }
1039
1040     die "Don't know whether a JS value can fail conversion to type $type."
1041 }
1042
1043 sub JSValueToNative
1044 {
1045     my $signature = shift;
1046     my $value = shift;
1047     my $okParam = shift;
1048     my $maybeOkParam = $okParam ? ", ${okParam}" : "";
1049
1050     my $type = $codeGenerator->StripModule($signature->type);
1051
1052     return "$value->toBoolean(exec)" if $type eq "boolean";
1053     return "$value->toNumber(exec)" if $type eq "float" or $type eq "SVGNumber";
1054     return "$value->toInt32(exec${maybeOkParam})" if $type eq "unsigned long" or $type eq "long" or $type eq "unsigned short";
1055
1056     return "static_cast<Range::CompareHow>($value->toInt32(exec))" if $type eq "CompareHow";
1057     return "static_cast<SVGPaint::SVGPaintType>($value->toInt32(exec))" if $type eq "SVGPaintType";
1058
1059     return "$value->toString(exec)" if $type eq "AtomicString";
1060     if ($type eq "DOMString") {
1061         return "valueToStringWithNullCheck(exec, $value)" if $signature->extendedAttributes->{"ConvertNullToNullString"};
1062         return "$value->toString(exec)";
1063     }
1064
1065     if ($type eq "Node") {
1066         $implIncludes{"kjs_dom.h"} = 1;
1067         return "toNode($value)";
1068     }
1069
1070     if ($type eq "EventTarget") {
1071         $implIncludes{"kjs_dom.h"} = 1;
1072         return "toEventTargetNode($value)";
1073     }
1074
1075     if ($type eq "Attr") {
1076         $implIncludes{"kjs_dom.h"} = 1;
1077         return "toAttr($value${maybeOkParam})";
1078     }
1079
1080     if ($type eq "DocumentType") {
1081         $implIncludes{"kjs_dom.h"} = 1;
1082         return "toDocumentType($value)";
1083     }
1084
1085     if ($type eq "Element") {
1086         $implIncludes{"kjs_dom.h"} = 1;
1087         return "toElement($value)";
1088     }
1089
1090     if ($type eq "NodeFilter") {
1091         $implIncludes{"kjs_traversal.h"} = 1;
1092         return "toNodeFilter($value)";
1093     }
1094
1095     if ($type eq "DOMWindow") {
1096         $implIncludes{"kjs_window.h"} = 1;
1097         return "toDOMWindow($value)";
1098     }
1099
1100     if ($type eq "SVGRect") {
1101         $implIncludes{"JSSVGRect.h"} = 1;
1102         return "toFloatRect($value)";
1103     }
1104
1105     if ($type eq "SVGPoint") {
1106         $implIncludes{"JSSVGPoint.h"} = 1;
1107         return "toFloatPoint($value)";
1108     }
1109
1110
1111     # Default, assume autogenerated type conversion routines
1112     $implIncludes{"JS$type.h"} = 1;
1113     return "to$type($value)";
1114 }
1115
1116 sub NativeToJSValue
1117 {
1118     my $signature = shift;
1119     my $value = shift;
1120
1121     my $type = $codeGenerator->StripModule($signature->type);
1122
1123     return "jsBoolean($value)" if $type eq "boolean";
1124     return "jsNumber($value)" if $codeGenerator->IsPrimitiveType($type) or $type eq "SVGPaintType";
1125
1126     if ($codeGenerator->IsStringType($type)) {
1127         my $conv = $signature->extendedAttributes->{"ConvertNullStringTo"};
1128         if (defined $conv) {
1129             return "jsStringOrNull($value)" if $conv eq "Null";
1130             return "jsStringOrUndefined($value)" if $conv eq "Undefined";
1131             return "jsStringOrFalse($value)" if $conv eq "False";
1132
1133             die "Unknown value for ConvertNullStringTo extended attribute";
1134         }
1135         return "jsString($value)";
1136     }
1137
1138     if ($type eq "RGBColor") {
1139         $implIncludes{"kjs_css.h"} = 1;
1140         return "getDOMRGBColor(exec, $value)";
1141     }
1142
1143     if ($type eq "SVGRect" or $type eq "SVGPoint" or $type eq "SVGNumber") {
1144         $implIncludes{"JS$type.h"} = 1;
1145         return "getJS$type(exec, $value)";
1146     }
1147
1148     if ($type eq "HTMLCollection") {
1149         $implIncludes{"kjs_html.h"} = 1;
1150         $implIncludes{"HTMLCollection.h"} = 1;
1151         return "getHTMLCollection(exec, WTF::getPtr($value))";
1152     }
1153
1154     if ($type eq "StyleSheetList") {
1155         $implIncludes{"StyleSheetList.h"} = 1;
1156         $implIncludes{"kjs_css.h"} = 1;
1157         return "toJS(exec, WTF::getPtr($value), imp)";
1158     }
1159
1160     if ($codeGenerator->IsSVGAnimatedType($type)) {
1161         $value =~ s/\(\)//;
1162         $value .= "Animated()";
1163     }
1164
1165     if ($type eq "DOMImplementation") {
1166         $implIncludes{"kjs_dom.h"} = 1;
1167         $implIncludes{"JSDOMImplementation.h"} = 1;
1168     } elsif ($type eq "Attr" or
1169              $type eq "CDATASection" or
1170              $type eq "Comment" or
1171              $type eq "Document" or
1172              $type eq "DocumentFragment" or
1173              $type eq "DocumentType" or
1174              $type eq "Element" or
1175              $type eq "EntityReference" or
1176              $type eq "HTMLDocument" or
1177              $type eq "Node" or
1178              $type eq "ProcessingInstruction" or
1179              $type eq "Text") {
1180         $implIncludes{"kjs_dom.h"} = 1;
1181         $implIncludes{"Comment.h"} = 1;
1182         $implIncludes{"CDATASection.h"} = 1;
1183         $implIncludes{"Node.h"} = 1;
1184         $implIncludes{"Element.h"} = 1;
1185         $implIncludes{"DocumentType.h"} = 1;
1186     } elsif ($type eq "EventTarget") {
1187         $implIncludes{"kjs_dom.h"} = 1;
1188         $implIncludes{"EventTargetNode.h"} = 1;
1189     } elsif ($type eq "Event") {
1190         $implIncludes{"kjs_events.h"} = 1;
1191         $implIncludes{"Event.h"} = 1;
1192     } elsif ($type eq "NodeList" or $type eq "NamedNodeMap") {
1193         $implIncludes{"kjs_dom.h"} = 1;
1194     } elsif ($type eq "CSSStyleSheet" or $type eq "StyleSheet" or $type eq "MediaList") {
1195         $implIncludes{"CSSStyleSheet.h"} = 1;
1196         $implIncludes{"MediaList.h"} = 1;
1197         $implIncludes{"kjs_css.h"} = 1;
1198     } elsif ($type eq "CSSStyleDeclaration" or $type eq "Rect") {
1199         $implIncludes{"CSSStyleDeclaration.h"} = 1;
1200         $implIncludes{"RectImpl.h"} = 1;
1201         $implIncludes{"kjs_css.h"} = 1;
1202     } elsif ($type eq "HTMLCanvasElement") {
1203         $implIncludes{"kjs_dom.h"} = 1;
1204         $implIncludes{"HTMLCanvasElement.h"} = 1;
1205     } elsif ($type eq "DOMWindow") {
1206         $implIncludes{"kjs_window.h"} = 1;
1207     } elsif ($type eq "DOMObject") {
1208         $implIncludes{"JSCanvasRenderingContext2D.h"} = 1;
1209     } elsif ($type eq "HTMLFormElement") {
1210         $implIncludes{"kjs_html.h"} = 1;
1211         $implIncludes{"HTMLFormElement.h"} = 1;
1212     } elsif ($type =~ /SVGPathSeg/) {
1213         $implIncludes{"JS$type.h"} = 1;
1214         $joinedName = $type;
1215         $joinedName =~ s/Abs|Rel//;
1216         $implIncludes{"$joinedName.h"} = 1;
1217     } else {
1218         # Default, include header with same name.
1219         $implIncludes{"JS$type.h"} = 1;
1220         $implIncludes{"$type.h"} = 1;
1221     }
1222
1223     return "toJS(exec, WTF::getPtr($value))";
1224 }
1225
1226 # Internal Helper
1227 sub GenerateHashTable
1228 {
1229     my $object = shift;
1230
1231     my $name = shift;
1232     my $size = shift;
1233     my $keys = shift;
1234     my $values = shift;
1235     my $specials = shift;
1236     my $parameters = shift;
1237
1238     # Helpers
1239     my @table = ();
1240     my @links = ();
1241
1242     my $maxDepth = 0;
1243     my $collisions = 0;
1244     my $numEntries = $size;
1245
1246     # Collect hashtable information
1247     my $i = 0;
1248     foreach (@{$keys}) {
1249         my $depth = 0;
1250         my $h = $object->GenerateHashValue($_) % $numEntries;
1251
1252         while (defined($table[$h])) {
1253             if (defined($links[$h])) {
1254                 $h = $links[$h];
1255                 $depth++;
1256             } else {
1257                 $collisions++;
1258                 $links[$h] = $size;
1259                 $h = $size;
1260                 $size++;
1261             }
1262         }
1263
1264         $table[$h] = $i;
1265
1266         $i++;
1267         $maxDepth = $depth if ($depth > $maxDepth);
1268     }
1269
1270     # Ensure table is big enough (in case of undef entries at the end)
1271     if ($#table + 1 < $size) {
1272         $#table = $size - 1;
1273     }
1274
1275     # Start outputing the hashtables
1276     my $nameEntries = "${name}Entries";
1277     $nameEntries =~ s/:/_/g;
1278
1279     # first, build the string table
1280     my %soffset = ();
1281     if (($name =~ /Proto/) or ($name =~ /Constructor/)) {
1282         my $type = $name;
1283         my $implClass;
1284
1285         if ($name =~ /Proto/) {
1286             $type =~ s/Proto.*//;
1287             $implClass = $type; $implClass =~ s/Wrapper$//;
1288             push(@implContent, "/* Hash table for prototype */\n");
1289         } else {
1290             $type =~ s/Constructor.*//;
1291             $implClass = $type; $implClass =~ s/Constructor$//;
1292             push(@implContent, "/* Hash table for constructor */\n");
1293         }
1294     } else {
1295         push(@implContent, "/* Hash table */\n");
1296     }
1297
1298     # Dump the hash table
1299     push(@implContent, "\nstatic const HashEntry $nameEntries\[\] =\n\{\n");
1300
1301     $i = 0;
1302     foreach $entry (@table) {
1303         if (defined($entry)) {
1304             my $key = @$keys[$entry];
1305
1306             push(@implContent, "    \{ \"" . $key . "\"");
1307             push(@implContent, ", " . @$values[$entry]);
1308             push(@implContent, ", " . @$specials[$entry]);
1309             push(@implContent, ", " . @$parameters[$entry]);
1310             push(@implContent, ", ");
1311
1312             if (defined($links[$i])) {
1313                 push(@implContent, "&${nameEntries}[$links[$i]]" . " \}");
1314             } else {
1315                 push(@implContent, "0 \}");
1316             }
1317         } else {
1318             push(@implContent, "    \{ 0, 0, 0, 0, 0 \}");
1319         }
1320
1321         push(@implContent, ",") unless($i eq $size - 1);
1322         push(@implContent, "\n");
1323
1324         $i++;
1325     }
1326
1327     if ($size eq 0) {
1328         # dummy bucket -- an empty table would crash Lookup::findEntry
1329         push(@implContent, "    \{ 0, 0, 0, 0, 0 \}\n") ;
1330         $numEntries = 1;
1331         $size = 1;
1332     }
1333     push(@implContent, "};\n\n");
1334     push(@implContent, "static const HashTable $name = \n");
1335     push(@implContent, "{\n    2, $size, $nameEntries, $numEntries\n};\n\n");
1336 }
1337
1338 # Internal helper
1339 sub GenerateHashValue
1340 {
1341     my $object = shift;
1342
1343     @chars = split(/ */, $_[0]);
1344
1345     # This hash is designed to work on 16-bit chunks at a time. But since the normal case
1346     # (above) is to hash UTF-16 characters, we just treat the 8-bit chars as if they
1347     # were 16-bit chunks, which should give matching results
1348
1349     my $EXP2_32 = 4294967296;
1350
1351     my $hash = 0x9e3779b9;
1352     my $l    = scalar @chars; #I wish this was in Ruby --- Maks
1353     my $rem  = $l & 1;
1354     $l = $l >> 1;
1355
1356     my $s = 0;
1357
1358     # Main loop
1359     for (; $l > 0; $l--) {
1360         $hash   += ord($chars[$s]);
1361         my $tmp = leftShift(ord($chars[$s+1]), 11) ^ $hash;
1362         $hash   = (leftShift($hash, 16)% $EXP2_32) ^ $tmp;
1363         $s += 2;
1364         $hash += $hash >> 11;
1365     }
1366
1367     # Handle end case
1368     if ($rem != 0) {
1369         $hash += ord($chars[$s]);
1370         $hash ^= (leftShift($hash, 11)% $EXP2_32);
1371         $hash += $hash >> 17;
1372     }
1373
1374     # Force "avalanching" of final 127 bits
1375     $hash ^= leftShift($hash, 3);
1376     $hash += ($hash >> 5);
1377     $hash = ($hash% $EXP2_32);
1378     $hash ^= (leftShift($hash, 2)% $EXP2_32);
1379     $hash += ($hash >> 15);
1380     $hash = $hash% $EXP2_32;
1381     $hash ^= (leftShift($hash, 10)% $EXP2_32);
1382
1383     # this avoids ever returning a hash code of 0, since that is used to
1384     # signal "hash not computed yet", using a value that is likely to be
1385     # effectively the same as 0 when the low bits are masked
1386     $hash = 0x80000000 if ($hash == 0);
1387
1388     return $hash;
1389 }
1390
1391 # Internal helper
1392 sub WriteData
1393 {
1394     if (defined($IMPL)) {
1395         # Write content to file.
1396         print $IMPL @implContentHeader;
1397
1398         foreach my $implInclude (sort keys(%implIncludes)) {
1399             my $checkType = $implInclude;
1400             $checkType =~ s/\.h//;
1401
1402             print $IMPL "#include \"$implInclude\"\n" unless($codeGenerator->IsSVGAnimatedType($checkType));
1403         }
1404
1405         print $IMPL @implContent;
1406         close($IMPL);
1407         undef($IMPL);
1408
1409         @implHeaderContent = ();
1410         @implContent = ();
1411         %implIncludes = ();
1412     }
1413
1414     if (defined($HEADER)) {
1415         # Write content to file.
1416         print $HEADER @headerContent;
1417         close($HEADER);
1418         undef($HEADER);
1419
1420         @headerContent = ();
1421     }
1422 }
1423
1424 sub constructorFor
1425 {
1426     my $className = shift;
1427     my $protoClassName = shift;
1428     my $interfaceName = shift;
1429     my $canConstruct = shift;
1430
1431 my $implContent = << "EOF";
1432 class ${className}Constructor : public DOMObject {
1433 public:
1434     ${className}Constructor(ExecState* exec)
1435     {
1436         setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype());
1437         putDirect(prototypePropertyName, ${protoClassName}::self(exec), None);
1438     }
1439     virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&);
1440     JSValue* getValueProperty(ExecState*, int token) const;
1441     virtual const ClassInfo* classInfo() const { return &info; }
1442     static const ClassInfo info;
1443 EOF
1444
1445     if ($canConstruct) {
1446 $implContent .= << "EOF";
1447     virtual bool implementsConstruct() const { return true; }
1448     virtual JSObject* construct(ExecState* exec, const List& args) { return static_cast<JSObject*>(toJS(exec, new $interfaceName)); }
1449 EOF
1450     }
1451
1452 $implContent .= << "EOF";
1453 };
1454
1455 const ClassInfo ${className}Constructor::info = { "${interfaceName}Constructor", 0, &${className}ConstructorTable, 0 };
1456
1457 bool ${className}Constructor::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
1458 {
1459     return getStaticValueSlot<${className}Constructor, DOMObject>(exec, &${className}ConstructorTable, this, propertyName, slot);
1460 }
1461
1462 JSValue* ${className}Constructor::getValueProperty(ExecState*, int token) const
1463 {
1464     // The token is the numeric value of its associated constant
1465     return jsNumber(token);
1466 }
1467
1468 EOF
1469
1470     return $implContent;
1471 }
1472
1473 sub protoFuncFor
1474 {
1475     my $className = shift;
1476
1477 my $implContent = << "EOF";
1478 class ${className}ProtoFunc : public InternalFunctionImp {
1479 public:
1480     ${className}ProtoFunc(ExecState* exec, int i, int len, const Identifier& name)
1481         : InternalFunctionImp(static_cast<FunctionPrototype*>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name)
1482         , id(i)
1483     {
1484         put(exec, lengthPropertyName, jsNumber(len), DontDelete|ReadOnly|DontEnum);
1485     }
1486     virtual JSValue* callAsFunction(ExecState* exec, JSObject* thisObj, const List& args);
1487 private:
1488     int id;
1489 };
1490
1491 EOF
1492
1493     return $implContent;
1494 }
1495
1496 1;