Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion Lib/test/test_string_literals.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@
assert ord(h) == 0x1d120
i = r'\U0001d120'
assert list(map(ord, i)) == [92, 85, 48, 48, 48, 49, 100, 49, 50, 48]
j = 'ä'
assert list(map(ord, j)) == [228]
k = '\ä'
assert list(map(ord, k)) == [92, 228]
"""


Expand All @@ -76,7 +80,7 @@ def test_template(self):
# Check that the template doesn't contain any non-printables
# except for \n.
for c in TEMPLATE:
assert c == '\n' or ' ' <= c <= '~', repr(c)
assert c == '\n' or ' ' <= c <= '~' or c == 'ä', repr(c)

def test_eval_str_normal(self):
self.assertEqual(eval(""" 'x' """), 'x')
Expand All @@ -89,6 +93,19 @@ def test_eval_str_normal(self):
self.assertEqual(eval(r""" '\U0001d120' """), chr(0x1d120))
self.assertEqual(eval(""" '\U0001d120' """), chr(0x1d120))

def test_eval_str_unicode(self):
for s in (
'ϼўТλФЙ',
'A͏B ﬖ̳AA̝',
'\U00100000\U0010ffff\U0010fffd',
'ä',
'\\ä',
"\\П",
"áàäéèęöő.\\n",
):
with self.subTest(s=s):
self.assertEqual(eval(f"{s!r}"), s)

def test_eval_str_incomplete(self):
self.assertRaises(SyntaxError, eval, r""" '\x' """)
self.assertRaises(SyntaxError, eval, r""" '\x0' """)
Expand Down
26 changes: 13 additions & 13 deletions Parser/string_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,22 +135,22 @@ static PyObject *
decode_unicode_with_escapes(Parser *parser, const char *s, size_t len, Token *t)
{
PyObject *v;
PyObject *u;
char *buf;
char *p;
const char *end;

/* check for integer overflow */
if (len > (size_t)PY_SSIZE_T_MAX / 6) {
if (len > ((size_t)PY_SSIZE_T_MAX - 1) / 6) {
return NULL;
}
/* "ä" (2 bytes) may become "\U000000E4" (10 bytes), or 1:5
"\ä" (3 bytes) may become "\u005c\U000000E4" (16 bytes), or ~1:6 */
u = PyBytes_FromStringAndSize((char *)NULL, (Py_ssize_t)len * 6);
if (u == NULL) {
/* "ä" (2 bytes) may become "\U000000E4" (10 bytes), or 1:5.
* "\ä" (3 bytes) may become "\u005c\U000000E4" (16 bytes), or ~1:6.
* Add +1 to allow writing a trailing null byte (for strcpy/sprintf). */
PyBytesWriter *writer = PyBytesWriter_Create((Py_ssize_t)len * 6 + 1);
if (writer == NULL) {
return NULL;
}
p = buf = PyBytes_AsString(u);
p = buf = PyBytesWriter_GetData(writer);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about just PyMem_Malloc()?

if (p == NULL) {
return NULL;
}
Expand All @@ -174,7 +174,7 @@ decode_unicode_with_escapes(Parser *parser, const char *s, size_t len, Token *t)
Py_ssize_t i;
w = decode_utf8(&s, end);
if (w == NULL) {
Py_DECREF(u);
PyBytesWriter_Discard(writer);
return NULL;
}
kind = PyUnicode_KIND(w);
Expand All @@ -186,7 +186,7 @@ decode_unicode_with_escapes(Parser *parser, const char *s, size_t len, Token *t)
p += 10;
}
/* Should be impossible to overflow */
assert(p - buf <= PyBytes_GET_SIZE(u));
assert(p - buf <= PyBytesWriter_GetSize(writer));
Py_DECREF(w);
}
else {
Expand All @@ -206,14 +206,14 @@ decode_unicode_with_escapes(Parser *parser, const char *s, size_t len, Token *t)
// when we are decoding the string but we preserve the line numbers.
if (v != NULL && first_invalid_escape_ptr != NULL && t != NULL) {
if (warn_invalid_escape_sequence(parser, s, first_invalid_escape_ptr, t) < 0) {
/* We have not decref u before because first_invalid_escape_ptr
points inside u. */
Py_XDECREF(u);
/* We have not discarded the writer before because
* first_invalid_escape_ptr points inside the writer buffer. */
PyBytesWriter_Discard(writer);
Py_DECREF(v);
return NULL;
}
}
Py_XDECREF(u);
PyBytesWriter_Discard(writer);
return v;
}

Expand Down
Loading