def specializectx(func): """A decorator that specializes 'func(ctx,...)' for each concrete subclass of AbstractMatchContext. During annotation, if 'ctx' is known to be a specific subclass, calling 'func' is a direct call; if 'ctx' is only known to be of class AbstractMatchContext, calling 'func' is an indirect call. """ assert func.func_code.co_varnames[0] == 'ctx' specname = '_spec_' + func.func_name while specname in _seen_specname: specname += '_' _seen_specname[specname] = True # Install a copy of the function under the name '_spec_funcname' in each # concrete subclass specialized_methods = [] for prefix, concreteclass in [('buf', BufMatchContext), ('str', StrMatchContext), ('uni', UnicodeMatchContext)]: newfunc = func_with_new_name(func, prefix + specname) assert not hasattr(concreteclass, specname) setattr(concreteclass, specname, newfunc) specialized_methods.append(newfunc) # Return a dispatcher function, specialized on the exact type of 'ctx' def dispatch(ctx, *args): return getattr(ctx, specname)(*args) dispatch._annspecialcase_ = 'specialize:argtype(0)' dispatch._specialized_methods_ = specialized_methods return func_with_new_name(dispatch, specname)
def specializectx(func): """A decorator that specializes 'func(ctx,...)' for each concrete subclass of AbstractMatchContext. During annotation, if 'ctx' is known to be a specific subclass, calling 'func' is a direct call; if 'ctx' is only known to be of class AbstractMatchContext, calling 'func' is an indirect call. """ assert func.func_code.co_varnames[0] == 'ctx' specname = '_spec_' + func.func_name while specname in _seen_specname: specname += '_' _seen_specname[specname] = True # Install a copy of the function under the name '_spec_funcname' in each # concrete subclass specialized_methods = [] for prefix, concreteclass in [('str', StrMatchContext), ('uni', UnicodeMatchContext)]: newfunc = func_with_new_name(func, prefix + specname) assert not hasattr(concreteclass, specname) setattr(concreteclass, specname, newfunc) specialized_methods.append(newfunc) # Return a dispatcher function, specialized on the exact type of 'ctx' def dispatch(ctx, *args): return getattr(ctx, specname)(*args) dispatch._annspecialcase_ = 'specialize:argtype(0)' dispatch._specialized_methods_ = specialized_methods return func_with_new_name(dispatch, specname)
def _new_copy_contents_fun(SRC_TP, DST_TP, CHAR_TP, name): @specialize.arg(0) def _str_ofs(TP, item): return (llmemory.offsetof(TP, 'chars') + llmemory.itemoffsetof(TP.chars, 0) + llmemory.sizeof(CHAR_TP) * item) @signature(types.any(), types.any(), types.int(), returns=types.any()) @specialize.arg(0) def _get_raw_buf(TP, src, ofs): assert typeOf(src).TO == TP assert ofs >= 0 return llmemory.cast_ptr_to_adr(src) + _str_ofs(TP, ofs) _get_raw_buf._always_inline_ = True @jit.oopspec('stroruni.copy_contents(src, dst, srcstart, dststart, length)') @signature(types.any(), types.any(), types.int(), types.int(), types.int(), returns=types.none()) def copy_string_contents(src, dst, srcstart, dststart, length): """Copies 'length' characters from the 'src' string to the 'dst' string, starting at position 'srcstart' and 'dststart'.""" # xxx Warning: don't try to do this at home. It relies on a lot # of details to be sure that it works correctly in all cases. # Notably: no GC operation at all from the first cast_ptr_to_adr() # because it might move the strings. The keepalive_until_here() # are obscurely essential to make sure that the strings stay alive # longer than the raw_memcopy(). assert length >= 0 # from here, no GC operations can happen src = _get_raw_buf(SRC_TP, src, srcstart) dst = _get_raw_buf(DST_TP, dst, dststart) llmemory.raw_memcopy(src, dst, llmemory.sizeof(CHAR_TP) * length) # end of "no GC" section keepalive_until_here(src) keepalive_until_here(dst) copy_string_contents._always_inline_ = True copy_string_contents = func_with_new_name(copy_string_contents, 'copy_%s_contents' % name) @jit.oopspec('stroruni.copy_string_to_raw(src, ptrdst, srcstart, length)') def copy_string_to_raw(src, ptrdst, srcstart, length): """ Copies 'length' characters from the 'src' string to the 'ptrdst' buffer, starting at position 'srcstart'. 'ptrdst' must be a non-gc Array of Char. """ # xxx Warning: same note as above apply: don't do this at home assert length >= 0 # from here, no GC operations can happen src = _get_raw_buf(SRC_TP, src, srcstart) adr = llmemory.cast_ptr_to_adr(ptrdst) dstbuf = adr + llmemory.itemoffsetof(typeOf(ptrdst).TO, 0) llmemory.raw_memcopy(src, dstbuf, llmemory.sizeof(CHAR_TP) * length) # end of "no GC" section keepalive_until_here(src) copy_string_to_raw._always_inline_ = True copy_string_to_raw = func_with_new_name(copy_string_to_raw, 'copy_%s_to_raw' % name) return copy_string_to_raw, copy_string_contents
def add_source_attribute(self, name, value, mixin=False): if isinstance(value, property): # special case for property object if value.fget is not None: newname = name + '__getter__' func = func_with_new_name(value.fget, newname) self.add_source_attribute(newname, func, mixin) if value.fset is not None: newname = name + '__setter__' func = func_with_new_name(value.fset, newname) self.add_source_attribute(newname, func, mixin) self.classdict[name] = Constant(value) return if isinstance(value, types.FunctionType): # for debugging if not hasattr(value, 'class_'): value.class_ = self.pyobj if self.specialize: # make a custom funcdesc that specializes on its first # argument (i.e. 'self'). from rpython.annotator.specialize import specialize_argtype def argtype0(funcdesc, args_s): return specialize_argtype(funcdesc, args_s, 0) funcdesc = FunctionDesc(self.bookkeeper, value, specializer=argtype0) self.classdict[name] = funcdesc return if mixin: # make a new copy of the FunctionDesc for this class, # but don't specialize further for all subclasses funcdesc = FunctionDesc(self.bookkeeper, value) self.classdict[name] = funcdesc return # NB. if value is, say, AssertionError.__init__, then we # should not use getdesc() on it. Never. The problem is # that the py lib has its own AssertionError.__init__ which # is of type FunctionType. But bookkeeper.immutablevalue() # will do the right thing in s_get_value(). if isinstance(value, staticmethod) and mixin: # make a new copy of staticmethod func = value.__get__(42) value = staticmethod(func_with_new_name(func, func.__name__)) if type(value) in MemberDescriptorTypes: # skip __slots__, showing up in the class as 'member' objects return if name == '__init__' and self.is_builtin_exception_class(): # pretend that built-in exceptions have no __init__, # unless explicitly specified in builtin.py from rpython.annotator.builtin import BUILTIN_ANALYZERS value = getattr(value, 'im_func', value) if value not in BUILTIN_ANALYZERS: return self.classdict[name] = Constant(value)
def import_from_mixin(M, special_methods=['__init__', '__del__']): """Copy all methods and class attributes from the class M into the current scope. Should be called when defining a class body. Function and staticmethod objects are duplicated, which means that annotation will not consider them as identical to another copy in another unrelated class. By default, "special" methods and class attributes, with a name like "__xxx__", are not copied unless they are "__init__" or "__del__". The list can be changed with the optional second argument. """ flatten = {} caller = sys._getframe(1) caller_name = caller.f_globals.get('__name__') immutable_fields = [] for base in inspect.getmro(M): if base is object: continue for key, value in base.__dict__.items(): if key == '_immutable_fields_': immutable_fields.extend(value) continue if key.startswith('__') and key.endswith('__'): if key not in special_methods: continue if key in flatten: continue if isinstance(value, types.FunctionType): value = func_with_new_name(value, value.__name__) elif isinstance(value, staticmethod): func = value.__get__(42) func = func_with_new_name(func, func.__name__) if caller_name: # staticmethods lack a unique im_class so further # distinguish them from themselves func.__module__ = caller_name value = staticmethod(func) elif isinstance(value, classmethod): raise AssertionError("classmethods not supported " "in 'import_from_mixin'") flatten[key] = value # target = caller.f_locals for key, value in flatten.items(): if key in target: raise Exception("import_from_mixin: would overwrite the value " "already defined locally for %r" % (key, )) if key == '_mixin_': raise Exception("import_from_mixin(M): class M should not " "have '_mixin_ = True'") target[key] = value if immutable_fields: target['_immutable_fields_'] = target.get('_immutable_fields_', []) + immutable_fields
def import_from_mixin(M, special_methods=['__init__', '__del__']): """Copy all methods and class attributes from the class M into the current scope. Should be called when defining a class body. Function and staticmethod objects are duplicated, which means that annotation will not consider them as identical to another copy in another unrelated class. By default, "special" methods and class attributes, with a name like "__xxx__", are not copied unless they are "__init__" or "__del__". The list can be changed with the optional second argument. """ flatten = {} caller = sys._getframe(1) caller_name = caller.f_globals.get('__name__') immutable_fields = [] for base in inspect.getmro(M): if base is object: continue for key, value in base.__dict__.items(): if key == '_immutable_fields_': immutable_fields.extend(value) continue if key.startswith('__') and key.endswith('__'): if key not in special_methods: continue if key in flatten: continue if isinstance(value, types.FunctionType): value = func_with_new_name(value, value.__name__) elif isinstance(value, staticmethod): func = value.__get__(42) func = func_with_new_name(func, func.__name__) if caller_name: # staticmethods lack a unique im_class so further # distinguish them from themselves func.__module__ = caller_name value = staticmethod(func) elif isinstance(value, classmethod): raise AssertionError("classmethods not supported " "in 'import_from_mixin'") flatten[key] = value # target = caller.f_locals for key, value in flatten.items(): if key in target: raise Exception("import_from_mixin: would overwrite the value " "already defined locally for %r" % (key,)) if key == '_mixin_': raise Exception("import_from_mixin(M): class M should not " "have '_mixin_ = True'") target[key] = value if immutable_fields: target['_immutable_fields_'] = target.get('_immutable_fields_', []) + immutable_fields
def test_func_rename_decorator(): def bar(): 'doc' bar2 = func_with_new_name(bar, 'bar2') assert bar.__doc__ == bar2.__doc__ == 'doc' bar.__doc__ = 'new doc' bar3 = func_with_new_name(bar, 'bar3') assert bar3.__doc__ == 'new doc' assert bar2.__doc__ != bar3.__doc__
def test_func_rename_decorator(): def bar(): 'doc' bar2 = func_with_new_name(bar, 'bar2') assert bar.func_doc == bar2.func_doc == 'doc' bar.func_doc = 'new doc' bar3 = func_with_new_name(bar, 'bar3') assert bar3.func_doc == 'new doc' assert bar2.func_doc != bar3.func_doc
def make_func_for_size(N): @jit.dont_look_inside def ll_append_0(ll_builder, ll_str): _ll_append(ll_builder, ll_str, 0, N) ll_append_0 = func_with_new_name(ll_append_0, "ll_append_0_%d" % N) # @jit.dont_look_inside def ll_append_start(ll_builder, ll_str, start): _ll_append(ll_builder, ll_str, start, N) ll_append_start = func_with_new_name(ll_append_start, "ll_append_start_%d" % N) return ll_append_0, ll_append_start, N
def new_dtype_getter(name): def _get_dtype(space): from pypy.module.micronumpy.interp_dtype import get_dtype_cache return get_dtype_cache(space).dtypes_by_name[name] def new(space, w_subtype, w_value=None): dtype = _get_dtype(space) return dtype.itemtype.coerce_subtype(space, w_subtype, w_value) def descr_reduce(self, space): return self.reduce(space) return func_with_new_name(new, name + "_box_new"), staticmethod(_get_dtype), func_with_new_name(descr_reduce, "descr_reduce")
def new_dtype_getter(name): def _get_dtype(space): from pypy.module.micronumpy.interp_dtype import get_dtype_cache return get_dtype_cache(space).dtypes_by_name[name] def new(space, w_subtype, w_value=None): dtype = _get_dtype(space) return dtype.itemtype.coerce_subtype(space, w_subtype, w_value) def descr_reduce(self, space): return self.reduce(space) return func_with_new_name( new, name + "_box_new"), staticmethod(_get_dtype), func_with_new_name( descr_reduce, "descr_reduce")
def _fix_fillchar(func): # XXX: hack from rpython.tool.sourcetools import func_with_new_name func = func_with_new_name(func, func.__name__) func.unwrap_spec = func.unwrap_spec.copy() func.unwrap_spec['w_fillchar'] = WrappedDefault(u' ') return func
def _make_binop_impl(symbol, specialnames): left, right = specialnames errormsg = "unsupported operand type(s) for %s: '%%T' and '%%T'" % ( symbol.replace('%', '%%'), ) seq_bug_compat = (symbol == '+' or symbol == '*') @use_special_method_shortcut(left) def shortcut_binop(space, w_obj1, w_obj2): # makes a few assumptions: if the two rpython types are the same, then # the left and the right implementations are the same too w_impl = space.lookup(w_obj1, left) if w_impl is not None: w_res = space.get_and_call_function(w_impl, w_obj1, w_obj2) if _check_notimplemented(space, w_res): return w_res raise oefmt(space.w_TypeError, errormsg, w_obj1, w_obj2) def binop_impl(space, w_obj1, w_obj2): # shortcut: rpython classes are the same if type(w_obj1) is type(w_obj2) and not w_obj1.user_overridden_class: w_res = shortcut_binop(space, w_obj1, w_obj2) if _check_notimplemented(space, w_res): return w_res w_res = _call_binop_impl(space, w_obj1, w_obj2, left, right, seq_bug_compat) if w_res is not None: return w_res raise oefmt(space.w_TypeError, errormsg, w_obj1, w_obj2) return func_with_new_name(binop_impl, "binop_%s_impl" % left.strip('_'))
def setup(space): for name in ObjSpace.ConstantTable + ObjSpace.ExceptionTable + BUILTIN_TYPES: setattr(space, "w_" + name, w_some_obj()) space.w_type = w_some_type() # for (name, _, arity, _) in ObjSpace.MethodTable: if name == "type": continue args = ["w_%d" % i for i in range(arity)] params = args[:] d = {"is_root": is_root, "w_some_obj": w_some_obj} if name in ("get",): params[-1] += "=None" exec compile2( """\ def meth(%s): %s return w_some_obj() """ % (", ".join(params), "; ".join(["is_root(%s)" % arg for arg in args])) ) in d meth = func_with_new_name(d["meth"], name) setattr(space, name, meth) # for name in ObjSpace.IrregularOpTable: assert hasattr(space, name) # missing?
def _binop_right_impl(ufunc_name): def impl(self, space, w_other, w_out=None): from pypy.module.micronumpy import interp_ufuncs return getattr(interp_ufuncs.get(space), ufunc_name).call(space, [w_other, self, w_out]) return func_with_new_name(impl, "binop_right_%s_impl" % ufunc_name)
def _unaryop_impl(ufunc_name): def impl(self, space, w_out=None): from pypy.module.micronumpy import interp_ufuncs return getattr(interp_ufuncs.get(space), ufunc_name).call(space, [self, w_out]) return func_with_new_name(impl, "unaryop_%s_impl" % ufunc_name)
def _make_inplace_impl(symbol, specialnames): specialname, = specialnames assert specialname.startswith('__i') and specialname.endswith('__') noninplacespacemethod = specialname[3:-2] if noninplacespacemethod in ['or', 'and']: noninplacespacemethod += '_' # not too clean seq_bug_compat = (symbol == '+=' or symbol == '*=') rhs_method = '__r' + specialname[3:] def inplace_impl(space, w_lhs, w_rhs): w_impl = space.lookup(w_lhs, specialname) if w_impl is not None: # 'seq_bug_compat' is for cpython bug-to-bug compatibility: # see objspace/test/test_descrobject.*rmul_overrides. # For cases like "list += object-overriding-__radd__". if (seq_bug_compat and space.type(w_lhs).flag_sequence_bug_compat and not space.type(w_rhs).flag_sequence_bug_compat): w_res = _invoke_binop(space, space.lookup(w_rhs, rhs_method), w_rhs, w_lhs) if w_res is not None: return w_res # xxx if __radd__ is defined but returns NotImplemented, # then it might be called again below. Oh well, too bad. # Anyway that's a case where we're likely to end up in # a TypeError. # w_res = space.get_and_call_function(w_impl, w_lhs, w_rhs) if _check_notimplemented(space, w_res): return w_res # XXX fix the error message we get here return getattr(space, noninplacespacemethod)(w_lhs, w_rhs) return func_with_new_name(inplace_impl, 'inplace_%s_impl'%specialname.strip('_'))
def install_general_args_trampoline(type_, mm, is_local, op_name): def function(space, w_transparent_list, __args__): args = __args__.prepend(space.wrap(op_name)) return space.call_args(w_transparent_list.w_controller, args) function = func_with_new_name(function, mm.name) mm.register(function, type_)
def _make_comparison(name): op = getattr(operator, name) requires_ordering = name not in ('eq', 'ne') # def _cmp(self, w_other): from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitive space = self.space if not isinstance(w_other, W_CData): return space.w_NotImplemented with self as ptr1, w_other as ptr2: if requires_ordering: if (isinstance(self.ctype, W_CTypePrimitive) or isinstance(w_other.ctype, W_CTypePrimitive)): raise OperationError( space.w_TypeError, space.wrap( "cannot do comparison on a primitive cdata")) ptr1 = rffi.cast(lltype.Unsigned, ptr1) ptr2 = rffi.cast(lltype.Unsigned, ptr2) result = op(ptr1, ptr2) return space.newbool(result) # return func_with_new_name(_cmp, name)
def format_num_helper_generator(fmt, digits): def format_num_helper(space, w_value): if (not space.isinstance_w(w_value, space.w_int) and not space.isinstance_w(w_value, space.w_long)): try: w_value = maybe_int(space, w_value) except OperationError: try: w_value = space.long(w_value) except OperationError as operr: if operr.match(space, space.w_TypeError): raise oefmt( space.w_TypeError, "%s formato: necesita un numero, no %T", fmt, w_value) else: raise try: value = space.int_w(w_value) return fmt % (value,) except OperationError as operr: if not operr.match(space, space.w_OverflowError): raise num = space.bigint_w(w_value) return num.format(digits) return func_with_new_name(format_num_helper, 'base%d_num_helper' % len(digits))
def make_helper(firstarg, stmt, miniglobals): header = "def f(%s):" % (', '.join(argnames[firstarg:],)) source = py.code.Source(stmt) source = source.putaround(header) exec source.compile() in miniglobals f = miniglobals['f'] return func_with_new_name(f, 'memo_%s_%d' % (name, firstarg))
def _relative_unaryop(name): def INSN(self, loc): code = loc.location_code() for possible_code in unrolling_location_codes: if code == possible_code: val = getattr(loc, "value_" + possible_code)() if possible_code == 'i': # This is for CALL or JMP only. if self.WORD == 4: _rx86_getattr(self, name + "_l")(val) self.add_pending_relocation() else: # xxx can we avoid "MOV r11, $val; JMP/CALL *r11" # in case it would fit a 32-bit displacement? # Hard, because we don't know yet where this insn # will end up... assert self.WORD == 8 self._load_scratch(val) _rx86_getattr(self, name + "_r")( X86_64_SCRATCH_REG.value) else: methname = name + "_" + possible_code _rx86_getattr(self, methname)(val) return func_with_new_name(INSN, "INSN_" + name)
def generic_new_descr(W_Type): def descr_new(space, w_subtype, __args__): self = space.allocate_instance(W_Type, w_subtype) W_Type.__init__(self, space) return space.wrap(self) descr_new = func_with_new_name(descr_new, 'descr_new_%s' % W_Type.__name__) return interp2app(descr_new)
def setup(space): for name in (ObjSpace.ConstantTable + ObjSpace.ExceptionTable + ['int', 'str', 'float', 'long', 'tuple', 'list', 'dict', 'unicode', 'complex', 'slice', 'bool', 'basestring', 'object', 'bytearray', 'buffer']): setattr(space, 'w_' + name, w_some_obj()) space.w_type = w_some_type() # for (name, _, arity, _) in ObjSpace.MethodTable: if name == 'type': continue args = ['w_%d' % i for i in range(arity)] params = args[:] d = {'is_root': is_root, 'w_some_obj': w_some_obj} if name in ('get',): params[-1] += '=None' exec compile2("""\ def meth(%s): %s return w_some_obj() """ % (', '.join(params), '; '.join(['is_root(%s)' % arg for arg in args]))) in d meth = func_with_new_name(d['meth'], name) setattr(space, name, meth) # for name in ObjSpace.IrregularOpTable: assert hasattr(space, name) # missing?
def _relative_unaryop(name): def INSN(self, loc): code = loc.location_code() for possible_code in unrolling_location_codes: if code == possible_code: val = getattr(loc, "value_" + possible_code)() if possible_code == 'i': # This is for CALL or JMP only. If target is # immediately starting with another JMP instruction, # follow it now. val = self._follow_jump_instructions(val) if self.WORD == 4: _rx86_getattr(self, name + "_l")(val) self.add_pending_relocation() else: # xxx can we avoid "MOV r11, $val; JMP/CALL *r11" # in case it would fit a 32-bit displacement? # Hard, because we don't know yet where this insn # will end up... assert self.WORD == 8 self._load_scratch(val) _rx86_getattr(self, name + "_r")(X86_64_SCRATCH_REG.value) else: methname = name + "_" + possible_code _rx86_getattr(self, methname)(val) return func_with_new_name(INSN, "INSN_" + name)
def make_helper(firstarg, stmt, miniglobals): header = "def f(%s):" % (', '.join(argnames[firstarg:], )) source = py.code.Source(stmt) source = source.putaround(header) exec source.compile() in miniglobals f = miniglobals['f'] return func_with_new_name(f, 'memo_%s_%d' % (name, firstarg))
def setup(space): for name in (ObjSpace.ConstantTable + ObjSpace.ExceptionTable + BUILTIN_TYPES): if name != "str": setattr(space, 'w_' + name, w_some_obj()) space.w_bytes = w_some_obj() space.w_text = w_some_obj() space.w_type = w_some_type() # for (name, _, arity, _) in ObjSpace.MethodTable: if name == 'type': continue args = ['w_%d' % i for i in range(arity)] params = args[:] d = {'is_root': is_root, 'w_some_obj': w_some_obj} if name in ('get',): params[-1] += '=None' exec compile2("""\ def meth(%s): %s return w_some_obj() """ % (', '.join(params), '; '.join(['is_root(%s)' % arg for arg in args]))) in d meth = func_with_new_name(d['meth'], name) setattr(space, name, meth) # for name in ObjSpace.IrregularOpTable: assert hasattr(space, name) # missing?
def interphook(self, name): "NOT_RPYTHON" def appcaller(space, *args_w): if not isinstance(space, ObjSpace): raise TypeError("first argument must be a space instance.") # the last argument can be an Arguments w_func = self.wget(space, name) if not args_w: return space.call_function(w_func) else: args = args_w[-1] assert args is not None if not isinstance(args, Arguments): return space.call_function(w_func, *args_w) else: if len(args_w) == 2: return space.call_obj_args(w_func, args_w[0], args) elif len(args_w) > 2: # xxx is this used at all? # ...which is merged with the previous arguments, if any args = args.replace_arguments(list(args_w[:-1]) + args.arguments_w) return space.call_args(w_func, args) def get_function(space): w_func = self.wget(space, name) return space.unwrap(w_func) appcaller = func_with_new_name(appcaller, name) appcaller.get_function = get_function return appcaller
def setup(space): for name in (ObjSpace.ConstantTable + ObjSpace.ExceptionTable + BUILTIN_TYPES): setattr(space, 'w_' + name, w_some_obj()) space.w_type = w_some_type() # for (name, _, arity, _) in ObjSpace.MethodTable: if name == 'type': continue args = ['w_%d' % i for i in range(arity)] params = args[:] d = {'is_root': is_root, 'w_some_obj': w_some_obj} if name in ('get',): params[-1] += '=None' exec compile2("""\ def meth(%s): %s return w_some_obj() """ % (', '.join(params), '; '.join(['is_root(%s)' % arg for arg in args]))) in d meth = func_with_new_name(d['meth'], name) setattr(space, name, meth) # for name in ObjSpace.IrregularOpTable: assert hasattr(space, name) # missing?
def new_unary_math_function(name, can_overflow, c99): if sys.platform == 'win32' and c99: c_func = math_llexternal(name, [rffi.DOUBLE], rffi.DOUBLE, save_err=rffi.RFFI_FULL_ERRNO_ZERO) else: c_func = llexternal(name, [rffi.DOUBLE], rffi.DOUBLE, save_err=rffi.RFFI_FULL_ERRNO_ZERO) def ll_math(x): r = c_func(x) # Error checking fun. Copied from CPython 2.6 errno = rposix.get_saved_errno() if not isfinite(r): if math.isnan(r): if math.isnan(x): errno = 0 else: errno = EDOM else: # isinf(r) if not isfinite(x): errno = 0 elif can_overflow: errno = ERANGE else: errno = EDOM if errno: _likely_raise(errno, r) return r return func_with_new_name(ll_math, 'll_math_' + name)
def add(Proto): for key, value in Proto.__dict__.items(): if (not key.startswith('__') and not key.startswith('_mixin_') or key == '__del__'): if hasattr(value, "func_name"): value = func_with_new_name(value, value.func_name) body[key] = value
def _unaryop(name): def INSN(self, loc): code = loc.location_code() for possible_code in unrolling_location_codes: if code == possible_code: val = getattr(loc, "value_" + possible_code)() # Faking out of certain operations for x86_64 fits32 = rx86.fits_in_32bits if possible_code == 'i' and not fits32(val): self._load_scratch(val) # for 'PUSH(imm)' _rx86_getattr(self, name + "_r")(X86_64_SCRATCH_REG.value) return if possible_code == 'j' and not fits32(val): val = self._addr_as_reg_offset(val) _rx86_getattr(self, name + "_m")(val) return if possible_code == 'm' and not fits32(val[1]): val = self._fix_static_offset_64_m(val) if possible_code == 'a' and not fits32(val[3]): val = self._fix_static_offset_64_a(val) methname = name + "_" + possible_code _rx86_getattr(self, methname)(val) return func_with_new_name(INSN, "INSN_" + name)
def new_unary_math_function(name, can_overflow, c99): if sys.platform == 'win32' and c99: c_func = math_llexternal(name, [rffi.DOUBLE], rffi.DOUBLE, save_err=rffi.RFFI_FULL_ERRNO_ZERO) else: c_func = llexternal(name, [rffi.DOUBLE], rffi.DOUBLE, save_err=rffi.RFFI_FULL_ERRNO_ZERO) def ll_math(x): r = c_func(x) # Error checking fun. Copied from CPython 2.6 errno = rposix.get_saved_errno() if not isfinite(r): if isnan(r): if isnan(x): errno = 0 else: errno = EDOM else: # isinf(r) if not isfinite(x): errno = 0 elif can_overflow: errno = ERANGE else: errno = EDOM if errno: _likely_raise(errno, r) return r return func_with_new_name(ll_math, 'll_math_' + name)
def interphook(self, name): def appcaller(space, *args_w): if not isinstance(space, ObjSpace): raise TypeError("first argument must be a space instance.") # the last argument can be an Arguments w_func = self.wget(space, name) if not args_w: return space.call_function(w_func) else: args = args_w[-1] assert args is not None if not isinstance(args, Arguments): return space.call_function(w_func, *args_w) else: if len(args_w) == 2: return space.call_obj_args(w_func, args_w[0], args) elif len(args_w) > 2: # xxx is this used at all? # ...which is merged with the previous arguments, if any args = args.replace_arguments( list(args_w[:-1]) + args.arguments_w) return space.call_args(w_func, args) def get_function(space): w_func = self.wget(space, name) return space.unwrap(w_func) appcaller = func_with_new_name(appcaller, name) appcaller.get_function = get_function return appcaller
def _make_inplace_impl(symbol, specialnames): specialname, = specialnames assert specialname.startswith('__i') and specialname.endswith('__') noninplacespacemethod = specialname[3:-2] if noninplacespacemethod in ['or', 'and']: noninplacespacemethod += '_' # not too clean seq_bug_compat = (symbol == '+=' or symbol == '*=') rhs_method = '__r' + specialname[3:] def inplace_impl(space, w_lhs, w_rhs): w_impl = space.lookup(w_lhs, specialname) if w_impl is not None: # 'seq_bug_compat' is for cpython bug-to-bug compatibility: # see objspace/test/test_descrobject.*rmul_overrides. # For cases like "list += object-overriding-__radd__". if (seq_bug_compat and space.type(w_lhs).flag_sequence_bug_compat and not space.type(w_rhs).flag_sequence_bug_compat): w_res = _invoke_binop(space, space.lookup(w_rhs, rhs_method), w_rhs, w_lhs) if w_res is not None: return w_res # xxx if __radd__ is defined but returns NotImplemented, # then it might be called again below. Oh well, too bad. # Anyway that's a case where we're likely to end up in # a TypeError. # w_res = space.get_and_call_function(w_impl, w_lhs, w_rhs) if _check_notimplemented(space, w_res): return w_res # XXX fix the error message we get here return getattr(space, noninplacespacemethod)(w_lhs, w_rhs) return func_with_new_name(inplace_impl, 'inplace_%s_impl' % specialname.strip('_'))
def install_args_w_trampoline(type_, mm, is_local, op_name): def function(space, w_transparent_list, *args_w): args = Arguments(space, [space.wrap(op_name)] + list(args_w[:-1]) + args_w[-1]) return space.call_args(w_transparent_list.w_controller, args) function = func_with_new_name(function, mm.name) mm.register(function, type_, *([W_ANY] * (mm.arity - 1)))
def specialize_call(self, hop): rtyper = hop.rtyper signature_args = self.normalize_args(*hop.args_s) args_r = [rtyper.getrepr(s_arg) for s_arg in signature_args] args_ll = [r_arg.lowleveltype for r_arg in args_r] s_result = hop.s_result r_result = rtyper.getrepr(s_result) ll_result = r_result.lowleveltype name = getattr(self, 'name', None) or self.instance.__name__ impl = getattr(self, 'lltypeimpl', None) fakeimpl = getattr(self, 'lltypefakeimpl', self.instance) if impl: if hasattr(self, 'lltypefakeimpl'): # If we have both an llimpl and an llfakeimpl, # we need a wrapper that selects the proper one and calls it from rpython.tool.sourcetools import func_with_new_name # Using '*args' is delicate because this wrapper is also # created for init-time functions like llarena.arena_malloc # which are called before the GC is fully initialized args = ', '.join(['arg%d' % i for i in range(len(args_ll))]) d = {'original_impl': impl, 's_result': s_result, 'fakeimpl': fakeimpl, '__name__': __name__, } exec py.code.compile(""" from rpython.rlib.objectmodel import running_on_llinterp from rpython.rlib.debug import llinterpcall from rpython.rlib.jit import dont_look_inside # note: we say 'dont_look_inside' mostly because the # JIT does not support 'running_on_llinterp', but in # theory it is probably right to stop jitting anyway. @dont_look_inside def ll_wrapper(%s): if running_on_llinterp: return llinterpcall(s_result, fakeimpl, %s) else: return original_impl(%s) """ % (args, args, args)) in d impl = func_with_new_name(d['ll_wrapper'], name + '_wrapper') if rtyper.annotator.translator.config.translation.sandbox: impl._dont_inline_ = True # store some attributes to the 'impl' function, where # the eventual call to rtyper.getcallable() will find them # and transfer them to the final lltype.functionptr(). impl._llfnobjattrs_ = { '_name': self.name, '_safe_not_sandboxed': self.safe_not_sandboxed, } obj = rtyper.getannmixlevel().delayedfunction( impl, signature_args, hop.s_result) else: FT = FuncType(args_ll, ll_result) obj = functionptr(FT, name, _external_name=self.name, _callable=fakeimpl, _safe_not_sandboxed=self.safe_not_sandboxed) vlist = [hop.inputconst(typeOf(obj), obj)] + hop.inputargs(*args_r) hop.exception_is_here() return hop.genop('direct_call', vlist, r_result)
def _make_descr_cmp(opname): op = getattr(rbigint, opname) @delegate_other def descr_impl(self, space, w_other): return space.newbool(op(self.num, w_other.asbigint())) return func_with_new_name(descr_impl, "descr_" + opname)
def _binop_impl(ufunc_name): def impl(self, space, w_other, w_out=None): from pypy.module.micronumpy import ufuncs return getattr(ufuncs.get(space), ufunc_name).call(space, [self, w_other, w_out], self.sig, self.cast, self.extobj) return func_with_new_name(impl, "binop_%s_impl" % ufunc_name)
def create_builder(name, strtype, builder_cls, newmethod): if strtype is str: unwrap = 'bytes' else: unwrap = unicode class W_Builder(W_Root): def __init__(self, space, size): if size < 0: self.builder = builder_cls() else: self.builder = builder_cls(size) def _check_done(self, space): if self.builder is None: raise oefmt(space.w_ValueError, "Can't operate on a built builder") @unwrap_spec(size=int) def descr__new__(space, w_subtype, size=-1): return W_Builder(space, size) @unwrap_spec(s=unwrap) def descr_append(self, space, s): self._check_done(space) self.builder.append(s) @unwrap_spec(s=unwrap, start=int, end=int) def descr_append_slice(self, space, s, start, end): self._check_done(space) if not 0 <= start <= end <= len(s): raise oefmt(space.w_ValueError, "bad start/stop") self.builder.append_slice(s, start, end) def descr_build(self, space): self._check_done(space) w_s = getattr(space, newmethod)(self.builder.build()) self.builder = None return w_s def descr_len(self, space): if self.builder is None: raise oefmt(space.w_ValueError, "no length of built builder") return space.newint(self.builder.getlength()) W_Builder.__name__ = "W_%s" % name W_Builder.typedef = TypeDef( name, __new__=interp2app( func_with_new_name(W_Builder.descr__new__.im_func, '%s_new' % (name, ))), append=interp2app(W_Builder.descr_append), append_slice=interp2app(W_Builder.descr_append_slice), build=interp2app(W_Builder.descr_build), __len__=interp2app(W_Builder.descr_len), ) W_Builder.typedef.acceptable_as_base_class = False return W_Builder
def new_malloc(TP, name): @enforceargs(int) def mallocstr(length): ll_assert(length >= 0, "negative string length") r = malloc(TP, length) if not we_are_translated() or not malloc_zero_filled: r.hash = 0 return r return func_with_new_name(mallocstr, name)
def new_malloc(TP, name): def mallocstr(length): ll_assert(length >= 0, "negative string length") r = malloc(TP, length) if not we_are_translated() or not malloc_zero_filled: r.hash = 0 return r mallocstr._annspecialcase_ = 'specialize:semierased' return func_with_new_name(mallocstr, name)
def _make_unaryop_impl(symbol, specialnames): specialname, = specialnames errormsg = "unsupported operand type for unary %s: '%%T'" % symbol def unaryop_impl(space, w_obj): w_impl = space.lookup(w_obj, specialname) if w_impl is None: raise operationerrfmt(space.w_TypeError, errormsg, w_obj) return space.get_and_call_function(w_impl, w_obj) return func_with_new_name(unaryop_impl, 'unaryop_%s_impl'%specialname.strip('_'))
def _see_interp2app(self, interp2app): """Called by GatewayCache.build()""" activation = interp2app._code.activation def check(): scope_w = [w_some_obj()] * NonConstant(42) w_result = activation._run(self, scope_w) is_root(w_result) check = func_with_new_name(check, 'check__' + interp2app.name) self._seen_extras.append(check)
def _make_unaryop_impl(symbol, specialnames): specialname, = specialnames errormsg = "tipo de operando no apoyado para unary %s: '%%T'" % symbol def unaryop_impl(space, w_obj): w_impl = space.lookup(w_obj, specialname) if w_impl is None: raise oefmt(space.w_TypeError, errormsg, w_obj) return space.get_and_call_function(w_impl, w_obj) return func_with_new_name(unaryop_impl, 'unaryop_%s_impl'%specialname.strip('_'))
def _see_interp2app(self, interp2app): "NOT_RPYTHON" activation = interp2app._code.activation def check(): scope_w = [w_some_obj()] * NonConstant(42) w_result = activation._run(self, scope_w) is_root(w_result) check = func_with_new_name(check, 'check__' + interp2app.name) self._seen_extras.append(check)
def create_builder(name, strtype, builder_cls): class W_Builder(W_Root): def __init__(self, space, size): if size < 0: self.builder = builder_cls() else: self.builder = builder_cls(size) def _check_done(self, space): if self.builder is None: raise OperationError(space.w_ValueError, space.wrap( "Can't operate on a built builder")) @unwrap_spec(size=int) def descr__new__(space, w_subtype, size=-1): return W_Builder(space, size) @unwrap_spec(s=strtype) def descr_append(self, space, s): self._check_done(space) self.builder.append(s) @unwrap_spec(s=strtype, start=int, end=int) def descr_append_slice(self, space, s, start, end): self._check_done(space) if not 0 <= start <= end <= len(s): raise OperationError(space.w_ValueError, space.wrap( "bad start/stop")) self.builder.append_slice(s, start, end) def descr_build(self, space): self._check_done(space) s = self.builder.build() self.builder = None if strtype is str: return space.wrapbytes(s) else: return space.wrap(s) def descr_len(self, space): if self.builder is None: raise OperationError(space.w_ValueError, space.wrap( "no length of built builder")) return space.wrap(self.builder.getlength()) W_Builder.__name__ = "W_%s" % name W_Builder.typedef = TypeDef(name, __new__ = interp2app(func_with_new_name( W_Builder.descr__new__.im_func, '%s_new' % (name,))), append = interp2app(W_Builder.descr_append), append_slice = interp2app(W_Builder.descr_append_slice), build = interp2app(W_Builder.descr_build), __len__ = interp2app(W_Builder.descr_len), ) W_Builder.typedef.acceptable_as_base_class = False return W_Builder
def declare_new_int_comparison(opname): import operator from rpython.tool.sourcetools import func_with_new_name op = getattr(operator, opname) def f(space, w_int1, w_int2): i = w_int1.intval j = w_int2.intval return space.newbool(op(i, j)) name = "%s__Int_Int" % (opname,) return func_with_new_name(f, name), name
def make_generic(funcname): def func(space, w_self): v = w_self._value if len(v) == 0: return space.w_False for idx in range(len(v)): if not getattr(unicodedb, funcname)(ord(v[idx])): return space.w_False return space.w_True return func_with_new_name(func, "unicode_%s__Unicode" % (funcname, ))
def test_rename(): def f(x, y=5): return x + y f.prop = int g = func_with_new_name(f, "g") assert g(4, 5) == 9 assert g.func_name == "g" assert f.func_defaults == (5,) assert g.prop is int
def _make_binop_impl(symbol, specialnames): left, right = specialnames errormsg = "unsupported operand type(s) for %s: '%%N' and '%%N'" % ( symbol.replace('%', '%%'), ) seq_bug_compat = (symbol == '+' or symbol == '*') printerrormsg = None if symbol == ">>": printerrormsg = errormsg + '. Did you mean "print(<message>, file=<output_stream>)"?' if symbol == "-": printerrormsg = errormsg + '. Did you mean "print(<-number>)"?' def binop_impl(space, w_obj1, w_obj2): w_typ1 = space.type(w_obj1) w_typ2 = space.type(w_obj2) w_left_src, w_left_impl = space.lookup_in_type_where(w_typ1, left) if space.is_w(w_typ1, w_typ2): w_right_impl = None else: w_right_src, w_right_impl = space.lookup_in_type_where( w_typ2, right) # the logic to decide if the reverse operation should be tried # before the direct one is very obscure. For now, and for # sanity reasons, we just compare the two places where the # __xxx__ and __rxxx__ methods where found by identity. # Note that space.is_w() is potentially not happy if one of them # is None... if w_right_src and (w_left_src is not w_right_src) and w_left_src: # 'seq_bug_compat' is for cpython bug-to-bug compatibility: # see objspace/std/test/test_unicodeobject.*concat_overrides # and objspace/test/test_descrobject.*rmul_overrides. # For cases like "unicode + string subclass". if ((seq_bug_compat and w_typ1.flag_sequence_bug_compat and not w_typ2.flag_sequence_bug_compat) # the non-bug-compat part is the following check: or space.issubtype_w(w_typ2, w_typ1)): if (not space.abstract_issubclass_w( w_left_src, w_right_src) and not space.abstract_issubclass_w( w_typ1, w_right_src)): w_obj1, w_obj2 = w_obj2, w_obj1 w_left_impl, w_right_impl = w_right_impl, w_left_impl w_res = _invoke_binop(space, w_left_impl, w_obj1, w_obj2) if w_res is not None: return w_res w_res = _invoke_binop(space, w_right_impl, w_obj2, w_obj1) if w_res is not None: return w_res if printerrormsg is not None and w_obj1 is space.fromcache( PrintCache).w_print: raise oefmt(space.w_TypeError, printerrormsg, w_typ1, w_typ2) raise oefmt(space.w_TypeError, errormsg, w_typ1, w_typ2) return func_with_new_name(binop_impl, "binop_%s_impl" % left.strip('_'))
def build_type_checkers(type_name, cls=None): """ Builds two api functions: Py_XxxCheck() and Py_XxxCheckExact(). - if `cls` is None, the type is space.w_[type]. - if `cls` is a string, it is the name of a space attribute, e.g. 'w_str'. - else `cls` must be a W_Class with a typedef. """ if cls is None: attrname = "w_" + type_name.lower() def get_w_type(space): return getattr(space, attrname) elif isinstance(cls, str): def get_w_type(space): return getattr(space, cls) else: def get_w_type(space): return space.gettypeobject(cls.typedef) check_name = "Py" + type_name + "_Check" def check(space, w_obj): "Implements the Py_Xxx_Check function" w_obj_type = space.type(w_obj) w_type = get_w_type(space) return space.is_w(w_obj_type, w_type) or space.is_true(space.issubtype(w_obj_type, w_type)) def check_exact(space, w_obj): "Implements the Py_Xxx_CheckExact function" w_obj_type = space.type(w_obj) w_type = get_w_type(space) return space.is_w(w_obj_type, w_type) check = cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)(func_with_new_name(check, check_name)) check_exact = cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)( func_with_new_name(check_exact, check_name + "Exact") ) return check, check_exact