Commit 312595ce authored by Benjamin Peterson's avatar Benjamin Peterson

hide the __class__ closure from the class body (#12370)

parent fe361dfa
...@@ -53,6 +53,9 @@ typedef struct _symtable_entry { ...@@ -53,6 +53,9 @@ typedef struct _symtable_entry {
unsigned ste_varkeywords : 1; /* true if block has varkeywords */ unsigned ste_varkeywords : 1; /* true if block has varkeywords */
unsigned ste_returns_value : 1; /* true if namespace uses return with unsigned ste_returns_value : 1; /* true if namespace uses return with
an argument */ an argument */
unsigned ste_needs_class_closure : 1; /* for class scopes, true if a
closure over __class__
should be created */
int ste_lineno; /* first line of block */ int ste_lineno; /* first line of block */
int ste_col_offset; /* offset of first line of block */ int ste_col_offset; /* offset of first line of block */
int ste_opt_lineno; /* lineno of last exec or import * */ int ste_opt_lineno; /* lineno of last exec or import * */
......
...@@ -390,12 +390,13 @@ def _call_with_frames_removed(f, *args, **kwds): ...@@ -390,12 +390,13 @@ def _call_with_frames_removed(f, *args, **kwds):
# keyword-only defaults) # keyword-only defaults)
# Python 3.4a1 3260 (add LOAD_CLASSDEREF; allow locals of class to override # Python 3.4a1 3260 (add LOAD_CLASSDEREF; allow locals of class to override
# free vars) # free vars)
# Python 3.4a1 3270 (various tweaks to the __class_ closure)
# #
# MAGIC must change whenever the bytecode emitted by the compiler may no # MAGIC must change whenever the bytecode emitted by the compiler may no
# longer be understood by older implementations of the eval loop (usually # longer be understood by older implementations of the eval loop (usually
# due to the addition of new opcodes). # due to the addition of new opcodes).
_MAGIC_BYTES = (3260).to_bytes(2, 'little') + b'\r\n' _MAGIC_BYTES = (3270).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(_MAGIC_BYTES, 'little') _RAW_MAGIC_NUMBER = int.from_bytes(_MAGIC_BYTES, 'little')
_PYCACHE = '__pycache__' _PYCACHE = '__pycache__'
......
...@@ -81,8 +81,7 @@ class TestSuper(unittest.TestCase): ...@@ -81,8 +81,7 @@ class TestSuper(unittest.TestCase):
self.assertEqual(E().f(), 'AE') self.assertEqual(E().f(), 'AE')
@unittest.expectedFailure def test_various___class___pathologies(self):
def test___class___set(self):
# See issue #12370 # See issue #12370
class X(A): class X(A):
def f(self): def f(self):
...@@ -91,6 +90,31 @@ class TestSuper(unittest.TestCase): ...@@ -91,6 +90,31 @@ class TestSuper(unittest.TestCase):
x = X() x = X()
self.assertEqual(x.f(), 'A') self.assertEqual(x.f(), 'A')
self.assertEqual(x.__class__, 413) self.assertEqual(x.__class__, 413)
class X:
x = __class__
def f():
__class__
self.assertIs(X.x, type(self))
with self.assertRaises(NameError) as e:
exec("""class X:
__class__
def f():
__class__""", globals(), {})
self.assertIs(type(e.exception), NameError) # Not UnboundLocalError
class X:
global __class__
__class__ = 42
def f():
__class__
self.assertEqual(globals()["__class__"], 42)
del globals()["__class__"]
self.assertNotIn("__class__", X.__dict__)
class X:
nonlocal __class__
__class__ = 42
def f():
__class__
self.assertEqual(__class__, 42)
def test___class___instancemethod(self): def test___class___instancemethod(self):
# See issue #14857 # See issue #14857
......
...@@ -10,6 +10,9 @@ What's New in Python 3.4.0 Alpha 1? ...@@ -10,6 +10,9 @@ What's New in Python 3.4.0 Alpha 1?
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #12370: Prevent class bodies from interfering with the __class__
closure.
- Issue #17237: Fix crash in the ASCII decoder on m68k. - Issue #17237: Fix crash in the ASCII decoder on m68k.
- Issue #17927: Frame objects kept arguments alive if they had been - Issue #17927: Frame objects kept arguments alive if they had been
......
...@@ -535,6 +535,37 @@ compiler_enter_scope(struct compiler *c, identifier name, ...@@ -535,6 +535,37 @@ compiler_enter_scope(struct compiler *c, identifier name,
compiler_unit_free(u); compiler_unit_free(u);
return 0; return 0;
} }
if (u->u_ste->ste_needs_class_closure) {
/* Cook up a implicit __class__ cell. */
_Py_IDENTIFIER(__class__);
PyObject *tuple, *name, *zero;
int res;
assert(u->u_scope_type == COMPILER_SCOPE_CLASS);
assert(PyDict_Size(u->u_cellvars) == 0);
name = _PyUnicode_FromId(&PyId___class__);
if (!name) {
compiler_unit_free(u);
return 0;
}
tuple = PyTuple_Pack(2, name, Py_TYPE(name));
if (!tuple) {
compiler_unit_free(u);
return 0;
}
zero = PyLong_FromLong(0);
if (!zero) {
Py_DECREF(tuple);
compiler_unit_free(u);
return 0;
}
res = PyDict_SetItem(u->u_cellvars, tuple, zero);
Py_DECREF(tuple);
Py_DECREF(zero);
if (res < 0) {
compiler_unit_free(u);
return 0;
}
}
u->u_freevars = dictbytype(u->u_ste->ste_symbols, FREE, DEF_FREE_CLASS, u->u_freevars = dictbytype(u->u_ste->ste_symbols, FREE, DEF_FREE_CLASS,
PyDict_Size(u->u_cellvars)); PyDict_Size(u->u_cellvars));
...@@ -1331,6 +1362,9 @@ compiler_mod(struct compiler *c, mod_ty mod) ...@@ -1331,6 +1362,9 @@ compiler_mod(struct compiler *c, mod_ty mod)
static int static int
get_ref_type(struct compiler *c, PyObject *name) get_ref_type(struct compiler *c, PyObject *name)
{ {
if (c->u->u_scope_type == COMPILER_SCOPE_CLASS &&
!PyUnicode_CompareWithASCIIString(name, "__class__"))
return CELL;
int scope = PyST_GetScope(c->u->u_ste, name); int scope = PyST_GetScope(c->u->u_ste, name);
if (scope == 0) { if (scope == 0) {
char buf[350]; char buf[350];
...@@ -1704,24 +1738,24 @@ compiler_class(struct compiler *c, stmt_ty s) ...@@ -1704,24 +1738,24 @@ compiler_class(struct compiler *c, stmt_ty s)
compiler_exit_scope(c); compiler_exit_scope(c);
return 0; return 0;
} }
/* return the (empty) __class__ cell */ if (c->u->u_ste->ste_needs_class_closure) {
str = PyUnicode_InternFromString("__class__"); /* return the (empty) __class__ cell */
if (str == NULL) { str = PyUnicode_InternFromString("__class__");
compiler_exit_scope(c); if (str == NULL) {
return 0; compiler_exit_scope(c);
} return 0;
i = compiler_lookup_arg(c->u->u_cellvars, str); }
Py_DECREF(str); i = compiler_lookup_arg(c->u->u_cellvars, str);
if (i == -1) { Py_DECREF(str);
/* This happens when nobody references the cell */ assert(i == 0);
PyErr_Clear();
/* Return None */
ADDOP_O(c, LOAD_CONST, Py_None, consts);
}
else {
/* Return the cell where to store __class__ */ /* Return the cell where to store __class__ */
ADDOP_I(c, LOAD_CLOSURE, i); ADDOP_I(c, LOAD_CLOSURE, i);
} }
else {
assert(PyDict_Size(c->u->u_cellvars) == 0);
/* This happens when nobody references the cell. Return None. */
ADDOP_O(c, LOAD_CONST, Py_None, consts);
}
ADDOP_IN_SCOPE(c, RETURN_VALUE); ADDOP_IN_SCOPE(c, RETURN_VALUE);
/* create the code object */ /* create the code object */
co = assemble(c, 1); co = assemble(c, 1);
......
This diff is collapsed.
...@@ -77,6 +77,7 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block, ...@@ -77,6 +77,7 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block,
ste->ste_child_free = 0; ste->ste_child_free = 0;
ste->ste_generator = 0; ste->ste_generator = 0;
ste->ste_returns_value = 0; ste->ste_returns_value = 0;
ste->ste_needs_class_closure = 0;
if (PyDict_SetItem(st->st_blocks, ste->ste_id, (PyObject *)ste) < 0) if (PyDict_SetItem(st->st_blocks, ste->ste_id, (PyObject *)ste) < 0)
goto fail; goto fail;
...@@ -514,13 +515,10 @@ analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags, ...@@ -514,13 +515,10 @@ analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags,
Note that the current block's free variables are included in free. 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. That's safe because no name can be free and local in the same scope.
The 'restricted' argument may be set to a string to restrict the analysis
to the one variable whose name equals that string (e.g. "__class__").
*/ */
static int static int
analyze_cells(PyObject *scopes, PyObject *free, const char *restricted) analyze_cells(PyObject *scopes, PyObject *free)
{ {
PyObject *name, *v, *v_cell; PyObject *name, *v, *v_cell;
int success = 0; int success = 0;
...@@ -537,9 +535,6 @@ analyze_cells(PyObject *scopes, PyObject *free, const char *restricted) ...@@ -537,9 +535,6 @@ analyze_cells(PyObject *scopes, PyObject *free, const char *restricted)
continue; continue;
if (!PySet_Contains(free, name)) if (!PySet_Contains(free, name))
continue; continue;
if (restricted != NULL &&
PyUnicode_CompareWithASCIIString(name, restricted))
continue;
/* Replace LOCAL with CELL for this name, and remove /* Replace LOCAL with CELL for this name, and remove
from free. It is safe to replace the value of name from free. It is safe to replace the value of name
in the dict, because it will not cause a resize. in the dict, because it will not cause a resize.
...@@ -555,6 +550,20 @@ analyze_cells(PyObject *scopes, PyObject *free, const char *restricted) ...@@ -555,6 +550,20 @@ analyze_cells(PyObject *scopes, PyObject *free, const char *restricted)
return success; return success;
} }
static int
drop_class_free(PySTEntryObject *ste, PyObject *free)
{
int res;
if (!GET_IDENTIFIER(__class__))
return 0;
res = PySet_Discard(free, __class__);
if (res < 0)
return 0;
if (res)
ste->ste_needs_class_closure = 1;
return 1;
}
/* Check for illegal statements in unoptimized namespaces */ /* Check for illegal statements in unoptimized namespaces */
static int static int
check_unoptimized(const PySTEntryObject* ste) { check_unoptimized(const PySTEntryObject* ste) {
...@@ -785,7 +794,6 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, ...@@ -785,7 +794,6 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
/* Special-case __class__ */ /* Special-case __class__ */
if (!GET_IDENTIFIER(__class__)) if (!GET_IDENTIFIER(__class__))
goto error; goto error;
assert(PySet_Contains(local, __class__) == 1);
if (PySet_Add(newbound, __class__) < 0) if (PySet_Add(newbound, __class__) < 0)
goto error; goto error;
} }
...@@ -818,11 +826,9 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, ...@@ -818,11 +826,9 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
Py_DECREF(temp); Py_DECREF(temp);
/* Check if any local variables must be converted to cell variables */ /* Check if any local variables must be converted to cell variables */
if (ste->ste_type == FunctionBlock && !analyze_cells(scopes, newfree, if (ste->ste_type == FunctionBlock && !analyze_cells(scopes, newfree))
NULL))
goto error; goto error;
else if (ste->ste_type == ClassBlock && !analyze_cells(scopes, newfree, else if (ste->ste_type == ClassBlock && !drop_class_free(ste, newfree))
"__class__"))
goto error; goto error;
/* Records the results of the analysis in the symbol table entry */ /* Records the results of the analysis in the symbol table entry */
if (!update_symbols(ste->ste_symbols, scopes, bound, newfree, if (!update_symbols(ste->ste_symbols, scopes, bound, newfree,
...@@ -1179,9 +1185,7 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s) ...@@ -1179,9 +1185,7 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
if (!symtable_enter_block(st, s->v.ClassDef.name, ClassBlock, if (!symtable_enter_block(st, s->v.ClassDef.name, ClassBlock,
(void *)s, s->lineno, s->col_offset)) (void *)s, s->lineno, s->col_offset))
VISIT_QUIT(st, 0); VISIT_QUIT(st, 0);
if (!GET_IDENTIFIER(__class__) || if (!GET_IDENTIFIER(__locals__) ||
!symtable_add_def(st, __class__, DEF_LOCAL) ||
!GET_IDENTIFIER(__locals__) ||
!symtable_add_def(st, __locals__, DEF_PARAM)) { !symtable_add_def(st, __locals__, DEF_PARAM)) {
symtable_exit_block(st, s); symtable_exit_block(st, s);
VISIT_QUIT(st, 0); VISIT_QUIT(st, 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment