Beispiel #1
0
def persistent_locals(f):
    """Function decorator to expose local variables after execution.

    Modify the function such that, at the exit of the function
    (regular exit or exceptions), the local dictionary is copied to a
    read-only function property 'locals'.

    This decorator wraps the function in a callable object, and
    modifies its bytecode by adding an external try...finally
    statement equivalent to the following:

    def f(self, *args, **kwargs):
        try:
            ... old code ...
        finally:
            self._locals = locals().copy()
            del self._locals['self']
    """

    # ### disassemble f
    f_code = bp.Code.from_code(f.func_code)

    # ### use bytecode injection to add try...finally statement around code
    finally_label = bp.Label()
    # try:
    code_before = (bp.SETUP_FINALLY, finally_label)
    #     [original code here]
    # finally:
    code_after = [
        (finally_label, None),
        # self._locals = locals().copy()
        (bp.LOAD_GLOBAL, 'locals'),
        (bp.CALL_FUNCTION, 0),
        (bp.LOAD_ATTR, 'copy'),
        (bp.CALL_FUNCTION, 0),
        (bp.LOAD_FAST, 'self'),
        (bp.STORE_ATTR, '_locals'),
        #   del self._locals['self']
        (bp.LOAD_FAST, 'self'),
        (bp.LOAD_ATTR, '_locals'),
        (bp.LOAD_CONST, 'self'),
        (bp.DELETE_SUBSCR, None),
        (bp.END_FINALLY, None),
        (bp.LOAD_CONST, None),
        (bp.RETURN_VALUE, None)
    ]

    f_code.code.insert(0, code_before)
    f_code.code.extend(code_after)

    # ### re-assemble
    f_code.args = ('self', ) + f_code.args
    func = new.function(f_code.to_code(), f.func_globals, f.func_name,
                        f.func_defaults, f.func_closure)

    return PersistentLocalsFunction(func)
Beispiel #2
0
def boolean_and():
    # a b -- (b and a)
    # TODO: Our other lang requires these are booleans
    # TODO: Use JUMP_IF_FALSE_OR_POP and leave either a or b
    label_out = bp.Label()
    return [
        (bp.POP_JUMP_IF_TRUE, label_out),  # a b -- a
        (bp.POP_TOP, None),
        (bp.LOAD_CONST, False),
        (label_out, None),
    ]
Beispiel #3
0
def if_stmt():
    # Stack: (p)redicate (c)onsequent (a)lternative
    # Example: "true  'yes' 'no' if" ==> 'yes'
    # Example: "false 'yes' 'no' if" ==> 'no'
    pop = bp.Label()
    return [
        (bp.ROT_THREE, None),  # p c a -- a p c
        (bp.ROT_TWO, None),  # a p c -- a c p
        (bp.POP_JUMP_IF_FALSE, pop),
        (bp.ROT_TWO, None),  #  a c  -- c a
        (pop, None),
        (bp.POP_TOP, None),
    ]
Beispiel #4
0
    def end_loop(startlabel):
        endlabel = bp.Label()

        # Update goto end-of-loop label
        start = labelpos[startlabel]
        c[start] = (bp.POP_JUMP_IF_TRUE, endlabel)

        c.append((endlabel, None))
        c.append((bp.LOAD_FAST, "memory"))
        c.append((bp.LOAD_FAST, "ptr"))
        c.append((bp.BINARY_SUBSCR, None))
        c.append((bp.LOAD_CONST, 0))
        c.append((bp.COMPARE_OP, "=="))
        c.append((bp.POP_JUMP_IF_FALSE, startlabel))
