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.
5 package CodeGeneratorInspector;
13 $typeTransform{"InspectorClient"} = {
14 "forward" => "InspectorClient",
15 "header" => "InspectorClient.h",
17 $typeTransform{"Backend"} = {
18 "forward" => "InspectorBackend",
19 "header" => "InspectorBackend.h",
20 "domainAccessor" => "m_inspectorController->inspectorBackend()",
22 $typeTransform{"Controller"} = {
23 "forwardHeader" => "InspectorController.h",
24 "domainAccessor" => "m_inspectorController",
26 $typeTransform{"Debug"} = {
27 "forward" => "InspectorDebuggerAgent",
28 "header" => "InspectorDebuggerAgent.h",
29 "domainAccessor" => "m_inspectorController->debuggerAgent()",
31 $typeTransform{"Resource"} = {
32 "forward" => "InspectorResourceAgent",
33 "header" => "InspectorResourceAgent.h",
34 "domainAccessor" => "m_inspectorController->m_resourceAgent",
36 $typeTransform{"DOM"} = {
37 "forward" => "InspectorDOMAgent",
38 "header" => "InspectorDOMAgent.h",
39 "domainAccessor" => "m_inspectorController->domAgent()",
41 $typeTransform{"CSS"} = {
42 "forward" => "InspectorCSSAgent",
43 "header" => "InspectorCSSAgent.h",
44 "domainAccessor" => "m_inspectorController->cssAgent()",
46 $typeTransform{"ApplicationCache"} = {
47 "forward" => "InspectorApplicationCacheAgent",
48 "header" => "InspectorApplicationCacheAgent.h",
49 "domainAccessor" => "m_inspectorController->applicationCacheAgent()",
51 $typeTransform{"FileSystem"} = {
52 "forward" => "InspectorFileSystemAgent",
53 "header" => "InspectorFileSystemAgent.h",
54 "domainAccessor" => "m_inspectorController->fileSystemAgent()",
56 $typeTransform{"Profiler"} = {
57 "forward" => "InspectorProfilerAgent",
58 "header" => "InspectorProfilerAgent.h",
59 "domainAccessor" => "m_inspectorController->profilerAgent()",
61 $typeTransform{"Frontend"} = {
62 "forward" => "InspectorFrontend",
63 "header" => "InspectorFrontend.h",
65 $typeTransform{"PassRefPtr"} = {
66 "forwardHeader" => "wtf/PassRefPtr.h",
68 $typeTransform{"Object"} = {
69 "param" => "PassRefPtr<InspectorObject>",
70 "variable" => "RefPtr<InspectorObject>",
71 "defaultValue" => "InspectorObject::create()",
72 "forward" => "InspectorObject",
73 "header" => "InspectorValues.h",
74 "JSONType" => "Object"
76 $typeTransform{"Array"} = {
77 "param" => "PassRefPtr<InspectorArray>",
78 "variable" => "RefPtr<InspectorArray>",
79 "defaultValue" => "InspectorArray::create()",
80 "forward" => "InspectorArray",
81 "header" => "InspectorValues.h",
84 $typeTransform{"Value"} = {
85 "param" => "PassRefPtr<InspectorValue>",
86 "variable" => "RefPtr<InspectorValue>",
87 "defaultValue" => "InspectorValue::null()",
88 "forward" => "InspectorValue",
89 "header" => "InspectorValues.h",
92 $typeTransform{"String"} = {
93 "param" => "const String&",
94 "variable" => "String",
96 "defaultValue" => "\"\"",
97 "forwardHeader" => "wtf/Forward.h",
98 "header" => "PlatformString.h",
99 "JSONType" => "String"
101 $typeTransform{"long"} = {
103 "variable" => "long",
104 "defaultValue" => "0",
107 "JSONType" => "Number"
109 $typeTransform{"int"} = {
112 "defaultValue" => "0",
115 "JSONType" => "Number",
117 $typeTransform{"unsigned long"} = {
118 "param" => "unsigned long",
119 "variable" => "unsigned long",
120 "defaultValue" => "0u",
123 "JSONType" => "Number"
125 $typeTransform{"unsigned int"} = {
126 "param" => "unsigned int",
127 "variable" => "unsigned int",
128 "defaultValue" => "0u",
131 "JSONType" => "Number"
133 $typeTransform{"double"} = {
135 "variable" => "double",
136 "defaultValue" => "0.0",
139 "JSONType" => "Number"
141 $typeTransform{"boolean"} = {
144 "defaultValue" => "false",
147 "JSONType" => "Boolean"
149 $typeTransform{"void"} = {
154 # Default License Templates
156 my $licenseTemplate = << "EOF";
157 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
158 // Use of this source code is governed by a BSD-style license that can be
159 // found in the LICENSE file.
164 my $outputHeadersDir;
165 my $writeDependencies;
170 my $backendClassName;
171 my $backendJSStubName;
174 my @backendMethodsImpl;
175 my $backendConstructor;
176 my @backendConstantDeclarations;
177 my @backendConstantDefinitions;
181 my $frontendClassName;
184 my @frontendMethodsImpl;
185 my $frontendConstructor;
186 my @frontendConstantDeclarations;
187 my @frontendConstantDefinitions;
190 # Default constructor
196 $codeGenerator = shift;
198 $outputHeadersDir = shift;
199 shift; # $useLayerOnTop
200 shift; # $preprocessor
201 $writeDependencies = shift;
204 bless($reference, $object);
208 # Params: 'idlDocument' struct
212 my $dataNode = shift;
214 $namespace = $dataNode->module;
215 $namespace =~ s/core/WebCore/;
218 # Params: 'idlDocument' struct
219 sub GenerateInterface
222 my $interface = shift;
225 my $className = $interface->name;
227 $frontendClassName = $className . "Frontend";
228 $frontendConstructor = " ${frontendClassName}(InspectorClient* inspectorClient) : m_inspectorClient(inspectorClient) { }";
229 $frontendFooter = " InspectorClient* m_inspectorClient;";
230 $frontendTypes{"String"} = 1;
231 $frontendTypes{"InspectorClient"} = 1;
232 $frontendTypes{"PassRefPtr"} = 1;
234 $backendClassName = $className . "BackendDispatcher";
235 $backendJSStubName = $className . "BackendStub";
237 push(@backendHead, " ${backendClassName}(InspectorController* inspectorController) : m_inspectorController(inspectorController) { }");
238 push(@backendHead, " void reportProtocolError(const long callId, const String& errorText) const;");
239 push(@backendHead, " void dispatch(const String& message);");
240 push(@backendHead, " static bool getCommandName(const String& message, String* result);");
241 $backendConstructor = join("\n", @backendHead);
242 $backendFooter = " InspectorController* m_inspectorController;";
243 $backendTypes{"Controller"} = 1;
244 $backendTypes{"InspectorClient"} = 1;
245 $backendTypes{"PassRefPtr"} = 1;
246 $backendTypes{"Object"} = 1;
248 push(@backendMethodsImpl, generateBackendMessageParser());
249 generateFunctions($interface);
251 # Make dispatcher methods private on the backend.
252 push(@backendConstantDeclarations, "");
253 push(@backendConstantDeclarations, "private:");
256 sub generateFunctions
258 my $interface = shift;
260 foreach my $function (@{$interface->functions}) {
261 if ($function->signature->extendedAttributes->{"notify"}) {
262 generateFrontendFunction($function);
264 generateBackendFunction($function);
267 push(@backendMethodsImpl, generateBackendDispatcher());
268 push(@backendMethodsImpl, generateBackendReportProtocolError());
270 foreach my $type (keys %backendTypes) {
271 if ($typeTransform{$type}{"JSONType"}) {
272 push(@backendMethodsImpl, generateArgumentGetters($type));
276 @backendStubJS = generateBackendStubJS($interface);
279 sub generateFrontendFunction
281 my $function = shift;
283 my $functionName = $function->signature->name;
285 my @argsFiltered = grep($_->direction eq "out", @{$function->parameters}); # just keep only out parameters for frontend interface.
286 map($frontendTypes{$_->type} = 1, @argsFiltered); # register required types.
287 my $arguments = join(", ", map($typeTransform{$_->type}->{"param"} . " " . $_->name, @argsFiltered)); # prepare arguments for function signature.
289 my $signature = " void ${functionName}(${arguments});";
290 if (!$frontendMethods{${signature}}) {
291 $frontendMethods{${signature}} = 1;
294 push(@function, "void ${frontendClassName}::${functionName}(${arguments})");
295 push(@function, "{");
296 push(@function, " RefPtr<InspectorObject> ${functionName}Message = InspectorObject::create();");
297 push(@function, " ${functionName}Message->setString(\"type\", \"event\");");
298 push(@function, " ${functionName}Message->setString(\"event\", \"$functionName\");");
299 push(@function, " RefPtr<InspectorObject> payloadDataObject = InspectorObject::create();");
300 my @pushArguments = map(" payloadDataObject->set" . $typeTransform{$_->type}->{"JSONType"} . "(\"" . $_->name . "\", " . $_->name . ");", @argsFiltered);
301 push(@function, @pushArguments);
302 push(@function, " ${functionName}Message->setObject(\"data\", payloadDataObject);");
303 push(@function, " m_inspectorClient->sendMessageToFrontend(${functionName}Message->toJSONString());");
305 push(@function, "}");
307 push(@frontendMethodsImpl, @function);
314 $value =~ s/\b(\w)/\U$1/g; # make a camel-case name for type name
319 sub generateBackendFunction
321 my $function = shift;
323 my $functionName = $function->signature->name;
325 push(@backendConstantDeclarations, " static const char* ${functionName}Cmd;");
326 push(@backendConstantDefinitions, "const char* ${backendClassName}::${functionName}Cmd = \"${functionName}\";");
328 map($backendTypes{$_->type} = 1, @{$function->parameters}); # register required types
329 my @inArgs = grep($_->direction eq "in" && !($_->name eq "callId") , @{$function->parameters});
330 my @outArgs = grep($_->direction eq "out", @{$function->parameters});
332 my $signature = " void ${functionName}(long callId, InspectorObject* requestMessageObject);";
333 !$backendMethods{${signature}} || die "Duplicate function was detected for signature '$signature'.";
334 $backendMethods{${signature}} = $functionName;
337 my $requestMessageObject = scalar(@inArgs) ? " requestMessageObject" : "";
338 push(@function, "void ${backendClassName}::${functionName}(long callId, InspectorObject*$requestMessageObject)");
339 push(@function, "{");
340 push(@function, " RefPtr<InspectorArray> protocolErrors = InspectorArray::create();");
343 my $domain = $function->signature->extendedAttributes->{"handler"} || "Controller";
344 my $domainAccessor = $typeTransform{$domain}->{"domainAccessor"};
345 $backendTypes{$domain} = 1;
346 push(@function, " if (!$domainAccessor)");
347 push(@function, " protocolErrors->pushString(\"Protocol Error: $domain handler is not available.\");");
350 # declare local variables for out arguments.
351 push(@function, map(" " . $typeTransform{$_->type}->{"variable"} . " " . $_->name . " = " . $typeTransform{$_->type}->{"defaultValue"} . ";", @outArgs));
354 if (scalar(@inArgs)) {
355 push(@function, " if (RefPtr<InspectorObject> argumentsContainer = requestMessageObject->getObject(\"arguments\")) {");
357 foreach my $parameter (@inArgs) {
358 my $name = $parameter->name;
359 my $type = $parameter->type;
360 my $typeString = camelCase($parameter->type);
361 push(@function, " " . $typeTransform{$type}->{"variable"} . " $name = get$typeString(argumentsContainer.get(), \"$name\", protocolErrors.get());");
367 my $args = join(", ", (map($_->name, @inArgs), map("&" . $_->name, @outArgs)));
368 push(@function, "$indent if (!protocolErrors->length())");
369 push(@function, "$indent $domainAccessor->$functionName($args);");
370 if (scalar(@inArgs)) {
371 push(@function, " } else {");
372 push(@function, " protocolErrors->pushString(\"Protocol Error: 'arguments' property with type 'object' was not found.\");");
373 push(@function, " }");
376 push(@function, " // use InspectorFrontend as a marker of WebInspector availability");
377 push(@function, " if ((callId || protocolErrors->length()) && m_inspectorController->hasFrontend()) {");
378 push(@function, " RefPtr<InspectorObject> responseMessage = InspectorObject::create();");
379 push(@function, " responseMessage->setNumber(\"seq\", callId);");
380 push(@function, " responseMessage->setBoolean(\"success\", !protocolErrors->length());");
382 push(@function, " if (protocolErrors->length())");
383 push(@function, " responseMessage->setArray(\"errors\", protocolErrors);");
384 if (scalar(@outArgs)) {
385 push(@function, " else {");
386 push(@function, " RefPtr<InspectorObject> responseData = InspectorObject::create();");
387 push(@function, map(" responseData->set" . $typeTransform{$_->type}->{"JSONType"} . "(\"" . $_->name . "\", " . $_->name . ");", @outArgs));
388 push(@function, " responseMessage->setObject(\"data\", responseData);");
389 push(@function, " }");
391 push(@function, " m_inspectorController->inspectorClient()->sendMessageToFrontend(responseMessage->toJSONString());");
392 push(@function, " }");
395 push(@function, "}");
397 push(@backendMethodsImpl, @function);
400 sub generateBackendReportProtocolError
402 my $reportProtocolError = << "EOF";
404 void ${backendClassName}::reportProtocolError(const long callId, const String& errorText) const
406 RefPtr<InspectorObject> message = InspectorObject::create();
407 message->setNumber("seq", callId);
408 message->setBoolean("success", false);
409 RefPtr<InspectorArray> errors = InspectorArray::create();
410 errors->pushString(errorText);
411 message->setArray("errors", errors);
412 m_inspectorController->inspectorClient()->sendMessageToFrontend(message->toJSONString());
415 return split("\n", $reportProtocolError);
418 sub generateArgumentGetters
421 my $json = $typeTransform{$type}{"JSONType"};
422 my $variable = $typeTransform{$type}{"variable"};
423 my $defaultValue = $typeTransform{$type}{"defaultValue"};
424 my $return = $typeTransform{$type}{"return"} ? $typeTransform{$type}{"return"} : $typeTransform{$type}{"param"};
426 my $typeString = camelCase($type);
427 push(@backendConstantDeclarations, "$return get$typeString(InspectorObject* object, const String& name, InspectorArray* protocolErrors);");
428 my $getterBody = << "EOF";
430 $return InspectorBackendDispatcher::get$typeString(InspectorObject* object, const String& name, InspectorArray* protocolErrors)
433 ASSERT(protocolErrors);
435 $variable value = $defaultValue;
436 InspectorObject::const_iterator end = object->end();
437 InspectorObject::const_iterator valueIterator = object->find(name);
439 if (valueIterator == end)
440 protocolErrors->pushString(String::format("Protocol Error: Argument '\%s' with type '$json' was not found.", name.utf8().data()));
442 if (!valueIterator->second->as$json(&value))
443 protocolErrors->pushString(String::format("Protocol Error: Argument '\%s' has wrong type. It should be '$json'.", name.utf8().data()));
449 return split("\n", $getterBody);
452 sub generateBackendDispatcher
455 my @methods = map($backendMethods{$_}, keys %backendMethods);
456 my @mapEntries = map(" dispatchMap.add(${_}Cmd, &${backendClassName}::$_);", @methods);
457 my $mapEntries = join("\n", @mapEntries);
459 my $backendDispatcherBody = << "EOF";
460 void ${backendClassName}::dispatch(const String& message)
462 typedef void (${backendClassName}::*CallHandler)(long callId, InspectorObject* messageObject);
463 typedef HashMap<String, CallHandler> DispatchMap;
464 DEFINE_STATIC_LOCAL(DispatchMap, dispatchMap, );
467 if (dispatchMap.isEmpty()) {
471 RefPtr<InspectorValue> parsedMessage = InspectorValue::parseJSON(message);
472 if (!parsedMessage) {
473 reportProtocolError(callId, "Protocol Error: Invalid message format. Message should be in JSON format.");
477 RefPtr<InspectorObject> messageObject = parsedMessage->asObject();
478 if (!messageObject) {
479 reportProtocolError(callId, "Protocol Error: Invalid message format. The message should be a JSONified object.");
483 RefPtr<InspectorValue> commandValue = messageObject->get("command");
485 reportProtocolError(callId, "Protocol Error: Invalid message format. 'command' property wasn't found.");
490 if (!commandValue->asString(&command)) {
491 reportProtocolError(callId, "Protocol Error: Invalid message format. The type of 'command' property should be string.");
495 RefPtr<InspectorValue> callIdValue = messageObject->get("seq");
497 reportProtocolError(callId, "Protocol Error: Invalid message format. 'seq' property was not found in the request.");
501 if (!callIdValue->asNumber(&callId)) {
502 reportProtocolError(callId, "Protocol Error: Invalid message format. The type of 'seq' property should be number.");
506 HashMap<String, CallHandler>::iterator it = dispatchMap.find(command);
507 if (it == dispatchMap.end()) {
508 reportProtocolError(callId, makeString("Protocol Error: Invalid command was received. '", command, "' wasn't found."));
512 ((*this).*it->second)(callId, messageObject.get());
515 return split("\n", $backendDispatcherBody);
518 sub generateBackendMessageParser
520 my $messageParserBody = << "EOF";
521 bool ${backendClassName}::getCommandName(const String& message, String* result)
523 RefPtr<InspectorValue> value = InspectorValue::parseJSON(message);
527 RefPtr<InspectorObject> object = value->asObject();
531 RefPtr<InspectorValue> commandValue = object->get("command");
535 return commandValue->asString(result);
539 return split("\n", $messageParserBody);
542 sub generateBackendStubJS
544 my $interface = shift;
545 my @backendFunctions = grep(!$_->signature->extendedAttributes->{"notify"}, @{$interface->functions});
548 foreach my $function (@backendFunctions) {
549 my $name = $function->signature->name;
550 my $domain = $function->signature->extendedAttributes->{"handler"};
551 my $argumentNames = join(",", map("\"" . $_->name . "\": \"" . lc($typeTransform{$_->type}->{"JSONType"}) . "\"", grep($_->direction eq "in", @{$function->parameters})));
552 push(@JSStubs, " this._registerDelegate('{" .
554 "\"domain\": \"$domain\", " .
555 "\"command\": \"$name\", " .
556 "\"arguments\": {$argumentNames}" .
560 my $JSStubs = join("\n", @JSStubs);
561 my $inspectorBackendStubJS = << "EOF";
564 WebInspector.InspectorBackendStub = function()
569 WebInspector.InspectorBackendStub.prototype = {
570 _registerDelegate: function(commandInfo)
572 var commandObject = JSON.parse(commandInfo);
573 this[commandObject.command] = this.sendMessageToBackend.bind(this, commandInfo);
576 sendMessageToBackend: function()
578 var args = Array.prototype.slice.call(arguments);
579 var request = JSON.parse(args.shift());
581 for (var key in request.arguments) {
582 if (args.length === 0) {
583 console.error("Protocol Error: Invalid number of arguments for 'InspectorBackend.%s' call. It should have the next arguments '%s'.", request.command, JSON.stringify(request.arguments));
586 var value = args.shift();
587 if (typeof value !== request.arguments[key]) {
588 console.error("Protocol Error: Invalid type of argument '%s' for 'InspectorBackend.%s' call. It should be '%s' but it is '%s'.", key, request.command, request.arguments[key], typeof value);
591 request.arguments[key] = value;
594 if (args.length === 1) {
595 if (typeof args[0] !== "function" && typeof args[0] !== "undefined") {
596 console.error("Protocol Error: Optional callback argument for 'InspectorBackend.%s' call should be a function but its type is '%s'.", request.command, typeof args[0]);
599 request.seq = WebInspector.Callback.wrap(args[0]);
602 if (window.dumpInspectorProtocolMessages)
603 console.log("frontend: " + JSON.stringify(request));
605 var message = JSON.stringify(request);
606 InspectorFrontendHost.sendMessageToBackend(message);
610 InspectorBackend = new WebInspector.InspectorBackendStub();
613 return split("\n", $inspectorBackendStubJS);
618 my $className = shift;
620 my $constructor = shift;
621 my $constants = shift;
625 my $forwardHeaders = join("\n", sort(map("#include <" . $typeTransform{$_}->{"forwardHeader"} . ">", grep($typeTransform{$_}->{"forwardHeader"}, keys %{$types}))));
626 my $forwardDeclarations = join("\n", sort(map("class " . $typeTransform{$_}->{"forward"} . ";", grep($typeTransform{$_}->{"forward"}, keys %{$types}))));
627 my $constantDeclarations = join("\n", @{$constants});
628 my $methodsDeclarations = join("\n", keys %{$methods});
630 my $headerBody = << "EOF";
631 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
632 // Use of this source code is governed by a BSD-style license that can be
633 // found in the LICENSE file.
634 #ifndef ${className}_h
635 #define ${className}_h
639 namespace $namespace {
647 $constantDeclarations
654 } // namespace $namespace
655 #endif // !defined(${className}_h)
663 my $className = shift;
665 my $constants = shift;
668 my @sourceContent = split("\r", $licenseTemplate);
669 push(@sourceContent, "\n#include \"config.h\"");
670 push(@sourceContent, "#include \"$className.h\"");
671 push(@sourceContent, "#include <wtf/text/StringConcatenate.h>");
672 push(@sourceContent, "#include <wtf/text/CString.h>");
673 push(@sourceContent, "");
674 push(@sourceContent, "#if ENABLE(INSPECTOR)");
675 push(@sourceContent, "");
678 foreach my $type (keys %{$types}) {
679 $headers{"#include \"" . $typeTransform{$type}->{"header"} . "\""} = 1 if !$typeTransform{$type}->{"header"} eq "";
681 push(@sourceContent, sort keys %headers);
682 push(@sourceContent, "");
683 push(@sourceContent, "namespace $namespace {");
684 push(@sourceContent, "");
685 push (@sourceContent, join("\n", @{$constants}));
686 push(@sourceContent, "");
687 push(@sourceContent, @{$methods});
688 push(@sourceContent, "");
689 push(@sourceContent, "} // namespace $namespace");
690 push(@sourceContent, "");
691 push(@sourceContent, "#endif // ENABLE(INSPECTOR)");
692 push(@sourceContent, "");
693 return @sourceContent;
700 open(my $SOURCE, ">$outputDir/$frontendClassName.cpp") || die "Couldn't open file $outputDir/$frontendClassName.cpp";
701 print $SOURCE join("\n", generateSource($frontendClassName, \%frontendTypes, \@frontendConstantDefinitions, \@frontendMethodsImpl));
705 open(my $HEADER, ">$outputHeadersDir/$frontendClassName.h") || die "Couldn't open file $outputHeadersDir/$frontendClassName.h";
706 print $HEADER generateHeader($frontendClassName, \%frontendTypes, $frontendConstructor, \@frontendConstantDeclarations, \%frontendMethods, $frontendFooter);
710 open($SOURCE, ">$outputDir/$backendClassName.cpp") || die "Couldn't open file $outputDir/$backendClassName.cpp";
711 print $SOURCE join("\n", generateSource($backendClassName, \%backendTypes, \@backendConstantDefinitions, \@backendMethodsImpl));
715 open($HEADER, ">$outputHeadersDir/$backendClassName.h") || die "Couldn't open file $outputHeadersDir/$backendClassName.h";
716 print $HEADER join("\n", generateHeader($backendClassName, \%backendTypes, $backendConstructor, \@backendConstantDeclarations, \%backendMethods, $backendFooter));
720 open(my $JS_STUB, ">$outputDir/$backendJSStubName.js") || die "Couldn't open file $outputDir/$backendJSStubName.js";
721 print $JS_STUB join("\n", @backendStubJS);