Example #1
0
 def _stack_trace_implementation(self):
     return BytecodeMethod(
         name='fillInStackTrace',
         descriptor='(Ljava/lang/Throwable;)Ljava/lang/VMThrowable;',
         instructions=[
             named_tuple_replace(Instruction.create('aload_0'), pos=1),
             named_tuple_replace(Instruction.create('areturn'), pos=2)
         ],
         max_locals=1,
         max_stack=1,
         args=[ObjectReferenceType('java/lang/Throwable')],
         is_native=False,
         exception_handlers=Handlers()
     )
Example #2
0
def test_hello_world(loader):
    """
    An integration test for the complete parsing of a simple HelloWorld
    example.

    The source example is as follows:

    .. code::

        class HelloWorld {
            public static void main(String[] args) {
                System.out.println("Hello World!");
            }
        }
    """
    cf = loader['HelloWorld']

    assert len(cf.constants) == 21
    assert len(cf.attributes) == 0
    assert len(cf.fields) == 0
    assert len(cf.methods) == 1

    main_method = cf.methods.find_one(name='main')
    assert main_method is not None

    # 0x08 for ACC_STATIC, 0x01 for ACC_PUBLIC
    assert main_method.access_flags.value == 0x9
    assert main_method.code.max_locals == 1
    assert main_method.code.max_stack == 2

    instruction_list = list(main_method.code.disassemble())
    assert instruction_list == [
        Instruction(mnemonic='getstatic',
                    opcode=178,
                    operands=[Operand(op_type=30, value=13)],
                    pos=0),
        Instruction(mnemonic='ldc',
                    opcode=18,
                    operands=[Operand(op_type=30, value=15)],
                    pos=3),
        Instruction(mnemonic='invokevirtual',
                    opcode=182,
                    operands=[Operand(op_type=30, value=21)],
                    pos=5),
        Instruction(mnemonic='return', opcode=177, operands=[], pos=8)
    ]
Example #3
0
def expand_constants(ins: Instruction, *, cf) -> Instruction:
    """Replace CONSTANT_INDEX operands with the literal Constant object from
    the constant pool.

    :param ins: Instruction to potentially modify.
    :param cf: The ClassFile instance used to resolve Constants.
    :return: Potentially modified instruction.
    """
    for i, operand in enumerate(ins.operands):
        if not isinstance(operand, Operand):
            continue

        if operand.op_type == OperandTypes.CONSTANT_INDEX:
            ins.operands[i] = cf.constants[operand.value]

    return ins
Example #4
0
def test_invoke_v():
    method_name = 'method_name'
    class_name = 'class_name'

    consts = ConstantPool()
    descriptor = '(II)V'
    key = MethodKey(method_name, descriptor)
    no_op = Instruction.create('nop')

    method = BytecodeMethod(
        name='method_name',
        descriptor='(II)V',
        max_locals=5,
        max_stack=5,
        instructions=[no_op, no_op],
        args=[Integer, Integer],
    )

    jvm_class = JvmClass(
        class_name,
        RootObjectType.refers_to,
        consts,
        methods={
            key: method
        }
    )

    method_ref = consts.create_method_ref(class_name, method_name, descriptor)
    instruction = constant_instruction('invokevirtual', method_ref)
    loader = FixedClassLoader({
        class_name: jvm_class
    })

    instance = loader.default_instance(class_name)
    arg_value = SOME_INT
    arguments = [instance, arg_value, arg_value]
    reversed_arguments = list(reversed(arguments))
    assert_instruction(
        constants=consts,
        loader=loader,
        instruction=instruction,
        op_stack=reversed_arguments,
        expected=[
            Pop(3),
            Invoke(class_name, key, arguments)
        ]
    )
Example #5
0
def assert_instruction(expected=None, **kwargs):
    """Assert that executing the instruction returns the `expected` Actions

    The `**kwargs` will be passed to a `DefaultInputs` instance.
    """
    if expected is None:
        expected = []
    args = dict(kwargs)

    # instruction can be a string, coerce it to an instruction if needed
    instruction = args[_INSTRUCTION_KEY]
    if isinstance(instruction, str):
        instruction = Instruction.create(instruction)
    args[_INSTRUCTION_KEY] = instruction

    inputs = DefaultInputs(**args)
    actions = execute_instruction(inputs)
    assert actions.has(*expected)
