POP_BLOCK LOAD_CONST L: END_FINALLY The special instructions use the block stack. Each block stack entry contains the instruction that created it (here SETUP_FINALLY), the level of the value stack at the time the block stack entry was created, and a label (here L). SETUP_FINALLY: Pushes the current value stack level and the label onto the block stack. POP_BLOCK: Pops en entry from the block stack, and pops the value stack until its level is the same as indicated on the block stack. (The label is ignored.) END_FINALLY: Pops a variable number of entries from the *value* stack and re-raises the exception they specify. The number of entries popped depends on the (pseudo) exception type. The block stack is unwound when an exception is raised: when a SETUP_FINALLY entry is found, the exception is pushed onto the value stack (and the exception condition is cleared), and the interpreter jumps to the label gotten from the block stack. */ static int compiler_try_finally(struct compiler *c, stmt_ty s) { basicblock *body, *end; body = compiler_new_block(c); end = compiler_new_block(c); if (body == NULL || end == NULL) return 0; ADDOP_JREL(c, SETUP_FINALLY, end); compiler_use_next_block(c, body); if (!compiler_push_fblock(c, FINALLY_TRY, body)) return 0; VISIT_SEQ(c, stmt, s->v.TryFinally.body); ADDOP(c, POP_BLOCK); compiler_pop_fblock(c, FINALLY_TRY, body); ADDOP_O(c, LOAD_CONST, Py_None, consts); compiler_use_next_block(c, end); if (!compiler_push_fblock(c, FINALLY_END, end)) return 0; VISIT_SEQ(c, stmt, s->v.TryFinally.finalbody); ADDOP(c, END_FINALLY); compiler_pop_fblock(c, FINALLY_END, end); return 1; } /* Code generated for "try: S except E1 as V1: S1 except E2 as V2: S2 ...": (The contents of the value stack is shown in [], with the top at the right; 'tb' is trace-back info, 'val' the exception's associated value, and 'exc' the exception.) Value stack Label Instruction Argument [] SETUP_EXCEPT L1 [] [] POP_BLOCK [] JUMP_FORWARD L0 [tb, val, exc] L1: DUP ) [tb, val, exc, exc] ) [tb, val, exc, exc, E1] COMPARE_OP EXC_MATCH ) only if E1 [tb, val, exc, 1-or-0] POP_JUMP_IF_FALSE L2 ) [tb, val, exc] POP [tb, val] (or POP if no V1) [tb] POP [] JUMP_FORWARD L0 [tb, val, exc] L2: DUP .............................etc....................... [tb, val, exc] Ln+1: END_FINALLY # re-raise exception [] L0: Of course, parts are not generated if Vi or Ei is not present. */ static int compiler_try_except(struct compiler *c, stmt_ty s) { basicblock *body, *orelse, *except, *end; int i, n; body = compiler_new_block(c); except = compiler_new_block(c); orelse = compiler_new_block(c); end = compiler_new_block(c); if (body == NULL || except == NULL || orelse == NULL || end == NULL) return 0; ADDOP_JREL(c, SETUP_EXCEPT, except); compiler_use_next_block(c, body); if (!compiler_push_fblock(c, EXCEPT, body)) return 0; VISIT_SEQ(c, stmt, s->v.TryExcept.body); ADDOP(c, POP_BLOCK); compiler_pop_fblock(c, EXCEPT, body); ADDOP_JREL(c, JUMP_FORWARD, orelse); n = asdl_seq_LEN(s->v.TryExcept.handlers); compiler_use_next_block(c, except); for (i = 0; i < n; i++) { excepthandler_ty handler = (excepthandler_ty)asdl_seq_GET( s->v.TryExcept.handlers, i); if (!handler->v.ExceptHandler.type && i < n-1) return compiler_error(c, "default 'except:' must be last"); c->u->u_lineno_set = 0; c->u->u_lineno = handler->lineno; c->u->u_col_offset = handler->col_offset; except = compiler_new_block(c); if (except == NULL) return 0; if (handler->v.ExceptHandler.type) { ADDOP(c, DUP_TOP); VISIT(c, expr, handler->v.ExceptHandler.type); ADDOP_I(c, COMPARE_OP, PyCmp_EXC_MATCH); ADDOP_JABS(c, POP_JUMP_IF_FALSE, except); } ADDOP(c, POP_TOP); if (handler->v.ExceptHandler.name) { basicblock *cleanup_end, *cleanup_body; cleanup_end = compiler_new_block(c); cleanup_body = compiler_new_block(c); if(!(cleanup_end || cleanup_body)) return 0; compiler_nameop(c, handler->v.ExceptHandler.name, Store); ADDOP(c, POP_TOP); /* try: # body except type as name: try: # body finally: name = None del name */ /* second try: */ ADDOP_JREL(c, SETUP_FINALLY, cleanup_end); compiler_use_next_block(c, cleanup_body); if (!compiler_push_fblock(c, FINALLY_TRY, cleanup_body)) return 0; /* second # body */ VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body); ADDOP(c, POP_BLOCK); ADDOP(c, POP_EXCEPT); compiler_pop_fblock(c, FINALLY_TRY, cleanup_body); /* finally: */ ADDOP_O(c, LOAD_CONST, Py_None, consts); compiler_use_next_block(c, cleanup_end); if (!compiler_push_fblock(c, FINALLY_END, cleanup_end)) return 0; /* name = None */ ADDOP_O(c, LOAD_CONST, Py_None, consts); compiler_nameop(c, handler->v.ExceptHandler.name, Store); /* del name */ compiler_nameop(c, handler->v.ExceptHandler.name, Del); ADDOP(c, END_FINALLY); compiler_pop_fblock(c, FINALLY_END, cleanup_end); } else { basicblock *cleanup_body; cleanup_body = compiler_new_block(c); if(!cleanup_body) return 0; ADDOP(c, POP_TOP); ADDOP(c, POP_TOP); compiler_use_next_block(c, cleanup_body); if (!compiler_push_fblock(c, FINALLY_TRY, cleanup_body)) return 0; VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body); ADDOP(c, POP_EXCEPT); compiler_pop_fblock(c, FINALLY_TRY, cleanup_body); } ADDOP_JREL(c, JUMP_FORWARD, end); compiler_use_next_block(c, except); } ADDOP(c, END_FINALLY); compiler_use_next_block(c, orelse); VISIT_SEQ(c, stmt, s->v.TryExcept.orelse); compiler_use_next_block(c, end); return 1; } static int compiler_import_as(struct compiler *c, identifier name, identifier asname) { /* The IMPORT_NAME opcode was already generated. This function merely needs to bind the result to a name. If there is a dot in name, we need to split it and emit a LOAD_ATTR for each name. */ const Py_UNICODE *src = PyUnicode_AS_UNICODE(name); const Py_UNICODE *dot = Py_UNICODE_strchr(src, '.'); if (dot) { /* Consume the base module name to get the first attribute */ src = dot + 1; while (dot) { /* NB src is only defined when dot != NULL */ PyObject *attr; dot = Py_UNICODE_strchr(src, '.'); attr = PyUnicode_FromUnicode(src, dot ? dot - src : Py_UNICODE_strlen(src)); if (!attr) return -1; ADDOP_O(c, LOAD_ATTR, attr, names); Py_DECREF(attr); src = dot + 1; } } return compiler_nameop(c, asname, Store); } static int compiler_import(struct compiler *c, stmt_ty s) { /* The Import node stores a module name like a.b.c as a single string. This is convenient for all cases except import a.b.c as d where we need to parse that string to extract the individual module names. XXX Perhaps change the representation to make this case simpler? */ int i, n = asdl_seq_LEN(s->v.Import.names); for (i = 0; i < n; i++) { alias_ty alias = (alias_ty)asdl_seq_GET(s->v.Import.names, i); int r; PyObject *level; level = PyLong_FromLong(0); if (level == NULL) return 0; ADDOP_O(c, LOAD_CONST, level, consts); Py_DECREF(level); ADDOP_O(c, LOAD_CONST, Py_None, consts); ADDOP_NAME(c, IMPORT_NAME, alias->name, names); if (alias->asname) { r = compiler_import_as(c, alias->name, alias->asname); if (!r) return r; } else { identifier tmp = alias->name; const Py_UNICODE *base = PyUnicode_AS_UNICODE(alias->name); Py_UNICODE *dot = Py_UNICODE_strchr(base, '.'); if (dot) tmp = PyUnicode_FromUnicode(base, dot - base); r = compiler_nameop(c, tmp, Store); if (dot) { Py_DECREF(tmp); } if (!r) return r; } } return 1; } static int compiler_from_import(struct compiler *c, stmt_ty s) { int i, n = asdl_seq_LEN(s->v.ImportFrom.names); PyObject *names = PyTuple_New(n); PyObject *level; static PyObject *empty_string; if (!empty_string) { empty_string = PyUnicode_FromString(""); if (!empty_string) return 0; } if (!names) return 0; level = PyLong_FromLong(s->v.ImportFrom.level); if (!level) { Py_DECREF(names); return 0; } /* build up the names */ for (i = 0; i < n; i++) { alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, i); Py_INCREF(alias->name); PyTuple_SET_ITEM(names, i, alias->name); } if (s->lineno > c->c_future->ff_lineno && s->v.ImportFrom.module && !PyUnicode_CompareWithASCIIString(s->v.ImportFrom.module, "__future__")) { Py_DECREF(level); Py_DECREF(names); return compiler_error(c, "from __future__ imports must occur " "at the beginning of the file"); } ADDOP_O(c, LOAD_CONST, level, consts); Py_DECREF(level); ADDOP_O(c, LOAD_CONST, names, consts); Py_DECREF(names); if (s->v.ImportFrom.module) { ADDOP_NAME(c, IMPORT_NAME, s->v.ImportFrom.module, names); } else { ADDOP_NAME(c, IMPORT_NAME, empty_string, names); } for (i = 0; i < n; i++) { alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, i); identifier store_name; if (i == 0 && *PyUnicode_AS_UNICODE(alias->name) == '*') { assert(n == 1); ADDOP(c, IMPORT_STAR); return 1; } ADDOP_NAME(c, IMPORT_FROM, alias->name, names); store_name = alias->name; if (alias->asname) store_name = alias->asname; if (!compiler_nameop(c, store_name, Store)) { Py_DECREF(names); return 0; } } /* remove imported module */ ADDOP(c, POP_TOP); return 1; } static int compiler_assert(struct compiler *c, stmt_ty s) { static PyObject *assertion_error = NULL; basicblock *end; if (Py_OptimizeFlag) return 1; if (assertion_error == NULL) { assertion_error = PyUnicode_InternFromString("AssertionError"); if (assertion_error == NULL) return 0; } if (s->v.Assert.test->kind == Tuple_kind && asdl_seq_LEN(s->v.Assert.test->v.Tuple.elts) > 0) { const char* msg = "assertion is always true, perhaps remove parentheses?"; if (PyErr_WarnExplicit(PyExc_SyntaxWarning, msg, c->c_filename, c->u->u_lineno, NULL, NULL) == -1) return 0; } VISIT(c, expr, s->v.Assert.test); end = compiler_new_block(c); if (end == NULL) return 0; ADDOP_JABS(c, POP_JUMP_IF_TRUE, end); ADDOP_O(c, LOAD_GLOBAL, assertion_error, names); if (s->v.Assert.msg) { VISIT(c, expr, s->v.Assert.msg); ADDOP_I(c, CALL_FUNCTION, 1); } ADDOP_I(c, RAISE_VARARGS, 1); compiler_use_next_block(c, end); return 1; } static int compiler_visit_stmt(struct compiler *c, stmt_ty s) { int i, n; /* Always assign a lineno to the next instruction for a stmt. */ c->u->u_lineno = s->lineno; c->u->u_col_offset = s->col_offset; c->u->u_lineno_set = 0; switch (s->kind) { case FunctionDef_kind: return compiler_function(c, s); case ClassDef_kind: return compiler_class(c, s); case Return_kind: if (c->u->u_ste->ste_type != FunctionBlock) return compiler_error(c, "'return' outside function"); if (s->v.Return.value) { VISIT(c, expr, s->v.Return.value); } else ADDOP_O(c, LOAD_CONST, Py_None, consts); ADDOP(c, RETURN_VALUE); break; case Delete_kind: VISIT_SEQ(c, expr, s->v.Delete.targets) break; case Assign_kind: n = asdl_seq_LEN(s->v.Assign.targets); VISIT(c, expr, s->v.Assign.value); for (i = 0; i < n; i++) { if (i < n - 1) ADDOP(c, DUP_TOP); VISIT(c, expr, (expr_ty)asdl_seq_GET(s->v.Assign.targets, i)); } break; case AugAssign_kind: return compiler_augassign(c, s); case For_kind: return compiler_for(c, s); case While_kind: return compiler_while(c, s); case If_kind: return compiler_if(c, s); case Raise_kind: n = 0; if (s->v.Raise.exc) { VISIT(c, expr, s->v.Raise.exc); n++; if (s->v.Raise.cause) { VISIT(c, expr, s->v.Raise.cause); n++; } } ADDOP_I(c, RAISE_VARARGS, n); break; case TryExcept_kind: return compiler_try_except(c, s); case TryFinally_kind: return compiler_try_finally(c, s); case Assert_kind: return compiler_assert(c, s); case Import_kind: return compiler_import(c, s); case ImportFrom_kind: return compiler_from_import(c, s); case Global_kind: case Nonlocal_kind: break; case Expr_kind: if (c->c_interactive && c->c_nestlevel <= 1) { VISIT(c, expr, s->v.Expr.value); ADDOP(c, PRINT_EXPR); } else if (s->v.Expr.value->kind != Str_kind && s->v.Expr.value->kind != Num_kind) { VISIT(c, expr, s->v.Expr.value); ADDOP(c, POP_TOP); } break; case Pass_kind: break; case Break_kind: if (!compiler_in_loop(c)) return compiler_error(c, "'break' outside loop"); ADDOP(c, BREAK_LOOP); break; case Continue_kind: return compiler_continue(c); case With_kind: return compiler_with(c, s); } return 1; } static int unaryop(unaryop_ty op) { switch (op) { case Invert: return UNARY_INVERT; case Not: return UNARY_NOT; case UAdd: return UNARY_POSITIVE; case USub: return UNARY_NEGATIVE; default: PyErr_Format(PyExc_SystemError, "unary op %d should not be possible", op); return 0; } } static int binop(struct compiler *c, operator_ty op) { switch (op) { case Add: return BINARY_ADD; case Sub: return BINARY_SUBTRACT; case Mult: return BINARY_MULTIPLY; case Div: return BINARY_TRUE_DIVIDE; case Mod: return BINARY_MODULO; case Pow: return BINARY_POWER; case LShift: return BINARY_LSHIFT; case RShift: return BINARY_RSHIFT; case BitOr: return BINARY_OR; case BitXor: return BINARY_XOR; case BitAnd: return BINARY_AND; case FloorDiv: return BINARY_FLOOR_DIVIDE; default: PyErr_Format(PyExc_SystemError, "binary op %d should not be possible", op); return 0; } } static int cmpop(cmpop_ty op) { switch (op) { case Eq: return PyCmp_EQ; case NotEq: return PyCmp_NE; case Lt: return PyCmp_LT; case LtE: return PyCmp_LE; case Gt: return PyCmp_GT; case GtE: return PyCmp_GE; case Is: return PyCmp_IS; case IsNot: return PyCmp_IS_NOT; case In: return PyCmp_IN; case NotIn: return PyCmp_NOT_IN; default: return PyCmp_BAD; } } static int inplace_binop(struct compiler *c, operator_ty op) { switch (op) { case Add: return INPLACE_ADD; case Sub: return INPLACE_SUBTRACT; case Mult: return INPLACE_MULTIPLY; case Div: return INPLACE_TRUE_DIVIDE; case Mod: return INPLACE_MODULO; case Pow: return INPLACE_POWER; case LShift: return INPLACE_LSHIFT; case RShift: return INPLACE_RSHIFT; case BitOr: return INPLACE_OR; case BitXor: return INPLACE_XOR; case BitAnd: return INPLACE_AND; case FloorDiv: return INPLACE_FLOOR_DIVIDE; default: PyErr_Format(PyExc_SystemError, "inplace binary op %d should not be possible", op); return 0; } } static int compiler_nameop(struct compiler *c, identifier name, expr_context_ty ctx) { int op, scope, arg; enum { OP_FAST, OP_GLOBAL, OP_DEREF, OP_NAME } optype; PyObject *dict = c->u->u_names; PyObject *mangled; /* XXX AugStore isn't used anywhere! */ mangled = _Py_Mangle(c->u->u_private, name); if (!mangled) return 0; op = 0; optype = OP_NAME; scope = PyST_GetScope(c->u->u_ste, mangled); switch (scope) { case FREE: dict = c->u->u_freevars; optype = OP_DEREF; break; case CELL: dict = c->u->u_cellvars; optype = OP_DEREF; break; case LOCAL: if (c->u->u_ste->ste_type == FunctionBlock) optype = OP_FAST; break; case GLOBAL_IMPLICIT: if (c->u->u_ste->ste_type == FunctionBlock && !c->u->u_ste->ste_unoptimized) optype = OP_GLOBAL; break; case GLOBAL_EXPLICIT: optype = OP_GLOBAL; break; default: /* scope can be 0 */ break; } /* XXX Leave assert here, but handle __doc__ and the like better */ assert(scope || PyUnicode_AS_UNICODE(name)[0] == '_'); switch (optype) { case OP_DEREF: switch (ctx) { case Load: op = LOAD_DEREF; break; case Store: op = STORE_DEREF; break; case AugLoad: case AugStore: break; case Del: op = DELETE_DEREF; break; case Param: default: PyErr_SetString(PyExc_SystemError, "param invalid for deref variable"); return 0; } break; case OP_FAST: switch (ctx) { case Load: op = LOAD_FAST; break; case Store: op = STORE_FAST; break; case Del: op = DELETE_FAST; break; case AugLoad: case AugStore: break; case Param: default: PyErr_SetString(PyExc_SystemError, "param invalid for local variable"); return 0; } ADDOP_O(c, op, mangled, varnames); Py_DECREF(mangled); return 1; case OP_GLOBAL: switch (ctx) { case Load: op = LOAD_GLOBAL; break; case Store: op = STORE_GLOBAL; break; case Del: op = DELETE_GLOBAL; break; case AugLoad: case AugStore: break; case Param: default: PyErr_SetString(PyExc_SystemError, "param invalid for global variable"); return 0; } break; case OP_NAME: switch (ctx) { case Load: op = LOAD_NAME; break; case Store: op = STORE_NAME; break; case Del: op = DELETE_NAME; break; case AugLoad: case AugStore: break; case Param: default: PyErr_SetString(PyExc_SystemError, "param invalid for name variable"); return 0; } break; } assert(op); arg = compiler_add_o(c, dict, mangled); Py_DECREF(mangled); if (arg < 0) return 0; return compiler_addop_i(c, op, arg); } static int compiler_boolop(struct compiler *c, expr_ty e) { basicblock *end; int jumpi, i, n; asdl_seq *s; assert(e->kind == BoolOp_kind); if (e->v.BoolOp.op == And) jumpi = JUMP_IF_FALSE_OR_POP; else jumpi = JUMP_IF_TRUE_OR_POP; end = compiler_new_block(c); if (end == NULL) return 0; s = e->v.BoolOp.values; n = asdl_seq_LEN(s) - 1; assert(n >= 0); for (i = 0; i < n; ++i) { VISIT(c, expr, (expr_ty)asdl_seq_GET(s, i)); ADDOP_JABS(c, jumpi, end); } VISIT(c, expr, (expr_ty)asdl_seq_GET(s, n)); compiler_use_next_block(c, end); return 1; } static int compiler_list(struct compiler *c, expr_ty e) { int n = asdl_seq_LEN(e->v.List.elts); if (e->v.List.ctx == Store) { int i, seen_star = 0; for (i = 0; i < n; i++) { expr_ty elt = asdl_seq_GET(e->v.List.elts, i); if (elt->kind == Starred_kind && !seen_star) { if ((i >= (1 << 8)) || (n-i-1 >= (INT_MAX >> 8))) return compiler_error(c, "too many expressions in " "star-unpacking assignment"); ADDOP_I(c, UNPACK_EX, (i + ((n-i-1) << 8))); seen_star = 1; asdl_seq_SET(e->v.List.elts, i, elt->v.Starred.value); } else if (elt->kind == Starred_kind) { return compiler_error(c, "two starred expressions in assignment"); } } if (!seen_star) { ADDOP_I(c, UNPACK_SEQUENCE, n); } } VISIT_SEQ(c, expr, e->v.List.elts); if (e->v.List.ctx == Load) { ADDOP_I(c, BUILD_LIST, n); } return 1; } static int compiler_tuple(struct compiler *c, expr_ty e) { int n = asdl_seq_LEN(e->v.Tuple.elts); if (e->v.Tuple.ctx == Store) { int i, seen_star = 0; for (i = 0; i < n; i++) { expr_ty elt = asdl_seq_GET(e->v.Tuple.elts, i); if (elt->kind == Starred_kind && !seen_star) { if ((i >= (1 << 8)) || (n-i-1 >= (INT_MAX >> 8))) return compiler_error(c, "too many expressions in " "star-unpacking assignment"); ADDOP_I(c, UNPACK_EX, (i + ((n-i-1) << 8))); seen_star = 1; asdl_seq_SET(e->v.Tuple.elts, i, elt->v.Starred.value); } else if (elt->kind == Starred_kind) { return compiler_error(c, "two starred expressions in assignment"); } } if (!seen_star) { ADDOP_I(c, UNPACK_SEQUENCE, n); } } VISIT_SEQ(c, expr, e->v.Tuple.elts); if (e->v.Tuple.ctx == Load) { ADDOP_I(c, BUILD_TUPLE, n); } return 1; } static int compiler_compare(struct compiler *c, expr_ty e) { int i, n; basicblock *cleanup = NULL; /* XXX the logic can be cleaned up for 1 or multiple comparisons */ VISIT(c, expr, e->v.Compare.left); n = asdl_seq_LEN(e->v.Compare.ops); assert(n > 0); if (n > 1) { cleanup = compiler_new_block(c); if (cleanup == NULL) return 0; VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, 0)); } for (i = 1; i < n; i++) { ADDOP(c, DUP_TOP); ADDOP(c, ROT_THREE); ADDOP_I(c, COMPARE_OP, cmpop((cmpop_ty)(asdl_seq_GET( e->v.Compare.ops, i - 1)))); ADDOP_JABS(c, JUMP_IF_FALSE_OR_POP, cleanup); NEXT_BLOCK(c); if (i < (n - 1)) VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, i)); } VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n - 1)); ADDOP_I(c, COMPARE_OP, cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, n - 1)))); if (n > 1) { basicblock *end = compiler_new_block(c); if (end == NULL) return 0; ADDOP_JREL(c, JUMP_FORWARD, end); compiler_use_next_block(c, cleanup); ADDOP(c, ROT_TWO); ADDOP(c, POP_TOP); compiler_use_next_block(c, end); } return 1; } static int compiler_call(struct compiler *c, expr_ty e) { VISIT(c, expr, e->v.Call.func); return compiler_call_helper(c, 0, e->v.Call.args, e->v.Call.keywords, e->v.Call.starargs, e->v.Call.kwargs); } /* shared code between compiler_call and compiler_class */ static int compiler_call_helper(struct compiler *c, int n, /* Args already pushed */ asdl_seq *args, asdl_seq *keywords, expr_ty starargs, expr_ty kwargs) { int code = 0; n += asdl_seq_LEN(args); VISIT_SEQ(c, expr, args); if (keywords) { VISIT_SEQ(c, keyword, keywords); n |= asdl_seq_LEN(keywords) << 8; } if (starargs) { VISIT(c, expr, starargs); code |= 1; } if (kwargs) { VISIT(c, expr, kwargs); code |= 2; } switch (code) { case 0: ADDOP_I(c, CALL_FUNCTION, n); break; case 1: ADDOP_I(c, CALL_FUNCTION_VAR, n); break; case 2: ADDOP_I(c, CALL_FUNCTION_KW, n); break; case 3: ADDOP_I(c, CALL_FUNCTION_VAR_KW, n); break; } return 1; } /* List and set comprehensions and generator expressions work by creating a nested function to perform the actual iteration. This means that the iteration variables don't leak into the current scope. The defined function is called immediately following its definition, with the result of that call being the result of the expression. The LC/SC version returns the populated container, while the GE version is flagged in symtable.c as a generator, so it returns the generator object when the function is called. This code *knows* that the loop cannot contain break, continue, or return, so it cheats and skips the SETUP_LOOP/POP_BLOCK steps used in normal loops. Possible cleanups: - iterate over the generator sequence instead of using recursion */ static int compiler_comprehension_generator(struct compiler *c, asdl_seq *generators, int gen_index, expr_ty elt, expr_ty val, int type) { /* generate code for the iterator, then each of the ifs, and then write to the element */ comprehension_ty gen; basicblock *start, *anchor, *skip, *if_cleanup; int i, n; start = compiler_new_block(c); skip = compiler_new_block(c); if_cleanup = compiler_new_block(c); anchor = compiler_new_block(c); if (start == NULL || skip == NULL || if_cleanup == NULL || anchor == NULL) return 0; gen = (comprehension_ty)asdl_seq_GET(generators, gen_index); if (gen_index == 0) { /* Receive outermost iter as an implicit argument */ c->u->u_argcount = 1; ADDOP_I(c, LOAD_FAST, 0); } else { /* Sub-iter - calculate on the fly */ VISIT(c, expr, gen->iter); ADDOP(c, GET_ITER); } compiler_use_next_block(c, start); ADDOP_JREL(c, FOR_ITER, anchor); NEXT_BLOCK(c); VISIT(c, expr, gen->target); /* XXX this needs to be cleaned up...a lot! */ n = asdl_seq_LEN(gen->ifs); for (i = 0; i < n; i++) { expr_ty e = (expr_ty)asdl_seq_GET(gen->ifs, i); VISIT(c, expr, e); ADDOP_JABS(c, POP_JUMP_IF_FALSE, if_cleanup); NEXT_BLOCK(c); } if (++gen_index < asdl_seq_LEN(generators)) if (!compiler_comprehension_generator(c, generators, gen_index, elt, val, type)) return 0; /* only append after the last for generator */ if (gen_index >= asdl_seq_LEN(generators)) { /* comprehension specific code */ switch (type) { case COMP_GENEXP: VISIT(c, expr, elt); ADDOP(c, YIELD_VALUE); ADDOP(c, POP_TOP); break; case COMP_LISTCOMP: VISIT(c, expr, elt); ADDOP_I(c, LIST_APPEND, gen_index + 1); break; case COMP_SETCOMP: VISIT(c, expr, elt); ADDOP_I(c, SET_ADD, gen_index + 1); break; case COMP_DICTCOMP: /* With 'd[k] = v', v is evaluated before k, so we do the same. */ VISIT(c, expr, val); VISIT(c, expr, elt); ADDOP_I(c, MAP_ADD, gen_index + 1); break; default: return 0; } compiler_use_next_block(c, skip); } compiler_use_next_block(c, if_cleanup); ADDOP_JABS(c, JUMP_ABSOLUTE, start); compiler_use_next_block(c, anchor); return 1; } static int compiler_comprehension(struct compiler *c, expr_ty e, int type, identifier name, asdl_seq *generators, expr_ty elt, expr_ty val) { PyCodeObject *co = NULL; expr_ty outermost_iter; outermost_iter = ((comprehension_ty) asdl_seq_GET(generators, 0))->iter; if (!compiler_enter_scope(c, name, (void *)e, e->lineno)) goto error; if (type != COMP_GENEXP) { int op; switch (type) { case COMP_LISTCOMP: op = BUILD_LIST; break; case COMP_SETCOMP: op = BUILD_SET; break; case COMP_DICTCOMP: op = BUILD_MAP; break; default: PyErr_Format(PyExc_SystemError, "unknown comprehension type %d", type); goto error_in_scope; } ADDOP_I(c, op, 0); } if (!compiler_comprehension_generator(c, generators, 0, elt, val, type)) goto error_in_scope; if (type != COMP_GENEXP) { ADDOP(c, RETURN_VALUE); } co = assemble(c, 1); compiler_exit_scope(c); if (co == NULL) goto error; if (!compiler_make_closure(c, co, 0)) goto error; Py_DECREF(co); VISIT(c, expr, outermost_iter); ADDOP(c, GET_ITER); ADDOP_I(c, CALL_FUNCTION, 1); return 1; error_in_scope: compiler_exit_scope(c); error: Py_XDECREF(co); return 0; } static int compiler_genexp(struct compiler *c, expr_ty e) { static identifier name; if (!name) { name = PyUnicode_FromString(" "); if (!name) return 0; } assert(e->kind == GeneratorExp_kind); return compiler_comprehension(c, e, COMP_GENEXP, name, e->v.GeneratorExp.generators, e->v.GeneratorExp.elt, NULL); } static int compiler_listcomp(struct compiler *c, expr_ty e) { static identifier name; if (!name) { name = PyUnicode_FromString(" "); if (!name) return 0; } assert(e->kind == ListComp_kind); return compiler_comprehension(c, e, COMP_LISTCOMP, name, e->v.ListComp.generators, e->v.ListComp.elt, NULL); } static int compiler_setcomp(struct compiler *c, expr_ty e) { static identifier name; if (!name) { name = PyUnicode_FromString(" "); if (!name) return 0; } assert(e->kind == SetComp_kind); return compiler_comprehension(c, e, COMP_SETCOMP, name, e->v.SetComp.generators, e->v.SetComp.elt, NULL); } static int compiler_dictcomp(struct compiler *c, expr_ty e) { static identifier name; if (!name) { name = PyUnicode_FromString(" "); if (!name) return 0; } assert(e->kind == DictComp_kind); return compiler_comprehension(c, e, COMP_DICTCOMP, name, e->v.DictComp.generators, e->v.DictComp.key, e->v.DictComp.value); } static int compiler_visit_keyword(struct compiler *c, keyword_ty k) { ADDOP_O(c, LOAD_CONST, k->arg, consts); VISIT(c, expr, k->value); return 1; } /* Test whether expression is constant. For constants, report whether they are true or false. Return values: 1 for true, 0 for false, -1 for non-constant. */ static int expr_constant(expr_ty e) { char *id; switch (e->kind) { case Ellipsis_kind: return 1; case Num_kind: return PyObject_IsTrue(e->v.Num.n); case Str_kind: return PyObject_IsTrue(e->v.Str.s); case Name_kind: /* optimize away names that can't be reassigned */ id = PyBytes_AS_STRING( _PyUnicode_AsDefaultEncodedString(e->v.Name.id, NULL)); if (strcmp(id, "True") == 0) return 1; if (strcmp(id, "False") == 0) return 0; if (strcmp(id, "None") == 0) return 0; if (strcmp(id, "__debug__") == 0) return ! Py_OptimizeFlag; /* fall through */ default: return -1; } } /* Implements the with statement from PEP 343. The semantics outlined in that PEP are as follows: with EXPR as VAR: BLOCK It is implemented roughly as: context = EXPR exit = context.__exit__ # not calling it value = context.__enter__() try: VAR = value # if VAR present in the syntax BLOCK finally: if an exception was raised: exc = copy of (exception, instance, traceback) else: exc = (None, None, None) exit(*exc) */ static int compiler_with(struct compiler *c, stmt_ty s) { basicblock *block, *finally; assert(s->kind == With_kind); block = compiler_new_block(c); finally = compiler_new_block(c); if (!block || !finally) return 0; /* Evaluate EXPR */ VISIT(c, expr, s->v.With.context_expr); ADDOP_JREL(c, SETUP_WITH, finally); /* SETUP_WITH pushes a finally block. */ compiler_use_next_block(c, block); if (!compiler_push_fblock(c, FINALLY_TRY, block)) { return 0; } if (s->v.With.optional_vars) { VISIT(c, expr, s->v.With.optional_vars); } else { /* Discard result from context.__enter__() */ ADDOP(c, POP_TOP); } /* BLOCK code */ VISIT_SEQ(c, stmt, s->v.With.body); /* End of try block; start the finally block */ ADDOP(c, POP_BLOCK); compiler_pop_fblock(c, FINALLY_TRY, block); ADDOP_O(c, LOAD_CONST, Py_None, consts); compiler_use_next_block(c, finally); if (!compiler_push_fblock(c, FINALLY_END, finally)) return 0; /* Finally block starts; context.__exit__ is on the stack under the exception or return information. Just issue our magic opcode. */ ADDOP(c, WITH_CLEANUP); /* Finally block ends. */ ADDOP(c, END_FINALLY); compiler_pop_fblock(c, FINALLY_END, finally); return 1; } static int compiler_visit_expr(struct compiler *c, expr_ty e) { int i, n; /* If expr e has a different line number than the last expr/stmt, set a new line number for the next instruction. */ if (e->lineno > c->u->u_lineno) { c->u->u_lineno = e->lineno; c->u->u_lineno_set = 0; } /* Updating the column offset is always harmless. */ c->u->u_col_offset = e->col_offset; switch (e->kind) { case BoolOp_kind: return compiler_boolop(c, e); case BinOp_kind: VISIT(c, expr, e->v.BinOp.left); VISIT(c, expr, e->v.BinOp.right); ADDOP(c, binop(c, e->v.BinOp.op)); break; case UnaryOp_kind: VISIT(c, expr, e->v.UnaryOp.operand); ADDOP(c, unaryop(e->v.UnaryOp.op)); break; case Lambda_kind: return compiler_lambda(c, e); case IfExp_kind: return compiler_ifexp(c, e); case Dict_kind: n = asdl_seq_LEN(e->v.Dict.values); ADDOP_I(c, BUILD_MAP, (n>0xFFFF ? 0xFFFF : n)); for (i = 0; i < n; i++) { VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Dict.values, i)); VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Dict.keys, i)); ADDOP(c, STORE_MAP); } break; case Set_kind: n = asdl_seq_LEN(e->v.Set.elts); VISIT_SEQ(c, expr, e->v.Set.elts); ADDOP_I(c, BUILD_SET, n); break; case GeneratorExp_kind: return compiler_genexp(c, e); case ListComp_kind: return compiler_listcomp(c, e); case SetComp_kind: return compiler_setcomp(c, e); case DictComp_kind: return compiler_dictcomp(c, e); case Yield_kind: if (c->u->u_ste->ste_type != FunctionBlock) return compiler_error(c, "'yield' outside function"); if (e->v.Yield.value) { VISIT(c, expr, e->v.Yield.value); } else { ADDOP_O(c, LOAD_CONST, Py_None, consts); } ADDOP(c, YIELD_VALUE); break; case Compare_kind: return compiler_compare(c, e); case Call_kind: return compiler_call(c, e); case Num_kind: ADDOP_O(c, LOAD_CONST, e->v.Num.n, consts); break; case Str_kind: ADDOP_O(c, LOAD_CONST, e->v.Str.s, consts); break; case Bytes_kind: ADDOP_O(c, LOAD_CONST, e->v.Bytes.s, consts); break; case Ellipsis_kind: ADDOP_O(c, LOAD_CONST, Py_Ellipsis, consts); break; /* The following exprs can be assignment targets. */ case Attribute_kind: if (e->v.Attribute.ctx != AugStore) VISIT(c, expr, e->v.Attribute.value); switch (e->v.Attribute.ctx) { case AugLoad: ADDOP(c, DUP_TOP); /* Fall through to load */ case Load: ADDOP_NAME(c, LOAD_ATTR, e->v.Attribute.attr, names); break; case AugStore: ADDOP(c, ROT_TWO); /* Fall through to save */ case Store: ADDOP_NAME(c, STORE_ATTR, e->v.Attribute.attr, names); break; case Del: ADDOP_NAME(c, DELETE_ATTR, e->v.Attribute.attr, names); break; case Param: default: PyErr_SetString(PyExc_SystemError, "param invalid in attribute expression"); return 0; } break; case Subscript_kind: switch (e->v.Subscript.ctx) { case AugLoad: VISIT(c, expr, e->v.Subscript.value); VISIT_SLICE(c, e->v.Subscript.slice, AugLoad); break; case Load: VISIT(c, expr, e->v.Subscript.value); VISIT_SLICE(c, e->v.Subscript.slice, Load); break; case AugStore: VISIT_SLICE(c, e->v.Subscript.slice, AugStore); break; case Store: VISIT(c, expr, e->v.Subscript.value); VISIT_SLICE(c, e->v.Subscript.slice, Store); break; case Del: VISIT(c, expr, e->v.Subscript.value); VISIT_SLICE(c, e->v.Subscript.slice, Del); break; case Param: default: PyErr_SetString(PyExc_SystemError, "param invalid in subscript expression"); return 0; } break; case Starred_kind: switch (e->v.Starred.ctx) { case Store: /* In all legitimate cases, the Starred node was already replaced * by compiler_list/compiler_tuple. XXX: is that okay? */ return compiler_error(c, "starred assignment target must be in a list or tuple"); default: return compiler_error(c, "can use starred expression only as assignment target"); } break; case Name_kind: return compiler_nameop(c, e->v.Name.id, e->v.Name.ctx); /* child nodes of List and Tuple will have expr_context set */ case List_kind: return compiler_list(c, e); case Tuple_kind: return compiler_tuple(c, e); } return 1; } static int compiler_augassign(struct compiler *c, stmt_ty s) { expr_ty e = s->v.AugAssign.target; expr_ty auge; assert(s->kind == AugAssign_kind); switch (e->kind) { case Attribute_kind: auge = Attribute(e->v.Attribute.value, e->v.Attribute.attr, AugLoad, e->lineno, e->col_offset, c->c_arena); if (auge == NULL) return 0; VISIT(c, expr, auge); VISIT(c, expr, s->v.AugAssign.value); ADDOP(c, inplace_binop(c, s->v.AugAssign.op)); auge->v.Attribute.ctx = AugStore; VISIT(c, expr, auge); break; case Subscript_kind: auge = Subscript(e->v.Subscript.value, e->v.Subscript.slice, AugLoad, e->lineno, e->col_offset, c->c_arena); if (auge == NULL) return 0; VISIT(c, expr, auge); VISIT(c, expr, s->v.AugAssign.value); ADDOP(c, inplace_binop(c, s->v.AugAssign.op)); auge->v.Subscript.ctx = AugStore; VISIT(c, expr, auge); break; case Name_kind: if (!compiler_nameop(c, e->v.Name.id, Load)) return 0; VISIT(c, expr, s->v.AugAssign.value); ADDOP(c, inplace_binop(c, s->v.AugAssign.op)); return compiler_nameop(c, e->v.Name.id, Store); default: PyErr_Format(PyExc_SystemError, "invalid node type (%d) for augmented assignment", e->kind); return 0; } return 1; } static int compiler_push_fblock(struct compiler *c, enum fblocktype t, basicblock *b) { struct fblockinfo *f; if (c->u->u_nfblocks >= CO_MAXBLOCKS) { PyErr_SetString(PyExc_SystemError, "too many statically nested blocks"); return 0; } f = &c->u->u_fblock[c->u->u_nfblocks++]; f->fb_type = t; f->fb_block = b; return 1; } static void compiler_pop_fblock(struct compiler *c, enum fblocktype t, basicblock *b) { 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(u->u_fblock[u->u_nfblocks].fb_block == b); } static int compiler_in_loop(struct compiler *c) { int i; struct compiler_unit *u = c->u; for (i = 0; i < u->u_nfblocks; ++i) { if (u->u_fblock[i].fb_type == LOOP) return 1; } return 0; } /* Raises a SyntaxError and returns 0. If something goes wrong, a different exception may be raised. */ static int compiler_error(struct compiler *c, const char *errstr) { PyObject *loc; PyObject *u = NULL, *v = NULL; loc = PyErr_ProgramText(c->c_filename, c->u->u_lineno); if (!loc) { Py_INCREF(Py_None); loc = Py_None; } u = Py_BuildValue("(ziiO)", c->c_filename, c->u->u_lineno, c->u->u_col_offset, loc); if (!u) goto exit; v = Py_BuildValue("(zO)", errstr, u); if (!v) goto exit; PyErr_SetObject(PyExc_SyntaxError, v); exit: Py_DECREF(loc); Py_XDECREF(u); Py_XDECREF(v); return 0; } static int compiler_handle_subscr(struct compiler *c, const char *kind, expr_context_ty ctx) { int op = 0; /* XXX this code is duplicated */ switch (ctx) { case AugLoad: /* fall through to Load */ case Load: op = BINARY_SUBSCR; break; case AugStore:/* fall through to Store */ case Store: op = STORE_SUBSCR; break; case Del: op = DELETE_SUBSCR; break; case Param: PyErr_Format(PyExc_SystemError, "invalid %s kind %d in subscript\n", kind, ctx); return 0; } if (ctx == AugLoad) { ADDOP(c, DUP_TOP_TWO); } else if (ctx == AugStore) { ADDOP(c, ROT_THREE); } ADDOP(c, op); return 1; } static int compiler_slice(struct compiler *c, slice_ty s, expr_context_ty ctx) { int n = 2; assert(s->kind == Slice_kind); /* only handles the cases where BUILD_SLICE is emitted */ if (s->v.Slice.lower) { VISIT(c, expr, s->v.Slice.lower); } else { ADDOP_O(c, LOAD_CONST, Py_None, consts); } if (s->v.Slice.upper) { VISIT(c, expr, s->v.Slice.upper); } else { ADDOP_O(c, LOAD_CONST, Py_None, consts); } if (s->v.Slice.step) { n++; VISIT(c, expr, s->v.Slice.step); } ADDOP_I(c, BUILD_SLICE, n); return 1; } static int compiler_visit_nested_slice(struct compiler *c, slice_ty s, expr_context_ty ctx) { switch (s->kind) { case Slice_kind: return compiler_slice(c, s, ctx); case Index_kind: VISIT(c, expr, s->v.Index.value); break; case ExtSlice_kind: default: PyErr_SetString(PyExc_SystemError, "extended slice invalid in nested slice"); return 0; } return 1; } static int compiler_visit_slice(struct compiler *c, slice_ty s, expr_context_ty ctx) { char * kindname = NULL; switch (s->kind) { case Index_kind: kindname = "index"; if (ctx != AugStore) { VISIT(c, expr, s->v.Index.value); } break; case Slice_kind: kindname = "slice"; if (ctx != AugStore) { if (!compiler_slice(c, s, ctx)) return 0; } break; case ExtSlice_kind: kindname = "extended slice"; if (ctx != AugStore) { int i, n = asdl_seq_LEN(s->v.ExtSlice.dims); for (i = 0; i < n; i++) { slice_ty sub = (slice_ty)asdl_seq_GET( s->v.ExtSlice.dims, i); if (!compiler_visit_nested_slice(c, sub, ctx)) return 0; } ADDOP_I(c, BUILD_TUPLE, n); } break; default: PyErr_Format(PyExc_SystemError, "invalid subscript kind %d", s->kind); return 0; } return compiler_handle_subscr(c, kindname, ctx); } /* End of the compiler section, beginning of the assembler section */ /* do depth-first search of basic block graph, starting with block. post records the block indices in post-order. XXX must handle implicit jumps from one block to next */ struct assembler { PyObject *a_bytecode; /* string containing bytecode */ int a_offset; /* offset into bytecode */ int a_nblocks; /* number of reachable blocks */ basicblock **a_postorder; /* list of blocks in dfs postorder */ PyObject *a_lnotab; /* string containing lnotab */ int a_lnotab_off; /* offset into lnotab */ int a_lineno; /* last lineno of emitted instruction */ int a_lineno_off; /* bytecode offset of last lineno */ }; static void dfs(struct compiler *c, basicblock *b, struct assembler *a) { int i; struct instr *instr = NULL; if (b->b_seen) return; b->b_seen = 1; if (b->b_next != NULL) dfs(c, b->b_next, a); for (i = 0; i < b->b_iused; i++) { instr = &b->b_instr[i]; if (instr->i_jrel || instr->i_jabs) dfs(c, instr->i_target, a); } a->a_postorder[a->a_nblocks++] = b; } static int stackdepth_walk(struct compiler *c, basicblock *b, int depth, int maxdepth) { int i, target_depth; struct instr *instr; if (b->b_seen || b->b_startdepth >= depth) return maxdepth; b->b_seen = 1; b->b_startdepth = depth; for (i = 0; i < b->b_iused; i++) { instr = &b->b_instr[i]; depth += opcode_stack_effect(instr->i_opcode, instr->i_oparg); if (depth > maxdepth) maxdepth = depth; assert(depth >= 0); /* invalid code or bug in stackdepth() */ if (instr->i_jrel || instr->i_jabs) { target_depth = depth; if (instr->i_opcode == FOR_ITER) { target_depth = depth-2; } else if (instr->i_opcode == SETUP_FINALLY || instr->i_opcode == SETUP_EXCEPT) { target_depth = depth+3; if (target_depth > maxdepth) maxdepth = target_depth; } maxdepth = stackdepth_walk(c, instr->i_target, target_depth, maxdepth); if (instr->i_opcode == JUMP_ABSOLUTE || instr->i_opcode == JUMP_FORWARD) { goto out; /* remaining code is dead */ } } } if (b->b_next) maxdepth = stackdepth_walk(c, b->b_next, depth, maxdepth); out: b->b_seen = 0; return maxdepth; } /* Find the flow path that needs the largest stack. We assume that * cycles in the flow graph have no net effect on the stack depth. */ static int stackdepth(struct compiler *c) { basicblock *b, *entryblock; entryblock = NULL; for (b = c->u->u_blocks; b != NULL; b = b->b_list) { b->b_seen = 0; b->b_startdepth = INT_MIN; entryblock = b; } if (!entryblock) return 0; return stackdepth_walk(c, entryblock, 0, 0); } static int assemble_init(struct assembler *a, int nblocks, int firstlineno) { memset(a, 0, sizeof(struct assembler)); a->a_lineno = firstlineno; a->a_bytecode = PyBytes_FromStringAndSize(NULL, DEFAULT_CODE_SIZE); if (!a->a_bytecode) return 0; a->a_lnotab = PyBytes_FromStringAndSize(NULL, DEFAULT_LNOTAB_SIZE); if (!a->a_lnotab) return 0; if (nblocks > PY_SIZE_MAX / sizeof(basicblock *)) { PyErr_NoMemory(); return 0; } a->a_postorder = (basicblock **)PyObject_Malloc( sizeof(basicblock *) * nblocks); if (!a->a_postorder) { PyErr_NoMemory(); return 0; } return 1; } static void assemble_free(struct assembler *a) { Py_XDECREF(a->a_bytecode); Py_XDECREF(a->a_lnotab); if (a->a_postorder) PyObject_Free(a->a_postorder); } /* Return the size of a basic block in bytes. */ static int instrsize(struct instr *instr) { if (!instr->i_hasarg) return 1; /* 1 byte for the opcode*/ if (instr->i_oparg > 0xffff) return 6; /* 1 (opcode) + 1 (EXTENDED_ARG opcode) + 2 (oparg) + 2(oparg extended) */ return 3; /* 1 (opcode) + 2 (oparg) */ } static int blocksize(basicblock *b) { int i; int size = 0; for (i = 0; i < b->b_iused; i++) size += instrsize(&b->b_instr[i]); return size; } /* Appends a pair to the end of the line number table, a_lnotab, representing the instruction's bytecode offset and line number. See Objects/lnotab_notes.txt for the description of the line number table. */ static int assemble_lnotab(struct assembler *a, struct instr *i) { int d_bytecode, d_lineno; int len; unsigned char *lnotab; d_bytecode = a->a_offset - a->a_lineno_off; d_lineno = i->i_lineno - a->a_lineno; assert(d_bytecode >= 0); assert(d_lineno >= 0); if(d_bytecode == 0 && d_lineno == 0) return 1; if (d_bytecode > 255) { int j, nbytes, ncodes = d_bytecode / 255; nbytes = a->a_lnotab_off + 2 * ncodes; len = PyBytes_GET_SIZE(a->a_lnotab); if (nbytes >= len) { if ((len <= INT_MAX / 2) && (len * 2 < nbytes)) len = nbytes; else if (len <= INT_MAX / 2) len *= 2; else { PyErr_NoMemory(); return 0; } if (_PyBytes_Resize(&a->a_lnotab, len) < 0) return 0; } lnotab = (unsigned char *) PyBytes_AS_STRING(a->a_lnotab) + a->a_lnotab_off; for (j = 0; j < ncodes; j++) { *lnotab++ = 255; *lnotab++ = 0; } d_bytecode -= ncodes * 255; a->a_lnotab_off += ncodes * 2; } assert(d_bytecode <= 255); if (d_lineno > 255) { int j, nbytes, ncodes = d_lineno / 255; nbytes = a->a_lnotab_off + 2 * ncodes; len = PyBytes_GET_SIZE(a->a_lnotab); if (nbytes >= len) { if ((len <= INT_MAX / 2) && len * 2 < nbytes) len = nbytes; else if (len <= INT_MAX / 2) len *= 2; else { PyErr_NoMemory(); return 0; } if (_PyBytes_Resize(&a->a_lnotab, len) < 0) return 0; } lnotab = (unsigned char *) PyBytes_AS_STRING(a->a_lnotab) + a->a_lnotab_off; *lnotab++ = d_bytecode; *lnotab++ = 255; d_bytecode = 0; for (j = 1; j < ncodes; j++) { *lnotab++ = 0; *lnotab++ = 255; } d_lineno -= ncodes * 255; a->a_lnotab_off += ncodes * 2; } len = PyBytes_GET_SIZE(a->a_lnotab); if (a->a_lnotab_off + 2 >= len) { if (_PyBytes_Resize(&a->a_lnotab, len * 2) < 0) return 0; } lnotab = (unsigned char *) PyBytes_AS_STRING(a->a_lnotab) + a->a_lnotab_off; a->a_lnotab_off += 2; if (d_bytecode) { *lnotab++ = d_bytecode; *lnotab++ = d_lineno; } else { /* First line of a block; def stmt, etc. */ *lnotab++ = 0; *lnotab++ = d_lineno; } a->a_lineno = i->i_lineno; a->a_lineno_off = a->a_offset; return 1; } /* assemble_emit() Extend the bytecode with a new instruction. Update lnotab if necessary. */ static int assemble_emit(struct assembler *a, struct instr *i) { int size, arg = 0, ext = 0; Py_ssize_t len = PyBytes_GET_SIZE(a->a_bytecode); char *code; size = instrsize(i); if (i->i_hasarg) { arg = i->i_oparg; ext = arg >> 16; } if (i->i_lineno && !assemble_lnotab(a, i)) return 0; if (a->a_offset + size >= len) { if (len > PY_SSIZE_T_MAX / 2) return 0; if (_PyBytes_Resize(&a->a_bytecode, len * 2) < 0) return 0; } code = PyBytes_AS_STRING(a->a_bytecode) + a->a_offset; a->a_offset += size; if (size == 6) { assert(i->i_hasarg); *code++ = (char)EXTENDED_ARG; *code++ = ext & 0xff; *code++ = ext >> 8; arg &= 0xffff; } *code++ = i->i_opcode; if (i->i_hasarg) { assert(size == 3 || size == 6); *code++ = arg & 0xff; *code++ = arg >> 8; } return 1; } static void assemble_jump_offsets(struct assembler *a, struct compiler *c) { basicblock *b; int bsize, totsize, extended_arg_count = 0, last_extended_arg_count; int i; /* Compute the size of each block and fixup jump args. Replace block pointer with position in bytecode. */ do { totsize = 0; for (i = a->a_nblocks - 1; i >= 0; i--) { b = a->a_postorder[i]; bsize = blocksize(b); b->b_offset = totsize; totsize += bsize; } last_extended_arg_count = extended_arg_count; extended_arg_count = 0; for (b = c->u->u_blocks; b != NULL; b = b->b_list) { bsize = b->b_offset; for (i = 0; i < b->b_iused; i++) { struct instr *instr = &b->b_instr[i]; /* Relative jumps are computed relative to the instruction pointer after fetching the jump instruction. */ bsize += instrsize(instr); if (instr->i_jabs) instr->i_oparg = instr->i_target->b_offset; else if (instr->i_jrel) { int delta = instr->i_target->b_offset - bsize; instr->i_oparg = delta; } else continue; if (instr->i_oparg > 0xffff) extended_arg_count++; } } /* XXX: This is an awful hack that could hurt performance, but on the bright side it should work until we come up with a better solution. The issue is that in the first loop blocksize() is called which calls instrsize() which requires i_oparg be set appropriately. There is a bootstrap problem because i_oparg is calculated in the second loop above. So we loop until we stop seeing new EXTENDED_ARGs. The only EXTENDED_ARGs that could be popping up are ones in jump instructions. So this should converge fairly quickly. */ } while (last_extended_arg_count != extended_arg_count); } static PyObject * dict_keys_inorder(PyObject *dict, int offset) { PyObject *tuple, *k, *v; Py_ssize_t i, pos = 0, size = PyDict_Size(dict); tuple = PyTuple_New(size); if (tuple == NULL) return NULL; while (PyDict_Next(dict, &pos, &k, &v)) { i = PyLong_AS_LONG(v); /* The keys of the dictionary are tuples. (see compiler_add_o) The object we want is always first, though. */ k = PyTuple_GET_ITEM(k, 0); Py_INCREF(k); assert((i - offset) < size); assert((i - offset) >= 0); PyTuple_SET_ITEM(tuple, i - offset, k); } return tuple; } static int compute_code_flags(struct compiler *c) { PySTEntryObject *ste = c->u->u_ste; int flags = 0, n; if (ste->ste_type != ModuleBlock) flags |= CO_NEWLOCALS; if (ste->ste_type == FunctionBlock) { if (!ste->ste_unoptimized) flags |= CO_OPTIMIZED; if (ste->ste_nested) flags |= CO_NESTED; if (ste->ste_generator) flags |= CO_GENERATOR; if (ste->ste_varargs) flags |= CO_VARARGS; if (ste->ste_varkeywords) flags |= CO_VARKEYWORDS; } /* (Only) inherit compilerflags in PyCF_MASK */ flags |= (c->c_flags->cf_flags & PyCF_MASK); n = PyDict_Size(c->u->u_freevars); if (n < 0) return -1; if (n == 0) { n = PyDict_Size(c->u->u_cellvars); if (n < 0) return -1; if (n == 0) { flags |= CO_NOFREE; } } return flags; } static PyCodeObject * makecode(struct compiler *c, struct assembler *a) { PyObject *tmp; PyCodeObject *co = NULL; PyObject *consts = NULL; PyObject *names = NULL; PyObject *varnames = NULL; PyObject *filename = NULL; PyObject *name = NULL; PyObject *freevars = NULL; PyObject *cellvars = NULL; PyObject *bytecode = NULL; int nlocals, flags; tmp = dict_keys_inorder(c->u->u_consts, 0); if (!tmp) goto error; consts = PySequence_List(tmp); /* optimize_code requires a list */ Py_DECREF(tmp); names = dict_keys_inorder(c->u->u_names, 0); varnames = dict_keys_inorder(c->u->u_varnames, 0); if (!consts || !names || !varnames) goto error; cellvars = dict_keys_inorder(c->u->u_cellvars, 0); if (!cellvars) goto error; freevars = dict_keys_inorder(c->u->u_freevars, PyTuple_Size(cellvars)); if (!freevars) goto error; filename = PyUnicode_FromString(c->c_filename); if (!filename) goto error; nlocals = PyDict_Size(c->u->u_varnames); flags = compute_code_flags(c); if (flags < 0) goto error; bytecode = PyCode_Optimize(a->a_bytecode, consts, names, a->a_lnotab); if (!bytecode) goto error; tmp = PyList_AsTuple(consts); /* PyCode_New requires a tuple */ if (!tmp) goto error; Py_DECREF(consts); consts = tmp; co = PyCode_New(c->u->u_argcount, c->u->u_kwonlyargcount, nlocals, stackdepth(c), flags, bytecode, consts, names, varnames, freevars, cellvars, filename, c->u->u_name, c->u->u_firstlineno, a->a_lnotab); error: Py_XDECREF(consts); Py_XDECREF(names); Py_XDECREF(varnames); Py_XDECREF(filename); Py_XDECREF(name); Py_XDECREF(freevars); Py_XDECREF(cellvars); Py_XDECREF(bytecode); return co; } /* For debugging purposes only */ #if 0 static void dump_instr(const struct instr *i) { const char *jrel = i->i_jrel ? "jrel " : ""; const char *jabs = i->i_jabs ? "jabs " : ""; char arg[128]; *arg = '\0'; if (i->i_hasarg) sprintf(arg, "arg: %d ", i->i_oparg); fprintf(stderr, "line: %d, opcode: %d %s%s%s\n", i->i_lineno, i->i_opcode, arg, jabs, jrel); } static void dump_basicblock(const basicblock *b) { const char *seen = b->b_seen ? "seen " : ""; const char *b_return = b->b_return ? "return " : ""; fprintf(stderr, "used: %d, depth: %d, offset: %d %s%s\n", b->b_iused, b->b_startdepth, b->b_offset, seen, b_return); if (b->b_instr) { int i; for (i = 0; i < b->b_iused; i++) { fprintf(stderr, " [%02d] ", i); dump_instr(b->b_instr + i); } } } #endif static PyCodeObject * assemble(struct compiler *c, int addNone) { basicblock *b, *entryblock; struct assembler a; int i, j, nblocks; PyCodeObject *co = NULL; /* Make sure every block that falls off the end returns None. XXX NEXT_BLOCK() isn't quite right, because if the last block ends with a jump or return b_next shouldn't set. */ if (!c->u->u_curblock->b_return) { NEXT_BLOCK(c); if (addNone) ADDOP_O(c, LOAD_CONST, Py_None, consts); ADDOP(c, RETURN_VALUE); } nblocks = 0; entryblock = NULL; for (b = c->u->u_blocks; b != NULL; b = b->b_list) { nblocks++; entryblock = b; } /* Set firstlineno if it wasn't explicitly set. */ if (!c->u->u_firstlineno) { if (entryblock && entryblock->b_instr) c->u->u_firstlineno = entryblock->b_instr->i_lineno; else c->u->u_firstlineno = 1; } if (!assemble_init(&a, nblocks, c->u->u_firstlineno)) goto error; dfs(c, entryblock, &a); /* Can't modify the bytecode after computing jump offsets. */ assemble_jump_offsets(&a, c); /* Emit code in reverse postorder from dfs. */ for (i = a.a_nblocks - 1; i >= 0; i--) { b = a.a_postorder[i]; for (j = 0; j < b->b_iused; j++) if (!assemble_emit(&a, &b->b_instr[j])) goto error; } if (_PyBytes_Resize(&a.a_lnotab, a.a_lnotab_off) < 0) goto error; if (_PyBytes_Resize(&a.a_bytecode, a.a_offset) < 0) goto error; co = makecode(c, &a); error: assemble_free(&a); return co; }
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4