Index: core/inspector/CodeGeneratorInstrumentation.py |
=================================================================== |
--- core/inspector/CodeGeneratorInstrumentation.py (revision 0) |
+++ core/inspector/CodeGeneratorInstrumentation.py (revision 0) |
@@ -0,0 +1,480 @@ |
+#!/usr/bin/env python |
+# Copyright (c) 2013 Google Inc. All rights reserved. |
+# |
+# Redistribution and use in source and binary forms, with or without |
+# modification, are permitted provided that the following conditions are |
+# met: |
+# |
+# * Redistributions of source code must retain the above copyright |
+# notice, this list of conditions and the following disclaimer. |
+# * Redistributions in binary form must reproduce the above |
+# copyright notice, this list of conditions and the following disclaimer |
+# in the documentation and/or other materials provided with the |
+# distribution. |
+# * Neither the name of Google Inc. nor the names of its |
+# contributors may be used to endorse or promote products derived from |
+# this software without specific prior written permission. |
+# |
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ |
+import optparse |
+import re |
+import string |
+import sys |
+ |
+template_h = string.Template("""// Code generated from InspectorInstrumentation.idl |
+ |
+#ifndef InspectorInstrumentationInl_h |
+#define InspectorInstrumentationInl_h |
+ |
+namespace WebCore { |
+ |
+namespace InspectorInstrumentation { |
+ |
+$impl_declarations |
+$inline_methods |
+} // namespace InspectorInstrumentation |
+ |
+} // namespace WebCore |
+ |
+#endif // !defined(InspectorInstrumentationInl_h) |
+""") |
+ |
+template_inline = string.Template(""" |
+inline void ${name}(${params_public}) |
+{ ${fast_return} |
+ if (InstrumentingAgents* instrumentingAgents = ${agents_getter}) |
+ ${name}Impl(${params_impl}); |
+} |
+""") |
+ |
+template_inline_forward = string.Template(""" |
+inline void ${name}(${params_public}) |
+{ ${fast_return} |
+ ${name}Impl(${params_impl}); |
+} |
+""") |
+ |
+template_inline_accepts_cookie = string.Template(""" |
+inline void ${name}(${params_public}) |
+{ ${fast_return} |
+ if (${cookie}.isValid()) |
+ ${name}Impl(${params_impl}); |
+} |
+""") |
+ |
+template_inline_returns_cookie = string.Template(""" |
+inline InspectorInstrumentationCookie ${name}(${params_public}) |
+{ ${fast_return} |
+ if (InstrumentingAgents* instrumentingAgents = ${agents_getter}) |
+ return ${name}Impl(${params_impl}); |
+ return InspectorInstrumentationCookie(); |
+} |
+""") |
+ |
+ |
+template_cpp = string.Template("""// Code generated from InspectorInstrumentation.idl |
+ |
+#include "config.h" |
+#include "core/inspector/InspectorInstrumentation.h" |
+ |
+#include "core/inspector/InspectorAgent.h" |
+#include "core/inspector/InspectorApplicationCacheAgent.h" |
+#include "core/inspector/InspectorCSSAgent.h" |
+#include "core/inspector/InspectorCanvasAgent.h" |
+#include "core/inspector/InspectorConsoleAgent.h" |
+#include "core/inspector/InspectorConsoleInstrumentation.h" |
+#include "core/inspector/InspectorDOMAgent.h" |
+#include "core/inspector/InspectorDOMDebuggerAgent.h" |
+#include "core/inspector/InspectorDOMStorageAgent.h" |
+#include "core/inspector/InspectorDatabaseAgent.h" |
+#include "core/inspector/InspectorDatabaseInstrumentation.h" |
+#include "core/inspector/InspectorDebuggerAgent.h" |
+#include "core/inspector/InspectorHeapProfilerAgent.h" |
+#include "core/inspector/InspectorLayerTreeAgent.h" |
+#include "core/inspector/InspectorPageAgent.h" |
+#include "core/inspector/InspectorProfilerAgent.h" |
+#include "core/inspector/InspectorResourceAgent.h" |
+#include "core/inspector/InspectorTimelineAgent.h" |
+#include "core/inspector/InspectorWorkerAgent.h" |
+#include "core/inspector/InstrumentingAgents.h" |
+#include "core/inspector/PageDebuggerAgent.h" |
+#include "core/inspector/PageRuntimeAgent.h" |
+#include "core/inspector/WorkerRuntimeAgent.h" |
+ |
+namespace WebCore { |
+ |
+namespace InspectorInstrumentation { |
+$out_of_line_methods |
+ |
+} // namespace InspectorInstrumentation |
+ |
+} // namespace WebCore |
+""") |
+ |
+template_outofline = string.Template(""" |
+void ${name}Impl(${params_impl}) |
+{${agent_calls} |
+}""") |
+ |
+template_agent_call = string.Template(""" |
+ if (${agent_class}* ${agent}Agent = ${agent_fetch}) |
+ ${agent}Agent->${name}(${params_agent});""") |
+ |
+template_timeline_agent_call = string.Template(""" |
+ int timelineAgentId = 0; |
+ if (InspectorTimelineAgent* timelineAgent = instrumentingAgents->inspectorTimelineAgent()) { |
+ if (timelineAgent->${name}(${params_agent})) |
+ timelineAgentId = timelineAgent->id(); |
+ }""") |
+ |
+template_outofline_returns_cookie = string.Template(""" |
+${return_type} ${name}Impl(${params_impl}) |
+{${agent_calls} |
+ return InspectorInstrumentationCookie(instrumentingAgents, ${timeline_agent_id}); |
+}""") |
+ |
+ |
+def load_model_from_idl(source): |
+ source = re.sub("//.*\n", "", source) # Remove line comments |
+ source = re.sub("\n", " ", source) # Remove newlines |
+ source = re.sub("/\*.*\*/", "", source) # Remove block comments |
+ source = re.sub("\s\s+", " ", source) # Collapse whitespace |
+ source = source.strip() |
+ |
+ match = re.match("interface\s\w*\s?\{(.*)\}", source) |
+ if not match: |
+ sys.stderr.write("Cannot parse the file") |
+ sys.exit(1) |
+ lines = match.group(1) |
+ |
+ methods = [] |
+ for line in map(str.strip, lines.split(";")): |
+ if len(line) == 0: |
+ continue |
+ methods.append(Method(line)) |
+ return methods |
+ |
+ |
+class Method: |
+ def __init__(self, source): |
+ match = re.match("(\[[\w|,|=|\s]*\])?\s?(\w*) (\w*)\((.*)\)", source) |
+ if not match: |
+ sys.stderr.write("Cannot parse %s\n" % source) |
+ sys.exit(1) |
+ |
+ self.method_options = [] |
+ if match.group(1): |
+ method_options_str = re.sub("\s", "", match.group(1)[1:-1]) |
+ if len(method_options_str) != 0: |
+ self.method_options = method_options_str.split(",") |
+ |
+ self.return_type = match.group(2) |
+ |
+ self.name = match.group(3) |
+ |
+ param_string = match.group(4) |
+ |
+ self.param_options = [] |
+ options_match = re.match("\[(.*)\]\s?(.*)", param_string) |
+ if options_match: |
+ param_string = options_match.group(2) |
+ self.param_options = map(str.strip, options_match.group(1).split(",")) |
+ |
+ self.params = map(Parameter, map(str.strip, param_string.split(","))) |
+ |
+ |
+class Parameter: |
+ def __init__(self, source): |
+ parts = map(str.strip, source.split("=")) |
+ if len(parts) == 1: |
+ self.default_value = None |
+ else: |
+ self.default_value = parts[1] |
+ |
+ param_decl = parts[0] |
+ |
+ if re.match("(const|unsigned long) ", param_decl): |
+ min_type_tokens = 2 |
+ else: |
+ min_type_tokens = 1 |
+ |
+ if len(param_decl.split(" ")) > min_type_tokens: |
+ parts = param_decl.split(" ") |
+ self.type = " ".join(parts[:-1]) |
+ self.name = parts[-1] |
+ else: |
+ self.type = param_decl |
+ self.name = generate_param_name(self.type) |
+ |
+ def to_str_full(self): |
+ if self.default_value is None: |
+ return self.to_str_class_and_name() |
+ return "%s %s = %s" % (self.type, self.name, self.default_value) |
+ |
+ def to_str_class_and_name(self): |
+ return "%s %s" % (self.type, self.name) |
+ |
+ def to_str_class(self): |
+ return self.type |
+ |
+ def to_str_name(self): |
+ return self.name |
+ |
+ |
+# This function is only needed to minimize the diff with the handwritten code. |
+# In fact it is sufficient to return a globally unique string. |
+def generate_param_name(param_type): |
+ base_name = re.match("(const |RefPtr<|PassRefPtr<)?(\w*)", param_type).group(2) |
+ |
+ custom_param_types = { |
+ "CharacterData": "characterData", |
+ "CSSSelector": "pseudoState", |
+ "DocumentStyleSheetCollection": "styleSheetCollection", |
+ "EventPath": "eventPath", |
+ "FormData": "formData", |
+ "InspectorCSSOMWrappers": "inspectorCSSOMWrappers", |
+ "InstrumentingAgents": "instrumentingAgents", |
+ "NamedFlow": "namedFlow", |
+ "RenderObject": "renderer", |
+ "RenderLayer": "renderLayer", |
+ "ResourceLoader": "resourceLoader", |
+ "PseudoElement": "pseudoElement", |
+ "ScriptState": "scriptState"} |
+ if base_name in custom_param_types: |
+ return custom_param_types[base_name] |
+ |
+ match = re.match("(.*)([A-Z][a-z]+)", base_name) |
+ if match: |
+ return match.group(2).lower() # CamelCaseWord -> word |
+ |
+ if base_name.lower() == base_name: |
+ return base_name + "_" |
+ return base_name.lower() |
+ |
+ |
+# This function is only needed to minimize the diff with the handwritten code. |
+# In fact is is sufficient to hardcode a constant string (e.g. "agent") into the template. |
+def agent_variable_name(agent): |
+ if re.match("DOM", agent): |
+ return re.sub("DOM", "dom", agent) |
+ if agent.upper() == agent: |
+ return agent.lower() |
+ return agent[0].lower() + agent[1:] |
+ |
+ |
+def agent_class_name(agent): |
+ custom_agent_names = ["Inspector", "PageDebugger", "PageRuntime", "WorkerRuntime"] |
+ if agent in custom_agent_names: |
+ return "%sAgent" % agent |
+ return "Inspector%sAgent" % agent |
+ |
+ |
+def agent_getter_name(agent): |
+ name = agent_class_name(agent) |
+ return name[0].lower() + name[1:] |
+ |
+ |
+def generate(input_path, output_h_dir, output_cpp_dir): |
+ fin = open(input_path, "r") |
+ declarations = load_model_from_idl(fin.read()) |
+ fin.close() |
+ |
+ impl_declarations = [] |
+ inline_methods = [] |
+ out_of_line_methods = [] |
+ |
+ for declaration in declarations: |
+ param_string_public = ", ".join(map(Parameter.to_str_full, declaration.params)) |
+ |
+ param_list_impl_parsed = declaration.params[:] |
+ |
+ accepts_cookie = (declaration.params[0].type == "const InspectorInstrumentationCookie&") |
+ if not accepts_cookie and not "Inline=Forward" in declaration.method_options: |
+ if not "Keep" in declaration.param_options: |
+ param_list_impl_parsed = param_list_impl_parsed[1:] |
+ param_list_impl_parsed = [Parameter("InstrumentingAgents*")] + param_list_impl_parsed |
+ |
+ generate_inline = not "Inline=Custom" in declaration.method_options |
+ if generate_inline: |
+ impl_declarations.append("%s %sImpl(%s);" % ( |
+ declaration.return_type, declaration.name, ", ".join(map(Parameter.to_str_class, param_list_impl_parsed)))) |
+ |
+ leading_impl_param_name = param_list_impl_parsed[0].name |
+ param_string_impl_full = ", ".join(map(Parameter.to_str_class_and_name, param_list_impl_parsed)) |
+ |
+ param_list_impl_names_only = map(Parameter.to_str_name, param_list_impl_parsed) |
+ param_string_impl_names_only = ", ".join(param_list_impl_names_only) |
+ param_string_agent = ", ".join(param_list_impl_names_only[1:]) |
+ |
+ def is_agent_name(name): |
+ return not "=" in name |
+ |
+ agents = filter(is_agent_name, declaration.method_options) |
+ |
+ if "Inline=FastReturn" in declaration.method_options or "Inline=Forward" in declaration.method_options: |
+ fast_return = "\n FAST_RETURN_IF_NO_FRONTENDS(%s());" % declaration.return_type |
+ else: |
+ fast_return = "" |
+ |
+ if accepts_cookie: |
+ if generate_inline: |
+ inline_methods.append( |
+ template_inline_accepts_cookie.substitute( |
+ None, |
+ name=declaration.name, |
+ fast_return=fast_return, |
+ params_public=param_string_public, |
+ params_impl=param_string_impl_names_only, |
+ cookie=leading_impl_param_name)) |
+ if len(agents): |
+ agent_calls = [] |
+ for agent in agents: |
+ if agent == "Timeline": |
+ agent_fetch = "retrieveTimelineAgent(%s)" % leading_impl_param_name |
+ else: |
+ agent_fetch = "%s.instrumentingAgents()->%s()" % (leading_impl_param_name, agent_getter_name(agent)) |
+ agent_calls.append( |
+ template_agent_call.substitute( |
+ None, |
+ name=declaration.name, |
+ agent_fetch=agent_fetch, |
+ params_agent=param_string_agent, |
+ agent_class=agent_class_name(agent), |
+ agent=agent_variable_name(agent))) |
+ out_of_line_methods.append( |
+ template_outofline.substitute( |
+ None, |
+ name=declaration.name, |
+ params_impl=param_string_impl_full, |
+ agent_calls="".join(agent_calls))) |
+ else: |
+ leading_public_param = declaration.params[0] |
+ selector_class = re.match("(\w*)", leading_public_param.type).group(1) |
+ agents_getter = "instrumentingAgentsFor%s(%s)" % (selector_class, leading_public_param.name) |
+ if declaration.return_type == "void": |
+ if generate_inline: |
+ if "Inline=Forward" in declaration.method_options: |
+ inline_methods.append( |
+ template_inline_forward.substitute( |
+ None, |
+ name=declaration.name, |
+ fast_return=fast_return, |
+ params_public=param_string_public, |
+ params_impl=param_string_impl_names_only)) |
+ else: |
+ inline_methods.append( |
+ template_inline.substitute( |
+ None, |
+ name=declaration.name, |
+ fast_return=fast_return, |
+ params_public=param_string_public, |
+ params_impl=param_string_impl_names_only, |
+ agents_getter=agents_getter)) |
+ if len(agents): |
+ agent_calls = [] |
+ for agent in agents: |
+ agent_fetch = "%s->%s()" % (leading_impl_param_name, agent_getter_name(agent)) |
+ agent_call = template_agent_call.substitute( |
+ None, |
+ name=declaration.name, |
+ agent_fetch=agent_fetch, |
+ params_agent=param_string_agent, |
+ agent_class=agent_class_name(agent), |
+ agent=agent_variable_name(agent)) |
+ agent_calls.append(agent_call) |
+ out_of_line_methods.append( |
+ template_outofline.substitute( |
+ None, |
+ name=declaration.name, |
+ params_impl=param_string_impl_full, |
+ agent_calls="".join(agent_calls))) |
+ elif declaration.return_type == "InspectorInstrumentationCookie": |
+ if generate_inline: |
+ inline_methods.append( |
+ template_inline_returns_cookie.substitute( |
+ None, |
+ name=declaration.name, |
+ fast_return=fast_return, |
+ params_public=param_string_public, |
+ params_impl=param_string_impl_names_only, |
+ agents_getter=agents_getter)) |
+ |
+ if len(agents): |
+ timeline_agent_id = "0" |
+ agent_calls = [] |
+ for agent in agents: |
+ if agent == "Timeline": |
+ agent_call = template_timeline_agent_call.substitute( |
+ None, |
+ name=declaration.name, |
+ params_agent=param_string_agent) |
+ timeline_agent_id = "timelineAgentId" |
+ else: |
+ agent_fetch = "%s->%s()" % (leading_impl_param_name, agent_getter_name(agent)) |
+ agent_call = template_agent_call.substitute( |
+ None, |
+ name=declaration.name, |
+ agent_fetch=agent_fetch, |
+ params_agent=param_string_agent, |
+ agent_class=agent_class_name(agent), |
+ agent=agent_variable_name(agent)) |
+ agent_calls.append(agent_call) |
+ |
+ out_of_line_methods.append( |
+ template_outofline_returns_cookie.substitute( |
+ None, |
+ return_type=declaration.return_type, |
+ name=declaration.name, |
+ params_impl=param_string_impl_full, |
+ agent_calls="".join(agent_calls), |
+ timeline_agent_id=timeline_agent_id)) |
+ else: |
+ sys.stderr.write("Unsupported return type %s" % declaration.return_type) |
+ sys.exit(1) |
+ |
+ fout = open(output_h_dir + "/InspectorInstrumentationInl.h", "w") |
+ fout.write(template_h.substitute(None, |
+ impl_declarations="\n".join(impl_declarations), |
+ inline_methods="".join(inline_methods))) |
+ fout.close() |
+ |
+ fout = open(output_cpp_dir + "/InspectorInstrumentationImpl.cpp", "w") |
+ fout.write(template_cpp.substitute(None, |
+ out_of_line_methods="\n".join(out_of_line_methods))) |
+ fout.close() |
+ |
+ |
+cmdline_parser = optparse.OptionParser() |
+cmdline_parser.add_option("--output_h_dir") |
+cmdline_parser.add_option("--output_cpp_dir") |
+ |
+try: |
+ arg_options, arg_values = cmdline_parser.parse_args() |
+ if (len(arg_values) != 1): |
+ raise Exception("Exactly one plain argument expected (found %s)" % len(arg_values)) |
+ input_path = arg_values[0] |
+ output_header_dirpath = arg_options.output_h_dir |
+ output_cpp_dirpath = arg_options.output_cpp_dir |
+ if not output_header_dirpath: |
+ raise Exception("Output .h directory must be specified") |
+ if not output_cpp_dirpath: |
+ raise Exception("Output .cpp directory must be specified") |
+except Exception: |
+ # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html |
+ exc = sys.exc_info()[1] |
+ sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc) |
+ sys.stderr.write("Usage: <script> InspectorInstrumentation.idl --output_h_dir <output_header_dir> --output_cpp_dir <output_cpp_dir>\n") |
+ exit(1) |
+ |
+generate(input_path, output_header_dirpath, output_cpp_dirpath) |