Beispiel #1
0
    def check_dump_bytecode(self, code, expected, lineno=None):
        with contextlib.redirect_stdout(io.StringIO()) as stderr:
            if lineno is not None:
                bytecode.dump_bytecode(code, lineno=True)
            else:
                bytecode.dump_bytecode(code)
            output = stderr.getvalue()

        self.assertEqual(output, expected)
Beispiel #2
0
    def check_dump_bytecode(self, code, expected, lineno=None):
        with contextlib.redirect_stdout(io.StringIO()) as stderr:
            if lineno is not None:
                bytecode.dump_bytecode(code, lineno=True)
            else:
                bytecode.dump_bytecode(code)
            output = stderr.getvalue()

        self.assertEqual(output, expected)
Beispiel #3
0
    def test_type_validation(self):
        class T:
            first_lineno = 1

        with self.assertRaises(TypeError):
            bytecode.dump_bytecode(T())
Beispiel #4
0
def emit_function(node: typing.Union[ast.AsyncFunctionDef, ast.FunctionDef,
                                     ast.Lambda], new_ctx: Context,
                  is_async: bool):
    """
        https://docs.python.org/3/library/dis.html#opcode-MAKE_FUNCTION
        MAKE_FUNCTION flags:
        0x01 a tuple of default values for positional-only and positional-or-keyword parameters in positional order
        0x02 a dictionary of keyword-only parameters’ default values
        0x04 an annotation dictionary
        0x08 a tuple containing cells for free variables, making a closure
        the code associated with the function (at TOS1)
        the qualified name of the function (at TOS)

        """

    parent_ctx: Context = new_ctx.parent
    name = getattr(node, 'name', '<lambda>')
    new_ctx.bc.name = f'{parent_ctx.bc.name}.{name}' if parent_ctx.bc.name else name

    for decorator in getattr(node, 'decorator_list', ()):
        py_emit(decorator, parent_ctx)

    if is_async:
        new_ctx.bc.flags |= CompilerFlags.COROUTINE

    if isinstance(node, ast.Lambda):
        py_emit(node.body, new_ctx)
        new_ctx.bc.append(RETURN_VALUE(lineno=node.lineno))
    else:
        head = node.body
        if isinstance(head, ast.Expr) and isinstance(head.value, ast.Str):
            new_ctx.bc.docstring = head.value.s
        for each in node.body:
            py_emit(each, new_ctx)

    args = node.args
    new_ctx.bc.argcount = len(args.args)
    new_ctx.bc.kwonlyargcount = len(args.kwonlyargs)
    make_function_flags = 0
    if new_ctx.sym_tb.freevars:
        make_function_flags |= 0x08

    if args.defaults:
        make_function_flags |= 0x01

    if args.kw_defaults:
        make_function_flags |= 0x02

    annotations = []
    argnames = []

    for arg in args.args:
        argnames.append(arg.arg)
        if arg.annotation:
            annotations.append((arg.arg, arg.annotation))

    for arg in args.kwonlyargs:
        argnames.append(arg.arg)
        if arg.annotation:
            annotations.append((arg.arg, arg.annotation))
    arg = args.vararg
    if arg:
        new_ctx.bc.flags |= CompilerFlags.VARARGS
        argnames.append(arg.arg)
        if arg.annotation:
            annotations.append((arg.arg, arg.annotation))

    arg = args.kwarg
    if arg:
        new_ctx.bc.flags |= CompilerFlags.VARKEYWORDS
        argnames.append(arg.arg)
        if arg.annotation:
            annotations.append((arg.arg, arg.annotation))

    if any(annotations):
        make_function_flags |= 0x04

    new_ctx.bc.argnames.extend(argnames)

    if make_function_flags & 0x01:
        for each in args.defaults:
            py_emit(each, parent_ctx)
        parent_ctx.bc.append(
            Instr('BUILD_TUPLE', len(args.defaults), lineno=node.lineno))

    if make_function_flags & 0x02:
        for each in args.kw_defaults:
            py_emit(each, parent_ctx)
        parent_ctx.bc.append(
            Instr('BUILD_TUPLE', len(args.kw_defaults), lineno=node.lineno))

    if make_function_flags & 0x04:
        keys, annotation_values = zip(*annotations)

        for each in annotation_values:
            py_emit(each, parent_ctx)
        parent_ctx.bc.append(
            Instr('LOAD_CONST', tuple(keys), lineno=node.lineno))

        parent_ctx.bc.append(
            Instr("BUILD_CONST_KEY_MAP",
                  len(annotation_values),
                  lineno=node.lineno))

    if make_function_flags & 0x08:
        new_ctx.load_closure(lineno=node.lineno)

    new_ctx.bc.append(Instr('LOAD_CONST', None))
    new_ctx.bc.append(Instr('RETURN_VALUE'))

    try:
        inner_code = new_ctx.bc.to_code()
    except RuntimeError:
        print(new_ctx.bc.filename)
        dump_bytecode(new_ctx.bc)
        raise
    parent_ctx.bc.append(Instr('LOAD_CONST', inner_code, lineno=node.lineno))

    # when it comes to nested, the name is not generated correctly now.
    parent_ctx.bc.append(
        Instr('LOAD_CONST', new_ctx.bc.name, lineno=node.lineno))

    parent_ctx.bc.append(
        Instr("MAKE_FUNCTION", make_function_flags, lineno=node.lineno))

    parent_ctx.bc.extend([CALL_FUNCTION(1, lineno=node.lineno)] *
                         len(getattr(node, 'decorator_list', ())))

    if isinstance(node, ast.Lambda):
        pass
    else:
        parent_ctx.store_name(node.name, lineno=node.lineno)
Beispiel #5
0
def f(a, b):
    #    res = a + b
    return


def g(a, b):
    res = a + b if a < b else b + a
    r = 0
    for a in range(res):
        r += 1
    return r or 2


for x in (f, g):
    #get byte code for f
    dis(x)
    print(f.__code__.co_code)
    c = Bytecode.from_code(x.__code__)
    cc = ConcreteBytecode.from_code(x.__code__)
    dump_bytecode(c)
    dump_bytecode(cc)

    #generate byte code
    cnew = c.to_code()

    x.__code__ = cnew
    dis(x)

    print(x(3, 5))
Beispiel #6
0
        # need to check the reurn value of the call, and return it directly before the finally block is setup
        yield calculate
        yield Instr('SETUP_FINALLY', return_finally)

        for instr in bytecode:
            yield instr

        # return value is now on the top of the stack, so we can cache it in postfix?
        yield return_finally
        yield Instr('DUP_TOP')              # copy the return value to the top
        yield Instr('LOAD_CONST', postfix)  # load a return function
        yield Instr('ROT_TWO')              # swap so it's a call
        yield Instr('CALL_FUNCTION', 1)     # invoke with the return value
        yield Instr('POP_TOP')
        yield Instr('END_FINALLY')


    bytecode[:] = list(generate_bytecodes(bytecode))

    dump_bytecode(bytecode)

    my_function.__code__ = bytecode.to_code()
    my_function(1,2,3)

    import dis
    dis.dis(my_function.__code__)