2010-10-14 Hayato Ito <hayato@chromium.org>
[WebKit-https.git] / WebCore / inspector / CodeGeneratorInspector.pm
1 # Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 package CodeGeneratorInspector;
6
7 use strict;
8
9 use Class::Struct;
10 use File::stat;
11
12 my %typeTransform;
13 $typeTransform{"InspectorClient"} = {
14     "forward" => "InspectorClient",
15     "header" => "InspectorClient.h",
16 };
17 $typeTransform{"Backend"} = {
18     "forward" => "InspectorBackend",
19     "header" => "InspectorBackend.h",
20     "domainAccessor" => "m_inspectorController->inspectorBackend()",
21 };
22 $typeTransform{"Controller"} = {
23     "forwardHeader" => "InspectorController.h",
24     "domainAccessor" => "m_inspectorController",
25 };
26 $typeTransform{"Debug"} = {
27     "forward" => "InspectorDebuggerAgent",
28     "header" => "InspectorDebuggerAgent.h",
29     "domainAccessor" => "m_inspectorController->debuggerAgent()",
30 };
31 $typeTransform{"DOM"} = {
32     "forward" => "InspectorDOMAgent",
33     "header" => "InspectorDOMAgent.h",
34     "domainAccessor" => "m_inspectorController->domAgent()",
35 };
36 $typeTransform{"ApplicationCache"} = {
37     "forward" => "InspectorApplicationCacheAgent",
38     "header" => "InspectorApplicationCacheAgent.h",
39     "domainAccessor" => "m_inspectorController->applicationCacheAgent()",
40 };
41 $typeTransform{"Profiler"} = {
42     "forward" => "InspectorProfilerAgent",
43     "header" => "InspectorProfilerAgent.h",
44     "domainAccessor" => "m_inspectorController->profilerAgent()",
45 };
46 $typeTransform{"Frontend"} = {
47     "forward" => "InspectorFrontend",
48     "header" => "InspectorFrontend.h",
49 };
50 $typeTransform{"PassRefPtr"} = {
51     "forwardHeader" => "wtf/PassRefPtr.h",
52 };
53 $typeTransform{"Object"} = {
54     "param" => "PassRefPtr<InspectorObject>",
55     "variable" => "RefPtr<InspectorObject>",
56     "defaultValue" => "InspectorObject::create()",
57     "forward" => "InspectorObject",
58     "header" => "InspectorValues.h",
59     "JSONType" => "Object"
60 };
61 $typeTransform{"Array"} = {
62     "param" => "PassRefPtr<InspectorArray>",
63     "variable" => "RefPtr<InspectorArray>",
64     "defaultValue" => "InspectorArray::create()",
65     "forward" => "InspectorArray",
66     "header" => "InspectorValues.h",
67     "JSONType" => "Array"
68 };
69 $typeTransform{"Value"} = {
70     "param" => "PassRefPtr<InspectorValue>",
71     "variable" => "RefPtr<InspectorValue>",
72     "defaultValue" => "InspectorValue::null()",
73     "forward" => "InspectorValue",
74     "header" => "InspectorValues.h",
75     "JSONType" => "Value"
76 };
77 $typeTransform{"String"} = {
78     "param" => "const String&",
79     "variable" => "String",
80     "defaultValue" => "\"\"",
81     "forwardHeader" => "wtf/Forward.h",
82     "header" => "PlatformString.h",
83     "JSONType" => "String"
84 };
85 $typeTransform{"long"} = {
86     "param" => "long",
87     "variable" => "long",
88     "defaultValue" => "0",
89     "forward" => "",
90     "header" => "",
91     "JSONType" => "Number"
92 };
93 $typeTransform{"int"} = {
94     "param" => "int",
95     "variable" => "int",
96     "defaultValue" => "0",
97     "forward" => "",
98     "header" => "",
99     "JSONType" => "Number",
100 };
101 $typeTransform{"unsigned long"} = {
102     "param" => "unsigned long",
103     "variable" => "unsigned long",
104     "defaultValue" => "0u",
105     "forward" => "",
106     "header" => "",
107     "JSONType" => "Number"
108 };
109 $typeTransform{"unsigned int"} = {
110     "param" => "unsigned int",
111     "variable" => "unsigned int",
112     "defaultValue" => "0u",
113     "forward" => "",
114     "header" => "",
115     "JSONType" => "Number"
116 };
117 $typeTransform{"boolean"} = {
118     "param" => "bool",
119     "variable"=> "bool",
120     "defaultValue" => "false",
121     "forward" => "",
122     "header" => "",
123     "JSONType" => "Boolean"
124 };
125 $typeTransform{"void"} = {
126     "forward" => "",
127     "header" => ""
128 };
129
130 # Default License Templates
131
132 my $licenseTemplate = << "EOF";
133 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
134 // Use of this source code is governed by a BSD-style license that can be
135 // found in the LICENSE file.
136 EOF
137
138 my $codeGenerator;
139 my $outputDir;
140 my $outputHeadersDir;
141 my $writeDependencies;
142 my $verbose;
143
144 my $namespace;
145
146 my $backendClassName;
147 my $backendJSStubName;
148 my %backendTypes;
149 my %backendMethods;
150 my @backendMethodsImpl;
151 my $backendConstructor;
152 my @backendConstantDeclarations;
153 my @backendConstantDefinitions;
154 my $backendFooter;
155 my @backendStubJS;
156
157 my $frontendClassName;
158 my %frontendTypes;
159 my %frontendMethods;
160 my @frontendMethodsImpl;
161 my $frontendConstructor;
162 my @frontendConstantDeclarations;
163 my @frontendConstantDefinitions;
164 my $frontendFooter;
165
166 # Default constructor
167 sub new
168 {
169     my $object = shift;
170     my $reference = { };
171
172     $codeGenerator = shift;
173     $outputDir = shift;
174     $outputHeadersDir = shift;
175     shift; # $useLayerOnTop
176     shift; # $preprocessor
177     $writeDependencies = shift;
178     $verbose = shift;
179
180     bless($reference, $object);
181     return $reference;
182 }
183
184 # Params: 'idlDocument' struct
185 sub GenerateModule
186 {
187     my $object = shift;
188     my $dataNode = shift;
189
190     $namespace = $dataNode->module;
191     $namespace =~ s/core/WebCore/;
192 }
193
194 # Params: 'idlDocument' struct
195 sub GenerateInterface
196 {
197     my $object = shift;
198     my $interface = shift;
199     my $defines = shift;
200
201     my $className = $interface->name;
202
203     $frontendClassName = $className . "Frontend";
204     $frontendConstructor = "    ${frontendClassName}(InspectorClient* inspectorClient) : m_inspectorClient(inspectorClient) { }";
205     $frontendFooter = "    InspectorClient* m_inspectorClient;";
206     $frontendTypes{"String"} = 1;
207     $frontendTypes{"InspectorClient"} = 1;
208     $frontendTypes{"PassRefPtr"} = 1;
209
210     $backendClassName = $className . "BackendDispatcher";
211     $backendJSStubName = $className . "BackendStub";
212     my @backendHead;
213     push(@backendHead, "    ${backendClassName}(InspectorController* inspectorController) : m_inspectorController(inspectorController) { }");
214     push(@backendHead, "    void reportProtocolError(const long callId, const String& errorText) const;");
215     push(@backendHead, "    void dispatch(const String& message);");
216     push(@backendHead, "    static bool getCommandName(const String& message, String* result);");
217     $backendConstructor = join("\n", @backendHead);
218     $backendFooter = "    InspectorController* m_inspectorController;";
219     $backendTypes{"Controller"} = 1;
220     $backendTypes{"InspectorClient"} = 1;
221     $backendTypes{"PassRefPtr"} = 1;
222     $backendTypes{"Object"} = 1;
223
224     push(@backendMethodsImpl, generateBackendMessageParser());
225     generateFunctions($interface);
226
227     # Make dispatcher methods private on the backend.
228     push(@backendConstantDeclarations, "");
229     push(@backendConstantDeclarations, "private:");
230 }
231
232 sub generateFunctions
233 {
234     my $interface = shift;
235
236     foreach my $function (@{$interface->functions}) {
237         if ($function->signature->extendedAttributes->{"notify"}) {
238             generateFrontendFunction($function);
239         } else {
240             generateBackendFunction($function);
241         }
242     }
243     push(@backendMethodsImpl, generateBackendDispatcher());
244     push(@backendMethodsImpl, generateBackendReportProtocolError());
245
246     @backendStubJS = generateBackendStubJS($interface);
247 }
248
249 sub generateFrontendFunction
250 {
251     my $function = shift;
252
253     my $functionName = $function->signature->name;
254
255     my @argsFiltered = grep($_->direction eq "out", @{$function->parameters}); # just keep only out parameters for frontend interface.
256     map($frontendTypes{$_->type} = 1, @argsFiltered); # register required types.
257     my $arguments = join(", ", map($typeTransform{$_->type}->{"param"} . " " . $_->name, @argsFiltered)); # prepare arguments for function signature.
258
259     my $signature = "    void ${functionName}(${arguments});";
260     if (!$frontendMethods{${signature}}) {
261         $frontendMethods{${signature}} = 1;
262
263         my @function;
264         push(@function, "void ${frontendClassName}::${functionName}(${arguments})");
265         push(@function, "{");
266         push(@function, "    RefPtr<InspectorObject> ${functionName}Message = InspectorObject::create();");
267         push(@function, "    ${functionName}Message->setString(\"type\", \"event\");");
268         push(@function, "    ${functionName}Message->setString(\"event\", \"$functionName\");");
269         push(@function, "    RefPtr<InspectorObject> payloadDataObject = InspectorObject::create();");
270         my @pushArguments = map("    payloadDataObject->set" . $typeTransform{$_->type}->{"JSONType"} . "(\"" . $_->name . "\", " . $_->name . ");", @argsFiltered);
271         push(@function, @pushArguments);
272         push(@function, "    ${functionName}Message->setObject(\"data\", payloadDataObject);");
273         push(@function, "    m_inspectorClient->sendMessageToFrontend(${functionName}Message->toJSONString());");
274
275         push(@function, "}");
276         push(@function, "");
277         push(@frontendMethodsImpl, @function);
278     }
279 }
280
281 sub generateBackendFunction
282 {
283     my $function = shift;
284
285     my $functionName = $function->signature->name;
286
287     push(@backendConstantDeclarations, "    static const char* ${functionName}Cmd;");
288     push(@backendConstantDefinitions, "const char* ${backendClassName}::${functionName}Cmd = \"${functionName}\";");
289
290     map($backendTypes{$_->type} = 1, @{$function->parameters}); # register required types
291     my @inArgs = grep($_->direction eq "in" && !($_->name eq "callId") , @{$function->parameters});
292     my @outArgs = grep($_->direction eq "out", @{$function->parameters});
293
294     my $signature = "    void ${functionName}(long callId, InspectorObject* requestMessageObject);";
295     !$backendMethods{${signature}} || die "Duplicate function was detected for signature '$signature'.";
296     $backendMethods{${signature}} = $functionName;
297
298     my @function;
299     my $requestMessageObject = scalar(@inArgs) ? " requestMessageObject" : "";
300     push(@function, "void ${backendClassName}::${functionName}(long callId, InspectorObject*$requestMessageObject)");
301     push(@function, "{");
302     push(@function, "    RefPtr<InspectorArray> protocolErrors = InspectorArray::create();");
303     push(@function, "");
304
305     my $domain = $function->signature->extendedAttributes->{"handler"} || "Controller";
306     my $domainAccessor = $typeTransform{$domain}->{"domainAccessor"};
307     $backendTypes{$domain} = 1;
308     push(@function, "    if (!$domainAccessor)");
309     push(@function, "        protocolErrors->pushString(String::format(\"Protocol Error: %s handler is not available.\", \"$domain\"));");
310     push(@function, "");
311
312     if (scalar(@inArgs)) {
313         # declare variables for all 'in' args;
314         push(@function, map("    " . $typeTransform{$_->type}->{"variable"} . " " . $_->name . " = " . $typeTransform{$_->type}->{"defaultValue"} . ";", @inArgs));
315
316         push(@function, "");
317         push(@function, "    RefPtr<InspectorObject> argumentsContainer;");
318         push(@function, "    if (!(argumentsContainer = requestMessageObject->getObject(\"arguments\"))) {");
319         push(@function, "        protocolErrors->pushString(String::format(\"Protocol Error: 'arguments' property with type 'object' was not found.\"));");
320         push(@function, "    } else {");
321         push(@function, "        InspectorObject::const_iterator argumentsEndIterator = argumentsContainer->end();");
322
323         foreach my $parameter (@inArgs) {
324             my $name = $parameter->name;
325             my $type = $parameter->type;
326             my $variableType = $typeTransform{$type}->{"variable"};
327             my $JSONType = $typeTransform{$type}->{"JSONType"};
328
329             push(@function, "");
330             push(@function, "        InspectorObject::const_iterator ${name}ValueIterator = argumentsContainer->find(\"$name\");");
331             push(@function, "        if (${name}ValueIterator == argumentsEndIterator) {");
332             push(@function, "            protocolErrors->pushString(String::format(\"Protocol Error: Argument '%s' with type '%s' was not found.\", \"$name\", \"$JSONType\"));");
333             push(@function, "        } else {");
334             push(@function, "            if (!${name}ValueIterator->second->as$JSONType(&$name)) {");
335             push(@function, "                protocolErrors->pushString(String::format(\"Protocol Error: Argument '%s' has wrong type. It should be '%s'.\", \"$name\", \"$JSONType\"));");
336             push(@function, "            }");
337             push(@function, "        }");
338         }
339         push(@function, "    }");
340     }
341
342     # declare local variables for out arguments.
343     push(@function, map("    " . $typeTransform{$_->type}->{"variable"} . " " . $_->name . " = " . $typeTransform{$_->type}->{"defaultValue"} . ";", @outArgs));
344
345     my $args = join(", ", (map($_->name, @inArgs), map("&" . $_->name, @outArgs)));
346     push(@function, "    if (!protocolErrors->length())");
347     push(@function, "        $domainAccessor->$functionName($args);");
348     push(@function, "");
349
350     push(@function, "    // use InspectorFrontend as a marker of WebInspector availability");
351     push(@function, "    if ((callId || protocolErrors->length()) && m_inspectorController->hasFrontend()) {");
352     push(@function, "        RefPtr<InspectorObject> responseMessage = InspectorObject::create();");
353     push(@function, "        responseMessage->setNumber(\"seq\", callId);");
354     push(@function, "        responseMessage->setBoolean(\"success\", !protocolErrors->length());");
355     push(@function, "");
356     push(@function, "        if (protocolErrors->length())");
357     push(@function, "            responseMessage->setArray(\"errors\", protocolErrors);");
358     if (scalar(@outArgs)) {
359         push(@function, "        else {");
360         push(@function, "            RefPtr<InspectorObject> responseData = InspectorObject::create();");
361         push(@function, map("            responseData->set" . $typeTransform{$_->type}->{"JSONType"} . "(\"" . $_->name . "\", " . $_->name . ");", @outArgs));
362         push(@function, "            responseMessage->setObject(\"data\", responseData);");
363         push(@function, "        }");
364     }
365     push(@function, "        m_inspectorController->inspectorClient()->sendMessageToFrontend(responseMessage->toJSONString());");
366     push(@function, "    }");
367
368
369     push(@function, "}");
370     push(@function, "");
371     push(@backendMethodsImpl, @function);
372 }
373
374 sub generateBackendReportProtocolError
375 {
376     my $reportProtocolError = << "EOF";
377
378 void ${backendClassName}::reportProtocolError(const long callId, const String& errorText) const
379 {
380     RefPtr<InspectorObject> message = InspectorObject::create();
381     message->setNumber("seq", callId);
382     message->setBoolean("success", false);
383     RefPtr<InspectorArray> errors = InspectorArray::create();
384     errors->pushString(errorText);
385     message->setArray("errors", errors);
386     m_inspectorController->inspectorClient()->sendMessageToFrontend(message->toJSONString());
387 }
388 EOF
389     return split("\n", $reportProtocolError);
390 }
391
392
393 sub generateBackendDispatcher
394 {
395     my @body;
396     my @methods = map($backendMethods{$_}, keys %backendMethods);
397     my @mapEntries = map("        dispatchMap.add(${_}Cmd, &${backendClassName}::$_);", @methods);
398     my $mapEntries = join("\n", @mapEntries);
399
400     my $backendDispatcherBody = << "EOF";
401 void ${backendClassName}::dispatch(const String& message)
402 {
403     typedef void (${backendClassName}::*CallHandler)(long callId, InspectorObject* messageObject);
404     typedef HashMap<String, CallHandler> DispatchMap;
405     DEFINE_STATIC_LOCAL(DispatchMap, dispatchMap, );
406     long callId = 0;
407
408     if (dispatchMap.isEmpty()) {
409 $mapEntries
410     }
411
412     RefPtr<InspectorValue> parsedMessage = InspectorValue::parseJSON(message);
413     if (!parsedMessage) {
414         reportProtocolError(callId, "Protocol Error: Invalid message format. Message should be in JSON format.");
415         return;
416     }
417
418     RefPtr<InspectorObject> messageObject = parsedMessage->asObject();
419     if (!messageObject) {
420         reportProtocolError(callId, "Protocol Error: Invalid message format. The message should be a JSONified object.");
421         return;
422     }
423
424     RefPtr<InspectorValue> commandValue = messageObject->get("command");
425     if (!commandValue) {
426         reportProtocolError(callId, "Protocol Error: Invalid message format. 'command' property wasn't found.");
427         return;
428     }
429
430     String command;
431     if (!commandValue->asString(&command)) {
432         reportProtocolError(callId, "Protocol Error: Invalid message format. The type of 'command' property should be string.");
433         return;
434     }
435
436     RefPtr<InspectorValue> callIdValue = messageObject->get("seq");
437     if (!callIdValue) {
438         reportProtocolError(callId, "Protocol Error: Invalid message format. 'seq' property was not found in the request.");
439         return;
440     }
441
442     if (!callIdValue->asNumber(&callId)) {
443         reportProtocolError(callId, "Protocol Error: Invalid message format. The type of 'seq' property should be number.");
444         return;
445     }
446
447     HashMap<String, CallHandler>::iterator it = dispatchMap.find(command);
448     if (it == dispatchMap.end()) {
449         reportProtocolError(callId, String::format("Protocol Error: Invalid command was received. '%s' wasn't found.", command.utf8().data()));
450         return;
451     }
452
453     ((*this).*it->second)(callId, messageObject.get());
454 }
455 EOF
456     return split("\n", $backendDispatcherBody);
457 }
458
459 sub generateBackendMessageParser
460 {
461     my $messageParserBody = << "EOF";
462 bool ${backendClassName}::getCommandName(const String& message, String* result)
463 {
464     RefPtr<InspectorValue> value = InspectorValue::parseJSON(message);
465     if (!value)
466         return false;
467
468     RefPtr<InspectorObject> object = value->asObject();
469     if (!object)
470         return false;
471
472     RefPtr<InspectorValue> commandValue = object->get("command");
473     if (!commandValue)
474         return false;
475
476     return commandValue->asString(result);
477 }
478
479 EOF
480     return split("\n", $messageParserBody);
481 }
482
483 sub generateBackendStubJS
484 {
485     my $interface = shift;
486     my @backendFunctions = grep(!$_->signature->extendedAttributes->{"notify"}, @{$interface->functions});
487     my @JSStubs;
488
489     foreach my $function (@backendFunctions) {
490         my $name = $function->signature->name;
491         my $domain = $function->signature->extendedAttributes->{"handler"};
492         my $argumentNames = join(",", map("\"" . $_->name . "\": \"" . lc($typeTransform{$_->type}->{"JSONType"}) . "\"", grep($_->direction eq "in", @{$function->parameters})));
493         push(@JSStubs, "    this._registerDelegate('{" .
494             "\"seq\": 0, " .
495             "\"domain\": \"$domain\", " .
496             "\"command\": \"$name\", " .
497             "\"arguments\": {$argumentNames}" .
498         "}');");
499     }
500
501     my $JSStubs = join("\n", @JSStubs);
502     my $inspectorBackendStubJS = << "EOF";
503 $licenseTemplate
504
505 WebInspector.InspectorBackendStub = function()
506 {
507 $JSStubs
508 }
509
510 WebInspector.InspectorBackendStub.prototype = {
511     _registerDelegate: function(commandInfo)
512     {
513         var commandObject = JSON.parse(commandInfo);
514         this[commandObject.command] = this.sendMessageToBackend.bind(this, commandInfo);
515     },
516
517     sendMessageToBackend: function()
518     {
519         var args = Array.prototype.slice.call(arguments);
520         var request = JSON.parse(args.shift());
521
522         for (var key in request.arguments) {
523             if (args.length === 0) {
524                 console.error("Protocol Error: Invalid number of arguments for 'InspectorBackend.%s' call. It should have the next arguments '%s'.", request.command, JSON.stringify(request.arguments));
525                 return;
526             }
527             var value = args.shift();
528             if (typeof value !== request.arguments[key]) {
529                 console.error("Protocol Error: Invalid type of argument '%s' for 'InspectorBackend.%s' call. It should be '%s' but it is '%s'.", key, request.command, request.arguments[key], typeof value);
530                 return;
531             }
532             request.arguments[key] = value;
533         }
534
535         if (args.length === 1) {
536             if (typeof args[0] !== "function" && typeof args[0] !== "undefined") {
537                 console.error("Protocol Error: Optional callback argument for 'InspectorBackend.%s' call should be a function but its type is '%s'.", request.command, typeof args[0]);
538                 return;
539             }
540             request.seq = WebInspector.Callback.wrap(args[0]);
541         }
542
543         var message = JSON.stringify(request);
544         InspectorFrontendHost.sendMessageToBackend(message);
545     }
546 }
547
548 InspectorBackend = new WebInspector.InspectorBackendStub();
549
550 EOF
551     return split("\n", $inspectorBackendStubJS);
552 }
553
554 sub generateHeader
555 {
556     my $className = shift;
557     my $types = shift;
558     my $constructor = shift;
559     my $constants = shift;
560     my $methods = shift;
561     my $footer = shift;
562
563     my $forwardHeaders = join("\n", sort(map("#include <" . $typeTransform{$_}->{"forwardHeader"} . ">", grep($typeTransform{$_}->{"forwardHeader"}, keys %{$types}))));
564     my $forwardDeclarations = join("\n", sort(map("class " . $typeTransform{$_}->{"forward"} . ";", grep($typeTransform{$_}->{"forward"}, keys %{$types}))));
565     my $constantDeclarations = join("\n", @{$constants});
566     my $methodsDeclarations = join("\n", keys %{$methods});
567
568     my $headerBody = << "EOF";
569 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
570 // Use of this source code is governed by a BSD-style license that can be
571 // found in the LICENSE file.
572 #ifndef ${className}_h
573 #define ${className}_h
574
575 ${forwardHeaders}
576
577 namespace $namespace {
578
579 $forwardDeclarations
580
581 class $className {
582 public:
583 $constructor
584
585 $constantDeclarations
586 $methodsDeclarations
587
588 private:
589 $footer
590 };
591
592 } // namespace $namespace
593 #endif // !defined(${className}_h)
594
595 EOF
596     return $headerBody;
597 }
598
599 sub generateSource
600 {
601     my $className = shift;
602     my $types = shift;
603     my $constants = shift;
604     my $methods = shift;
605
606     my @sourceContent = split("\r", $licenseTemplate);
607     push(@sourceContent, "\n#include \"config.h\"");
608     push(@sourceContent, "#include \"$className.h\"");
609     push(@sourceContent, "#include <wtf/text/CString.h>");
610     push(@sourceContent, "");
611     push(@sourceContent, "#if ENABLE(INSPECTOR)");
612     push(@sourceContent, "");
613
614     my %headers;
615     foreach my $type (keys %{$types}) {
616         $headers{"#include \"" . $typeTransform{$type}->{"header"} . "\""} = 1 if !$typeTransform{$type}->{"header"} eq  "";
617     }
618     push(@sourceContent, sort keys %headers);
619     push(@sourceContent, "");
620     push(@sourceContent, "namespace $namespace {");
621     push(@sourceContent, "");
622     push (@sourceContent, join("\n", @{$constants}));
623     push(@sourceContent, "");
624     push(@sourceContent, @{$methods});
625     push(@sourceContent, "");
626     push(@sourceContent, "} // namespace $namespace");
627     push(@sourceContent, "");
628     push(@sourceContent, "#endif // ENABLE(INSPECTOR)");
629     push(@sourceContent, "");
630     return @sourceContent;
631 }
632
633 sub finish
634 {
635     my $object = shift;
636
637     open(my $SOURCE, ">$outputDir/$frontendClassName.cpp") || die "Couldn't open file $outputDir/$frontendClassName.cpp";
638     print $SOURCE join("\n", generateSource($frontendClassName, \%frontendTypes, \@frontendConstantDefinitions, \@frontendMethodsImpl));
639     close($SOURCE);
640     undef($SOURCE);
641
642     open(my $HEADER, ">$outputHeadersDir/$frontendClassName.h") || die "Couldn't open file $outputHeadersDir/$frontendClassName.h";
643     print $HEADER generateHeader($frontendClassName, \%frontendTypes, $frontendConstructor, \@frontendConstantDeclarations, \%frontendMethods, $frontendFooter);
644     close($HEADER);
645     undef($HEADER);
646
647     open($SOURCE, ">$outputDir/$backendClassName.cpp") || die "Couldn't open file $outputDir/$backendClassName.cpp";
648     print $SOURCE join("\n", generateSource($backendClassName, \%backendTypes, \@backendConstantDefinitions, \@backendMethodsImpl));
649     close($SOURCE);
650     undef($SOURCE);
651
652     open($HEADER, ">$outputHeadersDir/$backendClassName.h") || die "Couldn't open file $outputHeadersDir/$backendClassName.h";
653     print $HEADER join("\n", generateHeader($backendClassName, \%backendTypes, $backendConstructor, \@backendConstantDeclarations, \%backendMethods, $backendFooter));
654     close($HEADER);
655     undef($HEADER);
656
657     open(my $JS_STUB, ">$outputDir/$backendJSStubName.js") || die "Couldn't open file $outputDir/$backendJSStubName.js";
658     print $JS_STUB join("\n", @backendStubJS);
659     close($JS_STUB);
660     undef($JS_STUB);
661 }
662
663 1;