1a811a6367fe12813e0f27a7748bc5ad395741c8
[WebKit-https.git] / Source / 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{"ApplicationCache"} = {
14     "forward" => "InspectorApplicationCacheAgent",
15     "header" => "InspectorApplicationCacheAgent.h",
16     "domainAccessor" => "m_applicationCacheAgent",
17 };
18 $typeTransform{"CSS"} = {
19     "forward" => "InspectorCSSAgent",
20     "header" => "InspectorCSSAgent.h",
21     "domainAccessor" => "m_cssAgent",
22 };
23 $typeTransform{"Console"} = {
24     "forward" => "InspectorConsoleAgent",
25     "header" => "InspectorConsoleAgent.h",
26     "domainAccessor" => "m_consoleAgent",
27 };
28 $typeTransform{"Page"} = {
29     "forward" => "InspectorPageAgent",
30     "header" => "InspectorPageAgent.h",
31     "domainAccessor" => "m_pageAgent",
32 };
33 $typeTransform{"Debugger"} = {
34     "forward" => "InspectorDebuggerAgent",
35     "header" => "InspectorDebuggerAgent.h",
36     "domainAccessor" => "m_debuggerAgent",
37 };
38 $typeTransform{"DOMDebugger"} = {
39     "forward" => "InspectorDOMDebuggerAgent",
40     "header" => "InspectorDOMDebuggerAgent.h",
41     "domainAccessor" => "m_domDebuggerAgent",
42 };
43 $typeTransform{"Database"} = {
44     "forward" => "InspectorDatabaseAgent",
45     "header" => "InspectorDatabaseAgent.h",
46     "domainAccessor" => "m_databaseAgent",
47 };
48 $typeTransform{"DOM"} = {
49     "forward" => "InspectorDOMAgent",
50     "header" => "InspectorDOMAgent.h",
51     "domainAccessor" => "m_domAgent",
52 };
53 $typeTransform{"DOMStorage"} = {
54     "forward" => "InspectorDOMStorageAgent",
55     "header" => "InspectorDOMStorageAgent.h",
56     "domainAccessor" => "m_domStorageAgent",
57 };
58 $typeTransform{"FileSystem"} = {
59     "forward" => "InspectorFileSystemAgent",
60     "header" => "InspectorFileSystemAgent.h",
61     "domainAccessor" => "m_fileSystemAgent",
62 };
63 $typeTransform{"Inspector"} = {
64     "forward" => "InspectorAgent",
65     "header" => "InspectorAgent.h",
66     "domainAccessor" => "m_inspectorAgent",
67 };
68 $typeTransform{"Network"} = {
69     "forward" => "InspectorResourceAgent",
70     "header" => "InspectorResourceAgent.h",
71     "domainAccessor" => "m_resourceAgent",
72 };
73 $typeTransform{"Profiler"} = {
74     "forward" => "InspectorProfilerAgent",
75     "header" => "InspectorProfilerAgent.h",
76     "domainAccessor" => "m_profilerAgent",
77 };
78 $typeTransform{"Runtime"} = {
79     "forward" => "InspectorRuntimeAgent",
80     "header" => "InspectorRuntimeAgent.h",
81     "domainAccessor" => "m_runtimeAgent",
82 };
83 $typeTransform{"Timeline"} = {
84     "forward" => "InspectorTimelineAgent",
85     "header" => "InspectorTimelineAgent.h",
86     "domainAccessor" => "m_timelineAgent",
87 };
88 $typeTransform{"Worker"} = {
89     "forward" => "InspectorWorkerAgent",
90     "header" => "InspectorWorkerAgent.h",
91     "domainAccessor" => "m_workerAgent",
92 };
93
94 $typeTransform{"Frontend"} = {
95     "forward" => "InspectorFrontend",
96     "header" => "InspectorFrontend.h",
97 };
98 $typeTransform{"PassRefPtr"} = {
99     "forwardHeader" => "wtf/PassRefPtr.h",
100 };
101 $typeTransform{"RefCounted"} = {
102     "forwardHeader" => "wtf/RefCounted.h",
103 };
104 $typeTransform{"InspectorFrontendChannel"} = {
105     "forward" => "InspectorFrontendChannel",
106     "header" => "InspectorFrontendChannel.h",
107 };
108 $typeTransform{"Object"} = {
109     "param" => "PassRefPtr<InspectorObject>",
110     "variable" => "RefPtr<InspectorObject>",
111     "defaultValue" => "InspectorObject::create()",
112     "forward" => "InspectorObject",
113     "header" => "InspectorValues.h",
114     "JSONType" => "Object",
115     "JSType" => "object",
116 };
117 $typeTransform{"Array"} = {
118     "param" => "PassRefPtr<InspectorArray>",
119     "variable" => "RefPtr<InspectorArray>",
120     "defaultValue" => "InspectorArray::create()",
121     "forward" => "InspectorArray",
122     "header" => "InspectorValues.h",
123     "JSONType" => "Array",
124     "JSType" => "object",
125 };
126 $typeTransform{"Value"} = {
127     "param" => "PassRefPtr<InspectorValue>",
128     "variable" => "RefPtr<InspectorValue>",
129     "defaultValue" => "InspectorValue::null()",
130     "forward" => "InspectorValue",
131     "header" => "InspectorValues.h",
132     "JSONType" => "Value",
133     "JSType" => "",
134 };
135 $typeTransform{"String"} = {
136     "param" => "const String&",
137     "variable" => "String",
138     "return" => "String",
139     "defaultValue" => "\"\"",
140     "forwardHeader" => "PlatformString.h",
141     "header" => "PlatformString.h",
142     "JSONType" => "String",
143     "JSType" => "string"
144 };
145 $typeTransform{"long"} = {
146     "param" => "long",
147     "variable" => "long",
148     "defaultValue" => "0",
149     "forward" => "",
150     "header" => "",
151     "JSONType" => "Number",
152     "JSType" => "number"
153 };
154 $typeTransform{"int"} = {
155     "param" => "int",
156     "variable" => "int",
157     "defaultValue" => "0",
158     "forward" => "",
159     "header" => "",
160     "JSONType" => "Number",
161     "JSType" => "number"
162 };
163 $typeTransform{"unsigned long"} = {
164     "param" => "unsigned long",
165     "variable" => "unsigned long",
166     "defaultValue" => "0u",
167     "forward" => "",
168     "header" => "",
169     "JSONType" => "Number",
170     "JSType" => "number"
171 };
172 $typeTransform{"unsigned int"} = {
173     "param" => "unsigned int",
174     "variable" => "unsigned int",
175     "defaultValue" => "0u",
176     "forward" => "",
177     "header" => "",
178     "JSONType" => "Number",
179     "JSType" => "number"
180 };
181 $typeTransform{"double"} = {
182     "param" => "double",
183     "variable" => "double",
184     "defaultValue" => "0.0",
185     "forward" => "",
186     "header" => "",
187     "JSONType" => "Number",
188     "JSType" => "number"
189 };
190 $typeTransform{"boolean"} = {
191     "param" => "bool",
192     "variable"=> "bool",
193     "defaultValue" => "false",
194     "forward" => "",
195     "header" => "",
196     "JSONType" => "Boolean",
197     "JSType" => "boolean"
198 };
199 $typeTransform{"void"} = {
200     "forward" => "",
201     "header" => ""
202 };
203 $typeTransform{"Vector"} = {
204     "header" => "wtf/Vector.h"
205 };
206
207 # Default License Templates
208
209 my $licenseTemplate = << "EOF";
210 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
211 // Use of this source code is governed by a BSD-style license that can be
212 // found in the LICENSE file.
213 EOF
214
215 my $codeGenerator;
216 my $outputDir;
217 my $outputHeadersDir;
218 my $writeDependencies;
219 my $verbose;
220
221 my $namespace;
222
223 my $backendClassName;
224 my $backendClassDeclaration;
225 my $backendJSStubName;
226 my %backendTypes;
227 my @backendMethods;
228 my @backendMethodsImpl;
229 my %backendMethodSignatures;
230 my $backendConstructor;
231 my @backendConstantDeclarations;
232 my @backendConstantDefinitions;
233 my @backendFooter;
234 my @backendJSStubs;
235 my @backendJSEvents;
236 my %backendDomains;
237
238 my $frontendClassName;
239 my %frontendTypes;
240 my @frontendMethods;
241 my @frontendAgentFields;
242 my @frontendMethodsImpl;
243 my %frontendMethodSignatures;
244 my $frontendConstructor;
245 my @frontendConstantDeclarations;
246 my @frontendConstantDefinitions;
247 my @frontendFooter;
248
249 # Default constructor
250 sub new
251 {
252     my $object = shift;
253     my $reference = { };
254
255     $codeGenerator = shift;
256     $outputDir = shift;
257     $outputHeadersDir = shift;
258     shift; # $useLayerOnTop
259     shift; # $preprocessor
260     $writeDependencies = shift;
261     $verbose = shift;
262
263     bless($reference, $object);
264     return $reference;
265 }
266
267 # Params: 'idlDocument' struct
268 sub GenerateModule
269 {
270     my $object = shift;
271     my $dataNode = shift;
272
273     $namespace = $dataNode->module;
274     $namespace =~ s/core/WebCore/;
275
276     $frontendClassName = "InspectorFrontend";
277     $frontendConstructor = "    ${frontendClassName}(InspectorFrontendChannel*);";
278     push(@frontendFooter, "private:");
279     push(@frontendFooter, "    InspectorFrontendChannel* m_inspectorFrontendChannel;");
280     $frontendTypes{"String"} = 1;
281     $frontendTypes{"InspectorFrontendChannel"} = 1;
282     $frontendTypes{"PassRefPtr"} = 1;
283
284     $backendClassName = "InspectorBackendDispatcher";
285     $backendClassDeclaration = "InspectorBackendDispatcher: public RefCounted<InspectorBackendDispatcher>";
286     $backendJSStubName = "InspectorBackendStub";
287     $backendTypes{"Inspector"} = 1;
288     $backendTypes{"InspectorFrontendChannel"} = 1;
289     $backendTypes{"PassRefPtr"} = 1;
290     $backendTypes{"RefCounted"} = 1;
291     $backendTypes{"Object"} = 1;
292 }
293
294 # Params: 'idlDocument' struct
295 sub GenerateInterface
296 {
297     my $object = shift;
298     my $interface = shift;
299     my $defines = shift;
300
301     my %agent = (
302         methodDeclarations => [],
303         methodSignatures => {}
304     );
305     generateFunctions($interface, \%agent);
306     if (@{$agent{methodDeclarations}}) {
307         generateAgentDeclaration($interface, \%agent);
308     }
309 }
310
311 sub generateAgentDeclaration
312 {
313     my $interface = shift;
314     my $agent = shift;
315     my $agentName = $interface->name;
316     push(@frontendMethods, "    class ${agentName} {");
317     push(@frontendMethods, "    public:");
318     push(@frontendMethods, "        ${agentName}(InspectorFrontendChannel* inspectorFrontendChannel) : m_inspectorFrontendChannel(inspectorFrontendChannel) { }");
319     push(@frontendMethods, @{$agent->{methodDeclarations}});
320     push(@frontendMethods, "        void setInspectorFrontendChannel(InspectorFrontendChannel* inspectorFrontendChannel) { m_inspectorFrontendChannel = inspectorFrontendChannel; }");
321     push(@frontendMethods, "        InspectorFrontendChannel* getInspectorFrontendChannel() { return m_inspectorFrontendChannel; }");
322     push(@frontendMethods, "    private:");
323     push(@frontendMethods, "        InspectorFrontendChannel* m_inspectorFrontendChannel;");
324     push(@frontendMethods, "    };");
325     push(@frontendMethods, "");
326
327     my $getterName = lc($agentName);
328     push(@frontendMethods, "    ${agentName}* ${getterName}() { return &m_${getterName}; }");
329     push(@frontendMethods, "");
330
331     push(@frontendFooter, "    ${agentName} m_${getterName};");
332
333     push(@frontendAgentFields, "m_${getterName}");
334 }
335
336 sub generateFrontendConstructorImpl
337 {
338     my @frontendConstructorImpl;
339     push(@frontendConstructorImpl, "${frontendClassName}::${frontendClassName}(InspectorFrontendChannel* inspectorFrontendChannel)");
340     push(@frontendConstructorImpl, "    : m_inspectorFrontendChannel(inspectorFrontendChannel)");
341     foreach my $agentField (@frontendAgentFields) {
342         push(@frontendConstructorImpl, "    , ${agentField}(inspectorFrontendChannel)");
343     }
344     push(@frontendConstructorImpl, "{");
345     push(@frontendConstructorImpl, "}");
346     return @frontendConstructorImpl;
347 }
348
349 sub generateFunctions
350 {
351     my $interface = shift;
352     my $agent = shift;
353
354     foreach my $function (@{$interface->functions}) {
355         if ($function->signature->extendedAttributes->{"event"}) {
356             generateFrontendFunction($interface, $function, $agent);
357         } else {
358             generateBackendFunction($interface, $function);
359         }
360     }
361
362     collectBackendJSStubFunctions($interface);
363     collectBackendJSStubEvents($interface);
364 }
365
366 sub generateFrontendFunction
367 {
368     my $interface = shift;
369     my $function = shift;
370     my $agent = shift;
371
372     my $functionName = $function->signature->name;
373
374     my $domain = $interface->name;
375     my @argsFiltered = grep($_->direction eq "out", @{$function->parameters}); # just keep only out parameters for frontend interface.
376     map($frontendTypes{$_->type} = 1, @argsFiltered); # register required types.
377     my $arguments = join(", ", map(typeTraits($_->type, "param") . " " . $_->name, @argsFiltered)); # prepare arguments for function signature.
378
379     my $signature = "        void ${functionName}(${arguments});";
380     !$agent->{methodSignatures}->{$signature} || die "Duplicate frontend function was detected for signature '$signature'.";
381     $agent->{methodSignatures}->{$signature} = 1;
382     push(@{$agent->{methodDeclarations}}, $signature);
383
384     my @function;
385     push(@function, "void ${frontendClassName}::${domain}::${functionName}(${arguments})");
386     push(@function, "{");
387     push(@function, "    RefPtr<InspectorObject> ${functionName}Message = InspectorObject::create();");
388     push(@function, "    ${functionName}Message->setString(\"method\", \"$domain.$functionName\");");
389     if (scalar(@argsFiltered)) {
390         push(@function, "    RefPtr<InspectorObject> paramsObject = InspectorObject::create();");
391
392         foreach my $parameter (@argsFiltered) {
393             my $optional = $parameter->extendedAttributes->{"optional"} ? "if (" . $parameter->name . ")\n        " : "";
394             push(@function, "    " . $optional . "paramsObject->set" . typeTraits($parameter->type, "JSONType") . "(\"" . $parameter->name . "\", " . $parameter->name . ");");
395         }
396         push(@function, "    ${functionName}Message->setObject(\"params\", paramsObject);");
397     }
398     push(@function, "    if (m_inspectorFrontendChannel)");
399     push(@function, "        m_inspectorFrontendChannel->sendMessageToFrontend(${functionName}Message->toJSONString());");
400     push(@function, "}");
401     push(@function, "");
402     push(@frontendMethodsImpl, @function);
403 }
404
405 sub camelCase
406 {
407     my $value = shift;
408     $value =~ s/\b(\w)/\U$1/g; # make a camel-case name for type name
409     $value =~ s/ //g;
410     return $value;
411 }
412
413 sub generateBackendFunction
414 {
415     my $interface = shift;
416     my $function = shift;
417
418     my $functionName = $function->signature->name;
419     my $fullQualifiedFunctionName = $interface->name . "_" . $functionName;
420     my $fullQualifiedFunctionNameDot = $interface->name . "." . $functionName;
421
422     push(@backendConstantDeclarations, "        k${fullQualifiedFunctionName}Cmd,");
423     push(@backendConstantDefinitions, "    \"${fullQualifiedFunctionNameDot}\",");
424
425     map($backendTypes{$_->type} = 1, @{$function->parameters}); # register required types
426     my @inArgs = grep($_->direction eq "in", @{$function->parameters});
427     my @outArgs = grep($_->direction eq "out", @{$function->parameters});
428     
429     my $signature = "    void ${fullQualifiedFunctionName}(long callId, InspectorObject* requestMessageObject);";
430     !$backendMethodSignatures{${signature}} || die "Duplicate function was detected for signature '$signature'.";
431     $backendMethodSignatures{${signature}} = "$fullQualifiedFunctionName";
432     push(@backendMethods, ${signature});
433
434     my @function;
435     my $requestMessageObject = scalar(@inArgs) ? " requestMessageObject" : "";
436     push(@function, "void ${backendClassName}::${fullQualifiedFunctionName}(long callId, InspectorObject*$requestMessageObject)");
437     push(@function, "{");
438     push(@function, "    RefPtr<InspectorArray> protocolErrors = InspectorArray::create();");
439     push(@function, "");
440
441     my $domain = $interface->name;
442     my $domainAccessor = typeTraits($domain, "domainAccessor");
443     $backendTypes{$domain} = 1;
444     $backendDomains{$domain} = 1;
445     push(@function, "    if (!$domainAccessor)");
446     push(@function, "        protocolErrors->pushString(\"$domain handler is not available.\");");
447     push(@function, "");
448
449     # declare local variables for out arguments.
450     if (scalar(@outArgs)) {
451         push(@function, map("    " . typeTraits($_->type, "variable") . " out_" . $_->name . " = " . typeTraits($_->type, "defaultValue") . ";", @outArgs));
452         push(@function, "");
453     }
454     push(@function, "    ErrorString error;");
455     push(@function, "");
456
457     my $indent = "";
458     if (scalar(@inArgs)) {
459         push(@function, "    RefPtr<InspectorObject> paramsContainer = requestMessageObject->getObject(\"params\");");
460         push(@function, "    InspectorObject* paramsContainerPtr = paramsContainer.get();");
461         push(@function, "    InspectorArray* protocolErrorsPtr = protocolErrors.get();");
462
463         foreach my $parameter (@inArgs) {
464             my $name = $parameter->name;
465             my $type = $parameter->type;
466             my $typeString = camelCase($parameter->type);
467             my $optionalFlagArgument = "0";
468             if ($parameter->extendedAttributes->{"optional"}) {
469                 push(@function, "    bool ${name}_valueFound = false;");
470                 $optionalFlagArgument = "&${name}_valueFound";
471             }
472             push(@function, "    " . typeTraits($type, "variable") . " in_$name = get$typeString(paramsContainerPtr, \"$name\", $optionalFlagArgument, protocolErrorsPtr);");
473         }
474         push(@function, "");
475         $indent = "    ";
476     }
477
478     my $args = join(", ",
479                     ("&error",
480                      map(($_->extendedAttributes->{"optional"} ?
481                           $_->name . "_valueFound ? &in_" . $_->name . " : 0" :
482                           "in_" . $_->name), @inArgs),
483                      map("&out_" . $_->name, @outArgs)));
484
485     push(@function, $indent . "if (!protocolErrors->length())");
486     push(@function, $indent . "    $domainAccessor->$functionName($args);");
487     push(@function, "");
488     push(@function, "    RefPtr<InspectorObject> result = InspectorObject::create();");
489     if (scalar(@outArgs)) {
490         push(@function, "    if (!protocolErrors->length() && !error.length()) {");
491         foreach my $parameter (@outArgs) {
492             my $offset = "        ";
493             # Don't add optional boolean parameter to the result unless it is "true"
494             if ($parameter->extendedAttributes->{"optional"} && $parameter->type eq "boolean") {
495                 push(@function, $offset . "if (out_" . $parameter->name . ")");
496                 $offset .= "    ";
497             }
498             push(@function, $offset . "result->set" . typeTraits($parameter->type, "JSONType") . "(\"" . $parameter->name . "\", out_" . $parameter->name . ");");
499         }
500         push(@function, "    }");
501     }
502     push(@function, "    sendResponse(callId, result, String::format(\"Some arguments of method '%s' can't be processed\", \"$fullQualifiedFunctionNameDot\"), protocolErrors, error);");
503     push(@function, "}");
504     push(@function, "");
505     push(@backendMethodsImpl, @function);
506 }
507
508 sub generateBackendSendResponse
509 {
510     my $sendResponse = << "EOF";
511
512 void ${backendClassName}::sendResponse(long callId, PassRefPtr<InspectorObject> result, const String& errorMessage, PassRefPtr<InspectorArray> protocolErrors, ErrorString invocationError)
513 {
514     if (protocolErrors->length()) {
515         reportProtocolError(&callId, InvalidParams, errorMessage, protocolErrors);
516         return;
517     }
518     if (invocationError.length()) {
519         reportProtocolError(&callId, ServerError, invocationError);
520         return;
521     }
522
523     RefPtr<InspectorObject> responseMessage = InspectorObject::create();
524     responseMessage->setObject("result", result);
525     responseMessage->setNumber("id", callId);
526     if (m_inspectorFrontendChannel)
527         m_inspectorFrontendChannel->sendMessageToFrontend(responseMessage->toJSONString());
528 }    
529 EOF
530     return split("\n", $sendResponse);
531 }
532
533 sub generateBackendReportProtocolError
534 {
535     my $reportProtocolError = << "EOF";
536
537 void ${backendClassName}::reportProtocolError(const long* const callId, CommonErrorCode code, const String& errorMessage) const
538 {
539     reportProtocolError(callId, code, errorMessage, 0);
540 }
541
542 void ${backendClassName}::reportProtocolError(const long* const callId, CommonErrorCode code, const String& errorMessage, PassRefPtr<InspectorArray> data) const
543 {
544     DEFINE_STATIC_LOCAL(Vector<int>,s_commonErrors,);
545     if (!s_commonErrors.size()) {
546         s_commonErrors.insert(ParseError, -32700);
547         s_commonErrors.insert(InvalidRequest, -32600);
548         s_commonErrors.insert(MethodNotFound, -32601);
549         s_commonErrors.insert(InvalidParams, -32602);
550         s_commonErrors.insert(InternalError, -32603);
551         s_commonErrors.insert(ServerError, -32000);
552     }
553     ASSERT(code >=0);
554     ASSERT((unsigned)code < s_commonErrors.size());
555     ASSERT(s_commonErrors[code]);
556     RefPtr<InspectorObject> error = InspectorObject::create();
557     error->setNumber("code", s_commonErrors[code]);
558     error->setString("message", errorMessage);
559     ASSERT(error);
560     if (data)
561         error->setArray("data", data);
562     RefPtr<InspectorObject> message = InspectorObject::create();
563     message->setObject("error", error);
564     if (callId)
565         message->setNumber("id", *callId);
566     else
567         message->setValue("id", InspectorValue::null());
568     if (m_inspectorFrontendChannel)
569         m_inspectorFrontendChannel->sendMessageToFrontend(message->toJSONString());
570 }
571 EOF
572     return split("\n", $reportProtocolError);
573 }
574
575 sub generateArgumentGetters
576 {
577     my $type = shift;
578     my $json = typeTraits($type, "JSONType");
579     my $variable = typeTraits($type, "variable");
580     my $defaultValue = typeTraits($type, "defaultValue");
581     my $return  = typeTraits($type, "return") ? typeTraits($type, "return") : typeTraits($type, "param");
582
583     my $typeString = camelCase($type);
584     push(@backendConstantDeclarations, "    static $return get$typeString(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors);");
585     my $getterBody = << "EOF";
586
587 $return InspectorBackendDispatcher::get$typeString(InspectorObject* object, const String& name, bool* valueFound, InspectorArray* protocolErrors)
588 {
589     ASSERT(protocolErrors);
590
591     if (valueFound)
592         *valueFound = false;
593
594     $variable value = $defaultValue;
595
596     if (!object) {
597         if (!valueFound) {
598             // Required parameter in missing params container.
599             protocolErrors->pushString(String::format("'params' object must contain required parameter '\%s' with type '$json'.", name.utf8().data()));
600         }
601         return value;
602     }
603
604     InspectorObject::const_iterator end = object->end();
605     InspectorObject::const_iterator valueIterator = object->find(name);
606
607     if (valueIterator == end) {
608         if (!valueFound)
609             protocolErrors->pushString(String::format("Parameter '\%s' with type '$json' was not found.", name.utf8().data()));
610         return value;
611     }
612
613     if (!valueIterator->second->as$json(&value))
614         protocolErrors->pushString(String::format("Parameter '\%s' has wrong type. It must be '$json'.", name.utf8().data()));
615     else
616         if (valueFound)
617             *valueFound = true;
618     return value;
619 }
620 EOF
621
622     return split("\n", $getterBody);
623 }
624
625 sub generateBackendDispatcher
626 {
627     my @body;
628     my @mapEntries = map("        &${backendClassName}::$_,", map ($backendMethodSignatures{$_}, @backendMethods));
629     my $mapEntries = join("\n", @mapEntries);
630
631     my $backendDispatcherBody = << "EOF";
632 void ${backendClassName}::dispatch(const String& message)
633 {
634     RefPtr<${backendClassName}> protect = this;
635     typedef void (${backendClassName}::*CallHandler)(long callId, InspectorObject* messageObject);
636     typedef HashMap<String, CallHandler> DispatchMap;
637     DEFINE_STATIC_LOCAL(DispatchMap, dispatchMap, );
638     long callId = 0;
639
640     if (dispatchMap.isEmpty()) {
641         static CallHandler handlers[] = {
642 $mapEntries
643         };
644         size_t length = sizeof(commandNames) / sizeof(commandNames[0]);
645         for (size_t i = 0; i < length; ++i)
646             dispatchMap.add(commandNames[i], handlers[i]);
647     }
648
649     RefPtr<InspectorValue> parsedMessage = InspectorValue::parseJSON(message);
650     if (!parsedMessage) {
651         reportProtocolError(0, ParseError, "Message must be in JSON format");
652         return;
653     }
654
655     RefPtr<InspectorObject> messageObject = parsedMessage->asObject();
656     if (!messageObject) {
657         reportProtocolError(0, InvalidRequest, "Message must be a JSONified object");
658         return;
659     }
660
661     RefPtr<InspectorValue> callIdValue = messageObject->get("id");
662     if (!callIdValue) {
663         reportProtocolError(0, InvalidRequest, "'id' property was not found");
664         return;
665     }
666
667     if (!callIdValue->asNumber(&callId)) {
668         reportProtocolError(0, InvalidRequest, "The type of 'id' property must be number");
669         return;
670     }
671
672     RefPtr<InspectorValue> methodValue = messageObject->get("method");
673     if (!methodValue) {
674         reportProtocolError(&callId, InvalidRequest, "'method' property wasn't found");
675         return;
676     }
677
678     String method;
679     if (!methodValue->asString(&method)) {
680         reportProtocolError(&callId, InvalidRequest, "The type of 'method' property must be string");
681         return;
682     }
683
684     HashMap<String, CallHandler>::iterator it = dispatchMap.find(method);
685     if (it == dispatchMap.end()) {
686         reportProtocolError(&callId, MethodNotFound, "'" + method + "' wasn't found");
687         return;
688     }
689
690     ((*this).*it->second)(callId, messageObject.get());
691 }
692 EOF
693     return split("\n", $backendDispatcherBody);
694 }
695
696 sub generateBackendMessageParser
697 {
698     my $messageParserBody = << "EOF";
699 bool ${backendClassName}::getCommandName(const String& message, String* result)
700 {
701     RefPtr<InspectorValue> value = InspectorValue::parseJSON(message);
702     if (!value)
703         return false;
704
705     RefPtr<InspectorObject> object = value->asObject();
706     if (!object)
707         return false;
708
709     if (!object->getString("method", result))
710         return false;
711
712     return true;
713 }
714 EOF
715     return split("\n", $messageParserBody);
716 }
717
718 sub collectBackendJSStubFunctions
719 {
720     my $interface = shift;
721     my @functions = grep(!$_->signature->extendedAttributes->{"event"}, @{$interface->functions});
722     my $domain = $interface->name;
723
724     foreach my $function (@functions) {
725         my $name = $function->signature->name;
726         my @inArgs = grep($_->direction eq "in", @{$function->parameters});
727         my $argumentNames = join(
728             ",",
729             map("\"" . $_->name . "\": {"
730                 . "\"optional\": " . ($_->extendedAttributes->{"optional"} ? "true " : "false") . ", "
731                 . "\"type\": \"" . typeTraits($_->type, "JSType") . "\""
732                 . "}",
733                  @inArgs));
734         push(@backendJSStubs, "    this._registerDelegate('{" .
735             "\"method\": \"$domain.$name\", " .
736             (scalar(@inArgs) ? "\"params\": {$argumentNames}, " : "") .
737             "\"id\": 0" .
738         "}');");
739     }
740 }
741
742 sub collectBackendJSStubEvents
743 {
744     my $interface = shift;
745     my @functions = grep($_->signature->extendedAttributes->{"event"}, @{$interface->functions});
746     my $domain = $interface->name;
747
748     foreach my $function (@functions) {
749         my $name = $domain . "." . $function->signature->name;
750         my @outArgs = grep($_->direction eq "out", @{$function->parameters});
751         my $argumentNames = join(",", map("\"" . $_->name . "\"" , @outArgs));
752         push(@backendJSEvents, "    this._eventArgs[\"" . $name . "\"] = [" . $argumentNames ."];");
753     }
754 }
755
756 sub generateBackendStubJS
757 {
758     my $JSStubs = join("\n", @backendJSStubs);
759     my $JSEvents = join("\n", @backendJSEvents);
760     my $inspectorBackendStubJS = << "EOF";
761 $licenseTemplate
762
763 InspectorBackendStub = function()
764 {
765     this._lastCallbackId = 1;
766     this._pendingResponsesCount = 0;
767     this._callbacks = {};
768     this._domainDispatchers = {};
769     this._eventArgs = {};
770 $JSStubs
771 $JSEvents
772 }
773
774 InspectorBackendStub.prototype = {
775     _wrap: function(callback)
776     {
777         var callbackId = this._lastCallbackId++;
778         this._callbacks[callbackId] = callback || function() {};
779         return callbackId;
780     },
781
782     _registerDelegate: function(requestString)
783     {
784         var domainAndFunction = JSON.parse(requestString).method.split(".");
785         var agentName = domainAndFunction[0] + "Agent";
786         if (!window[agentName])
787             window[agentName] = {};
788         window[agentName][domainAndFunction[1]] = this.sendMessageToBackend.bind(this, requestString);
789         window[agentName][domainAndFunction[1]]["invoke"] = this._invoke.bind(this, requestString)
790     },
791
792     _invoke: function(requestString, args, callback)
793     {
794         var request = JSON.parse(requestString);
795         request.params = args;
796         this.sendMessageObjectToBackend(request, callback);
797     },
798
799     sendMessageToBackend: function()
800     {
801         var args = Array.prototype.slice.call(arguments);
802         var request = JSON.parse(args.shift());
803         var callback = (args.length && typeof args[args.length - 1] === "function") ? args.pop() : 0;
804         var domainAndMethod = request.method.split(".");
805         var agentMethod = domainAndMethod[0] + "Agent." + domainAndMethod[1];
806
807         var hasParams = false;
808         if (request.params) {
809             for (var key in request.params) {
810                 var typeName = request.params[key].type;
811                 var optionalFlag = request.params[key].optional;
812
813                 if (args.length === 0 && !optionalFlag) {
814                     console.error("Protocol Error: Invalid number of arguments for method '" + agentMethod + "' call. It must have the next arguments '" + JSON.stringify(request.params) + "'.");
815                     return;
816                 }
817
818                 var value = args.shift();
819                 if (optionalFlag && typeof value === "undefined") {
820                     delete request.params[key];
821                     continue;
822                 }
823
824                 if (typeof value !== typeName) {
825                     console.error("Protocol Error: Invalid type of argument '" + key + "' for method '" + agentMethod + "' call. It must be '" + typeName + "' but it is '" + typeof value + "'.");
826                     return;
827                 }
828
829                 request.params[key] = value;
830                 hasParams = true;
831             }
832             if (!hasParams)
833                 delete request.params;
834         }
835
836         if (args.length === 1 && !callback) {
837             if (typeof args[0] !== "undefined") {
838                 console.error("Protocol Error: Optional callback argument for method '" + agentMethod + "' call must be a function but its type is '" + typeof args[0] + "'.");
839                 return;
840             }
841         }
842
843         this.sendMessageObjectToBackend(request, callback);
844     },
845
846     sendMessageObjectToBackend: function(messageObject, callback)
847     {
848         messageObject.id = this._wrap(callback || function() {});
849         var message = JSON.stringify(messageObject);
850         if (window.dumpInspectorProtocolMessages)
851             console.log("frontend: " + message);
852
853         ++this._pendingResponsesCount;
854         InspectorFrontendHost.sendMessageToBackend(message);
855     },
856
857     registerDomainDispatcher: function(domain, dispatcher)
858     {
859         this._domainDispatchers[domain] = dispatcher;
860     },
861
862     dispatch: function(message)
863     {
864         if (window.dumpInspectorProtocolMessages)
865             console.log("backend: " + ((typeof message === "string") ? message : JSON.stringify(message)));
866
867         var messageObject = (typeof message === "string") ? JSON.parse(message) : message;
868
869         if ("id" in messageObject) { // just a response for some request
870             if (messageObject.error) {
871                 messageObject.error.__proto__ = {
872                     getDescription: function()
873                     {
874                         switch(this.code) {
875                             case -32700: return "Parse error";
876                             case -32600: return "Invalid Request";
877                             case -32601: return "Method not found";
878                             case -32602: return "Invalid params";
879                             case -32603: return "Internal error";;
880                             case -32000: return "Server error";
881                         }
882                     },
883
884                     toString: function()
885                     {
886                         var description ="Unknown error code";
887                         return this.getDescription() + "(" + this.code + "): " + this.message + "." + (this.data ? " " + this.data.join(" ") : "");
888                     },
889
890                     getMessage: function()
891                     {
892                         return this.message;
893                     }
894                 }
895
896                 if (messageObject.error.code !== -32000)
897                     this.reportProtocolError(messageObject);
898             }
899
900             var arguments = [];
901             if (messageObject.result) {
902                 for (var key in messageObject.result)
903                     arguments.push(messageObject.result[key]);
904             }
905
906             var callback = this._callbacks[messageObject.id];
907             if (callback) {
908                 arguments.unshift(messageObject.error);
909                 callback.apply(null, arguments);
910                 --this._pendingResponsesCount;
911                 delete this._callbacks[messageObject.id];
912             }
913
914             if (this._scripts && !this._pendingResponsesCount)
915                 this.runAfterPendingDispatches();
916
917             return;
918         } else {
919             var method = messageObject.method.split(".");
920             var domainName = method[0];
921             var functionName = method[1];
922             if (!(domainName in this._domainDispatchers)) {
923                 console.error("Protocol Error: the message is for non-existing domain '" + domainName + "'");
924                 return;
925             }
926             var dispatcher = this._domainDispatchers[domainName];
927             if (!(functionName in dispatcher)) {
928                 console.error("Protocol Error: Attempted to dispatch an unimplemented method '" + messageObject.method + "'");
929                 return;
930             }
931
932             if (!this._eventArgs[messageObject.method]) {
933                 console.error("Protocol Error: Attempted to dispatch an unspecified method '" + messageObject.method + "'");
934                 return;
935             }
936
937             var params = [];
938             if (messageObject.params) {
939                 var paramNames = this._eventArgs[messageObject.method];
940                 for (var i = 0; i < paramNames.length; ++i)
941                     params.push(messageObject.params[paramNames[i]]);
942             }
943
944             dispatcher[functionName].apply(dispatcher, params);
945         }
946     },
947
948     reportProtocolError: function(messageObject)
949     {
950         console.error("Request with id = " + messageObject.id + " failed. " + messageObject.error);
951     },
952
953     runAfterPendingDispatches: function(script)
954     {
955         if (!this._scripts)
956             this._scripts = [];
957
958         if (script)
959             this._scripts.push(script);
960
961         if (!this._pendingResponsesCount) {
962             var scripts = this._scripts;
963             this._scripts = []
964             for (var id = 0; id < scripts.length; ++id)
965                  scripts[id].call(this);
966         }
967     }
968 }
969
970 InspectorBackend = new InspectorBackendStub();
971
972 EOF
973     return split("\n", $inspectorBackendStubJS);
974 }
975
976 sub generateHeader
977 {
978     my $className = shift;
979     my $classDeclaration = shift;
980     my $types = shift;
981     my $constructor = shift;
982     my $constants = shift;
983     my $methods = shift;
984     my $footer = shift;
985
986     my $forwardHeaders = join("\n", sort(map("#include <" . typeTraits($_, "forwardHeader") . ">", grep(typeTraits($_, "forwardHeader"), keys %{$types}))));
987     my $forwardDeclarations = join("\n", sort(map("class " . typeTraits($_, "forward") . ";", grep(typeTraits($_, "forward"), keys %{$types}))));
988     my $constantDeclarations = join("\n", @{$constants});
989     my $methodsDeclarations = join("\n", @{$methods});
990
991     my $headerBody = << "EOF";
992 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
993 // Use of this source code is governed by a BSD-style license that can be
994 // found in the LICENSE file.
995 #ifndef ${className}_h
996 #define ${className}_h
997
998 ${forwardHeaders}
999
1000 namespace $namespace {
1001
1002 $forwardDeclarations
1003
1004 typedef String ErrorString;
1005
1006 class $classDeclaration {
1007 public:
1008 $constructor
1009
1010 $constantDeclarations
1011 $methodsDeclarations
1012
1013 $footer
1014 };
1015
1016 } // namespace $namespace
1017 #endif // !defined(${className}_h)
1018
1019 EOF
1020     return $headerBody;
1021 }
1022
1023 sub generateSource
1024 {
1025     my $className = shift;
1026     my $types = shift;
1027     my $constants = shift;
1028     my $methods = shift;
1029
1030     my @sourceContent = split("\r", $licenseTemplate);
1031     push(@sourceContent, "\n#include \"config.h\"");
1032     push(@sourceContent, "#include \"$className.h\"");
1033     push(@sourceContent, "#include <wtf/text/WTFString.h>");
1034     push(@sourceContent, "#include <wtf/text/CString.h>");
1035     push(@sourceContent, "");
1036     push(@sourceContent, "#if ENABLE(INSPECTOR)");
1037     push(@sourceContent, "");
1038
1039     my %headers;
1040     foreach my $type (keys %{$types}) {
1041         $headers{"#include \"" . typeTraits($type, "header") . "\""} = 1 if !typeTraits($type, "header") eq  "";
1042     }
1043     push(@sourceContent, sort keys %headers);
1044     push(@sourceContent, "");
1045     push(@sourceContent, "namespace $namespace {");
1046     push(@sourceContent, "");
1047     push(@sourceContent, join("\n", @{$constants}));
1048     push(@sourceContent, "");
1049     push(@sourceContent, @{$methods});
1050     push(@sourceContent, "");
1051     push(@sourceContent, "} // namespace $namespace");
1052     push(@sourceContent, "");
1053     push(@sourceContent, "#endif // ENABLE(INSPECTOR)");
1054     push(@sourceContent, "");
1055     return @sourceContent;
1056 }
1057
1058 sub typeTraits
1059 {
1060     my $type = shift;
1061     my $trait = shift;
1062     return $typeTransform{$type}->{$trait};
1063 }
1064
1065 sub generateBackendAgentFieldsAndConstructor
1066 {
1067     my @arguments;
1068     my @fieldInitializers;
1069
1070     push(@arguments, "InspectorFrontendChannel* inspectorFrontendChannel");
1071     push(@fieldInitializers, "        : m_inspectorFrontendChannel(inspectorFrontendChannel)");
1072     push(@backendFooter, "    InspectorFrontendChannel* m_inspectorFrontendChannel;");
1073
1074     foreach my $domain (sort keys %backendDomains) {
1075         # Add agent field declaration to the footer.
1076         my $agentClassName = typeTraits($domain, "forward");
1077         my $field = typeTraits($domain, "domainAccessor");
1078         push(@backendFooter, "    ${agentClassName}* ${field};");
1079
1080         # Add agent parameter and initializer.
1081         my $arg = substr($field, 2);
1082         push(@fieldInitializers, "        , ${field}(${arg})");
1083         push(@arguments, "${agentClassName}* ${arg}");
1084     }
1085
1086     my $argumentString = join(", ", @arguments);
1087
1088     my @backendHead;
1089     push(@backendHead, "    ${backendClassName}(${argumentString})");
1090     push(@backendHead, @fieldInitializers);
1091     push(@backendHead, "    { }");
1092     push(@backendHead, "");
1093     push(@backendHead, "    void clearFrontend() { m_inspectorFrontendChannel = 0; }");
1094     push(@backendHead, "");
1095     push(@backendHead, "    enum CommonErrorCode {");
1096     push(@backendHead, "        ParseError = 0,");
1097     push(@backendHead, "        InvalidRequest,");
1098     push(@backendHead, "        MethodNotFound,");
1099     push(@backendHead, "        InvalidParams,");
1100     push(@backendHead, "        InternalError,");
1101     push(@backendHead, "        ServerError,");
1102     push(@backendHead, "        LastEntry,");
1103     push(@backendHead, "    };");
1104     push(@backendHead, "");
1105     push(@backendHead, "    void reportProtocolError(const long* const callId, CommonErrorCode, const String& errorMessage) const;");
1106     push(@backendHead, "    void reportProtocolError(const long* const callId, CommonErrorCode, const String& errorMessage, PassRefPtr<InspectorArray> data) const;");
1107     push(@backendHead, "    void dispatch(const String& message);");
1108     push(@backendHead, "    static bool getCommandName(const String& message, String* result);");
1109     push(@backendHead, "");
1110     push(@backendHead, "    enum MethodNames {");
1111     $backendConstructor = join("\n", @backendHead);
1112 }
1113
1114 sub finish
1115 {
1116     my $object = shift;
1117
1118     push(@backendMethodsImpl, generateBackendDispatcher());
1119     push(@backendMethodsImpl, generateBackendSendResponse());
1120     push(@backendMethodsImpl, generateBackendReportProtocolError());
1121     unshift(@frontendMethodsImpl, generateFrontendConstructorImpl(), "");
1122
1123     open(my $SOURCE, ">$outputDir/$frontendClassName.cpp") || die "Couldn't open file $outputDir/$frontendClassName.cpp";
1124     print $SOURCE join("\n", generateSource($frontendClassName, \%frontendTypes, \@frontendConstantDefinitions, \@frontendMethodsImpl));
1125     close($SOURCE);
1126     undef($SOURCE);
1127
1128     open(my $HEADER, ">$outputHeadersDir/$frontendClassName.h") || die "Couldn't open file $outputHeadersDir/$frontendClassName.h";
1129     print $HEADER generateHeader($frontendClassName, $frontendClassName, \%frontendTypes, $frontendConstructor, \@frontendConstantDeclarations, \@frontendMethods, join("\n", @frontendFooter));
1130     close($HEADER);
1131     undef($HEADER);
1132
1133     unshift(@backendConstantDefinitions, "const char* InspectorBackendDispatcher::commandNames[] = {");
1134     push(@backendConstantDefinitions, "};");
1135     push(@backendConstantDefinitions, "");
1136
1137     # Make dispatcher methods private on the backend.
1138     push(@backendConstantDeclarations, "};");
1139     push(@backendConstantDeclarations, "");
1140     push(@backendConstantDeclarations, "    static const char* commandNames[];");    
1141     push(@backendConstantDeclarations, "");
1142     push(@backendConstantDeclarations, "private:");
1143
1144     foreach my $type (keys %backendTypes) {
1145         if (typeTraits($type, "JSONType")) {
1146             push(@backendMethodsImpl, generateArgumentGetters($type));
1147         }
1148     }
1149
1150     push(@backendConstantDeclarations, "    void sendResponse(long callId, PassRefPtr<InspectorObject> result, const String& errorMessage, PassRefPtr<InspectorArray> protocolErrors, ErrorString invocationError);");
1151
1152     generateBackendAgentFieldsAndConstructor();
1153
1154     push(@backendMethodsImpl, generateBackendMessageParser());
1155     push(@backendMethodsImpl, "");
1156
1157     push(@backendConstantDeclarations, "");
1158
1159     open($SOURCE, ">$outputDir/$backendClassName.cpp") || die "Couldn't open file $outputDir/$backendClassName.cpp";
1160     print $SOURCE join("\n", generateSource($backendClassName, \%backendTypes, \@backendConstantDefinitions, \@backendMethodsImpl));
1161     close($SOURCE);
1162     undef($SOURCE);
1163
1164     open($HEADER, ">$outputHeadersDir/$backendClassName.h") || die "Couldn't open file $outputHeadersDir/$backendClassName.h";
1165     print $HEADER join("\n", generateHeader($backendClassName, $backendClassDeclaration, \%backendTypes, $backendConstructor, \@backendConstantDeclarations, \@backendMethods, join("\n", @backendFooter)));
1166     close($HEADER);
1167     undef($HEADER);
1168
1169     open(my $JS_STUB, ">$outputDir/$backendJSStubName.js") || die "Couldn't open file $outputDir/$backendJSStubName.js";
1170     print $JS_STUB join("\n", generateBackendStubJS());
1171     close($JS_STUB);
1172     undef($JS_STUB);
1173 }
1174
1175 1;