Example #6
0
def test_iinc():
    original_value = 8
    local_index = 2
    amount_to_add = 5

    locals_ = Locals(local_index + 1)
    locals_.store(local_index, Integer.create_instance(original_value))

    instruction = Instruction.create(
        'iinc', [local_operand(local_index),
                 literal_operand(amount_to_add)])

    assert_incrementing_instruction(
        instruction=instruction,
        locals=locals_,
        expected=[
            Push(Integer.create_instance(original_value + amount_to_add))
        ])
Example #7
0
def test_compare():
    ins = Instruction.create('return')
    assert ins == 'return'
    assert ins == ins
    assert ins != 'not_return'
Example #8
0
from jawa.util.bytecode import Instruction, Operand, OperandTypes

GOOD_TABLE_SWITCH = [
    Instruction(mnemonic='iconst_1', opcode=4, operands=[], pos=0),
    Instruction(
        mnemonic='tableswitch',
        opcode=170,
        operands=[
            # DEFAULT
            Operand(OperandTypes.BRANCH, value=30),
            # LOW
            Operand(OperandTypes.LITERAL, value=1),
            # HIGH
            Operand(OperandTypes.LITERAL, value=3),
            # TABLE
            Operand(OperandTypes.BRANCH, value=27),
            Operand(OperandTypes.BRANCH, value=28),
            Operand(OperandTypes.BRANCH, value=29)
        ],
        pos=1),
    Instruction(mnemonic='return', opcode=177, operands=[], pos=28),
    Instruction(mnemonic='return', opcode=177, operands=[], pos=29),
    Instruction(mnemonic='return', opcode=177, operands=[], pos=30),
    Instruction(mnemonic='return', opcode=177, operands=[], pos=31)
]

