Esempio n. 1
0
def setup_func_for_recursive_call(builder: IRBuilder, fdef: FuncDef,
                                  base: ImplicitClass) -> None:
    """Enable calling a nested function (with a callable class) recursively.

    Adds the instance of the callable class representing the given
    FuncDef to a register in the environment so that the function can
    be called recursively. Note that this needs to be done only for
    nested functions.
    """
    # First, set the attribute of the environment class so that GetAttr can be called on it.
    prev_env = builder.fn_infos[-2].env_class
    prev_env.attributes[fdef.name] = builder.type_to_rtype(fdef.type)

    if isinstance(base, GeneratorClass):
        # If we are dealing with a generator class, then we need to first get the register
        # holding the current environment class, and load the previous environment class from
        # there.
        prev_env_reg = builder.add(
            GetAttr(base.curr_env_reg, ENV_ATTR_NAME, -1))
    else:
        prev_env_reg = base.prev_env_reg

    # Obtain the instance of the callable class representing the FuncDef, and add it to the
    # current environment.
    val = builder.add(GetAttr(prev_env_reg, fdef.name, -1))
    target = builder.environment.add_local_reg(fdef, object_rprimitive)
    builder.assign(target, val, -1)
Esempio n. 2
0
def gen_glue_property(builder: IRBuilder,
                      sig: FuncSignature,
                      target: FuncIR,
                      cls: ClassIR,
                      base: ClassIR,
                      line: int,
                      do_pygetattr: bool) -> FuncIR:
    """Generate glue methods for properties that mediate between different subclass types.

    Similarly to methods, properties of derived types can be covariantly subtyped. Thus,
    properties also require glue. However, this only requires the return type to change.
    Further, instead of a method call, an attribute get is performed.

    If do_pygetattr is True, then get the attribute using the Python C
    API instead of a native call.
    """
    builder.enter()

    rt_arg = RuntimeArg(SELF_NAME, RInstance(cls))
    arg = builder.read(add_self_to_env(builder.environment, cls), line)
    builder.ret_types[-1] = sig.ret_type
    if do_pygetattr:
        retval = builder.py_get_attr(arg, target.name, line)
    else:
        retval = builder.add(GetAttr(arg, target.name, line))
    retbox = builder.coerce(retval, sig.ret_type, line)
    builder.add(Return(retbox))

    blocks, env, return_type, _ = builder.leave()
    return FuncIR(
        FuncDecl(target.name + '__' + base.name + '_glue',
                 cls.name, builder.module_name, FuncSignature([rt_arg], return_type)),
        blocks, env)
Esempio n. 3
0
 def test_get_attr_non_refcounted(self) -> None:
     self.assert_emit(
         GetAttr(self.r, 'x', 1),
         """cpy_r_r0 = ((mod___AObject *)cpy_r_r)->_x;
            if (unlikely(cpy_r_r0 == 2)) {
                PyErr_SetString(PyExc_AttributeError, "attribute 'x' of 'A' undefined");
            }
         """)
Esempio n. 4
0
    def gen_method_call(
            self,
            base: Value,
            name: str,
            arg_values: List[Value],
            result_type: Optional[RType],
            line: int,
            arg_kinds: Optional[List[int]] = None,
            arg_names: Optional[List[Optional[str]]] = None) -> Value:
        """Generate either a native or Python method call."""
        # If arg_kinds contains values other than arg_pos and arg_named, then fallback to
        # Python method call.
        if (arg_kinds is not None and not all(kind in (ARG_POS, ARG_NAMED)
                                              for kind in arg_kinds)):
            return self.py_method_call(base, name, arg_values, base.line,
                                       arg_kinds, arg_names)

        # If the base type is one of ours, do a MethodCall
        if (isinstance(base.type, RInstance)
                and base.type.class_ir.is_ext_class
                and not base.type.class_ir.builtin_base):
            if base.type.class_ir.has_method(name):
                decl = base.type.class_ir.method_decl(name)
                if arg_kinds is None:
                    assert arg_names is None, "arg_kinds not present but arg_names is"
                    arg_kinds = [ARG_POS for _ in arg_values]
                    arg_names = [None for _ in arg_values]
                else:
                    assert arg_names is not None, "arg_kinds present but arg_names is not"

                # Normalize args to positionals.
                assert decl.bound_sig
                arg_values = self.native_args_to_positional(
                    arg_values, arg_kinds, arg_names, decl.bound_sig, line)
                return self.add(MethodCall(base, name, arg_values, line))
            elif base.type.class_ir.has_attr(name):
                function = self.add(GetAttr(base, name, line))
                return self.py_call(function,
                                    arg_values,
                                    line,
                                    arg_kinds=arg_kinds,
                                    arg_names=arg_names)

        elif isinstance(base.type, RUnion):
            return self.union_method_call(base, base.type, name, arg_values,
                                          result_type, line, arg_kinds,
                                          arg_names)

        # Try to do a special-cased method call
        if not arg_kinds or arg_kinds == [ARG_POS] * len(arg_values):
            target = self.translate_special_method_call(
                base, name, arg_values, result_type, line)
            if target:
                return target

        # Fall back to Python method call
        return self.py_method_call(base, name, arg_values, line, arg_kinds,
                                   arg_names)
