Exemplo n.º 1
0
def generate_native_getters_and_setters(cl: ClassIR, emitter: Emitter) -> None:
    for attr, rtype in cl.attributes.items():
        attr_field = emitter.attr(attr)

        # Native getter
        emitter.emit_line('{}{}({} *self)'.format(
            emitter.ctype_spaced(rtype),
            native_getter_name(cl, attr, emitter.names),
            cl.struct_name(emitter.names)))
        emitter.emit_line('{')
        if rtype.is_refcounted:
            emit_undefined_check(rtype, emitter, attr_field, '==')
            emitter.emit_lines(
                'PyErr_SetString(PyExc_AttributeError, "attribute {} of {} undefined");'
                .format(repr(attr), repr(cl.name)), '} else {')
            emitter.emit_inc_ref('self->{}'.format(attr_field), rtype)
            emitter.emit_line('}')
        emitter.emit_line('return self->{};'.format(attr_field))
        emitter.emit_line('}')
        emitter.emit_line()
        # Native setter
        emitter.emit_line('bool {}({} *self, {}value)'.format(
            native_setter_name(cl, attr, emitter.names),
            cl.struct_name(emitter.names), emitter.ctype_spaced(rtype)))
        emitter.emit_line('{')
        if rtype.is_refcounted:
            emit_undefined_check(rtype, emitter, attr_field, '!=')
            emitter.emit_dec_ref('self->{}'.format(attr_field), rtype)
            emitter.emit_line('}')
        # This steal the reference to src, so we don't need to increment the arg
        emitter.emit_lines('self->{} = value;'.format(attr_field), 'return 1;',
                           '}')
        emitter.emit_line()
Exemplo n.º 2
0
def native_function_header(fn: FuncDecl, emitter: Emitter) -> str:
    args = []
    for arg in fn.sig.args:
        args.append('{}{}{}'.format(emitter.ctype_spaced(arg.type), REG_PREFIX, arg.name))

    return '{ret_type}{name}({args})'.format(
        ret_type=emitter.ctype_spaced(fn.sig.ret_type),
        name=emitter.native_function_name(fn),
        args=', '.join(args) or 'void')
Exemplo n.º 3
0
def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None:
    seen_attrs: Set[Tuple[str, RType]] = set()
    lines: List[str] = []
    lines += ['typedef struct {',
              'PyObject_HEAD',
              'CPyVTableItem *vtable;']
    if cl.has_method('__call__') and emitter.use_vectorcall():
        lines.append('vectorcallfunc vectorcall;')
    for base in reversed(cl.base_mro):
        if not base.is_trait:
            for attr, rtype in base.attributes.items():
                if (attr, rtype) not in seen_attrs:
                    lines.append('{}{};'.format(emitter.ctype_spaced(rtype),
                                                emitter.attr(attr)))
                    seen_attrs.add((attr, rtype))

                    if isinstance(rtype, RTuple):
                        emitter.declare_tuple_struct(rtype)

    lines.append('}} {};'.format(cl.struct_name(emitter.names)))
    lines.append('')
    emitter.context.declarations[cl.struct_name(emitter.names)] = HeaderDeclaration(
        lines,
        is_type=True
    )
Exemplo n.º 4
0
def declare_native_getters_and_setters(cl: ClassIR, emitter: Emitter) -> None:
    decls = emitter.context.declarations
    for attr, rtype in cl.attributes.items():
        getter_name = native_getter_name(cl, attr, emitter.names)
        setter_name = native_setter_name(cl, attr, emitter.names)
        decls[getter_name] = HeaderDeclaration(
            '{}{}({} *self);'.format(emitter.ctype_spaced(rtype), getter_name,
                                     cl.struct_name(emitter.names)),
            needs_export=True,
        )
        decls[setter_name] = HeaderDeclaration(
            'bool {}({} *self, {}value);'.format(
                native_setter_name(cl, attr, emitter.names),
                cl.struct_name(emitter.names), emitter.ctype_spaced(rtype)),
            needs_export=True,
        )
Exemplo n.º 5
0
 def declare_finals(
         self, module: str, final_names: Iterable[Tuple[str, RType]], emitter: Emitter) -> None:
     for name, typ in final_names:
         static_name = emitter.static_name(name, module)
         emitter.context.declarations[static_name] = HeaderDeclaration(
             '{}{};'.format(emitter.ctype_spaced(typ), static_name),
             [self.final_definition(module, name, typ, emitter)],
             needs_export=True)
