Exemple #1
0
 def test_call_two_args(self) -> None:
     decl = FuncDecl(
         'myfn', None, 'mod',
         FuncSignature([
             RuntimeArg('m', int_rprimitive),
             RuntimeArg('n', int_rprimitive)
         ], int_rprimitive))
     self.assert_emit(Call(decl, [self.m, self.k], 55),
                      "cpy_r_r0 = CPyDef_myfn(cpy_r_m, cpy_r_k);")
Exemple #2
0
 def test_simple(self) -> None:
     self.block.ops.append(Return(self.reg))
     fn = FuncIR('myfunc', None, 'mod', FuncSignature([self.arg], int_rprimitive),
                 [self.block], self.env)
     emitter = Emitter(EmitterContext(['mod']))
     generate_native_function(fn, emitter, 'prog.py', 'prog')
     result = emitter.fragments
     assert_string_arrays_equal(
         [
             'static CPyTagged CPyDef_myfunc(CPyTagged cpy_r_arg) {\n',
             'CPyL0: ;\n',
             '    return cpy_r_arg;\n',
             '}\n',
         ],
         result, msg='Generated code invalid')
Exemple #3
0
    def gen_glue_ne_method(self, cls: ClassIR, line: int) -> FuncIR:
        """Generate a __ne__ method from a __eq__ method. """
        self.builder.enter()

        rt_args = (RuntimeArg("self", RInstance(cls)), RuntimeArg("rhs", object_rprimitive))

        # The environment operates on Vars, so we make some up
        fake_vars = [(Var(arg.name), arg.type) for arg in rt_args]
        args = [
            self.builder.read(
                self.builder.environment.add_local_reg(
                    var, type, is_arg=True
                ),
                line
            )
            for var, type in fake_vars
        ]  # type: List[Value]
        self.builder.ret_types[-1] = object_rprimitive

        # If __eq__ returns NotImplemented, then __ne__ should also
        not_implemented_block, regular_block = BasicBlock(), BasicBlock()
        eqval = self.add(MethodCall(args[0], '__eq__', [args[1]], line))
        not_implemented = self.primitive_op(not_implemented_op, [], line)
        self.add(Branch(
            self.builder.binary_op(eqval, not_implemented, 'is', line),
            not_implemented_block,
            regular_block,
            Branch.BOOL_EXPR))

        self.builder.activate_block(regular_block)
        retval = self.builder.coerce(
            self.builder.unary_op(eqval, 'not', line), object_rprimitive, line
        )
        self.add(Return(retval))

        self.builder.activate_block(not_implemented_block)
        self.add(Return(not_implemented))

        blocks, env, ret_type, _ = self.builder.leave()
        return FuncIR(
            FuncDecl('__ne__', cls.name, self.module_name,
                     FuncSignature(rt_args, ret_type)),
            blocks, env)
Exemple #4
0
    def allocate_class(self, cdef: ClassDef) -> Value:
        # OK AND NOW THE FUN PART
        base_exprs = cdef.base_type_exprs + cdef.removed_base_type_exprs
        if base_exprs:
            bases = [self.accept(x) for x in base_exprs]
            tp_bases = self.primitive_op(new_tuple_op, bases, cdef.line)
        else:
            tp_bases = self.add(
                LoadErrorValue(object_rprimitive, is_borrowed=True))
        modname = self.load_static_unicode(self.module_name)
        template = self.add(
            LoadStatic(object_rprimitive, cdef.name + "_template",
                       self.module_name, NAMESPACE_TYPE))
        # Create the class
        tp = self.primitive_op(pytype_from_template_op,
                               [template, tp_bases, modname], cdef.line)
        # Immediately fix up the trait vtables, before doing anything with the class.
        ir = self.mapper.type_to_ir[cdef.info]
        if not ir.is_trait and not ir.builtin_base:
            self.add(
                Call(
                    FuncDecl(cdef.name + '_trait_vtable_setup', None,
                             self.module_name,
                             FuncSignature([], bool_rprimitive)), [], -1))
        # Populate a '__mypyc_attrs__' field containing the list of attrs
        self.primitive_op(py_setattr_op, [
            tp,
            self.load_static_unicode('__mypyc_attrs__'),
            self.create_mypyc_attrs_tuple(self.mapper.type_to_ir[cdef.info],
                                          cdef.line)
        ], cdef.line)

        # Save the class
        self.add(InitStatic(tp, cdef.name, self.module_name, NAMESPACE_TYPE))

        # Add it to the dict
        self.primitive_op(dict_set_item_op, [
            self.builder.load_globals_dict(),
            self.load_static_unicode(cdef.name),
            tp,
        ], cdef.line)

        return tp
