| /* | |
| * This file compiles an abstract syntax tree (AST) into Python bytecode. | |
| * | |
| * The primary entry point is _PyAST_Compile(), which returns a | |
| * PyCodeObject. The compiler makes several passes to build the code | |
| * object: | |
| * 1. Checks for future statements. See future.c | |
| * 2. Builds a symbol table. See symtable.c. | |
| * 3. Generate an instruction sequence. See compiler_mod() in this file, which | |
| * calls functions from codegen.c. | |
| * 4. Generate a control flow graph and run optimizations on it. See flowgraph.c. | |
| * 5. Assemble the basic blocks into final code. See optimize_and_assemble() in | |
| * this file, and assembler.c. | |
| * | |
| */ | |
| #include "Python.h" | |
| #include "pycore_ast.h" // PyAST_Check() | |
| #include "pycore_code.h" | |
| #include "pycore_compile.h" | |
| #include "pycore_flowgraph.h" // _PyCfg_FromInstructionSequence() | |
| #include "pycore_pystate.h" // _Py_GetConfig() | |
| #include "pycore_runtime.h" // _Py_ID() | |
| #include "pycore_setobject.h" // _PySet_NextEntry() | |
| #include "pycore_stats.h" | |
| #include "pycore_tuple.h" // _PyTuple_FromPair | |
| #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString() | |
| #include "cpython/code.h" | |
| #include <stdbool.h> | |
| #undef SUCCESS | |
| #undef ERROR | |
| #define SUCCESS 0 | |
| #define ERROR -1 | |
| #define RETURN_IF_ERROR(X) \ | |
| do { \ | |
| if ((X) == -1) { \ | |
| return ERROR; \ | |
| } \ | |
| } while (0) | |
| typedef _Py_SourceLocation location; | |
| typedef _PyJumpTargetLabel jump_target_label; | |
| typedef _PyInstructionSequence instr_sequence; | |
| typedef struct _PyCfgBuilder cfg_builder; | |
| typedef _PyCompile_FBlockInfo fblockinfo; | |
| typedef enum _PyCompile_FBlockType fblocktype; | |
| /* The following items change on entry and exit of code blocks. | |
| They must be saved and restored when returning to a block. | |
| */ | |
| struct compiler_unit { | |
| PySTEntryObject *u_ste; | |
| int u_scope_type; | |
| PyObject *u_private; /* for private name mangling */ | |
| PyObject *u_static_attributes; /* for class: attributes accessed via self.X */ | |
| PyObject *u_deferred_annotations; /* AnnAssign nodes deferred to the end of compilation */ | |
| PyObject *u_conditional_annotation_indices; /* indices of annotations that are conditionally executed (or -1 for unconditional annotations) */ | |
| long u_next_conditional_annotation_index; /* index of the next conditional annotation */ | |
| instr_sequence *u_instr_sequence; /* codegen output */ | |
| instr_sequence *u_stashed_instr_sequence; /* temporarily stashed parent instruction sequence */ | |
| int u_nfblocks; | |
| int u_in_conditional_block; | |
| _PyCompile_FBlockInfo u_fblock[CO_MAXBLOCKS]; | |
| _PyCompile_CodeUnitMetadata u_metadata; | |
| }; | |
| /* This struct captures the global state of a compilation. | |
| The u pointer points to the current compilation unit, while units | |
| for enclosing blocks are stored in c_stack. The u and c_stack are | |
| managed by _PyCompile_EnterScope() and _PyCompile_ExitScope(). | |
| Note that we don't track recursion levels during compilation - the | |
| task of detecting and rejecting excessive levels of nesting is | |
| handled by the symbol analysis pass. | |
| */ | |
| typedef struct _PyCompiler { | |
| PyObject *c_filename; | |
| struct symtable *c_st; | |
| _PyFutureFeatures c_future; /* module's __future__ */ | |
| PyCompilerFlags c_flags; | |
| int c_optimize; /* optimization level */ | |
| int c_interactive; /* true if in interactive mode */ | |
| PyObject *c_const_cache; /* Python dict holding all constants, | |
| including names tuple */ | |
| struct compiler_unit *u; /* compiler state for current block */ | |
| PyObject *c_stack; /* Python list holding compiler_unit ptrs */ | |
| bool c_save_nested_seqs; /* if true, construct recursive instruction sequences | |
| * (including instructions for nested code objects) | |
| */ | |
| int c_disable_warning; | |
| PyObject *c_module; | |
| } compiler; | |
| static int | |
| compiler_setup(compiler *c, mod_ty mod, PyObject *filename, | |
| PyCompilerFlags *flags, int optimize, PyArena *arena, | |
| PyObject *module) | |
| { | |
| PyCompilerFlags local_flags = _PyCompilerFlags_INIT; | |
| c->c_const_cache = PyDict_New(); | |
| if (!c->c_const_cache) { | |
| return ERROR; | |
| } | |
| c->c_stack = PyList_New(0); | |
| if (!c->c_stack) { | |
| return ERROR; | |
| } | |
| c->c_filename = Py_NewRef(filename); | |
| if (!_PyFuture_FromAST(mod, filename, &c->c_future)) { | |
| return ERROR; | |
| } | |
| c->c_module = Py_XNewRef(module); | |
| if (!flags) { | |
| flags = &local_flags; | |
| } | |
| int merged = c->c_future.ff_features | flags->cf_flags; | |
| c->c_future.ff_features = merged; | |
| flags->cf_flags = merged; | |
| c->c_flags = *flags; | |
| c->c_optimize = (optimize == -1) ? _Py_GetConfig()->optimization_level : optimize; | |
| c->c_save_nested_seqs = false; | |
| if (!_PyAST_Preprocess(mod, arena, filename, c->c_optimize, merged, | |
| 0, 1, module)) | |
| { | |
| return ERROR; | |
| } | |
| c->c_st = _PySymtable_Build(mod, filename, &c->c_future); | |
| if (c->c_st == NULL) { | |
| if (!PyErr_Occurred()) { | |
| PyErr_SetString(PyExc_SystemError, "no symtable"); | |
| } | |
| return ERROR; | |
| } | |
| return SUCCESS; | |
| } | |
| static void | |
| compiler_free(compiler *c) | |
| { | |
| if (c->c_st) { | |
| _PySymtable_Free(c->c_st); | |
| } | |
| Py_XDECREF(c->c_filename); | |
| Py_XDECREF(c->c_module); | |
| Py_XDECREF(c->c_const_cache); | |
| Py_XDECREF(c->c_stack); | |
| PyMem_Free(c); | |
| } | |
| static compiler* | |
| new_compiler(mod_ty mod, PyObject *filename, PyCompilerFlags *pflags, | |
| int optimize, PyArena *arena, PyObject *module) | |
| { | |
| compiler *c = PyMem_Calloc(1, sizeof(compiler)); | |
| if (c == NULL) { | |
| PyErr_NoMemory(); | |
| return NULL; | |
| } | |
| if (compiler_setup(c, mod, filename, pflags, optimize, arena, module) < 0) { | |
| compiler_free(c); | |
| return NULL; | |
| } | |
| return c; | |
| } | |
| static void | |
| compiler_unit_free(struct compiler_unit *u) | |
| { | |
| Py_CLEAR(u->u_instr_sequence); | |
| Py_CLEAR(u->u_stashed_instr_sequence); | |
| Py_CLEAR(u->u_ste); | |
| Py_CLEAR(u->u_metadata.u_name); | |
| Py_CLEAR(u->u_metadata.u_qualname); | |
| Py_CLEAR(u->u_metadata.u_consts); | |
| Py_CLEAR(u->u_metadata.u_names); | |
| Py_CLEAR(u->u_metadata.u_varnames); | |
| Py_CLEAR(u->u_metadata.u_freevars); | |
| Py_CLEAR(u->u_metadata.u_cellvars); | |
| Py_CLEAR(u->u_metadata.u_fasthidden); | |
| Py_CLEAR(u->u_private); | |
| Py_CLEAR(u->u_static_attributes); | |
| Py_CLEAR(u->u_deferred_annotations); | |
| Py_CLEAR(u->u_conditional_annotation_indices); | |
| PyMem_Free(u); | |
| } | |
| #define CAPSULE_NAME "compile.c compiler unit" | |
| int | |
| _PyCompile_MaybeAddStaticAttributeToClass(compiler *c, expr_ty e) | |
| { | |
| assert(e->kind == Attribute_kind); | |
| expr_ty attr_value = e->v.Attribute.value; | |
| if (attr_value->kind != Name_kind || | |
| e->v.Attribute.ctx != Store || | |
| !_PyUnicode_EqualToASCIIString(attr_value->v.Name.id, "self")) | |
| { | |
| return SUCCESS; | |
| } | |
| Py_ssize_t stack_size = PyList_GET_SIZE(c->c_stack); | |
| for (Py_ssize_t i = stack_size - 1; i >= 0; i--) { | |
| PyObject *capsule = PyList_GET_ITEM(c->c_stack, i); | |
| struct compiler_unit *u = (struct compiler_unit *)PyCapsule_GetPointer( | |
| capsule, CAPSULE_NAME); | |
| assert(u); | |
| if (u->u_scope_type == COMPILE_SCOPE_CLASS) { | |
| assert(u->u_static_attributes); | |
| RETURN_IF_ERROR(PySet_Add(u->u_static_attributes, e->v.Attribute.attr)); | |
| break; | |
| } | |
| } | |
| return SUCCESS; | |
| } | |
| int | |
| _PyCompile_SetQualname(compiler *c) | |
| { | |
| Py_ssize_t stack_size; | |
| struct compiler_unit *u = c->u; | |
| PyObject *name, *base; | |
| if (u->u_scope_type == COMPILE_SCOPE_MODULE) { | |
| return SUCCESS; | |
| } | |
| base = NULL; | |
| stack_size = PyList_GET_SIZE(c->c_stack); | |
| assert(stack_size >= 1); | |
| if (stack_size > 1) { | |
| int scope, force_global = 0; | |
| struct compiler_unit *parent; | |
| PyObject *mangled, *capsule; | |
| capsule = PyList_GET_ITEM(c->c_stack, stack_size - 1); | |
| parent = (struct compiler_unit *)PyCapsule_GetPointer(capsule, CAPSULE_NAME); | |
| assert(parent); | |
| if (parent->u_scope_type == COMPILE_SCOPE_ANNOTATIONS) { | |
| /* The parent is an annotation scope, so we need to | |
| look at the grandparent. */ | |
| if (stack_size == 2) { | |
| // If we're immediately within the module, we can skip | |
| // the rest and just set the qualname to be the same as name. | |
| u->u_metadata.u_qualname = Py_NewRef(u->u_metadata.u_name); | |
| return SUCCESS; | |
| } | |
| capsule = PyList_GET_ITEM(c->c_stack, stack_size - 2); | |
| parent = (struct compiler_unit *)PyCapsule_GetPointer(capsule, CAPSULE_NAME); | |
| assert(parent); | |
| } | |
| if (u->u_scope_type == COMPILE_SCOPE_FUNCTION | |
| || u->u_scope_type == COMPILE_SCOPE_ASYNC_FUNCTION | |
| || u->u_scope_type == COMPILE_SCOPE_CLASS) { | |
| assert(u->u_metadata.u_name); | |
| mangled = _Py_Mangle(parent->u_private, u->u_metadata.u_name); | |
| if (!mangled) { | |
| return ERROR; | |
| } | |
| scope = _PyST_GetScope(parent->u_ste, mangled); | |
| Py_DECREF(mangled); | |
| RETURN_IF_ERROR(scope); | |
| assert(scope != GLOBAL_IMPLICIT); | |
| if (scope == GLOBAL_EXPLICIT) | |
| force_global = 1; | |
| } | |
| if (!force_global) { | |
| if (parent->u_scope_type == COMPILE_SCOPE_FUNCTION | |
| || parent->u_scope_type == COMPILE_SCOPE_ASYNC_FUNCTION | |
| || parent->u_scope_type == COMPILE_SCOPE_LAMBDA) | |
| { | |
| _Py_DECLARE_STR(dot_locals, ".<locals>"); | |
| base = PyUnicode_Concat(parent->u_metadata.u_qualname, | |
| &_Py_STR(dot_locals)); | |
| if (base == NULL) { | |
| return ERROR; | |
| } | |
| } | |
| else { | |
| base = Py_NewRef(parent->u_metadata.u_qualname); | |
| } | |
| } | |
| if (u->u_ste->ste_function_name != NULL) { | |
| PyObject *tmp = base; | |
| base = PyUnicode_FromFormat("%U.%U", | |
| base, | |
| u->u_ste->ste_function_name); | |
| Py_DECREF(tmp); | |
| if (base == NULL) { | |
| return ERROR; | |
| } | |
| } | |
| } | |
| else if (u->u_ste->ste_function_name != NULL) { | |
| base = Py_NewRef(u->u_ste->ste_function_name); | |
| } | |
| if (base != NULL) { | |
| name = PyUnicode_Concat(base, _Py_LATIN1_CHR('.')); | |
| Py_DECREF(base); | |
| if (name == NULL) { | |
| return ERROR; | |
| } | |
| PyUnicode_Append(&name, u->u_metadata.u_name); | |
| if (name == NULL) { | |
| return ERROR; | |
| } | |
| } | |
| else { | |
| name = Py_NewRef(u->u_metadata.u_name); | |
| } | |
| // The qualified name is interned when the code object is created, | |
| // whatever its content. The body of a class also has it as a constant, | |
| // and intern_final_constants() only interns the constants selected by | |
| // _PyCode_ShouldInternString(). Intern the other qualified names now, | |
| // so that the same object is always used for both. | |
| if (!_PyCode_ShouldInternString(name)) { | |
| _PyUnicode_InternMortal(_PyInterpreterState_GET(), &name); | |
| } | |
| u->u_metadata.u_qualname = name; | |
| return SUCCESS; | |
| } | |
| /* Merge const *o* and return constant key object. | |
| * If recursive, insert all elements if o is a tuple or frozen set. | |
| */ | |
| static PyObject* | |
| const_cache_insert(PyObject *const_cache, PyObject *o, bool recursive) | |
| { | |
| assert(PyDict_CheckExact(const_cache)); | |
| // None and Ellipsis are immortal objects, and key is the singleton. | |
| // No need to merge object and key. | |
| if (o == Py_None || o == Py_Ellipsis) { | |
| return o; | |
| } | |
| PyObject *key = _PyCode_ConstantKey(o); | |
| if (key == NULL) { | |
| return NULL; | |
| } | |
| PyObject *t; | |
| int res = PyDict_SetDefaultRef(const_cache, key, key, &t); | |
| if (res != 0) { | |
| // o was not inserted into const_cache. t is either the existing value | |
| // or NULL (on error). | |
| Py_DECREF(key); | |
| return t; | |
| } | |
| Py_DECREF(t); | |
| if (!recursive) { | |
| return key; | |
| } | |
| // We registered o in const_cache. | |
| // When o is a tuple or frozenset, we want to merge its | |
| // items too. | |
| if (PyTuple_CheckExact(o)) { | |
| Py_ssize_t len = PyTuple_GET_SIZE(o); | |
| for (Py_ssize_t i = 0; i < len; i++) { | |
| PyObject *item = PyTuple_GET_ITEM(o, i); | |
| PyObject *u = const_cache_insert(const_cache, item, recursive); | |
| if (u == NULL) { | |
| Py_DECREF(key); | |
| return NULL; | |
| } | |
| // See _PyCode_ConstantKey() | |
| PyObject *v; // borrowed | |
| if (PyTuple_CheckExact(u)) { | |
| v = PyTuple_GET_ITEM(u, 1); | |
| } | |
| else { | |
| v = u; | |
| } | |
| if (v != item) { | |
| PyTuple_SET_ITEM(o, i, Py_NewRef(v)); | |
| Py_DECREF(item); | |
| } | |
| Py_DECREF(u); | |
| } | |
| } | |
| else if (PyFrozenSet_CheckExact(o)) { | |
| // *key* is tuple. And its first item is frozenset of | |
| // constant keys. | |
| // See _PyCode_ConstantKey() for detail. | |
| assert(PyTuple_CheckExact(key)); | |
| assert(PyTuple_GET_SIZE(key) == 2); | |
| Py_ssize_t len = PySet_GET_SIZE(o); | |
| if (len == 0) { // empty frozenset should not be re-created. | |
| return key; | |
| } | |
| PyObject *tuple = PyTuple_New(len); | |
| if (tuple == NULL) { | |
| Py_DECREF(key); | |
| return NULL; | |
| } | |
| Py_ssize_t i = 0, pos = 0; | |
| PyObject *item; | |
| Py_hash_t hash; | |
| while (_PySet_NextEntry(o, &pos, &item, &hash)) { | |
| PyObject *k = const_cache_insert(const_cache, item, recursive); | |
| if (k == NULL) { | |
| Py_DECREF(tuple); | |
| Py_DECREF(key); | |
| return NULL; | |
| } | |
| PyObject *u; | |
| if (PyTuple_CheckExact(k)) { | |
| u = Py_NewRef(PyTuple_GET_ITEM(k, 1)); | |
| Py_DECREF(k); | |
| } | |
| else { | |
| u = k; | |
| } | |
| PyTuple_SET_ITEM(tuple, i, u); // Steals reference of u. | |
| i++; | |
| } | |
| // Instead of rewriting o, we create new frozenset and embed in the | |
| // key tuple. Caller should get merged frozenset from the key tuple. | |
| PyObject *new = PyFrozenSet_New(tuple); | |
| Py_DECREF(tuple); | |
| if (new == NULL) { | |
| Py_DECREF(key); | |
| return NULL; | |
| } | |
| assert(PyTuple_GET_ITEM(key, 1) == o); | |
| Py_DECREF(o); | |
| PyTuple_SET_ITEM(key, 1, new); | |
| } | |
| return key; | |
| } | |
| static PyObject* | |
| merge_consts_recursive(PyObject *const_cache, PyObject *o) | |
| { | |
| return const_cache_insert(const_cache, o, true); | |
| } | |
| Py_ssize_t | |
| _PyCompile_DictAddObj(PyObject *dict, PyObject *o) | |
| { | |
| PyObject *v; | |
| Py_ssize_t arg; | |
| if (PyDict_GetItemRef(dict, o, &v) < 0) { | |
| return ERROR; | |
| } | |
| if (!v) { | |
| arg = PyDict_GET_SIZE(dict); | |
| v = PyLong_FromSsize_t(arg); | |
| if (!v) { | |
| return ERROR; | |
| } | |
| if (PyDict_SetItem(dict, o, v) < 0) { | |
| Py_DECREF(v); | |
| return ERROR; | |
| } | |
| } | |
| else | |
| arg = PyLong_AsLong(v); | |
| Py_DECREF(v); | |
| return arg; | |
| } | |
| Py_ssize_t | |
| _PyCompile_AddConst(compiler *c, PyObject *o) | |
| { | |
| PyObject *key = merge_consts_recursive(c->c_const_cache, o); | |
| if (key == NULL) { | |
| return ERROR; | |
| } | |
| Py_ssize_t arg = _PyCompile_DictAddObj(c->u->u_metadata.u_consts, key); | |
| Py_DECREF(key); | |
| return arg; | |
| } | |
| static PyObject * | |
| list2dict(PyObject *list) | |
| { | |
| Py_ssize_t i, n; | |
| PyObject *v, *k; | |
| PyObject *dict = PyDict_New(); | |
| if (!dict) return NULL; | |
| n = PyList_Size(list); | |
| for (i = 0; i < n; i++) { | |
| v = PyLong_FromSsize_t(i); | |
| if (!v) { | |
| Py_DECREF(dict); | |
| return NULL; | |
| } | |
| k = PyList_GET_ITEM(list, i); | |
| if (PyDict_SetItem(dict, k, v) < 0) { | |
| Py_DECREF(v); | |
| Py_DECREF(dict); | |
| return NULL; | |
| } | |
| Py_DECREF(v); | |
| } | |
| return dict; | |
| } | |
| /* Return new dict containing names from src that match scope(s). | |
| src is a symbol table dictionary. If the scope of a name matches | |
| either scope_type or flag is set, insert it into the new dict. The | |
| values are integers, starting at offset and increasing by one for | |
| each key. | |
| */ | |
| static PyObject * | |
| dictbytype(PyObject *src, int scope_type, int flag, Py_ssize_t offset) | |
| { | |
| Py_ssize_t i = offset, num_keys, key_i; | |
| PyObject *k, *v, *dest = PyDict_New(); | |
| PyObject *sorted_keys; | |
| assert(offset >= 0); | |
| if (dest == NULL) | |
| return NULL; | |
| /* Sort the keys so that we have a deterministic order on the indexes | |
| saved in the returned dictionary. These indexes are used as indexes | |
| into the free and cell var storage. Therefore if they aren't | |
| deterministic, then the generated bytecode is not deterministic. | |
| */ | |
| sorted_keys = PyDict_Keys(src); | |
| if (sorted_keys == NULL) { | |
| Py_DECREF(dest); | |
| return NULL; | |
| } | |
| if (PyList_Sort(sorted_keys) != 0) { | |
| Py_DECREF(sorted_keys); | |
| Py_DECREF(dest); | |
| return NULL; | |
| } | |
| num_keys = PyList_GET_SIZE(sorted_keys); | |
| for (key_i = 0; key_i < num_keys; key_i++) { | |
| k = PyList_GET_ITEM(sorted_keys, key_i); | |
| v = PyDict_GetItemWithError(src, k); | |
| if (!v) { | |
| if (!PyErr_Occurred()) { | |
| PyErr_SetObject(PyExc_KeyError, k); | |
| } | |
| Py_DECREF(sorted_keys); | |
| Py_DECREF(dest); | |
| return NULL; | |
| } | |
| long vi = PyLong_AsLong(v); | |
| if (vi == -1 && PyErr_Occurred()) { | |
| Py_DECREF(sorted_keys); | |
| Py_DECREF(dest); | |
| return NULL; | |
| } | |
| if (SYMBOL_TO_SCOPE(vi) == scope_type || vi & flag) { | |
| PyObject *item = PyLong_FromSsize_t(i); | |
| if (item == NULL) { | |
| Py_DECREF(sorted_keys); | |
| Py_DECREF(dest); | |
| return NULL; | |
| } | |
| i++; | |
| if (PyDict_SetItem(dest, k, item) < 0) { | |
| Py_DECREF(sorted_keys); | |
| Py_DECREF(item); | |
| Py_DECREF(dest); | |
| return NULL; | |
| } | |
| Py_DECREF(item); | |
| } | |
| } | |
| Py_DECREF(sorted_keys); | |
| return dest; | |
| } | |
| static int | |
| add_cell_names_from_symbols(PyObject *symbols, PyObject *names) | |
| { | |
| Py_ssize_t pos = 0; | |
| PyObject *k, *v; | |
| while (PyDict_Next(symbols, &pos, &k, &v)) { | |
| long flags = PyLong_AsLong(v); | |
| if (flags == -1 && PyErr_Occurred()) { | |
| return ERROR; | |
| } | |
| if (SYMBOL_TO_SCOPE(flags) == CELL) { | |
| if (PySet_Add(names, k) < 0) { | |
| return ERROR; | |
| } | |
| } | |
| } | |
| return SUCCESS; | |
| } | |
| static int | |
| add_inlined_comprehension_cell_names(PySTEntryObject *ste, PyObject *names) | |
| { | |
| for (Py_ssize_t i = 0; i < PyList_GET_SIZE(ste->ste_children); i++) { | |
| PySTEntryObject *child = | |
| (PySTEntryObject *)PyList_GET_ITEM(ste->ste_children, i); | |
| if (child->ste_type != InlinedComprehensionBlock) { | |
| continue; | |
| } | |
| if (add_cell_names_from_symbols(child->ste_symbols, names) < 0) { | |
| return ERROR; | |
| } | |
| if (add_inlined_comprehension_cell_names(child, names) < 0) { | |
| return ERROR; | |
| } | |
| } | |
| return SUCCESS; | |
| } | |
| /* Cells of the shared unit: this table's CELL names, plus cells that live | |
| * only on inlined comprehension children. */ | |
| static PyObject * | |
| compiler_cellvars(PySTEntryObject *ste) | |
| { | |
| PyObject *names = PySet_New(NULL); | |
| if (names == NULL) { | |
| return NULL; | |
| } | |
| if (add_cell_names_from_symbols(ste->ste_symbols, names) < 0) { | |
| Py_DECREF(names); | |
| return NULL; | |
| } | |
| if (add_inlined_comprehension_cell_names(ste, names) < 0) { | |
| Py_DECREF(names); | |
| return NULL; | |
| } | |
| PyObject *sorted = PySequence_List(names); | |
| Py_DECREF(names); | |
| if (sorted == NULL) { | |
| return NULL; | |
| } | |
| if (PyList_Sort(sorted) < 0) { | |
| Py_DECREF(sorted); | |
| return NULL; | |
| } | |
| PyObject *cellvars = list2dict(sorted); | |
| Py_DECREF(sorted); | |
| return cellvars; | |
| } | |
| int | |
| _PyCompile_EnterScope(compiler *c, identifier name, int scope_type, | |
| void *key, int lineno, PyObject *private, | |
| _PyCompile_CodeUnitMetadata *umd) | |
| { | |
| struct compiler_unit *u; | |
| u = (struct compiler_unit *)PyMem_Calloc(1, sizeof(struct compiler_unit)); | |
| if (!u) { | |
| PyErr_NoMemory(); | |
| return ERROR; | |
| } | |
| u->u_scope_type = scope_type; | |
| if (umd != NULL) { | |
| u->u_metadata = *umd; | |
| } | |
| else { | |
| u->u_metadata.u_argcount = 0; | |
| u->u_metadata.u_posonlyargcount = 0; | |
| u->u_metadata.u_kwonlyargcount = 0; | |
| } | |
| u->u_ste = _PySymtable_Lookup(c->c_st, key); | |
| if (!u->u_ste) { | |
| compiler_unit_free(u); | |
| return ERROR; | |
| } | |
| u->u_metadata.u_name = Py_NewRef(name); | |
| u->u_metadata.u_varnames = list2dict(u->u_ste->ste_varnames); | |
| if (!u->u_metadata.u_varnames) { | |
| compiler_unit_free(u); | |
| return ERROR; | |
| } | |
| u->u_metadata.u_cellvars = compiler_cellvars(u->u_ste); | |
| if (!u->u_metadata.u_cellvars) { | |
| compiler_unit_free(u); | |
| return ERROR; | |
| } | |
| if (u->u_ste->ste_needs_class_closure) { | |
| /* Cook up an implicit __class__ cell. */ | |
| Py_ssize_t res; | |
| assert(u->u_scope_type == COMPILE_SCOPE_CLASS); | |
| res = _PyCompile_DictAddObj(u->u_metadata.u_cellvars, &_Py_ID(__class__)); | |
| if (res < 0) { | |
| compiler_unit_free(u); | |
| return ERROR; | |
| } | |
| } | |
| if (u->u_ste->ste_needs_classdict) { | |
| /* Cook up an implicit __classdict__ cell. */ | |
| Py_ssize_t res; | |
| assert(u->u_scope_type == COMPILE_SCOPE_CLASS); | |
| res = _PyCompile_DictAddObj(u->u_metadata.u_cellvars, &_Py_ID(__classdict__)); | |
| if (res < 0) { | |
| compiler_unit_free(u); | |
| return ERROR; | |
| } | |
| } | |
| if (u->u_ste->ste_has_conditional_annotations) { | |
| /* Cook up an implicit __conditional_annotations__ cell */ | |
| Py_ssize_t res; | |
| assert(u->u_scope_type == COMPILE_SCOPE_CLASS || u->u_scope_type == COMPILE_SCOPE_MODULE); | |
| res = _PyCompile_DictAddObj(u->u_metadata.u_cellvars, &_Py_ID(__conditional_annotations__)); | |
| if (res < 0) { | |
| compiler_unit_free(u); | |
| return ERROR; | |
| } | |
| } | |
| u->u_metadata.u_freevars = dictbytype(u->u_ste->ste_symbols, FREE, DEF_FREE_CLASS, | |
| PyDict_GET_SIZE(u->u_metadata.u_cellvars)); | |
| if (!u->u_metadata.u_freevars) { | |
| compiler_unit_free(u); | |
| return ERROR; | |
| } | |
| if (scope_type == COMPILE_SCOPE_MODULE || scope_type == COMPILE_SCOPE_CLASS) { | |
| u->u_metadata.u_fasthidden = PySet_New(NULL); | |
| if (!u->u_metadata.u_fasthidden) { | |
| compiler_unit_free(u); | |
| return ERROR; | |
| } | |
| } | |
| else { | |
| u->u_metadata.u_fasthidden = NULL; | |
| } | |
| u->u_nfblocks = 0; | |
| u->u_metadata.u_firstlineno = lineno; | |
| u->u_metadata.u_consts = PyDict_New(); | |
| if (!u->u_metadata.u_consts) { | |
| compiler_unit_free(u); | |
| return ERROR; | |
| } | |
| u->u_metadata.u_names = PyDict_New(); | |
| if (!u->u_metadata.u_names) { | |
| compiler_unit_free(u); | |
| return ERROR; | |
| } | |
| u->u_deferred_annotations = NULL; | |
| u->u_conditional_annotation_indices = NULL; | |
| u->u_next_conditional_annotation_index = 0; | |
| if (scope_type == COMPILE_SCOPE_CLASS) { | |
| u->u_static_attributes = PySet_New(0); | |
| if (!u->u_static_attributes) { | |
| compiler_unit_free(u); | |
| return ERROR; | |
| } | |
| } | |
| else { | |
| u->u_static_attributes = NULL; | |
| } | |
| u->u_instr_sequence = (instr_sequence*)_PyInstructionSequence_New(); | |
| if (!u->u_instr_sequence) { | |
| compiler_unit_free(u); | |
| return ERROR; | |
| } | |
| u->u_stashed_instr_sequence = NULL; | |
| /* Push the old compiler_unit on the stack. */ | |
| if (c->u) { | |
| PyObject *capsule = PyCapsule_New(c->u, CAPSULE_NAME, NULL); | |
| if (!capsule || PyList_Append(c->c_stack, capsule) < 0) { | |
| Py_XDECREF(capsule); | |
| compiler_unit_free(u); | |
| return ERROR; | |
| } | |
| Py_DECREF(capsule); | |
| if (private == NULL) { | |
| private = c->u->u_private; | |
| } | |
| } | |
| u->u_private = Py_XNewRef(private); | |
| c->u = u; | |
| return SUCCESS; | |
| } | |
| void | |
| _PyCompile_ExitScope(compiler *c) | |
| { | |
| // Don't call PySequence_DelItem() with an exception raised | |
| PyObject *exc = PyErr_GetRaisedException(); | |
| instr_sequence *nested_seq = NULL; | |
| if (c->c_save_nested_seqs) { | |
| nested_seq = c->u->u_instr_sequence; | |
| Py_INCREF(nested_seq); | |
| } | |
| compiler_unit_free(c->u); | |
| /* Restore c->u to the parent unit. */ | |
| Py_ssize_t n = PyList_GET_SIZE(c->c_stack) - 1; | |
| if (n >= 0) { | |
| PyObject *capsule = PyList_GET_ITEM(c->c_stack, n); | |
| c->u = (struct compiler_unit *)PyCapsule_GetPointer(capsule, CAPSULE_NAME); | |
| assert(c->u); | |
| /* we are deleting from a list so this really shouldn't fail */ | |
| if (PySequence_DelItem(c->c_stack, n) < 0) { | |
| PyErr_FormatUnraisable("Exception ignored while removing " | |
| "the last compiler stack item"); | |
| } | |
| if (nested_seq != NULL) { | |
| if (_PyInstructionSequence_AddNested(c->u->u_instr_sequence, nested_seq) < 0) { | |
| PyErr_FormatUnraisable("Exception ignored while appending " | |
| "nested instruction sequence"); | |
| } | |
| } | |
| } | |
| else { | |
| c->u = NULL; | |
| } | |
| Py_XDECREF(nested_seq); | |
| PyErr_SetRaisedException(exc); | |
| } | |
| /* | |
| * Frame block handling functions | |
| */ | |
| int | |
| _PyCompile_PushFBlock(compiler *c, location loc, | |
| fblocktype t, jump_target_label block_label, | |
| jump_target_label exit, void *datum) | |
| { | |
| fblockinfo *f; | |
| if (c->u->u_nfblocks >= CO_MAXBLOCKS) { | |
| return _PyCompile_Error(c, loc, "too many statically nested blocks"); | |
| } | |
| f = &c->u->u_fblock[c->u->u_nfblocks++]; | |
| f->fb_type = t; | |
| f->fb_block = block_label; | |
| f->fb_loc = loc; | |
| f->fb_exit = exit; | |
| f->fb_datum = datum; | |
| if (t == COMPILE_FBLOCK_FINALLY_END) { | |
| c->c_disable_warning++; | |
| } | |
| return SUCCESS; | |
| } | |
| void | |
| _PyCompile_PopFBlock(compiler *c, fblocktype t, jump_target_label block_label) | |
| { | |
| struct compiler_unit *u = c->u; | |
| assert(u->u_nfblocks > 0); | |
| u->u_nfblocks--; | |
| assert(u->u_fblock[u->u_nfblocks].fb_type == t); | |
| assert(SAME_JUMP_TARGET_LABEL(u->u_fblock[u->u_nfblocks].fb_block, block_label)); | |
| if (t == COMPILE_FBLOCK_FINALLY_END) { | |
| c->c_disable_warning--; | |
| } | |
| } | |
| fblockinfo * | |
| _PyCompile_TopFBlock(compiler *c) | |
| { | |
| if (c->u->u_nfblocks == 0) { | |
| return NULL; | |
| } | |
| return &c->u->u_fblock[c->u->u_nfblocks - 1]; | |
| } | |
| bool | |
| _PyCompile_InExceptionHandler(compiler *c) | |
| { | |
| for (Py_ssize_t i = 0; i < c->u->u_nfblocks; i++) { | |
| fblockinfo *block = &c->u->u_fblock[i]; | |
| switch (block->fb_type) { | |
| case COMPILE_FBLOCK_TRY_EXCEPT: | |
| case COMPILE_FBLOCK_FINALLY_TRY: | |
| case COMPILE_FBLOCK_FINALLY_END: | |
| case COMPILE_FBLOCK_EXCEPTION_HANDLER: | |
| case COMPILE_FBLOCK_EXCEPTION_GROUP_HANDLER: | |
| case COMPILE_FBLOCK_HANDLER_CLEANUP: | |
| return true; | |
| default: | |
| break; | |
| } | |
| } | |
| return false; | |
| } | |
| void | |
| _PyCompile_DeferredAnnotations(compiler *c, | |
| PyObject **deferred_annotations, | |
| PyObject **conditional_annotation_indices) | |
| { | |
| *deferred_annotations = Py_XNewRef(c->u->u_deferred_annotations); | |
| *conditional_annotation_indices = Py_XNewRef(c->u->u_conditional_annotation_indices); | |
| } | |
| static location | |
| start_location(asdl_stmt_seq *stmts) | |
| { | |
| if (asdl_seq_LEN(stmts) > 0) { | |
| /* Set current line number to the line number of first statement. | |
| * This way line number for SETUP_ANNOTATIONS will always | |
| * coincide with the line number of first "real" statement in module. | |
| * If body is empty, then lineno will be set later in the assembly stage. | |
| */ | |
| stmt_ty st = (stmt_ty)asdl_seq_GET(stmts, 0); | |
| return SRC_LOCATION_FROM_AST(st); | |
| } | |
| return (const _Py_SourceLocation){1, 1, 0, 0}; | |
| } | |
| static int | |
| compiler_codegen(compiler *c, mod_ty mod) | |
| { | |
| RETURN_IF_ERROR(_PyCodegen_EnterAnonymousScope(c, mod)); | |
| assert(c->u->u_scope_type == COMPILE_SCOPE_MODULE); | |
| switch (mod->kind) { | |
| case Module_kind: { | |
| asdl_stmt_seq *stmts = mod->v.Module.body; | |
| RETURN_IF_ERROR(_PyCodegen_Module(c, start_location(stmts), stmts, false)); | |
| break; | |
| } | |
| case Interactive_kind: { | |
| c->c_interactive = 1; | |
| asdl_stmt_seq *stmts = mod->v.Interactive.body; | |
| RETURN_IF_ERROR(_PyCodegen_Module(c, start_location(stmts), stmts, true)); | |
| break; | |
| } | |
| case Expression_kind: { | |
| RETURN_IF_ERROR(_PyCodegen_Expression(c, mod->v.Expression.body)); | |
| break; | |
| } | |
| default: { | |
| PyErr_Format(PyExc_SystemError, | |
| "module kind %d should not be possible", | |
| mod->kind); | |
| return ERROR; | |
| }} | |
| return SUCCESS; | |
| } | |
| static PyCodeObject * | |
| compiler_mod(compiler *c, mod_ty mod) | |
| { | |
| PyCodeObject *co = NULL; | |
| int addNone = mod->kind != Expression_kind; | |
| assert(c->u == NULL); | |
| if (compiler_codegen(c, mod) < 0) { | |
| goto finally; | |
| } | |
| co = _PyCompile_OptimizeAndAssemble(c, addNone); | |
| finally: | |
| if (c->u != NULL) { | |
| _PyCompile_ExitScope(c); | |
| } | |
| return co; | |
| } | |
| /* Inlined comprehensions are compiled in the enclosing unit. If a name is | |
| * FREE in the comprehension, or is absent from its table (scope 0), resolve | |
| * it in enclosing tables until it is bound. Stop if the next table is a class: | |
| * nested scopes (including inlined comprehensions) do not see class locals, so | |
| * the name stays FREE. __class__ and friends are not allowed to be free | |
| * through a class; treat those loads as implicit globals. | |
| * | |
| * Names with no entry (scope 0) include loads synthesized by codegen, such as | |
| * the implicit receiver for zero-arg super(). */ | |
| static int | |
| compiler_resolve_inlined_free(PySTEntryObject **ste, PyObject *name) | |
| { | |
| int scope = _PyST_GetScope(*ste, name); | |
| RETURN_IF_ERROR(scope); | |
| while ((*ste)->ste_type == InlinedComprehensionBlock && | |
| (scope == FREE || scope == 0)) { | |
| PySTEntryObject *parent = (*ste)->ste_parent; | |
| assert(parent != NULL); | |
| if (parent->ste_type == ClassBlock) { | |
| if (_PyST_IsClassClosureName(name)) { | |
| return GLOBAL_IMPLICIT; | |
| } | |
| break; | |
| } | |
| *ste = parent; | |
| scope = _PyST_GetScope(*ste, name); | |
| RETURN_IF_ERROR(scope); | |
| } | |
| return scope; | |
| } | |
| int | |
| _PyCompile_GetRefType(compiler *c, PyObject *name) | |
| { | |
| if (c->u->u_scope_type == COMPILE_SCOPE_CLASS && _PyST_IsClassClosureName(name)) { | |
| return CELL; | |
| } | |
| PySTEntryObject *ste = c->u->u_ste; | |
| int scope = compiler_resolve_inlined_free(&ste, name); | |
| RETURN_IF_ERROR(scope); | |
| if (scope == 0) { | |
| PyErr_Format(PyExc_SystemError, | |
| "_PyST_GetScope(name=%R) failed: " | |
| "unknown scope in unit %S (%R); " | |
| "symbols: %R; locals: %R; " | |
| "globals: %R", | |
| name, | |
| c->u->u_metadata.u_name, ste->ste_id, | |
| ste->ste_symbols, c->u->u_metadata.u_varnames, | |
| c->u->u_metadata.u_names); | |
| return ERROR; | |
| } | |
| return scope; | |
| } | |
| static int | |
| dict_lookup_arg(PyObject *dict, PyObject *name) | |
| { | |
| PyObject *v = PyDict_GetItemWithError(dict, name); | |
| if (v == NULL) { | |
| return ERROR; | |
| } | |
| return PyLong_AsLong(v); | |
| } | |
| int | |
| _PyCompile_LookupCellvar(compiler *c, PyObject *name) | |
| { | |
| assert(c->u->u_metadata.u_cellvars); | |
| return dict_lookup_arg(c->u->u_metadata.u_cellvars, name); | |
| } | |
| int | |
| _PyCompile_LookupArg(compiler *c, PyCodeObject *co, PyObject *name) | |
| { | |
| /* Special case: If a class contains a method with a | |
| * free variable that has the same name as a method, | |
| * the name will be considered free *and* local in the | |
| * class. It should be handled by the closure, as | |
| * well as by the normal name lookup logic. | |
| */ | |
| int reftype = _PyCompile_GetRefType(c, name); | |
| if (reftype == -1) { | |
| return ERROR; | |
| } | |
| int arg; | |
| if (reftype == CELL) { | |
| arg = dict_lookup_arg(c->u->u_metadata.u_cellvars, name); | |
| } | |
| else { | |
| arg = dict_lookup_arg(c->u->u_metadata.u_freevars, name); | |
| } | |
| if (arg == -1 && !PyErr_Occurred()) { | |
| PyObject *freevars = _PyCode_GetFreevars(co); | |
| if (freevars == NULL) { | |
| PyErr_Clear(); | |
| } | |
| PyErr_Format(PyExc_SystemError, | |
| "compiler_lookup_arg(name=%R) with reftype=%d failed in %S; " | |
| "freevars of code %S: %R", | |
| name, | |
| reftype, | |
| c->u->u_metadata.u_name, | |
| co->co_name, | |
| freevars); | |
| Py_XDECREF(freevars); | |
| return ERROR; | |
| } | |
| return arg; | |
| } | |
| PyObject * | |
| _PyCompile_StaticAttributesAsTuple(compiler *c) | |
| { | |
| assert(c->u->u_static_attributes); | |
| PyObject *static_attributes_unsorted = PySequence_List(c->u->u_static_attributes); | |
| if (static_attributes_unsorted == NULL) { | |
| return NULL; | |
| } | |
| if (PyList_Sort(static_attributes_unsorted) != 0) { | |
| Py_DECREF(static_attributes_unsorted); | |
| return NULL; | |
| } | |
| PyObject *static_attributes = PySequence_Tuple(static_attributes_unsorted); | |
| Py_DECREF(static_attributes_unsorted); | |
| return static_attributes; | |
| } | |
| int | |
| _PyCompile_ResolveNameop(compiler *c, PyObject *mangled, | |
| _PyCompile_optype *optype, Py_ssize_t *arg) | |
| { | |
| PyObject *dict = c->u->u_metadata.u_names; | |
| *optype = COMPILE_OP_NAME; | |
| PySTEntryObject *ste = c->u->u_ste; | |
| assert(ste != NULL); | |
| int scope = compiler_resolve_inlined_free(&ste, mangled); | |
| RETURN_IF_ERROR(scope); | |
| switch (scope) { | |
| case FREE: | |
| dict = c->u->u_metadata.u_freevars; | |
| *optype = COMPILE_OP_DEREF; | |
| break; | |
| case CELL: | |
| dict = c->u->u_metadata.u_cellvars; | |
| *optype = COMPILE_OP_DEREF; | |
| break; | |
| case LOCAL: | |
| /* Inlined comprehensions isolate their locals as FAST, even when | |
| * nested in class or module scope. */ | |
| if (_PyST_IsFunctionLike(ste) || ste->ste_type == InlinedComprehensionBlock) { | |
| *optype = COMPILE_OP_FAST; | |
| } | |
| break; | |
| case GLOBAL_IMPLICIT: { | |
| /* Opcode depends on the enclosing non-inlined scope. */ | |
| PySTEntryObject *enclosing = ste; | |
| while (enclosing->ste_parent != NULL) { | |
| assert(enclosing->ste_type == InlinedComprehensionBlock); | |
| enclosing = enclosing->ste_parent; | |
| } | |
| if (_PyST_IsFunctionLike(enclosing)) { | |
| *optype = COMPILE_OP_GLOBAL; | |
| } | |
| break; | |
| } | |
| case GLOBAL_EXPLICIT: | |
| *optype = COMPILE_OP_GLOBAL; | |
| break; | |
| default: | |
| /* scope can be 0 */ | |
| break; | |
| } | |
| /* XXX Handle __doc__ and the like better */ | |
| assert(scope || PyUnicode_READ_CHAR(mangled, 0) == '_'); | |
| if (*optype != COMPILE_OP_FAST) { | |
| *arg = _PyCompile_DictAddObj(dict, mangled); | |
| RETURN_IF_ERROR(*arg); | |
| } | |
| return scope; | |
| } | |
| int | |
| _PyCompile_EnterInlinedComprehensionScope(compiler *c, PySTEntryObject *entry, | |
| _PyCompile_InlinedComprehensionState *state) | |
| { | |
| assert(state->saved_ste == NULL); | |
| state->saved_ste = c->u->u_ste; | |
| c->u->u_ste = (PySTEntryObject *)Py_NewRef(entry); | |
| return SUCCESS; | |
| } | |
| int | |
| _PyCompile_ExitInlinedComprehensionScope(compiler *c, | |
| _PyCompile_InlinedComprehensionState *state) | |
| { | |
| assert(state->saved_ste != NULL); | |
| Py_DECREF(c->u->u_ste); | |
| c->u->u_ste = state->saved_ste; | |
| state->saved_ste = NULL; | |
| return SUCCESS; | |
| } | |
| void | |
| _PyCompile_EnterConditionalBlock(struct _PyCompiler *c) | |
| { | |
| c->u->u_in_conditional_block++; | |
| } | |
| void | |
| _PyCompile_LeaveConditionalBlock(struct _PyCompiler *c) | |
| { | |
| assert(c->u->u_in_conditional_block > 0); | |
| c->u->u_in_conditional_block--; | |
| } | |
| int | |
| _PyCompile_AddDeferredAnnotation(compiler *c, stmt_ty s, | |
| PyObject **conditional_annotation_index) | |
| { | |
| if (c->u->u_deferred_annotations == NULL) { | |
| c->u->u_deferred_annotations = PyList_New(0); | |
| if (c->u->u_deferred_annotations == NULL) { | |
| return ERROR; | |
| } | |
| } | |
| if (c->u->u_conditional_annotation_indices == NULL) { | |
| c->u->u_conditional_annotation_indices = PyList_New(0); | |
| if (c->u->u_conditional_annotation_indices == NULL) { | |
| return ERROR; | |
| } | |
| } | |
| PyObject *ptr = PyLong_FromVoidPtr((void *)s); | |
| if (ptr == NULL) { | |
| return ERROR; | |
| } | |
| if (PyList_Append(c->u->u_deferred_annotations, ptr) < 0) { | |
| Py_DECREF(ptr); | |
| return ERROR; | |
| } | |
| Py_DECREF(ptr); | |
| PyObject *index; | |
| if (c->u->u_scope_type == COMPILE_SCOPE_MODULE || c->u->u_in_conditional_block) { | |
| index = PyLong_FromLong(c->u->u_next_conditional_annotation_index); | |
| if (index == NULL) { | |
| return ERROR; | |
| } | |
| *conditional_annotation_index = Py_NewRef(index); | |
| c->u->u_next_conditional_annotation_index++; | |
| } | |
| else { | |
| index = PyLong_FromLong(-1); | |
| if (index == NULL) { | |
| return ERROR; | |
| } | |
| } | |
| int rc = PyList_Append(c->u->u_conditional_annotation_indices, index); | |
| Py_DECREF(index); | |
| RETURN_IF_ERROR(rc); | |
| return SUCCESS; | |
| } | |
| /* Raises a SyntaxError and returns ERROR. | |
| * If something goes wrong, a different exception may be raised. | |
| */ | |
| int | |
| _PyCompile_Error(compiler *c, location loc, const char *format, ...) | |
| { | |
| va_list vargs; | |
| va_start(vargs, format); | |
| PyObject *msg = PyUnicode_FromFormatV(format, vargs); | |
| va_end(vargs); | |
| if (msg == NULL) { | |
| return ERROR; | |
| } | |
| _PyErr_RaiseSyntaxError(msg, c->c_filename, loc.lineno, loc.col_offset + 1, | |
| loc.end_lineno, loc.end_col_offset + 1); | |
| Py_DECREF(msg); | |
| return ERROR; | |
| } | |
| /* Emits a SyntaxWarning and returns 0 on success. | |
| If a SyntaxWarning raised as error, replaces it with a SyntaxError | |
| and returns -1. | |
| */ | |
| int | |
| _PyCompile_Warn(compiler *c, location loc, const char *format, ...) | |
| { | |
| if (c->c_disable_warning) { | |
| return 0; | |
| } | |
| va_list vargs; | |
| va_start(vargs, format); | |
| PyObject *msg = PyUnicode_FromFormatV(format, vargs); | |
| va_end(vargs); | |
| if (msg == NULL) { | |
| return ERROR; | |
| } | |
| int ret = _PyErr_EmitSyntaxWarning(msg, c->c_filename, loc.lineno, loc.col_offset + 1, | |
| loc.end_lineno, loc.end_col_offset + 1, | |
| c->c_module); | |
| Py_DECREF(msg); | |
| return ret; | |
| } | |
| PyObject * | |
| _PyCompile_Mangle(compiler *c, PyObject *name) | |
| { | |
| return _Py_Mangle(c->u->u_private, name); | |
| } | |
| PyObject * | |
| _PyCompile_MaybeMangle(compiler *c, PyObject *name) | |
| { | |
| return _Py_MaybeMangle(c->u->u_private, c->u->u_ste, name); | |
| } | |
| instr_sequence * | |
| _PyCompile_InstrSequence(compiler *c) | |
| { | |
| return c->u->u_instr_sequence; | |
| } | |
| int | |
| _PyCompile_StartAnnotationSetup(struct _PyCompiler *c) | |
| { | |
| instr_sequence *new_seq = (instr_sequence *)_PyInstructionSequence_New(); | |
| if (new_seq == NULL) { | |
| return ERROR; | |
| } | |
| assert(c->u->u_stashed_instr_sequence == NULL); | |
| c->u->u_stashed_instr_sequence = c->u->u_instr_sequence; | |
| c->u->u_instr_sequence = new_seq; | |
| return SUCCESS; | |
| } | |
| int | |
| _PyCompile_EndAnnotationSetup(struct _PyCompiler *c) | |
| { | |
| assert(c->u->u_stashed_instr_sequence != NULL); | |
| instr_sequence *parent_seq = c->u->u_stashed_instr_sequence; | |
| instr_sequence *anno_seq = c->u->u_instr_sequence; | |
| c->u->u_stashed_instr_sequence = NULL; | |
| c->u->u_instr_sequence = parent_seq; | |
| if (_PyInstructionSequence_SetAnnotationsCode(parent_seq, anno_seq) == ERROR) { | |
| Py_DECREF(anno_seq); | |
| return ERROR; | |
| } | |
| return SUCCESS; | |
| } | |
| int | |
| _PyCompile_FutureFeatures(compiler *c) | |
| { | |
| return c->c_future.ff_features; | |
| } | |
| struct symtable * | |
| _PyCompile_Symtable(compiler *c) | |
| { | |
| return c->c_st; | |
| } | |
| PySTEntryObject * | |
| _PyCompile_SymtableEntry(compiler *c) | |
| { | |
| return c->u->u_ste; | |
| } | |
| int | |
| _PyCompile_OptimizationLevel(compiler *c) | |
| { | |
| return c->c_optimize; | |
| } | |
| int | |
| _PyCompile_IsInteractiveTopLevel(compiler *c) | |
| { | |
| assert(c->c_stack != NULL); | |
| assert(PyList_CheckExact(c->c_stack)); | |
| bool is_nested_scope = PyList_GET_SIZE(c->c_stack) > 0; | |
| return c->c_interactive && !is_nested_scope; | |
| } | |
| int | |
| _PyCompile_ScopeType(compiler *c) | |
| { | |
| return c->u->u_scope_type; | |
| } | |
| PyObject * | |
| _PyCompile_Qualname(compiler *c) | |
| { | |
| assert(c->u->u_metadata.u_qualname); | |
| return c->u->u_metadata.u_qualname; | |
| } | |
| _PyCompile_CodeUnitMetadata * | |
| _PyCompile_Metadata(compiler *c) | |
| { | |
| return &c->u->u_metadata; | |
| } | |
| // Merge *obj* with constant cache, without recursion. | |
| int | |
| _PyCompile_ConstCacheMergeOne(PyObject *const_cache, PyObject **obj) | |
| { | |
| PyObject *key = const_cache_insert(const_cache, *obj, false); | |
| if (key == NULL) { | |
| return ERROR; | |
| } | |
| if (PyTuple_CheckExact(key)) { | |
| PyObject *item = PyTuple_GET_ITEM(key, 1); | |
| Py_SETREF(*obj, Py_NewRef(item)); | |
| Py_DECREF(key); | |
| } | |
| else { | |
| Py_SETREF(*obj, key); | |
| } | |
| return SUCCESS; | |
| } | |
| static PyObject * | |
| consts_dict_keys_inorder(PyObject *dict) | |
| { | |
| PyObject *consts, *k, *v; | |
| Py_ssize_t i, pos = 0, size = PyDict_GET_SIZE(dict); | |
| consts = PyList_New(size); /* PyCode_Optimize() requires a list */ | |
| if (consts == NULL) | |
| return NULL; | |
| while (PyDict_Next(dict, &pos, &k, &v)) { | |
| assert(PyLong_CheckExact(v)); | |
| i = PyLong_AsLong(v); | |
| /* The keys of the dictionary can be tuples wrapping a constant. | |
| * (see _PyCompile_DictAddObj and _PyCode_ConstantKey). In that case | |
| * the object we want is always second. */ | |
| if (PyTuple_CheckExact(k)) { | |
| k = PyTuple_GET_ITEM(k, 1); | |
| } | |
| assert(i < size); | |
| assert(i >= 0); | |
| PyList_SET_ITEM(consts, i, Py_NewRef(k)); | |
| } | |
| return consts; | |
| } | |
| /* Intern the strings of the constant *o* which intern_constants() in | |
| * codeobject.c would intern when a code object is created. | |
| * | |
| * The strings of a slice are interned in all builds, although | |
| * intern_constants() only looks into slices in the free-threaded build, | |
| * so that the constants of a compilation are the same objects in both | |
| * builds. | |
| * | |
| * Return a new reference to the constant to use instead of *o*. The items | |
| * of a tuple are replaced in place. A frozenset or a slice with an item | |
| * to replace cannot be modified: a new object is created and stored in its | |
| * entry of *const_cache*, which is where the other users of *o* find it. | |
| */ | |
| static PyObject * | |
| intern_constant(PyObject *const_cache, PyObject *o) | |
| { | |
| if (PyUnicode_CheckExact(o)) { | |
| Py_INCREF(o); | |
| if (_PyCode_ShouldInternString(o)) { | |
| _PyUnicode_InternMortal(_PyInterpreterState_GET(), &o); | |
| } | |
| return o; | |
| } | |
| if (PyTuple_CheckExact(o)) { | |
| Py_ssize_t len = PyTuple_GET_SIZE(o); | |
| for (Py_ssize_t i = 0; i < len; i++) { | |
| PyObject *item = PyTuple_GET_ITEM(o, i); | |
| PyObject *v = intern_constant(const_cache, item); | |
| if (v == NULL) { | |
| return NULL; | |
| } | |
| if (v != item) { | |
| // intern_constants() makes the same replacement in the | |
| // tuple if it is not made here. | |
| PyTuple_SET_ITEM(o, i, v); | |
| Py_DECREF(item); | |
| } | |
| else { | |
| Py_DECREF(v); | |
| } | |
| } | |
| return Py_NewRef(o); | |
| } | |
| if (!PyFrozenSet_CheckExact(o) && !PySlice_Check(o)) { | |
| return Py_NewRef(o); | |
| } | |
| // The key of a frozenset or of a slice is a pair whose second item is | |
| // the object which stands for all the equal constants. | |
| // See _PyCode_ConstantKey(). | |
| PyObject *key = const_cache_insert(const_cache, o, false); | |
| if (key == NULL) { | |
| return NULL; | |
| } | |
| assert(PyTuple_CheckExact(key)); | |
| assert(PyTuple_GET_SIZE(key) == 2); | |
| o = PyTuple_GET_ITEM(key, 1); | |
| PyObject *items; | |
| if (PyFrozenSet_CheckExact(o)) { | |
| items = PySequence_Tuple(o); | |
| } | |
| else { | |
| PySliceObject *slice = (PySliceObject *)o; | |
| items = PyTuple_Pack(3, slice->start, slice->stop, slice->step); | |
| } | |
| if (items == NULL) { | |
| Py_DECREF(key); | |
| return NULL; | |
| } | |
| bool modified = false; | |
| Py_ssize_t len = PyTuple_GET_SIZE(items); | |
| for (Py_ssize_t i = 0; i < len; i++) { | |
| PyObject *item = PyTuple_GET_ITEM(items, i); | |
| PyObject *v = intern_constant(const_cache, item); | |
| if (v == NULL) { | |
| Py_DECREF(items); | |
| Py_DECREF(key); | |
| return NULL; | |
| } | |
| if (v != item) { | |
| modified = true; | |
| } | |
| PyTuple_SET_ITEM(items, i, v); | |
| Py_DECREF(item); | |
| } | |
| if (modified) { | |
| PyObject *new; | |
| if (PyFrozenSet_CheckExact(o)) { | |
| new = PyFrozenSet_New(items); | |
| } | |
| else { | |
| new = PySlice_New(PyTuple_GET_ITEM(items, 0), | |
| PyTuple_GET_ITEM(items, 1), | |
| PyTuple_GET_ITEM(items, 2)); | |
| } | |
| if (new == NULL) { | |
| Py_DECREF(items); | |
| Py_DECREF(key); | |
| return NULL; | |
| } | |
| PyTuple_SET_ITEM(key, 1, new); | |
| Py_DECREF(o); | |
| o = new; | |
| } | |
| Py_DECREF(items); | |
| Py_INCREF(o); | |
| Py_DECREF(key); | |
| return o; | |
| } | |
| /* Intern the strings of the constants of a code unit, once the optimizer | |
| * has removed the constants which are not used. | |
| * | |
| * intern_constants() in codeobject.c interns the string constants of each | |
| * new code object. When it has to replace a string of a frozenset (or, in | |
| * the free-threaded build, of a slice) by a string which is already | |
| * interned, it makes a copy of the container for that code object only. | |
| * The constants of a code object would then be shared with the other code | |
| * objects of the compilation or not depending on which strings happen to | |
| * be interned in the process, and so would the output of marshal | |
| * (gh-129724). Replace the strings here instead, where all the code units | |
| * get the same container from the constant cache. | |
| */ | |
| static int | |
| intern_final_constants(PyObject *const_cache, PyObject *consts) | |
| { | |
| assert(PyList_CheckExact(consts)); | |
| Py_ssize_t len = PyList_GET_SIZE(consts); | |
| for (Py_ssize_t i = 0; i < len; i++) { | |
| PyObject *item = PyList_GET_ITEM(consts, i); | |
| PyObject *v = intern_constant(const_cache, item); | |
| if (v == NULL) { | |
| return ERROR; | |
| } | |
| PyList_SET_ITEM(consts, i, v); | |
| Py_DECREF(item); | |
| } | |
| return SUCCESS; | |
| } | |
| static int | |
| compute_code_flags(compiler *c) | |
| { | |
| PySTEntryObject *ste = c->u->u_ste; | |
| int flags = 0; | |
| if (_PyST_IsFunctionLike(ste)) { | |
| flags |= CO_NEWLOCALS | CO_OPTIMIZED; | |
| if (ste->ste_nested) | |
| flags |= CO_NESTED; | |
| if (ste->ste_generator && !ste->ste_coroutine) | |
| flags |= CO_GENERATOR; | |
| if (ste->ste_generator && ste->ste_coroutine) | |
| flags |= CO_ASYNC_GENERATOR; | |
| if (ste->ste_varargs) | |
| flags |= CO_VARARGS; | |
| if (ste->ste_varkeywords) | |
| flags |= CO_VARKEYWORDS; | |
| if (ste->ste_has_docstring) | |
| flags |= CO_HAS_DOCSTRING; | |
| if (ste->ste_method) | |
| flags |= CO_METHOD; | |
| } | |
| if (ste->ste_coroutine && !ste->ste_generator) { | |
| flags |= CO_COROUTINE; | |
| } | |
| /* (Only) inherit compilerflags in PyCF_MASK */ | |
| flags |= (c->c_flags.cf_flags & PyCF_MASK); | |
| return flags; | |
| } | |
| static PyCodeObject * | |
| optimize_and_assemble_code_unit(struct compiler_unit *u, PyObject *const_cache, | |
| int code_flags, PyObject *filename) | |
| { | |
| cfg_builder *g = NULL; | |
| instr_sequence optimized_instrs; | |
| memset(&optimized_instrs, 0, sizeof(instr_sequence)); | |
| PyCodeObject *co = NULL; | |
| PyObject *consts = consts_dict_keys_inorder(u->u_metadata.u_consts); | |
| if (consts == NULL) { | |
| goto error; | |
| } | |
| g = _PyCfg_FromInstructionSequence(u->u_instr_sequence); | |
| if (g == NULL) { | |
| goto error; | |
| } | |
| int nlocals = (int)PyDict_GET_SIZE(u->u_metadata.u_varnames); | |
| int nparams = (int)PyList_GET_SIZE(u->u_ste->ste_varnames); | |
| assert(u->u_metadata.u_firstlineno); | |
| if (_PyCfg_OptimizeCodeUnit(g, consts, const_cache, nlocals, | |
| nparams, u->u_metadata.u_firstlineno) < 0) { | |
| goto error; | |
| } | |
| int stackdepth; | |
| int nlocalsplus; | |
| if (_PyCfg_OptimizedCfgToInstructionSequence(g, &u->u_metadata, | |
| &stackdepth, &nlocalsplus, | |
| &optimized_instrs) < 0) { | |
| goto error; | |
| } | |
| if (intern_final_constants(const_cache, consts) < 0) { | |
| goto error; | |
| } | |
| /** Assembly **/ | |
| co = _PyAssemble_MakeCodeObject(&u->u_metadata, const_cache, consts, | |
| stackdepth, &optimized_instrs, nlocalsplus, | |
| code_flags, filename); | |
| error: | |
| Py_XDECREF(consts); | |
| PyInstructionSequence_Fini(&optimized_instrs); | |
| _PyCfgBuilder_Free(g); | |
| return co; | |
| } | |
| PyCodeObject * | |
| _PyCompile_OptimizeAndAssemble(compiler *c, int addNone) | |
| { | |
| struct compiler_unit *u = c->u; | |
| PyObject *const_cache = c->c_const_cache; | |
| PyObject *filename = c->c_filename; | |
| int code_flags = compute_code_flags(c); | |
| assert(code_flags >= 0); | |
| if (_PyCodegen_AddReturnAtEnd(c, addNone) < 0) { | |
| return NULL; | |
| } | |
| return optimize_and_assemble_code_unit(u, const_cache, code_flags, filename); | |
| } | |
| PyCodeObject * | |
| _PyAST_Compile(mod_ty mod, PyObject *filename, PyCompilerFlags *pflags, | |
| int optimize, PyArena *arena, PyObject *module) | |
| { | |
| assert(!PyErr_Occurred()); | |
| compiler *c = new_compiler(mod, filename, pflags, optimize, arena, module); | |
| if (c == NULL) { | |
| return NULL; | |
| } | |
| PyCodeObject *co = compiler_mod(c, mod); | |
| compiler_free(c); | |
| assert(co || PyErr_Occurred()); | |
| return co; | |
| } | |
| int | |
| _PyCompile_AstPreprocess(mod_ty mod, PyObject *filename, PyCompilerFlags *cf, | |
| int optimize, PyArena *arena, int no_const_folding, | |
| PyObject *module) | |
| { | |
| _PyFutureFeatures future; | |
| if (!_PyFuture_FromAST(mod, filename, &future)) { | |
| return -1; | |
| } | |
| int flags = future.ff_features | cf->cf_flags; | |
| if (optimize == -1) { | |
| optimize = _Py_GetConfig()->optimization_level; | |
| } | |
| if (!_PyAST_Preprocess(mod, arena, filename, optimize, flags, | |
| no_const_folding, 0, module)) | |
| { | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| // C implementation of inspect.cleandoc() | |
| // | |
| // Difference from inspect.cleandoc(): | |
| // - Do not remove leading and trailing blank lines to keep lineno. | |
| PyObject * | |
| _PyCompile_CleanDoc(PyObject *doc) | |
| { | |
| doc = PyObject_CallMethod(doc, "expandtabs", NULL); | |
| if (doc == NULL) { | |
| return NULL; | |
| } | |
| Py_ssize_t doc_size; | |
| const char *doc_utf8 = PyUnicode_AsUTF8AndSize(doc, &doc_size); | |
| if (doc_utf8 == NULL) { | |
| Py_DECREF(doc); | |
| return NULL; | |
| } | |
| const char *p = doc_utf8; | |
| const char *pend = p + doc_size; | |
| // First pass: find minimum indentation of any non-blank lines | |
| // after first line. | |
| while (p < pend && *p++ != '\n') { | |
| } | |
| Py_ssize_t margin = PY_SSIZE_T_MAX; | |
| while (p < pend) { | |
| const char *s = p; | |
| while (*p == ' ') p++; | |
| if (p < pend && *p != '\n') { | |
| margin = Py_MIN(margin, p - s); | |
| } | |
| while (p < pend && *p++ != '\n') { | |
| } | |
| } | |
| if (margin == PY_SSIZE_T_MAX) { | |
| margin = 0; | |
| } | |
| // Second pass: write cleandoc into buff. | |
| // copy first line without leading spaces. | |
| p = doc_utf8; | |
| while (*p == ' ') { | |
| p++; | |
| } | |
| if (p == doc_utf8 && margin == 0 ) { | |
| // doc is already clean. | |
| return doc; | |
| } | |
| char *buff = PyMem_Malloc(doc_size); | |
| if (buff == NULL){ | |
| Py_DECREF(doc); | |
| PyErr_NoMemory(); | |
| return NULL; | |
| } | |
| char *w = buff; | |
| while (p < pend) { | |
| int ch = *w++ = *p++; | |
| if (ch == '\n') { | |
| break; | |
| } | |
| } | |
| // copy subsequent lines without margin. | |
| while (p < pend) { | |
| for (Py_ssize_t i = 0; i < margin; i++, p++) { | |
| if (*p != ' ') { | |
| assert(*p == '\n' || *p == '\0'); | |
| break; | |
| } | |
| } | |
| while (p < pend) { | |
| int ch = *w++ = *p++; | |
| if (ch == '\n') { | |
| break; | |
| } | |
| } | |
| } | |
| Py_DECREF(doc); | |
| PyObject *res = PyUnicode_FromStringAndSize(buff, w - buff); | |
| PyMem_Free(buff); | |
| return res; | |
| } | |
| /* Access to compiler optimizations for unit tests. | |
| * | |
| * _PyCompile_CodeGen takes an AST, applies code-gen and | |
| * returns the unoptimized CFG as an instruction list. | |
| * | |
| */ | |
| PyObject * | |
| _PyCompile_CodeGen(PyObject *ast, PyObject *filename, PyCompilerFlags *pflags, | |
| int optimize, int compile_mode) | |
| { | |
| PyObject *res = NULL; | |
| PyObject *metadata = NULL; | |
| PyObject *consts_list = NULL; | |
| if (!PyAST_Check(ast)) { | |
| PyErr_SetString(PyExc_TypeError, "expected an AST"); | |
| return NULL; | |
| } | |
| PyArena *arena = _PyArena_New(); | |
| if (arena == NULL) { | |
| return NULL; | |
| } | |
| mod_ty mod = PyAST_obj2mod(ast, arena, compile_mode); | |
| if (mod == NULL || !_PyAST_Validate(mod)) { | |
| _PyArena_Free(arena); | |
| return NULL; | |
| } | |
| compiler *c = new_compiler(mod, filename, pflags, optimize, arena, NULL); | |
| if (c == NULL) { | |
| _PyArena_Free(arena); | |
| return NULL; | |
| } | |
| c->c_save_nested_seqs = true; | |
| metadata = PyDict_New(); | |
| if (metadata == NULL) { | |
| goto finally; | |
| } | |
| if (compiler_codegen(c, mod) < 0) { | |
| goto finally; | |
| } | |
| _PyCompile_CodeUnitMetadata *umd = &c->u->u_metadata; | |
| #define SET_METADATA_INT(key, value) do { \ | |
| PyObject *v = PyLong_FromLong((long)value); \ | |
| if (v == NULL) goto finally; \ | |
| int res = PyDict_SetItemString(metadata, key, v); \ | |
| Py_XDECREF(v); \ | |
| if (res < 0) goto finally; \ | |
| } while (0); | |
| SET_METADATA_INT("argcount", umd->u_argcount); | |
| SET_METADATA_INT("posonlyargcount", umd->u_posonlyargcount); | |
| SET_METADATA_INT("kwonlyargcount", umd->u_kwonlyargcount); | |
| #undef SET_METADATA_INT | |
| int addNone = mod->kind != Expression_kind; | |
| if (_PyCodegen_AddReturnAtEnd(c, addNone) < 0) { | |
| goto finally; | |
| } | |
| if (_PyInstructionSequence_ApplyLabelMap(_PyCompile_InstrSequence(c)) < 0) { | |
| goto finally; | |
| } | |
| /* After AddReturnAtEnd: co_consts indices match the final instruction stream. */ | |
| consts_list = consts_dict_keys_inorder(umd->u_consts); | |
| if (consts_list == NULL) { | |
| goto finally; | |
| } | |
| if (PyDict_SetItemString(metadata, "consts", consts_list) < 0) { | |
| goto finally; | |
| } | |
| /* Allocate a copy of the instruction sequence on the heap */ | |
| res = _PyTuple_FromPair((PyObject *)_PyCompile_InstrSequence(c), metadata); | |
| finally: | |
| Py_XDECREF(consts_list); | |
| Py_XDECREF(metadata); | |
| if (c->u != NULL) { | |
| _PyCompile_ExitScope(c); | |
| } | |
| compiler_free(c); | |
| _PyArena_Free(arena); | |
| return res; | |
| } | |
| int _PyCfg_JumpLabelsToTargets(cfg_builder *g); | |
| PyCodeObject * | |
| _PyCompile_Assemble(_PyCompile_CodeUnitMetadata *umd, PyObject *filename, | |
| PyObject *seq) | |
| { | |
| if (!_PyInstructionSequence_Check(seq)) { | |
| PyErr_SetString(PyExc_TypeError, "expected an instruction sequence"); | |
| return NULL; | |
| } | |
| cfg_builder *g = NULL; | |
| PyCodeObject *co = NULL; | |
| instr_sequence optimized_instrs; | |
| memset(&optimized_instrs, 0, sizeof(instr_sequence)); | |
| PyObject *const_cache = PyDict_New(); | |
| if (const_cache == NULL) { | |
| return NULL; | |
| } | |
| g = _PyCfg_FromInstructionSequence((instr_sequence*)seq); | |
| if (g == NULL) { | |
| goto error; | |
| } | |
| if (_PyCfg_JumpLabelsToTargets(g) < 0) { | |
| goto error; | |
| } | |
| int code_flags = 0; | |
| int stackdepth, nlocalsplus; | |
| if (_PyCfg_OptimizedCfgToInstructionSequence(g, umd, | |
| &stackdepth, &nlocalsplus, | |
| &optimized_instrs) < 0) { | |
| goto error; | |
| } | |
| PyObject *consts = consts_dict_keys_inorder(umd->u_consts); | |
| if (consts == NULL) { | |
| goto error; | |
| } | |
| co = _PyAssemble_MakeCodeObject(umd, const_cache, | |
| consts, stackdepth, &optimized_instrs, | |
| nlocalsplus, code_flags, filename); | |
| Py_DECREF(consts); | |
| error: | |
| Py_DECREF(const_cache); | |
| _PyCfgBuilder_Free(g); | |
| PyInstructionSequence_Fini(&optimized_instrs); | |
| return co; | |
| } | |
| /* Retained for API compatibility. | |
| * Optimization is now done in _PyCfg_OptimizeCodeUnit */ | |
| PyObject * | |
| PyCode_Optimize(PyObject *code, PyObject* Py_UNUSED(consts), | |
| PyObject *Py_UNUSED(names), PyObject *Py_UNUSED(lnotab_obj)) | |
| { | |
| return Py_NewRef(code); | |
| } |