Beispiel #5
0
def compile(source, memsize=300000, flush=True, modulus=None, verbose=False):
    # Bytecode
    c = []

    # Keep track of jump labels
    labels = []
    labelpos = {}


    # import sys
    c.append((bp.LOAD_CONST, -1))
    c.append((bp.LOAD_CONST, None))
    c.append((bp.IMPORT_NAME, "sys"))
    c.append((bp.STORE_FAST, "sys"))

    # memory = [0]*memsize
    c.append((bp.LOAD_CONST, 0))
    c.append((bp.BUILD_LIST, 1))
    c.append((bp.LOAD_CONST, memsize))
    c.append((bp.BINARY_MULTIPLY, None))
    c.append((bp.STORE_FAST, "memory"))

    # ptr = 0
    c.append((bp.LOAD_CONST, 0))
    c.append((bp.STORE_FAST, "ptr"))

    def add(value):
        if modulus is None:
            c.append((bp.LOAD_FAST, "memory"))
            c.append((bp.LOAD_FAST, "ptr"))
            c.append((bp.DUP_TOPX, 2))
            c.append((bp.BINARY_SUBSCR, None))
            c.append((bp.LOAD_CONST, value))
            c.append((bp.INPLACE_ADD, None))
            c.append((bp.ROT_THREE, None))
            c.append((bp.STORE_SUBSCR, None))
        else:
            c.append((bp.LOAD_FAST, "memory"))
            c.append((bp.LOAD_FAST, "ptr"))
            c.append((bp.BINARY_SUBSCR, None))
            c.append((bp.LOAD_CONST, value))
            c.append((bp.BINARY_ADD, None))
            c.append((bp.LOAD_CONST, modulus))
            c.append((bp.BINARY_MODULO, None))
            c.append((bp.LOAD_FAST, "memory"))
            c.append((bp.LOAD_FAST, "ptr"))
            c.append((bp.STORE_SUBSCR, None))

    def zero():
        c.append((bp.LOAD_CONST, 0))
        c.append((bp.LOAD_FAST, "memory"))
        c.append((bp.LOAD_FAST, "ptr"))
        c.append((bp.STORE_SUBSCR, None))

    def dot(count):
        # Prepare call to sys.stdout.write(chr(...))
        c.append((bp.LOAD_GLOBAL, "sys"))
        c.append((bp.LOAD_ATTR, "stdout"))
        c.append((bp.LOAD_ATTR, "write"))
        c.append((bp.LOAD_GLOBAL, "chr"))

        # Get value
        c.append((bp.LOAD_FAST, "memory"))
        c.append((bp.LOAD_FAST, "ptr"))
        c.append((bp.BINARY_SUBSCR, None))

        # Call chr
        c.append((bp.CALL_FUNCTION, 1))

        if count > 1:
            c.append((bp.LOAD_CONST, count))
            c.append((bp.BINARY_MULTIPLY, None))

        # Call sys.stdout.write and drop its return value
        c.append((bp.CALL_FUNCTION, 1))
        c.append((bp.POP_TOP, None))

        if flush:
            c.append((bp.LOAD_GLOBAL, "sys"))
            c.append((bp.LOAD_ATTR, "stdout"))
            c.append((bp.LOAD_ATTR, "flush"))
            c.append((bp.CALL_FUNCTION, 0))
            c.append((bp.POP_TOP, None))

    def comma(count):
        c.append((bp.LOAD_GLOBAL, "ord"))
        c.append((bp.LOAD_FAST, "sys"))
        c.append((bp.LOAD_ATTR, "stdin"))
        c.append((bp.LOAD_ATTR, "read"))
        c.append((bp.LOAD_CONST, count))
        c.append((bp.CALL_FUNCTION, 1))
        c.append((bp.CALL_FUNCTION, 1))
        c.append((bp.LOAD_FAST, "memory"))
        c.append((bp.LOAD_FAST, "ptr"))
        c.append((bp.STORE_SUBSCR, None))

    def move(amount):
        c.append((bp.LOAD_FAST, "ptr"))
        c.append((bp.LOAD_CONST, amount))
        c.append((bp.INPLACE_ADD, None))
        c.append((bp.STORE_FAST, "ptr"))

    def start_loop(label):
        c.append((label, None))
        c.append((bp.LOAD_FAST, "memory"))
        c.append((bp.LOAD_FAST, "ptr"))
        c.append((bp.BINARY_SUBSCR, None))
        c.append((bp.LOAD_CONST, 0))
        c.append((bp.COMPARE_OP, "=="))

        # We don't know the label of the end-of-loop position, so store a
        # temporary marker and get back to it later
        c.append((bp.POP_JUMP_IF_TRUE, None))
        labelpos[label] = len(c)-1

    def end_loop(startlabel):
        endlabel = bp.Label()

        # Update goto end-of-loop label
        start = labelpos[startlabel]
        c[start] = (bp.POP_JUMP_IF_TRUE, endlabel)

        c.append((endlabel, None))
        c.append((bp.LOAD_FAST, "memory"))
        c.append((bp.LOAD_FAST, "ptr"))
        c.append((bp.BINARY_SUBSCR, None))
        c.append((bp.LOAD_CONST, 0))
        c.append((bp.COMPARE_OP, "=="))
        c.append((bp.POP_JUMP_IF_FALSE, startlabel))

    # Translate Brainfuck to Python bytecode
    for (op, count) in optimize_source(source, verbose=verbose):
        if op == ">":
            move(count)
        elif op == "<":
            move(-count)
        elif op == "+":
            add(count)
        elif op == "-":
            add(-count)
        elif op == ".":
            dot(count)
        elif op == ",":
            comma(count)
        elif op == "[":
            labels.append(bp.Label())
            start_loop(labels[-1])
        elif op == "]":
            end_loop(labels.pop())
        elif op == "zero":
            zero()
        elif op is None:
            pass
        else:
            print("Unknown operator: %s" % op)
            sys.exit(1)

    # return None
    c.append((bp.LOAD_CONST, None))
    c.append((bp.RETURN_VALUE, None))
    return c
