Skip to content

Commit 3c3f313

Browse files
committed
gh-153569: simplify tokenizer source storage
1 parent 3ca2859 commit 3c3f313

6 files changed

Lines changed: 86 additions & 235 deletions

File tree

Modules/_testinternalcapi/tokenizer.c

Lines changed: 9 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -12,64 +12,26 @@ check(int condition, const char *message)
1212
return -1;
1313
}
1414

15-
static int
16-
check_system_error(int failed, const char *message)
17-
{
18-
if (!failed || !PyErr_ExceptionMatches(PyExc_SystemError)) {
19-
PyErr_SetString(PyExc_AssertionError, message);
20-
return -1;
21-
}
22-
PyErr_Clear();
23-
return 0;
24-
}
25-
2615
static PyObject *
2716
test_tokenizer_source(PyObject *Py_UNUSED(module),
2817
PyObject *Py_UNUSED(args))
2918
{
30-
_PyTok_SourceText source;
31-
_PyTok_SourceInit(&source);
19+
_PyTok_SourceText source = {0};
3220

33-
if (check_system_error(
34-
_PyTok_SourceAppendLine(&source, "", 0, 0) < 0,
35-
"accepted empty source line") < 0 ||
36-
check_system_error(
37-
_PyTok_SourceAppendLine(&source, "a\nb\n", 4, 0) < 0,
38-
"accepted multiple source lines") < 0 ||
39-
check_system_error(
40-
_PyTok_SourceAppendLine(&source, "a", 1, 1) < 0,
41-
"accepted missing implicit newline") < 0 ||
42-
check(_PyTok_SourceAppendLine(
43-
&source, "alpha\n", 6, 0) == 0,
21+
if (check(_PyTok_SourceAppend(
22+
&source, "alpha\n", 6) == 0,
4423
"wrong first source offset") < 0 ||
45-
check(_PyTok_SourceAppendLine(
46-
&source, "\xce\xb2\n", 3, 1) == 6,
24+
check(_PyTok_SourceAppend(
25+
&source, "\xce\xb2\n", 3) == 6,
4726
"wrong second source offset") < 0 ||
48-
check(!_PyTok_SourceLineIsImplicit(&source, 1) &&
49-
_PyTok_SourceLineIsImplicit(&source, 2),
50-
"wrong implicit newline flags") < 0) {
51-
goto error;
52-
}
53-
54-
Py_ssize_t view_len;
55-
const char *view = _PyTok_SourceSpanView(
56-
&source, _PyTok_SpanFromBounds(6, 8), &view_len);
57-
if (check(view != NULL && view_len == 2 &&
58-
memcmp(view, "\xce\xb2", 2) == 0,
59-
"wrong source span view") < 0 ||
60-
check_system_error(
61-
_PyTok_SourceSpanView(
62-
&source, _PyTok_SpanFromBounds(0, source.len + 1),
63-
&view_len) == NULL,
64-
"accepted invalid source span") < 0) {
27+
check(source.len == 9 &&
28+
memcmp(source.bytes, "alpha\n\xce\xb2\n", 9) == 0,
29+
"wrong source contents") < 0) {
6530
goto error;
6631
}
6732

6833
_PyTok_SourceClear(&source);
69-
if (_PyTok_SourceAppendLine(&source, "tail", 4, 0) < 0 ||
70-
check_system_error(
71-
_PyTok_SourceAppendLine(&source, "x\n", 2, 0) < 0,
72-
"appended after unterminated source line") < 0) {
34+
if (_PyTok_SourceAppend(&source, "tail", 4) < 0) {
7335
goto error;
7436
}
7537

Parser/tokenizer/decoder.c

Lines changed: 25 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -297,51 +297,34 @@ static int
297297
store_prepared_source(struct tok_state *tok, const char *data, Py_ssize_t len,
298298
int preserve_crlf, int add_final_newline)
299299
{
300-
Py_ssize_t pos = 0;
301-
while (pos < len) {
302-
Py_ssize_t raw_line_len;
303-
if (preserve_crlf) {
304-
const char *newline = memchr(data + pos, '\n', len - pos);
305-
raw_line_len = newline == NULL
306-
? len - pos : newline - data - pos + 1;
307-
}
308-
else {
309-
raw_line_len = raw_line_length(data + pos, len - pos);
310-
}
311-
int terminated = preserve_crlf
312-
? data[pos + raw_line_len - 1] == '\n'
313-
: data[pos + raw_line_len - 1] == '\n' ||
314-
data[pos + raw_line_len - 1] == '\r';
315-
int add_newline = add_final_newline &&
316-
pos + raw_line_len == len && !terminated;
317-
int normalize = add_newline ||
318-
(!preserve_crlf &&
319-
memchr(data + pos, '\r', raw_line_len) != NULL);
320-
321-
const char *line = data + pos;
322-
Py_ssize_t line_len = raw_line_len;
323-
char *normalized = NULL;
324-
int implicit = 0;
325-
if (normalize) {
326-
normalized = _PyTok_NormalizeNewlines(
327-
line, line_len, preserve_crlf, add_newline,
328-
&line_len, &implicit);
329-
if (normalized == NULL) {
330-
tok->done = E_NOMEM;
331-
return -1;
332-
}
333-
line = normalized;
334-
}
335-
_PyTok_Off appended = _PyTok_SourceAppendLine(
336-
&tok->source, line, line_len, implicit);
337-
PyMem_Free(normalized);
338-
if (appended < 0) {
339-
tok->done = PyErr_ExceptionMatches(PyExc_MemoryError)
340-
? E_NOMEM : E_ERROR;
300+
int normalize = (!preserve_crlf && memchr(data, '\r', len) != NULL) ||
301+
(add_final_newline && len > 0 && data[len - 1] != '\n');
302+
char *normalized = NULL;
303+
int implicit = 0;
304+
if (normalize) {
305+
normalized = _PyTok_NormalizeNewlines(
306+
data, len, preserve_crlf, add_final_newline,
307+
&len, &implicit);
308+
if (normalized == NULL) {
309+
tok->done = E_NOMEM;
341310
return -1;
342311
}
343-
pos += raw_line_len;
312+
data = normalized;
313+
}
314+
if (normalized != NULL) {
315+
assert(tok->source.bytes == NULL);
316+
tok->source = (_PyTok_SourceText){
317+
.bytes = normalized,
318+
.len = len,
319+
.cap = len + 1,
320+
};
321+
}
322+
else if (_PyTok_SourceAppend(&tok->source, data, len) < 0) {
323+
tok->done = PyErr_ExceptionMatches(PyExc_MemoryError)
324+
? E_NOMEM : E_ERROR;
325+
return -1;
344326
}
327+
tok->reader->prepared_implicit_newline = implicit;
345328
return 0;
346329
}
347330

Parser/tokenizer/reader.c

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -191,20 +191,18 @@ chunk_is_line(const _PyTok_Chunk *chunk)
191191
static _PyTok_ReadResult
192192
next_prepared(struct tok_state *tok, _PyTok_Chunk *chunk)
193193
{
194-
int lineno = tok->lineno + 1;
195-
if (lineno > tok->source.nlines) {
194+
const char *source_end = _PyTok_SourceData(&tok->source) + tok->source.len;
195+
if (tok->inp == source_end) {
196196
return _PYTOK_READ_EOF;
197197
}
198198
const char *start = tok->inp;
199-
const char *newline = memchr(
200-
start, '\n', tok->source.bytes + tok->source.len - start);
201-
_PyTok_Off end = newline != NULL
202-
? newline - tok->source.bytes + 1 : tok->source.len;
199+
const char *newline = memchr(start, '\n', source_end - start);
200+
const char *end = newline != NULL ? newline + 1 : source_end;
203201
chunk->data = (char *)start;
204-
chunk->len = tok->source.bytes + end - start;
202+
chunk->len = end - start;
205203
chunk->ownership = _PYTOK_CHUNK_BORROWED;
206-
chunk->implicit_newline = _PyTok_SourceLineIsImplicit(
207-
&tok->source, lineno);
204+
chunk->implicit_newline = end == source_end &&
205+
tok->reader->prepared_implicit_newline;
208206
return _PYTOK_READ_LINE;
209207
}
210208

@@ -260,7 +258,6 @@ initialize_file(struct tok_state *tok)
260258
if (result != _PYTOK_READ_LINE) {
261259
return -1;
262260
}
263-
reader->prefetched_count = 1;
264261
Py_ssize_t bom_len;
265262
_PyTok_EncodingResult detection = _PyTok_DetectEncoding(
266263
tok, &reader->prefetched_lines[0], NULL, 0, &bom_len);
@@ -278,16 +275,13 @@ initialize_file(struct tok_state *tok)
278275
reader->prefetched_lines[0].data = first;
279276
reader->prefetched_lines[0].ownership = _PYTOK_CHUNK_PYMEM;
280277
result = read_file_line(tok, &reader->prefetched_lines[1]);
281-
if (result == _PYTOK_READ_LINE) {
282-
reader->prefetched_count = 2;
283-
}
284-
else if (result == _PYTOK_READ_EOF) {
278+
if (result == _PYTOK_READ_EOF) {
285279
reader->file_eof = 1;
286280
}
287-
else {
281+
else if (result != _PYTOK_READ_LINE) {
288282
return -1;
289283
}
290-
_PyTok_Chunk *second = reader->prefetched_count == 2
284+
_PyTok_Chunk *second = reader->prefetched_lines[1].data != NULL
291285
? &reader->prefetched_lines[1] : NULL;
292286
detection = _PyTok_DetectEncoding(
293287
tok, &reader->prefetched_lines[0], second, 1, &bom_len);
@@ -357,10 +351,13 @@ next_file(struct tok_state *tok, _PyTok_Chunk *chunk)
357351
return _PYTOK_READ_LINE;
358352
}
359353
_PyTok_Chunk input = {0};
360-
if (reader->prefetched_index < reader->prefetched_count) {
361-
input = reader->prefetched_lines[reader->prefetched_index];
362-
reader->prefetched_lines[reader->prefetched_index++] =
363-
(_PyTok_Chunk){0};
354+
if (reader->prefetched_lines[0].data != NULL) {
355+
input = reader->prefetched_lines[0];
356+
reader->prefetched_lines[0] = (_PyTok_Chunk){0};
357+
}
358+
else if (reader->prefetched_lines[1].data != NULL) {
359+
input = reader->prefetched_lines[1];
360+
reader->prefetched_lines[1] = (_PyTok_Chunk){0};
364361
}
365362
else if (!reader->file_eof) {
366363
_PyTok_ReadResult result = read_file_line(tok, &input);
@@ -556,16 +553,18 @@ next_interactive(struct tok_state *tok, _PyTok_Chunk *chunk)
556553
_PyTok_ChunkClear(&decoded);
557554
return _PYTOK_READ_ERROR;
558555
}
556+
int implicit_newline;
559557
chunk->data = _PyTok_NormalizeNewlines(
560558
decoded.data, decoded.len, 0, 0,
561-
&chunk->len, &chunk->implicit_newline);
559+
&chunk->len, &implicit_newline);
562560
_PyTok_ChunkClear(&decoded);
563561
if (chunk->data == NULL) {
564562
PyErr_NoMemory();
565563
tok->done = E_NOMEM;
566564
return _PYTOK_READ_ERROR;
567565
}
568566
chunk->ownership = _PYTOK_CHUNK_PYMEM;
567+
chunk->implicit_newline = implicit_newline;
569568
return _PYTOK_READ_LINE;
570569
}
571570

@@ -648,6 +647,13 @@ _PyTok_ReaderUnderflow(struct tok_state *tok)
648647
}
649648
return 0;
650649
}
650+
if (tok->lineno == INT_MAX) {
651+
_PyTok_ChunkClear(&chunk);
652+
PyErr_SetString(PyExc_OverflowError,
653+
"too many tokenizer source lines");
654+
tok->done = E_ERROR;
655+
return 0;
656+
}
651657

652658
Py_ssize_t scan_len = chunk.len;
653659
if (kind == _PYTOK_READER_INTERACTIVE &&
@@ -678,9 +684,8 @@ _PyTok_ReaderUnderflow(struct tok_state *tok)
678684
if (!reset_buffer) {
679685
offsets = save_buffer_offsets(tok, tok->source.bytes);
680686
}
681-
_PyTok_Off source_start = _PyTok_SourceAppendLine(
682-
&tok->source, chunk.data, chunk.len,
683-
chunk.implicit_newline);
687+
_PyTok_Off source_start = _PyTok_SourceAppend(
688+
&tok->source, chunk.data, chunk.len);
684689
if (source_start < 0) {
685690
_PyTok_ChunkClear(&chunk);
686691
tok->done = PyErr_ExceptionMatches(PyExc_MemoryError)

Parser/tokenizer/reader_internal.h

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,13 @@ typedef enum {
3232

3333
typedef struct {
3434
char *data;
35-
Py_ssize_t len;
36-
int implicit_newline;
3735
PyObject *owner;
36+
Py_ssize_t len;
3837
_PyTok_ChunkOwnership ownership;
38+
unsigned char implicit_newline;
3939
} _PyTok_Chunk;
4040

4141
typedef struct _PyTok_Reader {
42-
_PyTok_ReaderKind kind;
4342
PyObject *readline;
4443
PyObject *decoder;
4544
const char *prompt;
@@ -50,19 +49,18 @@ typedef struct _PyTok_Reader {
5049
char *file_buffer;
5150
Py_ssize_t file_buffer_cap;
5251
_PyTok_Chunk prefetched_lines[2];
53-
int prefetched_index;
54-
int prefetched_count;
5552

5653
char *decoded;
5754
Py_ssize_t decoded_pos;
5855
Py_ssize_t decoded_len;
5956
Py_ssize_t decoded_cap;
60-
int decoded_tail_is_implicit;
61-
62-
int file_initialized;
63-
int file_eof;
64-
int decoder_finalized;
65-
int stop_interactive;
57+
_PyTok_ReaderKind kind;
58+
unsigned char decoded_tail_is_implicit;
59+
unsigned char file_initialized;
60+
unsigned char file_eof;
61+
unsigned char decoder_finalized;
62+
unsigned char stop_interactive;
63+
unsigned char prepared_implicit_newline;
6664
} _PyTok_Reader;
6765

6866
struct tok_state;

0 commit comments

Comments
 (0)