def build_mk_func(): nonlocal mk_fn_flag sub_a = sub.sc.output ins = [] frees = list(sub_a.syms_free) # get all cell names from bound variables cells = [n for n, sym in sub_a.syms_bound.items() if sym.ty is SymType.cell] if frees: # handle closure conversions if not PY35: mk_fn_flag |= I.MK_FN_HAS_CLOSURE for n in frees: if n in analysed.syms_bound: var_type = I.CellVar else: var_type = I.FreeVar ins.append(I.LOAD_CLOSURE(n, var_type)) ins.append(I.BUILD_TUPLE(len(frees))) # create code object of subroutine instructions = sub.build() py_code = make_code_obj( name, filename, line, doc, args, frees, cells, instructions ) ins.extend( [ I.LOAD_CONST(py_code), I.LOAD_CONST(name), I.MAKE_CLOSURE(mk_fn_flag) if PY35 and frees else I.MAKE_FUNCTION(mk_fn_flag), ] ) # if not anonymous function, # we shall assign the function to a variable if not anonymous: fn_sym = analysed.syms_bound.get(name) if not fn_sym: store = I.STORE_GLOBAL(name) elif fn_sym.ty is SymType.bound: store = I.STORE_FAST(name) elif fn_sym.ty is SymType.cell: store = I.STORE_DEREF(name, I.CellVar) else: raise ValueError ins.extend([I.DUP(), store]) return ins
def make_code_obj( name: str, filename: str, lineno: int, doc: str, args: List[str], frees: List[str], cells: List[str], instructions: List[BC.Instr], ): """Create code object from given metadata and instructions """ if not instructions: instructions.append(I.LOAD_CONST(None)) instructions.append(I.RETURN_VALUE()) instructions = list(merge_labels(instructions)) bc_code = BC.Bytecode(instructions) bc_code.name = name bc_code.filename = filename bc_code.first_lineno = lineno bc_code.docstring = doc bc_code.argnames.extend(args) bc_code.argcount = len(bc_code.argnames) bc_code.freevars.extend(frees) bc_code.cellvars.extend(cells) stack_size = bc_code.compute_stacksize() c_code = bc_code.to_concrete_bytecode() c_code.flags = BC.flags.infer_flags(c_code) py_code = c_code.to_code(stacksize=stack_size) return py_code
def new(self, ty, *args): """ Basically, it's new(ty, *args) == this = ty(*args, {}) this['.t'] = ty This is efficient and avoids redundant register allocation, hence I'm quite proud of this idea :) """ yield self.eval(ty) # build this object self << (lambda: [I.DUP()]) yield self.eval_all(args) n = len(args) + 1 # initialize this object self << ( lambda: [ I.BUILD_MAP(0), I.CALL_FUNCTION(n), I.DUP(), I.ROT3(), I.LOAD_CONST(RECORD_TYPE_FIELD), I.STORE_SUBSCR(), ] )
def for_in(self, n: str, seq, body): label_end = _new_unique_label("end.loop") label_iter = _new_unique_label("iter.loop") yield self.eval(seq) self << (lambda: [I.GET_ITER(), label_iter, I.FOR_ITER(label_end)]) self._bind(n, bound=False) yield self.eval(body) self << ( lambda: [ I.POP_TOP(), I.JUMP_ABSOLUTE(label_iter), label_end, I.LOAD_CONST(None), ] )
def loop(self, cond, body): """ Note, the value of `body` in the last iteration will be returned. e.g., z = 2 f = while z < 5 { z += z z } The value of `f` is 8 """ label_setup = _new_unique_label("while.setup") label_end = _new_unique_label("while.end") self << (lambda: [I.LOAD_CONST(None), label_setup]) yield self.eval(cond) self << (lambda: [I.POP_JUMP_IF_FALSE(label_end), I.POP_TOP()]) yield self.eval(body) self << (lambda: [I.JUMP_ABSOLUTE(label_setup), label_end])
def ret(self, v): yield self.eval(v) self << (lambda: [I.RETURN_VALUE(), I.LOAD_CONST(None)])
def set_item(self, base, item, val): yield self.eval(val) yield self.eval(base) yield self.eval(item) self << (lambda: [I.STORE_SUBSCR(), I.LOAD_CONST(None)])
def set_attr(self, base, n: str, val): yield self.eval(val) yield self.eval(base) self << (lambda: [I.STORE_ATTR(n), I.LOAD_CONST(None)])
def assign(self, n: str, v): yield self.eval(v) self._bind(n, False) self << (lambda: [I.LOAD_CONST(None)])
def assign_star(self, n: str, v): yield self.eval(v) self._bind(n, True) self << (lambda: [I.LOAD_CONST(None)])
def build(): return [I.LOAD_CONST(value)]