Exemple #5
0
 def test_register(self) -> None:
     self.env.temp_index = 0
     op = LoadInt(5)
     self.block.ops.append(op)
     self.env.add_op(op)
     fn = FuncIR('myfunc', None, 'mod', FuncSignature([self.arg], list_rprimitive),
                 [self.block], self.env)
     emitter = Emitter(EmitterContext(['mod']))
     generate_native_function(fn, emitter, 'prog.py', 'prog')
     result = emitter.fragments
     assert_string_arrays_equal(
         [
             'static PyObject *CPyDef_myfunc(CPyTagged cpy_r_arg) {\n',
             '    CPyTagged cpy_r_r0;\n',
             'CPyL0: ;\n',
             '    cpy_r_r0 = 10;\n',
             '}\n',
         ],
         result, msg='Generated code invalid')
    def fdef_to_sig(self, fdef: FuncDef) -> FuncSignature:
        if isinstance(fdef.type, CallableType):
            arg_types = [self.get_arg_rtype(typ, kind)
                         for typ, kind in zip(fdef.type.arg_types, fdef.type.arg_kinds)]
            ret = self.type_to_rtype(fdef.type.ret_type)
        else:
            # Handle unannotated functions
            arg_types = [object_rprimitive for arg in fdef.arguments]
            ret = object_rprimitive

        args = [RuntimeArg(arg_name, arg_type, arg_kind)
                for arg_name, arg_kind, arg_type in zip(fdef.arg_names, fdef.arg_kinds, arg_types)]

        # We force certain dunder methods to return objects to support letting them
        # return NotImplemented. It also avoids some pointless boxing and unboxing,
        # since tp_richcompare needs an object anyways.
        if fdef.name in ('__eq__', '__ne__', '__lt__', '__gt__', '__le__', '__ge__'):
            ret = object_rprimitive
        return FuncSignature(args, ret)
Exemple #7
0
    def generate_attr_defaults(self, cdef: ClassDef) -> None:
        """Generate an initialization method for default attr values (from class vars)"""
        cls = self.mapper.type_to_ir[cdef.info]
        if cls.builtin_base:
            return

        # Pull out all assignments in classes in the mro so we can initialize them
        # TODO: Support nested statements
        default_assignments = []
        for info in reversed(cdef.info.mro):
            if info not in self.mapper.type_to_ir:
                continue
            for stmt in info.defn.defs.body:
                if (isinstance(stmt, AssignmentStmt)
                        and isinstance(stmt.lvalues[0], NameExpr)
                        and not is_class_var(stmt.lvalues[0])
                        and not isinstance(stmt.rvalue, TempNode)):
                    if stmt.lvalues[0].name == '__slots__':
                        continue

                    # Skip type annotated assignments in dataclasses
                    if is_dataclass(cdef) and stmt.type:
                        continue

                    default_assignments.append(stmt)

        if not default_assignments:
            return

        self.builder.enter()
        self.builder.ret_types[-1] = bool_rprimitive

        rt_args = (RuntimeArg(SELF_NAME, RInstance(cls)),)
        self_var = self.builder.read(add_self_to_env(self.builder.environment, cls), -1)

        for stmt in default_assignments:
            lvalue = stmt.lvalues[0]
            assert isinstance(lvalue, NameExpr)
            if not stmt.is_final_def and not is_constant(stmt.rvalue):
                self.builder.warning('Unsupported default attribute value', stmt.rvalue.line)

            # If the attribute is initialized to None and type isn't optional,
            # don't initialize it to anything.
            attr_type = cls.attr_type(lvalue.name)
            if isinstance(stmt.rvalue, RefExpr) and stmt.rvalue.fullname == 'builtins.None':
                if (not is_optional_type(attr_type) and not is_object_rprimitive(attr_type)
                        and not is_none_rprimitive(attr_type)):
                    continue
            val = self.builder.coerce(self.accept(stmt.rvalue), attr_type, stmt.line)
            self.add(SetAttr(self_var, lvalue.name, val, -1))

        self.add(Return(self.primitive_op(true_op, [], -1)))

        blocks, env, ret_type, _ = self.builder.leave()
        ir = FuncIR(
            FuncDecl('__mypyc_defaults_setup',
                     cls.name, self.module_name,
                     FuncSignature(rt_args, ret_type)),
            blocks, env)
        self.builder.functions.append(ir)
        cls.methods[ir.name] = ir
