Commit 2115673f authored by Yuki Shiino's avatar Yuki Shiino Committed by Commit Bot

bind-gen: Accumulate the rendering results in a single buffer

This patch improves the code generation speed.
Ex) Navigator 10.5 sec => 8.5 sec

Bug: 839389
Change-Id: I90b90f00d09b304fa7f5beb9247eefc75bb75cf3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1980603
Commit-Queue: Yuki Shiino <yukishiino@chromium.org>
Reviewed-by: default avatarHitoshi Yoshida <peria@chromium.org>
Cr-Commit-Position: refs/heads/master@{#728386}
parent 2248e201
...@@ -160,10 +160,15 @@ class CodeNode(object): ...@@ -160,10 +160,15 @@ class CodeNode(object):
def __str__(self): def __str__(self):
""" """
Renders this CodeNode object into a Mako template. This is supposed to Renders this CodeNode object directly into the renderer's text buffer
be used in a Mako template as ${code_node}. and always returns the empty string. This is because it's faster to
accumulate the rendering result directly in a single text buffer than
making a lot of string pieces and concatenating them.
This function is supposed to be used in a Mako template as ${code_node}.
""" """
return self.render() self.render()
return ""
def render(self): def render(self):
""" """
...@@ -180,7 +185,7 @@ class CodeNode(object): ...@@ -180,7 +185,7 @@ class CodeNode(object):
self._is_rendering = True self._is_rendering = True
try: try:
text = self._render( self._render(
renderer=renderer, last_render_state=last_render_state) renderer=renderer, last_render_state=last_render_state)
finally: finally:
self._is_rendering = False self._is_rendering = False
...@@ -192,8 +197,6 @@ class CodeNode(object): ...@@ -192,8 +197,6 @@ class CodeNode(object):
request(accumulator) request(accumulator)
self._accumulate_requests = [] self._accumulate_requests = []
return text
def _render(self, renderer, last_render_state): def _render(self, renderer, last_render_state):
""" """
Renders this CodeNode object as a text string and also propagates Renders this CodeNode object as a text string and also propagates
...@@ -201,7 +204,7 @@ class CodeNode(object): ...@@ -201,7 +204,7 @@ class CodeNode(object):
Only limited subclasses may override this method. Only limited subclasses may override this method.
""" """
return renderer.render( renderer.render(
caller=self, caller=self,
template=self._template, template=self._template,
template_vars=self.template_vars) template_vars=self.template_vars)
...@@ -343,13 +346,16 @@ class LiteralNode(CodeNode): ...@@ -343,13 +346,16 @@ class LiteralNode(CodeNode):
""" """
def __init__(self, literal_text): def __init__(self, literal_text):
literal_text_gensym = CodeNode.gensym() CodeNode.__init__(self)
template_text = format_template(
"${{{literal_text}}}", literal_text=literal_text_gensym)
template_vars = {literal_text_gensym: literal_text}
CodeNode.__init__( self._literal_text = literal_text
self, template_text=template_text, template_vars=template_vars)
def _render(self, renderer, last_render_state):
renderer.push_caller(self)
try:
renderer.render_text(str(self._literal_text))
finally:
renderer.pop_caller()
class TextNode(CodeNode): class TextNode(CodeNode):
...@@ -425,33 +431,12 @@ class ListNode(CodeNode): ...@@ -425,33 +431,12 @@ class ListNode(CodeNode):
assert isinstance(head, str) assert isinstance(head, str)
assert isinstance(tail, str) assert isinstance(tail, str)
element_nodes_gensym = CodeNode.gensym() CodeNode.__init__(self)
element_nodes = []
template_text = format_template(
"""\
% if {element_nodes}:
{head}\\
% endif
% for node in {element_nodes}:
${node}\\
% if not loop.last:
{separator}\\
% endif
% endfor
% if {element_nodes}:
{tail}\\
% endif\
""",
element_nodes=element_nodes_gensym,
separator=separator,
head=head,
tail=tail)
template_vars = {element_nodes_gensym: element_nodes}
CodeNode.__init__(
self, template_text=template_text, template_vars=template_vars)
self._element_nodes = element_nodes self._element_nodes = []
self._separator = separator
self._head = head
self._tail = tail
if code_nodes is not None: if code_nodes is not None:
self.extend(code_nodes) self.extend(code_nodes)
...@@ -465,6 +450,23 @@ ${node}\\ ...@@ -465,6 +450,23 @@ ${node}\\
def __len__(self): def __len__(self):
return len(self._element_nodes) return len(self._element_nodes)
def _render(self, renderer, last_render_state):
renderer.push_caller(self)
try:
if self._element_nodes:
renderer.render_text(self._head)
is_first = True
for node in self._element_nodes:
if is_first:
is_first = False
else:
renderer.render_text(self._separator)
node.render()
if self._element_nodes:
renderer.render_text(self._tail)
finally:
renderer.pop_caller()
def append(self, node): def append(self, node):
if node is None: if node is None:
return return
...@@ -760,6 +762,14 @@ class SymbolNode(CodeNode): ...@@ -760,6 +762,14 @@ class SymbolNode(CodeNode):
self._definition_constructor = definition_constructor self._definition_constructor = definition_constructor
def _render(self, renderer, last_render_state): def _render(self, renderer, last_render_state):
self._request_symbol_definition(renderer)
renderer.render_text(self.name)
def request_symbol_definition(self):
self._request_symbol_definition(self.renderer)
def _request_symbol_definition(self, renderer):
symbol_scope_chain = tuple( symbol_scope_chain = tuple(
filter(lambda node: isinstance(node, SymbolScopeNode), filter(lambda node: isinstance(node, SymbolScopeNode),
renderer.callers_from_first_to_last)) renderer.callers_from_first_to_last))
...@@ -775,8 +785,6 @@ class SymbolNode(CodeNode): ...@@ -775,8 +785,6 @@ class SymbolNode(CodeNode):
if scope is self.outer: if scope is self.outer:
break break
return self.name
@property @property
def name(self): def name(self):
return self._name return self._name
......
...@@ -135,6 +135,7 @@ int var1 = var2 + var3; ...@@ -135,6 +135,7 @@ int var1 = var2 + var3;
])) ]))
with self.assertRaises(NameError): with self.assertRaises(NameError):
renderer.reset()
root.render() root.render()
callers_on_error = list(renderer.callers_on_error) callers_on_error = list(renderer.callers_on_error)
......
...@@ -150,11 +150,14 @@ def render_code_node(code_node): ...@@ -150,11 +150,14 @@ def render_code_node(code_node):
Renders |code_node| and turns it into text letting |code_node| apply all Renders |code_node| and turns it into text letting |code_node| apply all
necessary changes (side effects). Returns the resulting text. necessary changes (side effects). Returns the resulting text.
""" """
prev = "_" renderer = code_node.renderer
prev = None
current = "" current = ""
while current != prev: while current != prev:
renderer.reset()
prev = current prev = current
current = str(code_node) code_node.render()
current = renderer.to_text()
return current return current
......
...@@ -1176,9 +1176,9 @@ def make_v8_set_return_value(cg_context): ...@@ -1176,9 +1176,9 @@ def make_v8_set_return_value(cg_context):
T = TextNode T = TextNode
if cg_context.attribute_set or cg_context.return_type.unwrap().is_void: if cg_context.attribute_set or cg_context.return_type.unwrap().is_void:
# Render a SymbolNode |return_value| discarding the content text, and # Request a SymbolNode |return_value| to define itself without rendering
# let a symbol definition be inserted. # any text.
return T("<% str(return_value) %>") return T("<% return_value.request_symbol_definition() %>")
return_type_body = cg_context.return_type.unwrap() return_type_body = cg_context.return_type.unwrap()
if (cg_context.for_world == cg_context.MAIN_WORLD if (cg_context.for_world == cg_context.MAIN_WORLD
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
import sys import sys
import mako.runtime
import mako.template import mako.template
import mako.util
_MAKO_TEMPLATE_PASS_KEY = object() _MAKO_TEMPLATE_PASS_KEY = object()
...@@ -38,9 +40,22 @@ class MakoRenderer(object): ...@@ -38,9 +40,22 @@ class MakoRenderer(object):
"""Represents a renderer object implemented with Mako templates.""" """Represents a renderer object implemented with Mako templates."""
def __init__(self): def __init__(self):
self._text_buffer = None
self._caller_stack = [] self._caller_stack = []
self._caller_stack_on_error = [] self._caller_stack_on_error = []
def reset(self):
"""
Resets the rendering states of this object. Must be called before
the first call to |render| or |render_text|.
"""
self._text_buffer = mako.util.FastEncodingBuffer()
def to_text(self):
"""Returns the rendering result."""
assert self._text_buffer is not None
return self._text_buffer.getvalue()
def render(self, caller, template, template_vars): def render(self, caller, template, template_vars):
""" """
Renders the template with variable bindings. Renders the template with variable bindings.
...@@ -63,7 +78,9 @@ class MakoRenderer(object): ...@@ -63,7 +78,9 @@ class MakoRenderer(object):
try: try:
mako_template = template.mako_template( mako_template = template.mako_template(
pass_key=_MAKO_TEMPLATE_PASS_KEY) pass_key=_MAKO_TEMPLATE_PASS_KEY)
text = mako_template.render(**template_vars) mako_context = mako.runtime.Context(self._text_buffer,
**template_vars)
mako_template.render_context(mako_context)
except: except:
# Print stacktrace of template rendering. # Print stacktrace of template rendering.
sys.stderr.write("\n") sys.stderr.write("\n")
...@@ -89,7 +106,16 @@ class MakoRenderer(object): ...@@ -89,7 +106,16 @@ class MakoRenderer(object):
finally: finally:
self._caller_stack.pop() self._caller_stack.pop()
return text def render_text(self, text):
"""Renders a plain text as is."""
assert isinstance(text, str)
self._text_buffer.write(text)
def push_caller(self, caller):
self._caller_stack.append(caller)
def pop_caller(self):
self._caller_stack.pop()
@property @property
def callers_from_first_to_last(self): def callers_from_first_to_last(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment