Ejemplo n.º 1
0
 def test_check_int(self) -> None:
     emitter = Emitter(self.context)
     generate_arg_check('x', int_rprimitive, emitter, ReturnHandler('NULL'))
     generate_arg_check('y',
                        int_rprimitive,
                        emitter,
                        ReturnHandler('NULL'),
                        optional=True)
     lines = emitter.fragments
     self.assert_lines([
         'CPyTagged arg_x;',
         'if (likely(PyLong_Check(obj_x)))',
         '    arg_x = CPyTagged_BorrowFromObject(obj_x);',
         'else {',
         '    CPy_TypeError("int", obj_x); return NULL;',
         '}',
         'CPyTagged arg_y;',
         'if (obj_y == NULL) {',
         '    arg_y = CPY_INT_TAG;',
         '} else if (likely(PyLong_Check(obj_y)))',
         '    arg_y = CPyTagged_BorrowFromObject(obj_y);',
         'else {',
         '    CPy_TypeError("int", obj_y); return NULL;',
         '}',
     ], lines)
Ejemplo n.º 2
0
 def error(self) -> ErrorHandler:
     """Figure out how to deal with errors in the wrapper."""
     if self.cleanups or self.traceback_code:
         # We'll have a label at the end with error handling code.
         return GotoHandler('fail')
     else:
         # Nothing special needs to done to handle errors, so just return.
         return ReturnHandler('NULL')
Ejemplo n.º 3
0
def generate_wrapper_core(fn: FuncIR,
                          emitter: Emitter,
                          optional_args: Optional[List[RuntimeArg]] = None,
                          arg_names: Optional[List[str]] = None,
                          cleanups: Optional[List[str]] = None,
                          traceback_code: Optional[str] = None) -> None:
    """Generates the core part of a wrapper function for a native function.

    This expects each argument as a PyObject * named obj_{arg} as a precondition.
    It converts the PyObject *s to the necessary types, checking and unboxing if necessary,
    makes the call, then boxes the result if necessary and returns it.
    """

    optional_args = optional_args or []
    cleanups = cleanups or []
    use_goto = bool(cleanups or traceback_code)
    error = ReturnHandler('NULL') if not use_goto else GotoHandler('fail')

    arg_names = arg_names or [arg.name for arg in fn.args]
    for arg_name, arg in zip(arg_names, fn.args):
        # Suppress the argument check for *args/**kwargs, since we know it must be right.
        typ = arg.type if arg.kind not in (ARG_STAR, ARG_STAR2) else object_rprimitive
        generate_arg_check(arg_name,
                           typ,
                           emitter,
                           error,
                           optional=arg in optional_args)
    native_args = ', '.join('arg_{}'.format(arg) for arg in arg_names)
    if fn.ret_type.is_unboxed or use_goto:
        # TODO: The Py_RETURN macros return the correct PyObject * with reference count handling.
        #       Are they relevant?
        emitter.emit_line('{}retval = {}{}({});'.format(emitter.ctype_spaced(fn.ret_type),
                                                        NATIVE_PREFIX,
                                                        fn.cname(emitter.names),
                                                        native_args))
        emitter.emit_lines(*cleanups)
        if fn.ret_type.is_unboxed:
            emitter.emit_error_check('retval', fn.ret_type, 'return NULL;')
            emitter.emit_box('retval', 'retbox', fn.ret_type, declare_dest=True)

        emitter.emit_line('return {};'.format('retbox' if fn.ret_type.is_unboxed else 'retval'))
    else:
        emitter.emit_line('return {}{}({});'.format(NATIVE_PREFIX,
                                                    fn.cname(emitter.names),
                                                    native_args))
        # TODO: Tracebacks?

    if use_goto:
        emitter.emit_label('fail')
        emitter.emit_lines(*cleanups)
        if traceback_code:
            emitter.emit_lines(traceback_code)
        emitter.emit_lines('return NULL;')
