Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-43693: Revert commits 2c1e2583fdc4db6b43d163239ea42b0e8394171f and b2bf2bc1ece673d387341e06c8d3c2bc6e259747 #26530

Merged
merged 2 commits into from
Jun 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 7 additions & 23 deletions Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1058,24 +1058,17 @@ All of the following opcodes use their arguments.

.. opcode:: LOAD_CLOSURE (i)

Pushes a reference to the cell contained in slot ``i`` of the "fast locals"
storage. The name of the variable is ``co_fastlocalnames[i]``.

Note that ``LOAD_CLOSURE`` is effectively an alias for ``LOAD_FAST``.
It exists to keep bytecode a little more readable.

.. versionchanged:: 3.11
``i`` is no longer offset by the length of ``co_varnames``.
Pushes a reference to the cell contained in slot *i* of the cell and free
variable storage. The name of the variable is ``co_cellvars[i]`` if *i* is
less than the length of *co_cellvars*. Otherwise it is ``co_freevars[i -
len(co_cellvars)]``.


.. opcode:: LOAD_DEREF (i)

Loads the cell contained in slot ``i`` of the "fast locals" storage.
Loads the cell contained in slot *i* of the cell and free variable storage.
Pushes a reference to the object the cell contains on the stack.

.. versionchanged:: 3.11
``i`` is no longer offset by the length of ``co_varnames``.


.. opcode:: LOAD_CLASSDEREF (i)

Expand All @@ -1085,29 +1078,20 @@ All of the following opcodes use their arguments.

.. versionadded:: 3.4

.. versionchanged:: 3.11
``i`` is no longer offset by the length of ``co_varnames``.


.. opcode:: STORE_DEREF (i)

Stores TOS into the cell contained in slot ``i`` of the "fast locals"
Stores TOS into the cell contained in slot *i* of the cell and free variable
storage.

.. versionchanged:: 3.11
``i`` is no longer offset by the length of ``co_varnames``.


.. opcode:: DELETE_DEREF (i)

Empties the cell contained in slot ``i`` of the "fast locals" storage.
Empties the cell contained in slot *i* of the cell and free variable storage.
Used by the :keyword:`del` statement.

.. versionadded:: 3.2

.. versionchanged:: 3.11
``i`` is no longer offset by the length of ``co_varnames``.


.. opcode:: RAISE_VARARGS (argc)

Expand Down
20 changes: 5 additions & 15 deletions Include/cpython/code.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
#endif

typedef uint16_t _Py_CODEUNIT;
// Each oparg must fit in the second half of _Py_CODEUNIT, hence 8 bits.
#define _Py_MAX_OPARG 255

#ifdef WORDS_BIGENDIAN
# define _Py_OPCODE(word) ((word) >> 8)
Expand All @@ -16,11 +14,6 @@ typedef uint16_t _Py_CODEUNIT;

typedef struct _PyOpcache _PyOpcache;


// These are duplicated from pycore_code.h.
typedef unsigned char _PyLocalsPlusKind;
typedef _PyLocalsPlusKind *_PyLocalsPlusKinds;

