blob: 1d1507fa887402610bf74440b90f447d9099bbb6 [file] [log] [blame]
#!/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 {
$methods
} // namespace InspectorInstrumentation
} // namespace WebCore
#endif // !defined(InspectorInstrumentationInl_h)
""")
template_inline = string.Template("""
inline void ${name}(${params_public})
{ ${fast_return}
if (InstrumentingAgents* agents = ${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* agents = ${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 {
$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_fetch})
agent->${name}(${params_agent});""")
template_agent_call_timeline_returns_cookie = string.Template("""
int timelineAgentId = 0;
if (InspectorTimelineAgent* agent = agents->inspectorTimelineAgent()) {
if (agent->${name}(${params_agent}))
timelineAgentId = agent->id();
}""")
template_outofline_returns_cookie = string.Template("""
${return_type} ${name}Impl(${params_impl})
{${agent_calls}
return InspectorInstrumentationCookie(agents, ${timeline_agent_id});
}""")
def match_and_consume(pattern, source):
match = re.match(pattern, source)
if match:
return match, source[len(match.group(0)):].strip()
return None, source
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.options = []
if match.group(1):
options_str = re.sub("\s", "", match.group(1)[1:-1])
if len(options_str) != 0:
self.options = options_str.split(",")
self.return_type = match.group(2)
self.name = match.group(3)
# Splitting parameters by a comma, assuming that attribute lists contain no more than one attribute.
self.params = map(Parameter, map(str.strip, match.group(4).split(",")))
class Parameter:
def __init__(self, source):
self.options = []
match, source = match_and_consume("\[(\w*)\]", source)
if match:
self.options.append(match.group(1))
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
def generate_param_name(param_type):
base_name = re.match("(const |PassRefPtr<)?(\w*)", param_type).group(2)
return "param" + base_name
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_agent_call(agent, leading_impl_param_name, name, param_string_agent,
accepts_cookie=False, returns_cookie=False):
if not accepts_cookie:
agent_fetch = "%s->%s()" % (leading_impl_param_name, agent_getter_name(agent))
elif agent == "Timeline":
agent_fetch = "retrieveTimelineAgent(%s)" % leading_impl_param_name
else:
agent_fetch = "%s.instrumentingAgents()->%s()" % (leading_impl_param_name, agent_getter_name(agent))
if agent == "Timeline" and returns_cookie:
template = template_agent_call_timeline_returns_cookie
else:
template = template_agent_call
return template.substitute(
None,
name=name,
agent_class=agent_class_name(agent),
agent_fetch=agent_fetch,
params_agent=param_string_agent)
def generate(input_path, output_h_dir, output_cpp_dir):
fin = open(input_path, "r")
declarations = load_model_from_idl(fin.read())
fin.close()
header_lines = []
cpp_lines = []
for declaration in declarations:
param_string_public = ", ".join(map(Parameter.to_str_full, declaration.params))
param_list_impl = declaration.params[:]
accepts_cookie = (declaration.params[0].type == "const InspectorInstrumentationCookie&")
if not accepts_cookie and not "Inline=Forward" in declaration.options:
if not "Keep" in param_list_impl[0].options:
param_list_impl = param_list_impl[1:]
param_list_impl = [Parameter("InstrumentingAgents* agents")] + param_list_impl
generate_inline = not "Inline=Custom" in declaration.options
if generate_inline:
header_lines.append("%s %sImpl(%s);" % (
declaration.return_type, declaration.name, ", ".join(map(Parameter.to_str_class, param_list_impl))))
leading_impl_param_name = param_list_impl[0].name
param_string_impl_full = ", ".join(map(Parameter.to_str_class_and_name, param_list_impl))
param_list_impl_names_only = map(Parameter.to_str_name, param_list_impl)
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.options)
if "Inline=FastReturn" in declaration.options or "Inline=Forward" in declaration.options:
fast_return = "\n FAST_RETURN_IF_NO_FRONTENDS(%s());" % declaration.return_type
else:
fast_return = ""
if accepts_cookie:
if generate_inline:
header_lines.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:
agent_calls.append(generate_agent_call(
agent, leading_impl_param_name, declaration.name, param_string_agent, accepts_cookie=True))
cpp_lines.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.options:
template = template_inline_forward
else:
template = template_inline
header_lines.append(template.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_calls.append(generate_agent_call(
agent, leading_impl_param_name, declaration.name, param_string_agent))
cpp_lines.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:
header_lines.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):
agent_calls = []
for agent in agents:
agent_calls.append(generate_agent_call(
agent, leading_impl_param_name, declaration.name, param_string_agent, returns_cookie=True))
if "Timeline" in agents:
timeline_agent_id = "timelineAgentId"
else:
timeline_agent_id = "0"
cpp_lines.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, methods="\n".join(header_lines)))
fout.close()
fout = open(output_cpp_dir + "/InspectorInstrumentationImpl.cpp", "w")
fout.write(template_cpp.substitute(None, methods="\n".join(cpp_lines)))
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)