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