9b553723f38f96d5b5bbe5c21aab3ade0b80def7
[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   } else {
134     die ("Don't know what headers to include for module $module");
135   }
136 }
137
138 sub AddIncludesForType
139 {
140   my $type = shift;
141   
142   # When we're finished with the one-file-per-class 
143   # reorganization, we don't need these special cases.
144   
145   if ($type eq "DocumentType" or 
146       $type eq "Document" or
147       $type eq "DOMWindow" or
148       $type eq "DOMImplementation" or
149       $type eq "NodeList" or 
150       $type eq "Text" or 
151       $type eq "CharacterData" or
152       $type eq "CDATASection" or
153       $type eq "CanvasPattern" or
154       $type eq "Range" or
155       $type eq "DocumentFragment" or
156       $type eq "Node" or
157       $type eq "Attr" or
158       $type eq "Comment" or
159       $type eq "Element") {
160     $implIncludes{"${type}.h"} = 1;
161   } elsif ($type eq "CSSStyleSheet" or $type eq "StyleSheet") {
162     $implIncludes{"css_stylesheetimpl.h"} = 1;
163   } elsif ($type eq "CSSRuleList") {
164     $implIncludes{"css_ruleimpl.h"} = 1;
165   } elsif ($type eq "CSSPrimitiveValue" or $type eq "Counter" or $type eq "Rect" or $type eq "RGBColor") {
166     $implIncludes{"css_valueimpl.h"} = 1;
167   } elsif ($type eq "CSSStyleDeclaration") {
168     $implIncludes{"css_valueimpl.h"} = 1;
169   } elsif ($type eq "HTMLDocument") {
170     $implIncludes{"HTMLDocument.h"} = 1;
171   } elsif ($type eq "MutationEvent" or
172            $type eq "KeyboardEvent" or
173            $type eq "MouseEvent" or
174            $type eq "Event" or
175            $type eq "UIEvent" or
176            $type eq "WheelEvent") {
177     $implIncludes{"dom2_eventsimpl.h"} = 1;
178   } elsif ($type eq "NodeIterator" or
179            $type eq "TreeWalker") {
180     $implIncludes{"dom2_traversalimpl.h"} = 1;
181   } elsif ($type eq "ProcessingInstruction" or
182            $type eq "Entity" or
183            $type eq "EntityReference" or
184            $type eq "Notation") {
185     $implIncludes{"dom_xmlimpl.h"} = 1;
186   } elsif ($type eq "CanvasRenderingContext2D") {
187     $implIncludes{"CanvasGradient.h"} = 1;
188     $implIncludes{"CanvasPattern.h"} = 1;
189     $implIncludes{"CanvasRenderingContext2D.h"} = 1;
190     $implIncludes{"CanvasStyle.h"} = 1;
191   } elsif ($type eq "CanvasGradient") {
192     $implIncludes{"CanvasGradient.h"} = 1;
193     $implIncludes{"PlatformString.h"} = 1;
194   } elsif ($codeGenerator->IsPrimitiveType($type) or
195            $type eq "DOMString") {
196     # Do nothing
197   } else {
198     die "Don't know what to include for interface $type";
199   }
200 }
201
202 sub GenerateHeader
203 {
204   my $object = shift;
205   my $dataNode = shift;
206
207   my $interfaceName = $dataNode->name;
208   my $className = "JS$interfaceName";
209   my $implClassName = $interfaceName;
210   
211   # FIXME: If we're sure that an interface can't have more than
212   # one parent we can do the check in the parser instead
213   if (@{$dataNode->parents} > 1) {
214     die "A class can't have more than one parent";
215   }
216   
217   my $hasLegacyParent = $dataNode->extendedAttributes->{"LegacyParent"};
218   my $hasRealParent = @{$dataNode->parents} > 0;
219   my $hasParent = $hasLegacyParent || $hasRealParent;
220   my $parentClassName = GetParentClassName($dataNode);
221   
222   # - Add default header template
223   @headerContent = split("\r", $headerTemplate);
224
225   # - Add header protection
226   push(@headerContent, "\n#ifndef $className" . "_H");
227   push(@headerContent, "\n#define $className" . "_H\n\n");
228   
229   if (exists $dataNode->extendedAttributes->{"LegacyParent"}) {
230     push(@headerContent, GetLegacyHeaderIncludes($dataNode->extendedAttributes->{"LegacyParent"}));
231   } else {
232     if ($hasParent) {
233       push(@headerContent, "#include \"$parentClassName.h\"\n");
234     } else {
235       push(@headerContent, "#include \"kjs_binding.h\"\n");
236     }
237   }
238   
239   my $numAttributes = @{$dataNode->attributes};
240   my $numFunctions = @{$dataNode->functions};
241   my $numConstants = @{$dataNode->constants};
242   
243   push(@headerContent, "\nnamespace WebCore {\n\n");
244   
245   # Implementation class forward declaration
246   push(@headerContent, "class $implClassName;\n\n");
247
248   # Class declaration
249   push(@headerContent, "class $className : public $parentClassName {\n");
250   push(@headerContent, "public:\n");
251   
252   # Constructor
253   if ($dataNode->extendedAttributes->{"DoNotCache"}) {
254       push(@headerContent, "    $className($implClassName*);\n");
255   } else {
256       push(@headerContent, "    $className(KJS::ExecState*, $implClassName*);\n");
257   }
258     
259   # Destructor
260   if (!$hasParent or $interfaceName eq "Document") {
261     push(@headerContent, "    virtual ~$className();\n");
262   }
263   
264   # Getters
265   if ($numAttributes > 0) {
266     push(@headerContent, "    virtual bool getOwnPropertySlot(KJS::ExecState*, const KJS::Identifier&, KJS::PropertySlot&);\n");
267     push(@headerContent, "    KJS::JSValue* getValueProperty(KJS::ExecState*, int token) const;\n");
268   }
269   
270   # Check if we have any writable properties
271   my $hasReadWriteProperties = 0;
272   foreach(@{$dataNode->attributes}) {
273     if($_->type !~ /^readonly\ attribute$/) {
274       $hasReadWriteProperties = 1;
275     }
276   }
277   
278   if ($hasReadWriteProperties) {
279     push(@headerContent, "    virtual void put(KJS::ExecState*, const KJS::Identifier&, KJS::JSValue*, int attr = KJS::None);\n");
280     push(@headerContent, "    void putValueProperty(KJS::ExecState*, int, KJS::JSValue*, int attr);\n");
281   }
282   
283   # Class info
284   push(@headerContent, "    virtual const KJS::ClassInfo* classInfo() const { return &info; }\n");
285   push(@headerContent, "    static const KJS::ClassInfo info;\n");
286   
287   # Constructor object getter
288   if ($numConstants ne 0) {
289     push(@headerContent, "    static KJS::JSValue* getConstructor(KJS::ExecState*);\n")
290   }
291
292   # Attribute and function enums
293   if ($numAttributes + $numFunctions > 0) {
294     push(@headerContent, "    enum {\n")
295   }
296   
297   if ($numAttributes > 0) {
298     push(@headerContent, "        // Attributes\n        ");
299     
300     my $i = -1;
301     foreach(@{$dataNode->attributes}) {
302       my $attribute = $_;
303
304       $i++;
305       if((($i % 4) eq 0) and ($i ne 0)) {
306         push(@headerContent, "\n        ");
307       }
308
309       my $value = ucfirst($attribute->signature->name) . "AttrNum";
310       $value .= ", " if(($i < $numAttributes - 1));
311       $value .= ", " if(($i eq $numAttributes - 1) and ($numFunctions ne 0));
312       push(@headerContent, $value);
313     }
314   }
315   
316   if ($numFunctions > 0) {
317     if ($numAttributes > 0) {
318       push(@headerContent, "\n\n");
319     }
320     push(@headerContent,"        // Functions\n        ");
321
322     $i = -1;
323     foreach(@{$dataNode->functions}) {
324       my $function = $_;
325
326       $i++;
327       if ((($i % 4) eq 0) and ($i ne 0)) {
328         push(@headerContent, "\n        ");
329       }
330
331       my $value = ucfirst($function->signature->name) . "FuncNum";
332       $value .= ", " if ($i < $numFunctions - 1);
333       push(@headerContent, $value);
334     }
335   }
336   
337   if ($numAttributes + $numFunctions > 0) {
338     push(@headerContent, "\n    };\n");
339   }
340
341   if (!$hasParent) {
342     push(@headerContent, "    $implClassName* impl() const { return m_impl.get(); }\n");
343     push(@headerContent, "private:\n");
344     push(@headerContent, "    RefPtr<$implClassName> m_impl;\n");
345   }
346   
347   push(@headerContent, "};\n\n");
348   
349   if (!$hasParent) {
350     push(@headerContent, "KJS::JSValue* toJS(KJS::ExecState*, $implClassName*);\n");
351     push(@headerContent, "$implClassName* to${interfaceName}(KJS::JSValue*);\n\n");
352   }
353
354   # Add prototype declaration -- code adopted from the KJS_DEFINE_PROTOTYPE and KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE macros
355   if ($numFunctions > 0) {
356       push(@headerContent, "class ${className}Proto : public KJS::JSObject {\n");
357       if (!$dataNode->extendedAttributes->{"DoNotCache"}) {
358           push(@headerContent, "    friend KJS::JSObject* KJS_GCC_ROOT_NS_HACK cacheGlobalObject<${className}Proto>(KJS::ExecState*, const KJS::Identifier& propertyName);\n");
359       }
360       push(@headerContent, "public:\n");
361       if ($dataNode->extendedAttributes->{"DoNotCache"}) {
362           push(@headerContent, "    static KJS::JSObject* self();\n");
363       } else {
364           push(@headerContent, "    static KJS::JSObject* self(KJS::ExecState* exec);\n");
365       }
366       push(@headerContent, "    virtual const KJS::ClassInfo* classInfo() const { return &info; }\n");
367       push(@headerContent, "    static const KJS::ClassInfo info;\n");
368       push(@headerContent, "    bool getOwnPropertySlot(KJS::ExecState*, const KJS::Identifier&, KJS::PropertySlot&);\n");
369       push(@headerContent, "protected:\n");
370       if ($dataNode->extendedAttributes->{"DoNotCache"}) {
371           push(@headerContent, "    ${className}Proto() { }\n");
372       } else {
373           push(@headerContent, "    ${className}Proto(KJS::ExecState* exec)\n");
374           if ($hasParent) {
375               push(@headerContent, "        : KJS::JSObject(${parentClassName}Proto::self(exec)) { }\n");
376           } else {
377               push(@headerContent, "        : KJS::JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()) { }\n");
378           }
379       }
380       push(@headerContent, "};\n\n");
381   }
382   
383   push(@headerContent, "}\n\n#endif\n");
384 }
385
386 sub GenerateImplementation
387 {
388   my $object = shift;
389   my $dataNode = shift;
390   
391   my $interfaceName = $dataNode->name;
392   my $className = "JS$interfaceName";
393   my $implClassName = $interfaceName;
394   
395   my $hasLegacyParent = $dataNode->extendedAttributes->{"LegacyParent"};
396   my $hasRealParent = @{$dataNode->parents} > 0;
397   my $hasParent = $hasLegacyParent || $hasRealParent;
398   my $parentClassName = GetParentClassName($dataNode);
399   
400   # - Add default header template
401   @implContentHeader = split("\r", $headerTemplate);
402   push(@implContentHeader, "\n");
403   push(@implContentHeader,, "#include \"config.h\"\n");
404   push(@implContentHeader, "#include \"$className.h\"\n\n");
405
406
407   AddIncludesForType($interfaceName);
408
409   @implContent = ();
410
411   push(@implContent, "\nusing namespace KJS;\n\n");  
412   push(@implContent, "namespace WebCore {\n\n");
413   
414   # - Add all attributes in a hashtable definition
415   my $numAttributes = @{$dataNode->attributes};
416   if ($numAttributes > 0) {
417     my $hashSize = $numAttributes;
418     my $hashName = $className . "Table";
419
420     my @hashKeys = ();      # ie. 'insertBefore'
421     my @hashValues = ();    # ie. 'JSNode::InsertBefore'
422     my @hashSpecials = ();    # ie. 'DontDelete|Function'
423     my @hashParameters = ();  # ie. '2'
424
425     foreach my $attribute (@{$dataNode->attributes}) {
426       my $name = $attribute->signature->name;
427       push(@hashKeys, $name);
428       
429       my $value = $className . "::" . ucfirst($name) . "AttrNum";
430       push(@hashValues, $value);
431
432       my $special = "DontDelete";
433       $special .= "|ReadOnly" if($attribute->type =~ /readonly/);
434       push(@hashSpecials, $special);
435
436       my $numParameters = "0";
437       push(@hashParameters, $numParameters);      
438     }
439
440     $object->GenerateHashTable($hashName, $hashSize,
441                             \@hashKeys, \@hashValues,
442                              \@hashSpecials, \@hashParameters);
443   }
444   
445   # - Add all constants
446   my $numConstants = @{$dataNode->constants};
447   if ($numConstants ne 0) {
448     $hashSize = $numConstants;
449     $hashName = $className . "ConstructorTable";
450
451     @hashKeys = ();
452     @hashValues = ();
453     @hashSpecials = ();
454     @hashParameters = ();
455     
456     foreach my $constant (@{$dataNode->constants}) {
457       my $name = $constant->name;
458       push(@hashKeys, $name);
459      
460       my $value = "${implClassName}::$name";
461       push(@hashValues, $value);
462
463       my $special = "DontDelete|ReadOnly";
464       push(@hashSpecials, $special);
465
466       my $numParameters = 0;
467       push(@hashParameters, $numParameters); 
468     }
469     
470     $object->GenerateHashTable($hashName, $hashSize,
471                                \@hashKeys, \@hashValues,
472                                \@hashSpecials, \@hashParameters);
473                                
474     # Add Constructor class
475     push(@implContent, "class ${className}Constructor : public DOMObject {\n");
476     push(@implContent, "public:\n");
477     push(@implContent, "    ${className}Constructor(ExecState* exec) { " . 
478                        "setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype()); }\n");
479     push(@implContent, "    virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&);\n");
480     push(@implContent, "    JSValue* getValueProperty(ExecState*, int token) const;\n");
481     push(@implContent, "    virtual const ClassInfo* classInfo() const { return &info; }\n");
482     push(@implContent, "    static const ClassInfo info;\n");    
483     push(@implContent, "};\n\n");
484     
485     push(@implContent, "const ClassInfo ${className}Constructor::info = { \"${interfaceName}Constructor\", 0, " .
486                        "&${className}ConstructorTable, 0 };\n\n");
487                        
488     push(@implContent, "bool ${className}Constructor::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)\n{\n");
489     push(@implContent, "    return getStaticValueSlot<${className}Constructor, DOMObject>" .
490                        "(exec, &${className}ConstructorTable, this, propertyName, slot);\n}\n\n");
491
492     push(@implContent, "JSValue* ${className}Constructor::getValueProperty(ExecState*, int token) const\n{\n");
493     push(@implContent, "    // We use the token as the value to return directly\n");
494     push(@implContent, "    return jsNumber(token);\n}\n\n");
495   }
496   
497   # - Add all functions in a hashtable definition, if we have any.
498   my $numFunctions = @{$dataNode->functions};
499   if ($numFunctions ne 0) {
500     $hashSize = $numFunctions;
501     $hashName = $className . "ProtoTable";
502
503     @hashKeys = ();
504     @hashValues = ();
505     @hashSpecials = ();
506     @hashParameters = ();
507
508     foreach my $function (@{$dataNode->functions}) {
509       my $name = $function->signature->name;
510       push(@hashKeys, $name);
511     
512       my $value = $className . "::" . ucfirst($name) . "FuncNum";
513       push(@hashValues, $value);
514     
515       my $special = "DontDelete|Function";
516       push(@hashSpecials, $special);
517     
518       my $numParameters = @{$function->parameters};
519       push(@hashParameters, $numParameters);
520     }
521     
522     $object->GenerateHashTable($hashName, $hashSize,
523                                \@hashKeys, \@hashValues,
524                                \@hashSpecials, \@hashParameters);
525
526     push(@implContent, "class ${className}ProtoFunc : public InternalFunctionImp {\n");
527     push(@implContent, "public:\n");
528     push(@implContent, "    ${className}ProtoFunc(ExecState* exec, int i, int len, const Identifier& name)\n");
529     push(@implContent, "    : InternalFunctionImp(static_cast<FunctionPrototype*>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name)\n");
530     push(@implContent, "    , id(i)\n");
531     push(@implContent, "    {\n");
532     push(@implContent, "        put(exec, lengthPropertyName, jsNumber(len), DontDelete|ReadOnly|DontEnum);\n");
533     push(@implContent, "    }\n");
534     push(@implContent, "    virtual JSValue* callAsFunction(ExecState* exec, JSObject* thisObj, const List& args);\n");
535     push(@implContent, "private:\n");
536     push(@implContent, "    int id;\n");
537     push(@implContent, "};\n\n");
538
539     push(@implContent, "const ClassInfo ${className}Proto::info = { \"$className\", 0, &${className}ProtoTable, 0 };\n\n");
540     if ($dataNode->extendedAttributes->{"DoNotCache"}) {
541         push(@implContent, "JSObject* ${className}Proto::self()\n");
542         push(@implContent, "{\n");
543         push(@implContent, "    return new ${className}Proto();\n");
544         push(@implContent, "}\n\n");
545     } else {
546         push(@implContent, "JSObject* ${className}Proto::self(ExecState* exec)\n");
547         push(@implContent, "{\n");
548         push(@implContent, "    return ::cacheGlobalObject<${className}Proto>(exec, \"[[${className}.prototype]]\");\n");
549         push(@implContent, "}\n\n");
550     }
551     push(@implContent, "bool ${className}Proto::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)\n");
552     push(@implContent, "{\n");
553     push(@implContent, "    return getStaticFunctionSlot<${className}ProtoFunc, JSObject>(exec, &${className}ProtoTable, this, propertyName, slot);\n");
554     push(@implContent, "}\n\n");
555   }
556   
557   # - Initialize static ClassInfo object
558   push(@implContent, "const ClassInfo $className" . "::info = { \"$interfaceName\", ");
559   if ($hasParent) {
560     push(@implContent, "&" .$parentClassName . "::info, ");
561   } else {
562     push(@implContent, "0, ");
563   }
564   
565   if ($numAttributes > 0) {
566     push(@implContent, "&${className}Table, ");
567   } else {
568     push(@implContent, "0, ")
569   }
570   push(@implContent, "0 };\n\n");
571     
572   # Constructor
573   if ($dataNode->extendedAttributes->{"DoNotCache"}) {
574       push(@implContent, "${className}::$className($implClassName* impl)\n");
575       push(@implContent, "    : $parentClassName(impl)\n");
576   } else {
577       push(@implContent, "${className}::$className(ExecState* exec, $implClassName* impl)\n");
578       if ($hasParent) {
579           push(@implContent, "    : $parentClassName(exec, impl)\n");
580       } else {
581           push(@implContent, "    : m_impl(impl)\n");
582       }
583   }
584   
585   if ($dataNode->extendedAttributes->{"DoNotCache"}) {
586       push(@implContent, "{\n    setPrototype(${className}Proto::self());\n}\n\n");
587   } elsif ($numFunctions ne 0) {
588       push(@implContent, "{\n    setPrototype(${className}Proto::self(exec));\n}\n\n");
589   } else {
590       push(@implContent, "{\n}\n\n");    
591   }
592   
593   # Destructor
594   if (!$hasParent) {
595     push(@implContent, "${className}::~$className()\n");
596     push(@implContent, "{\n    ScriptInterpreter::forgetDOMObject(m_impl.get());\n}\n\n");    
597   }
598
599   # Document needs a special destructor because it's a special case for caching. It needs
600   # its own special handling rather than relying on the caching that Node normally does.
601   if ($interfaceName eq "Document") {
602     push(@implContent, "${className}::~$className()\n");
603     push(@implContent, "{\n    ScriptInterpreter::forgetDOMObject(static_cast<${implClassName}*>(m_impl.get()));\n}\n\n");    
604   }
605   
606   # Attributes
607   if ($numAttributes ne 0) {
608     push(@implContent, "bool ${className}::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)\n");
609     push(@implContent, "{\n    return getStaticValueSlot<$className, $parentClassName>" .
610                        "(exec, &${className}Table, this, propertyName, slot);\n}\n\n");
611   
612     push(@implContent, "JSValue* ${className}::getValueProperty(ExecState *exec, int token) const\n{\n");
613     push(@implContent, "    $implClassName* impl = static_cast<$implClassName*>(${className}::impl());\n\n");
614     push(@implContent, "    switch (token) {\n");
615
616     foreach my $attribute (@{$dataNode->attributes}) {
617       my $name = $attribute->signature->name;
618   
619       if (!@{$attribute->getterExceptions}) {
620         push(@implContent, "    case " . ucfirst($name) . "AttrNum:\n");
621         push(@implContent, "        return " . NativeToJSValue($attribute->signature, "impl->$name()") . ";\n");
622       } else {
623         push(@implContent, "    case " . ucfirst($name) . "AttrNum: {\n");
624         push(@implContent, "        ExceptionCode ec = 0;\n");
625         push(@implContent, "        KJS::JSValue* result = " . NativeToJSValue($attribute->signature, "impl->$name(ec)") . ";\n");
626         push(@implContent, "        setDOMException(exec, ec);\n");
627         push(@implContent, "        return result;\n");
628         push(@implContent, "    }\n");
629       }
630     }
631
632     push(@implContent, "    }\n    return 0;\n}\n\n");
633     
634     # Check if we have any writable attributes
635     my $hasReadWriteProperties = 0;
636     foreach my $attribute (@{$dataNode->attributes}) {
637       $hasReadWriteProperties = 1 if $attribute->type !~ /^readonly/;
638     }
639     if ($hasReadWriteProperties) {
640       push(@implContent, "void ${className}::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)\n");
641       push(@implContent, "{\n    lookupPut<$className, $parentClassName>" .
642                          "(exec, propertyName, value, attr, &${className}Table, this);\n}\n\n");
643                          
644       push(@implContent, "void ${className}::putValueProperty(ExecState* exec, int token, JSValue* value, int /*attr*/)\n");
645       push(@implContent, "{\n");
646       push(@implContent, "    $implClassName* impl = static_cast<$implClassName*>(${className}::impl());\n\n");
647       push(@implContent, "    switch (token) {\n");
648       
649       foreach my $attribute (@{$dataNode->attributes}) {
650         if ($attribute->type !~ /^readonly/) {
651           my $name = $attribute->signature->name;
652           push(@implContent, "    case " . ucfirst($name) ."AttrNum: {\n");
653           push(@implContent, "        ExceptionCode ec = 0;\n") if @{$attribute->setterExceptions};
654           push(@implContent, "        impl->set" . ucfirst($name) . "(" . JSValueToNative($attribute->signature, "value"));
655           push(@implContent, ", ec") if @{$attribute->setterExceptions};
656           push(@implContent, ");\n");
657           push(@implContent, "        setDOMException(exec, ec);\n") if @{$attribute->setterExceptions};
658           push(@implContent, "    }\n");
659           push(@implContent, "        break;\n");
660         }
661       }
662       push(@implContent, "    }\n}\n\n");      
663     }
664   }
665
666   if ($numConstants ne 0) {
667     push(@implContent, "JSValue* ${className}::getConstructor(ExecState* exec)\n{\n");
668     push(@implContent, "    return cacheGlobalObject<${className}Constructor>(exec, \"[[${interfaceName}.constructor]]\");\n");
669     push(@implContent, "}\n");
670   }    
671   
672   # Functions
673   if($numFunctions ne 0) {
674     push(@implContent, "JSValue* ${className}ProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)\n{\n");
675     push(@implContent, "    if (!thisObj->inherits(&${className}::info))\n");
676     push(@implContent, "      return throwError(exec, TypeError);\n\n");
677
678     push(@implContent, "    $implClassName* impl = static_cast<$implClassName*>(static_cast<$className*>(thisObj)->impl());\n\n");
679     
680     
681     push(@implContent, "    switch (id) {\n");
682     foreach my $function (@{$dataNode->functions}) {      
683       push(@implContent, "    case ${className}::" . ucfirst($function->signature->name) . "FuncNum: {\n");
684       
685       AddIncludesForType($function->signature->type);
686       
687       my $paramIndex = 0;
688       my $functionString = "impl->" . $function->signature->name . "(";
689       my $numParameters = @{$function->parameters};
690       
691       foreach my $parameter (@{$function->parameters}) {
692         my $name = $parameter->name;
693         push(@implContent, "        bool ${name}Ok;\n") if TypeCanFailConversion($parameter);
694         push(@implContent, "        " . GetNativeType($parameter) . " $name = " . JSValueToNative($parameter, "args[$paramIndex]", TypeCanFailConversion($parameter) ? "${name}Ok" : undef) . ";\n");        
695         if (TypeCanFailConversion($parameter)) {
696             push(@implContent, "        if (!${name}Ok) {\n");
697             push(@implContent, "            setDOMException(exec, TYPE_MISMATCH_ERR);\n");
698             push(@implContent, "            return jsUndefined();\n        }\n");
699         }          
700         
701         # If a parameter is "an index", it should throw an INDEX_SIZE_ERR
702         # exception        
703         if ($parameter->extendedAttributes->{"IsIndex"}) {
704           $implIncludes{"ExceptionCode.h"} = 1;
705           push(@implContent, "        if ($name < 0) {\n");
706           push(@implContent, "            setDOMException(exec, INDEX_SIZE_ERR);\n");
707           push(@implContent, "            return jsUndefined();\n        }\n");          
708         }
709         
710         $functionString .= ", " if $paramIndex;
711         $functionString .= $name;
712
713         $paramIndex++;
714       }
715   
716       if (@{$function->raisesExceptions}) {
717         $functionString .= ", " if $paramIndex;
718         $functionString .= "ec";
719         push(@implContent, "        ExceptionCode ec = 0;\n");
720       }
721       $functionString .= ")";
722       
723       if ($function->signature->type eq "void") {
724         push(@implContent, "\n        $functionString;\n");
725         push(@implContent, "        setDOMException(exec, ec);\n") if @{$function->raisesExceptions};
726         push(@implContent, "        return jsUndefined();\n");
727       } else {
728         push(@implContent, "\n        KJS::JSValue* result = " . NativeToJSValue($function->signature, $functionString) . ";\n");
729         push(@implContent, "        setDOMException(exec, ec);\n") if @{$function->raisesExceptions};
730         push(@implContent, "        return result;\n");
731       }
732
733       push(@implContent, "    }\n");
734     }
735     push(@implContent, "    }\n");
736     push(@implContent, "    return 0;\n");
737     push(@implContent, "}\n")
738   }
739   
740   if (!$hasParent) {
741     push(@implContent, "KJS::JSValue* toJS(KJS::ExecState* exec, $implClassName* obj)\n");
742     push(@implContent, "{\n");
743     push(@implContent, "    return KJS::cacheDOMObject<$implClassName, $className>(exec, obj);\n");
744     push(@implContent, "}\n");
745     push(@implContent, "$implClassName* to${interfaceName}(KJS::JSValue* val)\n");
746     push(@implContent, "{\n");
747     push(@implContent, "    return val->isObject(&${className}::info) ? static_cast<$className*>(val)->impl() : 0;\n");
748     push(@implContent, "}\n");
749   }
750
751   push(@implContent, "\n}\n");
752 }
753
754 sub GetNativeType
755 {
756   my $signature = shift;
757   
758   my $type = $signature->type;
759   
760   if ($type eq "boolean") {
761     return "bool";
762   } elsif ($type eq "unsigned long") {
763     if ($signature->extendedAttributes->{"IsIndex"}) {
764       # Special-case index arguments because we need to check that
765       # they aren't < 0.        
766       return "int";
767     } else {
768       return "unsigned";
769     } 
770   } elsif ($type eq "long") {
771     return "int";
772   } elsif ($type eq "unsigned short") {
773     return "unsigned short";
774   } elsif ($type eq "float") {
775     return "float";
776   } elsif ($type eq "AtomicString") {
777     return "AtomicString";
778   } elsif ($type eq "DOMString") {
779     return "String";
780   } elsif ($type eq "Node" or
781            $type eq "Element" or
782            $type eq "Attr" or
783            $type eq "DocumentType" or
784            $type eq "Range") {
785     return "${type}*";
786   } elsif ($type eq "NodeFilter") {
787       return "PassRefPtr<${type}>";
788   } elsif ($type eq "CompareHow") {
789     return "Range::CompareHow";
790   } elsif ($type eq "EventTarget") {
791     return "EventTargetNode*";
792   } elsif ($type eq "DOMWindow") {
793     return "DOMWindow*";
794   } else {
795     die "Don't know the native type of $type";
796   }
797 }
798
799 sub TypeCanFailConversion
800 {
801   my $signature = shift;
802   
803   my $type = $signature->type;
804
805   if ($type eq "boolean") {
806     return 0;
807   } elsif ($type eq "unsigned long" or $type eq "long") {
808     return 0; # or can it?
809   } elsif ($type eq "unsigned short") {
810     return 0; # or can it?
811   } elsif ($type eq "CompareHow") {
812     return 0; # or can it?
813   } elsif ($type eq "float") {
814     return 0;
815   } elsif ($type eq "AtomicString") {
816       return 0;
817   } elsif ($type eq "DOMString") {
818       return 0;
819   } elsif ($type eq "Node") {
820       return 0;
821   } elsif ($type eq "Element") {
822       return 0;
823   } elsif ($type eq "Attr") {
824       $implIncludes{"ExceptionCode.h"} = 1;
825       return 1;
826   } elsif ($type eq "DocumentType") {
827       return 0;
828   } elsif ($type eq "EventTarget") {
829       return 0;
830   }  elsif ($type eq "Range") {
831       return 0;
832   }  elsif ($type eq "NodeFilter") {
833       return 0;
834   } elsif ($type eq "DOMWindow") {
835       return 0;
836   } else {
837     die "Don't know whether a JS value can fail conversion to type $type."
838   }
839 }
840
841 sub JSValueToNative
842 {
843   my $signature = shift;
844   my $value = shift;
845   my $okParam = shift;
846   my $maybeOkParam = $okParam ? ", ${okParam}" : "";
847   
848   my $type = $signature->type;
849
850   if ($type eq "boolean") {
851     my $conv = $signature->extendedAttributes->{"ConvertUndefinedToTrue"};
852     if (defined $conv) {
853         return "valueToBooleanTreatUndefinedAsTrue(exec, $value)";
854     } else {
855         return "$value->toBoolean(exec)";
856     }
857   } elsif ($type eq "unsigned long" or $type eq "long") {
858     return "$value->toInt32(exec)";
859   } elsif ($type eq "unsigned short") {
860     return "$value->toInt32(exec)";
861   } elsif ($type eq "CompareHow") {
862     return "static_cast<Range::CompareHow>($value->toInt32(exec))";
863   } elsif ($type eq "float") {
864     return "$value->toNumber(exec)";
865   } elsif ($type eq "AtomicString") {
866     return "$value->toString(exec)";
867   } elsif ($type eq "DOMString") {
868     if ($signature->extendedAttributes->{"ConvertNullToNullString"}) {
869       return "valueToStringWithNullCheck(exec, $value)";
870     } else {
871       return "$value->toString(exec)";
872     }
873   } elsif ($type eq "Node") {
874     $implIncludes{"kjs_dom.h"} = 1;
875     return "toNode($value)";
876   } elsif ($type eq "EventTarget") {
877     $implIncludes{"kjs_dom.h"} = 1;
878     return "toEventTargetNode($value)";
879   }  elsif ($type eq "Attr") {
880     $implIncludes{"kjs_dom.h"} = 1;
881     return "toAttr($value${maybeOkParam})";
882   } elsif ($type eq "DocumentType") {
883       $implIncludes{"kjs_dom.h"} = 1;
884       return "toDocumentType($value)";
885   } elsif ($type eq "Range") {
886       $implIncludes{"JSRange.h"} = 1;
887       return "toRange($value)";
888   } elsif ($type eq "Element") {
889     $implIncludes{"kjs_dom.h"} = 1;
890     return "toElement($value)";
891   } elsif ($type eq "NodeFilter") {
892     $implIncludes{"kjs_traversal.h"} = 1;
893     return "toNodeFilter($value)";
894   } elsif ($type eq "DOMWindow") {
895     $implIncludes{"kjs_window.h"} = 1;
896     return "toDOMWindow($value)";
897   } else {
898     die "Don't know how to convert a JS value of type $type."
899   }
900 }
901
902 sub NativeToJSValue
903 {
904   my $signature = shift;
905   my $value = shift;
906   
907   my $type = $signature->type;
908   
909   if ($type eq "boolean") {
910     return "jsBoolean($value)";
911   } elsif ($type eq "long" or
912            $type eq "unsigned long" or 
913            $type eq "short" or 
914            $type eq "unsigned short" or
915            $type eq "float") {
916     return "jsNumber($value)";
917   } elsif ($type eq "DOMString") {
918     my $conv = $signature->extendedAttributes->{"ConvertNullStringTo"};
919     if (defined $conv) {
920       if ($conv eq "Null") {
921         return "jsStringOrNull($value)";
922       } elsif ($conv eq "Undefined") {
923         return "jsStringOrUndefined($value)";
924       } elsif ($conv eq "False") {
925         return "jsStringOrFalse($value)";
926       } else {
927         die "Unknown value for ConvertNullStringTo extended attribute";
928       }
929     } else {
930       return "jsString($value)";
931     }
932   } elsif ($type eq "DOMImplementation") {
933     $implIncludes{"kjs_dom.h"} = 1;
934     $implIncludes{"JSDOMImplementation.h"} = 1;
935     return "toJS(exec, $value)";
936   } elsif ($type eq "Node" or
937            $type eq "Text" or
938            $type eq "Comment" or
939            $type eq "CDATASection" or
940            $type eq "DocumentType" or
941            $type eq "Document" or
942            $type eq "EntityReference" or
943            $type eq "ProcessingInstruction" or
944            $type eq "HTMLDocument" or
945            $type eq "Element" or
946            $type eq "Attr" or
947            $type eq "DocumentFragment") {
948     $implIncludes{"kjs_dom.h"} = 1;
949     $implIncludes{"Comment.h"} = 1;
950     $implIncludes{"CDATASection.h"} = 1;
951     $implIncludes{"Node.h"} = 1;
952     $implIncludes{"Element.h"} = 1;
953     $implIncludes{"DocumentType.h"} = 1;
954     return "toJS(exec, $value)";
955   } elsif ($type eq "EventTarget") {
956     $implIncludes{"kjs_dom.h"} = 1;
957     $implIncludes{"EventTargetNode.h"} = 1;
958     return "toJS(exec, $value)";
959   } elsif ($type eq "Event") {
960     $implIncludes{"kjs_events.h"} = 1;
961     $implIncludes{"dom2_eventsimpl.h"} = 1;
962     return "toJS(exec, $value)";
963   } elsif ($type eq "NodeList" or $type eq "NamedNodeMap") {
964     $implIncludes{"kjs_dom.h"} = 1;
965     return "toJS(exec, $value)";
966   } elsif ($type eq "CSSStyleSheet" or $type eq "StyleSheet" or $type eq "MediaList") {
967     $implIncludes{"css_stylesheetimpl.h"} = 1;
968     $implIncludes{"css_ruleimpl.h"} = 1;
969     $implIncludes{"kjs_css.h"} = 1;
970     return "toJS(exec, $value)";    
971   } elsif ($type eq "StyleSheetList") {
972     $implIncludes{"css_stylesheetimpl.h"} = 1;
973     $implIncludes{"kjs_css.h"} = 1;
974     return "toJS(exec, $value, impl)";    
975   } elsif ($type eq "CSSRuleList") {
976     $implIncludes{"css_ruleimpl.h"} = 1;
977     return "toJS(exec, $value)";  
978   } elsif ($type eq "CSSStyleDeclaration" or $type eq "Rect") {
979     $implIncludes{"css_valueimpl.h"} = 1;
980     $implIncludes{"kjs_css.h"} = 1;
981     return "toJS(exec, $value)";
982   } elsif ($type eq "RGBColor") {
983     $implIncludes{"kjs_css.h"} = 1;
984     return "getDOMRGBColor(exec, $value)";
985   } elsif ($type eq "HTMLCanvasElement") {
986     $implIncludes{"kjs_dom.h"} = 1;
987     $implIncludes{"HTMLCanvasElement.h"} = 1;
988     return "toJS(exec, $value)";
989   } elsif ($type eq "CanvasGradient" or $type eq "Counter" or $type eq "Range") {
990     $implIncludes{"JS$type.h"} = 1;
991     return "toJS(exec, $value)";
992   } elsif ($type eq "NodeIterator" or
993            $type eq "TreeWalker") {
994     $implIncludes{"kjs_traversal.h"} = 1;
995     return "toJS(exec, $value)";
996   } elsif ($type eq "DOMWindow") {
997     $implIncludes{"kjs_window.h"} = 1;
998     return "toJS(exec, $value)";
999   } else {
1000     die "Don't know how to convert a value of type $type to a JS Value";
1001   }
1002   
1003   die ("$value " . $signature->type);
1004 }
1005
1006 # Internal Helper
1007 sub GenerateHashTable
1008 {
1009   my $object = shift;
1010
1011   my $name = shift;
1012   my $size = shift;
1013   my $keys = shift;
1014   my $values = shift;
1015   my $specials = shift;
1016   my $parameters = shift;
1017
1018   # Helpers
1019   my @table = ();
1020   my @links = ();
1021
1022   my $maxDepth = 0;
1023   my $collisions = 0;
1024   my $savedSize = $size;
1025
1026   # Collect hashtable information...
1027   my $i = 0;
1028   foreach(@{$keys}) {
1029     my $depth = 0;
1030     my $h = $object->GenerateHashValue($_) % $savedSize;
1031
1032     while(defined($table[$h])) {
1033       if(defined($links[$h])) {
1034         $h = $links[$h];
1035         $depth++;
1036       } else {
1037         $collisions++;
1038         $links[$h] = $size;
1039         $h = $size;
1040         $size++;
1041       }
1042     }
1043   
1044     $table[$h] = $i;
1045
1046     $i++;
1047     $maxDepth = $depth if($depth > $maxDepth);
1048   }
1049
1050   # Ensure table is big enough (in case of undef entries at the end)
1051   if($#table + 1 < $size) {
1052     $#table = $size - 1;
1053   }
1054
1055   # Start outputing the hashtables...
1056   my $nameEntries = "${name}Entries";
1057   $nameEntries =~ s/:/_/g;
1058
1059   # first, build the string table
1060   my %soffset = ();
1061   if(($name =~ /Proto/) or ($name =~ /Constructor/)) {
1062     my $type = $name;
1063     my $implClass;
1064
1065     if($name =~ /Proto/) {
1066       $type =~ s/Proto.*//;
1067       $implClass = $type; $implClass =~ s/Wrapper$//;
1068       push(@implContent, "/* Hash table for prototype */\n");
1069     } else {
1070       $type =~ s/Constructor.*//;
1071       $implClass = $type; $implClass =~ s/Constructor$//;
1072       push(@implContent, "/* Hash table for constructor */\n");
1073     }
1074   } else {
1075     push(@implContent, "/* Hash table */\n");
1076   }
1077
1078   # Dump the hash table...
1079   push(@implContent, "\nstatic const HashEntry $nameEntries\[\] =\n\{\n");
1080
1081   $i = 0;
1082   foreach $entry (@table) {
1083     if(defined($entry)) {
1084       my $key = @$keys[$entry];
1085
1086       push(@implContent, "    \{ \"" . $key . "\"");
1087       push(@implContent, ", " . @$values[$entry]);
1088       push(@implContent, ", " . @$specials[$entry]);
1089       push(@implContent, ", " . @$parameters[$entry]);
1090       push(@implContent, ", ");
1091
1092       if(defined($links[$i])) {
1093         push(@implContent, "&${nameEntries}[$links[$i]]" . " \}");
1094       } else {
1095         push(@implContent, "0 \}");
1096       }
1097     } else {
1098       push(@implContent, "    \{ 0, 0, 0, 0, 0 \}");
1099     }
1100
1101     push(@implContent, ",") unless($i eq $size - 1);
1102     push(@implContent, "\n");
1103
1104     $i++;
1105   }
1106
1107   push(@implContent, "};\n\n");
1108   push(@implContent, "static const HashTable $name = \n");
1109   push(@implContent, "{\n    2, $size, $nameEntries, $savedSize\n};\n\n");
1110 }
1111
1112 # Internal helper
1113 sub GenerateHashValue
1114 {
1115   my $object = shift;
1116
1117   @chars = split(/ */, $_[0]);
1118
1119   # This hash is designed to work on 16-bit chunks at a time. But since the normal case
1120   # (above) is to hash UTF-16 characters, we just treat the 8-bit chars as if they
1121   # were 16-bit chunks, which should give matching results
1122
1123   my $EXP2_32 = 4294967296;
1124
1125   my $hash = 0x9e3779b9;
1126   my $l    = scalar @chars; #I wish this was in Ruby --- Maks
1127   my $rem  = $l & 1;
1128   $l = $l >> 1;
1129
1130   my $s = 0;
1131
1132   # Main loop
1133   for (; $l > 0; $l--) {
1134     $hash   += ord($chars[$s]);
1135     my $tmp = (ord($chars[$s+1]) << 11) ^ $hash;
1136     $hash   = (($hash << 16)% $EXP2_32) ^ $tmp;
1137     $s += 2;
1138     $hash += $hash >> 11;
1139   }
1140
1141   # Handle end case
1142   if ($rem !=0) {
1143     $hash += ord($chars[$s]);
1144     $hash ^= (($hash << 11)% $EXP2_32);
1145     $hash += $hash >> 17;
1146   }
1147
1148   # Force "avalanching" of final 127 bits
1149   $hash ^= ($hash << 3);
1150   $hash += ($hash >> 5);
1151   $hash = ($hash% $EXP2_32);
1152   $hash ^= (($hash << 2)% $EXP2_32);
1153   $hash += ($hash >> 15);
1154   $hash = $hash% $EXP2_32;
1155   $hash ^= (($hash << 10)% $EXP2_32);
1156   
1157   # this avoids ever returning a hash code of 0, since that is used to
1158   # signal "hash not computed yet", using a value that is likely to be
1159   # effectively the same as 0 when the low bits are masked
1160   $hash = 0x80000000  if ($hash == 0);
1161
1162   return $hash;
1163 }
1164
1165 # Internal helper
1166 sub WriteData
1167 {
1168   if (defined($IMPL)) {
1169     # Write content to file.
1170     print $IMPL @implContentHeader;
1171   
1172     foreach my $implInclude (sort keys(%implIncludes)) {
1173       print $IMPL "#include \"$implInclude\"\n";
1174     }
1175
1176     print $IMPL @implContent;
1177     close($IMPL);
1178     undef($IMPL);
1179
1180     @implHeaderContent = "";
1181     @implContent = "";    
1182     %implIncludes = ();
1183   }
1184
1185   if (defined($HEADER)) {
1186     # Write content to file.
1187     print $HEADER @headerContent;
1188     close($HEADER);
1189     undef($HEADER);
1190
1191     @headerContent = "";
1192   }
1193 }
1194
1195 1;