Source code for jawa.attributes.code

import io
import inspect
import functools
from typing import Iterator
from struct import pack
from itertools import repeat
from collections import namedtuple

from jawa.attribute import Attribute, AttributeTable
from jawa.util.bytecode import (
    read_instruction,
    write_instruction,
    Instruction
)

CodeException = namedtuple('CodeException', [
    'start_pc', 'end_pc', 'handler_pc', 'catch_type'
])


[docs]class CodeAttribute(Attribute): """ A `CodeAttribute` contains the executable bytecode of a single method. As a quick example, lets make a "HelloWorld" class with a single method that simply returns when it's called: .. code-block:: python from jawa import ClassFile from jawa.util.bytecode import Instruction cf = ClassFile.create('HelloWorld') main = cf.methods.create( # The name of the method 'main', # The signature of the method '([Ljava/lang/String;)V', # Tell Jawa to automatically create an empty CodeAttribute for # us to use. code=True ) main.code.max_locals = 1 main.access_flags.acc_static = True main.code.assemble([ Instruction.from_mnemonic('return') ]) # Save it to disk so we can run it with the JVM. with open('HelloWorld.class', 'wb') as fout: cf.save(fout) """ ADDED_IN = '1.0.2' MINIMUM_CLASS_VERSION = (45, 3) def __init__(self, table, name_index=None): super(CodeAttribute, self).__init__( table, name_index or table.cf.constants.create_utf8( 'Code' ).index ) self.max_stack = 0 self.max_locals = 0 self.exception_table = [] self.attributes = AttributeTable(table.cf, parent=self) self._code = ''
[docs] def unpack(self, info): """ Read the CodeAttribute from the byte string `info`. .. note:: Advanced usage only. You will typically never need to call this method as it will be called for you when loading a ClassFile. :param info: A byte string containing an unparsed CodeAttribute. """ self.max_stack, self.max_locals, c_len = info.unpack('>HHI') self._code = info.read(c_len) # The exception table ex_table_len = info.u2() for _ in repeat(None, ex_table_len): self.exception_table.append(CodeException( *info.unpack('>HHHH') )) self.attributes = AttributeTable(self.cf, parent=self) self.attributes.unpack(info)
[docs] def pack(self): """ The `CodeAttribute` in packed byte string form. """ with io.BytesIO() as file_out: file_out.write(pack( '>HHI', self.max_stack, self.max_locals, len(self._code) )) file_out.write(self._code) file_out.write(pack('>H', len(self.exception_table))) for exception in self.exception_table: file_out.write(pack('>HHHH', *exception)) self.attributes.pack(file_out) return file_out.getvalue()
[docs] def assemble(self, code): """ Assembles an iterable of :class:`~jawa.util.bytecode.Instruction` objects into a method's code body. """ with io.BytesIO() as code_out: for ins in code: write_instruction(code_out, code_out.tell(), ins) self._code = code_out.getvalue()
[docs] def disassemble(self, *, transforms=None) -> Iterator[Instruction]: """ Disassembles this method, yielding an iterable of :class:`~jawa.util.bytecode.Instruction` objects. """ if transforms is None: if self.cf.classloader: transforms = self.cf.classloader.bytecode_transforms else: transforms = [] transforms = [self._bind_transform(t) for t in transforms] with io.BytesIO(self._code) as code: ins_iter = iter(lambda: read_instruction(code, code.tell()), None) for ins in ins_iter: for transform in transforms: ins = transform(ins) yield ins
def _bind_transform(self, transform): sig = inspect.signature(transform, follow_wrapped=True) return functools.partial( transform, **{k: v for k, v in { 'cf': self.cf, 'attribute': self }.items() if k in sig.parameters} )