• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
30import optparse
31import re
32import string
33import sys
34
35template_h = string.Template("""// Code generated from InspectorInstrumentation.idl
36
37#ifndef ${file_name}_h
38#define ${file_name}_h
39
40${includes}
41
42namespace blink {
43
44${forward_declarations}
45
46namespace InspectorInstrumentation {
47
48$methods
49} // namespace InspectorInstrumentation
50
51} // namespace blink
52
53#endif // !defined(${file_name}_h)
54""")
55
56template_inline = string.Template("""
57inline void ${name}(${params_public})
58{   ${fast_return}
59    if (${condition})
60        ${name}Impl(${params_impl});
61}
62""")
63
64template_inline_forward = string.Template("""
65inline void ${name}(${params_public})
66{   ${fast_return}
67    ${name}Impl(${params_impl});
68}
69""")
70
71template_inline_returns_value = string.Template("""
72inline ${return_type} ${name}(${params_public})
73{   ${fast_return}
74    if (${condition})
75        return ${name}Impl(${params_impl});
76    return ${default_return_value};
77}
78""")
79
80
81template_cpp = string.Template("""// Code generated from InspectorInstrumentation.idl
82
83#include "config.h"
84
85${includes}
86
87namespace blink {
88${extra_definitions}
89
90namespace InspectorInstrumentation {
91$methods
92
93} // namespace InspectorInstrumentation
94
95} // namespace blink
96""")
97
98template_outofline = string.Template("""
99${return_type} ${name}Impl(${params_impl})
100{${impl_lines}
101}""")
102
103template_agent_call = string.Template("""
104    if (${agent_class}* agent = ${agent_fetch})
105        ${maybe_return}agent->${name}(${params_agent});""")
106
107template_agent_call_timeline_returns_cookie = string.Template("""
108    int timelineAgentId = 0;
109    if (InspectorTimelineAgent* agent = agents->inspectorTimelineAgent()) {
110        if (agent->${name}(${params_agent}))
111            timelineAgentId = agent->id();
112    }""")
113
114
115template_instrumenting_agents_h = string.Template("""// Code generated from InspectorInstrumentation.idl
116
117#ifndef InstrumentingAgentsInl_h
118#define InstrumentingAgentsInl_h
119
120#include "platform/heap/Handle.h"
121#include "wtf/FastAllocBase.h"
122#include "wtf/Noncopyable.h"
123#include "wtf/PassRefPtr.h"
124#include "wtf/RefCounted.h"
125
126namespace blink {
127
128${forward_list}
129
130class InstrumentingAgents : public RefCountedWillBeGarbageCollectedFinalized<InstrumentingAgents> {
131    WTF_MAKE_NONCOPYABLE(InstrumentingAgents);
132    WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED;
133public:
134    static PassRefPtrWillBeRawPtr<InstrumentingAgents> create()
135    {
136        return adoptRefWillBeNoop(new InstrumentingAgents());
137    }
138    ~InstrumentingAgents() { }
139    void trace(Visitor*);
140    void reset();
141
142${accessor_list}
143
144private:
145    InstrumentingAgents();
146
147${member_list}
148};
149
150}
151
152#endif // !defined(InstrumentingAgentsInl_h)
153""")
154
155template_instrumenting_agent_accessor = string.Template("""
156    ${class_name}* ${getter_name}() const { return ${member_name}; }
157    void set${class_name}(${class_name}* agent) { ${member_name} = agent; }""")
158
159template_instrumenting_agents_cpp = string.Template("""
160InstrumentingAgents::InstrumentingAgents()
161    : $init_list
162{
163}
164
165void InstrumentingAgents::trace(Visitor* visitor)
166{
167    $trace_list
168}
169
170void InstrumentingAgents::reset()
171{
172    $reset_list
173}""")
174
175
176
177def match_and_consume(pattern, source):
178    match = re.match(pattern, source)
179    if match:
180        return match, source[len(match.group(0)):].strip()
181    return None, source
182
183
184def load_model_from_idl(source):
185    source = re.sub("//.*", "", source)  # Remove line comments
186    source = re.sub("/\*(.|\n)*?\*/", "", source, re.MULTILINE)  # Remove block comments
187    source = re.sub("\]\s*?\n\s*", "] ", source)  # Merge the method annotation with the next line
188    source = source.strip()
189
190    model = []
191
192    while len(source):
193        match, source = match_and_consume("interface\s(\w*)\s?\{([^\{]*)\}", source)
194        if not match:
195            sys.stderr.write("Cannot parse %s\n" % source[:100])
196            sys.exit(1)
197        model.append(File(match.group(1), match.group(2)))
198
199    return model
200
201
202class File:
203    def __init__(self, name, source):
204        self.name = name
205        self.header_name = self.name + "Inl"
206        self.includes = [include_inspector_header("InspectorInstrumentation")]
207        self.forward_declarations = []
208        self.declarations = []
209        for line in map(str.strip, source.split("\n")):
210            line = re.sub("\s{2,}", " ", line).strip()  # Collapse whitespace
211            if len(line) == 0:
212                continue
213            if line[0] == "#":
214                self.includes.append(line)
215            elif line.startswith("class "):
216                self.forward_declarations.append(line)
217            else:
218                self.declarations.append(Method(line))
219        self.includes.sort()
220        self.forward_declarations.sort()
221
222    def generate(self, cpp_lines, used_agents):
223        header_lines = []
224        for declaration in self.declarations:
225            for agent in set(declaration.agents):
226                used_agents.add(agent)
227            declaration.generate_header(header_lines)
228            declaration.generate_cpp(cpp_lines)
229
230        return template_h.substitute(None,
231                                     file_name=self.header_name,
232                                     includes="\n".join(self.includes),
233                                     forward_declarations="\n".join(self.forward_declarations),
234                                     methods="\n".join(header_lines))
235
236
237class Method:
238    def __init__(self, source):
239        match = re.match("(\[[\w|,|=|\s]*\])?\s?(\w*\*?) (\w*)\((.*)\)\s?;", source)
240        if not match:
241            sys.stderr.write("Cannot parse %s\n" % source)
242            sys.exit(1)
243
244        self.options = []
245        if match.group(1):
246            options_str = re.sub("\s", "", match.group(1)[1:-1])
247            if len(options_str) != 0:
248                self.options = options_str.split(",")
249
250        self.return_type = match.group(2)
251
252        self.name = match.group(3)
253
254        # Splitting parameters by a comma, assuming that attribute lists contain no more than one attribute.
255        self.params = map(Parameter, map(str.strip, match.group(4).split(",")))
256
257        self.accepts_cookie = len(self.params) and self.params[0].type == "const InspectorInstrumentationCookie&"
258        self.returns_cookie = self.return_type == "InspectorInstrumentationCookie"
259
260        self.returns_value = self.return_type != "void"
261
262        if self.return_type == "bool":
263            self.default_return_value = "false"
264        elif self.return_type == "int":
265            self.default_return_value = "0"
266        elif self.return_type == "String":
267            self.default_return_value = "\"\""
268        else:
269            self.default_return_value = self.return_type + "()"
270
271        for param in self.params:
272            if "DefaultReturn" in param.options:
273                self.default_return_value = param.name
274
275        self.params_impl = self.params
276        if not self.accepts_cookie and not "Inline=Forward" in self.options:
277            if not "Keep" in self.params_impl[0].options:
278                self.params_impl = self.params_impl[1:]
279            self.params_impl = [Parameter("InstrumentingAgents* agents")] + self.params_impl
280
281        self.agents = filter(lambda option: not "=" in option, self.options)
282
283    def generate_header(self, header_lines):
284        if "Inline=Custom" in self.options:
285            return
286
287        header_lines.append("%s %sImpl(%s);" % (
288            self.return_type, self.name, ", ".join(map(Parameter.to_str_class, self.params_impl))))
289
290        if "Inline=FastReturn" in self.options or "Inline=Forward" in self.options:
291            fast_return = "\n    FAST_RETURN_IF_NO_FRONTENDS(%s);" % self.default_return_value
292        else:
293            fast_return = ""
294
295        for param in self.params:
296            if "FastReturn" in param.options:
297                fast_return += "\n    if (!%s)\n        return %s;" % (param.name, self.default_return_value)
298
299        if self.accepts_cookie:
300            condition = "%s.isValid()" % self.params_impl[0].name
301            template = template_inline
302        elif "Inline=Forward" in self.options:
303            condition = ""
304            template = template_inline_forward
305        else:
306            condition = "InstrumentingAgents* agents = instrumentingAgentsFor(%s)" % self.params[0].name
307
308            if self.returns_value:
309                template = template_inline_returns_value
310            else:
311                template = template_inline
312
313        header_lines.append(template.substitute(
314            None,
315            name=self.name,
316            fast_return=fast_return,
317            return_type=self.return_type,
318            default_return_value=self.default_return_value,
319            params_public=", ".join(map(Parameter.to_str_full, self.params)),
320            params_impl=", ".join(map(Parameter.to_str_name, self.params_impl)),
321            condition=condition))
322
323    def generate_cpp(self, cpp_lines):
324        if len(self.agents) == 0:
325            return
326
327        body_lines = map(self.generate_ref_ptr, self.params)
328        body_lines += map(self.generate_agent_call, self.agents)
329
330        if self.returns_cookie:
331            if "Timeline" in self.agents:
332                timeline_agent_id = "timelineAgentId"
333            else:
334                timeline_agent_id = "0"
335            body_lines.append("\n    return InspectorInstrumentationCookie(agents, %s);" % timeline_agent_id)
336        elif self.returns_value:
337            body_lines.append("\n    return %s;" % self.default_return_value)
338
339        cpp_lines.append(template_outofline.substitute(
340            None,
341            return_type=self.return_type,
342            name=self.name,
343            params_impl=", ".join(map(Parameter.to_str_class_and_name, self.params_impl)),
344            impl_lines="".join(body_lines)))
345
346    def generate_agent_call(self, agent):
347        agent_class, agent_getter = agent_getter_signature(agent)
348
349        leading_param_name = self.params_impl[0].name
350        if not self.accepts_cookie:
351            agent_fetch = "%s->%s()" % (leading_param_name, agent_getter)
352        elif agent == "Timeline":
353            agent_fetch = "retrieveTimelineAgent(%s)" % leading_param_name
354        else:
355            agent_fetch = "%s.instrumentingAgents()->%s()" % (leading_param_name, agent_getter)
356
357        if agent == "Timeline" and self.returns_cookie:
358            template = template_agent_call_timeline_returns_cookie
359        else:
360            template = template_agent_call
361
362        if not self.returns_value or self.returns_cookie:
363            maybe_return = ""
364        else:
365            maybe_return = "return "
366
367        return template.substitute(
368            None,
369            name=self.name,
370            agent_class=agent_class,
371            agent_fetch=agent_fetch,
372            maybe_return=maybe_return,
373            params_agent=", ".join(map(Parameter.to_str_value, self.params_impl)[1:]))
374
375    def generate_ref_ptr(self, param):
376        if param.is_prp:
377            return "\n    RefPtr<%s> %s = %s;" % (param.inner_type, param.value, param.name)
378        else:
379            return ""
380
381class Parameter:
382    def __init__(self, source):
383        self.options = []
384        match, source = match_and_consume("\[(\w*)\]", source)
385        if match:
386            self.options.append(match.group(1))
387
388        parts = map(str.strip, source.split("="))
389        if len(parts) == 1:
390            self.default_value = None
391        else:
392            self.default_value = parts[1]
393
394        param_decl = parts[0]
395
396        if re.match("(const|unsigned long) ", param_decl):
397            min_type_tokens = 2
398        else:
399            min_type_tokens = 1
400
401        if len(param_decl.split(" ")) > min_type_tokens:
402            parts = param_decl.split(" ")
403            self.type = " ".join(parts[:-1])
404            self.name = parts[-1]
405        else:
406            self.type = param_decl
407            self.name = generate_param_name(self.type)
408
409        if re.match("PassRefPtr<", param_decl):
410            self.is_prp = True
411            self.value = self.name
412            self.name = "prp" + self.name[0].upper() + self.name[1:]
413            self.inner_type = re.match("PassRefPtr<(.+)>", param_decl).group(1)
414        else:
415            self.is_prp = False
416            self.value = self.name
417
418
419    def to_str_full(self):
420        if self.default_value is None:
421            return self.to_str_class_and_name()
422        return "%s %s = %s" % (self.type, self.name, self.default_value)
423
424    def to_str_class_and_name(self):
425        return "%s %s" % (self.type, self.name)
426
427    def to_str_class(self):
428        return self.type
429
430    def to_str_name(self):
431        return self.name
432
433    def to_str_value(self):
434        return self.value
435
436
437def generate_param_name(param_type):
438    base_name = re.match("(const |PassRefPtr<)?(\w*)", param_type).group(2)
439    return "param" + base_name
440
441
442def agent_class_name(agent):
443    custom_agent_names = ["PageDebugger", "PageRuntime", "WorkerRuntime"]
444    if agent in custom_agent_names:
445        return "%sAgent" % agent
446    return "Inspector%sAgent" % agent
447
448
449def agent_getter_signature(agent):
450    agent_class = agent_class_name(agent)
451    return agent_class, agent_class[0].lower() + agent_class[1:]
452
453
454def include_header(name):
455    return "#include \"%s.h\"" % name
456
457
458def include_inspector_header(name):
459    return include_header("core/inspector/" + name)
460
461
462def generate_instrumenting_agents(used_agents):
463    agents = list(used_agents)
464
465    forward_list = []
466    accessor_list = []
467    member_list = []
468    init_list = []
469    trace_list = []
470    reset_list = []
471
472    for agent in agents:
473        class_name, getter_name = agent_getter_signature(agent)
474        member_name = "m_" + getter_name
475
476        forward_list.append("class %s;" % class_name)
477        accessor_list.append(template_instrumenting_agent_accessor.substitute(
478            None,
479            class_name=class_name,
480            getter_name=getter_name,
481            member_name=member_name))
482        member_list.append("    RawPtrWillBeMember<%s> %s;" % (class_name, member_name))
483        init_list.append("%s(nullptr)" % member_name)
484        trace_list.append("visitor->trace(%s);" % member_name)
485        reset_list.append("%s = nullptr;" % member_name)
486
487    forward_list.sort()
488    accessor_list.sort()
489    member_list.sort()
490    init_list.sort()
491    trace_list.sort()
492    reset_list.sort()
493
494    header_lines = template_instrumenting_agents_h.substitute(
495        None,
496        forward_list="\n".join(forward_list),
497        accessor_list="\n".join(accessor_list),
498        member_list="\n".join(member_list))
499
500    cpp_lines = template_instrumenting_agents_cpp.substitute(
501        None,
502        init_list="\n    , ".join(init_list),
503        trace_list="\n    ".join(trace_list),
504        reset_list="\n    ".join(reset_list))
505
506    return header_lines, cpp_lines
507
508
509def generate(input_path, output_dir):
510    fin = open(input_path, "r")
511    files = load_model_from_idl(fin.read())
512    fin.close()
513
514    cpp_includes = []
515    cpp_lines = []
516    used_agents = set()
517    for f in files:
518        cpp_includes.append(include_header(f.header_name))
519
520        fout = open(output_dir + "/" + f.header_name + ".h", "w")
521        fout.write(f.generate(cpp_lines, used_agents))
522        fout.close()
523
524    for agent in used_agents:
525        cpp_includes.append(include_inspector_header(agent_class_name(agent)))
526    cpp_includes.append(include_header("InstrumentingAgentsInl"))
527    cpp_includes.sort()
528
529    instrumenting_agents_header, instrumenting_agents_cpp = generate_instrumenting_agents(used_agents)
530
531    fout = open(output_dir + "/" + "InstrumentingAgentsInl.h", "w")
532    fout.write(instrumenting_agents_header)
533    fout.close()
534
535    fout = open(output_dir + "/InspectorInstrumentationImpl.cpp", "w")
536    fout.write(template_cpp.substitute(None,
537                                       includes="\n".join(cpp_includes),
538                                       extra_definitions=instrumenting_agents_cpp,
539                                       methods="\n".join(cpp_lines)))
540    fout.close()
541
542
543cmdline_parser = optparse.OptionParser()
544cmdline_parser.add_option("--output_dir")
545
546try:
547    arg_options, arg_values = cmdline_parser.parse_args()
548    if (len(arg_values) != 1):
549        raise Exception("Exactly one plain argument expected (found %s)" % len(arg_values))
550    input_path = arg_values[0]
551    output_dirpath = arg_options.output_dir
552    if not output_dirpath:
553        raise Exception("Output directory must be specified")
554except Exception:
555    # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html
556    exc = sys.exc_info()[1]
557    sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc)
558    sys.stderr.write("Usage: <script> --output_dir <output_dir> InspectorInstrumentation.idl\n")
559    exit(1)
560
561generate(input_path, output_dirpath)
562