OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2013 Google Inc. All rights reserved. |
| 3 # |
| 4 # Redistribution and use in source and binary forms, with or without |
| 5 # modification, are permitted provided that the following conditions are |
| 6 # met: |
| 7 # |
| 8 # * Redistributions of source code must retain the above copyright |
| 9 # notice, this list of conditions and the following disclaimer. |
| 10 # * Redistributions in binary form must reproduce the above |
| 11 # copyright notice, this list of conditions and the following disclaimer |
| 12 # in the documentation and/or other materials provided with the |
| 13 # distribution. |
| 14 # * Neither the name of Google Inc. nor the names of its |
| 15 # contributors may be used to endorse or promote products derived from |
| 16 # this software without specific prior written permission. |
| 17 # |
| 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 |
| 30 import optparse |
| 31 import re |
| 32 import string |
| 33 import sys |
| 34 |
| 35 template_h = string.Template("""// Code generated from InspectorInstrumentation.
idl |
| 36 |
| 37 #ifndef InspectorInstrumentationInl_h |
| 38 #define InspectorInstrumentationInl_h |
| 39 |
| 40 namespace WebCore { |
| 41 |
| 42 namespace InspectorInstrumentation { |
| 43 |
| 44 $impl_declarations |
| 45 $inline_methods |
| 46 } // namespace InspectorInstrumentation |
| 47 |
| 48 } // namespace WebCore |
| 49 |
| 50 #endif // !defined(InspectorInstrumentationInl_h) |
| 51 """) |
| 52 |
| 53 template_inline = string.Template(""" |
| 54 inline void ${name}(${params_public}) |
| 55 { ${fast_return} |
| 56 if (InstrumentingAgents* instrumentingAgents = ${agents_getter}) |
| 57 ${name}Impl(${params_impl}); |
| 58 } |
| 59 """) |
| 60 |
| 61 template_inline_forward = string.Template(""" |
| 62 inline void ${name}(${params_public}) |
| 63 { ${fast_return} |
| 64 ${name}Impl(${params_impl}); |
| 65 } |
| 66 """) |
| 67 |
| 68 template_inline_accepts_cookie = string.Template(""" |
| 69 inline void ${name}(${params_public}) |
| 70 { ${fast_return} |
| 71 if (${cookie}.isValid()) |
| 72 ${name}Impl(${params_impl}); |
| 73 } |
| 74 """) |
| 75 |
| 76 template_inline_returns_cookie = string.Template(""" |
| 77 inline InspectorInstrumentationCookie ${name}(${params_public}) |
| 78 { ${fast_return} |
| 79 if (InstrumentingAgents* instrumentingAgents = ${agents_getter}) |
| 80 return ${name}Impl(${params_impl}); |
| 81 return InspectorInstrumentationCookie(); |
| 82 } |
| 83 """) |
| 84 |
| 85 |
| 86 template_cpp = string.Template("""// Code generated from InspectorInstrumentatio
n.idl |
| 87 |
| 88 #include "config.h" |
| 89 #include "core/inspector/InspectorInstrumentation.h" |
| 90 |
| 91 #include "core/inspector/InspectorAgent.h" |
| 92 #include "core/inspector/InspectorApplicationCacheAgent.h" |
| 93 #include "core/inspector/InspectorCSSAgent.h" |
| 94 #include "core/inspector/InspectorCanvasAgent.h" |
| 95 #include "core/inspector/InspectorConsoleAgent.h" |
| 96 #include "core/inspector/InspectorConsoleInstrumentation.h" |
| 97 #include "core/inspector/InspectorDOMAgent.h" |
| 98 #include "core/inspector/InspectorDOMDebuggerAgent.h" |
| 99 #include "core/inspector/InspectorDOMStorageAgent.h" |
| 100 #include "core/inspector/InspectorDatabaseAgent.h" |
| 101 #include "core/inspector/InspectorDatabaseInstrumentation.h" |
| 102 #include "core/inspector/InspectorDebuggerAgent.h" |
| 103 #include "core/inspector/InspectorHeapProfilerAgent.h" |
| 104 #include "core/inspector/InspectorLayerTreeAgent.h" |
| 105 #include "core/inspector/InspectorPageAgent.h" |
| 106 #include "core/inspector/InspectorProfilerAgent.h" |
| 107 #include "core/inspector/InspectorResourceAgent.h" |
| 108 #include "core/inspector/InspectorTimelineAgent.h" |
| 109 #include "core/inspector/InspectorWorkerAgent.h" |
| 110 #include "core/inspector/InstrumentingAgents.h" |
| 111 #include "core/inspector/PageDebuggerAgent.h" |
| 112 #include "core/inspector/PageRuntimeAgent.h" |
| 113 #include "core/inspector/WorkerRuntimeAgent.h" |
| 114 |
| 115 namespace WebCore { |
| 116 |
| 117 namespace InspectorInstrumentation { |
| 118 $out_of_line_methods |
| 119 |
| 120 } // namespace InspectorInstrumentation |
| 121 |
| 122 } // namespace WebCore |
| 123 """) |
| 124 |
| 125 template_outofline = string.Template(""" |
| 126 void ${name}Impl(${params_impl}) |
| 127 {${agent_calls} |
| 128 }""") |
| 129 |
| 130 template_agent_call = string.Template(""" |
| 131 if (${agent_class}* ${agent}Agent = ${agent_fetch}) |
| 132 ${agent}Agent->${name}(${params_agent});""") |
| 133 |
| 134 template_timeline_agent_call = string.Template(""" |
| 135 int timelineAgentId = 0; |
| 136 if (InspectorTimelineAgent* timelineAgent = instrumentingAgents->inspectorTi
melineAgent()) { |
| 137 if (timelineAgent->${name}(${params_agent})) |
| 138 timelineAgentId = timelineAgent->id(); |
| 139 }""") |
| 140 |
| 141 template_outofline_returns_cookie = string.Template(""" |
| 142 ${return_type} ${name}Impl(${params_impl}) |
| 143 {${agent_calls} |
| 144 return InspectorInstrumentationCookie(instrumentingAgents, ${timeline_agent_
id}); |
| 145 }""") |
| 146 |
| 147 |
| 148 def load_model_from_idl(source): |
| 149 source = re.sub("//.*\n", "", source) # Remove line comments |
| 150 source = re.sub("\n", " ", source) # Remove newlines |
| 151 source = re.sub("/\*.*\*/", "", source) # Remove block comments |
| 152 source = re.sub("\s\s+", " ", source) # Collapse whitespace |
| 153 source = source.strip() |
| 154 |
| 155 match = re.match("interface\s\w*\s?\{(.*)\}", source) |
| 156 if not match: |
| 157 sys.stderr.write("Cannot parse the file") |
| 158 sys.exit(1) |
| 159 lines = match.group(1) |
| 160 |
| 161 methods = [] |
| 162 for line in map(str.strip, lines.split(";")): |
| 163 if len(line) == 0: |
| 164 continue |
| 165 methods.append(Method(line)) |
| 166 return methods |
| 167 |
| 168 |
| 169 class Method: |
| 170 def __init__(self, source): |
| 171 match = re.match("(\[[\w|,|=|\s]*\])?\s?(\w*) (\w*)\((.*)\)", source) |
| 172 if not match: |
| 173 sys.stderr.write("Cannot parse %s\n" % source) |
| 174 sys.exit(1) |
| 175 |
| 176 self.method_options = [] |
| 177 if match.group(1): |
| 178 method_options_str = re.sub("\s", "", match.group(1)[1:-1]) |
| 179 if len(method_options_str) != 0: |
| 180 self.method_options = method_options_str.split(",") |
| 181 |
| 182 self.return_type = match.group(2) |
| 183 |
| 184 self.name = match.group(3) |
| 185 |
| 186 param_string = match.group(4) |
| 187 |
| 188 self.param_options = [] |
| 189 options_match = re.match("\[(.*)\]\s?(.*)", param_string) |
| 190 if options_match: |
| 191 param_string = options_match.group(2) |
| 192 self.param_options = map(str.strip, options_match.group(1).split(","
)) |
| 193 |
| 194 self.params = map(Parameter, map(str.strip, param_string.split(","))) |
| 195 |
| 196 |
| 197 class Parameter: |
| 198 def __init__(self, source): |
| 199 parts = map(str.strip, source.split("=")) |
| 200 if len(parts) == 1: |
| 201 self.default_value = None |
| 202 else: |
| 203 self.default_value = parts[1] |
| 204 |
| 205 param_decl = parts[0] |
| 206 |
| 207 if re.match("(const|unsigned long) ", param_decl): |
| 208 min_type_tokens = 2 |
| 209 else: |
| 210 min_type_tokens = 1 |
| 211 |
| 212 if len(param_decl.split(" ")) > min_type_tokens: |
| 213 parts = param_decl.split(" ") |
| 214 self.type = " ".join(parts[:-1]) |
| 215 self.name = parts[-1] |
| 216 else: |
| 217 self.type = param_decl |
| 218 self.name = generate_param_name(self.type) |
| 219 |
| 220 def to_str_full(self): |
| 221 if self.default_value is None: |
| 222 return self.to_str_class_and_name() |
| 223 return "%s %s = %s" % (self.type, self.name, self.default_value) |
| 224 |
| 225 def to_str_class_and_name(self): |
| 226 return "%s %s" % (self.type, self.name) |
| 227 |
| 228 def to_str_class(self): |
| 229 return self.type |
| 230 |
| 231 def to_str_name(self): |
| 232 return self.name |
| 233 |
| 234 |
| 235 # This function is only needed to minimize the diff with the handwritten code. |
| 236 # In fact it is sufficient to return a globally unique string. |
| 237 def generate_param_name(param_type): |
| 238 base_name = re.match("(const |RefPtr<|PassRefPtr<)?(\w*)", param_type).group
(2) |
| 239 |
| 240 custom_param_types = { |
| 241 "CharacterData": "characterData", |
| 242 "CSSSelector": "pseudoState", |
| 243 "DocumentStyleSheetCollection": "styleSheetCollection", |
| 244 "EventPath": "eventPath", |
| 245 "FormData": "formData", |
| 246 "InspectorCSSOMWrappers": "inspectorCSSOMWrappers", |
| 247 "InstrumentingAgents": "instrumentingAgents", |
| 248 "NamedFlow": "namedFlow", |
| 249 "RenderObject": "renderer", |
| 250 "RenderLayer": "renderLayer", |
| 251 "ResourceLoader": "resourceLoader", |
| 252 "PseudoElement": "pseudoElement", |
| 253 "ScriptState": "scriptState"} |
| 254 if base_name in custom_param_types: |
| 255 return custom_param_types[base_name] |
| 256 |
| 257 match = re.match("(.*)([A-Z][a-z]+)", base_name) |
| 258 if match: |
| 259 return match.group(2).lower() # CamelCaseWord -> word |
| 260 |
| 261 if base_name.lower() == base_name: |
| 262 return base_name + "_" |
| 263 return base_name.lower() |
| 264 |
| 265 |
| 266 # This function is only needed to minimize the diff with the handwritten code. |
| 267 # In fact is is sufficient to hardcode a constant string (e.g. "agent") into the
template. |
| 268 def agent_variable_name(agent): |
| 269 if re.match("DOM", agent): |
| 270 return re.sub("DOM", "dom", agent) |
| 271 if agent.upper() == agent: |
| 272 return agent.lower() |
| 273 return agent[0].lower() + agent[1:] |
| 274 |
| 275 |
| 276 def agent_class_name(agent): |
| 277 custom_agent_names = ["Inspector", "PageDebugger", "PageRuntime", "WorkerRun
time"] |
| 278 if agent in custom_agent_names: |
| 279 return "%sAgent" % agent |
| 280 return "Inspector%sAgent" % agent |
| 281 |
| 282 |
| 283 def agent_getter_name(agent): |
| 284 name = agent_class_name(agent) |
| 285 return name[0].lower() + name[1:] |
| 286 |
| 287 |
| 288 def generate(input_path, output_h_dir, output_cpp_dir): |
| 289 fin = open(input_path, "r") |
| 290 declarations = load_model_from_idl(fin.read()) |
| 291 fin.close() |
| 292 |
| 293 impl_declarations = [] |
| 294 inline_methods = [] |
| 295 out_of_line_methods = [] |
| 296 |
| 297 for declaration in declarations: |
| 298 param_string_public = ", ".join(map(Parameter.to_str_full, declaration.p
arams)) |
| 299 |
| 300 param_list_impl_parsed = declaration.params[:] |
| 301 |
| 302 accepts_cookie = (declaration.params[0].type == "const InspectorInstrume
ntationCookie&") |
| 303 if not accepts_cookie and not "Inline=Forward" in declaration.method_opt
ions: |
| 304 if not "Keep" in declaration.param_options: |
| 305 param_list_impl_parsed = param_list_impl_parsed[1:] |
| 306 param_list_impl_parsed = [Parameter("InstrumentingAgents*")] + param
_list_impl_parsed |
| 307 |
| 308 generate_inline = not "Inline=Custom" in declaration.method_options |
| 309 if generate_inline: |
| 310 impl_declarations.append("%s %sImpl(%s);" % ( |
| 311 declaration.return_type, declaration.name, ", ".join(map(Paramet
er.to_str_class, param_list_impl_parsed)))) |
| 312 |
| 313 leading_impl_param_name = param_list_impl_parsed[0].name |
| 314 param_string_impl_full = ", ".join(map(Parameter.to_str_class_and_name,
param_list_impl_parsed)) |
| 315 |
| 316 param_list_impl_names_only = map(Parameter.to_str_name, param_list_impl_
parsed) |
| 317 param_string_impl_names_only = ", ".join(param_list_impl_names_only) |
| 318 param_string_agent = ", ".join(param_list_impl_names_only[1:]) |
| 319 |
| 320 def is_agent_name(name): |
| 321 return not "=" in name |
| 322 |
| 323 agents = filter(is_agent_name, declaration.method_options) |
| 324 |
| 325 if "Inline=FastReturn" in declaration.method_options or "Inline=Forward"
in declaration.method_options: |
| 326 fast_return = "\n FAST_RETURN_IF_NO_FRONTENDS(%s());" % declarati
on.return_type |
| 327 else: |
| 328 fast_return = "" |
| 329 |
| 330 if accepts_cookie: |
| 331 if generate_inline: |
| 332 inline_methods.append( |
| 333 template_inline_accepts_cookie.substitute( |
| 334 None, |
| 335 name=declaration.name, |
| 336 fast_return=fast_return, |
| 337 params_public=param_string_public, |
| 338 params_impl=param_string_impl_names_only, |
| 339 cookie=leading_impl_param_name)) |
| 340 if len(agents): |
| 341 agent_calls = [] |
| 342 for agent in agents: |
| 343 if agent == "Timeline": |
| 344 agent_fetch = "retrieveTimelineAgent(%s)" % leading_impl
_param_name |
| 345 else: |
| 346 agent_fetch = "%s.instrumentingAgents()->%s()" % (leadin
g_impl_param_name, agent_getter_name(agent)) |
| 347 agent_calls.append( |
| 348 template_agent_call.substitute( |
| 349 None, |
| 350 name=declaration.name, |
| 351 agent_fetch=agent_fetch, |
| 352 params_agent=param_string_agent, |
| 353 agent_class=agent_class_name(agent), |
| 354 agent=agent_variable_name(agent))) |
| 355 out_of_line_methods.append( |
| 356 template_outofline.substitute( |
| 357 None, |
| 358 name=declaration.name, |
| 359 params_impl=param_string_impl_full, |
| 360 agent_calls="".join(agent_calls))) |
| 361 else: |
| 362 leading_public_param = declaration.params[0] |
| 363 selector_class = re.match("(\w*)", leading_public_param.type).group(
1) |
| 364 agents_getter = "instrumentingAgentsFor%s(%s)" % (selector_class, le
ading_public_param.name) |
| 365 if declaration.return_type == "void": |
| 366 if generate_inline: |
| 367 if "Inline=Forward" in declaration.method_options: |
| 368 inline_methods.append( |
| 369 template_inline_forward.substitute( |
| 370 None, |
| 371 name=declaration.name, |
| 372 fast_return=fast_return, |
| 373 params_public=param_string_public, |
| 374 params_impl=param_string_impl_names_only)) |
| 375 else: |
| 376 inline_methods.append( |
| 377 template_inline.substitute( |
| 378 None, |
| 379 name=declaration.name, |
| 380 fast_return=fast_return, |
| 381 params_public=param_string_public, |
| 382 params_impl=param_string_impl_names_only, |
| 383 agents_getter=agents_getter)) |
| 384 if len(agents): |
| 385 agent_calls = [] |
| 386 for agent in agents: |
| 387 agent_fetch = "%s->%s()" % (leading_impl_param_name, age
nt_getter_name(agent)) |
| 388 agent_call = template_agent_call.substitute( |
| 389 None, |
| 390 name=declaration.name, |
| 391 agent_fetch=agent_fetch, |
| 392 params_agent=param_string_agent, |
| 393 agent_class=agent_class_name(agent), |
| 394 agent=agent_variable_name(agent)) |
| 395 agent_calls.append(agent_call) |
| 396 out_of_line_methods.append( |
| 397 template_outofline.substitute( |
| 398 None, |
| 399 name=declaration.name, |
| 400 params_impl=param_string_impl_full, |
| 401 agent_calls="".join(agent_calls))) |
| 402 elif declaration.return_type == "InspectorInstrumentationCookie": |
| 403 if generate_inline: |
| 404 inline_methods.append( |
| 405 template_inline_returns_cookie.substitute( |
| 406 None, |
| 407 name=declaration.name, |
| 408 fast_return=fast_return, |
| 409 params_public=param_string_public, |
| 410 params_impl=param_string_impl_names_only, |
| 411 agents_getter=agents_getter)) |
| 412 |
| 413 if len(agents): |
| 414 timeline_agent_id = "0" |
| 415 agent_calls = [] |
| 416 for agent in agents: |
| 417 if agent == "Timeline": |
| 418 agent_call = template_timeline_agent_call.substitute
( |
| 419 None, |
| 420 name=declaration.name, |
| 421 params_agent=param_string_agent) |
| 422 timeline_agent_id = "timelineAgentId" |
| 423 else: |
| 424 agent_fetch = "%s->%s()" % (leading_impl_param_name,
agent_getter_name(agent)) |
| 425 agent_call = template_agent_call.substitute( |
| 426 None, |
| 427 name=declaration.name, |
| 428 agent_fetch=agent_fetch, |
| 429 params_agent=param_string_agent, |
| 430 agent_class=agent_class_name(agent), |
| 431 agent=agent_variable_name(agent)) |
| 432 agent_calls.append(agent_call) |
| 433 |
| 434 out_of_line_methods.append( |
| 435 template_outofline_returns_cookie.substitute( |
| 436 None, |
| 437 return_type=declaration.return_type, |
| 438 name=declaration.name, |
| 439 params_impl=param_string_impl_full, |
| 440 agent_calls="".join(agent_calls), |
| 441 timeline_agent_id=timeline_agent_id)) |
| 442 else: |
| 443 sys.stderr.write("Unsupported return type %s" % declaration.retu
rn_type) |
| 444 sys.exit(1) |
| 445 |
| 446 fout = open(output_h_dir + "/InspectorInstrumentationInl.h", "w") |
| 447 fout.write(template_h.substitute(None, |
| 448 impl_declarations="\n".join(impl_declaratio
ns), |
| 449 inline_methods="".join(inline_methods))) |
| 450 fout.close() |
| 451 |
| 452 fout = open(output_cpp_dir + "/InspectorInstrumentationImpl.cpp", "w") |
| 453 fout.write(template_cpp.substitute(None, |
| 454 out_of_line_methods="\n".join(out_of_line
_methods))) |
| 455 fout.close() |
| 456 |
| 457 |
| 458 cmdline_parser = optparse.OptionParser() |
| 459 cmdline_parser.add_option("--output_h_dir") |
| 460 cmdline_parser.add_option("--output_cpp_dir") |
| 461 |
| 462 try: |
| 463 arg_options, arg_values = cmdline_parser.parse_args() |
| 464 if (len(arg_values) != 1): |
| 465 raise Exception("Exactly one plain argument expected (found %s)" % len(a
rg_values)) |
| 466 input_path = arg_values[0] |
| 467 output_header_dirpath = arg_options.output_h_dir |
| 468 output_cpp_dirpath = arg_options.output_cpp_dir |
| 469 if not output_header_dirpath: |
| 470 raise Exception("Output .h directory must be specified") |
| 471 if not output_cpp_dirpath: |
| 472 raise Exception("Output .cpp directory must be specified") |
| 473 except Exception: |
| 474 # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html |
| 475 exc = sys.exc_info()[1] |
| 476 sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc) |
| 477 sys.stderr.write("Usage: <script> InspectorInstrumentation.idl --output_h_di
r <output_header_dir> --output_cpp_dir <output_cpp_dir>\n") |
| 478 exit(1) |
| 479 |
| 480 generate(input_path, output_header_dirpath, output_cpp_dirpath) |
OLD | NEW |