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