def add_non_ext_class_attr(builder: IRBuilder, non_ext: NonExtClassInfo, lvalue: NameExpr, stmt: AssignmentStmt, cdef: ClassDef, attr_to_cache: List[Tuple[Lvalue, RType]]) -> None: """Add a class attribute to __annotations__ of a non-extension class. If the attribute is initialized with a value, also add it to __dict__. """ # We populate __annotations__ because dataclasses uses it to determine # which attributes to compute on. # TODO: Maybe generate more precise types for annotations key = builder.load_str(lvalue.name) typ = builder.add(LoadAddress(type_object_op.type, type_object_op.src, stmt.line)) builder.call_c(dict_set_item_op, [non_ext.anns, key, typ], stmt.line) # Only add the attribute to the __dict__ if the assignment is of the form: # x: type = value (don't add attributes of the form 'x: type' to the __dict__). if not isinstance(stmt.rvalue, TempNode): rvalue = builder.accept(stmt.rvalue) builder.add_to_non_ext_dict(non_ext, lvalue.name, rvalue, stmt.line) # We cache enum attributes to speed up enum attribute lookup since they # are final. if ( cdef.info.bases and cdef.info.bases[0].type.fullname == 'enum.Enum' # Skip "_order_" and "__order__", since Enum will remove it and lvalue.name not in ('_order_', '__order__') ): # Enum values are always boxed, so use object_rprimitive. attr_to_cache.append((lvalue, object_rprimitive))
def transform_decorator(builder: IRBuilder, dec: Decorator) -> None: func_ir, func_reg = gen_func_item( builder, dec.func, dec.func.name, builder.mapper.fdef_to_sig(dec.func) ) if dec.func in builder.nested_fitems: assert func_reg is not None decorated_func = load_decorated_func(builder, dec.func, func_reg) builder.assign(get_func_target(builder, dec.func), decorated_func, dec.func.line) func_reg = decorated_func else: # Obtain the the function name in order to construct the name of the helper function. name = dec.func.fullname.split('.')[-1] helper_name = decorator_helper_name(name) # Load the callable object representing the non-decorated function, and decorate it. orig_func = builder.load_global_str(helper_name, dec.line) decorated_func = load_decorated_func(builder, dec.func, orig_func) # Set the callable object representing the decorated function as a global. builder.call_c(dict_set_item_op, [builder.load_globals_dict(), builder.load_static_unicode(dec.func.name), decorated_func], decorated_func.line) builder.functions.append(func_ir)
def dict_methods_fast_path( builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Optional[Value]: """Specialize a common case when list() is called on a dictionary view method call. For example: foo = list(bar.keys()) """ if not (len(expr.args) == 1 and expr.arg_kinds == [ARG_POS]): return None arg = expr.args[0] if not (isinstance(arg, CallExpr) and not arg.args and isinstance(arg.callee, MemberExpr)): return None base = arg.callee.expr attr = arg.callee.name rtype = builder.node_type(base) if not (is_dict_rprimitive(rtype) and attr in ('keys', 'values', 'items')): return None obj = builder.accept(base) # Note that it is not safe to use fast methods on dict subclasses, # so the corresponding helpers in CPy.h fallback to (inlined) # generic logic. if attr == 'keys': return builder.call_c(dict_keys_op, [obj], expr.line) elif attr == 'values': return builder.call_c(dict_values_op, [obj], expr.line) else: return builder.call_c(dict_items_op, [obj], expr.line)
def _visit_display(builder: IRBuilder, items: List[Expression], constructor_op: Callable[[List[Value], int], Value], append_op: CFunctionDescription, extend_op: CFunctionDescription, line: int, is_list: bool ) -> Value: accepted_items = [] for item in items: if isinstance(item, StarExpr): accepted_items.append((True, builder.accept(item.expr))) else: accepted_items.append((False, builder.accept(item))) result: Union[Value, None] = None initial_items = [] for starred, value in accepted_items: if result is None and not starred and is_list: initial_items.append(value) continue if result is None: result = constructor_op(initial_items, line) builder.call_c(extend_op if starred else append_op, [result, value], line) if result is None: result = constructor_op(initial_items, line) return result
def transform_import(builder: IRBuilder, node: Import) -> None: if node.is_mypy_only: return globals = builder.load_globals_dict() for node_id, as_name in node.ids: builder.gen_import(node_id, node.line) # Update the globals dict with the appropriate module: # * For 'import foo.bar as baz' we add 'foo.bar' with the name 'baz' # * For 'import foo.bar' we add 'foo' with the name 'foo' # Typically we then ignore these entries and access things directly # via the module static, but we will use the globals version for modules # that mypy couldn't find, since it doesn't analyze module references # from those properly. # Miscompiling imports inside of functions, like below in import from. if as_name: name = as_name base = node_id else: base = name = node_id.split('.')[0] # Python 3.7 has a nice 'PyImport_GetModule' function that we can't use :( mod_dict = builder.call_c(get_module_dict_op, [], node.line) obj = builder.call_c( dict_get_item_op, [mod_dict, builder.load_static_unicode(base)], node.line) builder.gen_method_call(globals, '__setitem__', [builder.load_static_unicode(name), obj], result_type=None, line=node.line)
def transform_del_item(builder: IRBuilder, target: AssignmentTarget, line: int) -> None: if isinstance(target, AssignmentTargetIndex): builder.gen_method_call(target.base, '__delitem__', [target.index], result_type=None, line=line) elif isinstance(target, AssignmentTargetAttr): if isinstance(target.obj_type, RInstance): cl = target.obj_type.class_ir if not cl.is_deletable(target.attr): builder.error('"{}" cannot be deleted'.format(target.attr), line) builder.note( 'Using "__deletable__ = ' + '[\'<attr>\']" in the class body enables "del obj.<attr>"', line) key = builder.load_str(target.attr) builder.call_c(py_delattr_op, [target.obj, key], line) elif isinstance(target, AssignmentTargetRegister): # Delete a local by assigning an error value to it, which will # prompt the insertion of uninit checks. builder.add( Assign(target.register, builder.add(LoadErrorValue(target.type, undefines=True)))) elif isinstance(target, AssignmentTargetTuple): for subtarget in target.items: transform_del_item(builder, subtarget, line)
def transform_assert_stmt(builder: IRBuilder, a: AssertStmt) -> None: if builder.options.strip_asserts: return cond = builder.accept(a.expr) ok_block, error_block = BasicBlock(), BasicBlock() builder.add_bool_branch(cond, ok_block, error_block) builder.activate_block(error_block) if a.msg is None: # Special case (for simpler generated code) builder.add( RaiseStandardError(RaiseStandardError.ASSERTION_ERROR, None, a.line)) elif isinstance(a.msg, StrExpr): # Another special case builder.add( RaiseStandardError(RaiseStandardError.ASSERTION_ERROR, a.msg.value, a.line)) else: # The general case -- explicitly construct an exception instance message = builder.accept(a.msg) exc_type = builder.load_module_attr_by_fullname( 'builtins.AssertionError', a.line) exc = builder.py_call(exc_type, [message], a.line) builder.call_c(raise_exception_op, [exc], a.line) builder.add(Unreachable()) builder.activate_block(ok_block)
def add_non_ext_class_attr_ann( builder: IRBuilder, non_ext: NonExtClassInfo, lvalue: NameExpr, stmt: AssignmentStmt, get_type_info: Optional[Callable[[AssignmentStmt], Optional[TypeInfo]]] = None ) -> None: """Add a class attribute to __annotations__ of a non-extension class.""" typ: Optional[Value] = None if get_type_info is not None: type_info = get_type_info(stmt) if type_info: typ = load_type(builder, type_info, stmt.line) if typ is None: # FIXME: if get_type_info is not provided, don't fall back to stmt.type? ann_type = get_proper_type(stmt.type) if isinstance(ann_type, Instance): typ = load_type(builder, ann_type.type, stmt.line) else: typ = builder.add( LoadAddress(type_object_op.type, type_object_op.src, stmt.line)) key = builder.load_str(lvalue.name) builder.call_c(dict_set_item_op, [non_ext.anns, key, typ], stmt.line)
def _visit_display(builder: IRBuilder, items: List[Expression], constructor_op: OpDescription, append_op: CFunctionDescription, extend_op: CFunctionDescription, line: int) -> Value: accepted_items = [] for item in items: if isinstance(item, StarExpr): accepted_items.append((True, builder.accept(item.expr))) else: accepted_items.append((False, builder.accept(item))) result = None # type: Union[Value, None] initial_items = [] for starred, value in accepted_items: if result is None and not starred and constructor_op.is_var_arg: initial_items.append(value) continue if result is None: result = builder.primitive_op(constructor_op, initial_items, line) builder.call_c(extend_op if starred else append_op, [result, value], line) if result is None: result = builder.primitive_op(constructor_op, initial_items, line) return result
def transform_decorator(builder: IRBuilder, dec: Decorator) -> None: func_ir, func_reg = gen_func_item(builder, dec.func, dec.func.name, builder.mapper.fdef_to_sig(dec.func)) decorated_func: Optional[Value] = None if func_reg: decorated_func = load_decorated_func(builder, dec.func, func_reg) builder.assign(get_func_target(builder, dec.func), decorated_func, dec.func.line) func_reg = decorated_func # If the prebuild pass didn't put this function in the function to decorators map (for example # if this is a registered singledispatch implementation with no other decorators), we should # treat this function as a regular function, not a decorated function elif dec.func in builder.fdefs_to_decorators: # Obtain the the function name in order to construct the name of the helper function. name = dec.func.fullname.split('.')[-1] # Load the callable object representing the non-decorated function, and decorate it. orig_func = builder.load_global_str(name, dec.line) decorated_func = load_decorated_func(builder, dec.func, orig_func) if decorated_func is not None: # Set the callable object representing the decorated function as a global. builder.call_c(dict_set_item_op, [ builder.load_globals_dict(), builder.load_str(dec.func.name), decorated_func ], decorated_func.line) maybe_insert_into_registry_dict(builder, dec.func) builder.functions.append(func_ir)
def setup_non_ext_dict(builder: IRBuilder, cdef: ClassDef, metaclass: Value, bases: Value) -> Value: """Initialize the class dictionary for a non-extension class. This class dictionary is passed to the metaclass constructor. """ # Check if the metaclass defines a __prepare__ method, and if so, call it. has_prepare = builder.call_c(py_hasattr_op, [metaclass, builder.load_str('__prepare__')], cdef.line) non_ext_dict = Register(dict_rprimitive) true_block, false_block, exit_block, = BasicBlock(), BasicBlock(), BasicBlock() builder.add_bool_branch(has_prepare, true_block, false_block) builder.activate_block(true_block) cls_name = builder.load_str(cdef.name) prepare_meth = builder.py_get_attr(metaclass, '__prepare__', cdef.line) prepare_dict = builder.py_call(prepare_meth, [cls_name, bases], cdef.line) builder.assign(non_ext_dict, prepare_dict, cdef.line) builder.goto(exit_block) builder.activate_block(false_block) builder.assign(non_ext_dict, builder.call_c(dict_new_op, [], cdef.line), cdef.line) builder.goto(exit_block) builder.activate_block(exit_block) return non_ext_dict
def convert_expr(builder: IRBuilder, format_ops: List[FormatOp], exprs: List[Expression], line: int) -> Optional[List[Value]]: """Convert expressions into string literals with the guidance of FormatOps.""" if len(format_ops) != len(exprs): return None converted = [] for x, format_op in zip(exprs, format_ops): node_type = builder.node_type(x) if format_op == FormatOp.STR: if is_str_rprimitive(node_type): var_str = builder.accept(x) elif is_int_rprimitive(node_type) or is_short_int_rprimitive( node_type): var_str = builder.call_c(int_to_str_op, [builder.accept(x)], line) else: var_str = builder.call_c(str_op, [builder.accept(x)], line) elif format_op == FormatOp.INT: if is_int_rprimitive(node_type) or is_short_int_rprimitive( node_type): var_str = builder.call_c(int_to_str_op, [builder.accept(x)], line) else: return None else: return None converted.append(var_str) return converted
def transform_raise_stmt(builder: IRBuilder, s: RaiseStmt) -> None: if s.expr is None: builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO) builder.add(Unreachable()) return exc = builder.accept(s.expr) builder.call_c(raise_exception_op, [exc], s.line) builder.add(Unreachable())
def maybe_insert_into_registry_dict(builder: IRBuilder, fitem: FuncDef) -> None: line = fitem.line is_singledispatch_main_func = fitem in builder.singledispatch_impls # dict of singledispatch_func to list of register_types (fitem is the function to register) to_register: DefaultDict[FuncDef, List[TypeInfo]] = defaultdict(list) for main_func, impls in builder.singledispatch_impls.items(): for dispatch_type, impl in impls: if fitem == impl: to_register[main_func].append(dispatch_type) if not to_register and not is_singledispatch_main_func: return if is_singledispatch_main_func: main_func_name = singledispatch_main_func_name(fitem.name) main_func_obj = load_func(builder, main_func_name, fitem.fullname, line) loaded_object_type = builder.load_module_attr_by_fullname( 'builtins.object', line) registry_dict = builder.builder.make_dict( [(loaded_object_type, main_func_obj)], line) dispatch_func_obj = builder.load_global_str(fitem.name, line) builder.call_c( py_setattr_op, [dispatch_func_obj, builder.load_str('registry'), registry_dict], line) for singledispatch_func, types in to_register.items(): # TODO: avoid recomputing the native IDs for all the functions every time we find a new # function native_ids = get_native_impl_ids(builder, singledispatch_func) if fitem not in native_ids: to_insert = load_func(builder, fitem.name, fitem.fullname, line) else: current_id = native_ids[fitem] load_literal = LoadLiteral(current_id, object_rprimitive) to_insert = builder.add(load_literal) # TODO: avoid reloading the registry here if we just created it dispatch_func_obj = load_func(builder, singledispatch_func.name, singledispatch_func.fullname, line) registry = load_singledispatch_registry(builder, dispatch_func_obj, line) for typ in types: loaded_type = load_type(builder, typ, line) builder.call_c(dict_set_item_op, [registry, loaded_type, to_insert], line) dispatch_cache = builder.builder.get_attr(dispatch_func_obj, 'dispatch_cache', dict_rprimitive, line) builder.gen_method_call(dispatch_cache, 'clear', [], None, line)
def finish_non_ext_dict(builder: IRBuilder, non_ext: NonExtClassInfo, line: int) -> None: # Add __annotations__ to the class dict. builder.call_c(dict_set_item_op, [non_ext.dict, builder.load_str('__annotations__'), non_ext.anns], -1) # We add a __doc__ attribute so if the non-extension class is decorated with the # dataclass decorator, dataclass will not try to look for __text_signature__. # https://github.com/python/cpython/blob/3.7/Lib/dataclasses.py#L957 filler_doc_str = 'mypyc filler docstring' builder.add_to_non_ext_dict( non_ext, '__doc__', builder.load_str(filler_doc_str), line) builder.add_to_non_ext_dict( non_ext, '__module__', builder.load_str(builder.module_name), line)
def generate_singledispatch_callable_class_ctor(builder: IRBuilder) -> None: """Create an __init__ that sets registry and dispatch_cache to empty dicts""" line = -1 class_ir = builder.fn_info.callable_class.ir with builder.enter_method(class_ir, '__init__', bool_rprimitive): empty_dict = builder.call_c(dict_new_op, [], line) builder.add(SetAttr(builder.self(), 'registry', empty_dict, line)) cache_dict = builder.call_c(dict_new_op, [], line) dispatch_cache_str = builder.load_str('dispatch_cache') # use the py_setattr_op instead of SetAttr so that it also gets added to our __dict__ builder.call_c(py_setattr_op, [builder.self(), dispatch_cache_str, cache_dict], line) # the generated C code seems to expect that __init__ returns a char, so just return 1 builder.add(Return(Integer(1, bool_rprimitive, line), line))
def transform_generator_expr(builder: IRBuilder, o: GeneratorExpr) -> Value: if any(o.is_async): builder.error('async comprehensions are unimplemented', o.line) builder.warning('Treating generator comprehension as list', o.line) return builder.call_c(iter_op, [translate_list_comprehension(builder, o)], o.line)
def transform_import_from(builder: IRBuilder, node: ImportFrom) -> None: if node.is_mypy_only: return module_state = builder.graph[builder.module_name] if module_state.ancestors is not None and module_state.ancestors: module_package = module_state.ancestors[0] elif builder.module_path.endswith("__init__.py"): module_package = builder.module_name else: module_package = '' id = importlib.util.resolve_name('.' * node.relative + node.id, module_package) globals = builder.load_globals_dict() imported_names = [name for name, _ in node.names] module = builder.gen_import_from(id, globals, imported_names, node.line) # Copy everything into our module's dict. # Note that we miscompile import from inside of functions here, # since that case *shouldn't* load it into the globals dict. # This probably doesn't matter much and the code runs basically right. for name, maybe_as_name in node.names: as_name = maybe_as_name or name obj = builder.call_c(import_from_op, [module, builder.load_str(id), builder.load_str(name), builder.load_str(as_name)], node.line) builder.gen_method_call( globals, '__setitem__', [builder.load_str(as_name), obj], result_type=None, line=node.line)
def dataclass_non_ext_info(builder: IRBuilder, cdef: ClassDef) -> Optional[NonExtClassInfo]: """Set up a NonExtClassInfo to track dataclass attributes. In addition to setting up a normal extension class for dataclasses, we also collect its class attributes like a non-extension class so that we can hand them to the dataclass decorator. """ if is_dataclass(cdef): return NonExtClassInfo( builder.call_c(dict_new_op, [], cdef.line), builder.add(TupleSet([], cdef.line)), builder.call_c(dict_new_op, [], cdef.line), builder.add(LoadAddress(type_object_op.type, type_object_op.src, cdef.line)) ) else: return None
def try_finally_entry_blocks(builder: IRBuilder, err_handler: BasicBlock, return_entry: BasicBlock, main_entry: BasicBlock, finally_block: BasicBlock, ret_reg: Optional[Register]) -> Value: old_exc = builder.alloc_temp(exc_rtuple) # Entry block for non-exceptional flow builder.activate_block(main_entry) if ret_reg: builder.add( Assign(ret_reg, builder.add(LoadErrorValue(builder.ret_types[-1])))) builder.goto(return_entry) builder.activate_block(return_entry) builder.add(Assign(old_exc, builder.add(LoadErrorValue(exc_rtuple)))) builder.goto(finally_block) # Entry block for errors builder.activate_block(err_handler) if ret_reg: builder.add( Assign(ret_reg, builder.add(LoadErrorValue(builder.ret_types[-1])))) builder.add(Assign(old_exc, builder.call_c(error_catch_op, [], -1))) builder.goto(finally_block) return old_exc
def add_get_to_callable_class(builder: IRBuilder, fn_info: FuncInfo) -> None: """Generate the '__get__' method for a callable class.""" line = fn_info.fitem.line with builder.enter_method(fn_info.callable_class.ir, '__get__', object_rprimitive, fn_info, self_type=object_rprimitive): instance = builder.add_argument('instance', object_rprimitive) builder.add_argument('owner', object_rprimitive) # If accessed through the class, just return the callable # object. If accessed through an object, create a new bound # instance method object. instance_block, class_block = BasicBlock(), BasicBlock() comparison = builder.translate_is_op(builder.read(instance), builder.none_object(), 'is', line) builder.add_bool_branch(comparison, class_block, instance_block) builder.activate_block(class_block) builder.add(Return(builder.self())) builder.activate_block(instance_block) builder.add( Return( builder.call_c( method_new_op, [builder.self(), builder.read(instance)], line)))
def translate_super_method_call(builder: IRBuilder, expr: CallExpr, callee: SuperExpr) -> Value: if callee.info is None or (len(callee.call.args) != 0 and len(callee.call.args) != 2): return translate_call(builder, expr, callee) # We support two-argument super but only when it is super(CurrentClass, self) # TODO: We could support it when it is a parent class in many cases? if len(callee.call.args) == 2: self_arg = callee.call.args[1] if (not isinstance(self_arg, NameExpr) or not isinstance(self_arg.node, Var) or not self_arg.node.is_self): return translate_call(builder, expr, callee) typ_arg = callee.call.args[0] if (not isinstance(typ_arg, NameExpr) or not isinstance(typ_arg.node, TypeInfo) or callee.info is not typ_arg.node): return translate_call(builder, expr, callee) ir = builder.mapper.type_to_ir[callee.info] # Search for the method in the mro, skipping ourselves. We # determine targets of super calls to native methods statically. for base in ir.mro[1:]: if callee.name in base.method_decls: break else: if (ir.is_ext_class and ir.builtin_base is None and not ir.inherits_python and callee.name == '__init__' and len(expr.args) == 0): # Call translates to object.__init__(self), which is a # no-op, so omit the call. return builder.none() return translate_call(builder, expr, callee) decl = base.method_decl(callee.name) arg_values = [builder.accept(arg) for arg in expr.args] arg_kinds, arg_names = expr.arg_kinds[:], expr.arg_names[:] if decl.kind != FUNC_STATICMETHOD: # Grab first argument vself: Value = builder.self() if decl.kind == FUNC_CLASSMETHOD: vself = builder.call_c(type_op, [vself], expr.line) elif builder.fn_info.is_generator: # For generator classes, the self target is the 6th value # in the symbol table (which is an ordered dict). This is sort # of ugly, but we can't search by name since the 'self' parameter # could be named anything, and it doesn't get added to the # environment indexes. self_targ = list(builder.symtables[-1].values())[6] vself = builder.read(self_targ, builder.fn_info.fitem.line) arg_values.insert(0, vself) arg_kinds.insert(0, ARG_POS) arg_names.insert(0, None) return builder.builder.call(decl, arg_values, arg_kinds, arg_names, expr.line)
def transform_with(builder: IRBuilder, expr: Expression, target: Optional[Lvalue], body: GenFunc, line: int) -> None: # This is basically a straight transcription of the Python code in PEP 343. # I don't actually understand why a bunch of it is the way it is. # We could probably optimize the case where the manager is compiled by us, # but that is not our common case at all, so. mgr_v = builder.accept(expr) typ = builder.call_c(type_op, [mgr_v], line) exit_ = builder.maybe_spill(builder.py_get_attr(typ, '__exit__', line)) value = builder.py_call( builder.py_get_attr(typ, '__enter__', line), [mgr_v], line ) mgr = builder.maybe_spill(mgr_v) exc = builder.maybe_spill_assignable(builder.true()) def try_body() -> None: if target: builder.assign(builder.get_assignment_target(target), value, line) body() def except_body() -> None: builder.assign(exc, builder.false(), line) out_block, reraise_block = BasicBlock(), BasicBlock() builder.add_bool_branch( builder.py_call(builder.read(exit_), [builder.read(mgr)] + get_sys_exc_info(builder), line), out_block, reraise_block ) builder.activate_block(reraise_block) builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO) builder.add(Unreachable()) builder.activate_block(out_block) def finally_body() -> None: out_block, exit_block = BasicBlock(), BasicBlock() builder.add( Branch(builder.read(exc), exit_block, out_block, Branch.BOOL) ) builder.activate_block(exit_block) none = builder.none_object() builder.py_call( builder.read(exit_), [builder.read(mgr), none, none, none], line ) builder.goto_and_activate(out_block) transform_try_finally_stmt( builder, lambda: transform_try_except(builder, try_body, [(None, None, except_body)], None, line), finally_body )
def find_non_ext_metaclass(builder: IRBuilder, cdef: ClassDef, bases: Value) -> Value: """Find the metaclass of a class from its defs and bases. """ if cdef.metaclass: declared_metaclass = builder.accept(cdef.metaclass) else: declared_metaclass = builder.add(LoadAddress(type_object_op.type, type_object_op.src, cdef.line)) return builder.call_c(py_calc_meta_op, [declared_metaclass, bases], cdef.line)
def transform_del_item(builder: IRBuilder, target: AssignmentTarget, line: int) -> None: if isinstance(target, AssignmentTargetIndex): builder.gen_method_call(target.base, '__delitem__', [target.index], result_type=None, line=line) elif isinstance(target, AssignmentTargetAttr): key = builder.load_static_unicode(target.attr) builder.call_c(py_delattr_op, [target.obj, key], line) elif isinstance(target, AssignmentTargetRegister): # Delete a local by assigning an error value to it, which will # prompt the insertion of uninit checks. builder.add( Assign(target.register, builder.add(LoadErrorValue(target.type, undefines=True)))) elif isinstance(target, AssignmentTargetTuple): for subtarget in target.items: transform_del_item(builder, subtarget, line)
def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: # OK AND NOW THE FUN PART base_exprs = cdef.base_type_exprs + cdef.removed_base_type_exprs if base_exprs: bases = [builder.accept(x) for x in base_exprs] tp_bases = builder.new_tuple(bases, cdef.line) else: tp_bases = builder.add( LoadErrorValue(object_rprimitive, is_borrowed=True)) modname = builder.load_static_unicode(builder.module_name) template = builder.add( LoadStatic(object_rprimitive, cdef.name + "_template", builder.module_name, NAMESPACE_TYPE)) # Create the class tp = builder.call_c(pytype_from_template_op, [template, tp_bases, modname], cdef.line) # Immediately fix up the trait vtables, before doing anything with the class. ir = builder.mapper.type_to_ir[cdef.info] if not ir.is_trait and not ir.builtin_base: builder.add( Call( FuncDecl(cdef.name + '_trait_vtable_setup', None, builder.module_name, FuncSignature([], bool_rprimitive)), [], -1)) # Populate a '__mypyc_attrs__' field containing the list of attrs builder.call_c(py_setattr_op, [ tp, builder.load_static_unicode('__mypyc_attrs__'), create_mypyc_attrs_tuple(builder, builder.mapper.type_to_ir[cdef.info], cdef.line) ], cdef.line) # Save the class builder.add(InitStatic(tp, cdef.name, builder.module_name, NAMESPACE_TYPE)) # Add it to the dict builder.call_c(dict_set_item_op, [ builder.load_globals_dict(), builder.load_static_unicode(cdef.name), tp, ], cdef.line) return tp
def add_register_method_to_callable_class(builder: IRBuilder, fn_info: FuncInfo) -> None: line = -1 with builder.enter_method(fn_info.callable_class.ir, 'register', object_rprimitive): cls_arg = builder.add_argument('cls', object_rprimitive) func_arg = builder.add_argument('func', object_rprimitive, ArgKind.ARG_OPT) ret_val = builder.call_c(register_function, [builder.self(), cls_arg, func_arg], line) builder.add(Return(ret_val, line))
def transform_dictionary_comprehension(builder: IRBuilder, o: DictionaryComprehension) -> Value: d = builder.call_c(dict_new_op, [], o.line) loop_params = list(zip(o.indices, o.sequences, o.condlists)) def gen_inner_stmts() -> None: k = builder.accept(o.key) v = builder.accept(o.value) builder.call_c(dict_set_item_op, [d, k, v], o.line) comprehension_helper(builder, loop_params, gen_inner_stmts, o.line) return d
def transform_slice_expr(builder: IRBuilder, expr: SliceExpr) -> Value: def get_arg(arg: Optional[Expression]) -> Value: if arg is None: return builder.none_object() else: return builder.accept(arg) args = [get_arg(expr.begin_index), get_arg(expr.end_index), get_arg(expr.stride)] return builder.call_c(new_slice_op, args, expr.line)
def unsafe_index( builder: IRBuilder, target: Value, index: Value, line: int ) -> Value: """Emit a potentially unsafe index into a target.""" # This doesn't really fit nicely into any of our data-driven frameworks # since we want to use __getitem__ if we don't have an unsafe version, # so we just check manually. if is_list_rprimitive(target.type): return builder.call_c(list_get_item_unsafe_op, [target, index], line) else: return builder.gen_method_call(target, '__getitem__', [index], None, line)