Commit d05ff27d authored by Fred Drake's avatar Fred Drake

More micro-optimizations.

A (minor) change to the bytecode format: for "extended" attributes
(handled by TALInterpreter.attrAction()), the instruction contains the
integer previously retrieved from the actionIndex table; the bytecode
generator now takes care of the lookup.  This allows attrAction() to
no longer need to lookup the table that it used to lookup the
integers.

Many handlers are now separated into TAL and non-TAL versions -- the
TALInterpreter constructor determines which dispatch table to use when
it initializes self.tal (which is not changed later).  This allows
each of the handlers to do only what is needed without having to
decide based on the self.tal attribute.

TALInterpreter.do_startTag() no longer contains a special case for an
empty attribute list; that case is already optimized away by the
bytecode generator.  What remains would actually work in that case,
but it simply doesn't occur.  This removes one test of the attrList
parameter.
parent adac2175
...@@ -157,6 +157,8 @@ class TALGenerator: ...@@ -157,6 +157,8 @@ class TALGenerator:
collect = [] collect = []
return output return output
actionIndex = {"replace":0, "insert":1, "metal":2, "tal":3, "xmlns":4,
0: 0, 1: 1, 2: 2, 3: 3, 4: 4}
def optimizeStartTag(self, collect, name, attrlist, end): def optimizeStartTag(self, collect, name, attrlist, end):
if not attrlist: if not attrlist:
collect.append("<%s%s" % (name, end)) collect.append("<%s%s" % (name, end))
...@@ -167,6 +169,9 @@ class TALGenerator: ...@@ -167,6 +169,9 @@ class TALGenerator:
item = attrlist[i] item = attrlist[i]
if len(item) > 2: if len(item) > 2:
opt = 0 opt = 0
name, value, action = item[:3]
action = self.actionIndex[action]
attrlist[i] = (name, value, action) + item[3:]
else: else:
if item[1] is None: if item[1] is None:
s = item[0] s = item[0]
......
...@@ -170,6 +170,10 @@ class TALInterpreter: ...@@ -170,6 +170,10 @@ class TALInterpreter:
self.wrap = wrap self.wrap = wrap
self.metal = metal self.metal = metal
self.tal = tal self.tal = tal
if tal:
self.dispatch = self.bytecode_handlers_tal
else:
self.dispatch = self.bytecode_handlers
assert showtal in (-1, 0, 1) assert showtal in (-1, 0, 1)
if showtal == -1: if showtal == -1:
showtal = (not tal) showtal = (not tal)
...@@ -178,7 +182,9 @@ class TALInterpreter: ...@@ -178,7 +182,9 @@ class TALInterpreter:
self.stackLimit = stackLimit self.stackLimit = stackLimit
self.html = 0 self.html = 0
self.endsep = "/>" self.endsep = "/>"
self.endlen = len(self.endsep)
self.macroStack = [] self.macroStack = []
self.popMacro = self.macroStack.pop
self.position = None, None # (lineno, offset) self.position = None, None # (lineno, offset)
self.col = 0 self.col = 0
self.level = 0 self.level = 0
...@@ -229,7 +235,8 @@ class TALInterpreter: ...@@ -229,7 +235,8 @@ class TALInterpreter:
self._stream_write("\n") self._stream_write("\n")
self.col = 0 self.col = 0
def stream_write(self, s): def stream_write(self, s,
len=len, rfind=rfind):
self._stream_write(s) self._stream_write(s)
i = rfind(s, '\n') i = rfind(s, '\n')
if i < 0: if i < 0:
...@@ -241,7 +248,7 @@ class TALInterpreter: ...@@ -241,7 +248,7 @@ class TALInterpreter:
def interpret(self, program): def interpret(self, program):
self.level = self.level + 1 self.level = self.level + 1
handlers = self.bytecode_handlers handlers = self.dispatch
_apply = apply _apply = apply
_tuple = tuple _tuple = tuple
tup = (self,) tup = (self,)
...@@ -272,29 +279,29 @@ class TALInterpreter: ...@@ -272,29 +279,29 @@ class TALInterpreter:
self.endsep = " />" self.endsep = " />"
else: else:
self.endsep = "/>" self.endsep = "/>"
self.endlen = len(self.endsep)
bytecode_handlers["mode"] = do_mode bytecode_handlers["mode"] = do_mode
def do_setPosition(self, position): def do_setPosition(self, position):
self.position = position self.position = position
bytecode_handlers["setPosition"] = do_setPosition bytecode_handlers["setPosition"] = do_setPosition
def do_startEndTag(self, (name, attrList)): def do_startEndTag(self, stuff):
self.do_startTag((name, attrList), self.endsep) self.do_startTag(stuff, self.endsep, self.endlen)
bytecode_handlers["startEndTag"] = do_startEndTag bytecode_handlers["startEndTag"] = do_startEndTag
def do_startTag(self, (name, attrList), end=">"): def do_startTag(self, (name, attrList),
if not attrList: end=">", endlen=1, _len=len, _quote=quote):
s = "<%s%s" % (name, end) # The bytecode generator does not cause calls to this method
self._stream_write(s) # for start tags with no attributes; those are optimized down
self.col = self.col + len(s) # to rawtext events. Hence, there is no special "fast path"
return # for that case.
_len = len
_quote = quote
_stream_write = self._stream_write _stream_write = self._stream_write
_stream_write("<" + name) _stream_write("<" + name)
col = self.col + _len(name) + 1 namelen = _len(name)
col = self.col + namelen + 1
wrap = self.wrap wrap = self.wrap
align = col + 1 + _len(name) align = col + 1 + namelen
if align >= wrap/2: if align >= wrap/2:
align = 4 # Avoid a narrow column far to the right align = 4 # Avoid a narrow column far to the right
try: try:
...@@ -309,25 +316,24 @@ class TALInterpreter: ...@@ -309,25 +316,24 @@ class TALInterpreter:
s = name s = name
else: else:
s = "%s=%s" % (name, _quote(value)) s = "%s=%s" % (name, _quote(value))
slen = _len(s)
if (wrap and if (wrap and
col >= align and col >= align and
col + 1 + _len(s) > wrap): col + 1 + slen > wrap):
_stream_write("\n" + " "*align) _stream_write("\n" + " "*align)
col = align + _len(s) col = align + slen
else: else:
s = " " + s s = " " + s
col = col + 1 + _len(s) col = col + 1 + slen
_stream_write(s) _stream_write(s)
_stream_write(end) _stream_write(end)
col = col + _len(end) col = col + endlen
finally: finally:
self.col = col self.col = col
bytecode_handlers["startTag"] = do_startTag bytecode_handlers["startTag"] = do_startTag
actionIndex = {"replace":0, "insert":1, "metal":2, "tal":3, "xmlns":4}
def attrAction(self, item): def attrAction(self, item):
name, value = item[:2] name, value, action = item[:3]
action = self.actionIndex[item[2]]
if not self.showtal and action > 1: if not self.showtal and action > 1:
return 0, name, value return 0, name, value
ok = 1 ok = 1
...@@ -349,7 +355,6 @@ class TALInterpreter: ...@@ -349,7 +355,6 @@ class TALInterpreter:
else: else:
if evalue is None: if evalue is None:
ok = 0 ok = 0
else:
value = evalue value = evalue
elif action == 2 and self.metal: elif action == 2 and self.metal:
i = rfind(name, ":") + 1 i = rfind(name, ":") + 1
...@@ -387,12 +392,13 @@ class TALInterpreter: ...@@ -387,12 +392,13 @@ class TALInterpreter:
sys.stderr.write("+--------------------------------------\n") sys.stderr.write("+--------------------------------------\n")
def do_beginScope(self, dict): def do_beginScope(self, dict):
if self.tal: self.engine.beginScope()
self.scopeLevel = self.scopeLevel + 1
def do_beginScope_tal(self, dict):
engine = self.engine engine = self.engine
engine.beginScope() engine.beginScope()
engine.setLocal("attrs", dict) engine.setLocal("attrs", dict)
else:
self.engine.beginScope()
self.scopeLevel = self.scopeLevel + 1 self.scopeLevel = self.scopeLevel + 1
bytecode_handlers["beginScope"] = do_beginScope bytecode_handlers["beginScope"] = do_beginScope
...@@ -401,22 +407,23 @@ class TALInterpreter: ...@@ -401,22 +407,23 @@ class TALInterpreter:
self.scopeLevel = self.scopeLevel - 1 self.scopeLevel = self.scopeLevel - 1
bytecode_handlers["endScope"] = do_endScope bytecode_handlers["endScope"] = do_endScope
def do_setLocal(self, (name, expr)): def do_setLocal(self, junk):
if self.tal: pass
def do_setLocal_tal(self, (name, expr)):
value = self.engine.evaluateValue(expr) value = self.engine.evaluateValue(expr)
self.engine.setLocal(name, value) self.engine.setLocal(name, value)
bytecode_handlers["setLocal"] = do_setLocal bytecode_handlers["setLocal"] = do_setLocal
def do_setGlobal(self, (name, expr)): def do_setGlobal_tal(self, (name, expr)):
if self.tal:
value = self.engine.evaluateValue(expr) value = self.engine.evaluateValue(expr)
self.engine.setGlobal(name, value) self.engine.setGlobal(name, value)
bytecode_handlers["setGlobal"] = do_setGlobal bytecode_handlers["setGlobal"] = do_setLocal
def do_insertText(self, (expr, block)): def do_insertText(self, (expr, block)):
if not self.tal:
self.interpret(block) self.interpret(block)
return
def do_insertText_tal(self, (expr, block)):
text = self.engine.evaluateText(expr) text = self.engine.evaluateText(expr)
if text is None: if text is None:
return return
...@@ -469,9 +476,9 @@ class TALInterpreter: ...@@ -469,9 +476,9 @@ class TALInterpreter:
self.interpret(program) self.interpret(program)
def do_loop(self, (name, expr, block)): def do_loop(self, (name, expr, block)):
if not self.tal:
self.interpret(block) self.interpret(block)
return
def do_loop_tal(self, (name, expr, block)):
iterator = self.engine.setRepeat(name, expr) iterator = self.engine.setRepeat(name, expr)
while iterator.next(): while iterator.next():
self.interpret(block) self.interpret(block)
...@@ -548,9 +555,9 @@ class TALInterpreter: ...@@ -548,9 +555,9 @@ class TALInterpreter:
bytecode_handlers["defineSlot"] = do_defineSlot bytecode_handlers["defineSlot"] = do_defineSlot
def do_onError(self, (block, handler)): def do_onError(self, (block, handler)):
if not self.tal:
self.interpret(block) self.interpret(block)
return
def do_onError_tal(self, (block, handler)):
state = self.saveState() state = self.saveState()
self.stream = stream = StringIO() self.stream = stream = StringIO()
self._stream_write = stream.write self._stream_write = stream.write
...@@ -569,6 +576,14 @@ class TALInterpreter: ...@@ -569,6 +576,14 @@ class TALInterpreter:
self.stream_write(stream.getvalue()) self.stream_write(stream.getvalue())
bytecode_handlers["onError"] = do_onError bytecode_handlers["onError"] = do_onError
bytecode_handlers_tal = bytecode_handlers.copy()
bytecode_handlers_tal["beginScope"] = do_beginScope_tal
bytecode_handlers_tal["setLocal"] = do_setLocal_tal
bytecode_handlers_tal["setGlobal"] = do_setGlobal_tal
bytecode_handlers_tal["insertText"] = do_insertText_tal
bytecode_handlers_tal["loop"] = do_loop_tal
bytecode_handlers_tal["onError"] = do_onError_tal
def test(): def test():
from driver import FILE, parsefile from driver import FILE, parsefile
......
This diff is collapsed.
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