def fn(n): a1 = (n, ) g.a = a1 a2 = (n - 1, ) g.a = a2 jit.promote(n) return a1[0] + a2[0] + gn(a1, a2)
def fn(n): jit.promote(n) try: n = ovfcheck(n + 1) except OverflowError: return 12 else: return n
def _apply(self, funDef): msg = assertClosureV(funDef, self.body) if msg != "True": return FinalBounce(ErrorV(msg)) newEnv = funDef.env promote(newEnv) funDef.env = newEnv.add_attribute(self.funName, funDef) return KeepBouncing(self.expr, funDef.env, self.k)
def stack_get_slice(self, i, j): assert i >= 0 and j >= i l = [None] * (j - i) a = 0 jit.promote(i) for k in range(i, j): l[a] = self.stack[k] a += 1 return l
def Interpret(tree): """Interpret the tree, iteratively.""" set_param(jitdriver, "trace_limit", 25000) register = ReturnType() tree = tree env = Map() k = EndK() while 1: jitdriver.jit_merge_point(tree=tree, env=env, k=k, register=register) if isinstance(k, FinalK): break if isinstance(tree, parser.Num): register, tree, env, k = k._apply(NumV(tree.val), tree, env, k) elif isinstance(tree, parser.Op): k = Op1K(tree.op, tree.lhs, tree.rhs, env, k) tree = tree.lhs elif isinstance(tree, parser.Id): promote(env) register = env.getvalue(tree.name) if isinstance(register, ErrorV): k = FinalK() else: register, tree, env, k = k._apply(register, tree, env, k) elif isinstance(tree, parser.If): k = If0K(tree.nul, tree.true, tree.false, env, k) tree = tree.nul elif isinstance(tree, parser.Func): assert isinstance(tree.arg, parser.Id) register, tree, env, k = k._apply(ClosureV(tree.arg, tree.body, env), tree, env, k) elif isinstance(tree, parser.App): k = App1K(tree.fun, env, k) tree = tree.arg jitdriver.can_enter_jit(tree=tree, env=env, k=k, register=register) elif isinstance(tree, parser.Rec): k = RecK(tree.funName, tree.body, tree.expr, k) dummy = NumV(42) promote(env) env = env.add_attribute(tree.funName, dummy) tree = tree.body else: msg = "Parsing error, tree %s is not valid" % tree.__str__() register = ErrorV(msg) k = FinalK() return register
def f(n): func = lib.getpointer(name, argtypes, restype) res = init_result while n < 10: driver.jit_merge_point(n=n, res=res, func=func) promote(func) res = g(func) n += 1 return res
def get_attr(self, name): map = self.map promote(map) index = map.getindex(name) if index != -1: return self.storage[index] else: print("Free variable : " + name) return 2
def lookup_where_with_method_cache(w_self, name): space = w_self.space promote(w_self) assert space.config.objspace.std.withmethodcache version_tag = promote(w_self.version_tag()) if version_tag is None: tup = w_self._lookup_where(name) return tup w_class, w_value = w_self._pure_lookup_where_with_method_cache(name, version_tag) return w_class, unwrap_cell(space, w_value)
def issubtype(w_self, w_type): promote(w_self) promote(w_type) if w_self.space.config.objspace.std.withtypeversion and we_are_jitted(): version_tag1 = w_self.version_tag() version_tag2 = w_type.version_tag() if version_tag1 is not None and version_tag2 is not None: res = _pure_issubtype(w_self, w_type, version_tag1, version_tag2) return res return _issubtype(w_self, w_type)
def fn(i): if i < 0: g.a1 = [7, 8, 9] g.a2 = [7, 8, 9, 10] jit.promote(i) a1 = g.a1 a1[i + 1] = 15 # make lists mutable a2 = g.a2 a2[i + 1] = 19 return a1[i] + a2[i] + a1[i] + a2[i]
def fn(n, i): res = 0 obj = A() while i > 0: myjitdriver.can_enter_jit(i=i, obj=obj) myjitdriver.jit_merge_point(i=i, obj=obj) promote(obj) res = obj.foo() i-=1 return res
def f(n, a, i): stufflist = StuffList() stufflist.lst = [Stuff(a), Stuff(3)] while n > 0: myjitdriver.can_enter_jit(n=n, i=i, stufflist=stufflist) myjitdriver.jit_merge_point(n=n, i=i, stufflist=stufflist) promote(i) v = Stuff(i) n -= stufflist.lst[v.x].x return n
def _apply(self, fun): msg = assertClosureV(fun, self.fun) if msg != "True": return FinalBounce(ErrorV(msg)) param = fun.arg assert isinstance(param, parser.Id) newEnv = fun.env promote(newEnv) newEnv = newEnv.add_attribute(param.name, self.arg) return KeepBouncing(fun.body, newEnv, self.k)
def fn(n, i): res = 0 obj = A() while i > 0: myjitdriver.can_enter_jit(i=i, obj=obj) myjitdriver.jit_merge_point(i=i, obj=obj) promote(obj) res = obj.foo() i -= 1 return res
def _apply(self, reg, tree, env, k): # reg is suppose to be te interpretation of fun funDef = reg msg = assertClosureV(funDef, self.body) if msg != "True": return ErrorV(msg), tree, env, FinalK() newEnv = funDef.env promote(newEnv) funDef.env = newEnv.add_attribute(self.funName, funDef) return funDef, self.expr, funDef.env, self.k
def lookup_where_with_method_cache(w_self, name): space = w_self.space promote(w_self) assert space.config.objspace.std.withmethodcache version_tag = promote(w_self.version_tag()) if version_tag is None: tup = w_self._lookup_where(name) return tup name = promote_string(name) w_class, w_value = w_self._pure_lookup_where_with_method_cache(name, version_tag) return w_class, unwrap_cell(space, w_value)
def write_attribute(self, name, value): assert isinstance(name, str) assert isinstance(value, int) map = self.map promote(map) index = map.getindex(name) if index != -1: self.storage[index] = value return self.map = map.add_attribute(name) self.storage.append(value)
def stack_get_slice_del(self, i): assert i >= 0 l = [None] * (self.stackpe - i) a = 0 jit.promote(i) for k in range(i, self.stackpe): l[a] = self.stack[k] self.stack[k] = None a += 1 self.stackpe = i return l
def _apply(self, reg, tree, env, k): # reg is expected to be the interpretation of fun fun = reg msg = assertClosureV(fun, self.fun) if msg != "True": return ErrorV(msg), tree, env, FinalK() param = fun.arg assert isinstance(param, parser.Id) newEnv = fun.env promote(newEnv) newEnv = newEnv.add_attribute(param.name, self.arg) return fun, fun.body, newEnv, self.k
def do_fast_call(self, cppthis, args_w, call_local): jit.promote(self) argchain = libffi.ArgChain() argchain.arg(cppthis) i = len(self.arg_defs) for i in range(len(args_w)): conv = self.converters[i] w_arg = args_w[i] conv.convert_argument_libffi(self.space, w_arg, argchain, call_local) for j in range(i + 1, len(self.arg_defs)): conv = self.converters[j] conv.default_argument_libffi(self.space, argchain) return self.executor.execute_libffi(self.space, self._libffifunc, argchain)
def libffi_stuff(i, j): lib = CDLL(libm_name) func = lib.getpointer('fabs', [types.double], types.double) res = 0.0 x = float(j) while i > 0: jitdriver2.jit_merge_point(i=i, res=res, func=func, x=x) promote(func) argchain = ArgChain() argchain.arg(x) res = func.call(argchain, rffi.DOUBLE) i -= 1 return res
def fn(n): if n > 0: a = a1 else: a = a2 a.l = [0, 0] jit.promote(a.x) a.l[a.x] = n a.x += 1 a.l[a.x] = n + 1 x1 = a.l[a.x] a.x -= 1 x2 = a.l[a.x] return x1 + x2
def _get_relative_name(space, modulename, level, w_globals): w = space.wrap ctxt_w_package = space.finditem_str(w_globals, "__package__") ctxt_w_package = jit.promote(ctxt_w_package) level = jit.promote(level) ctxt_package = None if ctxt_w_package is not None and ctxt_w_package is not space.w_None: try: ctxt_package = space.str_w(ctxt_w_package) except OperationError, e: if not e.match(space, space.w_TypeError): raise raise OperationError(space.w_ValueError, space.wrap("__package__ set to non-string"))
def _get_relative_name(space, modulename, level, w_globals): w = space.wrap ctxt_w_package = space.finditem_str(w_globals, '__package__') ctxt_w_package = jit.promote(ctxt_w_package) level = jit.promote(level) ctxt_package = None if ctxt_w_package is not None and ctxt_w_package is not space.w_None: try: ctxt_package = space.str_w(ctxt_w_package) except OperationError, e: if not e.match(space, space.w_TypeError): raise raise OperationError(space.w_ValueError, space.wrap("__package__ set to non-string"))
def pop(self): stackpos = jit.promote(self.stackpos) - 1 assert stackpos >= 0 w_res = self.stack_w[stackpos] self.stack_w[stackpos] = None self.stackpos = stackpos return w_res
def next_skip_x(self, shapelen, step): shapelen = jit.promote(len(self.res_shape)) offset = self.offset indices = [0] * shapelen for i in range(shapelen): indices[i] = self.indices[i] done = False for i in range(shapelen - 1, -1, -1): if indices[i] < self.res_shape[i] - step: indices[i] += step offset += self.strides[i] * step break else: remaining_step = (indices[i] + step) // self.res_shape[i] this_i_step = step - remaining_step * self.res_shape[i] offset += self.strides[i] * this_i_step indices[i] = indices[i] + this_i_step step = remaining_step else: done = True res = instantiate(ViewIterator) res.offset = offset res.indices = indices res.strides = self.strides res.backstrides = self.backstrides res.res_shape = self.res_shape res._done = done return res
def _dispatch_loop(self): code = self.code.co_code instr_index = 0 while True: jitdriver.jit_merge_point(code=code, instr_index=instr_index, frame=self) self.stack_depth = promote(self.stack_depth) op = ord(code[instr_index]) instr_index += 1 if op >= HAVE_ARGUMENT: low = ord(code[instr_index]) hi = ord(code[instr_index + 1]) oparg = (hi << 8) | low instr_index += 2 else: oparg = 0 if we_are_translated(): for opdesc in unrolling_opcode_descs: if op == opdesc.index: meth = getattr(self, opdesc.methodname) instr_index = meth(oparg, instr_index, code) break else: raise MissingOpcode(op) else: meth = getattr(self, opcode_method_names[op]) instr_index = meth(oparg, instr_index, code)
def _add_continuation_frame(self, func, nargs): if not isinstance(func, Builtins.Con_Func): self.raise_helper("Apply_Exception", [func]) func = jit.promote(func) # XXX this will promote lambdas, which will be inefficient pc = func.pc if isinstance(pc, BC_PC): bc_off = pc.off else: bc_off = -1 closure = Closure(func.container_closure, func.num_vars) if func.max_stack_size > nargs: max_stack_size = func.max_stack_size elif nargs == 0: # We make the stack size at least 1 so that RPython functions have room for 1 generator # frame. If they need more than that, they'll have to be clever. max_stack_size = 1 else: max_stack_size = nargs cf = Stack_Continuation_Frame(self.cur_cf, func, pc, max_stack_size, nargs, bc_off, closure) self.cur_cf = cf return cf
def op2(stack, func_int, func_str): # Operate on the top two stack items. The promotion hints force the # class of each arguments (IntBox or StrBox) to turn into a compile-time # constant if they weren't already. The effect we seek is to make the # calls to as_int() direct calls at compile-time, instead of indirect # ones. The JIT compiler cannot look into indirect calls, but it # can analyze and inline the code in directly-called functions. y = stack.pop() promote(y.__class__) x = stack.pop() promote(x.__class__) try: z = IntBox(func_int(x.as_int(), y.as_int())) except ValueError: z = StrBox(func_str(x.as_str(), y.as_str())) stack.append(z)
def interpret(bytecode, args): """The interpreter's entry point and portal function. """ loops = [] stack = empty_stack() pos = 0 while True: tinyjitdriver.jit_merge_point(args=args, loops=loops, stack=stack, bytecode=bytecode, pos=pos) bytecode = hint(bytecode, deepfreeze=True) if pos >= len(bytecode): break opcode = bytecode[pos] hint(opcode, concrete=True) # same as in tiny1.py pos += 1 if opcode == 'ADD': stack = op2(stack, func_add_int, func_add_str) elif opcode == 'SUB': stack = op2(stack, func_sub_int, func_sub_str) elif opcode == 'MUL': stack = op2(stack, func_mul_int, func_mul_str) elif opcode[0] == '#': n = myint(opcode, start=1) stack = Stack(args[n - 1], stack) elif opcode.startswith('->#'): n = myint(opcode, start=3) if n > len(args): raise IndexError stack, args[n - 1] = stack.pop() elif opcode == '{': loops.append(pos) elif opcode == '}': stack, flag = stack.pop() if flag.as_int() == 0: loops.pop() else: pos = loops[-1] # A common problem when interpreting loops or jumps: the 'pos' # above is read out of a list, so the hint-annotator thinks # it must be red (not a compile-time constant). But the # hint(opcode, concrete=True) in the next iteration of the # loop requires all variables the 'opcode' depends on to be # green, including this 'pos'. We promote 'pos' to a green # here, as early as possible. Note that in practice the 'pos' # read out of the 'loops' list will be a compile-time constant # because it was pushed as a compile-time constant by the '{' # case above into 'loops', which is a virtual list, so the # promotion below is just a way to make the colors match. pos = promote(pos) tinyjitdriver.can_enter_jit(args=args, loops=loops, stack=stack, bytecode=bytecode, pos=pos) else: stack = Stack(StrBox(opcode), stack) return stack
def next(self, shapelen): shapelen = jit.promote(len(self.res_shape)) offset = self.offset indices = [0] * shapelen for i in range(shapelen): indices[i] = self.indices[i] done = False for i in range(shapelen - 1, -1, -1): if indices[i] < self.res_shape[i] - 1: indices[i] += 1 offset += self.strides[i] break else: indices[i] = 0 offset -= self.backstrides[i] else: done = True res = instantiate(ViewIterator) res.offset = offset res.indices = indices res.strides = self.strides res.backstrides = self.backstrides res.res_shape = self.res_shape res._done = done return res
def getsingletonclass(self, space): w_cls = jit.promote(self.map).get_class() if w_cls.is_singleton: return w_cls w_cls = space.newclass(w_cls.name, w_cls, is_singleton=True) self.map = self.map.change_class(space, w_cls) return w_cls
def on_enter_jit(self, invariants, reds, bytecode, pos): # Now some strange code that makes a copy of the 'args' list in # a complicated way... this is a workaround forcing the whole 'args' # list to be virtual. It is a way to tell the JIT compiler that it # doesn't have to worry about the 'args' list being unpredictably # modified. oldloops = invariants oldargs = reds.args argcount = promote(len(oldargs)) args = [] n = 0 while n < argcount: hint(n, concrete=True) args.append(oldargs[n]) n += 1 reds.args = args # turn the green 'loops' from 'invariants' into a virtual list oldloops = hint(oldloops, deepfreeze=True) argcount = len(oldloops) loops = [] n = 0 while n < argcount: hint(n, concrete=True) loops.append(oldloops[n]) n += 1 reds.loops = loops
def f(n, a, i): frame = Frame(a, 0) frame.l[0] = a frame.l[1] = a + 1 frame.l[2] = a + 2 frame.l[3] = a + 3 if not i: return frame.l[0] + len(frame.l) x = 0 while n > 0: myjitdriver.can_enter_jit(frame=frame, n=n, x=x, i=i) myjitdriver.jit_merge_point(frame=frame, n=n, x=x, i=i) frame.s = jit.promote(frame.s) n -= 1 s = frame.s assert s >= 0 x += frame.l[s] frame.s += 1 s = frame.s assert s >= 0 x += frame.l[s] x += len(frame.l) x += f(n, n, 0) frame.s -= 1 return x
def op2(stack, func_int, func_float): # Operate on the top two stack items. The promotion hints force the # class of each arguments (IntBox or FloatBox) to turn into a compile-time # constant if they weren't already. The effect we seek is to make the # calls to as_int() direct calls at compile-time, instead of indirect # ones. The JIT compiler cannot look into indirect calls, but it # can analyze and inline the code in directly-called functions. stack, y = stack.pop() promote(y.__class__) stack, x = stack.pop() promote(x.__class__) if isinstance(x, IntBox) and isinstance(y, IntBox): z = IntBox(func_int(x.as_int(), y.as_int())) else: z = FloatBox(func_float(x.as_float(), y.as_float())) stack = Stack(z, stack) return stack
def create_frame(self, bc, w_self=None, w_scope=None, block=None, parent_interp=None): if w_self is None: w_self = self.w_top_self if w_scope is None: w_scope = self.w_object return Frame(jit.promote(bc), w_self, w_scope, block, parent_interp)
def maybe_handle_char_or_unichar_p(self, w_ffitype, w_obj): w_type = jit.promote(self.space.type(w_obj)) if w_ffitype.is_char_p() and w_type is self.space.w_str: strval = self.space.str_w(w_obj) self.handle_char_p(w_ffitype, w_obj, strval) return True elif w_ffitype.is_unichar_p() and (w_type is self.space.w_str or w_type is self.space.w_unicode): unicodeval = self.space.unicode_w(w_obj) self.handle_unichar_p(w_ffitype, w_obj, unicodeval) return True return False
def call__Type(space, w_type, __args__): promote(w_type) # invoke the __new__ of the type if not we_are_jitted(): # note that the annotator will figure out that w_type.w_bltin_new can # only be None if the newshortcut config option is not set w_bltin_new = w_type.w_bltin_new else: # for the JIT it is better to take the slow path because normal lookup # is nicely optimized, but the w_type.w_bltin_new attribute is not # known to the JIT w_bltin_new = None call_init = True if w_bltin_new is not None: w_newobject = space.call_obj_args(w_bltin_new, w_type, __args__) else: w_newtype, w_newdescr = w_type.lookup_where('__new__') w_newfunc = space.get(w_newdescr, w_type) if (space.config.objspace.std.newshortcut and not we_are_jitted() and isinstance(w_newtype, W_TypeObject) and not w_newtype.is_heaptype() and not space.is_w(w_newtype, space.w_type)): w_type.w_bltin_new = w_newfunc w_newobject = space.call_obj_args(w_newfunc, w_type, __args__) call_init = space.isinstance_w(w_newobject, w_type) # maybe invoke the __init__ of the type if (call_init and not (space.is_w(w_type, space.w_type) and not __args__.keywords and len(__args__.arguments_w) == 1)): w_descr = space.lookup(w_newobject, '__init__') w_result = space.get_and_call_args(w_descr, w_newobject, __args__) if not space.is_w(w_result, space.w_None): raise OperationError(space.w_TypeError, space.wrap("__init__() should return None")) return w_newobject
def call(self, argchain, RESULT, is_struct=False): # WARNING! This code is written carefully in a way that the JIT # optimizer will see a sequence of calls like the following: # # libffi_prepare_call # libffi_push_arg # libffi_push_arg # ... # libffi_call # # It is important that there is no other operation in the middle, else # the optimizer will fail to recognize the pattern and won't turn it # into a fast CALL. Note that "arg = arg.next" is optimized away, # assuming that argchain is completely virtual. self = jit.promote(self) if argchain.numargs != len(self.argtypes): raise TypeError, 'Wrong number of arguments: %d expected, got %d' %\ (len(self.argtypes), argchain.numargs) ll_args = self._prepare() i = 0 arg = argchain.first while arg: arg.push(self, ll_args, i) i += 1 arg = arg.next # if is_struct: assert types.is_struct(self.restype) res = self._do_call_raw(self.funcsym, ll_args) elif _fits_into_signed(RESULT): assert not types.is_struct(self.restype) res = self._do_call_int(self.funcsym, ll_args) elif RESULT is rffi.DOUBLE: return self._do_call_float(self.funcsym, ll_args) elif RESULT is rffi.FLOAT: return self._do_call_singlefloat(self.funcsym, ll_args) elif RESULT is rffi.LONGLONG or RESULT is rffi.ULONGLONG: assert IS_32_BIT res = self._do_call_longlong(self.funcsym, ll_args) elif RESULT is lltype.Void: return self._do_call_void(self.funcsym, ll_args) else: raise TypeError, 'Unsupported result type: %s' % RESULT # return rffi.cast(RESULT, res)
def f(n, a): frame = Frame([a,a+1,a+2,a+3], 0) x = 0 while n > 0: myjitdriver.can_enter_jit(frame=frame, n=n, x=x) myjitdriver.jit_merge_point(frame=frame, n=n, x=x) frame.s = promote(frame.s) n -= 1 s = frame.s assert s >= 0 x += frame.l[s] frame.s += 1 s = frame.s assert s >= 0 x += frame.l[s] x += len(frame.l) frame.s -= 1 return x
def f(codeno, n, a, frame): x = 0 while n > 0: myjitdriver.can_enter_jit(codeno=codeno, frame=frame, n=n, x=x) myjitdriver.jit_merge_point(codeno=codeno, frame=frame, n=n, x=x) frame.s = promote(frame.s) n -= 1 s = frame.s assert s >= 0 x += frame.l[s] frame.s += 1 if codeno == 0: subframe = Frame([n, n+1, n+2, n+3], 0) x += f(1, 10, 1, subframe) s = frame.s assert s >= 0 x += frame.l[s] x += len(frame.l) frame.s -= 1 return x
def send_ex(self, w_arg, operr=None): space = self.space if self.running: raise OperationError(space.w_ValueError, space.wrap('generator already executing')) frame = self.frame if frame is None: # xxx a bit ad-hoc, but we don't want to go inside # execute_frame() if the frame is actually finished if operr is None: operr = OperationError(space.w_StopIteration, space.w_None) raise operr # XXX it's not clear that last_instr should be promoted at all # but as long as it is necessary for call_assembler, let's do it early last_instr = jit.promote(frame.last_instr) if last_instr == -1: if w_arg and not space.is_w(w_arg, space.w_None): msg = "can't send non-None value to a just-started generator" raise OperationError(space.w_TypeError, space.wrap(msg)) else: if not w_arg: w_arg = space.w_None self.running = True try: try: w_result = frame.execute_frame(w_arg, operr) except OperationError: # errors finish a frame self.frame = None raise # if the frame is now marked as finished, it was RETURNed from if frame.frame_finished_execution: self.frame = None raise OperationError(space.w_StopIteration, space.w_None) else: return w_result # YIELDed finally: frame.f_backref = jit.vref_None self.running = False
def add_char_p_maybe(self, space, argchain, w_arg, w_argtype): """ Automatic conversion from string to char_p. The allocated buffer will be automatically freed after the call. """ w_type = jit.promote(space.type(w_arg)) if w_argtype.is_char_p() and w_type is space.w_str: strval = space.str_w(w_arg) buf = rffi.str2charp(strval) self.to_free.append(rffi.cast(rffi.VOIDP, buf)) addr = rffi.cast(rffi.ULONG, buf) argchain.arg(addr) return True elif w_argtype.is_unichar_p() and (w_type is space.w_str or w_type is space.w_unicode): unicodeval = space.unicode_w(w_arg) buf = rffi.unicode2wcharp(unicodeval) self.to_free.append(rffi.cast(rffi.VOIDP, buf)) addr = rffi.cast(rffi.ULONG, buf) argchain.arg(addr) return True return False
else: space.warn( "Parent module '%s' not found " "while handling absolute import" % ctxt_package, space.w_RuntimeWarning) rel_modulename = ctxt_package[:dot_position] rel_level = rel_modulename.count('.') + 1 if modulename: rel_modulename += '.' + modulename else: # __package__ not set, so figure it out and set it ctxt_w_name = space.finditem_str(w_globals, '__name__') ctxt_w_path = space.finditem_str(w_globals, '__path__') ctxt_w_name = jit.promote(ctxt_w_name) ctxt_name = None if ctxt_w_name is not None: try: ctxt_name = space.str_w(ctxt_w_name) except OperationError, e: if not e.match(space, space.w_TypeError): raise if not ctxt_name: return None, 0 m = max(level - 1, 0) if ctxt_w_path is None: # plain module m += 1 dot_position = _get_dot_position(ctxt_name, m)
def call(self, space, args_w): self = jit.promote(self) argchain = self.build_argchain(space, args_w) func_caller = CallFunctionConverter(space, self.func, argchain) return func_caller.do_and_wrap(self.w_restype)
def getclass(self, space): return promote(self.w__class__)
def done(self): final_iter = promote(self.final_iter) if final_iter < 0: assert False return self.iterators[final_iter].done()
def get_final_iter(self): final_iter = promote(self.final_iter) if final_iter < 0: assert False return self.iterators[final_iter]
def getdictvalue_no_unwrapping(self, w_dict, key): # NB: it's important to promote self here, so that self.version is a # no-op due to the quasi-immutable field self = jit.promote(self) return self._getdictvalue_no_unwrapping_pure(self.version, w_dict, key)
def type(self, w_obj): jit.promote(w_obj.__class__) return w_obj.getclass(self)