Ejemplo n.º 4
0
def generate_setter(cl: ClassIR,
                    attr: str,
                    rtype: RType,
                    emitter: Emitter) -> None:
    attr_field = emitter.attr(attr)
    emitter.emit_line('static int')
    emitter.emit_line('{}({} *self, PyObject *value, void *closure)'.format(
        setter_name(cl, attr, emitter.names),
        cl.struct_name(emitter.names)))
    emitter.emit_line('{')

    deletable = cl.is_deletable(attr)
    if not deletable:
        emitter.emit_line('if (value == NULL) {')
        emitter.emit_line('PyErr_SetString(PyExc_AttributeError,')
        emitter.emit_line('    "{} object attribute {} cannot be deleted");'.format(repr(cl.name),
                                                                                    repr(attr)))
        emitter.emit_line('return -1;')
        emitter.emit_line('}')

    # HACK: Don't consider refcounted values as always defined, since it's possible to
    #       access uninitialized values via 'gc.get_objects()'. Accessing non-refcounted
    #       values is benign.
    always_defined = cl.is_always_defined(attr) and not rtype.is_refcounted

    if rtype.is_refcounted:
        attr_expr = f'self->{attr_field}'
        if not always_defined:
            emitter.emit_undefined_attr_check(rtype, attr_expr, '!=')
        emitter.emit_dec_ref('self->{}'.format(attr_field), rtype)
        if not always_defined:
            emitter.emit_line('}')

    if deletable:
        emitter.emit_line('if (value != NULL) {')

    if rtype.is_unboxed:
        emitter.emit_unbox('value', 'tmp', rtype, error=ReturnHandler('-1'), declare_dest=True)
    elif is_same_type(rtype, object_rprimitive):
        emitter.emit_line('PyObject *tmp = value;')
    else:
        emitter.emit_cast('value', 'tmp', rtype, declare_dest=True)
        emitter.emit_lines('if (!tmp)',
                           '    return -1;')
    emitter.emit_inc_ref('tmp', rtype)
    emitter.emit_line(f'self->{attr_field} = tmp;')
    if deletable:
        emitter.emit_line('} else')
        emitter.emit_line('    self->{} = {};'.format(attr_field,
                                                      emitter.c_undefined_value(rtype)))
    emitter.emit_line('return 0;')
    emitter.emit_line('}')
Ejemplo n.º 5
0
 def test_check_list(self) -> None:
     emitter = Emitter(self.context)
     generate_arg_check('x', list_rprimitive, emitter,
                        ReturnHandler('NULL'))
     lines = emitter.fragments
     self.assert_lines([
         'PyObject *arg_x;',
         'if (likely(PyList_Check(obj_x)))',
         '    arg_x = obj_x;',
         'else {',
         '    CPy_TypeError("list", obj_x);',
         '    return NULL;',
         '}',
     ], lines)
Ejemplo n.º 6
0
def generate_setter(cl: ClassIR, attr: str, rtype: RType,
                    emitter: Emitter) -> None:
    attr_field = emitter.attr(attr)
    emitter.emit_line('static int')
    emitter.emit_line('{}({} *self, PyObject *value, void *closure)'.format(
        setter_name(cl, attr, emitter.names), cl.struct_name(emitter.names)))
    emitter.emit_line('{')

    deletable = cl.is_deletable(attr)
    if not deletable:
        emitter.emit_line('if (value == NULL) {')
        emitter.emit_line('PyErr_SetString(PyExc_AttributeError,')
        emitter.emit_line(
            '    "{} object attribute {} cannot be deleted");'.format(
                repr(cl.name), repr(attr)))
        emitter.emit_line('return -1;')
        emitter.emit_line('}')

    if rtype.is_refcounted:
        attr_expr = 'self->{}'.format(attr_field)
        emitter.emit_undefined_attr_check(rtype, attr_expr, '!=')
        emitter.emit_dec_ref('self->{}'.format(attr_field), rtype)
        emitter.emit_line('}')

    if deletable:
        emitter.emit_line('if (value != NULL) {')
    if rtype.is_unboxed:
        emitter.emit_unbox('value',
                           'tmp',
                           rtype,
                           error=ReturnHandler('-1'),
                           declare_dest=True)
    elif is_same_type(rtype, object_rprimitive):
        emitter.emit_line('PyObject *tmp = value;')
    else:
        emitter.emit_cast('value', 'tmp', rtype, declare_dest=True)
        emitter.emit_lines('if (!tmp)', '    return -1;')
    emitter.emit_inc_ref('tmp', rtype)
    emitter.emit_line('self->{} = tmp;'.format(attr_field))
    if deletable:
        emitter.emit_line('} else')
        emitter.emit_line('    self->{} = {};'.format(
            attr_field, emitter.c_undefined_value(rtype)))
    emitter.emit_line('return 0;')
    emitter.emit_line('}')