Exemplo n.º 6
0
 def final_definition(
         self, module: str, name: str, typ: RType, emitter: Emitter) -> str:
     static_name = emitter.static_name(name, module)
     # Here we rely on the fact that undefined value and error value are always the same
     if isinstance(typ, RTuple):
         # We need to inline because initializer must be static
         undefined = '{{ {} }}'.format(''.join(emitter.tuple_undefined_value_helper(typ)))
     else:
         undefined = emitter.c_undefined_value(typ)
     return '{}{} = {};'.format(emitter.ctype_spaced(typ), static_name, undefined)
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_code = 'return NULL;' if not use_goto else 'goto 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_code, 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;')
Exemplo n.º 8
0
def generate_native_function(fn: FuncIR, emitter: Emitter, source_path: str,
                             module_name: str) -> None:
    declarations = Emitter(emitter.context)
    names = generate_names_for_ir(fn.arg_regs, fn.blocks)
    body = Emitter(emitter.context, names)
    visitor = FunctionEmitterVisitor(body, declarations, source_path,
                                     module_name)

    declarations.emit_line(f'{native_function_header(fn.decl, emitter)} {{')
    body.indent()

    for r in all_values(fn.arg_regs, fn.blocks):
        if isinstance(r.type, RTuple):
            emitter.declare_tuple_struct(r.type)
        if isinstance(r.type, RArray):
            continue  # Special: declared on first assignment

        if r in fn.arg_regs:
            continue  # Skip the arguments

        ctype = emitter.ctype_spaced(r.type)
        init = ''
        declarations.emit_line('{ctype}{prefix}{name}{init};'.format(
            ctype=ctype, prefix=REG_PREFIX, name=names[r], init=init))

    # Before we emit the blocks, give them all labels
    blocks = fn.blocks
    for i, block in enumerate(blocks):
        block.label = i

    common = frequently_executed_blocks(fn.blocks[0])

    for i in range(len(blocks)):
        block = blocks[i]
        visitor.rare = block not in common
        next_block = None
        if i + 1 < len(blocks):
            next_block = blocks[i + 1]
        body.emit_label(block)
        visitor.next_block = next_block

        ops = block.ops
        visitor.ops = ops
        visitor.op_index = 0
        while visitor.op_index < len(ops):
            ops[visitor.op_index].accept(visitor)
            visitor.op_index += 1

    body.emit_line('}')

    emitter.emit_from_emitter(declarations)
    emitter.emit_from_emitter(body)
def generate_set_del_item_wrapper_inner(fn: FuncIR, emitter: Emitter,
                                        args: Sequence[RuntimeArg]) -> None:
    for arg in args:
        generate_arg_check(arg.name, arg.type, emitter, 'goto fail;', False)
    native_args = ', '.join('arg_{}'.format(arg.name) for arg in args)
    emitter.emit_line('{}val = {}{}({});'.format(
        emitter.ctype_spaced(fn.ret_type), NATIVE_PREFIX,
        fn.cname(emitter.names), native_args))
    emitter.emit_error_check('val', fn.ret_type, 'goto fail;')
    emitter.emit_dec_ref('val', fn.ret_type)
    emitter.emit_line('return 0;')
    emitter.emit_label('fail')
    emitter.emit_line('return -1;')
    emitter.emit_line('}')
Exemplo n.º 10
0
def generate_readonly_getter(cl: ClassIR, attr: str, rtype: RType,
                             func_ir: FuncIR, emitter: Emitter) -> None:
    emitter.emit_line('static PyObject *')
    emitter.emit_line('{}({} *self, void *closure)'.format(
        getter_name(cl, attr, emitter.names), cl.struct_name(emitter.names)))
    emitter.emit_line('{')
    if rtype.is_unboxed:
        emitter.emit_line('{}retval = {}{}((PyObject *) self);'.format(
            emitter.ctype_spaced(rtype), NATIVE_PREFIX,
            func_ir.cname(emitter.names)))
        emitter.emit_box('retval', 'retbox', rtype, declare_dest=True)
        emitter.emit_line('return retbox;')
    else:
        emitter.emit_line('return {}{}((PyObject *) self);'.format(
            NATIVE_PREFIX, func_ir.cname(emitter.names)))
    emitter.emit_line('}')
