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