Ejemplo n.º 7
0
def generate_property_setter(cl: ClassIR, attr: str, arg_type: RType,
                             func_ir: FuncIR, emitter: Emitter) -> None:

    emitter.emit_line('static int')
    emitter.emit_line('{}({} *self, PyObject *value, void *closure)'.format(
        setter_name(cl, attr, emitter.names), cl.struct_name(emitter.names)))
    emitter.emit_line('{')
    if arg_type.is_unboxed:
        emitter.emit_unbox('value',
                           'tmp',
                           arg_type,
                           error=ReturnHandler('-1'),
                           declare_dest=True)
        emitter.emit_line('{}{}((PyObject *) self, tmp);'.format(
            NATIVE_PREFIX, func_ir.cname(emitter.names)))
    else:
        emitter.emit_line('{}{}((PyObject *) self, value);'.format(
            NATIVE_PREFIX, func_ir.cname(emitter.names)))
    emitter.emit_line('return 0;')
    emitter.emit_line('}')
Ejemplo n.º 8
0
def generate_contains_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
    """Generates a wrapper for a native __contains__ method."""
    name = '{}{}{}'.format(DUNDER_PREFIX, fn.name, cl.name_prefix(emitter.names))
    emitter.emit_line(
        'static int {name}(PyObject *self, PyObject *obj_item) {{'.
        format(name=name))
    generate_arg_check('item', fn.args[1].type, emitter, ReturnHandler('-1'))
    emitter.emit_line('{}val = {}{}(self, arg_item);'.format(emitter.ctype_spaced(fn.ret_type),
                                                             NATIVE_PREFIX,
                                                             fn.cname(emitter.names)))
    emitter.emit_error_check('val', fn.ret_type, 'return -1;')
    if is_bool_rprimitive(fn.ret_type):
        emitter.emit_line('return val;')
    else:
        emitter.emit_line('int boolval = PyObject_IsTrue(val);')
        emitter.emit_dec_ref('val', fn.ret_type)
        emitter.emit_line('return boolval;')
    emitter.emit_line('}')

    return name
Ejemplo n.º 9
0
def generate_wrapper_core(fn: FuncIR,
                          emitter: Emitter,
                          optional_args: Optional[List[RuntimeArg]] = None,
                          arg_names: Optional[List[str]] = None,
                          cleanups: Optional[List[str]] = None,
                          traceback_code: Optional[str] = None) -> None:
    """Generates the core part of a wrapper function for a native function.

    This expects each argument as a PyObject * named obj_{arg} as a precondition.
    It converts the PyObject *s to the necessary types, checking and unboxing if necessary,
    makes the call, then boxes the result if necessary and returns it.
    """
    gen = WrapperGenerator(None, emitter)
    gen.set_target(fn)
    gen.arg_names = arg_names or [arg.name for arg in fn.args]
    gen.cleanups = cleanups or []
    gen.optional_args = optional_args or []
    gen.traceback_code = traceback_code or ''

    error = ReturnHandler('NULL') if not gen.use_goto() else GotoHandler('fail')
    gen.emit_arg_processing(error=error)
    gen.emit_call()
    gen.emit_error_handling()