mirror of
https://github.com/python/cpython.git
synced 2026-10-28 10:25:05 +08:00
* gh-159465: Improve incomplete input detection * gh-159465: Track newline effects and preserve parser layout * gh-159465: Share incomplete-input error cleanup
155 lines
4.2 KiB
C
155 lines
4.2 KiB
C
#include "Python.h"
|
|
#include "pycore_pymem.h" // PYMEM_DEADBYTE
|
|
|
|
#include "source.h"
|
|
|
|
#define SOURCE_BUFFER_GROWTH_FACTOR 2
|
|
|
|
void
|
|
_PyTok_SourceInit(_PyTok_SourceText *source)
|
|
{
|
|
*source = (_PyTok_SourceText){0};
|
|
}
|
|
|
|
void
|
|
_PyTok_SourceClear(_PyTok_SourceText *source)
|
|
{
|
|
PyMem_Free(source->bytes);
|
|
_PyTok_SourceInit(source);
|
|
}
|
|
|
|
void
|
|
_PyTok_SourceDiscard(_PyTok_SourceText *source)
|
|
{
|
|
assert(source->base_offset <= PY_SSIZE_T_MAX - source->len);
|
|
source->base_offset += source->len;
|
|
source->len = 0;
|
|
if (source->bytes != NULL) {
|
|
source->bytes[0] = '\0';
|
|
}
|
|
source->nlines = 0;
|
|
}
|
|
|
|
static int
|
|
reserve_bytes(_PyTok_SourceText *source, Py_ssize_t needed)
|
|
{
|
|
if (needed <= source->cap) {
|
|
return 0;
|
|
}
|
|
Py_ssize_t cap = source->cap > 0 ? source->cap : BUFSIZ;
|
|
while (cap < needed) {
|
|
if (cap > PY_SSIZE_T_MAX / SOURCE_BUFFER_GROWTH_FACTOR) {
|
|
cap = needed;
|
|
break;
|
|
}
|
|
cap *= SOURCE_BUFFER_GROWTH_FACTOR;
|
|
}
|
|
char *bytes;
|
|
#ifdef Py_DEBUG
|
|
/* Moving on growth makes stale interior pointers fail in debug builds. */
|
|
bytes = PyMem_Malloc(cap);
|
|
if (bytes != NULL && source->len > 0) {
|
|
memcpy(bytes, source->bytes, source->len);
|
|
}
|
|
#else
|
|
bytes = PyMem_Realloc(source->bytes, cap);
|
|
#endif
|
|
if (bytes == NULL) {
|
|
PyErr_NoMemory();
|
|
return -1;
|
|
}
|
|
#ifdef Py_DEBUG
|
|
if (source->bytes != NULL) {
|
|
memset(source->bytes, PYMEM_DEADBYTE, source->cap);
|
|
PyMem_Free(source->bytes);
|
|
}
|
|
#endif
|
|
source->bytes = bytes;
|
|
source->cap = cap;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
validate_line(const _PyTok_SourceText *source, const char *bytes,
|
|
Py_ssize_t len, int append_newline)
|
|
{
|
|
if (len < 0 || (len == 0 && !append_newline) || bytes == NULL ||
|
|
(source->nlines > 0 &&
|
|
(source->len == 0 || source->bytes[source->len - 1] != '\n'))) {
|
|
PyErr_SetString(PyExc_SystemError, "invalid tokenizer source line");
|
|
return -1;
|
|
}
|
|
const char *newline = memchr(bytes, '\n', len);
|
|
if (newline != NULL && (append_newline || newline != bytes + len - 1)) {
|
|
PyErr_SetString(PyExc_SystemError, "invalid tokenizer source line");
|
|
return -1;
|
|
}
|
|
if (source->nlines == INT_MAX ||
|
|
(source->nlines == INT_MAX - 1 && (newline != NULL || append_newline))) {
|
|
PyErr_SetString(PyExc_OverflowError, "too many tokenizer source lines");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static _PyTok_Off
|
|
append_line(_PyTok_SourceText *source, const char *bytes,
|
|
Py_ssize_t len, int append_newline)
|
|
{
|
|
if (validate_line(source, bytes, len, append_newline) < 0) {
|
|
return -1;
|
|
}
|
|
if (source->len > PY_SSIZE_T_MAX - len - append_newline - 1 ||
|
|
source->base_offset > PY_SSIZE_T_MAX - source->len - len - append_newline) {
|
|
PyErr_NoMemory();
|
|
return -1;
|
|
}
|
|
if (reserve_bytes(source, source->len + len + append_newline + 1) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
_PyTok_Off start = source->len;
|
|
memcpy(source->bytes + start, bytes, len);
|
|
source->len += len;
|
|
if (append_newline) {
|
|
source->bytes[source->len++] = '\n';
|
|
}
|
|
source->bytes[source->len] = '\0';
|
|
source->nlines++;
|
|
return source->base_offset + start;
|
|
}
|
|
|
|
_PyTok_Off
|
|
_PyTok_SourceAppendLine(_PyTok_SourceText *source, const char *bytes,
|
|
Py_ssize_t len)
|
|
{
|
|
return append_line(source, bytes, len, 0);
|
|
}
|
|
|
|
_PyTok_Off
|
|
_PyTok_SourceAppendLineWithNewline(_PyTok_SourceText *source, const char *bytes,
|
|
Py_ssize_t len)
|
|
{
|
|
return append_line(source, bytes, len, 1);
|
|
}
|
|
|
|
const char *
|
|
_PyTok_SourceLineView(const _PyTok_SourceText *source, Py_ssize_t lineno,
|
|
Py_ssize_t *len)
|
|
{
|
|
assert(len != NULL);
|
|
const char *line = _PyTok_SourceData(source);
|
|
const char *end = line + source->len;
|
|
while (lineno > 1) {
|
|
const char *newline = memchr(line, '\n', end - line);
|
|
if (newline == NULL) {
|
|
break;
|
|
}
|
|
line = newline + 1;
|
|
lineno--;
|
|
}
|
|
const char *newline = memchr(line, '\n', end - line);
|
|
*len = (newline != NULL ? newline : end) - line;
|
|
return line;
|
|
}
|