GOOD_LOOKUP_SWITCH = [
    Instruction(mnemonic='iconst_1', opcode=4, operands=[], pos=0),
    Instruction(mnemonic='lookupswitch',
                opcode=171,
                operands=[{
Example #9
0
def assemble(code):
    """
    Assemble the given iterable of mnemonics, operands, and lables.

    A convienience over constructing individual Instruction and Operand
    objects, the output of this function can be directly piped to
    :class:`~jawa.attributes.code.CodeAttribute.assemble()` to produce
    executable bytecode.

    As a simple example, lets produce an infinite loop:

        >>> from jawa.assemble import assemble, Label
        >>> print(list(assemble((
        ...     Label('start'),
        ...     ('goto', Label('start'))
        ... ))))
        [Instruction(mnemonic='goto', opcode=167, operands=[
            Operand(op_type=40, value=0)], pos=0)]

    For a more complex example, see examples/hello_world.py.
    """
    final = []

    # We need to make three passes, because we cannot know the offset for
    # jump labels until after we've figured out the PC for each instructions,
    # which is complicated by the variable-width instructions set and
    # alignment padding.
    for line in code:
        if isinstance(line, Label):
            final.append(line)
            continue

        mnemonic, operands = line[0], line[1:]
        operand_fmts = opcode_table[mnemonic]['operands']

        # We need to coerce each opcodes operands into their
        # final `Operand` form.
        final_operands = []
        for i, operand in enumerate(operands):
            if isinstance(operand, Operand):
                # Already in Operand form.
                final_operands.append(operand)
            elif isinstance(operand, Constant):
                # Convert constants into CONSTANT_INDEX'es
                final_operands.append(Operand(
                    OperandTypes.CONSTANT_INDEX,
                    operand.index
                ))
            elif isinstance(operand, dict):
                # lookupswitch's operand is a dict as
                # a special usability case.
                final_operands.append(operand)
            elif isinstance(operand, Label):
                final_operands.append(operand)
            else:
                # For anything else, lookup that opcode's operand
                # type from its definition.
                final_operands.append(Operand(
                    operand_fmts[i][1],
                    operand
                ))

        # Build the final, immutable `Instruction`.
        final.append(Instruction.create(mnemonic, final_operands))

    label_pcs = {}

    # The second pass, find the absolute PC for each label.
    current_pc = 0
    for ins in final:
        if isinstance(ins, Label):
            label_pcs[ins.name] = current_pc
            continue

        # size_on_disk must know the current pc because of alignment on
        # tableswitch and lookupswitch.
        current_pc += ins.size_on_disk(current_pc)

    # The third pass, now that we know where each label is we can figure
    # out the offset for each jump.
    current_pc = 0

    for ins in final:
        if isinstance(ins, Label):
            continue

        for i, operand in enumerate(ins.operands):
            if isinstance(operand, dict):
                # lookupswitch is a special case
                for k, v in operand.items():
                    if isinstance(v, Label):
                        operand[k] = Operand(40, label_pcs[v.name] - current_pc)
            elif isinstance(operand, Label):
                ins.operands[i] = Operand(
                    40,
                    label_pcs[operand.name] - current_pc
                )

        current_pc += ins.size_on_disk(current_pc)

        yield ins
Example #10
0
def literal_instruction(name, literal):
    """Return an instruction named `name` that has exactly one LITERAL operand with the value `literal`"""
    return Instruction.create(name, [literal_operand(literal)])
Example #11
0
def constant_instruction(name, constant):
    """Return an instruction named `name` that has exactly one CONSTANT_INDEX operand for the index of `constant`"""
    return Instruction.create(name, [constant_operand(constant)])
Example #12
0
def _create_instruction(name, pos, operands):
    ops = _create_operands(operands)
    instruction = Instruction.create(name, ops)
    if pos is not None:
        instruction = named_tuple_replace(instruction, pos=pos)
    return instruction
Example #13
0
def assemble(code):
    """
    A convienience method for 'assembling' bytecode over the regular
    :meth:`~jawa.attributes.code.CodeAttribute.assemble()` method with
    support for labels and direct constants.
    """
    final = []

    # We need to make three passes, because we cannot know the offset for
    # jump labels until after we've figured out the PC for each instructions,
    # which is complicated by the variable-width instructions set and
    # alignment padding.
    for line in code:
        if isinstance(line, Label):
            final.append(line)
            continue

        mnemonic, operands = line[0], line[1:]
        operand_fmts = definition_from_mnemonic(mnemonic)[1]

        # We need to coerce each opcodes operands into their
        # final `Operand` form.
        final_operands = []
        for i, operand in enumerate(operands):
            if isinstance(operand, Operand):
                # Already in Operand form.
                final_operands.append(operand)
            elif isinstance(operand, Constant):
                # Convert constants into CONSTANT_INDEX'es
                final_operands.append(Operand(
                    OperandTypes.CONSTANT_INDEX,
                    operand.index
                ))
            elif isinstance(operand, dict):
                    # lookupswitch's operand is a dict as
                # a special usability case.
                final_operands.append(operand)
            elif isinstance(operand, Label):
                final_operands.append(operand)
            else:
                # For anything else, lookup that opcode's operand
                # type from its definition.
                final_operands.append(Operand(
                    operand_fmts[i][1],
                    operand
                ))

        # Build the final, immutable `Instruction`.
        final.append(Instruction.from_mnemonic(
            mnemonic,
            operands=final_operands
        ))

    label_pcs = {}

    # The second pass, find the absolute PC for each label.
    current_pc = 0
    for ins in final:
        if isinstance(ins, Label):
            label_pcs[ins.name] = current_pc
            continue

        # size_on_disk must know the current pc because of alignment on
        # tableswitch and lookupswitch.
        current_pc += ins.size_on_disk(current_pc)

    # The third pass, now that we know where each label is we can figure
    # out the offset for each jump.
    current_pc = 0
    offset = lambda l: Operand(40, label_pcs[l.name] - current_pc)

    for ins in final:
        if isinstance(ins, Label):
            continue

        for i, operand in enumerate(ins.operands):
            if isinstance(operand, dict):
                # lookupswitch is a special case
                for k, v in operand.items():
                    if isinstance(v, Label):
                        operand[k] = offset(v)
            elif isinstance(operand, Label):
                ins.operands[i] = offset(operand)

        current_pc += ins.size_on_disk(current_pc)

        yield ins
Example #14
0
FIELD_DESCRIPTOR = 'I'
METHOD_NAME = 'some_method'
METHOD_DESCRIPTOR = '(II)I'
METHOD_KEY = MethodKey(METHOD_NAME, METHOD_DESCRIPTOR)
HANDLER = ExceptionHandler(
    start_pc=2,
    end_pc=3,
    handler_pc=4,
    catch_type=EXCEPTION_NAME
)

METHOD = BytecodeMethod(
    name='method_name',
    descriptor='(II)V',
    instructions=[
        named_tuple_replace(Instruction.create('nop'), pos=i) for i in range(5)
    ],
    max_locals=5,
    max_stack=15,
    args=[Integer, Integer],
    exception_handlers=Handlers([HANDLER])
)

COMPLEX_CLASS = JvmClass(
    name=COMPLEX_CLASS_NAME,
    name_of_base=RootObjectType.refers_to,
    constants=ConstantPool(),
    fields={
        FIELD_NAME: Integer
    },
    static_fields={
Example #15
0
from jawa.util.bytecode import Instruction

from pyjvm.core.actions import IncrementProgramCounter
from pyjvm.core.class_loaders import FixedClassLoader
from pyjvm.core.frame_locals import Locals
from pyjvm.core.jvm_types import Integer
from pyjvm.core.stack import Stack
from pyjvm.instructions.instructions import InstructorInputs, execute_instruction
from pyjvm.utils.utils import literal_operand, constant_operand

SOME_INT = Integer.create_instance(2)

_OP_STACK_KEY = 'op_stack'
_INSTRUCTION_KEY = 'instruction'

NON_EMPTY_INSTRUCTION_LIST = [Instruction.create('nop')]

NPE_CLASS_NAME = 'java/lang/NullPointerException'
CHECK_CAST_CLASS_NAME = 'java/lang/CheckCastException'


def dummy_loader():
    return FixedClassLoader({})


class DefaultInputs(InstructorInputs):
    """An `InstructorInputs` with sensible defaults

    This is useful for tests where no all the inputs are relevant.
    The instruction input must always be present, but the others can be omitted.
    The omitted ones will be created using the factory functions in the `DEFAULTS` dictionary.
Example #16
0
def assemble(code):
    """
    Assemble the given iterable of mnemonics, operands, and lables.

    A convienience over constructing individual Instruction and Operand
    objects, the output of this function can be directly piped to
    :class:`~jawa.attributes.code.CodeAttribute.assemble()` to produce
    executable bytecode.

    As a simple example, lets produce an infinite loop:

        >>> from jawa.assemble import assemble, Label
        >>> print(list(assemble((
        ...     Label('start'),
        ...     ('goto', Label('start'))
        ... ))))
        [Instruction(mnemonic='goto', opcode=167, operands=[
            Operand(op_type=40, value=0)], pos=0)]

    For a more complex example, see examples/hello_world.py.
    """
    final = []

    # We need to make three passes, because we cannot know the offset for
    # jump labels until after we've figured out the PC for each instructions,
    # which is complicated by the variable-width instructions set and
    # alignment padding.
    for line in code:
        if isinstance(line, Label):
            final.append(line)
            continue

        mnemonic, operands = line[0], line[1:]
        operand_fmts = definition_from_mnemonic(mnemonic)[1]

        # We need to coerce each opcodes operands into their
        # final `Operand` form.
        final_operands = []
        for i, operand in enumerate(operands):
            if isinstance(operand, Operand):
                # Already in Operand form.
                final_operands.append(operand)
            elif isinstance(operand, Constant):
                # Convert constants into CONSTANT_INDEX'es
                final_operands.append(Operand(OperandTypes.CONSTANT_INDEX, operand.index))
            elif isinstance(operand, dict):
                # lookupswitch's operand is a dict as
                # a special usability case.
                final_operands.append(operand)
            elif isinstance(operand, Label):
                final_operands.append(operand)
            else:
                # For anything else, lookup that opcode's operand
                # type from its definition.
                final_operands.append(Operand(operand_fmts[i][1], operand))

        # Build the final, immutable `Instruction`.
        final.append(Instruction.from_mnemonic(mnemonic, operands=final_operands))

    label_pcs = {}

    # The second pass, find the absolute PC for each label.
    current_pc = 0
    for ins in final:
        if isinstance(ins, Label):
            label_pcs[ins.name] = current_pc
            continue

        # size_on_disk must know the current pc because of alignment on
        # tableswitch and lookupswitch.
        current_pc += ins.size_on_disk(current_pc)

    # The third pass, now that we know where each label is we can figure
    # out the offset for each jump.
    current_pc = 0
    offset = lambda l: Operand(40, label_pcs[l.name] - current_pc)

    for ins in final:
        if isinstance(ins, Label):
            continue

        for i, operand in enumerate(ins.operands):
            if isinstance(operand, dict):
                # lookupswitch is a special case
                for k, v in operand.items():
                    if isinstance(v, Label):
                        operand[k] = offset(v)
            elif isinstance(operand, Label):
                ins.operands[i] = offset(operand)

        current_pc += ins.size_on_disk(current_pc)

        yield ins
Example #17
0
def test_compare():
    ins = Instruction.create('return')
    assert ins == 'return'
    assert ins == ins
    assert ins != 'not_return'