Esempio n. 5
0
 def get_attr(self, obj: Value, attr: str, result_type: RType, line: int) -> Value:
     """Get a native or Python attribute of an object."""
     if (isinstance(obj.type, RInstance) and obj.type.class_ir.is_ext_class
             and obj.type.class_ir.has_attr(attr)):
         return self.add(GetAttr(obj, attr, line))
     elif isinstance(obj.type, RUnion):
         return self.union_get_attr(obj, obj.type, attr, result_type, line)
     else:
         return self.py_get_attr(obj, attr, line)
Esempio n. 6
0
 def test_get_attr(self) -> None:
     self.assert_emit(
         GetAttr(self.r, 'y', 1),
         """cpy_r_r0 = ((mod___AObject *)cpy_r_r)->_y;
            if (unlikely(((mod___AObject *)cpy_r_r)->_y == CPY_INT_TAG)) {
                PyErr_SetString(PyExc_AttributeError, "attribute 'y' of 'A' undefined");
            } else {
                CPyTagged_IncRef(((mod___AObject *)cpy_r_r)->_y);
            }
         """)
Esempio n. 7
0
 def test_get_attr_merged(self) -> None:
     op = GetAttr(self.r, 'y', 1)
     branch = Branch(op, BasicBlock(8), BasicBlock(9), Branch.IS_ERROR)
     branch.traceback_entry = ('foobar', 123)
     self.assert_emit(op,
                      """\
         cpy_r_r0 = ((mod___AObject *)cpy_r_r)->_y;
         if (unlikely(cpy_r_r0 == CPY_INT_TAG)) {
             CPy_AttributeError("prog.py", "foobar", "A", "y", 123, CPyStatic_prog___globals);
             goto CPyL8;
         }
         CPyTagged_INCREF(cpy_r_r0);
         goto CPyL9;
         """,
                      next_branch=branch)
Esempio n. 8
0
            def get_default() -> Value:
                assert arg.initializer is not None

                # If it is constant, don't bother storing it
                if is_constant(arg.initializer):
                    return builder.accept(arg.initializer)

                # Because gen_arg_defaults runs before calculate_arg_defaults, we
                # add the static/attribute to final_names/the class here.
                elif not builder.fn_info.is_nested:
                    name = fitem.fullname + '.' + arg.variable.name
                    builder.final_names.append((name, target.type))
                    return builder.add(LoadStatic(target.type, name, builder.module_name))
                else:
                    name = arg.variable.name
                    builder.fn_info.callable_class.ir.attributes[name] = target.type
                    return builder.add(
                        GetAttr(builder.fn_info.callable_class.self_reg, name, arg.line))
Esempio n. 9
0
    def read(self, target: Union[Value, AssignmentTarget], line: int = -1) -> Value:
        if isinstance(target, Value):
            return target
        if isinstance(target, AssignmentTargetRegister):
            return target.register
        if isinstance(target, AssignmentTargetIndex):
            reg = self.gen_method_call(
                target.base, '__getitem__', [target.index], target.type, line)
            if reg is not None:
                return reg
            assert False, target.base.type
        if isinstance(target, AssignmentTargetAttr):
            if isinstance(target.obj.type, RInstance) and target.obj.type.class_ir.is_ext_class:
                return self.add(GetAttr(target.obj, target.attr, line))
            else:
                return self.py_get_attr(target.obj, target.attr, line)

        assert False, 'Unsupported lvalue: %r' % target
Esempio n. 10
0
def load_outer_env(builder: IRBuilder, base: Value,
                   outer_env: Environment) -> Value:
    """Loads the environment class for a given base into a register.

    Additionally, iterates through all of the SymbolNode and AssignmentTarget instances of the
    environment at the given index's symtable, and adds those instances to the environment of
    the current environment. This is done so that the current environment can access outer
    environment variables without having to reload all of the environment registers.

    Returns the register where the environment class was loaded.
    """
    env = builder.add(GetAttr(base, ENV_ATTR_NAME, builder.fn_info.fitem.line))
    assert isinstance(env.type,
                      RInstance), '{} must be of type RInstance'.format(env)

    for symbol, target in outer_env.symtable.items():
        env.type.class_ir.attributes[symbol.name] = target.type
        symbol_target = AssignmentTargetAttr(env, symbol.name)
        builder.environment.add_target(symbol, symbol_target)

    return env
Esempio n. 11
0
 def test_get_attr(self) -> None:
     self.assert_emit(
         GetAttr(self.r, 'y', 1),
         """cpy_r_r0 = native_A_gety((mod___AObject *)cpy_r_r); /* y */""")