def __init__(self, argument_type, break_types): self.argument_type = argument_type self.break_types = break_types if argument_type is None: raise FatalError() if break_types is None: raise FatalError()
def __init__(self, types): if len(types) <= 1: raise FatalError() self.types = types for t in types: if isinstance(t, OneOfType): raise FatalError()
def merge_types(types, mode): if mode not in ("exact", "super", "sub"): raise FatalError() for t in types: if not isinstance(t, Type): raise FatalError() to_drop = [] for i1, t1 in enumerate(types): for i2, t2 in enumerate(types): if i1 > i2 and t1.is_copyable_from( t2, DUMMY_REASONER) and t2.is_copyable_from( t1, DUMMY_REASONER): to_drop.append(i1) types = [t for i, t in enumerate(types) if i not in to_drop] to_drop = [] if mode != "exact": for i1, t1 in enumerate(types): for i2, t2 in enumerate(types): if i1 != i2 and t1.is_copyable_from(t2, DUMMY_REASONER): if mode == "super": to_drop.append(i2) elif mode == "sub": to_drop.append(i1) types = [t for i, t in enumerate(types) if i not in to_drop] if len(types) == 0: return NoValueType() elif len(types) == 1: return types[0] else: return OneOfType(types)
def __exit__(self, exc_type, exc_value, traceback): if isinstance(exc_value, FatalError): return if get_environment().validate_flow_control and isinstance( exc_value, BreakException) and self.target.break_types is not None: # Verifies that execution is leaving the target opcode at run-time in a way that was forecast # at verification time. break_types = self.target.break_types.get( exc_value.mode, []) + self.target.break_types.get("*", []) # if break_types is MISSING: # raise FatalError("Can not unwind {}: {}, target {} allowed {}".format(exc_value.mode, exc_value.value, self.target, break_types)) failures = [] reasoner = Reasoner() for allowed_break_type in break_types: allowed_out = allowed_break_type["out"] allowed_in = allowed_break_type.get("in", None) out_is_compatible = does_value_fit_through_type( exc_value.value, allowed_out, reasoner=reasoner) in_is_compatible = allowed_in is None or ( exc_value.restart_type is not None and exc_value.restart_type.is_copyable_from( allowed_in, reasoner)) if not out_is_compatible: failures.append(out_is_compatible) if out_is_compatible and in_is_compatible: break else: raise FatalError( "Can not unwind {} {}, target {}, allowed {}: {}".format( exc_value.mode, exc_value.value, self.target, break_types, reasoner.to_message())) exc_type_allows_restart = exc_value and isinstance( exc_value, BreakException) and exc_value.restart_type is not None if get_environment( ).validate_flow_control and self.manager.mode == "reset": raise FatalError() if not exc_type_allows_restart and self.manager.fully_wound(): self.manager.pop_frame() else: if get_environment( ).validate_flow_control and exc_type_allows_restart: if self.manager.mode not in ("shift", ): raise FatalError() self.manager.index -= 1
def detach_type(self, remove_type, multiplier=1): remove_type_id = id(remove_type) if remove_type_id not in self.attached_type_counts: raise FatalError() self.attached_type_counts[remove_type_id] -= multiplier if self.attached_type_counts[remove_type_id] < 0: raise FatalError() if self.attached_type_counts[remove_type_id] == 0: self.cached_effective_composite_type = None del self.attached_types[remove_type_id] del self.attached_type_counts[remove_type_id]
def chain(self, other, debug_info): can_merge_code_blocks = True if other is None: return self if other.argument_type_expression is not MISSING: # If the inner function needs an argument, we have no mechanism to provide it raise FatalError() if other.breaks_types is not MISSING: # The newly created function ignores other.breaks_types, so let's fail early if they're provided raise FatalError() if self.local_variable_type is not MISSING and other.local_variable_type is not MISSING: # We can only take local variables from one of the two functions can_merge_code_blocks = False if self.code_expressions is not MISSING and other.local_variable_type is not MISSING: # We have code that should execute before the other functions local variables are declared can_merge_code_blocks = False if self.extra_statics is not MISSING and other.extra_statics is not MISSING: # We can only take extra statics from one of the two functions can_merge_code_blocks = False if self.extra_statics is not MISSING and other.local_variable_type is not MISSING: # The inner local_variable_type might reference something from statics can_merge_code_blocks = False new_code_expressions = None our_code_expressions = default(self.code_expressions, MISSING, []) other_code_expressions = default(other.code_expressions, MISSING, []) if can_merge_code_blocks: new_code_expressions = our_code_expressions + other_code_expressions local_variable_type = default(self.local_variable_type, MISSING, other.local_variable_type) local_initializer = default(self.local_initializer, MISSING, other.local_initializer) extra_statics = default(self.extra_statics, MISSING, other.extra_statics) else: new_code_expressions = our_code_expressions + [ other.create("expression", debug_info) ] local_variable_type = self.local_variable_type local_initializer = self.local_initializer extra_statics = self.extra_statics return CodeBlockBuilder( code_expressions=new_code_expressions, local_variable_type=local_variable_type, local_initializer=local_initializer, extra_statics=extra_statics, argument_type_expression=self.argument_type_expression, breaks_types=self.breaks_types)
def __init__(self, micro_op_types, name, delegate=None): if not isinstance(micro_op_types, dict): raise FatalError() for tag in micro_op_types.keys(): if not isinstance(tag, tuple): raise FatalError() self._micro_op_types = micro_op_types self.name = name self._is_self_consistent = None self.is_copyable_cache = WeakIdentityKeyDictionary() self.delegate = delegate
def add(self, mode, out_type, in_type=None, opcode=None): if not isinstance(out_type, Type): raise FatalError() if in_type and not isinstance(in_type, Type): raise FatalError() if opcode: out_type.from_opcode = opcode break_type = { "out": out_type, } if in_type: break_type["in"] = in_type self.result[mode].append(break_type)
def __enter__(self): if self.manager.fully_wound(): if self.manager.mode not in ("wind", "reset"): raise FatalError() self.manager.mode = "wind" self.manager.frames.append(self) else: if self.manager.mode not in ("reset", "shift"): raise FatalError() self.manager.mode = "reset" self.manager.index += 1 return self
def function_lit(*args, **kwargs): if len(args) == 1: extra_statics = {} code, = args argument_type = local_type = no_value_type() break_types = infer_all() local_initializer = nop() elif len(args) == 2: extra_statics = {} argument_type, code = args local_type = no_value_type() break_types = infer_all() local_initializer = nop() elif len(args) == 3: extra_statics = {} argument_type, break_types, code = args local_type = no_value_type() local_initializer = nop() elif len(args) == 5: extra_statics = {} argument_type, break_types, local_type, local_initializer, code = args elif len(args) == 6: extra_statics, argument_type, break_types, local_type, local_initializer, code = args else: raise FatalError() check_is_opcode(local_initializer) check_is_opcode(argument_type) check_is_opcode(local_type) check_is_opcode(code) if not isinstance(break_types, dict): raise FatalError() static = spread_dict( { "argument": argument_type, "local": local_type, "outer": inferred_type(), "break_types": object_template_op(break_types) }, extra_statics) func = spread_dict( { "code": code, "static": object_template_op(static), "local_initializer": local_initializer }, **kwargs) return PythonObject(func)
def get_next_frame(self, thing): if self.fully_wound(): if get_environment().frame_shortcut and not is_restartable(thing): return PASS_THROUGH_FRAME if self.mode != "wind": raise FatalError() return Frame(self, thing) else: if self.mode not in ("shift", "reset"): raise FatalError() old_frame = self.frames[self.index] if old_frame.target is not thing: raise FatalError() return old_frame
def pop_restart_value(self): if not self.has_restart_value(): raise FatalError() restart_value = self.restart_value self.restart_value = MISSING self.manager.mode = "wind" return restart_value
def build(self): result = dict(self.result) if self.target: if getattr(self.target, "break_types", None) is not None: raise FatalError() self.target.break_types = result return result
def __init__(self, frame_manager, frames, callback, restart_type, break_types): if not isinstance(frame_manager, FrameManager): raise FatalError() self.frame_manager = frame_manager self.frames = frames self.callback = callback self.restart_type = restart_type self.break_types = break_types for frame in frames: if frame.has_restart_value(): raise FatalError() if break_types is None: raise FatalError()
def prepare_piece_of_context(declared_type, suggested_type): if suggested_type and not isinstance(suggested_type, Type): raise FatalError() final_type = prepare_lhs_type(declared_type, suggested_type) if not check_dangling_inferred_types(final_type, {}): raise PreparationException("Invalid inferred types") is_piece_self_consistent_reasoner = Reasoner() if isinstance( final_type, CompositeType ) and not final_type.is_self_consistent(is_piece_self_consistent_reasoner): raise FatalError(is_piece_self_consistent_reasoner.to_message()) return final_type
def is_copyable_to(self, other, reasoner): if isinstance(other, OneOfType): raise FatalError() for t in self.types: if not other.is_copyable_from(t, reasoner): reasoner.push_not_copyable_type(other, t) return False return True
def invoke(self, restart_value, frame_manager): if not self.restart_type.is_copyable_from( get_type_of_value(restart_value), DUMMY_REASONER): raise FatalError() self.restarted = True if self.frame_manager.fully_wound(): self.frame_manager.prepare_restart(self.frames, restart_value) return self.callback()
def prepare_restart(self, frames, restart_value): self.mode = "reset" self.frames = self.frames + [f.clone() for f in frames] for f in self.frames: if f.has_restart_value(): raise FatalError() self.frames[-1].restart_value = restart_value
def slice_frames(self): if self.fully_wound(): raise FatalError() self.mode = "wind" sliced_frames = self.frames[self.index:] self.frames = self.frames[:self.index] return sliced_frames
def unbound_dereference(name, **kwargs): if not isinstance(name, basestring): raise FatalError() return PythonObject(spread_dict( { "opcode": "unbound_dereference", "reference": name }, **kwargs), debug_reason="code")
def dynamic_dereference_op(reference, **kwargs): if not isinstance(reference, basestring): raise FatalError() return PythonObject(spread_dict( { "opcode": "dynamic_dereference", "reference": reference }, **kwargs), debug_reason="code")
def wrap_as_statement(node): """ Python ast treats statements and expressions differently. Always returns a ast.stmt object, even if passed an ast.expr. """ if isinstance(node, ast.stmt): return node if isinstance(node, ast.expr): return ast.Expr(node) raise FatalError()
def environment(rtti=MISSING, frame_shortcut=MISSING, validate_flow_control=MISSING, opcode_bindings=MISSING, consume_python_objects=MISSING, return_value_optimization=MISSING, transpile=MISSING, base=False): environment = get_environment() if (environment and environment.transpile or transpile is True ) and not (environment and environment.return_value_optimization or return_value_optimization is True): raise FatalError("Transpiling has return_value_optomization baked in") if base: global environment_stack if environment_stack is not None: raise FatalError() environment_stack = [] new_environment = Environment( rtti=rtti, frame_shortcut=frame_shortcut, validate_flow_control=validate_flow_control, opcode_bindings=opcode_bindings, consume_python_objects=consume_python_objects, return_value_optimization=return_value_optimization, transpile=transpile) else: new_environment = get_environment().clone( rtti=rtti, frame_shortcut=frame_shortcut, validate_flow_control=validate_flow_control, opcode_bindings=opcode_bindings, consume_python_objects=consume_python_objects, return_value_optimization=return_value_optimization, transpile=transpile) environment_stack.append(new_environment) try: yield new_environment finally: environment_stack.pop()
def unwrap_expr(node): """ Returns an ast.expr from a statement, but throws an exception if the statement can not be converted to an expression. """ if isinstance(node, ast.Expr): return node.value if isinstance(node, ast.expr): return node raise FatalError()
def function_type(argument_type, break_types): if not isinstance(break_types, dict): raise FatalError() return object_template_op( { "type": literal_op("Function"), "argument": argument_type, "break_types": object_template_op(break_types) }, debug_reason="type-literal")
def unbound_assignment(name, rvalue): if not isinstance(name, basestring): raise FatalError() check_is_opcode(rvalue) return PythonObject( { "opcode": "unbound_assignment", "reference": name, "rvalue": rvalue }, debug_reason="code")
def build_closed_function_type(data): if not isinstance(data._get("break_types"), Universal): raise FatalError() for mode, break_types in data._get("break_types")._items(): if not isinstance(mode, basestring): raise FatalError() if not isinstance(break_types, Universal): raise FatalError() for break_type in break_types._values(): if not isinstance(break_type, Universal): raise FatalError(break_type) return ClosedFunctionType( enrich_type(data._get("argument")), PythonDict( { mode: PythonList( [enrich_break_type(b) for b in break_types._values()]) for mode, break_types in data._get("break_types")._items() }, bind=DEFAULT_READONLY_COMPOSITE_TYPE, debug_reason="type"))
def transform_op(*args, **kwargs): if len(args) == 1: output, = args input = code = None elif len(args) == 3: input, output, code = args else: raise FatalError() if code: check_is_opcode(code) if input and not isinstance(input, basestring): raise FatalError() if not isinstance(output, basestring): raise FatalError() op = { "opcode": "transform", "output": output, } if input: op["input"] = input op["code"] = code return PythonObject(spread_dict(op, **kwargs), debug_reason="code")
def enrich_type(data): from lockdown.type_system.managers import get_manager if not isinstance(data, Universal): raise PreparationException("Unknown type data {}, {}".format( data, type(data))) if not data._contains("type"): raise PreparationException("Missing type in data {}".format(data)) if data._get("type") not in TYPES: raise PreparationException("Unknown type {}".format(data.type)) new_type = TYPES[data._get("type")](data) if not isinstance(new_type, Type): raise FatalError(data) return new_type
def __init__(self, data, code, prepare_context, static, argument_type, outer_type, local_type, local_initializer, break_types): self.data = data self.code = code self.prepare_context = prepare_context self.static = static self.argument_type = argument_type self.outer_type = outer_type self.local_type = local_type self.local_initializer = local_initializer self.break_types = break_types self.types_context = Universal( True, initial_wrapped={ "outer": self.outer_type, "argument": self.argument_type }, debug_reason="local-initialization-context") self.local_initialization_context_type = UniversalObjectType( { "outer": self.outer_type, "argument": self.argument_type, # "types": readonly_rich_composite_type }, wildcard_type=AnyType(), name="local-initialization-context-type") self.execution_context_type = UniversalObjectType( { # "prepare": readonly_rich_composite_type, "outer": self.outer_type, "argument": self.argument_type, # "static": readonly_rich_composite_type, "local": self.local_type, # "types": readonly_rich_composite_type }, wildcard_type=AnyType(), name="code-execution-context-type") if not self.execution_context_type.is_self_consistent(DUMMY_REASONER): raise FatalError() self.compiled_ast = None