Exemple #8
0
def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None:
    """Generate C code for a class.

    This is the main entry point to the module.
    """
    name = cl.name
    name_prefix = cl.name_prefix(emitter.names)
    fullname = '{}.{}'.format(module, name)

    setup_name = new_name = clear_name = dealloc_name = '0'
    traverse_name = getseters_name = vtable_name = '0'
    if not cl.is_trait:
        setup_name = '{}_setup'.format(name_prefix)
        new_name = '{}_new'.format(name_prefix)
        traverse_name = '{}_traverse'.format(name_prefix)
        clear_name = '{}_clear'.format(name_prefix)
        dealloc_name = '{}_dealloc'.format(name_prefix)
        getseters_name = '{}_getseters'.format(name_prefix)
        vtable_name = '{}_vtable'.format(name_prefix)

    methods_name = '{}_methods'.format(name_prefix)
    base_arg = "&{}".format(
        emitter.type_struct_name(cl.base)) if cl.base and not cl.traits else "0"

    def emit_line() -> None:
        emitter.emit_line()

    emit_line()
    generate_object_struct(cl, emitter)
    emit_line()

    # If there is a __init__ method, generate a function for tp_init and
    # extract the args (which we'll use for the native constructor)
    init_fn = cl.get_method('__init__')
    if init_fn:
        init_name = '{}_init'.format(name_prefix)
        init_args = init_fn.args[1:]
        generate_init_for_class(cl, init_name, init_fn, emitter)
    else:
        init_name = '0'
        init_args = []

    call_fn = cl.get_method('__call__')
    call_name = '{}{}'.format(PREFIX, call_fn.cname(emitter.names)) if call_fn else '0'

    if not cl.is_trait:
        emitter.emit_line('static PyObject *{}(void);'.format(setup_name))
        # TODO: Use RInstance
        ctor = FuncIR(cl.name, None, module, FuncSignature(init_args, object_rprimitive),
                      [], Environment())
        emitter.emit_line(native_function_header(ctor, emitter) + ';')

        emit_line()
        generate_new_for_class(cl, new_name, vtable_name, setup_name, emitter)
        emit_line()
        generate_traverse_for_class(cl, traverse_name, emitter)
        emit_line()
        generate_clear_for_class(cl, clear_name, emitter)
        emit_line()
        generate_dealloc_for_class(cl, dealloc_name, clear_name, emitter)
        emit_line()
        generate_native_getters_and_setters(cl, emitter)
        vtable_name = generate_vtables(cl, vtable_name, emitter)
        emit_line()
        generate_getseter_declarations(cl, emitter)
        emit_line()
        generate_getseters_table(cl, getseters_name, emitter)
        emit_line()
    generate_methods_table(cl, methods_name, emitter)
    emit_line()

    emitter.emit_line(textwrap.dedent("""\
        static PyTypeObject {type_struct} = {{
            PyVarObject_HEAD_INIT(NULL, 0)
            "{fullname}",              /* tp_name */
            sizeof({struct_name}),     /* tp_basicsize */
            0,                         /* tp_itemsize */
            (destructor){dealloc_name},  /* tp_dealloc */
            0,                         /* tp_print */
            0,                         /* tp_getattr */
            0,                         /* tp_setattr */
            0,                         /* tp_reserved */
            0,                         /* tp_repr */
            0,                         /* tp_as_number */
            0,                         /* tp_as_sequence */
            0,                         /* tp_as_mapping */
            0,                         /* tp_hash  */
            {tp_call},                 /* tp_call */
            0,                         /* tp_str */
            0,                         /* tp_getattro */
            0,                         /* tp_setattro */
            0,                         /* tp_as_buffer */
            Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
            0,                         /* tp_doc */
            (traverseproc){traverse_name}, /* tp_traverse */
            (inquiry){clear_name},     /* tp_clear */
            0,                         /* tp_richcompare */
            0,                         /* tp_weaklistoffset */
            0,                         /* tp_iter */
            0,                         /* tp_iternext */
            {methods_name},            /* tp_methods */
            0,                         /* tp_members */
            {getseters_name},          /* tp_getset */
            {base_arg},                /* tp_base */
            0,                         /* tp_dict */
            0,                         /* tp_descr_get */
            0,                         /* tp_descr_set */
            0,                         /* tp_dictoffset */
            {init_name},               /* tp_init */
            0,                         /* tp_alloc */
            {new_name},                /* tp_new */
        }};\
        """).format(type_struct=emitter.type_struct_name(cl),
                    struct_name=cl.struct_name(emitter.names),
                    fullname=fullname,
                    traverse_name=traverse_name,
                    clear_name=clear_name,
                    dealloc_name=dealloc_name,
                    tp_call=call_name,
                    new_name=new_name,
                    methods_name=methods_name,
                    getseters_name=getseters_name,
                    init_name=init_name,
                    base_arg=base_arg,
                    ))
    emitter.emit_line()
    if not cl.is_trait:
        generate_setup_for_class(cl, setup_name, vtable_name, emitter)
        emitter.emit_line()
        generate_constructor_for_class(cl, ctor, init_fn, setup_name, vtable_name, emitter)
        emitter.emit_line()
        generate_getseters(cl, emitter)
