def __init__(self): self.regex_cache = RegexpCache(self) self.ec = ExecutionContext(self) self.bytecode_cache = BytecodeCache() self.setup_constants() self.setup_functions() self.setup_classes()
class ObjSpace(object): """ This implements all the operations on the object. Since this is prebuilt, it should not contain any state """ (tp_int, tp_float, tp_str, tp_array, tp_null, tp_bool, tp_object, tp_file_res, tp_dir_res, tp_stream_context, tp_mysql_link, tp_mysql_result, tp_constant, tp_delayed_class_const, tp_xmlparser_res) = range(15) # in the same order as the types above TYPENAMES = ["integer", "double", "string", "array", "NULL", "boolean", "object", "resource", "resource", "resource", "resource", "resource", "constant", "delayed constant", "resource"] w_True = w_True w_False = w_False w_Null = w_Null def __init__(self): self.regex_cache = RegexpCache(self) self.ec = ExecutionContext(self) self.bytecode_cache = BytecodeCache() self.setup_constants() self.setup_functions() self.setup_classes() def _setup_constant_any_case(self, name, w_value, outdict): cases = [c.lower() + c.upper() for c in name] for x in range(1 << len(name)): l = [cases[i][(x >> i) & 1] for i in range(len(name))] outdict[''.join(l)] = w_value def setup_constants(self): dct = OrderedDict() for modulename, lst in get_constants_by_module(self): for k, w_obj in lst: dct[k] = w_obj dct['PHP_INT_MAX'] = self.wrap(sys.maxint) dct['PHP_INT_SIZE'] = self.wrap(4 if sys.maxint < 2 ** 32 else 8) self._setup_constant_any_case('true', self.w_True, dct) self._setup_constant_any_case('false', self.w_False, dct) self._setup_constant_any_case('null', self.w_Null, dct) self.prebuilt_constants = dct.keys() self.global_constant_cache = GlobalImmutCache(self, dct, force_lowcase=False) def setup_functions(self): self.global_function_cache = GlobalImmutCache(self, BUILTIN_FUNCTIONS) def setup_classes(self): self.prebuilt_classes = all_builtin_classes.keys() self.global_class_cache = GlobalImmutCache(self, all_builtin_classes) def int_w(self, w_obj): return w_obj.deref().int_w(self) def float_w(self, w_obj): return w_obj.deref().float_w(self) def is_true(self, w_obj): return w_obj.deref().is_true(self) @signature(types.any(), types.int(), returns=types.any()) def newint(self, v): return W_IntObject(v) def newfloat(self, v): return W_FloatObject(v) def newbool(self, v): if v: return self.w_True return self.w_False @signature(types.any(), types.str(can_be_None=True), returns=types.any()) def newstr(self, v): return W_StringObject.newconststr(v) @signature(types.any(), types.bytearray(), returns=types.any()) def newmutablestr(self, v): return W_StringObject.newmutablestr(v) def get_new_res_id(self): self.ec.interpreter.last_resource_id += 1 return self.ec.interpreter.last_resource_id def str_w(self, w_v, quiet=False): res = w_v.deref().str(self, quiet=quiet) assert res is not None return res def str_w_quiet(self, w_v, quiet=True): return w_v.deref().str(self, quiet=quiet) def as_string(self, w_v, quiet=False): return w_v.deref().as_string(self, quiet=quiet) def as_object(self, interp, w_v): w_arg = w_v.deref() if w_arg.tp == self.tp_object: w_obj = w_arg else: w_obj = self.default_object(interp) w_obj.cast_object_from(self, w_arg) return w_obj def as_number(self, w_v): return w_v.deref().as_number(self) def uplus(self, w_v): return w_v.deref().uplus(self) def uminus(self, w_v): return w_v.deref().uminus(self) def uplusplus(self, w_v): return w_v.deref().uplusplus(self) def uminusminus(self, w_v): return w_v.deref().uminusminus(self) def getitem(self, w_obj, w_item, give_notice=False): return w_obj.deref_temp().getitem(self, w_item.deref(), give_notice=give_notice) def setitem(self, w_obj, w_item, w_newvalue): # Warning: this API always makes a copy of the string or array. # If you want to do several changes to a string or array, look # at the documentation in setitem_maybe_inplace(). w_arr = w_obj.deref().copy() w_arr, w_val2 = w_arr.setitem2_maybe_inplace(self, w_item, w_newvalue) # w_val2 is generally ignored; it is generally just w_newvalue return w_arr def setitem_maybe_inplace(self, w_obj, w_arg, w_value): """Supports efficiently setting items into strings or arrays. Warning! Use only on objects that are known to be unique, i.e. not shared. These objects are: * the result of an earlier call to space.new_array_from_xxx() or space.newmutablestr(), as long as it didn't escape yet * or, the result of w_obj.deref_unique(), which will make a copy in case of doubt and give you a unique object. This returns a resulting object, often the same one but not always, and that result is still unique, so you can chain operations like setitem_maybe_inplace() on it. Known-unique objects can be stored in W_Reference objects by using 'w_reference.store(w_unique_obj, unique=True)'. Then 'w_reference.deref_unique()' will not need to make a copy. """ w_modified, _ = w_obj.setitem2_maybe_inplace(self, w_arg, w_value) return w_modified def appenditem_maybe_inplace(self, w_obj, w_value): """Same as setitem_maybe_inplace(), but for appending instead""" w_obj.appenditem_inplace(self, w_value) return w_obj # always for now, but may change in the future def packitem_maybe_inplace(self, w_obj, w_arg, w_value): """Same as setitem_maybe_inplace(), but if w_arg turns out to be a valid integer, ignore it and do a regular append instead.""" w_modified = w_obj.packitem_maybe_inplace(self, w_arg, w_value) return w_modified def concat(self, w_left, w_right): return self.as_string(w_left).strconcat(self, self.as_string(w_right)) def strlen(self, w_obj): return w_obj.deref_temp().strlen() def arraylen(self, w_obj): return w_obj.deref_temp().arraylen() def slice(self, w_arr, start, shift, keep_keys, keep_str_keys=False): if shift == 0: return self.new_array_from_list([]) if self.arraylen(w_arr) == 0: return self.new_array_from_list([]) if start > self.arraylen(w_arr): return self.new_array_from_list([]) if start < 0: start = self.arraylen(w_arr) + start if shift < 0: shift = self.arraylen(w_arr) + shift - start next_idx = 0 res_arr = [] idx = 0 with self.iter(w_arr) as itr: while not itr.done(): w_key, w_value = itr.next_item(self) if start <= idx < start + shift: if keep_keys: res_arr.append((w_key, w_value)) else: if keep_str_keys: if w_key.tp == self.tp_str: res_arr.append((w_key, w_value)) else: res_arr.append((self.newint(next_idx), w_value)) next_idx += 1 else: res_arr.append((self.newint(next_idx), w_value)) next_idx += 1 idx += 1 return self.new_array_from_pairs(res_arr) def getchar(self, w_obj): # get first character return w_obj.deref().as_string(self).getchar(self) @specialize.argtype(1) def wrap(self, v): if v is None: return self.w_Null if isinstance(v, bool): return self.newbool(v) elif isinstance(v, int): return self.newint(v) elif isinstance(v, str): return self.newstr(v) elif isinstance(v, float): return self.newfloat(v) elif not we_are_translated() and isinstance(v, long): raise TypeError("longs are not RPython!") elif isinstance(v, str): return self.newstr(v) else: raise NotImplementedError(v) def _freeze_(self): return True def call_args(self, w_callable, args_w): return w_callable.call_args(self.ec.interpreter, args_w) def new_array_from_list(self, lst_w): return W_ArrayObject.new_array_from_list(self, lst_w) def new_array_from_rdict(self, rdict_w): return W_ArrayObject.new_array_from_rdict(self, rdict_w) def get_rdict_from_array(self, w_arr): return w_arr.deref_temp().get_rdict_from_array() def rdict_remove(self, rdict, w_obj): rstr = w_obj.deref().str(self) try: del rdict[rstr] except KeyError: pass def new_array_from_dict(self, dict_w): "NOT_RPYTHON: for tests only (gets a random ordering)" rdict_w = OrderedDict() for key, w_value in dict_w.items(): rdict_w[key] = w_value return W_ArrayObject.new_array_from_rdict(self, rdict_w) def new_array_from_pairs(self, pairs_ww, allow_bogus=False): return W_ArrayObject.new_array_from_pairs(self, pairs_ww, allow_bogus=allow_bogus) new_map_from_pairs = new_array_from_pairs # for now def iter(self, w_arr): return ObjSpaceWithIter(self, w_arr) def create_iter(self, w_arr, contextclass=None): w_arr = w_arr.deref() return w_arr.create_iter(self, contextclass) def create_iter_ref(self, r_array, contextclass=None): if not isinstance(r_array, W_Reference): raise self.ec.fatal("foreach(1 as &2): argument 1 must be a " "variable") w_arr = r_array.deref_temp() return w_arr.create_iter_ref(self, r_array, contextclass) def str_eq(self, w_one, w_two, quiet=False): w_one = w_one.deref() w_two = w_two.deref() if w_one.tp != w_two.tp: w_one = self.as_string(w_one, quiet=quiet) w_two = self.as_string(w_two, quiet=quiet) return self._compare(w_one, w_two, ignore_order=True) == 0 def get_globals_wrapper(self): return self.ec.interpreter.globals def lookup_local_vars(self, name): frame = self.ec.interpreter.topframeref() try: return frame.get_ref_by_name(name, create_new=False) except KeyError: return None def as_array(self, w_obj): w_obj = w_obj.deref() if w_obj.tp == self.tp_object: return w_obj.get_rdict_array(self) if w_obj.tp != self.tp_array: if w_obj is self.w_Null: return self.new_array_from_list([]) w_obj = self.new_array_from_list([w_obj]) assert isinstance(w_obj, W_ArrayObject) return w_obj def is_array(self, w_obj): return w_obj.deref().tp == self.tp_array def is_str(self, w_obj): return w_obj.deref().tp == self.tp_str def is_null(self, w_obj): return w_obj.deref().tp == self.tp_null def is_object(self, w_obj): return w_obj.deref().tp == self.tp_object def is_resource(self, w_obj): return isinstance(w_obj, W_Resource) def gettypename(self, w_obj): w_obj = w_obj.deref() if isinstance(w_obj, W_InstanceObject): return 'instance of ' + w_obj.klass.name else: return self.TYPENAMES[w_obj.tp].lower() def eq(self, w_left, w_right): res = self._compare(w_left, w_right, ignore_order=True) return self.newbool(res == 0) def eq_w(self, w_left, w_right): res = self._compare(w_left, w_right, ignore_order=True) return res == 0 def ne(self, w_left, w_right): res = self._compare(w_left, w_right, ignore_order=True) return self.newbool(res != 0) def lt(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res < 0) def gt(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res > 0) def le(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res <= 0) def ge(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res >= 0) def mod(self, w_left, w_right): left = self.force_int(w_left) right = self.force_int(w_right) return self._mod(left, right) def _mod(self, left, right): if right == 0: self.ec.warn("Division by zero") return self.w_False elif right == -1: return self.newint(0) z = left % right if z != 0 and ((left < 0 and right > 0) or (left > 0 and right < 0)): z -= right return self.newint(z) def or_string(self, w_left, w_right): left = w_left.unwrap() right = w_right.unwrap() if len(left) < len(right): left, right = right, left s = StringBuilder(len(left)) for i in range(len(right)): char = chr(ord(left[i]) | ord(right[i])) s.append(char) for i in range(len(right), len(left)): s.append(left[i]) return self.newstr(s.build()) def or_(self, w_left, w_right): if (isinstance(w_left, W_StringObject) and isinstance(w_right, W_StringObject)): return self.or_string(w_left, w_right) else: left = w_left.int_w(self) right = w_right.int_w(self) return self.newint(left | right) def lshift(self, w_left, w_right): left = self.force_int(w_left) right = self.force_int(w_right) z = intmask(left << (right & MASK_31_63)) return W_IntObject(z) def rshift(self, w_left, w_right): left = self.force_int(w_left) right = self.force_int(w_right) z = intmask(left >> (right & MASK_31_63)) return W_IntObject(z) def is_w(self, w_a, w_b): return self._compare(w_a, w_b, strict=True, ignore_order=True) == 0 def _compare(self, w_left, w_right, strict=False, ignore_order=False): w_left = w_left.deref() w_right = w_right.deref() left_tp = w_left.tp right_tp = w_right.tp if strict: if left_tp != right_tp: return 1 if(left_tp == self.tp_float and right_tp == self.tp_float): return my_cmp(self.float_w(w_left), self.float_w(w_right), ignore_order) if(left_tp == self.tp_int and right_tp == self.tp_float): return my_cmp(self.float_w(w_left), self.float_w(w_right), ignore_order) if(left_tp == self.tp_float and right_tp == self.tp_int): return my_cmp(self.float_w(w_left), self.float_w(w_right), ignore_order) elif(left_tp == self.tp_int and right_tp == self.tp_int): return my_cmp(self.int_w(w_left), self.int_w(w_right), ignore_order) elif(left_tp == self.tp_array and right_tp == self.tp_array): return self._compare_array(w_left, w_right, strict) elif(left_tp == self.tp_null and right_tp == self.tp_null): return 0 elif(left_tp == self.tp_null and right_tp == self.tp_bool): if self.is_true(w_right): return -1 return 0 elif(left_tp == self.tp_bool and right_tp == self.tp_null): if self.is_true(w_left): return 1 return 0 elif(left_tp == self.tp_bool and right_tp == self.tp_bool): return my_cmp(self.is_true(w_left), self.is_true(w_right), ignore_order) elif(left_tp == self.tp_str and right_tp == self.tp_str): left = self.str_w(w_left) right = self.str_w(w_right) if not strict: # a small optimimization first, if both are single-char left_length = len(left) right_length = len(right) if (jit.isconstant(left_length) and left_length == 1 and jit.isconstant(right_length) and right_length == 1): return my_cmp(ord(left[0]), ord(right[0]), ignore_order) # w_right_num, right_valid = convert_string_to_number(right) if right_valid: w_left_num, left_valid = convert_string_to_number(left) if left_valid: return self._compare(w_left_num, w_right_num, ignore_order=ignore_order) return my_cmp(left, right, ignore_order) elif(left_tp == self.tp_null and right_tp == self.tp_str): return my_cmp("", self.str_w(w_right), ignore_order) elif(left_tp == self.tp_str and right_tp == self.tp_null): return my_cmp(self.str_w(w_left), "", ignore_order) elif(left_tp == self.tp_object and right_tp == self.tp_null): return 1 elif(left_tp == self.tp_null and right_tp == self.tp_object): return -1 elif(left_tp == self.tp_object and right_tp == self.tp_object): return w_left.compare(w_right, self, strict) else: if(left_tp == self.tp_null): if self.is_true(w_right): return -1 return 0 elif(right_tp == self.tp_null): if self.is_true(w_left): return 1 return 0 elif(left_tp == self.tp_bool or right_tp == self.tp_bool): return my_cmp(self.is_true(w_left), self.is_true(w_right), ignore_order) elif(left_tp == self.tp_array): return 1 elif(right_tp == self.tp_array): return -1 elif(left_tp == self.tp_object): return 1 elif(right_tp == self.tp_object): return -1 else: return self._compare(self.as_number(w_left), self.as_number(w_right), ignore_order=ignore_order) raise NotImplementedError() def _compare_object(self, w_left, w_right, strict): if w_left is w_right: return 0 elif strict or w_left.getclass() is not w_right.getclass(): return 1 left = w_left.get_instance_attrs() right = w_right.get_instance_attrs() if len(left) - len(right) < 0: return -1 if len(left) - len(right) > 0: return 1 for key, w_value in left.iteritems(): try: w_right_value = right[key] except KeyError: return 1 cmp_res = self._compare(w_value, w_right_value) if cmp_res == 0: continue else: return cmp_res return 0 def _compare_array(self, w_left, w_right, strict): if w_left.arraylen() - w_right.arraylen() < 0: return -1 if w_left.arraylen() - w_right.arraylen() > 0: return 1 with self.iter(w_left) as itr: while not itr.done(): w_key, w_value = itr.next_item(self) if w_right.isset_index(self, w_key): w_right_value = self.getitem(w_right, w_key) if strict: cmp_res = self._compare(w_value, w_right_value, strict=True) else: cmp_res = self._compare(w_value, w_right_value) if cmp_res == 0: continue else: return cmp_res else: return 1 return 0 def getclass(self, w_obj): return w_obj.deref().getclass() def get_type_name(self, tp): return self.TYPENAMES[tp].lower() def is_really_int(self, w_obj): if w_obj.tp == self.tp_str: w_obj, fully_processed = convert_string_to_number( self.str_w(w_obj)) if not fully_processed: return None if w_obj.tp == self.tp_int: return w_obj def overflow_convert(self, w_obj): return w_obj.overflow_convert(self) def _force_int_from_str(self, w_obj): s = self.str_w(w_obj) decstr = "" i = 0 while i < len(s) and s[i] in PHP_WHITESPACE: i += 1 for c in s[i:]: if ('0' <= c <= '9'): decstr += c elif c == '-': decstr += c elif c == '+': decstr += c else: break if decstr == '': return 0 return int(decstr) def force_int(self, w_obj): if w_obj.tp == self.tp_str: return self._force_int_from_str(w_obj) elif w_obj.tp == self.tp_int: return self.int_w(w_obj) return w_obj.as_number(self).int_w(self) @jit.elidable def is_valid_varname(self, name): if len(name) == 0: return False c = name[0] if not('a' <= c <= 'z' or 'A' <= c <= 'Z' or c == '_'): return False for i in range(1, len(name)): c = name[i] if not('a' <= c <= 'z' or 'A' <= c <= 'Z' or c == '_' or '0' <= c <= '9'): return False return True def is_integer(self, w_obj): if w_obj.tp == self.tp_int: return True if isinstance(w_obj, W_StringObject): return w_obj.is_really_valid_number() return False def getclassintfname(self, w_obj): w_obj = w_obj.deref() if isinstance(w_obj, W_InstanceObject): classname = w_obj.klass.get_identifier() return classname else: class_or_interface_name = self.str_w(w_obj) return class_or_interface_name def instanceof_w(self, w_left, w_right): from hippy.klass import ClassBase if isinstance(w_right, ClassBase): classintfname = w_right.name elif self.is_null(w_right): return False else: classintfname = self.getclassintfname(w_right) klass = self.getclass(w_left) if klass is None: return False return klass.is_subclass_of_class_or_intf_name(classintfname) def is_string(self, w_obj): s = self.str_w(w_obj) if not s: return True if s[0] == "0" and len(s) != 1: return True if s[0] < '0' or s[0] > '9': return True return False def instanceof(self, w_left, w_right): return self.newbool(self.instanceof_w(w_left, w_right)) def empty_ref(self): return W_Reference(self.w_Null) def default_object(self, interp): """Create a default object instance""" return k_stdClass.call_args(interp, []) def array_to_string_conversion(self, w_arr): out = "" with self.iter(w_arr) as itr: while not itr.done(): w_key, w_value = itr.next_item(self) if w_value.tp == self.tp_array: self.ec.notice("Array to string conversion") out += self.str_w(w_value) return self.newstr(out) def _get_callback_from_string(self, name): pos = name.find('::') if pos >= 0: clsname = name[:pos] methname = name[pos + 2:] return self._get_callback_from_class(clsname, methname) try: return self.ec.interpreter.lookup_function(name) except KeyError: raise InvalidCallback("function '%s' not found or invalid " "function name" % (name)) def _get_callback_from_class(self, clsname, methname): interp = self.ec.interpreter klass = interp.lookup_class_or_intf(clsname) if klass is None: raise InvalidCallback("class '%s' not found" % (clsname)) contextclass = interp.get_contextclass() w_this = interp.get_frame().w_this try: meth = klass.getstaticmeth(methname, contextclass, w_this, interp) except VisibilityError as e: raise InvalidCallback(e.msg_callback(static=True)) return meth.bind(w_this, klass) def _get_callback_from_instance(self, w_instance, methname): contextclass = self.ec.interpreter.get_contextclass() try: meth = w_instance.getmeth(self, methname, contextclass) except VisibilityError as e: raise InvalidCallback(e.msg_callback(static=False)) return meth def get_callback(self, fname, arg_no, w_obj, give_warning=True): try: return self._get_callback(w_obj) except InvalidCallback as e: if give_warning: err_msg = ("%s() expects parameter %d to be a valid callback, %s" % (fname, arg_no, e.msg)) self.ec.warn(err_msg) return None def _get_callback(self, w_obj): from hippy.objects.closureobject import W_ClosureObject if w_obj.tp == self.tp_str: name = self.str_w(w_obj) return self._get_callback_from_string(name) elif w_obj.tp == self.tp_array: if w_obj.arraylen() != 2: raise InvalidCallback("array must have exactly two members") w_instance = self.getitem(w_obj, self.wrap(0)).deref() if isinstance(w_instance, W_InstanceObject): methname = self.str_w(self.getitem(w_obj, self.wrap(1))) return self._get_callback_from_instance(w_instance, methname) clsname = self.str_w(w_instance) if not self.is_valid_varname(clsname): raise InvalidCallback("first array member is not a valid " "class name or object") methname = self.str_w(self.getitem(w_obj, self.wrap(1))) return self._get_callback_from_class(clsname, methname) elif isinstance(w_obj, W_InstanceObject): callable = w_obj.get_callable() if callable is not None: return callable raise InvalidCallback("no array or string given") def serialize(self, w_obj): from hippy.module.serialize import SerializerMemo assert not isinstance(w_obj, W_Reference) builder = StringBuilder() w_obj.serialize(self, builder, SerializerMemo()) return builder.build() def set_errno(self, errno): self.ec.interpreter.last_posix_errno = errno def get_errno(self): return self.ec.interpreter.last_posix_errno def compile_file(self, filename): return self.bytecode_cache.compile_file(filename, self)
class ObjSpace(object): """ This implements all the operations on the object. Since this is prebuilt, it should not contain any state """ (tp_int, tp_float, tp_str, tp_array, tp_null, tp_bool, tp_object, tp_file_res, tp_dir_res, tp_stream_context, tp_mysql_link, tp_mysql_result, tp_constant, tp_delayed_class_const, tp_xmlparser_res, tp_mcrypt_res) = range(16) # in the same order as the types above TYPENAMES = ["integer", "double", "string", "array", "NULL", "boolean", "object", "resource", "resource", "resource", "resource", "resource", "constant", "delayed constant", "resource", "resource"] w_True = w_True w_False = w_False w_Null = w_Null def __init__(self): self.regex_cache = RegexpCache(self) self.ec = ExecutionContext(self) self.bytecode_cache = BytecodeCache() self.setup_constants() self.setup_functions() self.setup_classes() def _setup_constant_any_case(self, name, w_value, outdict): cases = [c.lower() + c.upper() for c in name] for x in range(1 << len(name)): l = [cases[i][(x >> i) & 1] for i in range(len(name))] outdict[''.join(l)] = w_value def setup_constants(self): dct = OrderedDict() for modulename, lst in get_constants_by_module(self): for k, w_obj in lst: dct[k] = w_obj dct['PHP_INT_MAX'] = self.wrap(sys.maxint) dct['PHP_INT_SIZE'] = self.wrap(4 if sys.maxint < 2 ** 32 else 8) self._setup_constant_any_case('true', self.w_True, dct) self._setup_constant_any_case('false', self.w_False, dct) self._setup_constant_any_case('null', self.w_Null, dct) self.prebuilt_constants = dct.keys() self.global_constant_cache = GlobalImmutCache(self, dct, force_lowcase=False) def setup_functions(self): self.global_function_cache = GlobalImmutCache(self, BUILTIN_FUNCTIONS) def setup_classes(self): self.prebuilt_classes = all_builtin_classes.keys() self.global_class_cache = GlobalImmutCache(self, all_builtin_classes) def int_w(self, w_obj): return w_obj.deref().int_w(self) def float_w(self, w_obj): return w_obj.deref().float_w(self) def is_true(self, w_obj): return w_obj.deref().is_true(self) @signature(types.any(), types.int(), returns=types.any()) def newint(self, v): return W_IntObject(v) def newfloat(self, v): return W_FloatObject(v) def newbool(self, v): if v: return self.w_True return self.w_False @signature(types.any(), types.str(can_be_None=True), returns=types.any()) def newstr(self, v): return W_StringObject.newconststr(v) @signature(types.any(), types.bytearray(), returns=types.any()) def newmutablestr(self, v): return W_StringObject.newmutablestr(v) def get_new_res_id(self): self.ec.interpreter.last_resource_id += 1 return self.ec.interpreter.last_resource_id def str_w(self, w_v, quiet=False): res = w_v.deref().str(self, quiet=quiet) assert res is not None return res def str_w_quiet(self, w_v, quiet=True): return w_v.deref().str(self, quiet=quiet) def as_string(self, w_v, quiet=False): return w_v.deref().as_string(self, quiet=quiet) def as_object(self, interp, w_v): w_arg = w_v.deref() if w_arg.tp == self.tp_object: w_obj = w_arg else: w_obj = self.default_object(interp) w_obj.cast_object_from(self, w_arg) return w_obj def as_number(self, w_v): return w_v.deref().as_number(self) def uplus(self, w_v): return w_v.deref().uplus(self) def uminus(self, w_v): return w_v.deref().uminus(self) def uplusplus(self, w_v): return w_v.deref().uplusplus(self) def uminusminus(self, w_v): return w_v.deref().uminusminus(self) def getitem(self, w_obj, w_item, give_notice=False): return w_obj.deref_temp().getitem(self, w_item.deref(), give_notice=give_notice) def setitem(self, w_obj, w_item, w_newvalue): # Warning: this API always makes a copy of the string or array. # If you want to do several changes to a string or array, look # at the documentation in setitem_maybe_inplace(). w_arr = w_obj.deref().copy() w_arr, w_val2 = w_arr.setitem2_maybe_inplace(self, w_item, w_newvalue) # w_val2 is generally ignored; it is generally just w_newvalue return w_arr def setitem_maybe_inplace(self, w_obj, w_arg, w_value): """Supports efficiently setting items into strings or arrays. Warning! Use only on objects that are known to be unique, i.e. not shared. These objects are: * the result of an earlier call to space.new_array_from_xxx() or space.newmutablestr(), as long as it didn't escape yet * or, the result of w_obj.deref_unique(), which will make a copy in case of doubt and give you a unique object. This returns a resulting object, often the same one but not always, and that result is still unique, so you can chain operations like setitem_maybe_inplace() on it. Known-unique objects can be stored in W_Reference objects by using 'w_reference.store(w_unique_obj, unique=True)'. Then 'w_reference.deref_unique()' will not need to make a copy. """ w_modified, _ = w_obj.setitem2_maybe_inplace(self, w_arg, w_value) return w_modified def appenditem_maybe_inplace(self, w_obj, w_value): """Same as setitem_maybe_inplace(), but for appending instead""" w_obj.appenditem_inplace(self, w_value) return w_obj # always for now, but may change in the future def packitem_maybe_inplace(self, w_obj, w_arg, w_value): """Same as setitem_maybe_inplace(), but if w_arg turns out to be a valid integer, ignore it and do a regular append instead.""" w_modified = w_obj.packitem_maybe_inplace(self, w_arg, w_value) return w_modified def concat(self, w_left, w_right): return self.as_string(w_left).strconcat(self, self.as_string(w_right)) def strlen(self, w_obj): return w_obj.deref_temp().strlen() def arraylen(self, w_obj): return w_obj.deref_temp().arraylen() def slice(self, w_arr, start, shift, keep_keys, keep_str_keys=False): if shift == 0: return self.new_array_from_list([]) if self.arraylen(w_arr) == 0: return self.new_array_from_list([]) if start > self.arraylen(w_arr): return self.new_array_from_list([]) if start < 0: start = self.arraylen(w_arr) + start if shift < 0: shift = self.arraylen(w_arr) + shift - start next_idx = 0 res_arr = [] idx = 0 with self.iter(w_arr) as itr: while not itr.done(): w_key, w_value = itr.next_item(self) if start <= idx < start + shift: if keep_keys: res_arr.append((w_key, w_value)) else: if keep_str_keys: if w_key.tp == self.tp_str: res_arr.append((w_key, w_value)) else: res_arr.append((self.newint(next_idx), w_value)) next_idx += 1 else: res_arr.append((self.newint(next_idx), w_value)) next_idx += 1 idx += 1 return self.new_array_from_pairs(res_arr) def getchar(self, w_obj): # get first character return w_obj.deref().as_string(self).getchar(self) @specialize.argtype(1) def wrap(self, v): if v is None: return self.w_Null if isinstance(v, bool): return self.newbool(v) elif isinstance(v, int): return self.newint(v) elif isinstance(v, str): return self.newstr(v) elif isinstance(v, float): return self.newfloat(v) elif not we_are_translated() and isinstance(v, long): raise TypeError("longs are not RPython!") elif isinstance(v, str): return self.newstr(v) else: raise NotImplementedError(v) def _freeze_(self): return True def call_args(self, w_callable, args_w): return w_callable.call_args(self.ec.interpreter, args_w) def new_array_from_list(self, lst_w): return W_ArrayObject.new_array_from_list(self, lst_w) def new_array_from_rdict(self, rdict_w): return W_ArrayObject.new_array_from_rdict(self, rdict_w) def get_rdict_from_array(self, w_arr): return w_arr.deref_temp().get_rdict_from_array() def rdict_remove(self, rdict, w_obj): rstr = w_obj.deref().str(self) try: del rdict[rstr] except KeyError: pass def new_array_from_dict(self, dict_w): "NOT_RPYTHON: for tests only (gets a random ordering)" rdict_w = OrderedDict() for key, w_value in dict_w.items(): rdict_w[key] = w_value return W_ArrayObject.new_array_from_rdict(self, rdict_w) def new_array_from_pairs(self, pairs_ww, allow_bogus=False): return W_ArrayObject.new_array_from_pairs(self, pairs_ww, allow_bogus=allow_bogus) new_map_from_pairs = new_array_from_pairs # for now def iter(self, w_arr): return ObjSpaceWithIter(self, w_arr) def create_iter(self, w_arr, contextclass=None): w_arr = w_arr.deref() return w_arr.create_iter(self, contextclass) def create_iter_ref(self, r_array, contextclass=None): if not isinstance(r_array, W_Reference): raise self.ec.fatal("foreach(1 as &2): argument 1 must be a " "variable") w_arr = r_array.deref_temp() return w_arr.create_iter_ref(self, r_array, contextclass) def str_eq(self, w_one, w_two, quiet=False): w_one = w_one.deref() w_two = w_two.deref() if w_one.tp != w_two.tp: w_one = self.as_string(w_one, quiet=quiet) w_two = self.as_string(w_two, quiet=quiet) return self._compare(w_one, w_two, ignore_order=True) == 0 def get_globals_wrapper(self): return self.ec.interpreter.globals def lookup_local_vars(self, name): frame = self.ec.interpreter.topframeref() try: return frame.get_ref_by_name(name, create_new=False) except KeyError: return None def as_array(self, w_obj): w_obj = w_obj.deref() if w_obj.tp == self.tp_object: return w_obj.get_rdict_array(self) if w_obj.tp != self.tp_array: if w_obj is self.w_Null: return self.new_array_from_list([]) w_obj = self.new_array_from_list([w_obj]) assert isinstance(w_obj, W_ArrayObject) return w_obj def is_array(self, w_obj): return w_obj.deref().tp == self.tp_array def is_str(self, w_obj): return w_obj.deref().tp == self.tp_str def is_null(self, w_obj): return w_obj.deref().tp == self.tp_null def is_object(self, w_obj): return w_obj.deref().tp == self.tp_object def is_resource(self, w_obj): return isinstance(w_obj, W_Resource) def gettypename(self, w_obj): w_obj = w_obj.deref() if isinstance(w_obj, W_InstanceObject): return 'instance of ' + w_obj.getclass().name else: return self.TYPENAMES[w_obj.tp].lower() def eq(self, w_left, w_right): res = self._compare(w_left, w_right, ignore_order=True) return self.newbool(res == 0) def eq_w(self, w_left, w_right): res = self._compare(w_left, w_right, ignore_order=True) return res == 0 def ne(self, w_left, w_right): res = self._compare(w_left, w_right, ignore_order=True) return self.newbool(res != 0) def lt(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res < 0) def gt(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res > 0) def le(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res <= 0) def ge(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res >= 0) def mod(self, w_left, w_right): left = self.force_int(w_left) right = self.force_int(w_right) return self._mod(left, right) def _mod(self, left, right): if right == 0: self.ec.warn("Division by zero") return self.w_False elif right == -1: return self.newint(0) z = left % right if z != 0 and ((left < 0 and right > 0) or (left > 0 and right < 0)): z -= right return self.newint(z) def or_string(self, w_left, w_right): left = w_left.unwrap() right = w_right.unwrap() if len(left) < len(right): left, right = right, left s = StringBuilder(len(left)) for i in range(len(right)): char = chr(ord(left[i]) | ord(right[i])) s.append(char) for i in range(len(right), len(left)): s.append(left[i]) return self.newstr(s.build()) def or_(self, w_left, w_right): if (isinstance(w_left, W_StringObject) and isinstance(w_right, W_StringObject)): return self.or_string(w_left, w_right) else: left = w_left.int_w(self) right = w_right.int_w(self) return self.newint(left | right) def lshift(self, w_left, w_right): left = self.force_int(w_left) right = self.force_int(w_right) z = intmask(left << (right & MASK_31_63)) return W_IntObject(z) def rshift(self, w_left, w_right): left = self.force_int(w_left) right = self.force_int(w_right) z = intmask(left >> (right & MASK_31_63)) return W_IntObject(z) def is_w(self, w_a, w_b): return self._compare(w_a, w_b, strict=True, ignore_order=True) == 0 def _compare(self, w_left, w_right, strict=False, ignore_order=False): w_left = w_left.deref() w_right = w_right.deref() left_tp = w_left.tp right_tp = w_right.tp if strict: if left_tp != right_tp: return 1 if(left_tp == self.tp_float and right_tp == self.tp_float): return my_cmp(self.float_w(w_left), self.float_w(w_right), ignore_order) if(left_tp == self.tp_int and right_tp == self.tp_float): return my_cmp(self.float_w(w_left), self.float_w(w_right), ignore_order) if(left_tp == self.tp_float and right_tp == self.tp_int): return my_cmp(self.float_w(w_left), self.float_w(w_right), ignore_order) elif(left_tp == self.tp_int and right_tp == self.tp_int): return my_cmp(self.int_w(w_left), self.int_w(w_right), ignore_order) elif(left_tp == self.tp_array and right_tp == self.tp_array): if w_left is w_right: return 0 w_left_len = w_left.arraylen() w_right_len = w_right.arraylen() if w_left_len < w_right_len: return -1 elif w_left_len > w_right_len: return 1 return self._compare_aggregates(w_left, w_right, strict, ignore_order) elif(left_tp == self.tp_null and right_tp == self.tp_null): return 0 elif(left_tp == self.tp_null and right_tp == self.tp_bool): if self.is_true(w_right): return -1 return 0 elif(left_tp == self.tp_bool and right_tp == self.tp_null): if self.is_true(w_left): return 1 return 0 elif(left_tp == self.tp_bool and right_tp == self.tp_bool): return my_cmp(self.is_true(w_left), self.is_true(w_right), ignore_order) elif(left_tp == self.tp_str and right_tp == self.tp_str): left = self.str_w(w_left) right = self.str_w(w_right) if not strict: # a small optimimization first, if both are single-char left_length = len(left) right_length = len(right) if (jit.isconstant(left_length) and left_length == 1 and jit.isconstant(right_length) and right_length == 1): return my_cmp(ord(left[0]), ord(right[0]), ignore_order) # w_right_num, right_valid = convert_string_to_number(right) if right_valid: w_left_num, left_valid = convert_string_to_number(left) if left_valid: return self._compare(w_left_num, w_right_num, ignore_order=ignore_order) return my_cmp(left, right, ignore_order) elif(left_tp == self.tp_null and right_tp == self.tp_str): return my_cmp("", self.str_w(w_right), ignore_order) elif(left_tp == self.tp_str and right_tp == self.tp_null): return my_cmp(self.str_w(w_left), "", ignore_order) elif(left_tp == self.tp_object and right_tp == self.tp_null): return 1 elif(left_tp == self.tp_null and right_tp == self.tp_object): return -1 elif(left_tp == self.tp_object and right_tp == self.tp_object): if w_left is w_right: return 0 return self._compare_aggregates(w_left, w_right, strict, ignore_order) else: if(left_tp == self.tp_null): if self.is_true(w_right): return -1 return 0 elif(right_tp == self.tp_null): if self.is_true(w_left): return 1 return 0 elif(left_tp == self.tp_bool or right_tp == self.tp_bool): return my_cmp(self.is_true(w_left), self.is_true(w_right), ignore_order) elif(left_tp == self.tp_array): return 1 elif(right_tp == self.tp_array): return -1 elif(left_tp == self.tp_object): return 1 elif(right_tp == self.tp_object): return -1 else: return self._compare(self.as_number(w_left), self.as_number(w_right), ignore_order=ignore_order) raise NotImplementedError() def _compare_aggregates(self, w_left, w_right, strict, ignore_order): # Aggregate things (user objects, arrays) are most naturally compared # recursively. However that is slow and tends to blow up the stack. This # function iteratively compares such things. It tries very hard not to # allocate more lists than it has to, as this is a performance criticial # piece of code. We do that by continually pushing things we come across # onto a stack (obj_st and its mirror strict_st). Because this function # not only says "is/isn't" equal but also "greater than/less than", we # have to march over these things in their natural order which sometimes # means creating temporary intermediate lists. # # There is also one common idiom in the below: we know that calling # _compare can only become recursive if both left and right hand side # are aggregate types. If one side is not an aggregate, either _compares # type checks will fail or it will convert both sides into numbers. # Either way we know recursion won't happen. # The object stack comes in pairs (w_left, w_right). Everything pushed # on here must already have been deref'd. obj_st = [w_left.deref(), w_right.deref()] strict_st = [strict] # strict stack while len(obj_st) > 0: w_right = obj_st.pop() w_left = obj_st.pop() strict = strict_st.pop() if w_left is None: assert w_right is None return 1 # deferred inequality detected left_tp = w_left.tp right_tp = w_right.tp if left_tp == self.tp_array and right_tp == self.tp_array: if w_left is w_right: continue w_left_len = w_left.arraylen() w_right_len = w_right.arraylen() if w_left_len < w_right_len: return -1 elif w_left_len > w_right_len: return 1 with self.iter(w_left) as left_itr, self.iter(w_right) as right_itr: # We iterate over the array and deal with all simple # datatypes immediately. If we find two that are obviously # not equal, we can stop the search at that point. Complex # datatypes, however, must be pushed on the stack and dealt # with in order later. # If allocated, new_st is a list mirroring obj_st *but* # notice it stores in order w_right, w_left new_st = None while not left_itr.done(): # Especially if the two arrays in question are lists, # their keys are likely to be a) of primitive type b) in # identical order. We therefore iterate over the left # and right arrays at the same time hoping that we'll # often see the same keys at the same points and avoid # doing expensive lookups. w_key, w_left_val = left_itr.next_item(self) w_rkey, w_right_val = right_itr.next_item(self) if isinstance(w_key, W_IntObject) \ and isinstance(w_rkey, W_IntObject) \ and w_key.intval == w_rkey.intval: pass elif isinstance(w_key, W_ConstStringObject) \ and isinstance(w_rkey, W_ConstStringObject) \ and w_key._strval == w_rkey._strval: pass else: if not w_right.isset_index(self, w_key): if ignore_order: return -1 if new_st is None: new_st = [None, None] else: new_st.append(None) new_st.append(None) break w_right_val = self.getitem(w_right, w_key) w_left_val = w_left_val.deref() w_right_val = w_right_val.deref() if w_left_val is w_right_val: continue if (w_left_val.tp == self.tp_array \ or w_left_val.tp == self.tp_object) \ and \ (w_right_val.tp == self.tp_array \ or w_right_val.tp == self.tp_object): # We've encountered a compound datatype, so we # have to fall back to the slower code below. if ignore_order: obj_st.append(w_left_val) obj_st.append(w_right_val) strict_st.append(strict) elif new_st is None: new_st = [w_right_val, w_left_val] else: new_st.append(w_right_val) new_st.append(w_left_val) else: cmp_res = self._compare(w_left_val, w_right_val, \ strict, ignore_order) if cmp_res != 0: if ignore_order or new_st is None: return cmp_res new_st.append(w_right_val) new_st.append(w_left_val) break if new_st is not None: while len(new_st) > 0: obj_st.append(new_st.pop()) obj_st.append(new_st.pop()) strict_st.append(strict) # same for all new work elif left_tp == self.tp_object and right_tp == self.tp_object: # left and right are both InstanceObjects, but we don't know if # they define a custom comparison method or not. We first try # calling their compare method. If it raises # InlineObjectComparison, we then fall back to "generic" object # comparison, which is inlined here rather than in its more # natural home of instanceobject.py try: res = w_left.compare(w_right, self, strict) if res != 0: return res continue except InlineObjectComparison: pass if w_left is w_right: continue elif strict or w_left.getclass() is not w_right.getclass(): return 1 left = w_left.get_instance_attrs(self.ec.interpreter) right = w_right.get_instance_attrs(self.ec.interpreter) if len(left) - len(right) < 0: return -1 if len(left) - len(right) > 0: return 1 # Check for the case where there are no nested aggregates # in either object. See the array case for details; this is # a very similar optimisation. new_st = None left_attr_itr = left.iteritems() right_attr_itr = right.iteritems() for key, w_left_val in left_attr_itr: r_key, w_right_val = right_attr_itr.next() if key != r_key: # Most of the time, if left and right are objects of the # same classes, their attributes will be defined in the # same order, so we can simply try iterating over both # in sequence. Sometimes, even if both sets of # attributes are identical, they'll get out of sequence, # so we then switch to this slow path. Of course, this # path also serves to catch cases when the sets of # attributes aren't identical too. try: w_right_val = right[key] except KeyError: if ignore_order or new_st is None: return -1 new_st.append(w_right_val) new_st.append(w_left_val) w_left_val = w_left_val.deref() w_right_val = w_right_val.deref() if w_left_val is w_right_val: continue if (w_left_val.tp == self.tp_array \ or w_left_val.tp == self.tp_object) \ and \ (w_right_val.tp == self.tp_array \ or w_right_val.tp == self.tp_object): # slow case, we found an aggregate nesting. if ignore_order: obj_st.append(w_left_val) obj_st.append(w_right_val) strict_st.append(False) elif new_st is None: new_st = [w_right_val, w_left_val] else: new_st.append(w_right_val) new_st.append(w_left_val) else: cmp_res = self._compare(w_left_val, w_right_val, \ strict, ignore_order) if cmp_res != 0: if ignore_order or new_st is None: return cmp_res new_st.append(w_right_val) new_st.append(w_left_val) break if new_st is not None: while len(new_st) > 0: obj_st.append(new_st.pop()) obj_st.append(new_st.pop()) strict_st.append(False) # same for all new work else: # We know that at least one of the members is a non-aggregate. cmp_res = self._compare(w_left, w_right, strict, ignore_order) if cmp_res != 0: return cmp_res # definitely not equal return 0 def getclass(self, w_obj): return w_obj.deref().getclass() def get_type_name(self, tp): return self.TYPENAMES[tp].lower() def is_really_int(self, w_obj): if w_obj.tp == self.tp_str: w_obj, fully_processed = convert_string_to_number( self.str_w(w_obj)) if not fully_processed: return None if w_obj.tp == self.tp_int: return w_obj def overflow_convert(self, w_obj): return w_obj.overflow_convert(self) def _force_int_from_str(self, w_obj): s = self.str_w(w_obj) decstr = "" i = 0 while i < len(s) and s[i] in PHP_WHITESPACE: i += 1 for c in s[i:]: if ('0' <= c <= '9'): decstr += c elif c == '-': decstr += c elif c == '+': decstr += c else: break if decstr == '': return 0 return int(decstr) def force_int(self, w_obj): if w_obj.tp == self.tp_str: return self._force_int_from_str(w_obj) elif w_obj.tp == self.tp_int: return self.int_w(w_obj) return w_obj.as_number(self).int_w(self) @jit.elidable def is_valid_varname(self, name): if len(name) == 0: return False c = name[0] if not('a' <= c <= 'z' or 'A' <= c <= 'Z' or c == '_'): return False for i in range(1, len(name)): c = name[i] if not('a' <= c <= 'z' or 'A' <= c <= 'Z' or c == '_' or '0' <= c <= '9'): return False return True @jit.elidable def is_valid_clsname(self, name): if len(name) == 0: return False c = name[0] if not('a' <= c <= 'z' or 'A' <= c <= 'Z' or c == '_' or c == '\\'): return False for i in range(1, len(name)): c = name[i] if not('a' <= c <= 'z' or 'A' <= c <= 'Z' or c == '_' or '0' <= c <= '9' or c == '\\'): return False return True def is_integer(self, w_obj): if w_obj.tp == self.tp_int: return True if isinstance(w_obj, W_StringObject): return w_obj.is_really_valid_number() return False def getclassintfname(self, w_obj): w_obj = w_obj.deref() if isinstance(w_obj, W_InstanceObject): classname = w_obj.getclass().get_identifier() return classname else: class_or_interface_name = self.str_w(w_obj) return class_or_interface_name def instanceof_w(self, w_left, w_right): from hippy.klass import ClassBase if isinstance(w_right, ClassBase): classintfname = w_right.name elif self.is_null(w_right): return False else: classintfname = self.getclassintfname(w_right) klass = self.getclass(w_left) if klass is None: return False return klass.is_subclass_of_class_or_intf_name(classintfname) def is_string(self, w_obj): s = self.str_w(w_obj) if not s: return True if s[0] == "0" and len(s) != 1: return True if s[0] < '0' or s[0] > '9': return True return False def instanceof(self, w_left, w_right): return self.newbool(self.instanceof_w(w_left, w_right)) def empty_ref(self): return W_Reference(self.w_Null) def default_object(self, interp): """Create a default object instance""" return k_stdClass.call_args(interp, []) def array_to_string_conversion(self, w_arr): out = "" with self.iter(w_arr) as itr: while not itr.done(): w_key, w_value = itr.next_item(self) if w_value.tp == self.tp_array: self.ec.notice("Array to string conversion") out += self.str_w(w_value) return self.newstr(out) def _get_callback_from_string(self, name): pos = name.find('::') if pos >= 0: clsname = name[:pos] methname = name[pos + 2:] return self._get_callback_from_class(clsname, methname) func = self.ec.interpreter.lookup_function(name) if func is not None: return func raise InvalidCallback("function '%s' not found or invalid " "function name" % (name)) def _get_callback_from_class(self, clsname, methname): interp = self.ec.interpreter klass = interp.lookup_class_or_intf(clsname) if klass is None: raise InvalidCallback("class '%s' not found" % (clsname)) contextclass = interp.get_contextclass() w_this = interp.get_frame().w_this try: meth = klass.getstaticmeth(methname, contextclass, w_this, interp) except VisibilityError as e: raise InvalidCallback(e.msg_callback(static=True)) return meth.bind(w_this, klass) def _get_callback_from_instance(self, w_instance, methname): contextclass = self.ec.interpreter.get_contextclass() try: meth = w_instance.getmeth(self, methname, contextclass) except VisibilityError as e: raise InvalidCallback(e.msg_callback(static=False)) return meth def get_callback(self, fname, arg_no, w_obj, give_warning=True): try: return self._get_callback(w_obj) except InvalidCallback as e: if give_warning: err_msg = ("%s() expects parameter %d to be a valid callback, %s" % (fname, arg_no, e.msg)) self.ec.warn(err_msg) return None def _get_callback(self, w_obj): from hippy.objects.closureobject import W_ClosureObject if w_obj.tp == self.tp_str: name = self.str_w(w_obj) return self._get_callback_from_string(name) elif w_obj.tp == self.tp_array: if w_obj.arraylen() != 2: raise InvalidCallback("array must have exactly two members") w_instance = self.getitem(w_obj, self.wrap(0)).deref() if isinstance(w_instance, W_InstanceObject): methname = self.str_w(self.getitem(w_obj, self.wrap(1))) return self._get_callback_from_instance(w_instance, methname) clsname = self.str_w(w_instance) if not self.is_valid_clsname(clsname): raise InvalidCallback("first array member is not a valid class " "name or object") methname = self.str_w(self.getitem(w_obj, self.wrap(1))) return self._get_callback_from_class(clsname, methname) elif isinstance(w_obj, W_InstanceObject): callable = w_obj.get_callable() if callable is not None: return callable raise InvalidCallback("no array or string given") def serialize(self, w_obj): from hippy.module.serialize import SerializerMemo assert not isinstance(w_obj, W_Reference) builder = StringBuilder() w_obj.serialize(self, builder, SerializerMemo()) return builder.build() def set_errno(self, errno): self.ec.interpreter.last_posix_errno = errno def get_errno(self): return self.ec.interpreter.last_posix_errno def compile_file(self, filename): return self.bytecode_cache.compile_file(filename, self)
class ObjSpace(object): """ This implements all the operations on the object. Since this is prebuilt, it should not contain any state """ (tp_int, tp_float, tp_str, tp_array, tp_null, tp_bool, tp_object, tp_file_res, tp_dir_res, tp_stream_context, tp_mysql_link, tp_mysql_result, tp_constant, tp_delayed_class_const, tp_xmlparser_res, tp_mcrypt_res) = range(16) # in the same order as the types above TYPENAMES = [ "integer", "double", "string", "array", "NULL", "boolean", "object", "resource", "resource", "resource", "resource", "resource", "constant", "delayed constant", "resource", "resource" ] w_True = w_True w_False = w_False w_Null = w_Null def __init__(self): self.regex_cache = RegexpCache(self) self.ec = ExecutionContext(self) self.bytecode_cache = BytecodeCache() self.setup_constants() self.setup_functions() self.setup_classes() def _setup_constant_any_case(self, name, w_value, outdict): cases = [c.lower() + c.upper() for c in name] for x in range(1 << len(name)): l = [cases[i][(x >> i) & 1] for i in range(len(name))] outdict[''.join(l)] = w_value def setup_constants(self): dct = OrderedDict() for modulename, lst in get_constants_by_module(self): for k, w_obj in lst: dct[k] = w_obj dct['PHP_INT_MAX'] = self.wrap(sys.maxint) dct['PHP_INT_SIZE'] = self.wrap(4 if sys.maxint < 2**32 else 8) self._setup_constant_any_case('true', self.w_True, dct) self._setup_constant_any_case('false', self.w_False, dct) self._setup_constant_any_case('null', self.w_Null, dct) self.prebuilt_constants = dct.keys() self.global_constant_cache = GlobalImmutCache(self, dct, force_lowcase=False) def setup_functions(self): self.global_function_cache = GlobalImmutCache(self, BUILTIN_FUNCTIONS) def setup_classes(self): self.prebuilt_classes = all_builtin_classes.keys() self.global_class_cache = GlobalImmutCache(self, all_builtin_classes) def int_w(self, w_obj): return w_obj.deref().int_w(self) def float_w(self, w_obj): return w_obj.deref().float_w(self) def is_true(self, w_obj): return w_obj.deref().is_true(self) @signature(types.any(), types.int(), returns=types.any()) def newint(self, v): return W_IntObject(v) def newfloat(self, v): return W_FloatObject(v) def newbool(self, v): if v: return self.w_True return self.w_False @signature(types.any(), types.str(can_be_None=True), returns=types.any()) def newstr(self, v): return W_StringObject.newconststr(v) @signature(types.any(), types.bytearray(), returns=types.any()) def newmutablestr(self, v): return W_StringObject.newmutablestr(v) def get_new_res_id(self): self.ec.interpreter.last_resource_id += 1 return self.ec.interpreter.last_resource_id def str_w(self, w_v, quiet=False): res = w_v.deref().str(self, quiet=quiet) assert res is not None return res def str_w_quiet(self, w_v, quiet=True): return w_v.deref().str(self, quiet=quiet) def as_string(self, w_v, quiet=False): return w_v.deref().as_string(self, quiet=quiet) def as_object(self, interp, w_v): w_arg = w_v.deref() if w_arg.tp == self.tp_object: w_obj = w_arg else: w_obj = self.default_object(interp) w_obj.cast_object_from(self, w_arg) return w_obj def as_number(self, w_v): return w_v.deref().as_number(self) def uplus(self, w_v): return w_v.deref().uplus(self) def uminus(self, w_v): return w_v.deref().uminus(self) def uplusplus(self, w_v): return w_v.deref().uplusplus(self) def uminusminus(self, w_v): return w_v.deref().uminusminus(self) def getitem(self, w_obj, w_item, give_notice=False): return w_obj.deref_temp().getitem(self, w_item.deref(), give_notice=give_notice) def setitem(self, w_obj, w_item, w_newvalue): # Warning: this API always makes a copy of the string or array. # If you want to do several changes to a string or array, look # at the documentation in setitem_maybe_inplace(). w_arr = w_obj.deref().copy() w_arr, w_val2 = w_arr.setitem2_maybe_inplace(self, w_item, w_newvalue) # w_val2 is generally ignored; it is generally just w_newvalue return w_arr def setitem_maybe_inplace(self, w_obj, w_arg, w_value): """Supports efficiently setting items into strings or arrays. Warning! Use only on objects that are known to be unique, i.e. not shared. These objects are: * the result of an earlier call to space.new_array_from_xxx() or space.newmutablestr(), as long as it didn't escape yet * or, the result of w_obj.deref_unique(), which will make a copy in case of doubt and give you a unique object. This returns a resulting object, often the same one but not always, and that result is still unique, so you can chain operations like setitem_maybe_inplace() on it. Known-unique objects can be stored in W_Reference objects by using 'w_reference.store(w_unique_obj, unique=True)'. Then 'w_reference.deref_unique()' will not need to make a copy. """ w_modified, _ = w_obj.setitem2_maybe_inplace(self, w_arg, w_value) return w_modified def appenditem_maybe_inplace(self, w_obj, w_value): """Same as setitem_maybe_inplace(), but for appending instead""" w_obj.appenditem_inplace(self, w_value) return w_obj # always for now, but may change in the future def packitem_maybe_inplace(self, w_obj, w_arg, w_value): """Same as setitem_maybe_inplace(), but if w_arg turns out to be a valid integer, ignore it and do a regular append instead.""" w_modified = w_obj.packitem_maybe_inplace(self, w_arg, w_value) return w_modified def concat(self, w_left, w_right): return self.as_string(w_left).strconcat(self, self.as_string(w_right)) def strlen(self, w_obj): return w_obj.deref_temp().strlen() def arraylen(self, w_obj): return w_obj.deref_temp().arraylen() def slice(self, w_arr, start, shift, keep_keys, keep_str_keys=False): if shift == 0: return self.new_array_from_list([]) if self.arraylen(w_arr) == 0: return self.new_array_from_list([]) if start > self.arraylen(w_arr): return self.new_array_from_list([]) if start < 0: start = self.arraylen(w_arr) + start if shift < 0: shift = self.arraylen(w_arr) + shift - start next_idx = 0 res_arr = [] idx = 0 with self.iter(w_arr) as itr: while not itr.done(): w_key, w_value = itr.next_item(self) if start <= idx < start + shift: if keep_keys: res_arr.append((w_key, w_value)) else: if keep_str_keys: if w_key.tp == self.tp_str: res_arr.append((w_key, w_value)) else: res_arr.append( (self.newint(next_idx), w_value)) next_idx += 1 else: res_arr.append((self.newint(next_idx), w_value)) next_idx += 1 idx += 1 return self.new_array_from_pairs(res_arr) def getchar(self, w_obj): # get first character return w_obj.deref().as_string(self).getchar(self) @specialize.argtype(1) def wrap(self, v): if v is None: return self.w_Null if isinstance(v, bool): return self.newbool(v) elif isinstance(v, int): return self.newint(v) elif isinstance(v, str): return self.newstr(v) elif isinstance(v, float): return self.newfloat(v) elif not we_are_translated() and isinstance(v, long): raise TypeError("longs are not RPython!") elif isinstance(v, str): return self.newstr(v) else: raise NotImplementedError(v) def _freeze_(self): return True def call_args(self, w_callable, args_w): return w_callable.call_args(self.ec.interpreter, args_w) def new_array_from_list(self, lst_w): return W_ArrayObject.new_array_from_list(self, lst_w) def new_array_from_rdict(self, rdict_w): return W_ArrayObject.new_array_from_rdict(self, rdict_w) def get_rdict_from_array(self, w_arr): return w_arr.deref_temp().get_rdict_from_array() def rdict_remove(self, rdict, w_obj): rstr = w_obj.deref().str(self) try: del rdict[rstr] except KeyError: pass def new_array_from_dict(self, dict_w): "NOT_RPYTHON: for tests only (gets a random ordering)" rdict_w = OrderedDict() for key, w_value in dict_w.items(): rdict_w[key] = w_value return W_ArrayObject.new_array_from_rdict(self, rdict_w) def new_array_from_pairs(self, pairs_ww, allow_bogus=False): return W_ArrayObject.new_array_from_pairs(self, pairs_ww, allow_bogus=allow_bogus) new_map_from_pairs = new_array_from_pairs # for now def iter(self, w_arr): return ObjSpaceWithIter(self, w_arr) def create_iter(self, w_arr, contextclass=None): w_arr = w_arr.deref() return w_arr.create_iter(self, contextclass) def create_iter_ref(self, r_array, contextclass=None): if not isinstance(r_array, W_Reference): raise self.ec.fatal("foreach(1 as &2): argument 1 must be a " "variable") w_arr = r_array.deref_temp() return w_arr.create_iter_ref(self, r_array, contextclass) def str_eq(self, w_one, w_two, quiet=False): w_one = w_one.deref() w_two = w_two.deref() if w_one.tp != w_two.tp: w_one = self.as_string(w_one, quiet=quiet) w_two = self.as_string(w_two, quiet=quiet) return self._compare(w_one, w_two, ignore_order=True) == 0 def get_globals_wrapper(self): return self.ec.interpreter.globals def lookup_local_vars(self, name): frame = self.ec.interpreter.topframeref() try: return frame.get_ref_by_name(name, create_new=False) except KeyError: return None def as_array(self, w_obj): w_obj = w_obj.deref() if w_obj.tp == self.tp_object: return w_obj.get_rdict_array(self) if w_obj.tp != self.tp_array: if w_obj is self.w_Null: return self.new_array_from_list([]) w_obj = self.new_array_from_list([w_obj]) assert isinstance(w_obj, W_ArrayObject) return w_obj def is_array(self, w_obj): return w_obj.deref().tp == self.tp_array def is_str(self, w_obj): return w_obj.deref().tp == self.tp_str def is_null(self, w_obj): return w_obj.deref().tp == self.tp_null def is_object(self, w_obj): return w_obj.deref().tp == self.tp_object def is_resource(self, w_obj): return isinstance(w_obj, W_Resource) def gettypename(self, w_obj): w_obj = w_obj.deref() if isinstance(w_obj, W_InstanceObject): return 'instance of ' + w_obj.klass.name else: return self.TYPENAMES[w_obj.tp].lower() def eq(self, w_left, w_right): res = self._compare(w_left, w_right, ignore_order=True) return self.newbool(res == 0) def eq_w(self, w_left, w_right): res = self._compare(w_left, w_right, ignore_order=True) return res == 0 def ne(self, w_left, w_right): res = self._compare(w_left, w_right, ignore_order=True) return self.newbool(res != 0) def lt(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res < 0) def gt(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res > 0) def le(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res <= 0) def ge(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res >= 0) def mod(self, w_left, w_right): left = self.force_int(w_left) right = self.force_int(w_right) return self._mod(left, right) def _mod(self, left, right): if right == 0: self.ec.warn("Division by zero") return self.w_False elif right == -1: return self.newint(0) z = left % right if z != 0 and ((left < 0 and right > 0) or (left > 0 and right < 0)): z -= right return self.newint(z) def or_string(self, w_left, w_right): left = w_left.unwrap() right = w_right.unwrap() if len(left) < len(right): left, right = right, left s = StringBuilder(len(left)) for i in range(len(right)): char = chr(ord(left[i]) | ord(right[i])) s.append(char) for i in range(len(right), len(left)): s.append(left[i]) return self.newstr(s.build()) def or_(self, w_left, w_right): if (isinstance(w_left, W_StringObject) and isinstance(w_right, W_StringObject)): return self.or_string(w_left, w_right) else: left = w_left.int_w(self) right = w_right.int_w(self) return self.newint(left | right) def lshift(self, w_left, w_right): left = self.force_int(w_left) right = self.force_int(w_right) z = intmask(left << (right & MASK_31_63)) return W_IntObject(z) def rshift(self, w_left, w_right): left = self.force_int(w_left) right = self.force_int(w_right) z = intmask(left >> (right & MASK_31_63)) return W_IntObject(z) def is_w(self, w_a, w_b): return self._compare(w_a, w_b, strict=True, ignore_order=True) == 0 def _compare(self, w_left, w_right, strict=False, ignore_order=False): w_left = w_left.deref() w_right = w_right.deref() left_tp = w_left.tp right_tp = w_right.tp if strict: if left_tp != right_tp: return 1 if (left_tp == self.tp_float and right_tp == self.tp_float): return my_cmp(self.float_w(w_left), self.float_w(w_right), ignore_order) if (left_tp == self.tp_int and right_tp == self.tp_float): return my_cmp(self.float_w(w_left), self.float_w(w_right), ignore_order) if (left_tp == self.tp_float and right_tp == self.tp_int): return my_cmp(self.float_w(w_left), self.float_w(w_right), ignore_order) elif (left_tp == self.tp_int and right_tp == self.tp_int): return my_cmp(self.int_w(w_left), self.int_w(w_right), ignore_order) elif (left_tp == self.tp_array and right_tp == self.tp_array): return self._compare_array(w_left, w_right, strict) elif (left_tp == self.tp_null and right_tp == self.tp_null): return 0 elif (left_tp == self.tp_null and right_tp == self.tp_bool): if self.is_true(w_right): return -1 return 0 elif (left_tp == self.tp_bool and right_tp == self.tp_null): if self.is_true(w_left): return 1 return 0 elif (left_tp == self.tp_bool and right_tp == self.tp_bool): return my_cmp(self.is_true(w_left), self.is_true(w_right), ignore_order) elif (left_tp == self.tp_str and right_tp == self.tp_str): left = self.str_w(w_left) right = self.str_w(w_right) if not strict: # a small optimimization first, if both are single-char left_length = len(left) right_length = len(right) if (jit.isconstant(left_length) and left_length == 1 and jit.isconstant(right_length) and right_length == 1): return my_cmp(ord(left[0]), ord(right[0]), ignore_order) # w_right_num, right_valid = convert_string_to_number(right) if right_valid: w_left_num, left_valid = convert_string_to_number(left) if left_valid: return self._compare(w_left_num, w_right_num, ignore_order=ignore_order) return my_cmp(left, right, ignore_order) elif (left_tp == self.tp_null and right_tp == self.tp_str): return my_cmp("", self.str_w(w_right), ignore_order) elif (left_tp == self.tp_str and right_tp == self.tp_null): return my_cmp(self.str_w(w_left), "", ignore_order) elif (left_tp == self.tp_object and right_tp == self.tp_null): return 1 elif (left_tp == self.tp_null and right_tp == self.tp_object): return -1 elif (left_tp == self.tp_object and right_tp == self.tp_object): return w_left.compare(w_right, self, strict) else: if (left_tp == self.tp_null): if self.is_true(w_right): return -1 return 0 elif (right_tp == self.tp_null): if self.is_true(w_left): return 1 return 0 elif (left_tp == self.tp_bool or right_tp == self.tp_bool): return my_cmp(self.is_true(w_left), self.is_true(w_right), ignore_order) elif (left_tp == self.tp_array): return 1 elif (right_tp == self.tp_array): return -1 elif (left_tp == self.tp_object): return 1 elif (right_tp == self.tp_object): return -1 else: return self._compare(self.as_number(w_left), self.as_number(w_right), ignore_order=ignore_order) raise NotImplementedError() def _compare_object(self, w_left, w_right, strict): if w_left is w_right: return 0 elif strict or w_left.getclass() is not w_right.getclass(): return 1 left = w_left.get_instance_attrs() right = w_right.get_instance_attrs() if len(left) - len(right) < 0: return -1 if len(left) - len(right) > 0: return 1 for key, w_value in left.iteritems(): try: w_right_value = right[key] except KeyError: return 1 cmp_res = self._compare(w_value, w_right_value) if cmp_res == 0: continue else: return cmp_res return 0 def _compare_array(self, w_left, w_right, strict): if w_left.arraylen() - w_right.arraylen() < 0: return -1 if w_left.arraylen() - w_right.arraylen() > 0: return 1 with self.iter(w_left) as itr: while not itr.done(): w_key, w_value = itr.next_item(self) if w_right.isset_index(self, w_key): w_right_value = self.getitem(w_right, w_key) if strict: cmp_res = self._compare(w_value, w_right_value, strict=True) else: cmp_res = self._compare(w_value, w_right_value) if cmp_res == 0: continue else: return cmp_res else: return 1 return 0 def getclass(self, w_obj): return w_obj.deref().getclass() def get_type_name(self, tp): return self.TYPENAMES[tp].lower() def is_really_int(self, w_obj): if w_obj.tp == self.tp_str: w_obj, fully_processed = convert_string_to_number( self.str_w(w_obj)) if not fully_processed: return None if w_obj.tp == self.tp_int: return w_obj def overflow_convert(self, w_obj): return w_obj.overflow_convert(self) def _force_int_from_str(self, w_obj): s = self.str_w(w_obj) decstr = "" i = 0 while i < len(s) and s[i] in PHP_WHITESPACE: i += 1 for c in s[i:]: if ('0' <= c <= '9'): decstr += c elif c == '-': decstr += c elif c == '+': decstr += c else: break if decstr == '': return 0 return int(decstr) def force_int(self, w_obj): if w_obj.tp == self.tp_str: return self._force_int_from_str(w_obj) elif w_obj.tp == self.tp_int: return self.int_w(w_obj) return w_obj.as_number(self).int_w(self) @jit.elidable def is_valid_varname(self, name): if len(name) == 0: return False c = name[0] if not ('a' <= c <= 'z' or 'A' <= c <= 'Z' or c == '_'): return False for i in range(1, len(name)): c = name[i] if not ('a' <= c <= 'z' or 'A' <= c <= 'Z' or c == '_' or '0' <= c <= '9'): return False return True def is_integer(self, w_obj): if w_obj.tp == self.tp_int: return True if isinstance(w_obj, W_StringObject): return w_obj.is_really_valid_number() return False def getclassintfname(self, w_obj): w_obj = w_obj.deref() if isinstance(w_obj, W_InstanceObject): classname = w_obj.klass.get_identifier() return classname else: class_or_interface_name = self.str_w(w_obj) return class_or_interface_name def instanceof_w(self, w_left, w_right): from hippy.klass import ClassBase if isinstance(w_right, ClassBase): classintfname = w_right.name elif self.is_null(w_right): return False else: classintfname = self.getclassintfname(w_right) klass = self.getclass(w_left) if klass is None: return False return klass.is_subclass_of_class_or_intf_name(classintfname) def is_string(self, w_obj): s = self.str_w(w_obj) if not s: return True if s[0] == "0" and len(s) != 1: return True if s[0] < '0' or s[0] > '9': return True return False def instanceof(self, w_left, w_right): return self.newbool(self.instanceof_w(w_left, w_right)) def empty_ref(self): return W_Reference(self.w_Null) def default_object(self, interp): """Create a default object instance""" return k_stdClass.call_args(interp, []) def array_to_string_conversion(self, w_arr): out = "" with self.iter(w_arr) as itr: while not itr.done(): w_key, w_value = itr.next_item(self) if w_value.tp == self.tp_array: self.ec.notice("Array to string conversion") out += self.str_w(w_value) return self.newstr(out) def _get_callback_from_string(self, name): pos = name.find('::') if pos >= 0: clsname = name[:pos] methname = name[pos + 2:] return self._get_callback_from_class(clsname, methname) try: return self.ec.interpreter.lookup_function(name) except KeyError: raise InvalidCallback("function '%s' not found or invalid " "function name" % (name)) def _get_callback_from_class(self, clsname, methname): interp = self.ec.interpreter klass = interp.lookup_class_or_intf(clsname) if klass is None: raise InvalidCallback("class '%s' not found" % (clsname)) contextclass = interp.get_contextclass() w_this = interp.get_frame().w_this try: meth = klass.getstaticmeth(methname, contextclass, w_this, interp) except VisibilityError as e: raise InvalidCallback(e.msg_callback(static=True)) return meth.bind(w_this, klass) def _get_callback_from_instance(self, w_instance, methname): contextclass = self.ec.interpreter.get_contextclass() try: meth = w_instance.getmeth(self, methname, contextclass) except VisibilityError as e: raise InvalidCallback(e.msg_callback(static=False)) return meth def get_callback(self, fname, arg_no, w_obj, give_warning=True): try: return self._get_callback(w_obj) except InvalidCallback as e: if give_warning: err_msg = ( "%s() expects parameter %d to be a valid callback, %s" % (fname, arg_no, e.msg)) self.ec.warn(err_msg) return None def _get_callback(self, w_obj): from hippy.objects.closureobject import W_ClosureObject if w_obj.tp == self.tp_str: name = self.str_w(w_obj) return self._get_callback_from_string(name) elif w_obj.tp == self.tp_array: if w_obj.arraylen() != 2: raise InvalidCallback("array must have exactly two members") w_instance = self.getitem(w_obj, self.wrap(0)).deref() if isinstance(w_instance, W_InstanceObject): methname = self.str_w(self.getitem(w_obj, self.wrap(1))) return self._get_callback_from_instance(w_instance, methname) clsname = self.str_w(w_instance) if not self.is_valid_varname(clsname): raise InvalidCallback("first array member is not a valid " "class name or object") methname = self.str_w(self.getitem(w_obj, self.wrap(1))) return self._get_callback_from_class(clsname, methname) elif isinstance(w_obj, W_InstanceObject): callable = w_obj.get_callable() if callable is not None: return callable raise InvalidCallback("no array or string given") def serialize(self, w_obj): from hippy.module.serialize import SerializerMemo assert not isinstance(w_obj, W_Reference) builder = StringBuilder() w_obj.serialize(self, builder, SerializerMemo()) return builder.build() def set_errno(self, errno): self.ec.interpreter.last_posix_errno = errno def get_errno(self): return self.ec.interpreter.last_posix_errno def compile_file(self, filename): return self.bytecode_cache.compile_file(filename, self)
class ObjSpace(object): """ This implements all the operations on the object. Since this is prebuilt, it should not contain any state """ (tp_int, tp_float, tp_str, tp_array, tp_null, tp_bool, tp_object, tp_file_res, tp_dir_res, tp_stream_context, tp_mysql_link, tp_mysql_result, tp_constant, tp_delayed_class_const, tp_xmlparser_res, tp_mcrypt_res) = range(16) # in the same order as the types above TYPENAMES = [ "integer", "double", "string", "array", "NULL", "boolean", "object", "resource", "resource", "resource", "resource", "resource", "constant", "delayed constant", "resource", "resource" ] w_True = w_True w_False = w_False w_Null = w_Null def __init__(self): self.regex_cache = RegexpCache(self) self.ec = ExecutionContext(self) self.bytecode_cache = BytecodeCache() self.setup_constants() self.setup_functions() self.setup_classes() def _setup_constant_any_case(self, name, w_value, outdict): cases = [c.lower() + c.upper() for c in name] for x in range(1 << len(name)): l = [cases[i][(x >> i) & 1] for i in range(len(name))] outdict[''.join(l)] = w_value def setup_constants(self): dct = OrderedDict() for modulename, lst in get_constants_by_module(self): for k, w_obj in lst: dct[k] = w_obj dct['PHP_INT_MAX'] = self.wrap(sys.maxint) dct['PHP_INT_SIZE'] = self.wrap(4 if sys.maxint < 2**32 else 8) self._setup_constant_any_case('true', self.w_True, dct) self._setup_constant_any_case('false', self.w_False, dct) self._setup_constant_any_case('null', self.w_Null, dct) self.prebuilt_constants = dct.keys() self.global_constant_cache = GlobalImmutCache(self, dct, force_lowcase=False) def setup_functions(self): self.global_function_cache = GlobalImmutCache(self, BUILTIN_FUNCTIONS) def setup_classes(self): self.prebuilt_classes = all_builtin_classes.keys() self.global_class_cache = GlobalImmutCache(self, all_builtin_classes) def int_w(self, w_obj): return w_obj.deref().int_w(self) def float_w(self, w_obj): return w_obj.deref().float_w(self) def is_true(self, w_obj): return w_obj.deref().is_true(self) @signature(types.any(), types.int(), returns=types.any()) def newint(self, v): return W_IntObject(v) def newfloat(self, v): return W_FloatObject(v) def newbool(self, v): if v: return self.w_True return self.w_False @signature(types.any(), types.str(can_be_None=True), returns=types.any()) def newstr(self, v): return W_StringObject.newconststr(v) @signature(types.any(), types.bytearray(), returns=types.any()) def newmutablestr(self, v): return W_StringObject.newmutablestr(v) def get_new_res_id(self): self.ec.interpreter.last_resource_id += 1 return self.ec.interpreter.last_resource_id def str_w(self, w_v, quiet=False): res = w_v.deref().str(self, quiet=quiet) assert res is not None return res def str_w_quiet(self, w_v, quiet=True): return w_v.deref().str(self, quiet=quiet) def as_string(self, w_v, quiet=False): return w_v.deref().as_string(self, quiet=quiet) def as_object(self, interp, w_v): w_arg = w_v.deref() if w_arg.tp == self.tp_object: w_obj = w_arg else: w_obj = self.default_object(interp) w_obj.cast_object_from(self, w_arg) return w_obj def as_number(self, w_v): return w_v.deref().as_number(self) def uplus(self, w_v): return w_v.deref().uplus(self) def uminus(self, w_v): return w_v.deref().uminus(self) def uplusplus(self, w_v): return w_v.deref().uplusplus(self) def uminusminus(self, w_v): return w_v.deref().uminusminus(self) def getitem(self, w_obj, w_item, give_notice=False): return w_obj.deref_temp().getitem(self, w_item.deref(), give_notice=give_notice) def setitem(self, w_obj, w_item, w_newvalue): # Warning: this API always makes a copy of the string or array. # If you want to do several changes to a string or array, look # at the documentation in setitem_maybe_inplace(). w_arr = w_obj.deref().copy() w_arr, w_val2 = w_arr.setitem2_maybe_inplace(self, w_item, w_newvalue) # w_val2 is generally ignored; it is generally just w_newvalue return w_arr def setitem_maybe_inplace(self, w_obj, w_arg, w_value): """Supports efficiently setting items into strings or arrays. Warning! Use only on objects that are known to be unique, i.e. not shared. These objects are: * the result of an earlier call to space.new_array_from_xxx() or space.newmutablestr(), as long as it didn't escape yet * or, the result of w_obj.deref_unique(), which will make a copy in case of doubt and give you a unique object. This returns a resulting object, often the same one but not always, and that result is still unique, so you can chain operations like setitem_maybe_inplace() on it. Known-unique objects can be stored in W_Reference objects by using 'w_reference.store(w_unique_obj, unique=True)'. Then 'w_reference.deref_unique()' will not need to make a copy. """ w_modified, _ = w_obj.setitem2_maybe_inplace(self, w_arg, w_value) return w_modified def appenditem_maybe_inplace(self, w_obj, w_value): """Same as setitem_maybe_inplace(), but for appending instead""" w_obj.appenditem_inplace(self, w_value) return w_obj # always for now, but may change in the future def packitem_maybe_inplace(self, w_obj, w_arg, w_value): """Same as setitem_maybe_inplace(), but if w_arg turns out to be a valid integer, ignore it and do a regular append instead.""" w_modified = w_obj.packitem_maybe_inplace(self, w_arg, w_value) return w_modified def concat(self, w_left, w_right): return self.as_string(w_left).strconcat(self, self.as_string(w_right)) def strlen(self, w_obj): return w_obj.deref_temp().strlen() def arraylen(self, w_obj): return w_obj.deref_temp().arraylen() def slice(self, w_arr, start, shift, keep_keys, keep_str_keys=False): if shift == 0: return self.new_array_from_list([]) if self.arraylen(w_arr) == 0: return self.new_array_from_list([]) if start > self.arraylen(w_arr): return self.new_array_from_list([]) if start < 0: start = self.arraylen(w_arr) + start if shift < 0: shift = self.arraylen(w_arr) + shift - start next_idx = 0 res_arr = [] idx = 0 with self.iter(w_arr) as itr: while not itr.done(): w_key, w_value = itr.next_item(self) if start <= idx < start + shift: if keep_keys: res_arr.append((w_key, w_value)) else: if keep_str_keys: if w_key.tp == self.tp_str: res_arr.append((w_key, w_value)) else: res_arr.append( (self.newint(next_idx), w_value)) next_idx += 1 else: res_arr.append((self.newint(next_idx), w_value)) next_idx += 1 idx += 1 return self.new_array_from_pairs(res_arr) def getchar(self, w_obj): # get first character return w_obj.deref().as_string(self).getchar(self) @specialize.argtype(1) def wrap(self, v): if v is None: return self.w_Null if isinstance(v, bool): return self.newbool(v) elif isinstance(v, int): return self.newint(v) elif isinstance(v, str): return self.newstr(v) elif isinstance(v, float): return self.newfloat(v) elif not we_are_translated() and isinstance(v, long): raise TypeError("longs are not RPython!") elif isinstance(v, str): return self.newstr(v) else: raise NotImplementedError(v) def _freeze_(self): return True def call_args(self, w_callable, args_w): return w_callable.call_args(self.ec.interpreter, args_w) def new_array_from_list(self, lst_w): return W_ArrayObject.new_array_from_list(self, lst_w) def new_array_from_rdict(self, rdict_w): return W_ArrayObject.new_array_from_rdict(self, rdict_w) def get_rdict_from_array(self, w_arr): return w_arr.deref_temp().get_rdict_from_array() def rdict_remove(self, rdict, w_obj): rstr = w_obj.deref().str(self) try: del rdict[rstr] except KeyError: pass def new_array_from_dict(self, dict_w): "NOT_RPYTHON: for tests only (gets a random ordering)" rdict_w = OrderedDict() for key, w_value in dict_w.items(): rdict_w[key] = w_value return W_ArrayObject.new_array_from_rdict(self, rdict_w) def new_array_from_pairs(self, pairs_ww, allow_bogus=False): return W_ArrayObject.new_array_from_pairs(self, pairs_ww, allow_bogus=allow_bogus) new_map_from_pairs = new_array_from_pairs # for now def iter(self, w_arr): return ObjSpaceWithIter(self, w_arr) def create_iter(self, w_arr, contextclass=None): w_arr = w_arr.deref() return w_arr.create_iter(self, contextclass) def create_iter_ref(self, r_array, contextclass=None): if not isinstance(r_array, W_Reference): raise self.ec.fatal("foreach(1 as &2): argument 1 must be a " "variable") w_arr = r_array.deref_temp() return w_arr.create_iter_ref(self, r_array, contextclass) def str_eq(self, w_one, w_two, quiet=False): w_one = w_one.deref() w_two = w_two.deref() if w_one.tp != w_two.tp: w_one = self.as_string(w_one, quiet=quiet) w_two = self.as_string(w_two, quiet=quiet) return self._compare(w_one, w_two, ignore_order=True) == 0 def get_globals_wrapper(self): return self.ec.interpreter.globals def lookup_local_vars(self, name): frame = self.ec.interpreter.topframeref() try: return frame.get_ref_by_name(name, create_new=False) except KeyError: return None def as_array(self, w_obj): w_obj = w_obj.deref() if w_obj.tp == self.tp_object: return w_obj.get_rdict_array(self) if w_obj.tp != self.tp_array: if w_obj is self.w_Null: return self.new_array_from_list([]) w_obj = self.new_array_from_list([w_obj]) assert isinstance(w_obj, W_ArrayObject) return w_obj def is_array(self, w_obj): return w_obj.deref().tp == self.tp_array def is_str(self, w_obj): return w_obj.deref().tp == self.tp_str def is_null(self, w_obj): return w_obj.deref().tp == self.tp_null def is_object(self, w_obj): return w_obj.deref().tp == self.tp_object def is_resource(self, w_obj): return isinstance(w_obj, W_Resource) def gettypename(self, w_obj): w_obj = w_obj.deref() if isinstance(w_obj, W_InstanceObject): return 'instance of ' + w_obj.getclass().name else: return self.TYPENAMES[w_obj.tp].lower() def eq(self, w_left, w_right): res = self._compare(w_left, w_right, ignore_order=True) return self.newbool(res == 0) def eq_w(self, w_left, w_right): res = self._compare(w_left, w_right, ignore_order=True) return res == 0 def ne(self, w_left, w_right): res = self._compare(w_left, w_right, ignore_order=True) return self.newbool(res != 0) def lt(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res < 0) def gt(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res > 0) def le(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res <= 0) def ge(self, w_left, w_right): res = self._compare(w_left, w_right) return self.newbool(res >= 0) def mod(self, w_left, w_right): left = self.force_int(w_left) right = self.force_int(w_right) return self._mod(left, right) def _mod(self, left, right): if right == 0: self.ec.warn("Division by zero") return self.w_False elif right == -1: return self.newint(0) z = left % right if z != 0 and ((left < 0 and right > 0) or (left > 0 and right < 0)): z -= right return self.newint(z) def or_string(self, w_left, w_right): left = w_left.unwrap() right = w_right.unwrap() if len(left) < len(right): left, right = right, left s = StringBuilder(len(left)) for i in range(len(right)): char = chr(ord(left[i]) | ord(right[i])) s.append(char) for i in range(len(right), len(left)): s.append(left[i]) return self.newstr(s.build()) def or_(self, w_left, w_right): if (isinstance(w_left, W_StringObject) and isinstance(w_right, W_StringObject)): return self.or_string(w_left, w_right) else: left = w_left.int_w(self) right = w_right.int_w(self) return self.newint(left | right) def lshift(self, w_left, w_right): left = self.force_int(w_left) right = self.force_int(w_right) z = intmask(left << (right & MASK_31_63)) return W_IntObject(z) def rshift(self, w_left, w_right): left = self.force_int(w_left) right = self.force_int(w_right) z = intmask(left >> (right & MASK_31_63)) return W_IntObject(z) def is_w(self, w_a, w_b): return self._compare(w_a, w_b, strict=True, ignore_order=True) == 0 def _compare(self, w_left, w_right, strict=False, ignore_order=False): w_left = w_left.deref() w_right = w_right.deref() left_tp = w_left.tp right_tp = w_right.tp if strict: if left_tp != right_tp: return 1 if (left_tp == self.tp_float and right_tp == self.tp_float): return my_cmp(self.float_w(w_left), self.float_w(w_right), ignore_order) if (left_tp == self.tp_int and right_tp == self.tp_float): return my_cmp(self.float_w(w_left), self.float_w(w_right), ignore_order) if (left_tp == self.tp_float and right_tp == self.tp_int): return my_cmp(self.float_w(w_left), self.float_w(w_right), ignore_order) elif (left_tp == self.tp_int and right_tp == self.tp_int): return my_cmp(self.int_w(w_left), self.int_w(w_right), ignore_order) elif (left_tp == self.tp_array and right_tp == self.tp_array): if w_left is w_right: return 0 w_left_len = w_left.arraylen() w_right_len = w_right.arraylen() if w_left_len < w_right_len: return -1 elif w_left_len > w_right_len: return 1 return self._compare_aggregates(w_left, w_right, strict, ignore_order) elif (left_tp == self.tp_null and right_tp == self.tp_null): return 0 elif (left_tp == self.tp_null and right_tp == self.tp_bool): if self.is_true(w_right): return -1 return 0 elif (left_tp == self.tp_bool and right_tp == self.tp_null): if self.is_true(w_left): return 1 return 0 elif (left_tp == self.tp_bool and right_tp == self.tp_bool): return my_cmp(self.is_true(w_left), self.is_true(w_right), ignore_order) elif (left_tp == self.tp_str and right_tp == self.tp_str): left = self.str_w(w_left) right = self.str_w(w_right) if not strict: # a small optimimization first, if both are single-char left_length = len(left) right_length = len(right) if (jit.isconstant(left_length) and left_length == 1 and jit.isconstant(right_length) and right_length == 1): return my_cmp(ord(left[0]), ord(right[0]), ignore_order) # w_right_num, right_valid = convert_string_to_number(right) if right_valid: w_left_num, left_valid = convert_string_to_number(left) if left_valid: return self._compare(w_left_num, w_right_num, ignore_order=ignore_order) return my_cmp(left, right, ignore_order) elif (left_tp == self.tp_null and right_tp == self.tp_str): return my_cmp("", self.str_w(w_right), ignore_order) elif (left_tp == self.tp_str and right_tp == self.tp_null): return my_cmp(self.str_w(w_left), "", ignore_order) elif (left_tp == self.tp_object and right_tp == self.tp_null): return 1 elif (left_tp == self.tp_null and right_tp == self.tp_object): return -1 elif (left_tp == self.tp_object and right_tp == self.tp_object): if w_left is w_right: return 0 return self._compare_aggregates(w_left, w_right, strict, ignore_order) else: if (left_tp == self.tp_null): if self.is_true(w_right): return -1 return 0 elif (right_tp == self.tp_null): if self.is_true(w_left): return 1 return 0 elif (left_tp == self.tp_bool or right_tp == self.tp_bool): return my_cmp(self.is_true(w_left), self.is_true(w_right), ignore_order) elif (left_tp == self.tp_array): return 1 elif (right_tp == self.tp_array): return -1 elif (left_tp == self.tp_object): return 1 elif (right_tp == self.tp_object): return -1 else: return self._compare(self.as_number(w_left), self.as_number(w_right), ignore_order=ignore_order) raise NotImplementedError() def _compare_aggregates(self, w_left, w_right, strict, ignore_order): # Aggregate things (user objects, arrays) are most naturally compared # recursively. However that is slow and tends to blow up the stack. This # function iteratively compares such things. It tries very hard not to # allocate more lists than it has to, as this is a performance criticial # piece of code. We do that by continually pushing things we come across # onto a stack (obj_st and its mirror strict_st). Because this function # not only says "is/isn't" equal but also "greater than/less than", we # have to march over these things in their natural order which sometimes # means creating temporary intermediate lists. # # There is also one common idiom in the below: we know that calling # _compare can only become recursive if both left and right hand side # are aggregate types. If one side is not an aggregate, either _compares # type checks will fail or it will convert both sides into numbers. # Either way we know recursion won't happen. # The object stack comes in pairs (w_left, w_right). Everything pushed # on here must already have been deref'd. obj_st = [w_left.deref(), w_right.deref()] strict_st = [strict] # strict stack while len(obj_st) > 0: w_right = obj_st.pop() w_left = obj_st.pop() strict = strict_st.pop() if w_left is None: assert w_right is None return 1 # deferred inequality detected left_tp = w_left.tp right_tp = w_right.tp if left_tp == self.tp_array and right_tp == self.tp_array: if w_left is w_right: continue w_left_len = w_left.arraylen() w_right_len = w_right.arraylen() if w_left_len < w_right_len: return -1 elif w_left_len > w_right_len: return 1 with self.iter(w_left) as left_itr, self.iter( w_right) as right_itr: # We iterate over the array and deal with all simple # datatypes immediately. If we find two that are obviously # not equal, we can stop the search at that point. Complex # datatypes, however, must be pushed on the stack and dealt # with in order later. # If allocated, new_st is a list mirroring obj_st *but* # notice it stores in order w_right, w_left new_st = None while not left_itr.done(): # Especially if the two arrays in question are lists, # their keys are likely to be a) of primitive type b) in # identical order. We therefore iterate over the left # and right arrays at the same time hoping that we'll # often see the same keys at the same points and avoid # doing expensive lookups. w_key, w_left_val = left_itr.next_item(self) w_rkey, w_right_val = right_itr.next_item(self) if isinstance(w_key, W_IntObject) \ and isinstance(w_rkey, W_IntObject) \ and w_key.intval == w_rkey.intval: pass elif isinstance(w_key, W_ConstStringObject) \ and isinstance(w_rkey, W_ConstStringObject) \ and w_key._strval == w_rkey._strval: pass else: if not w_right.isset_index(self, w_key): if ignore_order: return -1 if new_st is None: new_st = [None, None] else: new_st.append(None) new_st.append(None) break w_right_val = self.getitem(w_right, w_key) w_left_val = w_left_val.deref() w_right_val = w_right_val.deref() if w_left_val is w_right_val: continue if (w_left_val.tp == self.tp_array \ or w_left_val.tp == self.tp_object) \ and \ (w_right_val.tp == self.tp_array \ or w_right_val.tp == self.tp_object): # We've encountered a compound datatype, so we # have to fall back to the slower code below. if ignore_order: obj_st.append(w_left_val) obj_st.append(w_right_val) strict_st.append(strict) elif new_st is None: new_st = [w_right_val, w_left_val] else: new_st.append(w_right_val) new_st.append(w_left_val) else: cmp_res = self._compare(w_left_val, w_right_val, \ strict, ignore_order) if cmp_res != 0: if ignore_order or new_st is None: return cmp_res new_st.append(w_right_val) new_st.append(w_left_val) break if new_st is not None: while len(new_st) > 0: obj_st.append(new_st.pop()) obj_st.append(new_st.pop()) strict_st.append(strict) # same for all new work elif left_tp == self.tp_object and right_tp == self.tp_object: # left and right are both InstanceObjects, but we don't know if # they define a custom comparison method or not. We first try # calling their compare method. If it raises # InlineObjectComparison, we then fall back to "generic" object # comparison, which is inlined here rather than in its more # natural home of instanceobject.py try: res = w_left.compare(w_right, self, strict) if res != 0: return res continue except InlineObjectComparison: pass if w_left is w_right: continue elif strict or w_left.getclass() is not w_right.getclass(): return 1 left = w_left.get_instance_attrs(self.ec.interpreter) right = w_right.get_instance_attrs(self.ec.interpreter) if len(left) - len(right) < 0: return -1 if len(left) - len(right) > 0: return 1 # Check for the case where there are no nested aggregates # in either object. See the array case for details; this is # a very similar optimisation. new_st = None left_attr_itr = left.iteritems() right_attr_itr = right.iteritems() for key, w_left_val in left_attr_itr: r_key, w_right_val = right_attr_itr.next() if key != r_key: # Most of the time, if left and right are objects of the # same classes, their attributes will be defined in the # same order, so we can simply try iterating over both # in sequence. Sometimes, even if both sets of # attributes are identical, they'll get out of sequence, # so we then switch to this slow path. Of course, this # path also serves to catch cases when the sets of # attributes aren't identical too. try: w_right_val = right[key] except KeyError: if ignore_order or new_st is None: return -1 new_st.append(w_right_val) new_st.append(w_left_val) w_left_val = w_left_val.deref() w_right_val = w_right_val.deref() if w_left_val is w_right_val: continue if (w_left_val.tp == self.tp_array \ or w_left_val.tp == self.tp_object) \ and \ (w_right_val.tp == self.tp_array \ or w_right_val.tp == self.tp_object): # slow case, we found an aggregate nesting. if ignore_order: obj_st.append(w_left_val) obj_st.append(w_right_val) strict_st.append(False) elif new_st is None: new_st = [w_right_val, w_left_val] else: new_st.append(w_right_val) new_st.append(w_left_val) else: cmp_res = self._compare(w_left_val, w_right_val, \ strict, ignore_order) if cmp_res != 0: if ignore_order or new_st is None: return cmp_res new_st.append(w_right_val) new_st.append(w_left_val) break if new_st is not None: while len(new_st) > 0: obj_st.append(new_st.pop()) obj_st.append(new_st.pop()) strict_st.append(False) # same for all new work else: # We know that at least one of the members is a non-aggregate. cmp_res = self._compare(w_left, w_right, strict, ignore_order) if cmp_res != 0: return cmp_res # definitely not equal return 0 def getclass(self, w_obj): return w_obj.deref().getclass() def get_type_name(self, tp): return self.TYPENAMES[tp].lower() def is_really_int(self, w_obj): if w_obj.tp == self.tp_str: w_obj, fully_processed = convert_string_to_number( self.str_w(w_obj)) if not fully_processed: return None if w_obj.tp == self.tp_int: return w_obj def overflow_convert(self, w_obj): return w_obj.overflow_convert(self) def _force_int_from_str(self, w_obj): s = self.str_w(w_obj) decstr = "" i = 0 while i < len(s) and s[i] in PHP_WHITESPACE: i += 1 for c in s[i:]: if ('0' <= c <= '9'): decstr += c elif c == '-': decstr += c elif c == '+': decstr += c else: break if decstr == '': return 0 return int(decstr) def force_int(self, w_obj): if w_obj.tp == self.tp_str: return self._force_int_from_str(w_obj) elif w_obj.tp == self.tp_int: return self.int_w(w_obj) return w_obj.as_number(self).int_w(self) @jit.elidable def is_valid_varname(self, name): if len(name) == 0: return False c = name[0] if not ('a' <= c <= 'z' or 'A' <= c <= 'Z' or c == '_'): return False for i in range(1, len(name)): c = name[i] if not ('a' <= c <= 'z' or 'A' <= c <= 'Z' or c == '_' or '0' <= c <= '9'): return False return True @jit.elidable def is_valid_clsname(self, name): if len(name) == 0: return False c = name[0] if not ('a' <= c <= 'z' or 'A' <= c <= 'Z' or c == '_' or c == '\\'): return False for i in range(1, len(name)): c = name[i] if not ('a' <= c <= 'z' or 'A' <= c <= 'Z' or c == '_' or '0' <= c <= '9' or c == '\\'): return False return True def is_integer(self, w_obj): if w_obj.tp == self.tp_int: return True if isinstance(w_obj, W_StringObject): return w_obj.is_really_valid_number() return False def getclassintfname(self, w_obj): w_obj = w_obj.deref() if isinstance(w_obj, W_InstanceObject): classname = w_obj.getclass().get_identifier() return classname else: class_or_interface_name = self.str_w(w_obj) return class_or_interface_name def instanceof_w(self, w_left, w_right): from hippy.klass import ClassBase if isinstance(w_right, ClassBase): classintfname = w_right.name elif self.is_null(w_right): return False else: classintfname = self.getclassintfname(w_right) klass = self.getclass(w_left) if klass is None: return False return klass.is_subclass_of_class_or_intf_name(classintfname) def is_string(self, w_obj): s = self.str_w(w_obj) if not s: return True if s[0] == "0" and len(s) != 1: return True if s[0] < '0' or s[0] > '9': return True return False def instanceof(self, w_left, w_right): return self.newbool(self.instanceof_w(w_left, w_right)) def empty_ref(self): return W_Reference(self.w_Null) def default_object(self, interp): """Create a default object instance""" return k_stdClass.call_args(interp, []) def array_to_string_conversion(self, w_arr): out = "" with self.iter(w_arr) as itr: while not itr.done(): w_key, w_value = itr.next_item(self) if w_value.tp == self.tp_array: self.ec.notice("Array to string conversion") out += self.str_w(w_value) return self.newstr(out) def _get_callback_from_string(self, name): pos = name.find('::') if pos >= 0: clsname = name[:pos] methname = name[pos + 2:] return self._get_callback_from_class(clsname, methname) func = self.ec.interpreter.lookup_function(name) if func is not None: return func raise InvalidCallback("function '%s' not found or invalid " "function name" % (name)) def _get_callback_from_class(self, clsname, methname): interp = self.ec.interpreter klass = interp.lookup_class_or_intf(clsname) if klass is None: raise InvalidCallback("class '%s' not found" % (clsname)) contextclass = interp.get_contextclass() w_this = interp.get_frame().w_this try: meth = klass.getstaticmeth(methname, contextclass, w_this, interp) except VisibilityError as e: raise InvalidCallback(e.msg_callback(static=True)) return meth.bind(w_this, klass) def _get_callback_from_instance(self, w_instance, methname): contextclass = self.ec.interpreter.get_contextclass() try: meth = w_instance.getmeth(self, methname, contextclass) except VisibilityError as e: raise InvalidCallback(e.msg_callback(static=False)) return meth def get_callback(self, fname, arg_no, w_obj, give_warning=True): try: return self._get_callback(w_obj) except InvalidCallback as e: if give_warning: err_msg = ( "%s() expects parameter %d to be a valid callback, %s" % (fname, arg_no, e.msg)) self.ec.warn(err_msg) return None def _get_callback(self, w_obj): from hippy.objects.closureobject import W_ClosureObject if w_obj.tp == self.tp_str: name = self.str_w(w_obj) return self._get_callback_from_string(name) elif w_obj.tp == self.tp_array: if w_obj.arraylen() != 2: raise InvalidCallback("array must have exactly two members") w_instance = self.getitem(w_obj, self.wrap(0)).deref() if isinstance(w_instance, W_InstanceObject): methname = self.str_w(self.getitem(w_obj, self.wrap(1))) return self._get_callback_from_instance(w_instance, methname) clsname = self.str_w(w_instance) if not self.is_valid_clsname(clsname): raise InvalidCallback( "first array member is not a valid class " "name or object") methname = self.str_w(self.getitem(w_obj, self.wrap(1))) return self._get_callback_from_class(clsname, methname) elif isinstance(w_obj, W_InstanceObject): callable = w_obj.get_callable() if callable is not None: return callable raise InvalidCallback("no array or string given") def serialize(self, w_obj): from hippy.module.serialize import SerializerMemo assert not isinstance(w_obj, W_Reference) builder = StringBuilder() w_obj.serialize(self, builder, SerializerMemo()) return builder.build() def set_errno(self, errno): self.ec.interpreter.last_posix_errno = errno def get_errno(self): return self.ec.interpreter.last_posix_errno def compile_file(self, filename): return self.bytecode_cache.compile_file(filename, self)