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