Author: Amaury Forgeot d'Arc <[email protected]>
Branch: string-NUL
Changeset: r51938:56ffa1a912b1
Date: 2012-01-28 20:59 +0100
http://bitbucket.org/pypy/pypy/changeset/56ffa1a912b1/
Log: The NUL byte is now disallowed in many posix functions. We use the
annotator to "prove" that a string has no NUL.
diff --git a/pypy/annotation/binaryop.py b/pypy/annotation/binaryop.py
--- a/pypy/annotation/binaryop.py
+++ b/pypy/annotation/binaryop.py
@@ -434,13 +434,18 @@
class __extend__(pairtype(SomeString, SomeString)):
def union((str1, str2)):
- return SomeString(can_be_None=str1.can_be_None or str2.can_be_None)
+ result = SomeString(can_be_None=str1.can_be_None or str2.can_be_None)
+ if str1.no_NUL and str2.no_NUL:
+ result.no_NUL = True
+ return result
def add((str1, str2)):
# propagate const-ness to help getattr(obj, 'prefix' + const_name)
result = SomeString()
if str1.is_immutable_constant() and str2.is_immutable_constant():
result.const = str1.const + str2.const
+ if str1.no_NUL and str2.no_NUL:
+ result.no_NUL = True
return result
class __extend__(pairtype(SomeChar, SomeChar)):
@@ -475,7 +480,19 @@
raise NotImplementedError(
"string formatting mixing strings and unicode not
supported")
getbookkeeper().count('strformat', str, s_tuple)
- return SomeString()
+ result = SomeString()
+ no_NUL = str.no_NUL
+ for s_item in s_tuple.items:
+ if isinstance(s_item, SomeFloat):
+ pass
+ elif isinstance(s_item, SomeString) and s_item.no_NUL:
+ pass
+ else:
+ no_NUL = False
+ break
+ if no_NUL:
+ result.no_NUL = True
+ return result
class __extend__(pairtype(SomeString, SomeObject)):
@@ -806,7 +823,16 @@
# mixing Nones with other objects
-def _make_none_union(classname, constructor_args='', glob=None):
+def _make_none_union(classname, constructor_args='', glob=None,
+ copy_attributes=()):
+ if copy_attributes:
+ copy_attrs = (
+ 'result.__dict__.update((name, getattr(obj, name)) '
+ 'for name in %(copy_attributes)s if name in obj.__dict__)'
+ % locals())
+ else:
+ copy_attrs = ''
+
if glob is None:
glob = globals()
loc = locals()
@@ -814,21 +840,26 @@
class __extend__(pairtype(%(classname)s, SomePBC)):
def union((obj, pbc)):
if pbc.isNone():
- return %(classname)s(%(constructor_args)s)
+ result = %(classname)s(%(constructor_args)s)
+ %(copy_attrs)s
+ return result
else:
return SomeObject()
class __extend__(pairtype(SomePBC, %(classname)s)):
def union((pbc, obj)):
if pbc.isNone():
- return %(classname)s(%(constructor_args)s)
+ result = %(classname)s(%(constructor_args)s)
+ %(copy_attrs)s
+ return result
else:
return SomeObject()
""" % loc)
exec source.compile() in glob
_make_none_union('SomeInstance', 'classdef=obj.classdef, can_be_None=True')
-_make_none_union('SomeString', 'can_be_None=True')
+_make_none_union('SomeString', 'can_be_None=True',
+ copy_attributes=('no_NUL',))
_make_none_union('SomeUnicodeString', 'can_be_None=True')
_make_none_union('SomeList', 'obj.listdef')
_make_none_union('SomeDict', 'obj.dictdef')
diff --git a/pypy/annotation/bookkeeper.py b/pypy/annotation/bookkeeper.py
--- a/pypy/annotation/bookkeeper.py
+++ b/pypy/annotation/bookkeeper.py
@@ -344,8 +344,12 @@
elif issubclass(tp, str): # py.lib uses annotated str subclasses
if len(x) == 1:
result = SomeChar()
+ if not '\x00' in x:
+ result.no_NUL = True
else:
result = SomeString()
+ if not '\x00' in x:
+ result.no_NUL = True
elif tp is unicode:
if len(x) == 1:
result = SomeUnicodeCodePoint()
diff --git a/pypy/annotation/model.py b/pypy/annotation/model.py
--- a/pypy/annotation/model.py
+++ b/pypy/annotation/model.py
@@ -229,19 +229,27 @@
"Stands for an object which is known to be a string."
knowntype = str
immutable = True
+ can_be_None=False
+ no_NUL = False
+
def __init__(self, can_be_None=False):
- self.can_be_None = can_be_None
+ if can_be_None:
+ self.can_be_None = True
def can_be_none(self):
return self.can_be_None
def nonnoneify(self):
- return SomeString(can_be_None=False)
+ result = SomeString(can_be_None=False)
+ if self.no_NUL:
+ result.no_NUL = True
+ return result
class SomeUnicodeString(SomeObject):
"Stands for an object which is known to be an unicode string"
knowntype = unicode
immutable = True
+ no_NUL = False
def __init__(self, can_be_None=False):
self.can_be_None = can_be_None
@@ -502,6 +510,8 @@
s_None = SomePBC([], can_be_None=True)
s_Bool = SomeBool()
s_ImpossibleValue = SomeImpossibleValue()
+s_Str0 = SomeString()
+s_Str0.no_NUL = True
# ____________________________________________________________
# weakrefs
@@ -716,8 +726,7 @@
def not_const(s_obj):
if s_obj.is_constant():
- new_s_obj = SomeObject()
- new_s_obj.__class__ = s_obj.__class__
+ new_s_obj = SomeObject.__new__(s_obj.__class__)
dic = new_s_obj.__dict__ = s_obj.__dict__.copy()
if 'const' in dic:
del new_s_obj.const
diff --git a/pypy/annotation/test/test_annrpython.py
b/pypy/annotation/test/test_annrpython.py
--- a/pypy/annotation/test/test_annrpython.py
+++ b/pypy/annotation/test/test_annrpython.py
@@ -456,6 +456,20 @@
return ''.join(g(n))
s = a.build_types(f, [int])
assert s.knowntype == str
+ assert s.no_NUL
+
+ def test_str_split(self):
+ a = self.RPythonAnnotator()
+ def g(n):
+ if n:
+ return "test string"
+ def f(n):
+ if n:
+ return g(n).split(' ')
+ s = a.build_types(f, [int])
+ assert isinstance(s, annmodel.SomeList)
+ s_item = s.listdef.listitem.s_value
+ assert s_item.no_NUL
def test_str_splitlines(self):
a = self.RPythonAnnotator()
@@ -1841,7 +1855,7 @@
return obj.indirect()
a = self.RPythonAnnotator()
s = a.build_types(f, [bool])
- assert s == annmodel.SomeString(can_be_None=True)
+ assert annmodel.SomeString(can_be_None=True).contains(s)
def test_dont_see_AttributeError_clause(self):
class Stuff:
@@ -2018,6 +2032,37 @@
s = a.build_types(g, [int])
assert not s.can_be_None
+ def test_string_noNUL_canbeNone(self):
+ def f(a):
+ if a:
+ return "abc"
+ else:
+ return None
+ a = self.RPythonAnnotator()
+ s = a.build_types(f, [int])
+ assert s.can_be_None
+ assert s.no_NUL
+
+ def test_str_or_None(self):
+ def f(a):
+ if a:
+ return "abc"
+ else:
+ return None
+ def g(a):
+ x = f(a)
+ #assert x is not None
+ if x is None:
+ return "abcd"
+ return x
+ if isinstance(x, str):
+ return x
+ return "impossible"
+ a = self.RPythonAnnotator()
+ s = a.build_types(f, [int])
+ assert s.can_be_None
+ assert s.no_NUL
+
def test_emulated_pbc_call_simple(self):
def f(a,b):
return a + b
@@ -2071,6 +2116,19 @@
assert isinstance(s, annmodel.SomeIterator)
assert s.variant == ('items',)
+ def test_iteritems_str0(self):
+ def it(d):
+ return d.iteritems()
+ def f():
+ d0 = {'1a': '2a', '3': '4'}
+ for item in it(d0):
+ return "%s=%s" % item
+ raise ValueError
+ a = self.RPythonAnnotator()
+ s = a.build_types(f, [])
+ assert isinstance(s, annmodel.SomeString)
+ assert s.no_NUL
+
def test_non_none_and_none_with_isinstance(self):
class A(object):
pass
diff --git a/pypy/annotation/unaryop.py b/pypy/annotation/unaryop.py
--- a/pypy/annotation/unaryop.py
+++ b/pypy/annotation/unaryop.py
@@ -491,7 +491,10 @@
if isinstance(str, SomeUnicodeString):
return immutablevalue(u"")
return immutablevalue("")
- return str.basestringclass()
+ result = str.basestringclass()
+ if str.no_NUL and s_item.no_NUL:
+ result.no_NUL = True
+ return result
def iter(str):
return SomeIterator(str)
@@ -502,18 +505,28 @@
def method_split(str, patt, max=-1):
getbookkeeper().count("str_split", str, patt)
- return getbookkeeper().newlist(str.basestringclass())
+ s_item = str.basestringclass()
+ if str.no_NUL:
+ s_item.no_NUL = True
+ result = getbookkeeper().newlist(s_item)
+ return result
def method_rsplit(str, patt, max=-1):
getbookkeeper().count("str_rsplit", str, patt)
- return getbookkeeper().newlist(str.basestringclass())
+ result = getbookkeeper().newlist(str.basestringclass())
+ if str.no_NUL:
+ result.no_NUL = True
+ return result
def method_replace(str, s1, s2):
return str.basestringclass()
def getslice(str, s_start, s_stop):
check_negative_slice(s_start, s_stop)
- return str.basestringclass()
+ result = str.basestringclass()
+ if str.no_NUL:
+ result.no_NUL = True
+ return result
class __extend__(SomeUnicodeString):
def method_encode(uni, s_enc):
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -1312,6 +1312,15 @@
def str_w(self, w_obj):
return w_obj.str_w(self)
+ def str0_w(self, w_obj):
+ "Like str_w, but rejects strings with NUL bytes."
+ from pypy.rlib import rstring
+ result = w_obj.str_w(self)
+ if '\x00' in result:
+ raise OperationError(self.w_TypeError, self.wrap(
+ 'argument must be a string without NUL characters'))
+ return rstring.assert_str0(result)
+
def int_w(self, w_obj):
return w_obj.int_w(self)
diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py
--- a/pypy/interpreter/gateway.py
+++ b/pypy/interpreter/gateway.py
@@ -130,6 +130,9 @@
def visit_str_or_None(self, el, app_sig):
self.checked_space_method(el, app_sig)
+ def visit_str0(self, el, app_sig):
+ self.checked_space_method(el, app_sig)
+
def visit_nonnegint(self, el, app_sig):
self.checked_space_method(el, app_sig)
@@ -249,6 +252,9 @@
def visit_str_or_None(self, typ):
self.run_args.append("space.str_or_None_w(%s)" % (self.scopenext(),))
+ def visit_str0(self, typ):
+ self.run_args.append("space.str0_w(%s)" % (self.scopenext(),))
+
def visit_nonnegint(self, typ):
self.run_args.append("space.gateway_nonnegint_w(%s)" % (
self.scopenext(),))
@@ -383,6 +389,9 @@
def visit_str_or_None(self, typ):
self.unwrap.append("space.str_or_None_w(%s)" % (self.nextarg(),))
+ def visit_str0(self, typ):
+ self.unwrap.append("space.str0_w(%s)" % (self.nextarg(),))
+
def visit_nonnegint(self, typ):
self.unwrap.append("space.gateway_nonnegint_w(%s)" % (self.nextarg(),))
diff --git a/pypy/interpreter/mixedmodule.py b/pypy/interpreter/mixedmodule.py
--- a/pypy/interpreter/mixedmodule.py
+++ b/pypy/interpreter/mixedmodule.py
@@ -50,7 +50,7 @@
space.call_method(self.w_dict, 'update', self.w_initialdict)
for w_submodule in self.submodules_w:
- name = space.str_w(w_submodule.w_name)
+ name = space.str0_w(w_submodule.w_name)
space.setitem(self.w_dict, space.wrap(name.split(".")[-1]),
w_submodule)
space.getbuiltinmodule(name)
diff --git a/pypy/interpreter/module.py b/pypy/interpreter/module.py
--- a/pypy/interpreter/module.py
+++ b/pypy/interpreter/module.py
@@ -31,7 +31,8 @@
def install(self):
"""NOT_RPYTHON: installs this module into space.builtin_modules"""
w_mod = self.space.wrap(self)
- self.space.builtin_modules[self.space.unwrap(self.w_name)] = w_mod
+ modulename = self.space.str0_w(self.w_name)
+ self.space.builtin_modules[modulename] = w_mod
def setup_after_space_initialization(self):
"""NOT_RPYTHON: to allow built-in modules to do some more setup
diff --git a/pypy/module/bz2/interp_bz2.py b/pypy/module/bz2/interp_bz2.py
--- a/pypy/module/bz2/interp_bz2.py
+++ b/pypy/module/bz2/interp_bz2.py
@@ -328,7 +328,7 @@
if basemode == "a":
raise OperationError(space.w_ValueError,
space.wrap("cannot append to bz2 file"))
- stream = open_path_helper(space.str_w(w_path), os_flags, False)
+ stream = open_path_helper(space.str0_w(w_path), os_flags, False)
if reading:
bz2stream = ReadBZ2Filter(space, stream, buffering)
buffering = 0 # by construction, the ReadBZ2Filter acts like
diff --git a/pypy/module/gc/interp_gc.py b/pypy/module/gc/interp_gc.py
--- a/pypy/module/gc/interp_gc.py
+++ b/pypy/module/gc/interp_gc.py
@@ -49,7 +49,7 @@
# ____________________________________________________________
-@unwrap_spec(filename=str)
+@unwrap_spec(filename='str0')
def dump_heap_stats(space, filename):
tb = rgc._heap_stats()
if not tb:
diff --git a/pypy/module/imp/importing.py b/pypy/module/imp/importing.py
--- a/pypy/module/imp/importing.py
+++ b/pypy/module/imp/importing.py
@@ -138,7 +138,7 @@
ctxt_package = None
if ctxt_w_package is not None and ctxt_w_package is not space.w_None:
try:
- ctxt_package = space.str_w(ctxt_w_package)
+ ctxt_package = space.str0_w(ctxt_w_package)
except OperationError, e:
if not e.match(space, space.w_TypeError):
raise
@@ -187,7 +187,7 @@
ctxt_name = None
if ctxt_w_name is not None:
try:
- ctxt_name = space.str_w(ctxt_w_name)
+ ctxt_name = space.str0_w(ctxt_w_name)
except OperationError, e:
if not e.match(space, space.w_TypeError):
raise
@@ -230,7 +230,7 @@
return rel_modulename, rel_level
-@unwrap_spec(name=str, level=int)
+@unwrap_spec(name='str0', level=int)
def importhook(space, name, w_globals=None,
w_locals=None, w_fromlist=None, level=-1):
modulename = name
@@ -377,8 +377,8 @@
fromlist_w = space.fixedview(w_all)
for w_name in fromlist_w:
if try_getattr(space, w_mod, w_name) is None:
- load_part(space, w_path, prefix, space.str_w(w_name),
w_mod,
- tentative=1)
+ load_part(space, w_path, prefix, space.str0_w(w_name),
+ w_mod, tentative=1)
return w_mod
else:
return first
@@ -432,7 +432,7 @@
def __init__(self, space):
pass
- @unwrap_spec(path=str)
+ @unwrap_spec(path='str0')
def descr_init(self, space, path):
if not path:
raise OperationError(space.w_ImportError, space.wrap(
@@ -513,7 +513,7 @@
if w_loader:
return FindInfo.fromLoader(w_loader)
- path = space.str_w(w_pathitem)
+ path = space.str0_w(w_pathitem)
filepart = os.path.join(path, partname)
if os.path.isdir(filepart) and case_ok(filepart):
initfile = os.path.join(filepart, '__init__')
@@ -671,7 +671,7 @@
space.wrap("reload() argument must be module"))
w_modulename = space.getattr(w_module, space.wrap("__name__"))
- modulename = space.str_w(w_modulename)
+ modulename = space.str0_w(w_modulename)
if not space.is_w(check_sys_modules(space, w_modulename), w_module):
raise operationerrfmt(
space.w_ImportError,
diff --git a/pypy/module/imp/interp_imp.py b/pypy/module/imp/interp_imp.py
--- a/pypy/module/imp/interp_imp.py
+++ b/pypy/module/imp/interp_imp.py
@@ -44,7 +44,7 @@
return space.interp_w(W_File, w_file).stream
def find_module(space, w_name, w_path=None):
- name = space.str_w(w_name)
+ name = space.str0_w(w_name)
if space.is_w(w_path, space.w_None):
w_path = None
@@ -75,7 +75,7 @@
def load_module(space, w_name, w_file, w_filename, w_info):
w_suffix, w_filemode, w_modtype = space.unpackiterable(w_info)
- filename = space.str_w(w_filename)
+ filename = space.str0_w(w_filename)
filemode = space.str_w(w_filemode)
if space.is_w(w_file, space.w_None):
stream = None
@@ -92,7 +92,7 @@
space, w_name, find_info, reuse=True)
def load_source(space, w_modulename, w_filename, w_file=None):
- filename = space.str_w(w_filename)
+ filename = space.str0_w(w_filename)
stream = get_file(space, w_file, filename, 'U')
@@ -105,7 +105,7 @@
stream.close()
return w_mod
-@unwrap_spec(filename=str)
+@unwrap_spec(filename='str0')
def _run_compiled_module(space, w_modulename, filename, w_file, w_module):
# the function 'imp._run_compiled_module' is a pypy-only extension
stream = get_file(space, w_file, filename, 'rb')
@@ -119,7 +119,7 @@
if space.is_w(w_file, space.w_None):
stream.close()
-@unwrap_spec(filename=str)
+@unwrap_spec(filename='str0')
def load_compiled(space, w_modulename, filename, w_file=None):
w_mod = space.wrap(Module(space, w_modulename))
importing._prepare_module(space, w_mod, filename, None)
@@ -138,7 +138,7 @@
return space.wrap(Module(space, w_name, add_package=False))
def init_builtin(space, w_name):
- name = space.str_w(w_name)
+ name = space.str0_w(w_name)
if name not in space.builtin_modules:
return
if space.finditem(space.sys.get('modules'), w_name) is not None:
@@ -151,7 +151,7 @@
return None
def is_builtin(space, w_name):
- name = space.str_w(w_name)
+ name = space.str0_w(w_name)
if name not in space.builtin_modules:
return space.wrap(0)
if space.finditem(space.sys.get('modules'), w_name) is not None:
diff --git a/pypy/module/posix/interp_posix.py
b/pypy/module/posix/interp_posix.py
--- a/pypy/module/posix/interp_posix.py
+++ b/pypy/module/posix/interp_posix.py
@@ -37,7 +37,7 @@
if space.isinstance_w(w_obj, space.w_unicode):
w_obj = space.call_method(w_obj, 'encode',
getfilesystemencoding(space))
- return space.str_w(w_obj)
+ return space.str0_w(w_obj)
class FileEncoder(object):
def __init__(self, space, w_obj):
@@ -56,7 +56,7 @@
self.w_obj = w_obj
def as_bytes(self):
- return self.space.str_w(self.w_obj)
+ return self.space.str0_w(self.w_obj)
def as_unicode(self):
space = self.space
@@ -71,7 +71,7 @@
fname = FileEncoder(space, w_fname)
return func(fname, *args)
else:
- fname = space.str_w(w_fname)
+ fname = space.str0_w(w_fname)
return func(fname, *args)
return dispatch
@@ -401,7 +401,7 @@
fullpath = rposix._getfullpathname(path)
w_fullpath = space.wrap(fullpath)
else:
- path = space.str_w(w_path)
+ path = space.str0_w(w_path)
fullpath = rposix._getfullpathname(path)
w_fullpath = space.wrap(fullpath)
except OSError, e:
@@ -548,7 +548,7 @@
for s in result
]
else:
- dirname = space.str_w(w_dirname)
+ dirname = space.str0_w(w_dirname)
result = rposix.listdir(dirname)
result_w = [space.wrap(s) for s in result]
except OSError, e:
@@ -635,7 +635,7 @@
import signal
os.kill(os.getpid(), signal.SIGABRT)
-@unwrap_spec(src=str, dst=str)
+@unwrap_spec(src='str0', dst='str0')
def link(space, src, dst):
"Create a hard link to a file."
try:
@@ -650,7 +650,7 @@
except OSError, e:
raise wrap_oserror(space, e)
-@unwrap_spec(path=str)
+@unwrap_spec(path='str0')
def readlink(space, path):
"Return a string representing the path to which the symbolic link points."
try:
@@ -765,7 +765,7 @@
w_keys = space.call_method(w_env, 'keys')
for w_key in space.unpackiterable(w_keys):
w_value = space.getitem(w_env, w_key)
- env[space.str_w(w_key)] = space.str_w(w_value)
+ env[space.str0_w(w_key)] = space.str0_w(w_value)
return env
def execve(space, w_command, w_args, w_env):
@@ -1103,7 +1103,7 @@
except OSError, e:
raise wrap_oserror(space, e)
-@unwrap_spec(path=str, uid=c_uid_t, gid=c_gid_t)
+@unwrap_spec(path='str0', uid=c_uid_t, gid=c_gid_t)
def chown(space, path, uid, gid):
check_uid_range(space, uid)
check_uid_range(space, gid)
@@ -1113,7 +1113,7 @@
raise wrap_oserror(space, e, path)
return space.w_None
-@unwrap_spec(path=str, uid=c_uid_t, gid=c_gid_t)
+@unwrap_spec(path='str0', uid=c_uid_t, gid=c_gid_t)
def lchown(space, path, uid, gid):
check_uid_range(space, uid)
check_uid_range(space, gid)
diff --git a/pypy/module/sys/state.py b/pypy/module/sys/state.py
--- a/pypy/module/sys/state.py
+++ b/pypy/module/sys/state.py
@@ -74,7 +74,7 @@
#
return importlist
-@unwrap_spec(srcdir=str)
+@unwrap_spec(srcdir='str0')
def pypy_initial_path(space, srcdir):
try:
path = getinitialpath(get(space), srcdir)
diff --git a/pypy/module/zipimport/interp_zipimport.py
b/pypy/module/zipimport/interp_zipimport.py
--- a/pypy/module/zipimport/interp_zipimport.py
+++ b/pypy/module/zipimport/interp_zipimport.py
@@ -342,7 +342,7 @@
space = self.space
return space.wrap(self.filename)
-@unwrap_spec(name=str)
+@unwrap_spec(name='str0')
def descr_new_zipimporter(space, w_type, name):
w = space.wrap
ok = False
diff --git a/pypy/rlib/rstring.py b/pypy/rlib/rstring.py
--- a/pypy/rlib/rstring.py
+++ b/pypy/rlib/rstring.py
@@ -205,3 +205,43 @@
assert p.const is None
return SomeUnicodeBuilder(can_be_None=True)
+#___________________________________________________________________
+# Support functions for SomeString.no_NUL
+
+def assert_str0(fname):
+ assert '\x00' not in fname, "NUL byte in string"
+ return fname
+
+class Entry(ExtRegistryEntry):
+ _about_ = assert_str0
+
+ def compute_result_annotation(self, s_obj):
+ assert isinstance(s_obj, (SomeString, SomeUnicodeString))
+ if s_obj.no_NUL:
+ return s_obj
+ new_s_obj = SomeObject.__new__(s_obj.__class__)
+ new_s_obj.__dict__ = s_obj.__dict__.copy()
+ new_s_obj.no_NUL = True
+ return new_s_obj
+
+ def specialize_call(self, hop):
+ hop.exception_cannot_occur()
+ return hop.inputarg(hop.args_r[0], arg=0)
+
+def check_str0(fname):
+ """A 'probe' to trigger a failure at translation time, if the
+ string was not proved to not contain NUL characters."""
+ assert '\x00' not in fname, "NUL byte in string"
+
+class Entry(ExtRegistryEntry):
+ _about_ = check_str0
+
+ def compute_result_annotation(self, s_obj):
+ if not isinstance(s_obj, SomeString):
+ return s_obj
+ if not s_obj.no_NUL:
+ raise ValueError("Value is not no_NUL")
+
+ def specialize_call(self, hop):
+ pass
+
diff --git a/pypy/rpython/extfuncregistry.py b/pypy/rpython/extfuncregistry.py
--- a/pypy/rpython/extfuncregistry.py
+++ b/pypy/rpython/extfuncregistry.py
@@ -85,7 +85,8 @@
# llinterpreter
path_functions = [
- ('join', [str, str], str),
+ ('join', [ll_os.str0, ll_os.str0], ll_os.str0),
+ ('dirname', [ll_os.str0], ll_os.str0),
]
for name, args, res in path_functions:
diff --git a/pypy/rpython/lltypesystem/rffi.py
b/pypy/rpython/lltypesystem/rffi.py
--- a/pypy/rpython/lltypesystem/rffi.py
+++ b/pypy/rpython/lltypesystem/rffi.py
@@ -15,7 +15,7 @@
from pypy.translator.tool.cbuild import ExternalCompilationInfo
from pypy.rpython.annlowlevel import llhelper
from pypy.rlib.objectmodel import we_are_translated
-from pypy.rlib.rstring import StringBuilder, UnicodeBuilder
+from pypy.rlib.rstring import StringBuilder, UnicodeBuilder, assert_str0
from pypy.rlib import jit
from pypy.rpython.lltypesystem import llmemory
import os, sys
@@ -694,7 +694,7 @@
while cp[i] != lastchar:
b.append(cp[i])
i += 1
- return b.build()
+ return assert_str0(b.build())
# str -> char*
# Can't inline this because of the raw address manipulation.
@@ -798,7 +798,7 @@
while i < maxlen and cp[i] != lastchar:
b.append(cp[i])
i += 1
- return b.build()
+ return assert_str0(b.build())
# char* and size -> str (which can contain null bytes)
def charpsize2str(cp, size):
@@ -836,6 +836,7 @@
array[i] = str2charp(l[i])
array[len(l)] = lltype.nullptr(CCHARP.TO)
return array
+liststr2charpp._annenforceargs_ = [[annmodel.s_Str0]] # List of strings
def free_charpp(ref):
""" frees list of char**, NULL terminated
diff --git a/pypy/rpython/module/ll_os.py b/pypy/rpython/module/ll_os.py
--- a/pypy/rpython/module/ll_os.py
+++ b/pypy/rpython/module/ll_os.py
@@ -31,6 +31,12 @@
from pypy.rlib import rgc
from pypy.rlib.objectmodel import specialize
+str0 = SomeString()
+str0.no_NUL = True
+
+unicode0 = SomeUnicodeString()
+unicode0.no_NUL = True
+
def monkeypatch_rposix(posixfunc, unicodefunc, signature):
func_name = posixfunc.__name__
@@ -68,6 +74,7 @@
class StringTraits:
str = str
+ str0 = str0
CHAR = rffi.CHAR
CCHARP = rffi.CCHARP
charp2str = staticmethod(rffi.charp2str)
@@ -85,6 +92,7 @@
class UnicodeTraits:
str = unicode
+ str0 = unicode0
CHAR = rffi.WCHAR_T
CCHARP = rffi.CWCHARP
charp2str = staticmethod(rffi.wcharp2unicode)
@@ -301,7 +309,7 @@
rffi.free_charpp(l_args)
raise OSError(rposix.get_errno(), "execv failed")
- return extdef([str, [str]], s_ImpossibleValue, llimpl=execv_llimpl,
+ return extdef([str0, [str0]], s_ImpossibleValue, llimpl=execv_llimpl,
export_name="ll_os.ll_os_execv")
@@ -319,7 +327,8 @@
# appropriate
envstrs = []
for item in env.iteritems():
- envstrs.append("%s=%s" % item)
+ envstr = "%s=%s" % item
+ envstrs.append(envstr)
l_args = rffi.liststr2charpp(args)
l_env = rffi.liststr2charpp(envstrs)
@@ -332,7 +341,7 @@
raise OSError(rposix.get_errno(), "execve failed")
return extdef(
- [str, [str], {str: str}],
+ [str0, [str0], {str0: str0}],
s_ImpossibleValue,
llimpl=execve_llimpl,
export_name="ll_os.ll_os_execve")
@@ -353,7 +362,7 @@
raise OSError(rposix.get_errno(), "os_spawnv failed")
return rffi.cast(lltype.Signed, childpid)
- return extdef([int, str, [str]], int, llimpl=spawnv_llimpl,
+ return extdef([int, str0, [str0]], int, llimpl=spawnv_llimpl,
export_name="ll_os.ll_os_spawnv")
@registering_if(os, 'spawnve')
@@ -378,7 +387,7 @@
raise OSError(rposix.get_errno(), "os_spawnve failed")
return rffi.cast(lltype.Signed, childpid)
- return extdef([int, str, [str], {str: str}], int,
+ return extdef([int, str0, [str0], {str0: str0}], int,
llimpl=spawnve_llimpl,
export_name="ll_os.ll_os_spawnve")
@@ -816,7 +825,7 @@
def os_open_oofakeimpl(path, flags, mode):
return os.open(OOSupport.from_rstr(path), flags, mode)
- return extdef([traits.str, int, int], int, traits.ll_os_name('open'),
+ return extdef([str0, int, int], int, traits.ll_os_name('open'),
llimpl=os_open_llimpl, oofakeimpl=os_open_oofakeimpl)
@registering_if(os, 'getloadavg')
@@ -1174,8 +1183,8 @@
raise OSError(error, "os_readdir failed")
return result
- return extdef([traits.str], # a single argument which is a str
- [traits.str], # returns a list of strings
+ return extdef([traits.str0], # a single argument which is a str
+ [traits.str0], # returns a list of strings
traits.ll_os_name('listdir'),
llimpl=os_listdir_llimpl)
@@ -1241,7 +1250,7 @@
if res == -1:
raise OSError(rposix.get_errno(), "os_chown failed")
- return extdef([str, int, int], None, "ll_os.ll_os_chown",
+ return extdef([str0, int, int], None, "ll_os.ll_os_chown",
llimpl=os_chown_llimpl)
@registering_if(os, 'lchown')
@@ -1254,7 +1263,7 @@
if res == -1:
raise OSError(rposix.get_errno(), "os_lchown failed")
- return extdef([str, int, int], None, "ll_os.ll_os_lchown",
+ return extdef([str0, int, int], None, "ll_os.ll_os_lchown",
llimpl=os_lchown_llimpl)
@registering_if(os, 'readlink')
@@ -1283,12 +1292,11 @@
lltype.free(buf, flavor='raw')
bufsize *= 4
# convert the result to a string
- l = [buf[i] for i in range(res)]
- result = ''.join(l)
+ result = rffi.charp2strn(buf, res)
lltype.free(buf, flavor='raw')
return result
- return extdef([str], str,
+ return extdef([str0], str0,
"ll_os.ll_os_readlink",
llimpl=os_readlink_llimpl)
@@ -1424,7 +1432,7 @@
if res < 0:
raise OSError(rposix.get_errno(), "os_mkdir failed")
- return extdef([traits.str, int], s_None, llimpl=os_mkdir_llimpl,
+ return extdef([traits.str0, int], s_None, llimpl=os_mkdir_llimpl,
export_name=traits.ll_os_name('mkdir'))
@registering_str_unicode(os.rmdir)
@@ -1454,7 +1462,7 @@
from pypy.rpython.module.ll_win32file import make_chmod_impl
chmod_llimpl = make_chmod_impl(traits)
- return extdef([traits.str, int], s_None, llimpl=chmod_llimpl,
+ return extdef([traits.str0, int], s_None, llimpl=chmod_llimpl,
export_name=traits.ll_os_name('chmod'))
@registering_str_unicode(os.rename)
@@ -1476,7 +1484,7 @@
if not win32traits.MoveFile(oldpath, newpath):
raise rwin32.lastWindowsError()
- return extdef([traits.str, traits.str], s_None, llimpl=rename_llimpl,
+ return extdef([traits.str0, traits.str0], s_None, llimpl=rename_llimpl,
export_name=traits.ll_os_name('rename'))
@registering_str_unicode(getattr(os, 'mkfifo', None))
@@ -1489,7 +1497,7 @@
if res < 0:
raise OSError(rposix.get_errno(), "os_mkfifo failed")
- return extdef([traits.str, int], s_None, llimpl=mkfifo_llimpl,
+ return extdef([traits.str0, int], s_None, llimpl=mkfifo_llimpl,
export_name=traits.ll_os_name('mkfifo'))
@registering_str_unicode(getattr(os, 'mknod', None))
@@ -1503,7 +1511,7 @@
if res < 0:
raise OSError(rposix.get_errno(), "os_mknod failed")
- return extdef([traits.str, int, int], s_None, llimpl=mknod_llimpl,
+ return extdef([traits.str0, int, int], s_None, llimpl=mknod_llimpl,
export_name=traits.ll_os_name('mknod'))
@registering(os.umask)
@@ -1555,7 +1563,7 @@
if res < 0:
raise OSError(rposix.get_errno(), "os_link failed")
- return extdef([str, str], s_None, llimpl=link_llimpl,
+ return extdef([str0, str0], s_None, llimpl=link_llimpl,
export_name="ll_os.ll_os_link")
@registering_if(os, 'symlink')
@@ -1568,7 +1576,7 @@
if res < 0:
raise OSError(rposix.get_errno(), "os_symlink failed")
- return extdef([str, str], s_None, llimpl=symlink_llimpl,
+ return extdef([str0, str0], s_None, llimpl=symlink_llimpl,
export_name="ll_os.ll_os_symlink")
@registering_if(os, 'fork')
diff --git a/pypy/rpython/module/ll_os_stat.py
b/pypy/rpython/module/ll_os_stat.py
--- a/pypy/rpython/module/ll_os_stat.py
+++ b/pypy/rpython/module/ll_os_stat.py
@@ -236,7 +236,7 @@
def register_stat_variant(name, traits):
if name != 'fstat':
arg_is_path = True
- s_arg = traits.str
+ s_arg = traits.str0
ARG1 = traits.CCHARP
else:
arg_is_path = False
@@ -251,8 +251,6 @@
[s_arg], s_StatResult, traits.ll_os_name(name),
llimpl=posix_stat_llimpl)
- assert traits.str is str
-
if sys.platform.startswith('linux'):
# because we always use _FILE_OFFSET_BITS 64 - this helps things work
that are not a c compiler
_functions = {'stat': 'stat64',
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit