def find_key_in_heap( self, ref: z3.ExprRef, typ: Type, proxy_generator: Callable[[Type], object], snapshot: SnapshotRef = SnapshotRef(-1) ) -> object: with self.framework(): for (curref, curtyp, curval) in itertools.chain(*self.heaps[snapshot:]): could_match = dynamic_typing.unify(curtyp, typ) if not could_match: continue if self.smt_fork(curref == ref): debug('HEAP key lookup ', ref, ': Found existing. ', 'type:', name_of_type(type(curval)), 'id:', id(curval) % 1000) return curval ret = proxy_generator(typ) debug('HEAP key lookup ', ref, ': Created new. ', 'type:', name_of_type(type(ret)), 'id:', id(ret) % 1000) #assert dynamic_typing.unify(python_type(ret), typ), 'proxy type was {} and type required was {}'.format(type(ret), typ) self.add_value_to_heaps(ref, typ, ret) return ret
def describe(self, include_postexec=False) -> str: ret = "" if self.exc: exc = self.exc exc_type = name_of_type(type(exc)) tb = test_stack(exc.__traceback__) ret = f"exc={exc_type}: {str(exc)} {tb}" else: ret = f"ret={self.ret!r}" if include_postexec: a = [repr(a) for a in self.post_args] a += [f"{k}={v!r}" for k, v in self.post_kwargs.items()] ret += f' post=({", ".join(a)})' return ret
def find_key_in_heap( self, ref: z3.ExprRef, typ: Type, proxy_generator: Callable[[Type], object], snapshot: SnapshotRef = SnapshotRef(-1), ) -> object: with NoTracing(): for (curref, curtyp, curval) in itertools.chain(*self.heaps[snapshot:]): could_match = dynamic_typing.unify(curtyp, typ) if not could_match: continue if self.smt_fork(curref == ref): debug( "HEAP key lookup ", ref, ": Found existing. ", "type:", name_of_type(type(curval)), "id:", id(curval) % 1000, ) return curval ret = proxy_generator(typ) debug( "HEAP key lookup ", ref, ": Created new. ", "type:", name_of_type(type(ret)), "id:", id(ret) % 1000, ) self.add_value_to_heaps(ref, typ, ret) return ret
def get_smt_proxy_type(cls: type) -> type: if issubclass(cls, SmtProxyMarker): return cls global _SMT_PROXY_TYPES cls_name = name_of_type(cls) if cls not in _SMT_PROXY_TYPES: def symbolic_init(self): self.__class__ = cls class_body = { '__init__': symbolic_init } try: proxy_cls = type(cls_name + '_proxy', (SmtProxyMarker, cls), class_body) except TypeError as e: if 'is not an acceptable base type' in str(e): raise CrosshairUnsupported(f'Cannot subclass {cls_name}') else: raise _SMT_PROXY_TYPES[cls] = proxy_cls return _SMT_PROXY_TYPES[cls]
def attempt_call(conditions: Conditions, fn: Callable, short_circuit: ShortCircuitingContext, enforced_conditions: EnforcedConditions) -> CallAnalysis: space = context_statespace() bound_args = gen_args(conditions.sig) msg_gen = MessageGenerator(fn) with space.framework(): # TODO: looks wrong(-ish) to guard this with space.framework(). # Copy on custom objects may require patched builtins. (datetime.timedelta is one such case) original_args = copy.deepcopy(bound_args) space.checkpoint() lcls: Mapping[str, object] = bound_args.arguments # In preconditions, __old__ exists but is just bound to the same args. # This lets people write class invariants using `__old__` to, for example, # demonstrate immutability. lcls = {'__old__': AttributeHolder(lcls), **lcls} expected_exceptions = conditions.raises for precondition in conditions.pre: with ExceptionFilter(expected_exceptions) as efilter: with enforced_conditions.enabled_enforcement(), short_circuit: precondition_ok = precondition.evaluate(lcls) if not precondition_ok: debug('Failed to meet precondition', precondition.expr_source) return CallAnalysis(failing_precondition=precondition) if efilter.ignore: debug('Ignored exception in precondition.', efilter.analysis) return efilter.analysis elif efilter.user_exc is not None: (user_exc, tb) = efilter.user_exc debug('Exception attempting to meet precondition', precondition.expr_source, ':', user_exc, tb.format()) return CallAnalysis( failing_precondition=precondition, failing_precondition_reason= f'it raised "{repr(user_exc)} at {tb.format()[-1]}"') with ExceptionFilter(expected_exceptions) as efilter: with enforced_conditions.enabled_enforcement(), short_circuit: assert not space.running_framework_code __return__ = fn(*bound_args.args, **bound_args.kwargs) lcls = { **bound_args.arguments, '__return__': __return__, '_': __return__, '__old__': AttributeHolder(original_args.arguments), fn.__name__: fn } if efilter.ignore: debug('Ignored exception in function.', efilter.analysis) return efilter.analysis elif efilter.user_exc is not None: space.check_deferred_assumptions() (e, tb) = efilter.user_exc detail = name_of_type(type(e)) + ': ' + str(e) frame_filename, frame_lineno = frame_summary_for_fn(tb, fn) debug('exception while evaluating function body:', detail, frame_filename, 'line', frame_lineno) detail += ' ' + get_input_description(fn.__name__, original_args, _MISSING) return CallAnalysis(VerificationStatus.REFUTED, [ msg_gen.make(MessageType.EXEC_ERR, detail, frame_filename, frame_lineno, ''.join(tb.format())) ]) for argname, argval in bound_args.arguments.items(): if (conditions.mutable_args is not None and argname not in conditions.mutable_args): old_val, new_val = original_args.arguments[argname], argval if not deep_eq(old_val, new_val, set()): space.check_deferred_assumptions() detail = 'Argument "{}" is not marked as mutable, but changed from {} to {}'.format( argname, old_val, new_val) debug('Mutablity problem:', detail) return CallAnalysis( VerificationStatus.REFUTED, [msg_gen.make(MessageType.POST_ERR, detail, None, 0, '')]) (post_condition, ) = conditions.post with ExceptionFilter(expected_exceptions) as efilter: # TODO: re-enable post-condition short circuiting. This will require refactoring how # enforced conditions and short curcuiting interact, so that post-conditions are # selectively run when, and only when, performing a short circuit. #with enforced_conditions.enabled_enforcement(), short_circuit: isok = bool(post_condition.evaluate(lcls)) if efilter.ignore: debug('Ignored exception in postcondition.', efilter.analysis) return efilter.analysis elif efilter.user_exc is not None: space.check_deferred_assumptions() (e, tb) = efilter.user_exc detail = repr(e) + ' ' + get_input_description( fn.__name__, original_args, __return__, post_condition.addl_context) debug('exception while calling postcondition:', detail) failures = [ msg_gen.make(MessageType.POST_ERR, detail, post_condition.filename, post_condition.line, ''.join(tb.format())) ] return CallAnalysis(VerificationStatus.REFUTED, failures) if isok: debug('Postcondition confirmed.') return CallAnalysis(VerificationStatus.CONFIRMED) else: space.check_deferred_assumptions() detail = 'false ' + \ get_input_description( fn.__name__, original_args, __return__, post_condition.addl_context) debug(detail) failures = [ msg_gen.make(MessageType.POST_FAIL, detail, post_condition.filename, post_condition.line, '') ] return CallAnalysis(VerificationStatus.REFUTED, failures)