mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-08-07 02:10:27 +00:00
Added LOAD_METHOD, CALL_METHOD opcodes
these were added in Python 3.7a1, so bytecode magic number has been updated refer python/cpython@f2392133eb
This commit is contained in:
parent
2b53efd719
commit
7328c09cd4
11 changed files with 245 additions and 5 deletions
2
third_party/python/Include/opcode.h
generated
vendored
2
third_party/python/Include/opcode.h
generated
vendored
|
@ -125,6 +125,8 @@ COSMOPOLITAN_C_START_
|
|||
#define BUILD_CONST_KEY_MAP 156
|
||||
#define BUILD_STRING 157
|
||||
#define BUILD_TUPLE_UNPACK_WITH_CALL 158
|
||||
#define LOAD_METHOD 160
|
||||
#define CALL_METHOD 161
|
||||
|
||||
/* EXCEPT_HANDLER is a special, implicit block type which is created when
|
||||
entering an except handler. It is not an opcode but we define it here
|
||||
|
|
|
@ -240,6 +240,7 @@ _code_type = type(_write_atomic.__code__)
|
|||
# Python 3.6b1 3377 (set __class__ cell from type.__new__ #23722)
|
||||
# Python 3.6b2 3378 (add BUILD_TUPLE_UNPACK_WITH_CALL #28257)
|
||||
# Python 3.6rc1 3379 (more thorough __class__ validation #23722)
|
||||
# Python 3.7a1 3390 (add LOAD_METHOD and CALL_METHOD opcodes #26110)
|
||||
#
|
||||
# MAGIC must change whenever the bytecode emitted by the compiler may no
|
||||
# longer be understood by older implementations of the eval loop (usually
|
||||
|
@ -248,7 +249,7 @@ _code_type = type(_write_atomic.__code__)
|
|||
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
|
||||
# in PC/launcher.c must also be updated.
|
||||
|
||||
MAGIC_NUMBER = (3379).to_bytes(2, 'little') + b'\r\n'
|
||||
MAGIC_NUMBER = (3390).to_bytes(2, 'little') + b'\r\n'
|
||||
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
|
||||
|
||||
_PYCACHE = '__pycache__'
|
||||
|
|
3
third_party/python/Lib/opcode.py
vendored
3
third_party/python/Lib/opcode.py
vendored
|
@ -208,4 +208,7 @@ def_op('BUILD_CONST_KEY_MAP', 156)
|
|||
def_op('BUILD_STRING', 157)
|
||||
def_op('BUILD_TUPLE_UNPACK_WITH_CALL', 158)
|
||||
|
||||
name_op('LOAD_METHOD', 160)
|
||||
def_op('CALL_METHOD', 161)
|
||||
|
||||
del def_op, name_op, jrel_op, jabs_op
|
||||
|
|
24
third_party/python/Lib/test/test_syntax.py
vendored
24
third_party/python/Lib/test/test_syntax.py
vendored
|
@ -203,6 +203,30 @@ three.
|
|||
Traceback (most recent call last):
|
||||
SyntaxError: more than 255 arguments
|
||||
|
||||
>>> class C:
|
||||
... def meth(self, *args):
|
||||
... return args
|
||||
>>> obj = C()
|
||||
>>> obj.meth(
|
||||
... 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
... 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
|
||||
... 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
|
||||
... 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
|
||||
... 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
|
||||
... 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
|
||||
... 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
|
||||
... 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
|
||||
... 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
|
||||
... 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163,
|
||||
... 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177,
|
||||
... 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
|
||||
... 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205,
|
||||
... 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
|
||||
... 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233,
|
||||
... 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
|
||||
... 248, 249, 250, 251, 252, 253, 254) # doctest: +ELLIPSIS
|
||||
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ..., 252, 253, 254)
|
||||
|
||||
>>> f(lambda x: x[0] = 3)
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: lambda cannot contain assignment
|
||||
|
|
83
third_party/python/Objects/object.c
vendored
83
third_party/python/Objects/object.c
vendored
|
@ -1091,6 +1091,89 @@ _PyObject_NextNotImplemented(PyObject *self)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Specialized version of _PyObject_GenericGetAttrWithDict
|
||||
specifically for the LOAD_METHOD opcode.
|
||||
|
||||
Return 1 if a method is found, 0 if it's a regular attribute
|
||||
from __dict__ or something returned by using a descriptor
|
||||
protocol.
|
||||
|
||||
`method` will point to the resolved attribute or NULL. In the
|
||||
latter case, an error will be set.
|
||||
*/
|
||||
int
|
||||
_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
|
||||
{
|
||||
PyTypeObject *tp = Py_TYPE(obj);
|
||||
PyObject *descr;
|
||||
descrgetfunc f = NULL;
|
||||
PyObject **dictptr, *dict;
|
||||
PyObject *attr;
|
||||
int meth_found = 0;
|
||||
|
||||
assert(*method == NULL);
|
||||
|
||||
if (Py_TYPE(obj)->tp_getattro != PyObject_GenericGetAttr
|
||||
|| !PyUnicode_Check(name)) {
|
||||
*method = PyObject_GetAttr(obj, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tp->tp_dict == NULL && PyType_Ready(tp) < 0)
|
||||
return 0;
|
||||
|
||||
descr = _PyType_Lookup(tp, name);
|
||||
if (descr != NULL) {
|
||||
Py_INCREF(descr);
|
||||
if (PyFunction_Check(descr) ||
|
||||
(Py_TYPE(descr) == &PyMethodDescr_Type)) {
|
||||
meth_found = 1;
|
||||
} else {
|
||||
f = descr->ob_type->tp_descr_get;
|
||||
if (f != NULL && PyDescr_IsData(descr)) {
|
||||
*method = f(descr, obj, (PyObject *)obj->ob_type);
|
||||
Py_DECREF(descr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dictptr = _PyObject_GetDictPtr(obj);
|
||||
if (dictptr != NULL && (dict = *dictptr) != NULL) {
|
||||
Py_INCREF(dict);
|
||||
attr = PyDict_GetItem(dict, name);
|
||||
if (attr != NULL) {
|
||||
Py_INCREF(attr);
|
||||
*method = attr;
|
||||
Py_DECREF(dict);
|
||||
Py_XDECREF(descr);
|
||||
return 0;
|
||||
}
|
||||
Py_DECREF(dict);
|
||||
}
|
||||
|
||||
if (meth_found) {
|
||||
*method = descr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (f != NULL) {
|
||||
*method = f(descr, obj, (PyObject *)Py_TYPE(obj));
|
||||
Py_DECREF(descr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (descr != NULL) {
|
||||
*method = descr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyErr_Format(PyExc_AttributeError,
|
||||
"'%.50s' object has no attribute '%U'",
|
||||
tp->tp_name, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Generic GetAttr functions - put these in your tp_[gs]etattro slot */
|
||||
|
||||
PyObject *
|
||||
|
|
1
third_party/python/PC/launcher.c
vendored
1
third_party/python/PC/launcher.c
vendored
|
@ -1095,6 +1095,7 @@ static PYC_MAGIC magic_values[] = {
|
|||
{ 3250, 3310, L"3.4" },
|
||||
{ 3320, 3351, L"3.5" },
|
||||
{ 3360, 3379, L"3.6" },
|
||||
{ 3390, 3399, L"3.7" },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
|
87
third_party/python/Python/ceval.c
vendored
87
third_party/python/Python/ceval.c
vendored
|
@ -51,6 +51,7 @@
|
|||
#define CHECKEXC 1 /* Double-check exception checking */
|
||||
#endif
|
||||
|
||||
extern int _PyObject_GetMethod(PyObject *, PyObject *, PyObject **);
|
||||
typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *);
|
||||
|
||||
#ifdef LLTRACE
|
||||
|
@ -3312,6 +3313,92 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(LOAD_METHOD) {
|
||||
/* Designed to work in tamdem with CALL_METHOD. */
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
PyObject *obj = TOP();
|
||||
PyObject *meth = NULL;
|
||||
|
||||
int meth_found = _PyObject_GetMethod(obj, name, &meth);
|
||||
|
||||
if (meth == NULL) {
|
||||
/* Most likely attribute wasn't found. */
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (meth_found) {
|
||||
/* We can bypass temporary bound method object.
|
||||
meth is unbound method and obj is self.
|
||||
|
||||
meth | self | arg1 | ... | argN
|
||||
*/
|
||||
SET_TOP(meth);
|
||||
PUSH(obj); // self
|
||||
}
|
||||
else {
|
||||
/* meth is not an unbound method (but a regular attr, or
|
||||
something was returned by a descriptor protocol). Set
|
||||
the second element of the stack to NULL, to signal
|
||||
CALL_METHOD that it's not a method call.
|
||||
|
||||
NULL | meth | arg1 | ... | argN
|
||||
*/
|
||||
SET_TOP(NULL);
|
||||
Py_DECREF(obj);
|
||||
PUSH(meth);
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(CALL_METHOD) {
|
||||
/* Designed to work in tamdem with LOAD_METHOD. */
|
||||
PyObject **sp, *res, *meth;
|
||||
|
||||
sp = stack_pointer;
|
||||
|
||||
meth = PEEK(oparg + 2);
|
||||
if (meth == NULL) {
|
||||
/* `meth` is NULL when LOAD_METHOD thinks that it's not
|
||||
a method call.
|
||||
|
||||
Stack layout:
|
||||
|
||||
... | NULL | callable | arg1 | ... | argN
|
||||
^- TOP()
|
||||
^- (-oparg)
|
||||
^- (-oparg-1)
|
||||
^- (-oparg-2)
|
||||
|
||||
`callable` will be POPed by call_function.
|
||||
NULL will will be POPed manually later.
|
||||
*/
|
||||
res = call_function(&sp, oparg, NULL);
|
||||
stack_pointer = sp;
|
||||
(void)POP(); /* POP the NULL. */
|
||||
}
|
||||
else {
|
||||
/* This is a method call. Stack layout:
|
||||
|
||||
... | method | self | arg1 | ... | argN
|
||||
^- TOP()
|
||||
^- (-oparg)
|
||||
^- (-oparg-1)
|
||||
^- (-oparg-2)
|
||||
|
||||
`self` and `method` will be POPed by call_function.
|
||||
We'll be passing `oparg + 1` to call_function, to
|
||||
make it accept the `self` as a first argument.
|
||||
*/
|
||||
res = call_function(&sp, oparg + 1, NULL);
|
||||
stack_pointer = sp;
|
||||
}
|
||||
|
||||
PUSH(res);
|
||||
if (res == NULL)
|
||||
goto error;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
PREDICTED(CALL_FUNCTION);
|
||||
TARGET(CALL_FUNCTION) {
|
||||
PyObject **sp, *res;
|
||||
|
|
39
third_party/python/Python/compile.c
vendored
39
third_party/python/Python/compile.c
vendored
|
@ -1058,6 +1058,8 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
|
|||
return -oparg;
|
||||
case CALL_FUNCTION:
|
||||
return -oparg;
|
||||
case CALL_METHOD:
|
||||
return -oparg-1;
|
||||
case CALL_FUNCTION_KW:
|
||||
return -oparg-1;
|
||||
case CALL_FUNCTION_EX:
|
||||
|
@ -1096,6 +1098,8 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
|
|||
/* If there's a fmt_spec on the stack, we go from 2->1,
|
||||
else 1->1. */
|
||||
return (oparg & FVS_MASK) == FVS_HAVE_SPEC ? -1 : 0;
|
||||
case LOAD_METHOD:
|
||||
return 1;
|
||||
default:
|
||||
return PY_INVALID_STACK_EFFECT;
|
||||
}
|
||||
|
@ -3415,9 +3419,44 @@ compiler_compare(struct compiler *c, expr_ty e)
|
|||
return 1;
|
||||
}
|
||||
|
||||
// Return 1 if the method call was optimized, -1 if not, and 0 on error.
|
||||
static int
|
||||
maybe_optimize_method_call(struct compiler *c, expr_ty e)
|
||||
{
|
||||
Py_ssize_t argsl, i;
|
||||
expr_ty meth = e->v.Call.func;
|
||||
asdl_seq *args = e->v.Call.args;
|
||||
|
||||
/* Check that the call node is an attribute access, and that
|
||||
the call doesn't have keyword parameters. */
|
||||
if (meth->kind != Attribute_kind || meth->v.Attribute.ctx != Load ||
|
||||
asdl_seq_LEN(e->v.Call.keywords))
|
||||
return -1;
|
||||
|
||||
/* Check that there are no *varargs types of arguments. */
|
||||
argsl = asdl_seq_LEN(args);
|
||||
for (i = 0; i < argsl; i++) {
|
||||
expr_ty elt = asdl_seq_GET(args, i);
|
||||
if (elt->kind == Starred_kind) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Alright, we can optimize the code. */
|
||||
VISIT(c, expr, meth->v.Attribute.value);
|
||||
ADDOP_NAME(c, LOAD_METHOD, meth->v.Attribute.attr, names);
|
||||
VISIT_SEQ(c, expr, e->v.Call.args);
|
||||
ADDOP_I(c, CALL_METHOD, asdl_seq_LEN(e->v.Call.args));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
compiler_call(struct compiler *c, expr_ty e)
|
||||
{
|
||||
int ret = maybe_optimize_method_call(c, e);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
}
|
||||
VISIT(c, expr, e->v.Call.func);
|
||||
return compiler_call_helper(c, 0,
|
||||
e->v.Call.args,
|
||||
|
|
4
third_party/python/Python/opcode_targets.inc
vendored
4
third_party/python/Python/opcode_targets.inc
vendored
|
@ -167,8 +167,8 @@ static void *const opcode_targets[256] = {
|
|||
&&TARGET_BUILD_STRING,
|
||||
&&TARGET_BUILD_TUPLE_UNPACK_WITH_CALL,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_LOAD_METHOD,
|
||||
&&TARGET_CALL_METHOD,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
|
|
2
third_party/python/pycomp.c
vendored
2
third_party/python/pycomp.c
vendored
|
@ -137,7 +137,7 @@ main(int argc, char *argv[])
|
|||
p = PyBytes_AS_STRING(marshalled);
|
||||
n = PyBytes_GET_SIZE(marshalled);
|
||||
CHECK_NE(-1, (fd = open(outpath, O_CREAT|O_TRUNC|O_WRONLY, 0644)));
|
||||
WRITE16LE(m+0, 3379); /* Python 3.6rc1 */
|
||||
WRITE16LE(m+0, 3390); /* Python 3.7a1 */
|
||||
WRITE16LE(m+2, READ16LE("\r\n"));
|
||||
WRITE32LE(m+4, st.st_mtim.tv_sec); /* tsk tsk y2038 */
|
||||
WRITE32LE(m+8, n);
|
||||
|
|
2
third_party/python/pyobj.c
vendored
2
third_party/python/pyobj.c
vendored
|
@ -641,7 +641,7 @@ Objectify(void)
|
|||
assert(PyBytes_CheckExact(marsh));
|
||||
mardata = PyBytes_AS_STRING(marsh);
|
||||
marsize = PyBytes_GET_SIZE(marsh);
|
||||
WRITE16LE(header+0, 3379); /* Python 3.6rc1 */
|
||||
WRITE16LE(header+0, 3390); /* Python 3.7a1 */
|
||||
WRITE16LE(header+2, READ16LE("\r\n"));
|
||||
WRITE32LE(header+4, timestamp.tv_sec);
|
||||
WRITE32LE(header+8, marsize);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue