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{"InspectorBackend"} = {
18 "forward" => "InspectorBackend",
19 "header" => "InspectorBackend.h",
21 $typeTransform{"PassRefPtr"} = {
22 "forwardHeader" => "wtf/PassRefPtr.h",
24 $typeTransform{"Object"} = {
25 "param" => "PassRefPtr<InspectorObject>",
26 "retVal" => "PassRefPtr<InspectorObject>",
27 "forward" => "InspectorObject",
28 "header" => "InspectorValues.h",
29 "accessorSuffix" => ""
31 $typeTransform{"Array"} = {
32 "param" => "PassRefPtr<InspectorArray>",
33 "retVal" => "PassRefPtr<InspectorArray>",
34 "forward" => "InspectorArray",
35 "header" => "InspectorValues.h",
36 "accessorSuffix" => ""
38 $typeTransform{"Value"} = {
39 "param" => "PassRefPtr<InspectorValue>",
40 "retVal" => "PassRefPtr<InspectorValue>",
41 "forward" => "InspectorValue",
42 "header" => "InspectorValues.h",
43 "accessorSuffix" => ""
45 $typeTransform{"String"} = {
46 "param" => "const String&",
48 "forward" => "String",
49 "header" => "PlatformString.h",
50 "accessorSuffix" => "String"
52 $typeTransform{"long"} = {
57 "accessorSuffix" => "Number"
59 $typeTransform{"int"} = {
64 "accessorSuffix" => "Number",
66 $typeTransform{"unsigned long"} = {
67 "param" => "unsigned long",
68 "retVal" => "unsigned long",
71 "accessorSuffix" => "Number"
73 $typeTransform{"boolean"} = {
78 "accessorSuffix" => "Bool"
80 $typeTransform{"void"} = {
86 # Default License Templates
88 my $licenseTemplate = << "EOF";
89 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
90 // Use of this source code is governed by a BSD-style license that can be
91 // found in the LICENSE file.
97 my $writeDependencies;
102 my $backendClassName;
105 my @backendMethodsImpl;
106 my $backendConstructor;
109 my $frontendClassName;
112 my @frontendMethodsImpl;
113 my $frontendConstructor;
116 my $callId = new domSignature(); # it is just structure for describing parameters from IDLStructure.pm.
117 $callId->type("long");
118 $callId->name("callId");
120 # Default constructor
126 $codeGenerator = shift;
128 $outputHeadersDir = shift;
129 shift; # $useLayerOnTop
130 shift; # $preprocessor
131 $writeDependencies = shift;
134 bless($reference, $object);
138 # Params: 'idlDocument' struct
142 my $dataNode = shift;
144 $namespace = $dataNode->module;
145 $namespace =~ s/core/WebCore/;
148 # Params: 'idlDocument' struct
149 sub GenerateInterface
152 my $interface = shift;
155 my $className = $interface->name;
157 $frontendClassName = "Remote" . $className . "Frontend";
158 $frontendConstructor = " ${frontendClassName}(InspectorClient* inspectorClient) : m_inspectorClient(inspectorClient) { }";
159 $frontendFooter = " InspectorClient* m_inspectorClient;";
160 $frontendTypes{"String"} = 1;
161 $frontendTypes{"InspectorClient"} = 1;
162 $frontendTypes{"PassRefPtr"} = 1;
164 $backendClassName = $className . "BackendDispatcher";
166 push(@backendHead, " ${backendClassName}(InspectorBackend* inspectorBackend) : m_inspectorBackend(inspectorBackend) { }");
167 push(@backendHead, " void dispatch(const String& message, String* exception);");
168 push(@backendHead, "private:");
169 $backendConstructor = join("\n", @backendHead);
170 $backendFooter = " InspectorBackend* m_inspectorBackend;";
171 $backendTypes{"InspectorBackend"} = 1;
172 $backendTypes{"PassRefPtr"} = 1;
173 $backendTypes{"Array"} = 1;
175 generateBackendPrivateFunctions();
176 generateFunctions($interface);
179 sub generateFunctions
181 my $interface = shift;
183 foreach my $function (@{$interface->functions}) {
184 generateFrontendFunction($function);
185 generateBackendFunction($function);
187 push(@backendMethodsImpl, generateBackendDispatcher());
190 sub generateFrontendFunction
192 my $function = shift;
195 my $notify = $function->signature->extendedAttributes->{"notify"};
197 $functionName = $function->signature->name;
199 my $customResponse = $function->signature->extendedAttributes->{"customResponse"};
200 $functionName = $customResponse ? $customResponse : "did" . ucfirst($function->signature->name);
203 my @argsFiltered = grep($_->direction eq "out", @{$function->parameters}); # just keep only out parameters for frontend interface.
204 unshift(@argsFiltered, $callId) if !$notify; # Add callId as the first argument for all frontend did* methods.
205 map($frontendTypes{$_->type} = 1, @argsFiltered); # register required types.
206 my $arguments = join(", ", map($typeTransform{$_->type}->{"param"} . " " . $_->name, @argsFiltered)); # prepare arguments for function signature.
207 my @pushArguments = map(" arguments->push" . $typeTransform{$_->type}->{"accessorSuffix"} . "(" . $_->name . ");", @argsFiltered);
209 my $signature = " void ${functionName}(${arguments});";
210 if (!$frontendMethods{${signature}}) {
211 $frontendMethods{${signature}} = 1;
214 push(@function, "void ${frontendClassName}::${functionName}(${arguments})");
215 push(@function, "{");
216 push(@function, " RefPtr<InspectorArray> arguments = InspectorArray::create();");
217 push(@function, " arguments->pushString(\"$functionName\");");
218 push(@function, @pushArguments);
219 push(@function, " m_inspectorClient->sendMessageToFrontend(arguments->toJSONString());");
220 push(@function, "}");
222 push(@frontendMethodsImpl, @function);
226 sub generateBackendPrivateFunctions
228 my $privateFunctions = << "EOF";
229 static String formatWrongArgumentsCountMessage(unsigned expected, unsigned actual)
231 return String::format(\"Wrong number of parameters: %d (expected: %d)\", actual, expected);
234 static String formatWrongArgumentTypeMessage(unsigned position, const char* name, const char* expectedType)
236 return String::format(\"Failed to convert parameter %d (%s) to %s\", position, name, expectedType);
239 push(@backendMethodsImpl, $privateFunctions);
242 sub generateBackendFunction
244 my $function = shift;
245 return if $function->signature->extendedAttributes->{"notify"};
247 my $functionName = $function->signature->name;
249 my @argsFiltered = grep($_->direction eq "in", @{$function->parameters});
250 map($backendTypes{$_->type} = 1, @argsFiltered); # register required types
251 my $arguments = join(", ", map($typeTransform{$_->type}->{"param"} . " " . $_->name, @argsFiltered));
253 my $signature = " void ${functionName}(PassRefPtr<InspectorArray> args, String* exception);";
254 !$backendMethods{${signature}} || die "Duplicate function was detected for signature '$signature'.";
255 $backendMethods{${signature}} = $functionName;
258 push(@function, "void ${backendClassName}::${functionName}(PassRefPtr<InspectorArray> args, String* exception)");
259 push(@function, "{");
260 my $i = 1; # zero element is the method name.
261 my $expectedParametersCount = scalar(@argsFiltered);
262 my $expectedParametersCountWithMethodName = scalar(@argsFiltered) + 1;
263 push(@function, " if (args->length() != $expectedParametersCountWithMethodName) {");
264 push(@function, " *exception = formatWrongArgumentsCountMessage(args->length() - 1, $expectedParametersCount);");
265 push(@function, " ASSERT_NOT_REACHED();");
266 push(@function, " return;");
267 push(@function, " }");
269 foreach my $parameter (@argsFiltered) {
270 my $parameterType = $parameter->type;
271 push(@function, " " . $typeTransform{$parameterType}->{"retVal"} . " " . $parameter->name . ";");
272 push(@function, " if (!args->get(" . $i . ")->as" . $typeTransform{$parameterType}->{"accessorSuffix"} . "(&" . $parameter->name . ")) {");
273 push(@function, " *exception = formatWrongArgumentTypeMessage($i, \"" . $parameter->name . "\", \"$parameterType\");");
274 push(@function, " ASSERT_NOT_REACHED();");
275 push(@function, " return;");
276 push(@function, " }");
279 push(@function, " m_inspectorBackend->$functionName(" . join(", ", map($_->name, @argsFiltered)) . ");");
280 push(@function, "}");
282 push(@backendMethodsImpl, @function);
285 sub generateBackendDispatcher
288 my @methods = map($backendMethods{$_}, keys %backendMethods);
289 my @mapEntries = map("dispatchMap.add(\"$_\", &${backendClassName}::$_);", @methods);
291 push(@body, "void ${backendClassName}::dispatch(const String& message, String* exception)");
293 push(@body, " typedef void (${backendClassName}::*CallHandler)(PassRefPtr<InspectorArray> args, String* exception);");
294 push(@body, " typedef HashMap<String, CallHandler> DispatchMap;");
295 push(@body, " DEFINE_STATIC_LOCAL(DispatchMap, dispatchMap, );");
296 push(@body, " if (dispatchMap.isEmpty()) {");
297 push(@body, map(" $_", @mapEntries));
300 push(@body, " RefPtr<InspectorValue> parsedMessage = InspectorValue::parseJSON(message);");
301 push(@body, " if (!parsedMessage) {");
302 push(@body, " ASSERT_NOT_REACHED();");
303 push(@body, " *exception = \"Error: Invalid message format. Message should be in JSON format.\";");
304 push(@body, " return;");
307 push(@body, " RefPtr<InspectorArray> messageArray = parsedMessage->asArray();");
308 push(@body, " if (!messageArray) {");
309 push(@body, " ASSERT_NOT_REACHED();");
310 push(@body, " *exception = \"Error: Invalid message format. The message should be a JSONified array of arguments.\";");
311 push(@body, " return;");
314 push(@body, " if (!messageArray->length()) {");
315 push(@body, " ASSERT_NOT_REACHED();");
316 push(@body, " *exception = \"Error: Invalid message format. Empty message was received.\";");
317 push(@body, " return;");
320 push(@body, " String methodName;");
321 push(@body, " if (!messageArray->get(0)->asString(&methodName)) {");
322 push(@body, " ASSERT_NOT_REACHED();");
323 push(@body, " *exception = \"Error: Invalid message format. The first element of the message should be method name.\";");
324 push(@body, " return;");
327 push(@body, " HashMap<String, CallHandler>::iterator it = dispatchMap.find(methodName);");
328 push(@body, " if (it == dispatchMap.end()) {");
329 push(@body, " ASSERT_NOT_REACHED();");
330 push(@body, " *exception = String::format(\"Error: Invalid method name. '%s' wasn't found.\", methodName.utf8().data());");
331 push(@body, " return;");
334 push(@body, " ((*this).*it->second)(messageArray, exception);");
341 my $className = shift;
343 my $constructor = shift;
347 my $forwardHeaders = join("\n", sort(map("#include <" . $typeTransform{$_}->{"forwardHeader"} . ">", grep($typeTransform{$_}->{"forwardHeader"}, keys %{$types}))));
348 my $forwardDeclarations = join("\n", sort(map("class " . $typeTransform{$_}->{"forward"} . ";", grep($typeTransform{$_}->{"forward"}, keys %{$types}))));
349 my $methodsDeclarations = join("\n", keys %{$methods});
351 my $headerBody = << "EOF";
352 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
353 // Use of this source code is governed by a BSD-style license that can be
354 // found in the LICENSE file.
355 #ifndef ${className}_h
356 #define ${className}_h
360 namespace $namespace {
374 } // namespace $namespace
375 #endif // !defined(${className}_h)
383 my $className = shift;
387 my @sourceContent = split("\r", $licenseTemplate);
388 push(@sourceContent, "\n#include \"config.h\"");
389 push(@sourceContent, "#include \"$className.h\"");
390 push(@sourceContent, "");
391 push(@sourceContent, "#if ENABLE(INSPECTOR)");
392 push(@sourceContent, "");
395 foreach my $type (keys %{$types}) {
396 $headers{"#include \"" . $typeTransform{$type}->{"header"} . "\""} = 1 if !$typeTransform{$type}->{"header"} eq "";
398 push(@sourceContent, sort keys %headers);
399 push(@sourceContent, "");
400 push(@sourceContent, "namespace $namespace {");
401 push(@sourceContent, "");
402 push(@sourceContent, @{$methods});
403 push(@sourceContent, "");
404 push(@sourceContent, "} // namespace $namespace");
405 push(@sourceContent, "");
406 push(@sourceContent, "#endif // ENABLE(INSPECTOR)");
407 push(@sourceContent, "");
408 return @sourceContent;
415 open(my $SOURCE, ">$outputDir/$frontendClassName.cpp") || die "Couldn't open file $outputDir/$frontendClassName.cpp";
416 print $SOURCE join("\n", generateSource($frontendClassName, \%frontendTypes, \@frontendMethodsImpl));
420 open(my $HEADER, ">$outputHeadersDir/$frontendClassName.h") || die "Couldn't open file $outputHeadersDir/$frontendClassName.h";
421 print $HEADER generateHeader($frontendClassName, \%frontendTypes, $frontendConstructor, \%frontendMethods, $frontendFooter);
425 open($SOURCE, ">$outputDir/$backendClassName.cpp") || die "Couldn't open file $outputDir/$backendClassName.cpp";
426 print $SOURCE join("\n", generateSource($backendClassName, \%backendTypes, \@backendMethodsImpl));
430 open($HEADER, ">$outputHeadersDir/$backendClassName.h") || die "Couldn't open file $outputHeadersDir/$backendClassName.h";
431 print $HEADER join("\n", generateHeader($backendClassName, \%backendTypes, $backendConstructor, \%backendMethods, $backendFooter));