Exemplo n.º 11
0
def generate_native_function(fn: FuncIR,
                             emitter: Emitter,
                             source_path: str,
                             module_name: str,
                             optimize_int: bool = True) -> None:
    if optimize_int:
        const_int_regs = find_constant_integer_registers(fn.blocks)
    else:
        const_int_regs = {}
    declarations = Emitter(emitter.context)
    names = generate_names_for_ir(fn.arg_regs, fn.blocks)
    body = Emitter(emitter.context, names)
    visitor = FunctionEmitterVisitor(body, declarations, source_path,
                                     module_name, const_int_regs)

    declarations.emit_line('{} {{'.format(
        native_function_header(fn.decl, emitter)))
    body.indent()

    for r in all_values(fn.arg_regs, fn.blocks):
        if isinstance(r.type, RTuple):
            emitter.declare_tuple_struct(r.type)

        if r in fn.arg_regs:
            continue  # Skip the arguments

        ctype = emitter.ctype_spaced(r.type)
        init = ''
        if r not in const_int_regs:
            declarations.emit_line('{ctype}{prefix}{name}{init};'.format(
                ctype=ctype, prefix=REG_PREFIX, name=names[r], init=init))

    # Before we emit the blocks, give them all labels
    for i, block in enumerate(fn.blocks):
        block.label = i

    for block in fn.blocks:
        body.emit_label(block)
        for op in block.ops:
            op.accept(visitor)

    body.emit_line('}')

    emitter.emit_from_emitter(declarations)
    emitter.emit_from_emitter(body)
Exemplo n.º 12
0
def generate_bool_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
    """Generates a wrapper for native __bool__ methods."""
    name = '{}{}{}'.format(DUNDER_PREFIX, fn.name, cl.name_prefix(emitter.names))
    emitter.emit_line('static int {name}(PyObject *self) {{'.format(
        name=name
    ))
    emitter.emit_line('{}val = {}{}(self);'.format(emitter.ctype_spaced(fn.ret_type),
                                                   NATIVE_PREFIX,
                                                   fn.cname(emitter.names)))
    emitter.emit_error_check('val', fn.ret_type, 'return -1;')
    # This wouldn't be that hard to fix but it seems unimportant and
    # getting error handling and unboxing right would be fiddly. (And
    # way easier to do in IR!)
    assert is_bool_rprimitive(fn.ret_type), "Only bool return supported for __bool__"
    emitter.emit_line('return val;')
    emitter.emit_line('}')

    return name
Exemplo n.º 13
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
Exemplo n.º 14
0
def generate_len_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
    """Generates a wrapper for native __len__ methods."""
    name = '{}{}{}'.format(DUNDER_PREFIX, fn.name,
                           cl.name_prefix(emitter.names))
    emitter.emit_line(
        'static Py_ssize_t {name}(PyObject *self) {{'.format(name=name))
    emitter.emit_line('{}retval = {}{}{}(self);'.format(
        emitter.ctype_spaced(fn.ret_type), emitter.get_group_prefix(fn.decl),
        NATIVE_PREFIX, fn.cname(emitter.names)))
    emitter.emit_error_check('retval', fn.ret_type, 'return -1;')
    if is_int_rprimitive(fn.ret_type):
        emitter.emit_line('Py_ssize_t val = CPyTagged_AsSsize_t(retval);')
    else:
        emitter.emit_line('Py_ssize_t val = PyLong_AsSsize_t(retval);')
    emitter.emit_dec_ref('retval', fn.ret_type)
    emitter.emit_line('if (PyErr_Occurred()) return -1;')
    emitter.emit_line('return val;')
    emitter.emit_line('}')

    return name
Exemplo n.º 15
0
def generate_native_function(fn: FuncIR,
                             emitter: Emitter,
                             source_path: str,
                             module_name: str) -> None:
    declarations = Emitter(emitter.context, fn.env)
    body = Emitter(emitter.context, fn.env)
    visitor = FunctionEmitterVisitor(body, declarations, source_path, module_name)

    declarations.emit_line('{} {{'.format(native_function_header(fn.decl, emitter)))
    body.indent()

    for r, i in fn.env.indexes.items():
        if isinstance(r.type, RTuple):
            emitter.declare_tuple_struct(r.type)
        if i < len(fn.args):
            continue  # skip the arguments
        ctype = emitter.ctype_spaced(r.type)
        init = ''
        if r in fn.env.vars_needing_init:
            init = ' = {}'.format(declarations.c_error_value(r.type))
        declarations.emit_line('{ctype}{prefix}{name}{init};'.format(ctype=ctype,
                                                                     prefix=REG_PREFIX,
                                                                     name=r.name,
                                                                     init=init))

    # Before we emit the blocks, give them all labels
    for i, block in enumerate(fn.blocks):
        block.label = i

    for block in fn.blocks:
        body.emit_label(block)
        for op in block.ops:
            op.accept(visitor)

    body.emit_line('}')

    emitter.emit_from_emitter(declarations)
    emitter.emit_from_emitter(body)