diff options
author | nkozlovskiy <nmk@ydb.tech> | 2023-09-29 12:24:06 +0300 |
---|---|---|
committer | nkozlovskiy <nmk@ydb.tech> | 2023-09-29 12:41:34 +0300 |
commit | e0e3e1717e3d33762ce61950504f9637a6e669ed (patch) | |
tree | bca3ff6939b10ed60c3d5c12439963a1146b9711 /contrib/tools/python3/src/Python/symtable.c | |
parent | 38f2c5852db84c7b4d83adfcb009eb61541d1ccd (diff) | |
download | ydb-e0e3e1717e3d33762ce61950504f9637a6e669ed.tar.gz |
add ydb deps
Diffstat (limited to 'contrib/tools/python3/src/Python/symtable.c')
-rw-r--r-- | contrib/tools/python3/src/Python/symtable.c | 2160 |
1 files changed, 2160 insertions, 0 deletions
diff --git a/contrib/tools/python3/src/Python/symtable.c b/contrib/tools/python3/src/Python/symtable.c new file mode 100644 index 0000000000..0b259b08b6 --- /dev/null +++ b/contrib/tools/python3/src/Python/symtable.c @@ -0,0 +1,2160 @@ +#include "Python.h" +#include "pycore_ast.h" // identifier, stmt_ty +#include "pycore_compile.h" // _Py_Mangle(), _PyFuture_FromAST() +#include "pycore_parser.h" // _PyParser_ASTFromString() +#include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_symtable.h" // PySTEntryObject +#include "structmember.h" // PyMemberDef + +/* error strings used for warnings */ +#define GLOBAL_PARAM \ +"name '%U' is parameter and global" + +#define NONLOCAL_PARAM \ +"name '%U' is parameter and nonlocal" + +#define GLOBAL_AFTER_ASSIGN \ +"name '%U' is assigned to before global declaration" + +#define NONLOCAL_AFTER_ASSIGN \ +"name '%U' is assigned to before nonlocal declaration" + +#define GLOBAL_AFTER_USE \ +"name '%U' is used prior to global declaration" + +#define NONLOCAL_AFTER_USE \ +"name '%U' is used prior to nonlocal declaration" + +#define GLOBAL_ANNOT \ +"annotated name '%U' can't be global" + +#define NONLOCAL_ANNOT \ +"annotated name '%U' can't be nonlocal" + +#define IMPORT_STAR_WARNING "import * only allowed at module level" + +#define NAMED_EXPR_COMP_IN_CLASS \ +"assignment expression within a comprehension cannot be used in a class body" + +#define NAMED_EXPR_COMP_CONFLICT \ +"assignment expression cannot rebind comprehension iteration variable '%U'" + +#define NAMED_EXPR_COMP_INNER_LOOP_CONFLICT \ +"comprehension inner loop cannot rebind assignment expression target '%U'" + +#define NAMED_EXPR_COMP_ITER_EXPR \ +"assignment expression cannot be used in a comprehension iterable expression" + +#define ANNOTATION_NOT_ALLOWED \ +"'%s' can not be used within an annotation" + + +#define LOCATION(x) \ + (x)->lineno, (x)->col_offset, (x)->end_lineno, (x)->end_col_offset + +#define ST_LOCATION(x) \ + (x)->ste_lineno, (x)->ste_col_offset, (x)->ste_end_lineno, (x)->ste_end_col_offset + +static PySTEntryObject * +ste_new(struct symtable *st, identifier name, _Py_block_ty block, + void *key, int lineno, int col_offset, + int end_lineno, int end_col_offset) +{ + PySTEntryObject *ste = NULL; + PyObject *k = NULL; + + k = PyLong_FromVoidPtr(key); + if (k == NULL) + goto fail; + ste = PyObject_New(PySTEntryObject, &PySTEntry_Type); + if (ste == NULL) { + Py_DECREF(k); + goto fail; + } + ste->ste_table = st; + ste->ste_id = k; /* ste owns reference to k */ + + Py_INCREF(name); + ste->ste_name = name; + + ste->ste_symbols = NULL; + ste->ste_varnames = NULL; + ste->ste_children = NULL; + + ste->ste_directives = NULL; + + ste->ste_type = block; + ste->ste_nested = 0; + ste->ste_free = 0; + ste->ste_varargs = 0; + ste->ste_varkeywords = 0; + ste->ste_opt_lineno = 0; + ste->ste_opt_col_offset = 0; + ste->ste_lineno = lineno; + ste->ste_col_offset = col_offset; + ste->ste_end_lineno = end_lineno; + ste->ste_end_col_offset = end_col_offset; + + if (st->st_cur != NULL && + (st->st_cur->ste_nested || + st->st_cur->ste_type == FunctionBlock)) + ste->ste_nested = 1; + ste->ste_child_free = 0; + ste->ste_generator = 0; + ste->ste_coroutine = 0; + ste->ste_comprehension = NoComprehension; + ste->ste_returns_value = 0; + ste->ste_needs_class_closure = 0; + ste->ste_comp_iter_target = 0; + ste->ste_comp_iter_expr = 0; + + ste->ste_symbols = PyDict_New(); + ste->ste_varnames = PyList_New(0); + ste->ste_children = PyList_New(0); + if (ste->ste_symbols == NULL + || ste->ste_varnames == NULL + || ste->ste_children == NULL) + goto fail; + + if (PyDict_SetItem(st->st_blocks, ste->ste_id, (PyObject *)ste) < 0) + goto fail; + + return ste; + fail: + Py_XDECREF(ste); + return NULL; +} + +static PyObject * +ste_repr(PySTEntryObject *ste) +{ + return PyUnicode_FromFormat("<symtable entry %U(%ld), line %d>", + ste->ste_name, + PyLong_AS_LONG(ste->ste_id), ste->ste_lineno); +} + +static void +ste_dealloc(PySTEntryObject *ste) +{ + ste->ste_table = NULL; + Py_XDECREF(ste->ste_id); + Py_XDECREF(ste->ste_name); + Py_XDECREF(ste->ste_symbols); + Py_XDECREF(ste->ste_varnames); + Py_XDECREF(ste->ste_children); + Py_XDECREF(ste->ste_directives); + PyObject_Free(ste); +} + +#define OFF(x) offsetof(PySTEntryObject, x) + +static PyMemberDef ste_memberlist[] = { + {"id", T_OBJECT, OFF(ste_id), READONLY}, + {"name", T_OBJECT, OFF(ste_name), READONLY}, + {"symbols", T_OBJECT, OFF(ste_symbols), READONLY}, + {"varnames", T_OBJECT, OFF(ste_varnames), READONLY}, + {"children", T_OBJECT, OFF(ste_children), READONLY}, + {"nested", T_INT, OFF(ste_nested), READONLY}, + {"type", T_INT, OFF(ste_type), READONLY}, + {"lineno", T_INT, OFF(ste_lineno), READONLY}, + {NULL} +}; + +PyTypeObject PySTEntry_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "symtable entry", + sizeof(PySTEntryObject), + 0, + (destructor)ste_dealloc, /* tp_dealloc */ + 0, /* tp_vectorcall_offset */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_as_async */ + (reprfunc)ste_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + ste_memberlist, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +static int symtable_analyze(struct symtable *st); +static int symtable_enter_block(struct symtable *st, identifier name, + _Py_block_ty block, void *ast, + int lineno, int col_offset, + int end_lineno, int end_col_offset); +static int symtable_exit_block(struct symtable *st); +static int symtable_visit_stmt(struct symtable *st, stmt_ty s); +static int symtable_visit_expr(struct symtable *st, expr_ty s); +static int symtable_visit_genexp(struct symtable *st, expr_ty s); +static int symtable_visit_listcomp(struct symtable *st, expr_ty s); +static int symtable_visit_setcomp(struct symtable *st, expr_ty s); +static int symtable_visit_dictcomp(struct symtable *st, expr_ty s); +static int symtable_visit_arguments(struct symtable *st, arguments_ty); +static int symtable_visit_excepthandler(struct symtable *st, excepthandler_ty); +static int symtable_visit_alias(struct symtable *st, alias_ty); +static int symtable_visit_comprehension(struct symtable *st, comprehension_ty); +static int symtable_visit_keyword(struct symtable *st, keyword_ty); +static int symtable_visit_params(struct symtable *st, asdl_arg_seq *args); +static int symtable_visit_annotation(struct symtable *st, expr_ty annotation); +static int symtable_visit_argannotations(struct symtable *st, asdl_arg_seq *args); +static int symtable_implicit_arg(struct symtable *st, int pos); +static int symtable_visit_annotations(struct symtable *st, stmt_ty, arguments_ty, expr_ty); +static int symtable_visit_withitem(struct symtable *st, withitem_ty item); +static int symtable_visit_match_case(struct symtable *st, match_case_ty m); +static int symtable_visit_pattern(struct symtable *st, pattern_ty s); +static int symtable_raise_if_annotation_block(struct symtable *st, const char *, expr_ty); +static int symtable_raise_if_comprehension_block(struct symtable *st, expr_ty); + + +#define DUPLICATE_ARGUMENT \ +"duplicate argument '%U' in function definition" + +static struct symtable * +symtable_new(void) +{ + struct symtable *st; + + st = (struct symtable *)PyMem_Malloc(sizeof(struct symtable)); + if (st == NULL) { + PyErr_NoMemory(); + return NULL; + } + + st->st_filename = NULL; + st->st_blocks = NULL; + + if ((st->st_stack = PyList_New(0)) == NULL) + goto fail; + if ((st->st_blocks = PyDict_New()) == NULL) + goto fail; + st->st_cur = NULL; + st->st_private = NULL; + return st; + fail: + _PySymtable_Free(st); + return NULL; +} + +/* When compiling the use of C stack is probably going to be a lot + lighter than when executing Python code but still can overflow + and causing a Python crash if not checked (e.g. eval("()"*300000)). + Using the current recursion limit for the compiler seems too + restrictive (it caused at least one test to fail) so a factor is + used to allow deeper recursion when compiling an expression. + + Using a scaling factor means this should automatically adjust when + the recursion limit is adjusted for small or large C stack allocations. +*/ +#define COMPILER_STACK_FRAME_SCALE 3 + +struct symtable * +_PySymtable_Build(mod_ty mod, PyObject *filename, PyFutureFeatures *future) +{ + struct symtable *st = symtable_new(); + asdl_stmt_seq *seq; + int i; + PyThreadState *tstate; + int recursion_limit = Py_GetRecursionLimit(); + int starting_recursion_depth; + + if (st == NULL) + return NULL; + if (filename == NULL) { + _PySymtable_Free(st); + return NULL; + } + Py_INCREF(filename); + st->st_filename = filename; + st->st_future = future; + + /* Setup recursion depth check counters */ + tstate = _PyThreadState_GET(); + if (!tstate) { + _PySymtable_Free(st); + return NULL; + } + /* Be careful here to prevent overflow. */ + int recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; + starting_recursion_depth = (recursion_depth < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? + recursion_depth * COMPILER_STACK_FRAME_SCALE : recursion_depth; + st->recursion_depth = starting_recursion_depth; + st->recursion_limit = (recursion_limit < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? + recursion_limit * COMPILER_STACK_FRAME_SCALE : recursion_limit; + + /* Make the initial symbol information gathering pass */ + if (!symtable_enter_block(st, &_Py_ID(top), ModuleBlock, (void *)mod, 0, 0, 0, 0)) { + _PySymtable_Free(st); + return NULL; + } + + st->st_top = st->st_cur; + switch (mod->kind) { + case Module_kind: + seq = mod->v.Module.body; + for (i = 0; i < asdl_seq_LEN(seq); i++) + if (!symtable_visit_stmt(st, + (stmt_ty)asdl_seq_GET(seq, i))) + goto error; + break; + case Expression_kind: + if (!symtable_visit_expr(st, mod->v.Expression.body)) + goto error; + break; + case Interactive_kind: + seq = mod->v.Interactive.body; + for (i = 0; i < asdl_seq_LEN(seq); i++) + if (!symtable_visit_stmt(st, + (stmt_ty)asdl_seq_GET(seq, i))) + goto error; + break; + case FunctionType_kind: + PyErr_SetString(PyExc_RuntimeError, + "this compiler does not handle FunctionTypes"); + goto error; + } + if (!symtable_exit_block(st)) { + _PySymtable_Free(st); + return NULL; + } + /* Check that the recursion depth counting balanced correctly */ + if (st->recursion_depth != starting_recursion_depth) { + PyErr_Format(PyExc_SystemError, + "symtable analysis recursion depth mismatch (before=%d, after=%d)", + starting_recursion_depth, st->recursion_depth); + _PySymtable_Free(st); + return NULL; + } + /* Make the second symbol analysis pass */ + if (symtable_analyze(st)) + return st; + _PySymtable_Free(st); + return NULL; + error: + (void) symtable_exit_block(st); + _PySymtable_Free(st); + return NULL; +} + + +void +_PySymtable_Free(struct symtable *st) +{ + Py_XDECREF(st->st_filename); + Py_XDECREF(st->st_blocks); + Py_XDECREF(st->st_stack); + PyMem_Free((void *)st); +} + +PySTEntryObject * +PySymtable_Lookup(struct symtable *st, void *key) +{ + PyObject *k, *v; + + k = PyLong_FromVoidPtr(key); + if (k == NULL) + return NULL; + v = PyDict_GetItemWithError(st->st_blocks, k); + if (v) { + assert(PySTEntry_Check(v)); + Py_INCREF(v); + } + else if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_KeyError, + "unknown symbol table entry"); + } + + Py_DECREF(k); + return (PySTEntryObject *)v; +} + +long +_PyST_GetSymbol(PySTEntryObject *ste, PyObject *name) +{ + PyObject *v = PyDict_GetItemWithError(ste->ste_symbols, name); + if (!v) + return 0; + assert(PyLong_Check(v)); + return PyLong_AS_LONG(v); +} + +int +_PyST_GetScope(PySTEntryObject *ste, PyObject *name) +{ + long symbol = _PyST_GetSymbol(ste, name); + return (symbol >> SCOPE_OFFSET) & SCOPE_MASK; +} + +static int +error_at_directive(PySTEntryObject *ste, PyObject *name) +{ + Py_ssize_t i; + PyObject *data; + assert(ste->ste_directives); + for (i = 0; i < PyList_GET_SIZE(ste->ste_directives); i++) { + data = PyList_GET_ITEM(ste->ste_directives, i); + assert(PyTuple_CheckExact(data)); + assert(PyUnicode_CheckExact(PyTuple_GET_ITEM(data, 0))); + if (PyUnicode_Compare(PyTuple_GET_ITEM(data, 0), name) == 0) { + PyErr_RangedSyntaxLocationObject(ste->ste_table->st_filename, + PyLong_AsLong(PyTuple_GET_ITEM(data, 1)), + PyLong_AsLong(PyTuple_GET_ITEM(data, 2)) + 1, + PyLong_AsLong(PyTuple_GET_ITEM(data, 3)), + PyLong_AsLong(PyTuple_GET_ITEM(data, 4)) + 1); + + return 0; + } + } + PyErr_SetString(PyExc_RuntimeError, + "BUG: internal directive bookkeeping broken"); + return 0; +} + + +/* Analyze raw symbol information to determine scope of each name. + + The next several functions are helpers for symtable_analyze(), + which determines whether a name is local, global, or free. In addition, + it determines which local variables are cell variables; they provide + bindings that are used for free variables in enclosed blocks. + + There are also two kinds of global variables, implicit and explicit. An + explicit global is declared with the global statement. An implicit + global is a free variable for which the compiler has found no binding + in an enclosing function scope. The implicit global is either a global + or a builtin. Python's module and class blocks use the xxx_NAME opcodes + to handle these names to implement slightly odd semantics. In such a + block, the name is treated as global until it is assigned to; then it + is treated as a local. + + The symbol table requires two passes to determine the scope of each name. + The first pass collects raw facts from the AST via the symtable_visit_* + functions: the name is a parameter here, the name is used but not defined + here, etc. The second pass analyzes these facts during a pass over the + PySTEntryObjects created during pass 1. + + When a function is entered during the second pass, the parent passes + the set of all name bindings visible to its children. These bindings + are used to determine if non-local variables are free or implicit globals. + Names which are explicitly declared nonlocal must exist in this set of + visible names - if they do not, a syntax error is raised. After doing + the local analysis, it analyzes each of its child blocks using an + updated set of name bindings. + + The children update the free variable set. If a local variable is added to + the free variable set by the child, the variable is marked as a cell. The + function object being defined must provide runtime storage for the variable + that may outlive the function's frame. Cell variables are removed from the + free set before the analyze function returns to its parent. + + During analysis, the names are: + symbols: dict mapping from symbol names to flag values (including offset scope values) + scopes: dict mapping from symbol names to scope values (no offset) + local: set of all symbol names local to the current scope + bound: set of all symbol names local to a containing function scope + free: set of all symbol names referenced but not bound in child scopes + global: set of all symbol names explicitly declared as global +*/ + +#define SET_SCOPE(DICT, NAME, I) { \ + PyObject *o = PyLong_FromLong(I); \ + if (!o) \ + return 0; \ + if (PyDict_SetItem((DICT), (NAME), o) < 0) { \ + Py_DECREF(o); \ + return 0; \ + } \ + Py_DECREF(o); \ +} + +/* Decide on scope of name, given flags. + + The namespace dictionaries may be modified to record information + about the new name. For example, a new global will add an entry to + global. A name that was global can be changed to local. +*/ + +static int +analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags, + PyObject *bound, PyObject *local, PyObject *free, + PyObject *global) +{ + if (flags & DEF_GLOBAL) { + if (flags & DEF_NONLOCAL) { + PyErr_Format(PyExc_SyntaxError, + "name '%U' is nonlocal and global", + name); + return error_at_directive(ste, name); + } + SET_SCOPE(scopes, name, GLOBAL_EXPLICIT); + if (PySet_Add(global, name) < 0) + return 0; + if (bound && (PySet_Discard(bound, name) < 0)) + return 0; + return 1; + } + if (flags & DEF_NONLOCAL) { + if (!bound) { + PyErr_Format(PyExc_SyntaxError, + "nonlocal declaration not allowed at module level"); + return error_at_directive(ste, name); + } + if (!PySet_Contains(bound, name)) { + PyErr_Format(PyExc_SyntaxError, + "no binding for nonlocal '%U' found", + name); + + return error_at_directive(ste, name); + } + SET_SCOPE(scopes, name, FREE); + ste->ste_free = 1; + return PySet_Add(free, name) >= 0; + } + if (flags & DEF_BOUND) { + SET_SCOPE(scopes, name, LOCAL); + if (PySet_Add(local, name) < 0) + return 0; + if (PySet_Discard(global, name) < 0) + return 0; + return 1; + } + /* If an enclosing block has a binding for this name, it + is a free variable rather than a global variable. + Note that having a non-NULL bound implies that the block + is nested. + */ + if (bound && PySet_Contains(bound, name)) { + SET_SCOPE(scopes, name, FREE); + ste->ste_free = 1; + return PySet_Add(free, name) >= 0; + } + /* If a parent has a global statement, then call it global + explicit? It could also be global implicit. + */ + if (global && PySet_Contains(global, name)) { + SET_SCOPE(scopes, name, GLOBAL_IMPLICIT); + return 1; + } + if (ste->ste_nested) + ste->ste_free = 1; + SET_SCOPE(scopes, name, GLOBAL_IMPLICIT); + return 1; +} + +#undef SET_SCOPE + +/* If a name is defined in free and also in locals, then this block + provides the binding for the free variable. The name should be + marked CELL in this block and removed from the free list. + + Note that the current block's free variables are included in free. + That's safe because no name can be free and local in the same scope. +*/ + +static int +analyze_cells(PyObject *scopes, PyObject *free) +{ + PyObject *name, *v, *v_cell; + int success = 0; + Py_ssize_t pos = 0; + + v_cell = PyLong_FromLong(CELL); + if (!v_cell) + return 0; + while (PyDict_Next(scopes, &pos, &name, &v)) { + long scope; + assert(PyLong_Check(v)); + scope = PyLong_AS_LONG(v); + if (scope != LOCAL) + continue; + if (!PySet_Contains(free, name)) + continue; + /* Replace LOCAL with CELL for this name, and remove + from free. It is safe to replace the value of name + in the dict, because it will not cause a resize. + */ + if (PyDict_SetItem(scopes, name, v_cell) < 0) + goto error; + if (PySet_Discard(free, name) < 0) + goto error; + } + success = 1; + error: + Py_DECREF(v_cell); + return success; +} + +static int +drop_class_free(PySTEntryObject *ste, PyObject *free) +{ + int res; + res = PySet_Discard(free, &_Py_ID(__class__)); + if (res < 0) + return 0; + if (res) + ste->ste_needs_class_closure = 1; + return 1; +} + +/* Enter the final scope information into the ste_symbols dict. + * + * All arguments are dicts. Modifies symbols, others are read-only. +*/ +static int +update_symbols(PyObject *symbols, PyObject *scopes, + PyObject *bound, PyObject *free, int classflag) +{ + PyObject *name = NULL, *itr = NULL; + PyObject *v = NULL, *v_scope = NULL, *v_new = NULL, *v_free = NULL; + Py_ssize_t pos = 0; + + /* Update scope information for all symbols in this scope */ + while (PyDict_Next(symbols, &pos, &name, &v)) { + long scope, flags; + assert(PyLong_Check(v)); + flags = PyLong_AS_LONG(v); + v_scope = PyDict_GetItemWithError(scopes, name); + assert(v_scope && PyLong_Check(v_scope)); + scope = PyLong_AS_LONG(v_scope); + flags |= (scope << SCOPE_OFFSET); + v_new = PyLong_FromLong(flags); + if (!v_new) + return 0; + if (PyDict_SetItem(symbols, name, v_new) < 0) { + Py_DECREF(v_new); + return 0; + } + Py_DECREF(v_new); + } + + /* Record not yet resolved free variables from children (if any) */ + v_free = PyLong_FromLong(FREE << SCOPE_OFFSET); + if (!v_free) + return 0; + + itr = PyObject_GetIter(free); + if (itr == NULL) { + Py_DECREF(v_free); + return 0; + } + + while ((name = PyIter_Next(itr))) { + v = PyDict_GetItemWithError(symbols, name); + + /* Handle symbol that already exists in this scope */ + if (v) { + /* Handle a free variable in a method of + the class that has the same name as a local + or global in the class scope. + */ + if (classflag && + PyLong_AS_LONG(v) & (DEF_BOUND | DEF_GLOBAL)) { + long flags = PyLong_AS_LONG(v) | DEF_FREE_CLASS; + v_new = PyLong_FromLong(flags); + if (!v_new) { + goto error; + } + if (PyDict_SetItem(symbols, name, v_new) < 0) { + Py_DECREF(v_new); + goto error; + } + Py_DECREF(v_new); + } + /* It's a cell, or already free in this scope */ + Py_DECREF(name); + continue; + } + else if (PyErr_Occurred()) { + goto error; + } + /* Handle global symbol */ + if (bound && !PySet_Contains(bound, name)) { + Py_DECREF(name); + continue; /* it's a global */ + } + /* Propagate new free symbol up the lexical stack */ + if (PyDict_SetItem(symbols, name, v_free) < 0) { + goto error; + } + Py_DECREF(name); + } + Py_DECREF(itr); + Py_DECREF(v_free); + return 1; +error: + Py_XDECREF(v_free); + Py_XDECREF(itr); + Py_XDECREF(name); + return 0; +} + +/* Make final symbol table decisions for block of ste. + + Arguments: + ste -- current symtable entry (input/output) + bound -- set of variables bound in enclosing scopes (input). bound + is NULL for module blocks. + free -- set of free variables in enclosed scopes (output) + globals -- set of declared global variables in enclosing scopes (input) + + The implementation uses two mutually recursive functions, + analyze_block() and analyze_child_block(). analyze_block() is + responsible for analyzing the individual names defined in a block. + analyze_child_block() prepares temporary namespace dictionaries + used to evaluated nested blocks. + + The two functions exist because a child block should see the name + bindings of its enclosing blocks, but those bindings should not + propagate back to a parent block. +*/ + +static int +analyze_child_block(PySTEntryObject *entry, PyObject *bound, PyObject *free, + PyObject *global, PyObject* child_free); + +static int +analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, + PyObject *global) +{ + PyObject *name, *v, *local = NULL, *scopes = NULL, *newbound = NULL; + PyObject *newglobal = NULL, *newfree = NULL, *allfree = NULL; + PyObject *temp; + int i, success = 0; + Py_ssize_t pos = 0; + + local = PySet_New(NULL); /* collect new names bound in block */ + if (!local) + goto error; + scopes = PyDict_New(); /* collect scopes defined for each name */ + if (!scopes) + goto error; + + /* Allocate new global and bound variable dictionaries. These + dictionaries hold the names visible in nested blocks. For + ClassBlocks, the bound and global names are initialized + before analyzing names, because class bindings aren't + visible in methods. For other blocks, they are initialized + after names are analyzed. + */ + + /* TODO(jhylton): Package these dicts in a struct so that we + can write reasonable helper functions? + */ + newglobal = PySet_New(NULL); + if (!newglobal) + goto error; + newfree = PySet_New(NULL); + if (!newfree) + goto error; + newbound = PySet_New(NULL); + if (!newbound) + goto error; + + /* Class namespace has no effect on names visible in + nested functions, so populate the global and bound + sets to be passed to child blocks before analyzing + this one. + */ + if (ste->ste_type == ClassBlock) { + /* Pass down known globals */ + temp = PyNumber_InPlaceOr(newglobal, global); + if (!temp) + goto error; + Py_DECREF(temp); + /* Pass down previously bound symbols */ + if (bound) { + temp = PyNumber_InPlaceOr(newbound, bound); + if (!temp) + goto error; + Py_DECREF(temp); + } + } + + while (PyDict_Next(ste->ste_symbols, &pos, &name, &v)) { + long flags = PyLong_AS_LONG(v); + if (!analyze_name(ste, scopes, name, flags, + bound, local, free, global)) + goto error; + } + + /* Populate global and bound sets to be passed to children. */ + if (ste->ste_type != ClassBlock) { + /* Add function locals to bound set */ + if (ste->ste_type == FunctionBlock) { + temp = PyNumber_InPlaceOr(newbound, local); + if (!temp) + goto error; + Py_DECREF(temp); + } + /* Pass down previously bound symbols */ + if (bound) { + temp = PyNumber_InPlaceOr(newbound, bound); + if (!temp) + goto error; + Py_DECREF(temp); + } + /* Pass down known globals */ + temp = PyNumber_InPlaceOr(newglobal, global); + if (!temp) + goto error; + Py_DECREF(temp); + } + else { + /* Special-case __class__ */ + if (PySet_Add(newbound, &_Py_ID(__class__)) < 0) + goto error; + } + + /* Recursively call analyze_child_block() on each child block. + + newbound, newglobal now contain the names visible in + nested blocks. The free variables in the children will + be collected in allfree. + */ + allfree = PySet_New(NULL); + if (!allfree) + goto error; + for (i = 0; i < PyList_GET_SIZE(ste->ste_children); ++i) { + PyObject *c = PyList_GET_ITEM(ste->ste_children, i); + PySTEntryObject* entry; + assert(c && PySTEntry_Check(c)); + entry = (PySTEntryObject*)c; + if (!analyze_child_block(entry, newbound, newfree, newglobal, + allfree)) + goto error; + /* Check if any children have free variables */ + if (entry->ste_free || entry->ste_child_free) + ste->ste_child_free = 1; + } + + temp = PyNumber_InPlaceOr(newfree, allfree); + if (!temp) + goto error; + Py_DECREF(temp); + + /* Check if any local variables must be converted to cell variables */ + if (ste->ste_type == FunctionBlock && !analyze_cells(scopes, newfree)) + goto error; + else if (ste->ste_type == ClassBlock && !drop_class_free(ste, newfree)) + goto error; + /* Records the results of the analysis in the symbol table entry */ + if (!update_symbols(ste->ste_symbols, scopes, bound, newfree, + ste->ste_type == ClassBlock)) + goto error; + + temp = PyNumber_InPlaceOr(free, newfree); + if (!temp) + goto error; + Py_DECREF(temp); + success = 1; + error: + Py_XDECREF(scopes); + Py_XDECREF(local); + Py_XDECREF(newbound); + Py_XDECREF(newglobal); + Py_XDECREF(newfree); + Py_XDECREF(allfree); + if (!success) + assert(PyErr_Occurred()); + return success; +} + +static int +analyze_child_block(PySTEntryObject *entry, PyObject *bound, PyObject *free, + PyObject *global, PyObject* child_free) +{ + PyObject *temp_bound = NULL, *temp_global = NULL, *temp_free = NULL; + PyObject *temp; + + /* Copy the bound and global dictionaries. + + These dictionaries are used by all blocks enclosed by the + current block. The analyze_block() call modifies these + dictionaries. + + */ + temp_bound = PySet_New(bound); + if (!temp_bound) + goto error; + temp_free = PySet_New(free); + if (!temp_free) + goto error; + temp_global = PySet_New(global); + if (!temp_global) + goto error; + + if (!analyze_block(entry, temp_bound, temp_free, temp_global)) + goto error; + temp = PyNumber_InPlaceOr(child_free, temp_free); + if (!temp) + goto error; + Py_DECREF(temp); + Py_DECREF(temp_bound); + Py_DECREF(temp_free); + Py_DECREF(temp_global); + return 1; + error: + Py_XDECREF(temp_bound); + Py_XDECREF(temp_free); + Py_XDECREF(temp_global); + return 0; +} + +static int +symtable_analyze(struct symtable *st) +{ + PyObject *free, *global; + int r; + + free = PySet_New(NULL); + if (!free) + return 0; + global = PySet_New(NULL); + if (!global) { + Py_DECREF(free); + return 0; + } + r = analyze_block(st->st_top, NULL, free, global); + Py_DECREF(free); + Py_DECREF(global); + return r; +} + +/* symtable_enter_block() gets a reference via ste_new. + This reference is released when the block is exited, via the DECREF + in symtable_exit_block(). +*/ + +static int +symtable_exit_block(struct symtable *st) +{ + Py_ssize_t size; + + st->st_cur = NULL; + size = PyList_GET_SIZE(st->st_stack); + if (size) { + if (PyList_SetSlice(st->st_stack, size - 1, size, NULL) < 0) + return 0; + if (--size) + st->st_cur = (PySTEntryObject *)PyList_GET_ITEM(st->st_stack, size - 1); + } + return 1; +} + +static int +symtable_enter_block(struct symtable *st, identifier name, _Py_block_ty block, + void *ast, int lineno, int col_offset, + int end_lineno, int end_col_offset) +{ + PySTEntryObject *prev = NULL, *ste; + + ste = ste_new(st, name, block, ast, lineno, col_offset, end_lineno, end_col_offset); + if (ste == NULL) + return 0; + if (PyList_Append(st->st_stack, (PyObject *)ste) < 0) { + Py_DECREF(ste); + return 0; + } + prev = st->st_cur; + /* bpo-37757: For now, disallow *all* assignment expressions in the + * outermost iterator expression of a comprehension, even those inside + * a nested comprehension or a lambda expression. + */ + if (prev) { + ste->ste_comp_iter_expr = prev->ste_comp_iter_expr; + } + /* The entry is owned by the stack. Borrow it for st_cur. */ + Py_DECREF(ste); + st->st_cur = ste; + + /* Annotation blocks shouldn't have any affect on the symbol table since in + * the compilation stage, they will all be transformed to strings. They are + * only created if future 'annotations' feature is activated. */ + if (block == AnnotationBlock) { + return 1; + } + + if (block == ModuleBlock) + st->st_global = st->st_cur->ste_symbols; + + if (prev) { + if (PyList_Append(prev->ste_children, (PyObject *)ste) < 0) { + return 0; + } + } + return 1; +} + +static long +symtable_lookup(struct symtable *st, PyObject *name) +{ + PyObject *mangled = _Py_Mangle(st->st_private, name); + if (!mangled) + return 0; + long ret = _PyST_GetSymbol(st->st_cur, mangled); + Py_DECREF(mangled); + return ret; +} + +static int +symtable_add_def_helper(struct symtable *st, PyObject *name, int flag, struct _symtable_entry *ste, + int lineno, int col_offset, int end_lineno, int end_col_offset) +{ + PyObject *o; + PyObject *dict; + long val; + PyObject *mangled = _Py_Mangle(st->st_private, name); + + + if (!mangled) + return 0; + dict = ste->ste_symbols; + if ((o = PyDict_GetItemWithError(dict, mangled))) { + val = PyLong_AS_LONG(o); + if ((flag & DEF_PARAM) && (val & DEF_PARAM)) { + /* Is it better to use 'mangled' or 'name' here? */ + PyErr_Format(PyExc_SyntaxError, DUPLICATE_ARGUMENT, name); + PyErr_RangedSyntaxLocationObject(st->st_filename, + lineno, col_offset + 1, + end_lineno, end_col_offset + 1); + goto error; + } + val |= flag; + } + else if (PyErr_Occurred()) { + goto error; + } + else { + val = flag; + } + if (ste->ste_comp_iter_target) { + /* This name is an iteration variable in a comprehension, + * so check for a binding conflict with any named expressions. + * Otherwise, mark it as an iteration variable so subsequent + * named expressions can check for conflicts. + */ + if (val & (DEF_GLOBAL | DEF_NONLOCAL)) { + PyErr_Format(PyExc_SyntaxError, + NAMED_EXPR_COMP_INNER_LOOP_CONFLICT, name); + PyErr_RangedSyntaxLocationObject(st->st_filename, + lineno, col_offset + 1, + end_lineno, end_col_offset + 1); + goto error; + } + val |= DEF_COMP_ITER; + } + o = PyLong_FromLong(val); + if (o == NULL) + goto error; + if (PyDict_SetItem(dict, mangled, o) < 0) { + Py_DECREF(o); + goto error; + } + Py_DECREF(o); + + if (flag & DEF_PARAM) { + if (PyList_Append(ste->ste_varnames, mangled) < 0) + goto error; + } else if (flag & DEF_GLOBAL) { + /* XXX need to update DEF_GLOBAL for other flags too; + perhaps only DEF_FREE_GLOBAL */ + val = flag; + if ((o = PyDict_GetItemWithError(st->st_global, mangled))) { + val |= PyLong_AS_LONG(o); + } + else if (PyErr_Occurred()) { + goto error; + } + o = PyLong_FromLong(val); + if (o == NULL) + goto error; + if (PyDict_SetItem(st->st_global, mangled, o) < 0) { + Py_DECREF(o); + goto error; + } + Py_DECREF(o); + } + Py_DECREF(mangled); + return 1; + +error: + Py_DECREF(mangled); + return 0; +} + +static int +symtable_add_def(struct symtable *st, PyObject *name, int flag, + int lineno, int col_offset, int end_lineno, int end_col_offset) +{ + return symtable_add_def_helper(st, name, flag, st->st_cur, + lineno, col_offset, end_lineno, end_col_offset); +} + +/* VISIT, VISIT_SEQ and VIST_SEQ_TAIL take an ASDL type as their second argument. + They use the ASDL name to synthesize the name of the C type and the visit + function. + + VISIT_SEQ_TAIL permits the start of an ASDL sequence to be skipped, which is + useful if the first node in the sequence requires special treatment. + + VISIT_QUIT macro returns the specified value exiting from the function but + first adjusts current recursion counter depth. +*/ + +#define VISIT_QUIT(ST, X) \ + return --(ST)->recursion_depth,(X) + +#define VISIT(ST, TYPE, V) \ + if (!symtable_visit_ ## TYPE((ST), (V))) \ + VISIT_QUIT((ST), 0); + +#define VISIT_SEQ(ST, TYPE, SEQ) { \ + int i; \ + asdl_ ## TYPE ## _seq *seq = (SEQ); /* avoid variable capture */ \ + for (i = 0; i < asdl_seq_LEN(seq); i++) { \ + TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, i); \ + if (!symtable_visit_ ## TYPE((ST), elt)) \ + VISIT_QUIT((ST), 0); \ + } \ +} + +#define VISIT_SEQ_TAIL(ST, TYPE, SEQ, START) { \ + int i; \ + asdl_ ## TYPE ## _seq *seq = (SEQ); /* avoid variable capture */ \ + for (i = (START); i < asdl_seq_LEN(seq); i++) { \ + TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, i); \ + if (!symtable_visit_ ## TYPE((ST), elt)) \ + VISIT_QUIT((ST), 0); \ + } \ +} + +#define VISIT_SEQ_WITH_NULL(ST, TYPE, SEQ) { \ + int i = 0; \ + asdl_ ## TYPE ## _seq *seq = (SEQ); /* avoid variable capture */ \ + for (i = 0; i < asdl_seq_LEN(seq); i++) { \ + TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, i); \ + if (!elt) continue; /* can be NULL */ \ + if (!symtable_visit_ ## TYPE((ST), elt)) \ + VISIT_QUIT((ST), 0); \ + } \ +} + +static int +symtable_record_directive(struct symtable *st, identifier name, int lineno, + int col_offset, int end_lineno, int end_col_offset) +{ + PyObject *data, *mangled; + int res; + if (!st->st_cur->ste_directives) { + st->st_cur->ste_directives = PyList_New(0); + if (!st->st_cur->ste_directives) + return 0; + } + mangled = _Py_Mangle(st->st_private, name); + if (!mangled) + return 0; + data = Py_BuildValue("(Niiii)", mangled, lineno, col_offset, end_lineno, end_col_offset); + if (!data) + return 0; + res = PyList_Append(st->st_cur->ste_directives, data); + Py_DECREF(data); + return res == 0; +} + + +static int +symtable_visit_stmt(struct symtable *st, stmt_ty s) +{ + if (++st->recursion_depth > st->recursion_limit) { + PyErr_SetString(PyExc_RecursionError, + "maximum recursion depth exceeded during compilation"); + VISIT_QUIT(st, 0); + } + switch (s->kind) { + case FunctionDef_kind: + if (!symtable_add_def(st, s->v.FunctionDef.name, DEF_LOCAL, LOCATION(s))) + VISIT_QUIT(st, 0); + if (s->v.FunctionDef.args->defaults) + VISIT_SEQ(st, expr, s->v.FunctionDef.args->defaults); + if (s->v.FunctionDef.args->kw_defaults) + VISIT_SEQ_WITH_NULL(st, expr, s->v.FunctionDef.args->kw_defaults); + if (!symtable_visit_annotations(st, s, s->v.FunctionDef.args, + s->v.FunctionDef.returns)) + VISIT_QUIT(st, 0); + if (s->v.FunctionDef.decorator_list) + VISIT_SEQ(st, expr, s->v.FunctionDef.decorator_list); + if (!symtable_enter_block(st, s->v.FunctionDef.name, + FunctionBlock, (void *)s, + LOCATION(s))) + VISIT_QUIT(st, 0); + VISIT(st, arguments, s->v.FunctionDef.args); + VISIT_SEQ(st, stmt, s->v.FunctionDef.body); + if (!symtable_exit_block(st)) + VISIT_QUIT(st, 0); + break; + case ClassDef_kind: { + PyObject *tmp; + if (!symtable_add_def(st, s->v.ClassDef.name, DEF_LOCAL, LOCATION(s))) + VISIT_QUIT(st, 0); + VISIT_SEQ(st, expr, s->v.ClassDef.bases); + VISIT_SEQ(st, keyword, s->v.ClassDef.keywords); + if (s->v.ClassDef.decorator_list) + VISIT_SEQ(st, expr, s->v.ClassDef.decorator_list); + if (!symtable_enter_block(st, s->v.ClassDef.name, ClassBlock, + (void *)s, s->lineno, s->col_offset, + s->end_lineno, s->end_col_offset)) + VISIT_QUIT(st, 0); + tmp = st->st_private; + st->st_private = s->v.ClassDef.name; + VISIT_SEQ(st, stmt, s->v.ClassDef.body); + st->st_private = tmp; + if (!symtable_exit_block(st)) + VISIT_QUIT(st, 0); + break; + } + case Return_kind: + if (s->v.Return.value) { + VISIT(st, expr, s->v.Return.value); + st->st_cur->ste_returns_value = 1; + } + break; + case Delete_kind: + VISIT_SEQ(st, expr, s->v.Delete.targets); + break; + case Assign_kind: + VISIT_SEQ(st, expr, s->v.Assign.targets); + VISIT(st, expr, s->v.Assign.value); + break; + case AnnAssign_kind: + if (s->v.AnnAssign.target->kind == Name_kind) { + expr_ty e_name = s->v.AnnAssign.target; + long cur = symtable_lookup(st, e_name->v.Name.id); + if (cur < 0) { + VISIT_QUIT(st, 0); + } + if ((cur & (DEF_GLOBAL | DEF_NONLOCAL)) + && (st->st_cur->ste_symbols != st->st_global) + && s->v.AnnAssign.simple) { + PyErr_Format(PyExc_SyntaxError, + cur & DEF_GLOBAL ? GLOBAL_ANNOT : NONLOCAL_ANNOT, + e_name->v.Name.id); + PyErr_RangedSyntaxLocationObject(st->st_filename, + s->lineno, + s->col_offset + 1, + s->end_lineno, + s->end_col_offset + 1); + VISIT_QUIT(st, 0); + } + if (s->v.AnnAssign.simple && + !symtable_add_def(st, e_name->v.Name.id, + DEF_ANNOT | DEF_LOCAL, LOCATION(e_name))) { + VISIT_QUIT(st, 0); + } + else { + if (s->v.AnnAssign.value + && !symtable_add_def(st, e_name->v.Name.id, DEF_LOCAL, LOCATION(e_name))) { + VISIT_QUIT(st, 0); + } + } + } + else { + VISIT(st, expr, s->v.AnnAssign.target); + } + if (!symtable_visit_annotation(st, s->v.AnnAssign.annotation)) { + VISIT_QUIT(st, 0); + } + + if (s->v.AnnAssign.value) { + VISIT(st, expr, s->v.AnnAssign.value); + } + break; + case AugAssign_kind: + VISIT(st, expr, s->v.AugAssign.target); + VISIT(st, expr, s->v.AugAssign.value); + break; + case For_kind: + VISIT(st, expr, s->v.For.target); + VISIT(st, expr, s->v.For.iter); + VISIT_SEQ(st, stmt, s->v.For.body); + if (s->v.For.orelse) + VISIT_SEQ(st, stmt, s->v.For.orelse); + break; + case While_kind: + VISIT(st, expr, s->v.While.test); + VISIT_SEQ(st, stmt, s->v.While.body); + if (s->v.While.orelse) + VISIT_SEQ(st, stmt, s->v.While.orelse); + break; + case If_kind: + /* XXX if 0: and lookup_yield() hacks */ + VISIT(st, expr, s->v.If.test); + VISIT_SEQ(st, stmt, s->v.If.body); + if (s->v.If.orelse) + VISIT_SEQ(st, stmt, s->v.If.orelse); + break; + case Match_kind: + VISIT(st, expr, s->v.Match.subject); + VISIT_SEQ(st, match_case, s->v.Match.cases); + break; + case Raise_kind: + if (s->v.Raise.exc) { + VISIT(st, expr, s->v.Raise.exc); + if (s->v.Raise.cause) { + VISIT(st, expr, s->v.Raise.cause); + } + } + break; + case Try_kind: + VISIT_SEQ(st, stmt, s->v.Try.body); + VISIT_SEQ(st, stmt, s->v.Try.orelse); + VISIT_SEQ(st, excepthandler, s->v.Try.handlers); + VISIT_SEQ(st, stmt, s->v.Try.finalbody); + break; + case TryStar_kind: + VISIT_SEQ(st, stmt, s->v.TryStar.body); + VISIT_SEQ(st, stmt, s->v.TryStar.orelse); + VISIT_SEQ(st, excepthandler, s->v.TryStar.handlers); + VISIT_SEQ(st, stmt, s->v.TryStar.finalbody); + break; + case Assert_kind: + VISIT(st, expr, s->v.Assert.test); + if (s->v.Assert.msg) + VISIT(st, expr, s->v.Assert.msg); + break; + case Import_kind: + VISIT_SEQ(st, alias, s->v.Import.names); + break; + case ImportFrom_kind: + VISIT_SEQ(st, alias, s->v.ImportFrom.names); + break; + case Global_kind: { + int i; + asdl_identifier_seq *seq = s->v.Global.names; + for (i = 0; i < asdl_seq_LEN(seq); i++) { + identifier name = (identifier)asdl_seq_GET(seq, i); + long cur = symtable_lookup(st, name); + if (cur < 0) + VISIT_QUIT(st, 0); + if (cur & (DEF_PARAM | DEF_LOCAL | USE | DEF_ANNOT)) { + const char* msg; + if (cur & DEF_PARAM) { + msg = GLOBAL_PARAM; + } else if (cur & USE) { + msg = GLOBAL_AFTER_USE; + } else if (cur & DEF_ANNOT) { + msg = GLOBAL_ANNOT; + } else { /* DEF_LOCAL */ + msg = GLOBAL_AFTER_ASSIGN; + } + PyErr_Format(PyExc_SyntaxError, + msg, name); + PyErr_RangedSyntaxLocationObject(st->st_filename, + s->lineno, + s->col_offset + 1, + s->end_lineno, + s->end_col_offset + 1); + VISIT_QUIT(st, 0); + } + if (!symtable_add_def(st, name, DEF_GLOBAL, LOCATION(s))) + VISIT_QUIT(st, 0); + if (!symtable_record_directive(st, name, s->lineno, s->col_offset, + s->end_lineno, s->end_col_offset)) + VISIT_QUIT(st, 0); + } + break; + } + case Nonlocal_kind: { + int i; + asdl_identifier_seq *seq = s->v.Nonlocal.names; + for (i = 0; i < asdl_seq_LEN(seq); i++) { + identifier name = (identifier)asdl_seq_GET(seq, i); + long cur = symtable_lookup(st, name); + if (cur < 0) + VISIT_QUIT(st, 0); + if (cur & (DEF_PARAM | DEF_LOCAL | USE | DEF_ANNOT)) { + const char* msg; + if (cur & DEF_PARAM) { + msg = NONLOCAL_PARAM; + } else if (cur & USE) { + msg = NONLOCAL_AFTER_USE; + } else if (cur & DEF_ANNOT) { + msg = NONLOCAL_ANNOT; + } else { /* DEF_LOCAL */ + msg = NONLOCAL_AFTER_ASSIGN; + } + PyErr_Format(PyExc_SyntaxError, msg, name); + PyErr_RangedSyntaxLocationObject(st->st_filename, + s->lineno, + s->col_offset + 1, + s->end_lineno, + s->end_col_offset + 1); + VISIT_QUIT(st, 0); + } + if (!symtable_add_def(st, name, DEF_NONLOCAL, LOCATION(s))) + VISIT_QUIT(st, 0); + if (!symtable_record_directive(st, name, s->lineno, s->col_offset, + s->end_lineno, s->end_col_offset)) + VISIT_QUIT(st, 0); + } + break; + } + case Expr_kind: + VISIT(st, expr, s->v.Expr.value); + break; + case Pass_kind: + case Break_kind: + case Continue_kind: + /* nothing to do here */ + break; + case With_kind: + VISIT_SEQ(st, withitem, s->v.With.items); + VISIT_SEQ(st, stmt, s->v.With.body); + break; + case AsyncFunctionDef_kind: + if (!symtable_add_def(st, s->v.AsyncFunctionDef.name, DEF_LOCAL, LOCATION(s))) + VISIT_QUIT(st, 0); + if (s->v.AsyncFunctionDef.args->defaults) + VISIT_SEQ(st, expr, s->v.AsyncFunctionDef.args->defaults); + if (s->v.AsyncFunctionDef.args->kw_defaults) + VISIT_SEQ_WITH_NULL(st, expr, + s->v.AsyncFunctionDef.args->kw_defaults); + if (!symtable_visit_annotations(st, s, s->v.AsyncFunctionDef.args, + s->v.AsyncFunctionDef.returns)) + VISIT_QUIT(st, 0); + if (s->v.AsyncFunctionDef.decorator_list) + VISIT_SEQ(st, expr, s->v.AsyncFunctionDef.decorator_list); + if (!symtable_enter_block(st, s->v.AsyncFunctionDef.name, + FunctionBlock, (void *)s, + s->lineno, s->col_offset, + s->end_lineno, s->end_col_offset)) + VISIT_QUIT(st, 0); + st->st_cur->ste_coroutine = 1; + VISIT(st, arguments, s->v.AsyncFunctionDef.args); + VISIT_SEQ(st, stmt, s->v.AsyncFunctionDef.body); + if (!symtable_exit_block(st)) + VISIT_QUIT(st, 0); + break; + case AsyncWith_kind: + VISIT_SEQ(st, withitem, s->v.AsyncWith.items); + VISIT_SEQ(st, stmt, s->v.AsyncWith.body); + break; + case AsyncFor_kind: + VISIT(st, expr, s->v.AsyncFor.target); + VISIT(st, expr, s->v.AsyncFor.iter); + VISIT_SEQ(st, stmt, s->v.AsyncFor.body); + if (s->v.AsyncFor.orelse) + VISIT_SEQ(st, stmt, s->v.AsyncFor.orelse); + break; + } + VISIT_QUIT(st, 1); +} + +static int +symtable_extend_namedexpr_scope(struct symtable *st, expr_ty e) +{ + assert(st->st_stack); + assert(e->kind == Name_kind); + + PyObject *target_name = e->v.Name.id; + Py_ssize_t i, size; + struct _symtable_entry *ste; + size = PyList_GET_SIZE(st->st_stack); + assert(size); + + /* Iterate over the stack in reverse and add to the nearest adequate scope */ + for (i = size - 1; i >= 0; i--) { + ste = (struct _symtable_entry *) PyList_GET_ITEM(st->st_stack, i); + + /* If we find a comprehension scope, check for a target + * binding conflict with iteration variables, otherwise skip it + */ + if (ste->ste_comprehension) { + long target_in_scope = _PyST_GetSymbol(ste, target_name); + if (target_in_scope & DEF_COMP_ITER) { + PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_CONFLICT, target_name); + PyErr_RangedSyntaxLocationObject(st->st_filename, + e->lineno, + e->col_offset + 1, + e->end_lineno, + e->end_col_offset + 1); + VISIT_QUIT(st, 0); + } + continue; + } + + /* If we find a FunctionBlock entry, add as GLOBAL/LOCAL or NONLOCAL/LOCAL */ + if (ste->ste_type == FunctionBlock) { + long target_in_scope = _PyST_GetSymbol(ste, target_name); + if (target_in_scope & DEF_GLOBAL) { + if (!symtable_add_def(st, target_name, DEF_GLOBAL, LOCATION(e))) + VISIT_QUIT(st, 0); + } else { + if (!symtable_add_def(st, target_name, DEF_NONLOCAL, LOCATION(e))) + VISIT_QUIT(st, 0); + } + if (!symtable_record_directive(st, target_name, LOCATION(e))) + VISIT_QUIT(st, 0); + + return symtable_add_def_helper(st, target_name, DEF_LOCAL, ste, LOCATION(e)); + } + /* If we find a ModuleBlock entry, add as GLOBAL */ + if (ste->ste_type == ModuleBlock) { + if (!symtable_add_def(st, target_name, DEF_GLOBAL, LOCATION(e))) + VISIT_QUIT(st, 0); + if (!symtable_record_directive(st, target_name, LOCATION(e))) + VISIT_QUIT(st, 0); + + return symtable_add_def_helper(st, target_name, DEF_GLOBAL, ste, LOCATION(e)); + } + /* Disallow usage in ClassBlock */ + if (ste->ste_type == ClassBlock) { + PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_CLASS); + PyErr_RangedSyntaxLocationObject(st->st_filename, + e->lineno, + e->col_offset + 1, + e->end_lineno, + e->end_col_offset + 1); + VISIT_QUIT(st, 0); + } + } + + /* We should always find either a FunctionBlock, ModuleBlock or ClassBlock + and should never fall to this case + */ + assert(0); + return 0; +} + +static int +symtable_handle_namedexpr(struct symtable *st, expr_ty e) +{ + if (st->st_cur->ste_comp_iter_expr > 0) { + /* Assignment isn't allowed in a comprehension iterable expression */ + PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_ITER_EXPR); + PyErr_RangedSyntaxLocationObject(st->st_filename, + e->lineno, + e->col_offset + 1, + e->end_lineno, + e->end_col_offset + 1); + return 0; + } + if (st->st_cur->ste_comprehension) { + /* Inside a comprehension body, so find the right target scope */ + if (!symtable_extend_namedexpr_scope(st, e->v.NamedExpr.target)) + return 0; + } + VISIT(st, expr, e->v.NamedExpr.value); + VISIT(st, expr, e->v.NamedExpr.target); + return 1; +} + +static int +symtable_visit_expr(struct symtable *st, expr_ty e) +{ + if (++st->recursion_depth > st->recursion_limit) { + PyErr_SetString(PyExc_RecursionError, + "maximum recursion depth exceeded during compilation"); + VISIT_QUIT(st, 0); + } + switch (e->kind) { + case NamedExpr_kind: + if (!symtable_raise_if_annotation_block(st, "named expression", e)) { + VISIT_QUIT(st, 0); + } + if(!symtable_handle_namedexpr(st, e)) + VISIT_QUIT(st, 0); + break; + case BoolOp_kind: + VISIT_SEQ(st, expr, e->v.BoolOp.values); + break; + case BinOp_kind: + VISIT(st, expr, e->v.BinOp.left); + VISIT(st, expr, e->v.BinOp.right); + break; + case UnaryOp_kind: + VISIT(st, expr, e->v.UnaryOp.operand); + break; + case Lambda_kind: { + if (e->v.Lambda.args->defaults) + VISIT_SEQ(st, expr, e->v.Lambda.args->defaults); + if (e->v.Lambda.args->kw_defaults) + VISIT_SEQ_WITH_NULL(st, expr, e->v.Lambda.args->kw_defaults); + if (!symtable_enter_block(st, &_Py_ID(lambda), + FunctionBlock, (void *)e, + e->lineno, e->col_offset, + e->end_lineno, e->end_col_offset)) + VISIT_QUIT(st, 0); + VISIT(st, arguments, e->v.Lambda.args); + VISIT(st, expr, e->v.Lambda.body); + if (!symtable_exit_block(st)) + VISIT_QUIT(st, 0); + break; + } + case IfExp_kind: + VISIT(st, expr, e->v.IfExp.test); + VISIT(st, expr, e->v.IfExp.body); + VISIT(st, expr, e->v.IfExp.orelse); + break; + case Dict_kind: + VISIT_SEQ_WITH_NULL(st, expr, e->v.Dict.keys); + VISIT_SEQ(st, expr, e->v.Dict.values); + break; + case Set_kind: + VISIT_SEQ(st, expr, e->v.Set.elts); + break; + case GeneratorExp_kind: + if (!symtable_visit_genexp(st, e)) + VISIT_QUIT(st, 0); + break; + case ListComp_kind: + if (!symtable_visit_listcomp(st, e)) + VISIT_QUIT(st, 0); + break; + case SetComp_kind: + if (!symtable_visit_setcomp(st, e)) + VISIT_QUIT(st, 0); + break; + case DictComp_kind: + if (!symtable_visit_dictcomp(st, e)) + VISIT_QUIT(st, 0); + break; + case Yield_kind: + if (!symtable_raise_if_annotation_block(st, "yield expression", e)) { + VISIT_QUIT(st, 0); + } + if (e->v.Yield.value) + VISIT(st, expr, e->v.Yield.value); + st->st_cur->ste_generator = 1; + if (st->st_cur->ste_comprehension) { + return symtable_raise_if_comprehension_block(st, e); + } + break; + case YieldFrom_kind: + if (!symtable_raise_if_annotation_block(st, "yield expression", e)) { + VISIT_QUIT(st, 0); + } + VISIT(st, expr, e->v.YieldFrom.value); + st->st_cur->ste_generator = 1; + if (st->st_cur->ste_comprehension) { + return symtable_raise_if_comprehension_block(st, e); + } + break; + case Await_kind: + if (!symtable_raise_if_annotation_block(st, "await expression", e)) { + VISIT_QUIT(st, 0); + } + VISIT(st, expr, e->v.Await.value); + st->st_cur->ste_coroutine = 1; + break; + case Compare_kind: + VISIT(st, expr, e->v.Compare.left); + VISIT_SEQ(st, expr, e->v.Compare.comparators); + break; + case Call_kind: + VISIT(st, expr, e->v.Call.func); + VISIT_SEQ(st, expr, e->v.Call.args); + VISIT_SEQ_WITH_NULL(st, keyword, e->v.Call.keywords); + break; + case FormattedValue_kind: + VISIT(st, expr, e->v.FormattedValue.value); + if (e->v.FormattedValue.format_spec) + VISIT(st, expr, e->v.FormattedValue.format_spec); + break; + case JoinedStr_kind: + VISIT_SEQ(st, expr, e->v.JoinedStr.values); + break; + case Constant_kind: + /* Nothing to do here. */ + break; + /* The following exprs can be assignment targets. */ + case Attribute_kind: + VISIT(st, expr, e->v.Attribute.value); + break; + case Subscript_kind: + VISIT(st, expr, e->v.Subscript.value); + VISIT(st, expr, e->v.Subscript.slice); + break; + case Starred_kind: + VISIT(st, expr, e->v.Starred.value); + break; + case Slice_kind: + if (e->v.Slice.lower) + VISIT(st, expr, e->v.Slice.lower) + if (e->v.Slice.upper) + VISIT(st, expr, e->v.Slice.upper) + if (e->v.Slice.step) + VISIT(st, expr, e->v.Slice.step) + break; + case Name_kind: + if (!symtable_add_def(st, e->v.Name.id, + e->v.Name.ctx == Load ? USE : DEF_LOCAL, LOCATION(e))) + VISIT_QUIT(st, 0); + /* Special-case super: it counts as a use of __class__ */ + if (e->v.Name.ctx == Load && + st->st_cur->ste_type == FunctionBlock && + _PyUnicode_EqualToASCIIString(e->v.Name.id, "super")) { + if (!symtable_add_def(st, &_Py_ID(__class__), USE, LOCATION(e))) + VISIT_QUIT(st, 0); + } + break; + /* child nodes of List and Tuple will have expr_context set */ + case List_kind: + VISIT_SEQ(st, expr, e->v.List.elts); + break; + case Tuple_kind: + VISIT_SEQ(st, expr, e->v.Tuple.elts); + break; + } + VISIT_QUIT(st, 1); +} + +static int +symtable_visit_pattern(struct symtable *st, pattern_ty p) +{ + if (++st->recursion_depth > st->recursion_limit) { + PyErr_SetString(PyExc_RecursionError, + "maximum recursion depth exceeded during compilation"); + VISIT_QUIT(st, 0); + } + switch (p->kind) { + case MatchValue_kind: + VISIT(st, expr, p->v.MatchValue.value); + break; + case MatchSingleton_kind: + /* Nothing to do here. */ + break; + case MatchSequence_kind: + VISIT_SEQ(st, pattern, p->v.MatchSequence.patterns); + break; + case MatchStar_kind: + if (p->v.MatchStar.name) { + symtable_add_def(st, p->v.MatchStar.name, DEF_LOCAL, LOCATION(p)); + } + break; + case MatchMapping_kind: + VISIT_SEQ(st, expr, p->v.MatchMapping.keys); + VISIT_SEQ(st, pattern, p->v.MatchMapping.patterns); + if (p->v.MatchMapping.rest) { + symtable_add_def(st, p->v.MatchMapping.rest, DEF_LOCAL, LOCATION(p)); + } + break; + case MatchClass_kind: + VISIT(st, expr, p->v.MatchClass.cls); + VISIT_SEQ(st, pattern, p->v.MatchClass.patterns); + VISIT_SEQ(st, pattern, p->v.MatchClass.kwd_patterns); + break; + case MatchAs_kind: + if (p->v.MatchAs.pattern) { + VISIT(st, pattern, p->v.MatchAs.pattern); + } + if (p->v.MatchAs.name) { + symtable_add_def(st, p->v.MatchAs.name, DEF_LOCAL, LOCATION(p)); + } + break; + case MatchOr_kind: + VISIT_SEQ(st, pattern, p->v.MatchOr.patterns); + break; + } + VISIT_QUIT(st, 1); +} + +static int +symtable_implicit_arg(struct symtable *st, int pos) +{ + PyObject *id = PyUnicode_FromFormat(".%d", pos); + if (id == NULL) + return 0; + if (!symtable_add_def(st, id, DEF_PARAM, ST_LOCATION(st->st_cur))) { + Py_DECREF(id); + return 0; + } + Py_DECREF(id); + return 1; +} + +static int +symtable_visit_params(struct symtable *st, asdl_arg_seq *args) +{ + int i; + + if (!args) + return -1; + + for (i = 0; i < asdl_seq_LEN(args); i++) { + arg_ty arg = (arg_ty)asdl_seq_GET(args, i); + if (!symtable_add_def(st, arg->arg, DEF_PARAM, LOCATION(arg))) + return 0; + } + + return 1; +} + +static int +symtable_visit_annotation(struct symtable *st, expr_ty annotation) +{ + int future_annotations = st->st_future->ff_features & CO_FUTURE_ANNOTATIONS; + if (future_annotations && + !symtable_enter_block(st, &_Py_ID(_annotation), AnnotationBlock, + (void *)annotation, annotation->lineno, + annotation->col_offset, annotation->end_lineno, + annotation->end_col_offset)) { + VISIT_QUIT(st, 0); + } + VISIT(st, expr, annotation); + if (future_annotations && !symtable_exit_block(st)) { + VISIT_QUIT(st, 0); + } + return 1; +} + +static int +symtable_visit_argannotations(struct symtable *st, asdl_arg_seq *args) +{ + int i; + + if (!args) + return -1; + + for (i = 0; i < asdl_seq_LEN(args); i++) { + arg_ty arg = (arg_ty)asdl_seq_GET(args, i); + if (arg->annotation) + VISIT(st, expr, arg->annotation); + } + + return 1; +} + +static int +symtable_visit_annotations(struct symtable *st, stmt_ty o, arguments_ty a, expr_ty returns) +{ + int future_annotations = st->st_future->ff_features & CO_FUTURE_ANNOTATIONS; + if (future_annotations && + !symtable_enter_block(st, &_Py_ID(_annotation), AnnotationBlock, + (void *)o, o->lineno, o->col_offset, o->end_lineno, + o->end_col_offset)) { + VISIT_QUIT(st, 0); + } + if (a->posonlyargs && !symtable_visit_argannotations(st, a->posonlyargs)) + return 0; + if (a->args && !symtable_visit_argannotations(st, a->args)) + return 0; + if (a->vararg && a->vararg->annotation) + VISIT(st, expr, a->vararg->annotation); + if (a->kwarg && a->kwarg->annotation) + VISIT(st, expr, a->kwarg->annotation); + if (a->kwonlyargs && !symtable_visit_argannotations(st, a->kwonlyargs)) + return 0; + if (future_annotations && !symtable_exit_block(st)) { + VISIT_QUIT(st, 0); + } + if (returns && !symtable_visit_annotation(st, returns)) { + VISIT_QUIT(st, 0); + } + return 1; +} + +static int +symtable_visit_arguments(struct symtable *st, arguments_ty a) +{ + /* skip default arguments inside function block + XXX should ast be different? + */ + if (a->posonlyargs && !symtable_visit_params(st, a->posonlyargs)) + return 0; + if (a->args && !symtable_visit_params(st, a->args)) + return 0; + if (a->kwonlyargs && !symtable_visit_params(st, a->kwonlyargs)) + return 0; + if (a->vararg) { + if (!symtable_add_def(st, a->vararg->arg, DEF_PARAM, LOCATION(a->vararg))) + return 0; + st->st_cur->ste_varargs = 1; + } + if (a->kwarg) { + if (!symtable_add_def(st, a->kwarg->arg, DEF_PARAM, LOCATION(a->kwarg))) + return 0; + st->st_cur->ste_varkeywords = 1; + } + return 1; +} + + +static int +symtable_visit_excepthandler(struct symtable *st, excepthandler_ty eh) +{ + if (eh->v.ExceptHandler.type) + VISIT(st, expr, eh->v.ExceptHandler.type); + if (eh->v.ExceptHandler.name) + if (!symtable_add_def(st, eh->v.ExceptHandler.name, DEF_LOCAL, LOCATION(eh))) + return 0; + VISIT_SEQ(st, stmt, eh->v.ExceptHandler.body); + return 1; +} + +static int +symtable_visit_withitem(struct symtable *st, withitem_ty item) +{ + VISIT(st, expr, item->context_expr); + if (item->optional_vars) { + VISIT(st, expr, item->optional_vars); + } + return 1; +} + +static int +symtable_visit_match_case(struct symtable *st, match_case_ty m) +{ + VISIT(st, pattern, m->pattern); + if (m->guard) { + VISIT(st, expr, m->guard); + } + VISIT_SEQ(st, stmt, m->body); + return 1; +} + +static int +symtable_visit_alias(struct symtable *st, alias_ty a) +{ + /* Compute store_name, the name actually bound by the import + operation. It is different than a->name when a->name is a + dotted package name (e.g. spam.eggs) + */ + PyObject *store_name; + PyObject *name = (a->asname == NULL) ? a->name : a->asname; + Py_ssize_t dot = PyUnicode_FindChar(name, '.', 0, + PyUnicode_GET_LENGTH(name), 1); + if (dot != -1) { + store_name = PyUnicode_Substring(name, 0, dot); + if (!store_name) + return 0; + } + else { + store_name = name; + Py_INCREF(store_name); + } + if (!_PyUnicode_EqualToASCIIString(name, "*")) { + int r = symtable_add_def(st, store_name, DEF_IMPORT, LOCATION(a)); + Py_DECREF(store_name); + return r; + } + else { + if (st->st_cur->ste_type != ModuleBlock) { + int lineno = a->lineno; + int col_offset = a->col_offset; + int end_lineno = a->end_lineno; + int end_col_offset = a->end_col_offset; + PyErr_SetString(PyExc_SyntaxError, IMPORT_STAR_WARNING); + PyErr_RangedSyntaxLocationObject(st->st_filename, + lineno, col_offset + 1, + end_lineno, end_col_offset + 1); + Py_DECREF(store_name); + return 0; + } + Py_DECREF(store_name); + return 1; + } +} + + +static int +symtable_visit_comprehension(struct symtable *st, comprehension_ty lc) +{ + st->st_cur->ste_comp_iter_target = 1; + VISIT(st, expr, lc->target); + st->st_cur->ste_comp_iter_target = 0; + st->st_cur->ste_comp_iter_expr++; + VISIT(st, expr, lc->iter); + st->st_cur->ste_comp_iter_expr--; + VISIT_SEQ(st, expr, lc->ifs); + if (lc->is_async) { + st->st_cur->ste_coroutine = 1; + } + return 1; +} + + +static int +symtable_visit_keyword(struct symtable *st, keyword_ty k) +{ + VISIT(st, expr, k->value); + return 1; +} + + +static int +symtable_handle_comprehension(struct symtable *st, expr_ty e, + identifier scope_name, asdl_comprehension_seq *generators, + expr_ty elt, expr_ty value) +{ + int is_generator = (e->kind == GeneratorExp_kind); + comprehension_ty outermost = ((comprehension_ty) + asdl_seq_GET(generators, 0)); + /* Outermost iterator is evaluated in current scope */ + st->st_cur->ste_comp_iter_expr++; + VISIT(st, expr, outermost->iter); + st->st_cur->ste_comp_iter_expr--; + /* Create comprehension scope for the rest */ + if (!scope_name || + !symtable_enter_block(st, scope_name, FunctionBlock, (void *)e, + e->lineno, e->col_offset, + e->end_lineno, e->end_col_offset)) { + return 0; + } + switch(e->kind) { + case ListComp_kind: + st->st_cur->ste_comprehension = ListComprehension; + break; + case SetComp_kind: + st->st_cur->ste_comprehension = SetComprehension; + break; + case DictComp_kind: + st->st_cur->ste_comprehension = DictComprehension; + break; + default: + st->st_cur->ste_comprehension = GeneratorExpression; + break; + } + if (outermost->is_async) { + st->st_cur->ste_coroutine = 1; + } + + /* Outermost iter is received as an argument */ + if (!symtable_implicit_arg(st, 0)) { + symtable_exit_block(st); + return 0; + } + /* Visit iteration variable target, and mark them as such */ + st->st_cur->ste_comp_iter_target = 1; + VISIT(st, expr, outermost->target); + st->st_cur->ste_comp_iter_target = 0; + /* Visit the rest of the comprehension body */ + VISIT_SEQ(st, expr, outermost->ifs); + VISIT_SEQ_TAIL(st, comprehension, generators, 1); + if (value) + VISIT(st, expr, value); + VISIT(st, expr, elt); + st->st_cur->ste_generator = is_generator; + int is_async = st->st_cur->ste_coroutine && !is_generator; + if (!symtable_exit_block(st)) { + return 0; + } + if (is_async) { + st->st_cur->ste_coroutine = 1; + } + return 1; +} + +static int +symtable_visit_genexp(struct symtable *st, expr_ty e) +{ + return symtable_handle_comprehension(st, e, &_Py_ID(genexpr), + e->v.GeneratorExp.generators, + e->v.GeneratorExp.elt, NULL); +} + +static int +symtable_visit_listcomp(struct symtable *st, expr_ty e) +{ + return symtable_handle_comprehension(st, e, &_Py_ID(listcomp), + e->v.ListComp.generators, + e->v.ListComp.elt, NULL); +} + +static int +symtable_visit_setcomp(struct symtable *st, expr_ty e) +{ + return symtable_handle_comprehension(st, e, &_Py_ID(setcomp), + e->v.SetComp.generators, + e->v.SetComp.elt, NULL); +} + +static int +symtable_visit_dictcomp(struct symtable *st, expr_ty e) +{ + return symtable_handle_comprehension(st, e, &_Py_ID(dictcomp), + e->v.DictComp.generators, + e->v.DictComp.key, + e->v.DictComp.value); +} + +static int +symtable_raise_if_annotation_block(struct symtable *st, const char *name, expr_ty e) +{ + if (st->st_cur->ste_type != AnnotationBlock) { + return 1; + } + + PyErr_Format(PyExc_SyntaxError, ANNOTATION_NOT_ALLOWED, name); + PyErr_RangedSyntaxLocationObject(st->st_filename, + e->lineno, + e->col_offset + 1, + e->end_lineno, + e->end_col_offset + 1); + return 0; +} + +static int +symtable_raise_if_comprehension_block(struct symtable *st, expr_ty e) { + _Py_comprehension_ty type = st->st_cur->ste_comprehension; + PyErr_SetString(PyExc_SyntaxError, + (type == ListComprehension) ? "'yield' inside list comprehension" : + (type == SetComprehension) ? "'yield' inside set comprehension" : + (type == DictComprehension) ? "'yield' inside dict comprehension" : + "'yield' inside generator expression"); + PyErr_RangedSyntaxLocationObject(st->st_filename, + e->lineno, e->col_offset + 1, + e->end_lineno, e->end_col_offset + 1); + VISIT_QUIT(st, 0); +} + +struct symtable * +_Py_SymtableStringObjectFlags(const char *str, PyObject *filename, + int start, PyCompilerFlags *flags) +{ + struct symtable *st; + mod_ty mod; + PyArena *arena; + + arena = _PyArena_New(); + if (arena == NULL) + return NULL; + + mod = _PyParser_ASTFromString(str, filename, start, flags, arena); + if (mod == NULL) { + _PyArena_Free(arena); + return NULL; + } + PyFutureFeatures *future = _PyFuture_FromAST(mod, filename); + if (future == NULL) { + _PyArena_Free(arena); + return NULL; + } + future->ff_features |= flags->cf_flags; + st = _PySymtable_Build(mod, filename, future); + PyObject_Free((void *)future); + _PyArena_Free(arena); + return st; +} |