Beispiel #6
0
def create_head_hook(state, loop_end_label):
    vars_storage = state.vars_storage
    real_vars_indexes = state.real_vars_indexes
    manual_store_counter = state.manual_store_counter

    unpacked_straight = []
    for index in real_vars_indexes:
        unpacked_straight.append(vars_storage[index])
    if manual_store_counter is not None:
        unpacked_straight.append(manual_store_counter)

    content = [
        (byteplay.DUP_TOP, None),
        (byteplay.LOAD_CONST, exec_loop),
        (byteplay.ROT_TWO, None),
        (byteplay.LOAD_CONST, state.settings),
        (byteplay.LOAD_CONST, state.content),
        (byteplay.LOAD_CONST, vars_storage),
        (byteplay.LOAD_CONST, real_vars_indexes),
        (byteplay.LOAD_CONST, manual_store_counter is not None),
        (byteplay.LOAD_CONST, globals),
        (byteplay.CALL_FUNCTION, 0),
        (byteplay.LOAD_CONST, locals),
        (byteplay.CALL_FUNCTION, 0),
        (byteplay.BUILD_LIST, 0),
        (byteplay.DUP_TOP, None),
        (byteplay.STORE_FAST, state.real_folded_arr),
    ]
    if python_version < (2, 7):
        for folded_code in state.consts:
            content += [
                (byteplay.DUP_TOP, None),
            ] + folded_code + [
                (byteplay.LIST_APPEND, None),
            ]
    else:
        for folded_code in state.consts:
            content += folded_code + [
                (byteplay.LIST_APPEND, 1),
            ]
    content += [
        (byteplay.CALL_FUNCTION, 9),
        (byteplay.DELETE_FAST, state.real_folded_arr),
        (byteplay.DUP_TOP, None),
        (byteplay.LOAD_CONST, None),
    ]
    head_end_label = byteplay.Label()

    # Let's "res" is a return value of "exec_loop".
    # Now the stack looks like:
    #     None, res, res, iterator, ...
    content += [
        (byteplay.COMPARE_OP, 'is not'),
    ]
    if python_version < (2, 7):
        content += [
            (byteplay.JUMP_IF_FALSE, head_end_label),
            (byteplay.POP_TOP, None),
        ]
    else:
        content += [
            (byteplay.POP_JUMP_IF_FALSE, head_end_label),
        ]

    # Code below runs if res is not None (optimization was successful).
    # Now the stack looks like:
    #     res, iterator, ...
    content += [
        (byteplay.UNPACK_SEQUENCE, len(unpacked_straight)),
    ]
    # Store changed variables
    for straight in unpacked_straight:
        content.append((vars_opers_map[straight[0]][1], straight[1]))
    # We need to pop the iterator because the loop will be skipped
    content += [
        (byteplay.POP_TOP, None),
        (byteplay.JUMP_ABSOLUTE, loop_end_label),
    ]

    # Code below runs if res is None (optimization failed).
    # Now the stack looks like:
    #     None, iterator, ...           (in Python 2.7)
    #     False, None, iterator, ...    (in Python < 2.7)
    content += [
        (head_end_label, None),
        (byteplay.POP_TOP, None),
    ]
    if python_version < (2, 7):
        content += [
            (byteplay.POP_TOP, None),
        ]
    # Right before the loop (before GET_ITER instruction) iterator
    # must be at the top of the stack.
    return content