Example #1
0
def generate_arg_check(name: str, typ: RType, emitter: Emitter,
                       error_code: str, optional: bool = False) -> None:
    """Insert a runtime check for argument and unbox if necessary.

    The object is named PyObject *obj_{}. This is expected to generate
    a value of name arg_{} (unboxed if necessary). For each primitive a runtime
    check ensures the correct type.
    """
    if typ.is_unboxed:
        # Borrow when unboxing to avoid reference count manipulation.
        emitter.emit_unbox('obj_{}'.format(name), 'arg_{}'.format(name), typ,
                           error_code, declare_dest=True, borrow=True, optional=optional)
    elif is_object_rprimitive(typ):
        # Object is trivial since any object is valid
        if optional:
            emitter.emit_line('PyObject *arg_{};'.format(name))
            emitter.emit_line('if (obj_{} == NULL) {{'.format(name))
            emitter.emit_line('arg_{} = {};'.format(name, emitter.c_error_value(typ)))
            emitter.emit_lines('} else {', 'arg_{} = obj_{}; '.format(name, name), '}')
        else:
            emitter.emit_line('PyObject *arg_{} = obj_{};'.format(name, name))
    else:
        emitter.emit_cast('obj_{}'.format(name), 'arg_{}'.format(name), typ,
                          declare_dest=True, optional=optional)
        if optional:
            emitter.emit_line('if (obj_{} != NULL && arg_{} == NULL) {}'.format(
                              name, name, error_code))
        else:
            emitter.emit_line('if (arg_{} == NULL) {}'.format(name, error_code))
Example #2
0
def is_subtype(left: RType, right: RType) -> bool:
    if is_object_rprimitive(right):
        return True
    elif isinstance(right, ROptional):
        if is_subtype(left, none_rprimitive) or is_subtype(left, right.value_type):
            return True
    return left.accept(SubtypeVisitor(right))
Example #3
0
def is_subtype(left: RType, right: RType) -> bool:
    if is_object_rprimitive(right):
        return True
    elif isinstance(right, RUnion):
        if isinstance(left, RUnion):
            for left_item in left.items:
                if not any(
                        is_subtype(left_item, right_item)
                        for right_item in right.items):
                    return False
            return True
        else:
            return any(is_subtype(left, item) for item in right.items)
    return left.accept(SubtypeVisitor(right))
Example #4
0
def generate_arg_check(name: str, typ: RType, emitter: Emitter) -> None:
    """Insert a runtime check for argument and unbox if necessary.

    The object is named PyObject *obj_{}. This is expected to generate
    a value of name arg_{} (unboxed if necessary). For each primitive a runtime
    check ensures the correct type.
    """
    if typ.is_unboxed:
        # Borrow when unboxing to avoid reference count manipulation.
        emitter.emit_unbox('obj_{}'.format(name), 'arg_{}'.format(name), typ,
                           'return NULL;', declare_dest=True, borrow=True)
    elif is_object_rprimitive(typ):
        # Trivial, since any object is valid.
        emitter.emit_line('PyObject *arg_{} = obj_{};'.format(name, name))
    else:
        emitter.emit_cast('obj_{}'.format(name), 'arg_{}'.format(name), typ,
                          declare_dest=True)
        emitter.emit_line('if (arg_{} == NULL) return NULL;'.format(name))
