Author: Armin Rigo <[email protected]>
Branch:
Changeset: r97018:19e211d4c76b
Date: 2019-07-24 11:53 +0200
http://bitbucket.org/pypy/pypy/changeset/19e211d4c76b/
Log: Change the API of rffi.get_nonmovingbuffer(), now renamed to
rffi.get_nonmovingbuffer_ll(), because it is not safe: it needs to
return the real llstr object on which it operated, and this is the
object that needs to be passed to free_nonmovingbuffer_ll().
Otherwise, it's possible to come up with rpython code that would,
say, convert a char to a string once for get() and once for free(),
which defeats pinning.
diff --git a/pypy/module/_cffi_backend/ctypefunc.py
b/pypy/module/_cffi_backend/ctypefunc.py
--- a/pypy/module/_cffi_backend/ctypefunc.py
+++ b/pypy/module/_cffi_backend/ctypefunc.py
@@ -10,6 +10,7 @@
from rpython.rlib.objectmodel import we_are_translated, instantiate
from rpython.rlib.objectmodel import keepalive_until_here
from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+from rpython.rtyper.annlowlevel import llstr
from pypy.interpreter.error import OperationError, oefmt
from pypy.module import _cffi_backend
@@ -163,9 +164,9 @@
cif_descr = self.cif_descr # 'self' should have been promoted here
size = cif_descr.exchange_size
mustfree_max_plus_1 = 0
+ keepalives = [llstr(None)] * len(args_w) # llstrings
buffer = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw')
try:
- keepalives = [None] * len(args_w) # None or strings
for i in range(len(args_w)):
data = rffi.ptradd(buffer, cif_descr.exchange_args[i])
w_obj = args_w[i]
@@ -191,9 +192,10 @@
if flag == 1:
lltype.free(raw_cdata, flavor='raw')
elif flag >= 4:
- value = keepalives[i]
- assert value is not None
- rffi.free_nonmovingbuffer(value, raw_cdata, chr(flag))
+ llobj = keepalives[i]
+ assert llobj # not NULL
+ rffi.free_nonmovingbuffer_ll(raw_cdata,
+ llobj, chr(flag))
lltype.free(buffer, flavor='raw')
keepalive_until_here(args_w)
return w_res
diff --git a/pypy/module/_cffi_backend/ctypeptr.py
b/pypy/module/_cffi_backend/ctypeptr.py
--- a/pypy/module/_cffi_backend/ctypeptr.py
+++ b/pypy/module/_cffi_backend/ctypeptr.py
@@ -303,16 +303,8 @@
else:
return lltype.nullptr(rffi.CCHARP.TO)
- def _prepare_pointer_call_argument(self, w_init, cdata, keepalives, i):
+ def _prepare_pointer_call_argument(self, w_init, cdata):
space = self.space
- if self.accept_str and space.isinstance_w(w_init, space.w_bytes):
- # special case to optimize strings passed to a "char *" argument
- value = space.bytes_w(w_init)
- if isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveBool):
- self._must_be_string_of_zero_or_one(value)
- keepalives[i] = value
- return misc.write_string_as_charp(cdata, value)
- #
if (space.isinstance_w(w_init, space.w_list) or
space.isinstance_w(w_init, space.w_tuple)):
length = space.int_w(space.len(w_init))
@@ -358,14 +350,27 @@
return 1
def convert_argument_from_object(self, cdata, w_ob, keepalives, i):
+ # writes the pointer to cdata[0], writes the must-free flag in
+ # the byte just before cdata[0], and returns True if something
+ # must be done later to free.
from pypy.module._cffi_backend.ctypefunc import set_mustfree_flag
- result = (not isinstance(w_ob, cdataobj.W_CData) and
- self._prepare_pointer_call_argument(w_ob, cdata,
- keepalives, i))
+ if isinstance(w_ob, cdataobj.W_CData):
+ result = 0
+ else:
+ space = self.space
+ if self.accept_str and space.isinstance_w(w_ob, space.w_bytes):
+ # special case to optimize strings passed to a "char *"
argument
+ value = space.bytes_w(w_ob)
+ if isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveBool):
+ self._must_be_string_of_zero_or_one(value)
+ keepalives[i] = misc.write_string_as_charp(cdata, value)
+ return True
+ result = self._prepare_pointer_call_argument(w_ob, cdata)
+
if result == 0:
self.convert_from_object(cdata, w_ob)
set_mustfree_flag(cdata, result)
- return result
+ return result == 1 # 0 or 2 => False, nothing to do later
def getcfield(self, attr):
from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion
diff --git a/pypy/module/_cffi_backend/misc.py
b/pypy/module/_cffi_backend/misc.py
--- a/pypy/module/_cffi_backend/misc.py
+++ b/pypy/module/_cffi_backend/misc.py
@@ -111,11 +111,13 @@
def write_raw_longdouble_data(target, source):
rffi.cast(rffi.LONGDOUBLEP, target)[0] = source
[email protected]_look_inside # lets get_nonmovingbuffer_final_null be inlined
[email protected]_look_inside # lets get_nonmovingbuffer_ll_final_null be inlined
def write_string_as_charp(target, string):
- buf, buf_flag = rffi.get_nonmovingbuffer_final_null(string)
+ from pypy.module._cffi_backend.ctypefunc import set_mustfree_flag
+ buf, llobj, buf_flag = rffi.get_nonmovingbuffer_ll_final_null(string)
+ set_mustfree_flag(target, ord(buf_flag)) # 4, 5 or 6
rffi.cast(rffi.CCHARPP, target)[0] = buf
- return ord(buf_flag) # 4, 5 or 6
+ return llobj
# ____________________________________________________________
diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py
--- a/pypy/module/_ssl/interp_ssl.py
+++ b/pypy/module/_ssl/interp_ssl.py
@@ -140,7 +140,7 @@
def __init__(self, ctx, protos):
self.protos = protos
- self.buf, self.bufflag = rffi.get_nonmovingbuffer(protos)
+ self.buf, self.llobj, self.bufflag =
rffi.get_nonmovingbuffer_ll(protos)
NPN_STORAGE.set(rffi.cast(lltype.Unsigned, self.buf), self)
# set both server and client callbacks, because the context
@@ -151,8 +151,8 @@
ctx, self.selectNPN_cb, self.buf)
def __del__(self):
- rffi.free_nonmovingbuffer(
- self.protos, self.buf, self.bufflag)
+ rffi.free_nonmovingbuffer_ll(
+ self.buf, self.llobj, self.bufflag)
@staticmethod
def advertiseNPN_cb(s, data_ptr, len_ptr, args):
@@ -186,7 +186,7 @@
def __init__(self, ctx, protos):
self.protos = protos
- self.buf, self.bufflag = rffi.get_nonmovingbuffer(protos)
+ self.buf, self.llobj, self.bufflag =
rffi.get_nonmovingbuffer_ll(protos)
ALPN_STORAGE.set(rffi.cast(lltype.Unsigned, self.buf), self)
with rffi.scoped_str2charp(protos) as protos_buf:
@@ -197,8 +197,8 @@
ctx, self.selectALPN_cb, self.buf)
def __del__(self):
- rffi.free_nonmovingbuffer(
- self.protos, self.buf, self.bufflag)
+ rffi.free_nonmovingbuffer_ll(
+ self.buf, self.llobj, self.bufflag)
@staticmethod
def selectALPN_cb(s, out_ptr, outlen_ptr, client, client_len, args):
diff --git a/rpython/rlib/rdtoa.py b/rpython/rlib/rdtoa.py
--- a/rpython/rlib/rdtoa.py
+++ b/rpython/rlib/rdtoa.py
@@ -60,14 +60,14 @@
try:
# note: don't use the class scoped_view_charp here, it
# break some tests because this function is used by the GC
- ll_input, flag = rffi.get_nonmovingbuffer_final_null(input)
+ ll_input, llobj, flag = rffi.get_nonmovingbuffer_ll_final_null(input)
try:
result = dg_strtod(ll_input, end_ptr)
endpos = (rffi.cast(lltype.Signed, end_ptr[0]) -
rffi.cast(lltype.Signed, ll_input))
finally:
- rffi.free_nonmovingbuffer(input, ll_input, flag)
+ rffi.free_nonmovingbuffer_ll(ll_input, llobj, flag)
finally:
lltype.free(end_ptr, flavor='raw')
diff --git a/rpython/rtyper/lltypesystem/rffi.py
b/rpython/rtyper/lltypesystem/rffi.py
--- a/rpython/rtyper/lltypesystem/rffi.py
+++ b/rpython/rtyper/lltypesystem/rffi.py
@@ -7,7 +7,7 @@
from rpython.rtyper.lltypesystem.llmemory import itemoffsetof
from rpython.rtyper.llannotation import lltype_to_annotation
from rpython.tool.sourcetools import func_with_new_name
-from rpython.rlib.objectmodel import Symbolic, specialize
+from rpython.rlib.objectmodel import Symbolic, specialize, not_rpython
from rpython.rlib.objectmodel import keepalive_until_here, enforceargs
from rpython.rlib import rarithmetic, rgc
from rpython.rtyper.extregistry import ExtRegistryEntry
@@ -269,10 +269,11 @@
arg = args[i]
if TARGET == CCHARP or TARGET is VOIDP:
if arg is None:
+ from rpython.rtyper.annlowlevel import llstr
arg = lltype.nullptr(CCHARP.TO) # None => (char*)NULL
- to_free = to_free + (arg, '\x04')
+ to_free = to_free + (arg, llstr(None), '\x04')
elif isinstance(arg, str):
- tup = get_nonmovingbuffer_final_null(arg)
+ tup = get_nonmovingbuffer_ll_final_null(arg)
to_free = to_free + tup
arg = tup[0]
elif isinstance(arg, unicode):
@@ -306,10 +307,10 @@
arg = args[i]
if TARGET == CCHARP or TARGET is VOIDP:
if arg is None:
- to_free = to_free[2:]
+ to_free = to_free[3:]
elif isinstance(arg, str):
- free_nonmovingbuffer(arg, to_free[0], to_free[1])
- to_free = to_free[2:]
+ free_nonmovingbuffer_ll(to_free[0], to_free[1], to_free[2])
+ to_free = to_free[3:]
elif TARGET == CWCHARP:
if arg is None:
to_free = to_free[1:]
@@ -835,84 +836,85 @@
return assert_str0(charpsize2str(cp, size))
charp2str._annenforceargs_ = [lltype.SomePtr(TYPEP)]
- # str -> char*, flag
+ # str -> (buf, llobj, flag)
# Can't inline this because of the raw address manipulation.
@jit.dont_look_inside
- def get_nonmovingbuffer(data):
+ def get_nonmovingbuffer_ll(data):
"""
Either returns a non-moving copy or performs neccessary pointer
arithmetic to return a pointer to the characters of a string if the
string is already nonmovable or could be pinned. Must be followed by a
- free_nonmovingbuffer call.
+ free_nonmovingbuffer_ll call.
- Also returns a char:
- * \4: no pinning, returned pointer is inside 'data' which is
nonmovable
- * \5: 'data' was pinned, returned pointer is inside
+ The return type is a 3-tuple containing the "char *" result,
+ a pointer to the low-level string object, and a flag as a char:
+
+ * \4: no pinning, returned pointer is inside nonmovable 'llobj'
+ * \5: 'llobj' was pinned, returned pointer is inside
* \6: pinning failed, returned pointer is raw malloced
For strings (not unicodes), the len()th character of the resulting
raw buffer is available, but not initialized. Use
- get_nonmovingbuffer_final_null() instead of get_nonmovingbuffer()
+ get_nonmovingbuffer_ll_final_null() instead of get_nonmovingbuffer_ll()
to get a regular null-terminated "char *".
"""
- lldata = llstrtype(data)
+ llobj = llstrtype(data)
count = len(data)
if rgc.must_split_gc_address_space():
flag = '\x06' # always make a copy in this case
- elif we_are_translated_to_c() and not rgc.can_move(data):
+ elif we_are_translated_to_c() and not rgc.can_move(llobj):
flag = '\x04' # no copy needed
else:
- if we_are_translated_to_c() and rgc.pin(data):
+ if we_are_translated_to_c() and rgc.pin(llobj):
flag = '\x05' # successfully pinned
else:
flag = '\x06' # must still make a copy
if flag == '\x06':
buf = lltype.malloc(TYPEP.TO, count + (TYPEP is CCHARP),
flavor='raw')
- copy_string_to_raw(lldata, buf, 0, count)
- return buf, '\x06'
+ copy_string_to_raw(llobj, buf, 0, count)
+ return buf, llobj, '\x06'
# ^^^ raw malloc used to get a nonmovable copy
#
# following code is executed after we're translated to C, if:
# - rgc.can_move(data) and rgc.pin(data) both returned true
# - rgc.can_move(data) returned false
- data_start = cast_ptr_to_adr(lldata) + \
+ data_start = cast_ptr_to_adr(llobj) + \
offsetof(STRTYPE, 'chars') + itemoffsetof(STRTYPE.chars, 0)
- return cast(TYPEP, data_start), flag
+ return cast(TYPEP, data_start), llobj, flag
# ^^^ already nonmovable. Therefore it's not raw allocated nor
# pinned.
- get_nonmovingbuffer._always_inline_ = 'try' # get rid of the returned tuple
- get_nonmovingbuffer._annenforceargs_ = [strtype]
+ get_nonmovingbuffer_ll._always_inline_ = 'try' # get rid of the returned
tuple
+ get_nonmovingbuffer_ll._annenforceargs_ = [strtype]
@jit.dont_look_inside
- def get_nonmovingbuffer_final_null(data):
- tup = get_nonmovingbuffer(data)
- buf, flag = tup
+ def get_nonmovingbuffer_ll_final_null(data):
+ tup = get_nonmovingbuffer_ll(data)
+ buf = tup[0]
buf[len(data)] = lastchar
return tup
- get_nonmovingbuffer_final_null._always_inline_ = 'try'
- get_nonmovingbuffer_final_null._annenforceargs_ = [strtype]
+ get_nonmovingbuffer_ll_final_null._always_inline_ = 'try'
+ get_nonmovingbuffer_ll_final_null._annenforceargs_ = [strtype]
- # (str, char*, char) -> None
+ # args-from-tuple-returned-by-get_nonmoving_buffer() -> None
# Can't inline this because of the raw address manipulation.
@jit.dont_look_inside
- def free_nonmovingbuffer(data, buf, flag):
+ def free_nonmovingbuffer_ll(buf, llobj, flag):
"""
- Keep 'data' alive and unpin it if it was pinned (flag==\5).
+ Keep 'llobj' alive and unpin it if it was pinned (flag==\5).
Otherwise free the non-moving copy (flag==\6).
"""
if flag == '\x05':
- rgc.unpin(data)
+ rgc.unpin(llobj)
if flag == '\x06':
lltype.free(buf, flavor='raw')
# if flag == '\x04': data was already nonmovable,
# we have nothing to clean up
- keepalive_until_here(data)
- free_nonmovingbuffer._annenforceargs_ = [strtype, None, None]
+ keepalive_until_here(llobj)
# int -> (char*, str, int)
# Can't inline this because of the raw address manipulation.
@@ -1002,24 +1004,41 @@
charpsize2str._annenforceargs_ = [None, int]
return (str2charp, free_charp, charp2str,
- get_nonmovingbuffer, free_nonmovingbuffer,
- get_nonmovingbuffer_final_null,
+ get_nonmovingbuffer_ll, free_nonmovingbuffer_ll,
+ get_nonmovingbuffer_ll_final_null,
alloc_buffer, str_from_buffer, keep_buffer_alive_until_here,
charp2strn, charpsize2str, str2chararray, str2rawmem,
)
(str2charp, free_charp, charp2str,
- get_nonmovingbuffer, free_nonmovingbuffer, get_nonmovingbuffer_final_null,
+ get_nonmovingbuffer_ll, free_nonmovingbuffer_ll,
+ get_nonmovingbuffer_ll_final_null,
alloc_buffer, str_from_buffer, keep_buffer_alive_until_here,
charp2strn, charpsize2str, str2chararray, str2rawmem,
) = make_string_mappings(str)
(unicode2wcharp, free_wcharp, wcharp2unicode,
- get_nonmoving_unicodebuffer, free_nonmoving_unicodebuffer, __not_usable,
+ get_nonmoving_unicodebuffer_ll, free_nonmoving_unicodebuffer_ll, __not_usable,
alloc_unicodebuffer, unicode_from_buffer, keep_unicodebuffer_alive_until_here,
wcharp2unicoden, wcharpsize2unicode, unicode2wchararray, unicode2rawmem,
) = make_string_mappings(unicode)
+
+@not_rpython
+def _deprecated_get_nonmovingbuffer(*args):
+ raise Exception(
+"""The function rffi.get_nonmovingbuffer() has been removed because
+it was unsafe. Use rffi.get_nonmovingbuffer_ll() instead. It returns
+a 3-tuple instead of a 2-tuple, and all three arguments must be passed
+to rffi.free_nonmovingbuffer_ll() (instead of the original string and the
+two tuple items). Or else, use a high-level API like
+'with rffi.scoped_nonmovingbuffer()'.""")
+
+get_nonmovingbuffer = _deprecated_get_nonmovingbuffer
+get_nonmovingbuffer_final_null = _deprecated_get_nonmovingbuffer
+free_nonmovingbuffer = _deprecated_get_nonmovingbuffer
+
+
def wcharpsize2utf8(w, size):
""" Helper to convert WCHARP pointer to utf8 in one go.
Equivalent to wcharpsize2unicode().encode("utf8")
@@ -1315,14 +1334,13 @@
class scoped_nonmovingbuffer:
def __init__(self, data):
- self.data = data
+ self.buf, self.llobj, self.flag = get_nonmovingbuffer_ll(data)
__init__._annenforceargs_ = [None, annmodel.SomeString(can_be_None=False)]
def __enter__(self):
- self.buf, self.flag = get_nonmovingbuffer(self.data)
return self.buf
def __exit__(self, *args):
- free_nonmovingbuffer(self.data, self.buf, self.flag)
+ free_nonmovingbuffer_ll(self.buf, self.llobj, self.flag)
__init__._always_inline_ = 'try'
__enter__._always_inline_ = 'try'
__exit__._always_inline_ = 'try'
@@ -1334,13 +1352,13 @@
content of the 'char[]' array will not be modified.
"""
def __init__(self, data):
- self.data = data
+ self.buf, self.llobj, self.flag = get_nonmovingbuffer_ll_final_null(
+ data)
__init__._annenforceargs_ = [None, annmodel.SomeString(can_be_None=False)]
def __enter__(self):
- self.buf, self.flag = get_nonmovingbuffer_final_null(self.data)
return self.buf
def __exit__(self, *args):
- free_nonmovingbuffer(self.data, self.buf, self.flag)
+ free_nonmovingbuffer_ll(self.buf, self.llobj, self.flag)
__init__._always_inline_ = 'try'
__enter__._always_inline_ = 'try'
__exit__._always_inline_ = 'try'
diff --git a/rpython/rtyper/lltypesystem/test/test_rffi.py
b/rpython/rtyper/lltypesystem/test/test_rffi.py
--- a/rpython/rtyper/lltypesystem/test/test_rffi.py
+++ b/rpython/rtyper/lltypesystem/test/test_rffi.py
@@ -535,7 +535,7 @@
def test_nonmovingbuffer(self):
d = 'some cool data that should not move'
def f():
- buf, flag = get_nonmovingbuffer(d)
+ buf, llobj, flag = get_nonmovingbuffer_ll(d)
try:
counter = 0
for i in range(len(d)):
@@ -543,7 +543,7 @@
counter += 1
return counter
finally:
- free_nonmovingbuffer(d, buf, flag)
+ free_nonmovingbuffer_ll(buf, llobj, flag)
assert f() == len(d)
fn = self.compile(f, [], gcpolicy='ref')
assert fn() == len(d)
@@ -553,13 +553,13 @@
def f():
counter = 0
for n in range(32):
- buf, flag = get_nonmovingbuffer(d)
+ buf, llobj, flag = get_nonmovingbuffer_ll(d)
try:
for i in range(len(d)):
if buf[i] == d[i]:
counter += 1
finally:
- free_nonmovingbuffer(d, buf, flag)
+ free_nonmovingbuffer_ll(buf, llobj, flag)
return counter
fn = self.compile(f, [], gcpolicy='semispace')
# The semispace gc uses raw_malloc for its internal data structs
@@ -574,13 +574,13 @@
def f():
counter = 0
for n in range(32):
- buf, flag = get_nonmovingbuffer(d)
+ buf, llobj, flag = get_nonmovingbuffer_ll(d)
try:
for i in range(len(d)):
if buf[i] == d[i]:
counter += 1
finally:
- free_nonmovingbuffer(d, buf, flag)
+ free_nonmovingbuffer_ll(buf, llobj, flag)
return counter
fn = self.compile(f, [], gcpolicy='incminimark')
# The incminimark gc uses raw_malloc for its internal data structs
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit