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, V1: S1 except E2, 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 = false; c->u->u_lineno = handler->lineno; 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) { VISIT(c, expr, handler->v.ExceptHandler.name); } else { ADDOP(c, POP_TOP); } ADDOP(c, POP_TOP); VISIT_SEQ(c, stmt, handler->v.ExceptHandler.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 char *src = PyString_AS_STRING(name); const char *dot = 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 = strchr(src, '.'); attr = PyString_FromStringAndSize(src, dot ? dot - src : 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; if (c->c_flags && (c->c_flags->cf_flags & CO_FUTURE_ABSOLUTE_IMPORT)) level = PyInt_FromLong(0); else level = PyInt_FromLong(-1); 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 char *base = PyString_AS_STRING(alias->name); char *dot = strchr(base, '.'); if (dot) tmp = PyString_FromStringAndSize(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 = PyString_FromString(""); if (!empty_string) return 0; } if (!names) return 0; if (s->v.ImportFrom.level == 0 && c->c_flags && !(c->c_flags->cf_flags & CO_FUTURE_ABSOLUTE_IMPORT)) level = PyInt_FromLong(-1); else level = PyInt_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 && !strcmp(PyString_AS_STRING(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 && *PyString_AS_STRING(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 = PyString_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, RAISE_VARARGS, 2); } else { 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_lineno_set = false; 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 Print_kind: return compiler_print(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.type) { VISIT(c, expr, s->v.Raise.type); n++; if (s->v.Raise.inst) { VISIT(c, expr, s->v.Raise.inst); n++; if (s->v.Raise.tback) { VISIT(c, expr, s->v.Raise.tback); 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 Exec_kind: VISIT(c, expr, s->v.Exec.body); if (s->v.Exec.globals) { VISIT(c, expr, s->v.Exec.globals); if (s->v.Exec.locals) { VISIT(c, expr, s->v.Exec.locals); } else { ADDOP(c, DUP_TOP); } } else { ADDOP_O(c, LOAD_CONST, Py_None, consts); ADDOP(c, DUP_TOP); } ADDOP(c, EXEC_STMT); break; case Global_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: if (c->c_flags && c->c_flags->cf_flags & CO_FUTURE_DIVISION) return BINARY_TRUE_DIVIDE; else return BINARY_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: if (c->c_flags && c->c_flags->cf_flags & CO_FUTURE_DIVISION) return INPLACE_TRUE_DIVIDE; else return INPLACE_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 || PyString_AS_STRING(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: PyErr_Format(PyExc_SyntaxError, "can not delete variable '%s' referenced " "in nested scope", PyString_AS_STRING(name)); Py_DECREF(mangled); return 0; 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) { 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) { 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) { int n, code = 0; VISIT(c, expr, e->v.Call.func); n = asdl_seq_LEN(e->v.Call.args); VISIT_SEQ(c, expr, e->v.Call.args); if (e->v.Call.keywords) { VISIT_SEQ(c, keyword, e->v.Call.keywords); n |= asdl_seq_LEN(e->v.Call.keywords) << 8; } if (e->v.Call.starargs) { VISIT(c, expr, e->v.Call.starargs); code |= 1; } if (e->v.Call.kwargs) { VISIT(c, expr, e->v.Call.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; } static int compiler_listcomp_generator(struct compiler *c, asdl_seq *generators, int gen_index, expr_ty elt) { /* generate code for the iterator, then each of the ifs, and then write to the element */ comprehension_ty l; 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; l = (comprehension_ty)asdl_seq_GET(generators, gen_index); VISIT(c, expr, l->iter); ADDOP(c, GET_ITER); compiler_use_next_block(c, start); ADDOP_JREL(c, FOR_ITER, anchor); NEXT_BLOCK(c); VISIT(c, expr, l->target); /* XXX this needs to be cleaned up...a lot! */ n = asdl_seq_LEN(l->ifs); for (i = 0; i < n; i++) { expr_ty e = (expr_ty)asdl_seq_GET(l->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_listcomp_generator(c, generators, gen_index, elt)) return 0; /* only append after the last for generator */ if (gen_index >= asdl_seq_LEN(generators)) { VISIT(c, expr, elt); ADDOP_I(c, LIST_APPEND, gen_index+1); 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_listcomp(struct compiler *c, expr_ty e) { assert(e->kind == ListComp_kind); ADDOP_I(c, BUILD_LIST, 0); return compiler_listcomp_generator(c, e->v.ListComp.generators, 0, e->v.ListComp.elt); } static int compiler_genexp_generator(struct compiler *c, asdl_seq *generators, int gen_index, expr_ty elt) { /* generate code for the iterator, then each of the ifs, and then write to the element */ comprehension_ty ge; basicblock *start, *anchor, *skip, *if_cleanup, *end; int i, n; start = compiler_new_block(c); skip = compiler_new_block(c); if_cleanup = compiler_new_block(c); anchor = compiler_new_block(c); end = compiler_new_block(c); if (start == NULL || skip == NULL || if_cleanup == NULL || anchor == NULL || end == NULL) return 0; ge = (comprehension_ty)asdl_seq_GET(generators, gen_index); ADDOP_JREL(c, SETUP_LOOP, end); if (!compiler_push_fblock(c, LOOP, start)) return 0; 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, ge->iter); ADDOP(c, GET_ITER); } compiler_use_next_block(c, start); ADDOP_JREL(c, FOR_ITER, anchor); NEXT_BLOCK(c); VISIT(c, expr, ge->target); /* XXX this needs to be cleaned up...a lot! */ n = asdl_seq_LEN(ge->ifs); for (i = 0; i < n; i++) { expr_ty e = (expr_ty)asdl_seq_GET(ge->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_genexp_generator(c, generators, gen_index, elt)) return 0; /* only append after the last 'for' generator */ if (gen_index >= asdl_seq_LEN(generators)) { VISIT(c, expr, elt); ADDOP(c, YIELD_VALUE); ADDOP(c, POP_TOP); 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); ADDOP(c, POP_BLOCK); compiler_pop_fblock(c, LOOP, start); compiler_use_next_block(c, end); return 1; } static int compiler_genexp(struct compiler *c, expr_ty e) { static identifier name; PyCodeObject *co; expr_ty outermost_iter = ((comprehension_ty) (asdl_seq_GET(e->v.GeneratorExp.generators, 0)))->iter; if (!name) { name = PyString_FromString(" "); if (!name) return 0; } if (!compiler_enter_scope(c, name, (void *)e, e->lineno)) return 0; compiler_genexp_generator(c, e->v.GeneratorExp.generators, 0, e->v.GeneratorExp.elt); co = assemble(c, 1); compiler_exit_scope(c); if (co == NULL) return 0; compiler_make_closure(c, co, 0); Py_DECREF(co); VISIT(c, expr, outermost_iter); ADDOP(c, GET_ITER); ADDOP_I(c, CALL_FUNCTION, 1); return 1; } 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) { switch (e->kind) { case Num_kind: return PyObject_IsTrue(e->v.Num.n); case Str_kind: return PyObject_IsTrue(e->v.Str.s); case Name_kind: /* __debug__ is not assignable, so we can optimize * it away in if and while statements */ if (strcmp(PyString_AS_STRING(e->v.Name.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 = false; } 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 ListComp_kind: return compiler_listcomp(c, e); case GeneratorExp_kind: return compiler_genexp(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 Repr_kind: VISIT(c, expr, e->v.Repr.value); ADDOP(c, UNARY_CONVERT); break; 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; /* 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 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("(ziOO)", c->c_filename, c->u->u_lineno, Py_None, 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_I(c, DUP_TOPX, 2); } 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_simple_slice(struct compiler *c, slice_ty s, expr_context_ty ctx) { int op = 0, slice_offset = 0, stack_count = 0; assert(s->v.Slice.step == NULL); if (s->v.Slice.lower) { slice_offset++; stack_count++; if (ctx != AugStore) VISIT(c, expr, s->v.Slice.lower); } if (s->v.Slice.upper) { slice_offset += 2; stack_count++; if (ctx != AugStore) VISIT(c, expr, s->v.Slice.upper); } if (ctx == AugLoad) { switch (stack_count) { case 0: ADDOP(c, DUP_TOP); break; case 1: ADDOP_I(c, DUP_TOPX, 2); break; case 2: ADDOP_I(c, DUP_TOPX, 3); break; } } else if (ctx == AugStore) { switch (stack_count) { case 0: ADDOP(c, ROT_TWO); break; case 1: ADDOP(c, ROT_THREE); break; case 2: ADDOP(c, ROT_FOUR); break; } } switch (ctx) { case AugLoad: /* fall through to Load */ case Load: op = SLICE; break; case AugStore:/* fall through to Store */ case Store: op = STORE_SLICE; break; case Del: op = DELETE_SLICE; break; case Param: default: PyErr_SetString(PyExc_SystemError, "param invalid in simple slice"); return 0; } ADDOP(c, op + slice_offset); return 1; } static int compiler_visit_nested_slice(struct compiler *c, slice_ty s, expr_context_ty ctx) { switch (s->kind) { case Ellipsis_kind: ADDOP_O(c, LOAD_CONST, Py_Ellipsis, consts); break; 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 Ellipsis_kind: kindname = "ellipsis"; if (ctx != AugStore) { ADDOP_O(c, LOAD_CONST, Py_Ellipsis, consts); } break; case Slice_kind: kindname = "slice"; if (!s->v.Slice.step) return compiler_simple_slice(c, s, ctx); 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; 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) { maxdepth = stackdepth_walk(c, instr->i_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 = PyString_FromStringAndSize(NULL, DEFAULT_CODE_SIZE); if (!a->a_bytecode) return 0; a->a_lnotab = PyString_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 = PyString_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 (_PyString_Resize(&a->a_lnotab, len) < 0) return 0; } lnotab = (unsigned char *) PyString_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 = PyString_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 (_PyString_Resize(&a->a_lnotab, len) < 0) return 0; } lnotab = (unsigned char *) PyString_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 = PyString_GET_SIZE(a->a_lnotab); if (a->a_lnotab_off + 2 >= len) { if (_PyString_Resize(&a->a_lnotab, len * 2) < 0) return 0; } lnotab = (unsigned char *) PyString_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 = PyString_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 (_PyString_Resize(&a->a_bytecode, len * 2) < 0) return 0; } code = PyString_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, last_extended_arg_count = 0; int i; /* Compute the size of each block and fixup jump args. Replace block pointer with position in bytecode. */ start: 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; } 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. In the meantime, should the goto be dropped in favor of a loop? 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. */ if (last_extended_arg_count != extended_arg_count) { last_extended_arg_count = extended_arg_count; goto start; } } 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 = PyInt_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 = PyString_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, 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 (_PyString_Resize(&a.a_lnotab, a.a_lnotab_off) < 0) goto error; if (_PyString_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