Example #5
0
    def emit_cast(self,
                  src: str,
                  dest: str,
                  typ: RType,
                  declare_dest: bool = False,
                  custom_message: Optional[str] = None,
                  optional: bool = False,
                  src_type: Optional[RType] = None) -> None:
        """Emit code for casting a value of given type.

        Somewhat strangely, this supports unboxed types but only
        operates on boxed versions.  This is necessary to properly
        handle types such as Optional[int] in compatability glue.

        Assign NULL (error value) to dest if the value has an incompatible type.

        Always copy/steal the reference in src.

        Args:
            src: Name of source C variable
            dest: Name of target C variable
            typ: Type of value
            declare_dest: If True, also declare the variable 'dest'

        """
        if custom_message is not None:
            err = custom_message
        else:
            err = 'PyErr_SetString(PyExc_TypeError, "{} object expected");'.format(
                self.pretty_name(typ))

        # Special case casting *from* optional
        if src_type and is_optional_type(
                src_type) and not is_object_rprimitive(typ):
            value_type = optional_value_type(src_type)
            assert value_type is not None
            if is_same_type(value_type, typ):
                if declare_dest:
                    self.emit_line('PyObject *{};'.format(dest))
                self.emit_arg_check(src, dest, typ,
                                    '({} != Py_None)'.format(src), optional)
                self.emit_lines('    {} = {};'.format(dest, src), 'else {',
                                err, '{} = NULL;'.format(dest), '}')
                return

        # TODO: Verify refcount handling.
        if (is_list_rprimitive(typ) or is_dict_rprimitive(typ)
                or is_set_rprimitive(typ) or is_float_rprimitive(typ)
                or is_str_rprimitive(typ) or is_int_rprimitive(typ)
                or is_bool_rprimitive(typ)):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            if is_list_rprimitive(typ):
                prefix = 'PyList'
            elif is_dict_rprimitive(typ):
                prefix = 'PyDict'
            elif is_set_rprimitive(typ):
                prefix = 'PySet'
            elif is_float_rprimitive(typ):
                prefix = 'CPyFloat'
            elif is_str_rprimitive(typ):
                prefix = 'PyUnicode'
            elif is_int_rprimitive(typ):
                prefix = 'PyLong'
            elif is_bool_rprimitive(typ):
                prefix = 'PyBool'
            else:
                assert False, prefix
            self.emit_arg_check(src, dest, typ,
                                '({}_Check({}))'.format(prefix, src), optional)
            self.emit_lines('    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif is_tuple_rprimitive(typ):
            if declare_dest:
                self.emit_line('{} {};'.format(self.ctype(typ), dest))
            self.emit_arg_check(src, dest, typ,
                                '(PyTuple_Check({}))'.format(src), optional)
            self.emit_lines('    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif isinstance(typ, RInstance):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            if typ.class_ir.children:
                check = '(PyObject_TypeCheck({}, {}))'.format(
                    src, self.type_struct_name(typ.class_ir))
            else:
                # If the class has no children, just check the type directly
                check = '(Py_TYPE({}) == {})'.format(
                    src, self.type_struct_name(typ.class_ir))
            self.emit_arg_check(src, dest, typ, check, optional)
            self.emit_lines('    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif is_none_rprimitive(typ):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            self.emit_arg_check(src, dest, typ, '({} == Py_None)'.format(src),
                                optional)
            self.emit_lines('    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif is_object_rprimitive(typ):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            self.emit_arg_check(src, dest, typ, '', optional)
            self.emit_line('{} = {};'.format(dest, src))
            if optional:
                self.emit_line('}')
        elif isinstance(typ, RUnion):
            self.emit_union_cast(src, dest, typ, declare_dest, err, optional,
                                 src_type)
        elif isinstance(typ, RTuple):
            assert not optional
            self.emit_tuple_cast(src, dest, typ, declare_dest, err, src_type)
        else:
            assert False, 'Cast not implemented: %s' % typ
Example #6
0
    def emit_cast(self,
                  src: str,
                  dest: str,
                  typ: RType,
                  declare_dest: bool = False,
                  custom_message: Optional[str] = None,
                  optional: bool = False,
                  src_type: Optional[RType] = None,
                  likely: bool = True) -> None:
        """Emit code for casting a value of given type.

        Somewhat strangely, this supports unboxed types but only
        operates on boxed versions.  This is necessary to properly
        handle types such as Optional[int] in compatibility glue.

        Assign NULL (error value) to dest if the value has an incompatible type.

        Always copy/steal the reference in src.

        Args:
            src: Name of source C variable
            dest: Name of target C variable
            typ: Type of value
            declare_dest: If True, also declare the variable 'dest'
            likely: If the cast is likely to succeed (can be False for unions)
        """
        if custom_message is not None:
            err = custom_message
        else:
            err = 'CPy_TypeError("{}", {});'.format(self.pretty_name(typ), src)

        # Special case casting *from* optional
        if src_type and is_optional_type(
                src_type) and not is_object_rprimitive(typ):
            value_type = optional_value_type(src_type)
            assert value_type is not None
            if is_same_type(value_type, typ):
                if declare_dest:
                    self.emit_line('PyObject *{};'.format(dest))
                check = '({} != Py_None)'
                if likely:
                    check = '(likely{})'.format(check)
                self.emit_arg_check(src, dest, typ, check.format(src),
                                    optional)
                self.emit_lines('    {} = {};'.format(dest, src), 'else {',
                                err, '{} = NULL;'.format(dest), '}')
                return

        # TODO: Verify refcount handling.
        if (is_list_rprimitive(typ) or is_dict_rprimitive(typ)
                or is_set_rprimitive(typ) or is_float_rprimitive(typ)
                or is_str_rprimitive(typ) or is_int_rprimitive(typ)
                or is_bool_rprimitive(typ)):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            if is_list_rprimitive(typ):
                prefix = 'PyList'
            elif is_dict_rprimitive(typ):
                prefix = 'PyDict'
            elif is_set_rprimitive(typ):
                prefix = 'PySet'
            elif is_float_rprimitive(typ):
                prefix = 'CPyFloat'
            elif is_str_rprimitive(typ):
                prefix = 'PyUnicode'
            elif is_int_rprimitive(typ):
                prefix = 'PyLong'
            elif is_bool_rprimitive(typ):
                prefix = 'PyBool'
            else:
                assert False, 'unexpected primitive type'
            check = '({}_Check({}))'
            if likely:
                check = '(likely{})'.format(check)
            self.emit_arg_check(src, dest, typ, check.format(prefix, src),
                                optional)
            self.emit_lines('    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif is_tuple_rprimitive(typ):
            if declare_dest:
                self.emit_line('{} {};'.format(self.ctype(typ), dest))
            check = '(PyTuple_Check({}))'
            if likely:
                check = '(likely{})'.format(check)
            self.emit_arg_check(src, dest, typ, check.format(src), optional)
            self.emit_lines('    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif isinstance(typ, RInstance):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            concrete = all_concrete_classes(typ.class_ir)
            n_types = len(concrete)
            # If there are too many concrete subclasses or we can't find any
            # (meaning the code ought to be dead), fall back to a normal typecheck.
            # Otherwise check all the subclasses.
            if n_types == 0 or n_types > FAST_ISINSTANCE_MAX_SUBCLASSES + 1:
                check = '(PyObject_TypeCheck({}, {}))'.format(
                    src, self.type_struct_name(typ.class_ir))
            else:
                full_str = '(Py_TYPE({src}) == {targets[0]})'
                for i in range(1, n_types):
                    full_str += ' || (Py_TYPE({src}) == {targets[%d]})' % i
                if n_types > 1:
                    full_str = '(%s)' % full_str
                check = full_str.format(
                    src=src,
                    targets=[self.type_struct_name(ir) for ir in concrete])
            if likely:
                check = '(likely{})'.format(check)
            self.emit_arg_check(src, dest, typ, check, optional)
            self.emit_lines('    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif is_none_rprimitive(typ):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            check = '({} == Py_None)'
            if likely:
                check = '(likely{})'.format(check)
            self.emit_arg_check(src, dest, typ, check.format(src), optional)
            self.emit_lines('    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif is_object_rprimitive(typ):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            self.emit_arg_check(src, dest, typ, '', optional)
            self.emit_line('{} = {};'.format(dest, src))
            if optional:
                self.emit_line('}')
        elif isinstance(typ, RUnion):
            self.emit_union_cast(src, dest, typ, declare_dest, err, optional,
                                 src_type)
        elif isinstance(typ, RTuple):
            assert not optional
            self.emit_tuple_cast(src, dest, typ, declare_dest, err, src_type)
        else:
            assert False, 'Cast not implemented: %s' % typ
Example #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
Example #8
0
File: emit.py Project: kxing/mypyc
    def emit_cast(self,
                  src: str,
                  dest: str,
                  typ: RType,
                  declare_dest: bool = False,
                  custom_message: Optional[str] = None) -> None:
        """Emit code for casting a value of given type.

        Somewhat strangely, this supports unboxed types but only
        operates on boxed versions.  This is necessary to properly
        handle types such as Optional[int] in compatability glue.

        Assign NULL (error value) to dest if the value has an incompatible type.

        Always copy/steal the reference in src.

        Args:
            src: Name of source C variable
            dest: Name of target C variable
            typ: Type of value
            declare_dest: If True, also declare the variable 'dest'

        """
        if custom_message is not None:
            err = custom_message
        else:
            err = 'PyErr_SetString(PyExc_TypeError, "{} object expected");'.format(
                self.pretty_name(typ))
        # TODO: Verify refcount handling.
        if (is_list_rprimitive(typ) or is_dict_rprimitive(typ)
                or is_set_rprimitive(typ) or is_float_rprimitive(typ)
                or is_str_rprimitive(typ) or is_int_rprimitive(typ)
                or is_bool_rprimitive(typ)):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            if is_list_rprimitive(typ):
                prefix = 'PyList'
            elif is_dict_rprimitive(typ):
                prefix = 'PyDict'
            elif is_set_rprimitive(typ):
                prefix = 'PySet'
            elif is_float_rprimitive(typ):
                prefix = 'PyFloat'
            elif is_str_rprimitive(typ):
                prefix = 'PyUnicode'
            elif is_int_rprimitive(typ):
                prefix = 'PyLong'
            elif is_bool_rprimitive(typ):
                prefix = 'PyBool'
            else:
                assert False, prefix
            self.emit_lines('if ({}_Check({}))'.format(prefix, src),
                            '    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif is_tuple_rprimitive(typ):
            if declare_dest:
                self.emit_line('{} {};'.format(self.ctype(typ), dest))
            self.emit_lines('if (PyTuple_Check({}))'.format(src),
                            '    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif isinstance(typ, RInstance):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            self.emit_lines(
                'if (PyObject_TypeCheck({}, &{}))'.format(
                    src, self.type_struct_name(typ.class_ir)),
                '    {} = {};'.format(dest, src), 'else {', err,
                '{} = NULL;'.format(dest), '}')
        elif is_none_rprimitive(typ):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            self.emit_lines('if ({} == Py_None)'.format(src),
                            '    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif is_object_rprimitive(typ):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            self.emit_line('{} = {};'.format(dest, src))
        elif isinstance(typ, ROptional):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            self.emit_lines('if ({} == Py_None)'.format(src),
                            '    {} = {};'.format(dest, src), 'else {')
            self.emit_cast(src, dest, typ.value_type, custom_message=err)
            self.emit_line('}')
        else:
            assert False, 'Cast not implemented: %s' % typ