Reviewed by Hyatt.
[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 sub FileIsNewer
79 {
80   my $fileName = shift;
81   my $mtime = shift;
82   
83   my $statInfo = stat($fileName);
84   
85   if (!defined($statInfo)) {
86     # If the file doesn't exist it can't be newer
87     return 0;
88   }
89   
90   return ($statInfo->mtime > $mtime);
91 }
92
93 sub ShouldGenerateFiles
94 {
95   my $object = shift;
96   my $dataNode = shift;  
97   my $name = shift;
98
99   my $idlmtime = stat($dataNode->fileName)->mtime;
100   
101   if (FileIsNewer("$outputDir/JS$name.h", $idlmtime)) {
102     return 0;
103   }
104
105   if (FileIsNewer("$outputDir/JS$name.cpp", $idlmtime)) {
106     return 0;
107   }
108   
109 #  my $headerFileName = ;
110 #  my $implFileName = "$outputDir/JS$name.cpp";
111
112   return 1;
113 }
114
115 # Params: 'domClass' struct
116 sub GenerateInterface
117 {
118   my $object = shift;
119   my $dataNode = shift;
120
121   # FIXME: Check dates to see if we need to re-generate anything
122   
123   # Start actual generation..
124 #  print "  |  |>  Generating header...\n";
125   $object->GenerateHeader($dataNode);
126   
127 #  print "  |  |>  Generating implementation...\n";
128   $object->GenerateImplementation($dataNode);
129
130   my $name = $dataNode->name;
131   
132   # Open files for writing...
133   my $headerFileName = "$outputDir/JS$name.h";
134   my $implFileName = "$outputDir/JS$name.cpp";
135
136   open($IMPL, ">$implFileName") || die "Couldn't open file $implFileName";
137   open($HEADER, ">$headerFileName") || die "Couldn't open file $headerFileName";
138
139 #  print " |-\n |\n";
140 }
141
142 # Params: 'idlDocument' struct
143 sub GenerateModule
144 {
145   my $object = shift;
146   my $dataNode = shift;  
147   
148   $module = $dataNode->module;
149
150 }
151
152 sub GetParentClassName
153 {
154   my $dataNode = shift;
155   return $dataNode->extendedAttributes->{"LegacyParent"} if $dataNode->extendedAttributes->{"LegacyParent"};
156   return "KJS::DOMObject" if @{$dataNode->parents} eq 0;
157   return "JS" . $dataNode->parents(0);
158 }
159
160 sub GetLegacyHeaderIncludes
161 {
162   my $legacyParent = shift;
163   if ($legacyParent eq "JSCanvasRenderingContext2DBase") {
164     return "#include \"JSCanvasRenderingContext2DBase.h\"\n\n";
165   } elsif ($module eq "events") {
166     return "#include \"kjs_events.h\"\n\n";
167   } elsif ($module eq "core") {
168     return "#include \"kjs_dom.h\"\n\n";
169   } elsif ($module eq "html") {
170     return "#include \"kjs_html.h\"\n\n";
171   } else {
172     die ("Don't know what headers to include for module $module");
173   }
174 }
175
176 sub AddIncludesForType
177 {
178   my $type = shift;
179   
180   # When we're finished with the one-file-per-class 
181   # reorganization, we don't need these special cases.
182   
183   if ($type eq "DocumentType" or 
184       $type eq "Document" or
185       $type eq "DOMImplementation" or
186       $type eq "NodeList" or 
187       $type eq "Text" or 
188       $type eq "CharacterData") {
189     $implIncludes{"${type}Impl.h"} = 1;
190   } elsif ($type eq "Attr" or
191            $type eq "Element") {
192     $implIncludes{"dom_elementimpl.h"} = 1;
193   } elsif ($type eq "CSSStyleSheet") {
194     $implIncludes{"css_stylesheetimpl.h"} = 1;
195   } elsif ($type eq "HTMLDocument") {
196     $implIncludes{"html_documentimpl.h"} = 1;
197   } elsif ($type eq "MutationEvent" or
198            $type eq "WheelEvent") {
199     $implIncludes{"dom2_eventsimpl.h"} = 1;
200   } elsif ($type eq "ProcessingInstruction" or
201            $type eq "Entity" or
202            $type eq "Notation") {
203     $implIncludes{"dom_xmlimpl.h"} = 1;
204   } elsif ($type eq "CanvasRenderingContext2D") {
205     $implIncludes{"CanvasGradient.h"} = 1;
206     $implIncludes{"CanvasPattern.h"} = 1;
207     $implIncludes{"CanvasRenderingContext2D.h"} = 1;
208     $implIncludes{"CanvasStyle.h"} = 1;
209   } elsif ($type eq "CanvasGradient") {
210     $implIncludes{"CanvasGradient.h"} = 1;
211     $implIncludes{"PlatformString.h"} = 1;
212   } elsif ($type eq "CanvasPattern") {
213     $implIncludes{"$type.h"} = 1;
214   } elsif ($codeGenerator->IsPrimitiveType($type) or
215            $type eq "DOMString") {
216     # Do nothing
217   } else {
218     die "Don't know what to include for interface $type";
219   }
220 }
221
222 sub GetLegacyImplementationIncludes
223 {
224   my $interfaceName = shift;
225   
226   if ($module eq "events") {
227     return "#include \"dom2_eventsimpl.h\"\n";
228   } elsif ($module eq "core") {
229     return "#include \"${interfaceName}Impl.h\"\n";
230   } else {
231     die ("Don't know what headers to include for module $module");
232   }  
233 }
234
235 sub GenerateHeader
236 {
237   my $object = shift;
238   my $dataNode = shift;
239
240   my $interfaceName = $dataNode->name;
241   my $className = "JS$interfaceName";
242   my $implClassName = $interfaceName . "Impl";
243   # Canvas classes don't have Impl suffix (remove this when removing the Impl suffix elsewhere).
244   $implClassName = $interfaceName if $interfaceName =~ /^Canvas/;
245   
246   # FIXME: If we're sure that an interface can't have more than
247   # one parent we can do the check in the parser instead
248   if (@{$dataNode->parents} > 1) {
249     die "A class can't have more than one parent";
250   }
251   
252   my $hasLegacyParent = $dataNode->extendedAttributes->{"LegacyParent"};
253   my $hasRealParent = @{$dataNode->parents} > 0;
254   my $hasParent = $hasLegacyParent || $hasRealParent;
255   my $parentClassName = GetParentClassName($dataNode);
256   
257   # - Add default header template
258   @headerContent = split("\r", $headerTemplate);
259
260   # - Add header protection
261   push(@headerContent, "\n#ifndef $className" . "_H");
262   push(@headerContent, "\n#define $className" . "_H\n\n");
263   
264   if (exists $dataNode->extendedAttributes->{"LegacyParent"}) {
265     push(@headerContent, GetLegacyHeaderIncludes($dataNode->extendedAttributes->{"LegacyParent"}));
266   } else {
267     if ($hasParent) {
268       push(@headerContent, "#include \"$parentClassName.h\"\n");
269     } else {
270       push(@headerContent, "#include \"kjs_binding.h\"\n");
271     }
272   }
273   
274   my $numAttributes = @{$dataNode->attributes};
275   my $numFunctions = @{$dataNode->functions};
276   my $numConstants = @{$dataNode->constants};
277   
278   push(@headerContent, "\nnamespace WebCore {\n\n");
279   
280   # Implementation class forward declaration
281   push(@headerContent, "class $implClassName;\n\n");
282
283   # Class declaration
284   push(@headerContent, "class $className : public $parentClassName {\n");
285   push(@headerContent, "public:\n");
286   
287   # Constructor
288   push(@headerContent, "    $className(KJS::ExecState*, $implClassName*);\n");
289     
290   # Destructor
291   if (!$hasParent) {
292     push(@headerContent, "    virtual ~$className();\n");
293   }
294   
295   # Getters
296   if ($numAttributes > 0) {
297     push(@headerContent, "    virtual bool getOwnPropertySlot(KJS::ExecState*, const KJS::Identifier&, KJS::PropertySlot&);\n");
298     push(@headerContent, "    KJS::JSValue* getValueProperty(KJS::ExecState*, int token) const;\n");
299   }
300   
301   # Check if we have any writable properties
302   my $hasReadWriteProperties = 0;
303   foreach(@{$dataNode->attributes}) {
304     if($_->type !~ /^readonly\ attribute$/) {
305       $hasReadWriteProperties = 1;
306     }
307   }
308   
309   if ($hasReadWriteProperties) {
310     push(@headerContent, "    virtual void put(KJS::ExecState*, const KJS::Identifier&, KJS::JSValue*, int attr = KJS::None);\n");
311     push(@headerContent, "    void putValueProperty(KJS::ExecState*, int, KJS::JSValue*, int attr);\n");
312   }
313   
314   # Class info
315   push(@headerContent, "    virtual const KJS::ClassInfo* classInfo() const { return &info; }\n");
316   push(@headerContent, "    static const KJS::ClassInfo info;\n");
317   
318   # Constructor object getter
319   if ($numConstants ne 0) {
320     push(@headerContent, "    static KJS::JSValue* getConstructor(KJS::ExecState*);\n")
321   }
322
323   # Attribute and function enums
324   if ($numAttributes + $numFunctions > 0) {
325     push(@headerContent, "    enum {\n")
326   }
327   
328   if ($numAttributes > 0) {
329     push(@headerContent, "        // Attributes\n        ");
330     
331     my $i = -1;
332     foreach(@{$dataNode->attributes}) {
333       my $attribute = $_;
334
335       $i++;
336       if((($i % 4) eq 0) and ($i ne 0)) {
337         push(@headerContent, "\n        ");
338       }
339
340       my $value = ucfirst($attribute->signature->name);
341       $value .= ", " if(($i < $numAttributes - 1));
342       $value .= ", " if(($i eq $numAttributes - 1) and ($numFunctions ne 0));
343       push(@headerContent, $value);
344     }
345   }
346   
347   if ($numFunctions > 0) {
348     if ($numAttributes > 0) {
349       push(@headerContent, "\n\n");
350     }
351     push(@headerContent,"        // Functions\n        ");
352
353     $i = -1;
354     foreach(@{$dataNode->functions}) {
355       my $function = $_;
356
357       $i++;
358       if ((($i % 4) eq 0) and ($i ne 0)) {
359         push(@headerContent, "\n        ");
360       }
361
362       my $value = ucfirst($function->signature->name);
363       $value .= ", " if ($i < $numFunctions - 1);
364       push(@headerContent, $value);
365     }
366   }
367   
368   if ($numAttributes + $numFunctions > 0) {
369     push(@headerContent, "\n    };\n");
370   }
371
372   if (!$hasParent) {
373     push(@headerContent, "    $implClassName* impl() const { return m_impl.get(); }\n");
374     push(@headerContent, "private:\n");
375     push(@headerContent, "    RefPtr<$implClassName> m_impl;\n");
376   }
377   
378   push(@headerContent, "};\n\n");
379   
380   if (!$hasParent) {
381     push(@headerContent, "KJS::JSValue* toJS(KJS::ExecState*, $implClassName*);\n\n");
382   }
383
384   # Add prototype declaration
385   if ($numFunctions > 0) {
386     if ($hasParent) {
387       push(@headerContent, "KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE(${className}Proto, ${parentClassName}Proto);\n\n");
388     } else {
389       push(@headerContent, "KJS_DEFINE_PROTOTYPE(${className}Proto);\n\n");
390     }
391   }
392   
393   push(@headerContent, "}\n\n#endif\n");
394 }
395
396 sub GenerateImplementation
397 {
398   my $object = shift;
399   my $dataNode = shift;
400   
401   my $interfaceName = $dataNode->name;
402   my $className = "JS$interfaceName";
403   my $implClassName = $interfaceName . "Impl";
404   # Canvas classes don't have Impl suffix (remove this when removing the Impl suffix elsewhere).
405   $implClassName = $interfaceName if $interfaceName =~ /^Canvas/;
406   
407   my $hasLegacyParent = $dataNode->extendedAttributes->{"LegacyParent"};
408   my $hasRealParent = @{$dataNode->parents} > 0;
409   my $hasParent = $hasLegacyParent || $hasRealParent;
410   my $parentClassName = GetParentClassName($dataNode);
411   
412   # - Add default header template
413   @implContentHeader = split("\r", $headerTemplate);
414   push(@implContentHeader, "\n");
415   push(@implContentHeader, "#include \"$className.h\"\n\n");
416
417
418   AddIncludesForType($interfaceName);
419
420   @implContent = ();
421
422   push(@implContent, "\nusing namespace KJS;\n\n");  
423   push(@implContent, "namespace WebCore {\n\n");
424   
425   # - Add all attributes in a hashtable definition
426   my $numAttributes = @{$dataNode->attributes};
427   if ($numAttributes > 0) {
428     my $hashSize = $numAttributes;
429     my $hashName = $className . "Table";
430
431     my @hashKeys = ();      # ie. 'insertBefore'
432     my @hashValues = ();    # ie. 'JSNode::InsertBefore'
433     my @hashSpecials = ();    # ie. 'DontDelete|Function'
434     my @hashParameters = ();  # ie. '2'
435
436     foreach my $attribute (@{$dataNode->attributes}) {
437       my $name = $attribute->signature->name;
438       push(@hashKeys, $name);
439       
440       my $value = $className . "::" . ucfirst($name);
441       push(@hashValues, $value);
442
443       my $special = "DontDelete";
444       $special .= "|ReadOnly" if($attribute->type =~ /readonly/);
445       push(@hashSpecials, $special);
446
447       my $numParameters = "0";
448       push(@hashParameters, $numParameters);      
449     }
450
451     $object->GenerateHashTable($hashName, $hashSize,
452                             \@hashKeys, \@hashValues,
453                              \@hashSpecials, \@hashParameters);
454   }
455   
456   # - Add all constants
457   my $numConstants = @{$dataNode->constants};
458   if ($numConstants ne 0) {
459     $hashSize = $numConstants;
460     $hashName = $className . "ConstructorTable";
461
462     @hashKeys = ();
463     @hashValues = ();
464     @hashSpecials = ();
465     @hashParameters = ();
466     
467     foreach my $constant (@{$dataNode->constants}) {
468       my $name = $constant->name;
469       push(@hashKeys, $name);
470      
471       my $value = "${implClassName}::$name";
472       push(@hashValues, $value);
473
474       my $special = "DontDelete|ReadOnly";
475       push(@hashSpecials, $special);
476
477       my $numParameters = 0;
478       push(@hashParameters, $numParameters); 
479     }
480     
481     $object->GenerateHashTable($hashName, $hashSize,
482                                \@hashKeys, \@hashValues,
483                                \@hashSpecials, \@hashParameters);
484                                
485     # Add Constructor class
486     push(@implContent, "class ${className}Constructor : public DOMObject {\n");
487     push(@implContent, "public:\n");
488     push(@implContent, "    ${className}Constructor(ExecState* exec) { " . 
489                        "setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype()); }\n");
490     push(@implContent, "    virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&);\n");
491     push(@implContent, "    JSValue* getValueProperty(ExecState*, int token) const;\n");
492     push(@implContent, "    virtual const ClassInfo* classInfo() const { return &info; }\n");
493     push(@implContent, "    static const ClassInfo info;\n");    
494     push(@implContent, "};\n\n");
495     
496     push(@implContent, "const ClassInfo ${className}Constructor::info = { \"${interfaceName}Constructor\", 0, " .
497                        "&${className}ConstructorTable, 0 };\n\n");
498                        
499     push(@implContent, "bool ${className}Constructor::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)\n{\n");
500     push(@implContent, "    return getStaticValueSlot<${className}Constructor, DOMObject>" .
501                        "(exec, &${className}ConstructorTable, this, propertyName, slot);\n}\n\n");
502
503     push(@implContent, "JSValue* ${className}Constructor::getValueProperty(ExecState*, int token) const\n{\n");
504     push(@implContent, "    // We use the token as the value to return directly\n");
505     push(@implContent, "    return jsNumber(token);\n}\n\n");
506   }
507   
508   # - Add all functions in a hashtable definition, if we have any.
509   my $numFunctions = @{$dataNode->functions};
510   if ($numFunctions ne 0) {
511     $hashSize = $numFunctions;
512     $hashName = $className . "ProtoTable";
513
514     @hashKeys = ();
515     @hashValues = ();
516     @hashSpecials = ();
517     @hashParameters = ();
518
519     foreach my $function (@{$dataNode->functions}) {
520       my $name = $function->signature->name;
521       push(@hashKeys, $name);
522     
523       my $value = $className . "::" . ucfirst($name);
524       push(@hashValues, $value);
525     
526       my $special = "DontDelete|Function";
527       push(@hashSpecials, $special);
528     
529       my $numParameters = @{$function->parameters};
530       push(@hashParameters, $numParameters);
531     }
532     
533     $object->GenerateHashTable($hashName, $hashSize,
534                                \@hashKeys, \@hashValues,
535                                \@hashSpecials, \@hashParameters);
536
537     push(@implContent, "KJS_IMPLEMENT_PROTOFUNC(${className}ProtoFunc)\n");
538     push(@implContent, "KJS_IMPLEMENT_PROTOTYPE(\"$className\", ${className}Proto, ${className}ProtoFunc)\n\n");
539   }
540   
541   # - Initialize static ClassInfo object
542   push(@implContent, "const ClassInfo $className" . "::info = { \"$interfaceName\", ");
543   if ($hasParent) {
544     push(@implContent, "&" .$parentClassName . "::info, ");
545   } else {
546     push(@implContent, "0, ");
547   }
548   
549   if ($numAttributes > 0) {
550     push(@implContent, "&${className}Table, ");
551   } else {
552     push(@implContent, "0, ")
553   }
554   push(@implContent, "0 };\n\n");
555     
556   # Constructor
557   push(@implContent, "${className}::$className(ExecState* exec, $implClassName* impl)\n");
558   if ($hasParent) {
559     push(@implContent, "    : $parentClassName(exec, impl)\n");
560   } else {
561     push(@implContent, "    : m_impl(impl)\n");
562   }
563   
564   if ($numFunctions ne 0) {
565     push(@implContent, "{\n    setPrototype(${className}Proto::self(exec));\n}\n\n");
566   } else {
567     push(@implContent, "{\n}\n\n");    
568   }
569   
570   # Destructor
571   if (!$hasParent) {
572     push(@implContent, "${className}::~$className()\n");
573     push(@implContent, "{\n    ScriptInterpreter::forgetDOMObject(m_impl.get());\n}\n\n");    
574   }
575   
576   # Attributes
577   if ($numAttributes ne 0) {
578     push(@implContent, "bool ${className}::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)\n");
579     push(@implContent, "{\n    return getStaticValueSlot<$className, $parentClassName>" .
580                        "(exec, &${className}Table, this, propertyName, slot);\n}\n\n");
581   
582     push(@implContent, "JSValue* ${className}::getValueProperty(ExecState *exec, int token) const\n{\n");
583     push(@implContent, "    $implClassName* impl = static_cast<$implClassName*>(${className}::impl());\n\n");
584     push(@implContent, "    switch (token) {\n");
585
586     foreach my $attribute (@{$dataNode->attributes}) {
587       my $name = $attribute->signature->name;
588   
589       push(@implContent, "    case " . ucfirst($name) . ":\n");
590       push(@implContent, "        return " . NativeToJSValue($attribute->signature, "impl->$name()") . ";\n");
591     }
592
593     push(@implContent, "    }\n    return jsUndefined();\n}\n\n");
594     
595     # Check if we have any writable attributes and if they raise exceptions
596     my $hasReadWriteProperties = 0;
597     my $raisesExceptions = 0;
598     foreach my $attribute (@{$dataNode->attributes}) {
599       if($attribute->type !~ /^readonly\ attribute$/) {
600         $hasReadWriteProperties = 1;
601
602         if (@{$attribute->raisesExceptions}) {
603           $raisesExceptions = 1;
604         }
605       }
606     }
607     
608     if ($hasReadWriteProperties) {
609       push(@implContent, "void ${className}::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)\n");
610       push(@implContent, "{\n    lookupPut<$className, $parentClassName>" .
611                          "(exec, propertyName, value, attr, &${className}Table, this);\n}\n\n");
612                          
613       push(@implContent, "void ${className}::putValueProperty(ExecState* exec, int token, JSValue* value, int /*attr*/)\n");
614       push(@implContent, "{\n");
615       if ($raisesExceptions) {
616         push(@implContent, "    DOMExceptionTranslator exception(exec);\n");
617       }
618       push(@implContent, "    $implClassName* impl = static_cast<$implClassName*>(${className}::impl());\n\n");
619       push(@implContent, "    switch (token) {\n");
620       
621       foreach my $attribute (@{$dataNode->attributes}) {
622         if($attribute->type !~ /^readonly\ attribute$/) {
623           my $name = $attribute->signature->name;
624           push(@implContent, "    case " . ucfirst($name) .":\n");
625           push(@implContent, "        impl->set" . ucfirst($name) . "(" . JSValueToNative($attribute->signature, "value"));
626           if (@{$attribute->raisesExceptions}) {
627             push(@implContent, ", exception")
628           }
629           push(@implContent, ");\n        break;\n");
630         }
631       }
632       push(@implContent, "    }\n}\n\n");      
633     }
634   }
635
636     
637   if ($numConstants ne 0) {
638     push(@implContent, "JSValue* ${className}::getConstructor(ExecState* exec)\n{\n");
639     push(@implContent, "    return cacheGlobalObject<${className}Constructor>(exec, \"[[${className}.constructor]]\");\n");
640     push(@implContent, "}\n");
641   }    
642   
643   # Functions
644   if($numFunctions ne 0) {
645     my $raisesExceptions = 0;
646     # Check if any of the functions throw exceptions
647     foreach my $function (@{$dataNode->functions}) {
648       if (@{$function->raisesExceptions}) {
649         $raisesExceptions = 1;
650       }
651     }
652     push(@implContent, "JSValue* ${className}ProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)\n{\n");
653     push(@implContent, "    if (!thisObj->inherits(&${className}::info))\n");
654     push(@implContent, "      return throwError(exec, TypeError);\n\n");
655
656     if ($raisesExceptions) {
657       push(@implContent, "    DOMExceptionTranslator exception(exec);\n");
658     }
659     
660     push(@implContent, "    $implClassName* impl = static_cast<$implClassName*>(static_cast<$className*>(thisObj)->impl());\n\n");
661     
662     
663     push(@implContent, "    switch (id) {\n");
664     foreach my $function (@{$dataNode->functions}) {      
665       push(@implContent, "    case ${className}::" . ucfirst($function->signature->name) . ": {\n");
666       
667       AddIncludesForType($function->signature->type);
668       
669       my $paramIndex = 0;
670       my $functionString = "impl->" . $function->signature->name . "(";
671       my $numParameters = @{$function->parameters};
672       
673       foreach my $parameter (@{$function->parameters}) {
674         my $name = $parameter->name;
675         push(@implContent, "        " . GetNativeType($parameter) . " $name = " . JSValueToNative($parameter, "args[$paramIndex]") . ";\n");        
676         
677         # If a parameter is "an index", it should throw an INDEX_SIZE_ERR
678         # exception        
679         if ($parameter->extendedAttributes->{"IsIndex"}) {
680           $implIncludes{"ExceptionCode.h"} = 1;
681           push(@implContent, "        if ($name < 0) {\n");
682           push(@implContent, "            setDOMException(exec, INDEX_SIZE_ERR);\n");
683           push(@implContent, "            break;\n        }\n");          
684         }
685         $paramIndex++;
686         
687         if ($paramIndex < $numParameters) {
688           $functionString .= "$name, ";
689         } else {
690           $functionString .= "$name";
691         }         
692       }
693   
694       if (@{$function->raisesExceptions}) {
695         $functionString .= ", exception)";
696       } else {
697         $functionString .= ")";
698       }
699       
700       if ($function->signature->type eq "void") {
701         push(@implContent, "\n        $functionString;\n        return jsUndefined();\n");
702       } else {
703         push(@implContent, "\n        return " . NativeToJSValue($function->signature, $functionString) . ";\n");
704       }
705       
706       push(@implContent, "    }\n");
707     }
708     push(@implContent, "    }\n");
709     push(@implContent, "    return jsUndefined();\n");
710     push(@implContent, "}\n")
711   }
712   
713   if (!$hasParent) {
714     push(@implContent, "KJS::JSValue* toJS(KJS::ExecState* exec, $implClassName* obj)\n");
715     push(@implContent, "{\n");
716     push(@implContent, "    return KJS::cacheDOMObject<$implClassName, $className>(exec, obj);\n");
717     push(@implContent, "}\n");
718   }
719
720   push(@implContent, "\n}\n");
721 }
722
723 sub GetNativeType
724 {
725   my $signature = shift;
726   
727   my $type = $signature->type;
728   
729   if ($type eq "boolean") {
730     return "bool";
731   } elsif ($type eq "unsigned long") {
732     if ($signature->extendedAttributes->{"IsIndex"}) {
733       # Special-case index arguments because we need to check that
734       # they aren't < 0.        
735       return "int";
736     } else {
737       return "unsigned";
738     } 
739   } elsif ($type eq "long") {
740     return "int";
741   } elsif ($type eq "unsigned short") {
742     return "unsigned short";
743   } elsif ($type eq "float") {
744     return "float";
745   } elsif ($type eq "AtomicString") {
746     return "AtomicString";
747   } elsif ($type eq "DOMString") {
748     return "String";
749   } elsif ($type eq "views::AbstractView") {
750     return "AbstractViewImpl*";
751   } elsif ($type eq "Node" or $type eq "Attr" or
752            $type eq "DocumentType") {
753     return "${type}Impl*";
754   } else {
755     die "Don't know how the native type of $type";
756   }
757 }
758
759 sub JSValueToNative
760 {
761   my $signature = shift;
762   my $value = shift;
763   
764   my $type = $signature->type;
765
766   if ($type eq "boolean") {
767     return "$value->toBoolean(exec)";
768   } elsif ($type eq "unsigned long" or $type eq "long") {
769     return "$value->toInt32(exec)";
770   } elsif ($type eq "unsigned short") {
771     return "$value->toInt32(exec)";
772   } elsif ($type eq "float") {
773     return "$value->toNumber(exec)";
774   } elsif ($type eq "AtomicString") {
775     return "AtomicString($value->toString(exec).domString())";
776   } elsif ($type eq "DOMString") {
777     if ($signature->extendedAttributes->{"ConvertNullToNullString"}) {
778       return "valueToStringWithNullCheck(exec, $value)";
779     } else {
780       return "$value->toString(exec).domString()";
781     }
782   } elsif ($type eq "views::AbstractView") {
783     $implIncludes{"kjs_views.h"} = 1;
784     return "toAbstractView($value)";
785   } elsif ($type eq "Node") {
786     $implIncludes{"kjs_dom.h"} = 1;
787     return "toNode($value)";
788   } elsif ($type eq "Attr") {
789     $implIncludes{"kjs_dom.h"} = 1;
790     return "toAttr($value)";
791   } elsif ($type eq "DocumentType") {
792       $implIncludes{"kjs_dom.h"} = 1;
793       return "toDocumentType($value)";
794   } else {
795     die "Don't know how to convert a JS value of type $type."
796   }
797 }
798
799 sub NativeToJSValue
800 {
801   my $signature = shift;
802   my $value = shift;
803   
804   my $type = $signature->type;
805   
806   if ($type eq "boolean") {
807     return "jsBoolean($value)";
808   } elsif ($type eq "long" or $type eq "unsigned long" or 
809            $type eq "unsigned short" or $type eq "float") {
810     return "jsNumber($value)";
811   } elsif ($type eq "DOMString") {
812     my $conv = $signature->extendedAttributes->{"ConvertNullStringTo"};
813     if (defined $conv) {
814       if ($conv eq "Null") {
815         return "jsStringOrNull($value)";
816       } else {
817         die "Unknown value for ConvertNullStringTo extended attribute";
818       }
819     } else {
820       return "jsString($value)";
821     }
822   } elsif ($type eq "Node" or $type eq "Text" or
823            $type eq "DocumentType" or $type eq "Document" or
824            $type eq "HTMLDocument" or $type eq "Element" or
825            $type eq "Attr") {
826     # Add necessary includes
827     $implIncludes{"kjs_dom.h"} = 1;
828     $implIncludes{"NodeImpl.h"} = 1;
829     return "toJS(exec, $value)";
830   } elsif ($type eq "NodeList" or $type eq "NamedNodeMap") {
831     # Add necessary includes
832     $implIncludes{"kjs_dom.h"} = 1;
833     return "toJS(exec, $value)";
834   } elsif ($type eq "CSSStyleSheet" or $type eq "StyleSheet") {
835     # Add necessary includes
836     $implIncludes{"css_ruleimpl.h"} = 1;
837     $implIncludes{"kjs_css.h"} = 1;
838     return "toJS(exec, $value)";    
839   } elsif ($type eq "CSSStyleDeclaration") {
840     # Add necessary includes
841     $implIncludes{"css_valueimpl.h"} = 1;
842     $implIncludes{"kjs_css.h"} = 1;
843     return "toJS(exec, $value)";
844   } elsif ($type eq "HTMLCanvasElement") {
845     $implIncludes{"kjs_dom.h"} = 1;
846     $implIncludes{"html_canvasimpl.h"} = 1;
847     return "toJS(exec, $value)";
848   } elsif ($type eq "CanvasGradient") {
849     $implIncludes{"kjs_html.h"} = 1;
850     return "toJS(exec, $value)";
851   } else {
852     die "Don't know how to convert a value of type $type to a JS Value";
853   }
854   
855   die ("$value " . $signature->type);
856 }
857
858 # Internal Helper
859 sub GenerateHashTable
860 {
861   my $object = shift;
862
863   my $name = shift;
864   my $size = shift;
865   my $keys = shift;
866   my $values = shift;
867   my $specials = shift;
868   my $parameters = shift;
869
870   # Helpers
871   my @table = ();
872   my @links = ();
873
874   my $maxDepth = 0;
875   my $collisions = 0;
876   my $savedSize = $size;
877
878   # Collect hashtable information...
879   my $i = 0;
880   foreach(@{$keys}) {
881     my $depth = 0;
882     my $h = $object->GenerateHashValue($_) % $savedSize;
883
884     while(defined($table[$h])) {
885       if(defined($links[$h])) {
886         $h = $links[$h];
887         $depth++;
888       } else {
889         $collisions++;
890         $links[$h] = $size;
891         $h = $size;
892         $size++;
893       }
894     }
895   
896     $table[$h] = $i;
897
898     $i++;
899     $maxDepth = $depth if($depth > $maxDepth);
900   }
901
902   # Ensure table is big enough (in case of undef entries at the end)
903   if($#table + 1 < $size) {
904     $#table = $size - 1;
905   }
906
907   # Start outputing the hashtables...
908   my $nameEntries = "${name}Entries";
909   $nameEntries =~ s/:/_/g;
910
911   # first, build the string table
912   my %soffset = ();
913   if(($name =~ /Proto/) or ($name =~ /Constructor/)) {
914     my $type = $name;
915     my $implClass;
916
917     if($name =~ /Proto/) {
918       $type =~ s/Proto.*//;
919       $implClass = $type; $implClass =~ s/Wrapper$//;
920       push(@implContent, "/* Hash table for prototype */\n");
921     } else {
922       $type =~ s/Constructor.*//;
923       $implClass = $type; $implClass =~ s/Constructor$//;
924       push(@implContent, "/* Hash table for constructor */\n");
925     }
926   } else {
927     push(@implContent, "/* Hash table */\n");
928   }
929
930   # Dump the hash table...
931   push(@implContent, "\nstatic const HashEntry $nameEntries\[\] =\n\{\n");
932
933   $i = 0;
934   foreach $entry (@table) {
935     if(defined($entry)) {
936       my $key = @$keys[$entry];
937
938       push(@implContent, "    \{ \"" . $key . "\"");
939       push(@implContent, ", " . @$values[$entry]);
940       push(@implContent, ", " . @$specials[$entry]);
941       push(@implContent, ", " . @$parameters[$entry]);
942       push(@implContent, ", ");
943
944       if(defined($links[$i])) {
945         push(@implContent, "&${nameEntries}[$links[$i]]" . " \}");
946       } else {
947         push(@implContent, "0 \}");
948       }
949     } else {
950       push(@implContent, "    \{ 0, 0, 0, 0, 0 \}");
951     }
952
953     push(@implContent, ",") unless($i eq $size - 1);
954     push(@implContent, "\n");
955
956     $i++;
957   }
958
959   push(@implContent, "};\n\n");
960   push(@implContent, "static const HashTable $name = \n");
961   push(@implContent, "{\n    2, $size, $nameEntries, $savedSize\n};\n\n");
962 }
963
964 # Internal helper
965 sub GenerateHashValue
966 {
967   my $object = shift;
968
969   @chars = split(/ */, $_[0]);
970
971   # This hash is designed to work on 16-bit chunks at a time. But since the normal case
972   # (above) is to hash UTF-16 characters, we just treat the 8-bit chars as if they
973   # were 16-bit chunks, which should give matching results
974
975   my $EXP2_32 = 4294967296;
976
977   my $hash = 0x9e3779b9;
978   my $l    = scalar @chars; #I wish this was in Ruby --- Maks
979   my $rem  = $l & 1;
980   $l = $l >> 1;
981
982   my $s = 0;
983
984   # Main loop
985   for (; $l > 0; $l--) {
986     $hash   += ord($chars[$s]);
987     my $tmp = (ord($chars[$s+1]) << 11) ^ $hash;
988     $hash   = (($hash << 16)% $EXP2_32) ^ $tmp;
989     $s += 2;
990     $hash += $hash >> 11;
991   }
992
993   # Handle end case
994   if ($rem !=0) {
995     $hash += ord($chars[$s]);
996     $hash ^= (($hash << 11)% $EXP2_32);
997     $hash += $hash >> 17;
998   }
999
1000   # Force "avalanching" of final 127 bits
1001   $hash ^= ($hash << 3);
1002   $hash += ($hash >> 5);
1003   $hash = ($hash% $EXP2_32);
1004   $hash ^= (($hash << 2)% $EXP2_32);
1005   $hash += ($hash >> 15);
1006   $hash = $hash% $EXP2_32;
1007   $hash ^= (($hash << 10)% $EXP2_32);
1008   
1009   # this avoids ever returning a hash code of 0, since that is used to
1010   # signal "hash not computed yet", using a value that is likely to be
1011   # effectively the same as 0 when the low bits are masked
1012   $hash = 0x80000000  if ($hash == 0);
1013
1014   return $hash;
1015 }
1016
1017 # Internal helper
1018 sub WriteData
1019 {
1020   if (defined($IMPL)) {
1021     # Write content to file.
1022     print $IMPL @implContentHeader;
1023   
1024     foreach my $implInclude (sort keys(%implIncludes)) {
1025       print $IMPL "#include \"$implInclude\"\n";
1026     }
1027
1028     print $IMPL @implContent;
1029     close($IMPL);
1030     undef($IMPL);
1031
1032     @implHeaderContent = "";
1033     @implContent = "";    
1034     %implIncludes = ();
1035   }
1036
1037   if (defined($HEADER)) {
1038     # Write content to file.
1039     print $HEADER @headerContent;
1040     close($HEADER);
1041     undef($HEADER);
1042
1043     @headerContent = "";
1044   }
1045 }
1046
1047 1;