/* Bytecode object */
struct PyCodeObject {
PyObject_HEAD
Expand Down Expand Up @@ -60,8 +53,9 @@ struct PyCodeObject {
int co_kwonlyargcount; /* #keyword only arguments */
int co_stacksize; /* #entries needed for evaluation stack */
int co_firstlineno; /* first source line number */
PyObject *co_localsplusnames; /* tuple mapping offsets to names */
_PyLocalsPlusKinds co_localspluskinds; /* array mapping to local kinds */
PyObject *co_varnames; /* tuple of strings (local variable names) */
PyObject *co_cellvars; /* tuple of strings (cell variable names) */
PyObject *co_freevars; /* tuple of strings (free variable names) */
PyObject *co_filename; /* unicode (where it was loaded from) */
PyObject *co_name; /* unicode (name, for reference) */
PyObject *co_linetable; /* string (encoding addr<->lineno mapping) See
Expand All @@ -71,15 +65,11 @@ struct PyCodeObject {
/* These fields are set with computed values on new code objects. */

int *co_cell2arg; /* Maps cell vars which are arguments. */
// redundant values (derived from co_localsplusnames and co_localspluskinds)
// These are redundant but offer some performance benefit.
int co_nlocalsplus; /* number of local + cell + free variables */
int co_nlocals; /* number of local variables */
int co_ncellvars; /* number of cell variables */
int co_nfreevars; /* number of free variables */
// lazily-computed values
PyObject *co_varnames; /* tuple of strings (local variable names) */
PyObject *co_cellvars; /* tuple of strings (cell variable names) */
PyObject *co_freevars; /* tuple of strings (free variable names) */

/* The remaining fields are zeroed out on new code objects. */

Expand Down Expand Up @@ -153,7 +143,7 @@ struct PyCodeObject {
PyAPI_DATA(PyTypeObject) PyCode_Type;

#define PyCode_Check(op) Py_IS_TYPE(op, &PyCode_Type)
#define PyCode_GetNumFree(op) ((op)->co_nfreevars)
#define PyCode_GetNumFree(op) (PyTuple_GET_SIZE((op)->co_freevars))

/* Public interface */
PyAPI_FUNC(PyCodeObject *) PyCode_New(
Expand Down
62 changes: 3 additions & 59 deletions Include/internal/pycore_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,57 +26,6 @@ struct _PyOpcache {
};


/* "Locals plus" for a code object is the set of locals + cell vars +
* free vars. This relates to variable names as well as offsets into
* the "fast locals" storage array of execution frames. The compiler
* builds the list of names, their offsets, and the corresponding
* kind of local.
*
* Those kinds represent the source of the initial value and the
* variable's scope (as related to closures). A "local" is an
* argument or other variable defined in the current scope. A "free"
* variable is one that is defined in an outer scope and comes from
* the function's closure. A "cell" variable is a local that escapes
* into an inner function as part of a closure, and thus must be
* wrapped in a cell. Any "local" can also be a "cell", but the
* "free" kind is mutually exclusive with both.
*/

// We would use an enum if C let us specify the storage type.
typedef unsigned char _PyLocalsPlusKind;
/* Note that these all fit within _PyLocalsPlusKind, as do combinations. */
// Later, we will use the smaller numbers to differentiate the different
// kinds of locals (e.g. pos-only arg, varkwargs, local-only).
#define CO_FAST_LOCAL 0x20
#define CO_FAST_CELL 0x40
#define CO_FAST_FREE 0x80

typedef _PyLocalsPlusKind *_PyLocalsPlusKinds;

static inline int
_PyCode_InitLocalsPlusKinds(int num, _PyLocalsPlusKinds *pkinds)
{
if (num == 0) {
*pkinds = NULL;
return 0;
}
_PyLocalsPlusKinds kinds = PyMem_NEW(_PyLocalsPlusKind, num);
if (kinds == NULL) {
PyErr_NoMemory();
return -1;
}
*pkinds = kinds;
return 0;
}

static inline void
_PyCode_ClearLocalsPlusKinds(_PyLocalsPlusKinds kinds)
{
if (kinds != NULL) {
PyMem_Free(kinds);
}
}

struct _PyCodeConstructor {
/* metadata */
PyObject *filename;
Expand All @@ -93,13 +42,13 @@ struct _PyCodeConstructor {
PyObject *names;

/* mapping frame offsets to information */
PyObject *localsplusnames;
_PyLocalsPlusKinds localspluskinds;
PyObject *varnames;
PyObject *cellvars;
PyObject *freevars;

/* args (within varnames) */
int argcount;
int posonlyargcount;
// XXX Replace argcount with posorkwargcount (argcount - posonlyargcount).
int kwonlyargcount;

/* needed to create the frame */
Expand All @@ -126,11 +75,6 @@ PyAPI_FUNC(PyCodeObject *) _PyCode_New(struct _PyCodeConstructor *);

int _PyCode_InitOpcache(PyCodeObject *co);

/* Getters for internal PyCodeObject data. */
PyAPI_FUNC(PyObject *) _PyCode_GetVarnames(PyCodeObject *);
PyAPI_FUNC(PyObject *) _PyCode_GetCellvars(PyCodeObject *);
PyAPI_FUNC(PyObject *) _PyCode_GetFreevars(PyCodeObject *);


#ifdef __cplusplus
}
Expand Down
6 changes: 3 additions & 3 deletions Lib/ctypes/test/test_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ class struct_frozen(Structure):
continue
items.append((entry.name.decode("ascii"), entry.size))

expected = [("__hello__", 128),
("__phello__", -128),
("__phello__.spam", 128),
expected = [("__hello__", 138),
("__phello__", -138),
("__phello__.spam", 138),
]
self.assertEqual(items, expected, "PyImport_FrozenModules example "
"in Doc/library/ctypes.rst may be out of date")
Expand Down
53 changes: 26 additions & 27 deletions Lib/dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,15 +273,15 @@ def get_instructions(x, *, first_line=None):
the disassembled code object.
"""
co = _get_code_object(x)
cell_names = co.co_cellvars + co.co_freevars
linestarts = dict(findlinestarts(co))
if first_line is not None:
line_offset = first_line - co.co_firstlineno
else:
line_offset = 0
return _get_instructions_bytes(co.co_code,
co._varname_from_oparg,
co.co_names, co.co_consts,
linestarts, line_offset)
return _get_instructions_bytes(co.co_code, co.co_varnames, co.co_names,
co.co_consts, cell_names, linestarts,
line_offset)

def _get_const_info(const_index, const_list):
"""Helper to get optional details about const references
Expand All @@ -295,16 +295,16 @@ def _get_const_info(const_index, const_list):
argval = const_list[const_index]
return argval, repr(argval)

def _get_name_info(name_index, get_name, **extrainfo):
def _get_name_info(name_index, name_list):
"""Helper to get optional details about named references

Returns the dereferenced name as both value and repr if the name
list is defined.
Otherwise returns the name index and its repr().
"""
argval = name_index
if get_name is not None:
argval = get_name(name_index, **extrainfo)
if name_list is not None:
argval = name_list[name_index]
argrepr = argval
else:
argrepr = repr(argval)
Expand Down Expand Up @@ -336,10 +336,8 @@ def parse_exception_table(code):
except StopIteration:
return entries

def _get_instructions_bytes(code, varname_from_oparg=None,
names=None, constants=None,
linestarts=None, line_offset=0,
exception_entries=()):
def _get_instructions_bytes(code, varnames=None, names=None, constants=None,
cells=None, linestarts=None, line_offset=0, exception_entries=()):
"""Iterate over the instructions in a bytecode string.

Generates a sequence of Instruction namedtuples giving the details of each
Expand All @@ -348,7 +346,6 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
arguments.

"""
get_name = None if names is None else names.__getitem__
labels = set(findlabels(code))
for start, end, target, _, _ in exception_entries:
for i in range(start, end):
Expand All @@ -371,18 +368,20 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
if op in hasconst:
argval, argrepr = _get_const_info(arg, constants)
elif op in hasname:
argval, argrepr = _get_name_info(arg, get_name)
argval, argrepr = _get_name_info(arg, names)
elif op in hasjabs:
argval = arg*2
argrepr = "to " + repr(argval)
elif op in hasjrel:
argval = offset + 2 + arg*2
argrepr = "to " + repr(argval)
elif op in haslocal or op in hasfree:
argval, argrepr = _get_name_info(arg, varname_from_oparg)
elif op in haslocal:
argval, argrepr = _get_name_info(arg, varnames)
elif op in hascompare:
argval = cmp_op[arg]
argrepr = argval
elif op in hasfree:
argval, argrepr = _get_name_info(arg, cells)
elif op == FORMAT_VALUE:
argval, argrepr = FORMAT_VALUE_CONVERTERS[arg & 0x3]
argval = (argval, bool(arg & 0x4))
Expand All @@ -399,11 +398,11 @@ def _get_instructions_bytes(code, varname_from_oparg=None,

def disassemble(co, lasti=-1, *, file=None):
"""Disassemble a code object."""
cell_names = co.co_cellvars + co.co_freevars
linestarts = dict(findlinestarts(co))
exception_entries = parse_exception_table(co)
_disassemble_bytes(co.co_code, lasti,
co._varname_from_oparg,
co.co_names, co.co_consts, linestarts, file=file,
_disassemble_bytes(co.co_code, lasti, co.co_varnames, co.co_names,
co.co_consts, cell_names, linestarts, file=file,
exception_entries=exception_entries)

def _disassemble_recursive(co, *, file=None, depth=None):
Expand All @@ -417,8 +416,8 @@ def _disassemble_recursive(co, *, file=None, depth=None):
print("Disassembly of %r:" % (x,), file=file)
_disassemble_recursive(x, file=file, depth=depth)

def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
names=None, constants=None, linestarts=None,
def _disassemble_bytes(code, lasti=-1, varnames=None, names=None,
constants=None, cells=None, linestarts=None,
*, file=None, line_offset=0, exception_entries=()):
# Omit the line number column entirely if we have no line number info
show_lineno = bool(linestarts)
Expand All @@ -435,8 +434,8 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
offset_width = len(str(maxoffset))
else:
offset_width = 4
for instr in _get_instructions_bytes(code, varname_from_oparg, names,
constants, linestarts,
for instr in _get_instructions_bytes(code, varnames, names,
constants, cells, linestarts,
line_offset=line_offset, exception_entries=exception_entries):
new_source_line = (show_lineno and
instr.starts_line is not None and
Expand Down Expand Up @@ -518,16 +517,16 @@ def __init__(self, x, *, first_line=None, current_offset=None):
else:
self.first_line = first_line
self._line_offset = first_line - co.co_firstlineno
self._cell_names = co.co_cellvars + co.co_freevars
self._linestarts = dict(findlinestarts(co))
self._original_object = x
self.current_offset = current_offset
self.exception_entries = parse_exception_table(co)

def __iter__(self):
co = self.codeobj
return _get_instructions_bytes(co.co_code,
co._varname_from_oparg,
co.co_names, co.co_consts,
return _get_instructions_bytes(co.co_code, co.co_varnames, co.co_names,
co.co_consts, self._cell_names,
self._linestarts,
line_offset=self._line_offset,
exception_entries=self.exception_entries)
Expand Down Expand Up @@ -555,9 +554,9 @@ def dis(self):
else:
offset = -1
with io.StringIO() as output:
_disassemble_bytes(co.co_code,
varname_from_oparg=co._varname_from_oparg,
_disassemble_bytes(co.co_code, varnames=co.co_varnames,
names=co.co_names, constants=co.co_consts,
cells=self._cell_names,
linestarts=self._linestarts,
line_offset=self._line_offset,
file=output,
Expand Down
4 changes: 1 addition & 3 deletions Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,6 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.11a1 3450 Use exception table for unwinding ("zero cost" exception handling)
# Python 3.11a1 3451 (Add CALL_METHOD_KW)
# Python 3.11a1 3452 (drop nlocals from marshaled code objects)
# Python 3.11a1 3453 (add co_fastlocalnames and co_fastlocalkinds)
# Python 3.11a1 3454 (compute cell offsets relative to locals bpo-43693)

#
# MAGIC must change whenever the bytecode emitted by the compiler may no
Expand All @@ -366,7 +364,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.

MAGIC_NUMBER = (3454).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3452).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

_PYCACHE = '__pycache__'
Expand Down
Loading