def prepare_class_def(path: str, module_name: str, cdef: ClassDef,
                      errors: Errors, mapper: Mapper) -> None:

    ir = mapper.type_to_ir[cdef.info]
    info = cdef.info

    attrs = get_mypyc_attrs(cdef)
    if attrs.get("allow_interpreted_subclasses") is True:
        ir.allow_interpreted_subclasses = True

    # We sort the table for determinism here on Python 3.5
    for name, node in sorted(info.names.items()):
        # Currently all plugin generated methods are dummies and not included.
        if node.plugin_generated:
            continue

        if isinstance(node.node, Var):
            assert node.node.type, "Class member %s missing type" % name
            if not node.node.is_classvar and name != '__slots__':
                ir.attributes[name] = mapper.type_to_rtype(node.node.type)
        elif isinstance(node.node, (FuncDef, Decorator)):
            prepare_method_def(ir, module_name, cdef, mapper, node.node)
        elif isinstance(node.node, OverloadedFuncDef):
            # Handle case for property with both a getter and a setter
            if node.node.is_property:
                if is_valid_multipart_property_def(node.node):
                    for item in node.node.items:
                        prepare_method_def(ir, module_name, cdef, mapper, item)
                else:
                    errors.error("Unsupported property decorator semantics",
                                 path, cdef.line)

            # Handle case for regular function overload
            else:
                assert node.node.impl
                prepare_method_def(ir, module_name, cdef, mapper,
                                   node.node.impl)

    # Check for subclassing from builtin types
    for cls in info.mro:
        # Special case exceptions and dicts
        # XXX: How do we handle *other* things??
        if cls.fullname == 'builtins.BaseException':
            ir.builtin_base = 'PyBaseExceptionObject'
        elif cls.fullname == 'builtins.dict':
            ir.builtin_base = 'PyDictObject'
        elif cls.fullname.startswith('builtins.'):
            if not can_subclass_builtin(cls.fullname):
                # Note that if we try to subclass a C extension class that
                # isn't in builtins, bad things will happen and we won't
                # catch it here! But this should catch a lot of the most
                # common pitfalls.
                errors.error(
                    "Inheriting from most builtin types is unimplemented",
                    path, cdef.line)

    if ir.builtin_base:
        ir.attributes.clear()

    # Set up a constructor decl
    init_node = cdef.info['__init__'].node
    if not ir.is_trait and not ir.builtin_base and isinstance(
            init_node, FuncDef):
        init_sig = mapper.fdef_to_sig(init_node)

        defining_ir = mapper.type_to_ir.get(init_node.info)
        # If there is a nontrivial __init__ that wasn't defined in an
        # extension class, we need to make the constructor take *args,
        # **kwargs so it can call tp_init.
        if ((defining_ir is None or not defining_ir.is_ext_class
             or cdef.info['__init__'].plugin_generated)
                and init_node.info.fullname != 'builtins.object'):
            init_sig = FuncSignature([
                init_sig.args[0],
                RuntimeArg("args", tuple_rprimitive, ARG_STAR),
                RuntimeArg("kwargs", dict_rprimitive, ARG_STAR2)
            ], init_sig.ret_type)

        ctor_sig = FuncSignature(init_sig.args[1:], RInstance(ir))
        ir.ctor = FuncDecl(cdef.name, None, module_name, ctor_sig)
        mapper.func_to_decl[cdef.info] = ir.ctor

    # Set up the parent class
    bases = [
        mapper.type_to_ir[base.type] for base in info.bases
        if base.type in mapper.type_to_ir
    ]
    if not all(c.is_trait for c in bases[1:]):
        errors.error("Non-trait bases must appear first in parent list", path,
                     cdef.line)
    ir.traits = [c for c in bases if c.is_trait]

    mro = []
    base_mro = []
    for cls in info.mro:
        if cls not in mapper.type_to_ir:
            if cls.fullname != 'builtins.object':
                ir.inherits_python = True
            continue
        base_ir = mapper.type_to_ir[cls]
        if not base_ir.is_trait:
            base_mro.append(base_ir)
        mro.append(base_ir)

        if cls.defn.removed_base_type_exprs or not base_ir.is_ext_class:
            ir.inherits_python = True

    base_idx = 1 if not ir.is_trait else 0
    if len(base_mro) > base_idx:
        ir.base = base_mro[base_idx]
    ir.mro = mro
    ir.base_mro = base_mro

    for base in bases:
        if base.children is not None:
            base.children.append(ir)

    if is_dataclass(cdef):
        ir.is_augmented = True