Source code for jawa.fields

from typing import IO, Callable, Iterator, Optional
from struct import unpack, pack
from itertools import repeat

from jawa.util.flags import Flags
from jawa.attribute import AttributeTable
from jawa.constants import Constant, UTF8
from jawa.attributes.constant_value import ConstantValueAttribute
from jawa.util.descriptor import field_descriptor


[docs]class Field(object): def __init__(self, cf): self._cf = cf self.access_flags = Flags('>H', { 'acc_public': 0x0001, 'acc_private': 0x0002, 'acc_protected': 0x0004, 'acc_static': 0x0008, 'acc_final': 0x0010, 'acc_volatile': 0x0040, 'acc_transient': 0x0080, 'acc_synthetic': 0x1000, 'acc_enum': 0x4000 }) self._name_index = 0 self._descriptor_index = 0 self.attributes = AttributeTable(cf) @property def descriptor(self) -> UTF8: """ The UTF8 Constant containing the field's descriptor. """ return self._cf.constants[self._descriptor_index] @property def type(self): """ A :class:`~jawa.util.descriptor.JVMType` representing the field's type. """ return field_descriptor(self.descriptor.value) @property def name(self) -> UTF8: """ The UTF8 Constant containing the field's name. """ return self._cf.constants[self._name_index] @property def value(self) -> ConstantValueAttribute: """ A shortcut for the field's ConstantValue attribute, should one exist. """ return self.attributes.find_one(name='ConstantValue')
[docs] def unpack(self, source: IO): """ Read the Field from the file-like object `fio`. .. 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 source: Any file-like object providing `read()` """ self.access_flags.unpack(source.read(2)) self._name_index, self._descriptor_index = unpack('>HH', source.read(4)) self.attributes.unpack(source)
[docs] def pack(self, out: IO): """ Write the Field to the file-like object `out`. .. note:: Advanced usage only. You will typically never need to call this method as it will be called for you when saving a ClassFile. :param out: Any file-like object providing `write()` """ out.write(self.access_flags.pack()) out.write(pack('>HH', self._name_index, self._descriptor_index)) self.attributes.pack(out)
[docs]class FieldTable(object): def __init__(self, cf): self._cf = cf self._table = []
[docs] def append(self, field: Field): self._table.append(field)
[docs] def find_and_remove(self, f: Callable): """ Removes any and all fields for which `f(field)` returns `True`. """ self._table = [fld for fld in self._table if not f(fld)]
[docs] def remove(self, field: Field): """ Removes a `Field` from the table by identity. """ self._table = [fld for fld in self._table if fld is not field]
[docs] def create(self, name: str, descriptor: str, value: Constant=None) -> Field: """ Creates a new field from `name` and `descriptor`. For example:: >>> from jawa.cf import ClassFile >>> cf = ClassFile.create('BeerCounter') >>> field = cf.fields.create('BeerCount', 'I') To automatically create a static field, pass a value:: >>> from jawa.cf import ClassFile >>> cf = ClassFile.create('BeerCounter') >>> field = cf.fields.create( ... 'MaxBeer', ... 'I', ... cf.constants.create_integer(99) ... ) :param name: Name of the new field. :param descriptor: Type descriptor of the new field. :param value: Optional static value for the field. """ field = Field(self._cf) name = self._cf.constants.create_utf8(name) descriptor = self._cf.constants.create_utf8(descriptor) field._name_index = name.index field._descriptor_index = descriptor.index field.access_flags.acc_public = True if value is not None: field.attributes.create(ConstantValueAttribute, value) field.access_flags.acc_static = True self.append(field) return field
def __iter__(self): for field in self._table: yield field
[docs] def unpack(self, source: IO): """ Read the FieldTable from the file-like object `source`. .. 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 source: Any file-like object providing `read()` """ field_count = unpack('>H', source.read(2))[0] for _ in repeat(None, field_count): field = Field(self._cf) field.unpack(source) self.append(field)
[docs] def pack(self, out: IO): """ Write the FieldTable to the file-like object `out`. .. note:: Advanced usage only. You will typically never need to call this method as it will be called for you when saving a ClassFile. :param out: Any file-like object providing `write()` """ out.write(pack('>H', len(self))) for field in self._table: field.pack(out)
def __len__(self): return len(self._table)
[docs] def find(self, *, name: str=None, type_: str=None, f: Callable=None) -> Iterator[Field]: """ Iterates over the fields table, yielding each matching method. Calling without any arguments is equivalent to iterating over the table. :param name: The name of the field(s) to find. :param type_: The field descriptor (Ex: 'I') :param f: Any callable which takes one argument (the field). """ for field in self._table: if name is not None and field.name.value != name: continue descriptor = field.descriptor.value if type_ is not None and type_ != descriptor: continue if f is not None and not f(field): continue yield field
[docs] def find_one(self, **kwargs) -> Optional[Field]: """ Same as ``find()`` but returns only the first result. """ return next(self.find(**kwargs), None)