Changeset 41
- Timestamp:
- 12/20/04 21:43:48
- Files:
-
- trunk/codewalk.py (modified) (17 diffs)
- trunk/logic.py (modified) (1 diff)
- trunk/storage/storepypgsql.py (modified) (1 diff)
- trunk/test_codewalk.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/codewalk.py
r40 r41 96 96 if k not in ('BINARY_SUBSCR',) 97 97 ]) 98 del k, v99 98 100 99 binary_repr = {'BINARY_POWER': '**', … … 132 131 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 133 132 'co_stacksize', 'co_varnames'] 133 134 134 135 135 class Visitor(object): … … 425 425 426 426 def pop(self, index=-1): 427 """ Returns a tuple!! of (value, tainted)."""427 """pop(index) -> Returns a tuple!! of (value, tainted).""" 428 428 if index < 0: 429 429 index += len(self) … … 441 441 442 442 class EarlyBinder(Rewriter): 443 """EarlyBinder(func, reduce_getattr=True, taintlist=[])443 """EarlyBinder(func, reduce_getattr=True, bind_late=[]) 444 444 445 445 Deep-evaluate function, replacing free vars with constants. … … 448 448 replaced with x.y where possible. 449 449 450 taintlist: a list of names (globals, freevars, or attributes)450 bind_late: a list of names (globals, freevars, or attributes) 451 451 which should not be early-bound. For example, if you want 452 452 datetime.date.today() to be bound late, include 'today' 453 in the taintlist.453 in the bind_late. 454 454 455 455 Example: k = lambda x: x.Date == datetime.date(2004, 1, 1) … … 483 483 """ 484 484 485 def __init__(self, func, reduce_getattr=True, taintlist=[]):485 def __init__(self, func, reduce_getattr=True, bind_late=[]): 486 486 Rewriter.__init__(self, func) 487 487 self.reduce_getattr = reduce_getattr … … 498 498 # This stack is not passed out of this class in any way. 499 499 self.stack = TaintableStack() 500 self. taintlist = taintlist500 self.bind_late = bind_late 501 501 502 502 def code_object(self): … … 530 530 531 531 def reduce(self, number_of_terms, transform=None, overwrite_length=None): 532 """If no stack args are t ainted, rewrite previous opcodes.532 """If no stack args are to be bound late, rewrite previous opcodes. 533 533 534 534 number_of_terms: the number of terms to pop off the stack. … … 560 560 # tainted, because CALL_FUNCTION will do it normally. 561 561 if self.reduce_getattr: 562 if (len(terms) == 3 563 and terms[2] == getattr 564 and taints[1] 565 and not taints[0]): 566 # Form a new LOAD_ATTR instruction. 567 pos = self.name_index(terms[0]) 568 # Unlike normal CALL_FUNCTION, we can't 569 # assume each arg is a constant; therefore, 570 # our overwrite_length is indeterminate. 571 # We'll just cheat and keep track of the last 572 # LOAD_GLOBAL where we looked up getattr. ;) 573 start = self.last_getattr 574 # Grab and reuse opcodes of first (LOAD_FAST) term. 575 bits = self.newcode[start + 3:-6] 576 bits += ['LOAD_ATTR', pos & 0xFF, pos >> 8] 577 bits = tuple(bits) 578 self.put(start, len(self.newcode), *bits) 579 self.stack.append(None) 580 self.stack.taint() 581 return True 562 if (len(terms) == 3 and terms[2] == getattr 563 and taints[1] and not taints[0]): 564 # Form a new LOAD_ATTR instruction. 565 pos = self.name_index(terms[0]) 566 # Unlike normal CALL_FUNCTION, we can't assume each arg 567 # is a constant; therefore, our overwrite_length is 568 # indeterminate. We'll just cheat and keep track of 569 # the last LOAD_GLOBAL where we looked up getattr. ;) 570 start = self.last_getattr 571 # Grab and reuse opcodes of first (LOAD_FAST) term. 572 bits = self.newcode[start + 3:-6] 573 bits += ['LOAD_ATTR', pos & 0xFF, pos >> 8] 574 bits = tuple(bits) 575 self.put(start, len(self.newcode), *bits) 576 self.stack.append(None) 577 self.stack.taint() 578 return None 582 579 583 580 # Don't form the new object. … … 585 582 self.stack.append(None) 586 583 self.stack.taint() 587 return False584 return None 588 585 589 586 # Callback the transform. … … 600 597 pos = self.const_index(result) 601 598 self.tail(overwrite_length, 'LOAD_CONST', pos & 0xFF, pos >> 8) 602 return True 599 600 return result 603 601 604 602 def visit_BUILD_TUPLE(self, lo, hi): … … 627 625 def visit_LOAD_ATTR(self, lo, hi): 628 626 name = self.co_names[lo + (hi << 8)] 629 self.reduce(1, lambda terms: getattr(terms[0], name))630 if name in self.taintlist:627 result = self.reduce(1, lambda terms: getattr(terms[0], name)) 628 if result in self.bind_late or getattr(result, 'bind_late', False): 631 629 self.stack.taint() 632 630 … … 642 640 self.tail(3, 'LOAD_CONST', pos & 0xFF, pos >> 8) 643 641 self.stack.append(value) 644 if name in self.taintlist:642 if value in self.bind_late or getattr(value, 'bind_late', False): 645 643 self.stack.taint() 646 644 647 645 def visit_LOAD_FAST(self, lo, hi): 648 646 self.stack.append(self.co_varnames[lo + (hi << 8)]) 647 # LOAD_FAST references our bound variable, which is always bound late. 649 648 self.stack.taint() 650 649 … … 658 657 self.tail(3, 'LOAD_CONST', pos & 0xFF, pos >> 8) 659 658 self.stack.append(value) 660 if name in self.taintlist:659 if value in self.bind_late or getattr(value, 'bind_late', False): 661 660 self.stack.taint() 662 661 else: … … 681 680 682 681 # Add visit_BINARY, visit_INPLACE methods to EarlyBinder. 683 for k ey, opdin binary_operators.iteritems():684 setattr(EarlyBinder, "visit_" + k ey,685 lambda self, opr= opd: self.binary_op(opr))686 for k ey, opdin inplace_operators.iteritems():687 setattr(EarlyBinder, "visit_" + k ey,682 for k, v in binary_operators.iteritems(): 683 setattr(EarlyBinder, "visit_" + k, 684 lambda self, opr=v: self.binary_op(opr)) 685 for k, v in inplace_operators.iteritems(): 686 setattr(EarlyBinder, "visit_" + k, 688 687 # Yes, we really do call binary_op for inplace methods. 689 lambda self, opr=opd: self.binary_op(opr)) 690 del key, opd 688 lambda self, opr=v: self.binary_op(opr)) 691 689 692 690 … … 886 884 887 885 # Add visit_BINARY methods to LambdaDecompiler. 888 for k ey, opin binary_repr.iteritems():889 setattr(LambdaDecompiler, "visit_" + k ey,890 lambda self, op= op: self.binary_op(op))886 for k, v in binary_repr.iteritems(): 887 setattr(LambdaDecompiler, "visit_" + k, 888 lambda self, op=v: self.binary_op(op)) 891 889 892 890 … … 964 962 self.flag = None 965 963 964 del k, v 965 trunk/logic.py
r40 r41 234 234 def _load_func(self, func): 235 235 # Early-bind as much as possible. 236 binder = codewalk.EarlyBinder(func, taintlist=['now', 'today', 237 'iscurrentweek']) 236 binder = codewalk.EarlyBinder(func, bind_late=[datetime.datetime.now, datetime.date.today]) 238 237 self.func = binder.function() 239 238 trunk/storage/storepypgsql.py
r40 r41 471 471 def coerce_datetime_date(self, cls, key): return u"date" 472 472 def coerce_datetime_time(self, cls, key): return u"time" 473 def coerce_datetime_timedelta(self, cls, key): return u"interval" 473 474 def coerce_datetime_timedelta(self, cls, key): 475 # I was seriously disinterested in writing a parser for interval. 476 return u"float8" 477 474 478 def coerce_decimal(self, cls, key): return u"numeric" 475 479 trunk/test_codewalk.py
r40 r41 147 147 # Test a tainted (late-bound) function 148 148 e = lambda x: x.FirstDate >= datetime.date.today() 149 r = codewalk.EarlyBinder(e, taintlist=['today']).bytecode()149 r = codewalk.EarlyBinder(e, bind_late=[datetime.date.today]).bytecode() 150 150 self.assertEqual(r, nums(['LOAD_FAST', 0, 0, 151 151 'LOAD_ATTR', 1, 0,
