Пример #1
0
    def __init__(self, source: IO = None):
        # Default to J2SE_7
        self._version = ClassVersion(0x32, 0)
        self._constants = ConstantPool()
        self.access_flags = Flags(
            '>H', {
                'acc_public': 0x0001,
                'acc_final': 0x0010,
                'acc_super': 0x0020,
                'acc_interface': 0x0200,
                'acc_abstract': 0x0400,
                'acc_synthetic': 0x1000,
                'acc_annotation': 0x2000,
                'acc_enum': 0x4000
            })
        self._this = 0
        self._super = 0
        self._interfaces = []
        self.fields = FieldTable(self)
        self.methods = MethodTable(self)
        self.attributes = AttributeTable(self)
        #: The ClassLoader bound to this ClassFile, if any.
        self.classloader = None

        if source:
            self._from_io(source)
Пример #2
0
def test_ldc():
    value = 5.0
    constants = ConstantPool()
    constant = constants.create_double(value)

    assert_incrementing_instruction(
        instruction=constant_instruction('ldc', constant),
        constants=constants,
        expected=[Push(Double.create_instance(value))])
Пример #3
0
    def search_constant_pool(self, *, path: str, **options):
        """Partially load the class at `path`, yield all matching constants
        from the ConstantPool.

        This is an optimization method that does not load a complete ClassFile,
        nor does it add the results to the ClassLoader cache.

        :param path: Fully-qualified path to a ClassFile.
        :param options: A list of options to pass into `ConstantPool.find()`
        """
        with self.open(f'{path}.class') as source:
            # Skip over the magic, minor, and major version.
            source.read(8)
            pool = ConstantPool()
            pool.unpack(source)
            yield from pool.find(**options)
Пример #4
0
Файл: cf.py Проект: TkTech/Jawa
    def __init__(self, source: IO=None):
        # Default to J2SE_7
        self._version = ClassVersion(0x32, 0)
        self._constants = ConstantPool()
        self.access_flags = Flags('>H', {
            'acc_public': 0x0001,
            'acc_final': 0x0010,
            'acc_super': 0x0020,
            'acc_interface': 0x0200,
            'acc_abstract': 0x0400,
            'acc_synthetic': 0x1000,
            'acc_annotation': 0x2000,
            'acc_enum': 0x4000
        })
        self._this = 0
        self._super = 0
        self._interfaces = []
        self.fields = FieldTable(self)
        self.methods = MethodTable(self)
        self.attributes = AttributeTable(self)
        #: The ClassLoader bound to this ClassFile, if any.
        self.classloader = None

        if source:
            self._from_io(source)
Пример #5
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)
        ]
    )
Пример #6
0
def test_string_constant():
    text = 'some_text'
    consts = ConstantPool()
    const = consts.create_string(text)

    chars = [ord(c) for c in text]
    char_array = ArrayReferenceType(Integer).create_instance(chars)
    hash_value = hash(text) % (2**32)
    hash_ = Integer.create_instance(hash_value)

    reference_type = ObjectReferenceType('java/lang/String')
    jvm_object = JvmObject({'hash': hash_, 'value': char_array})

    assert_incrementing_instruction(
        instruction=constant_instruction('ldc', const),
        constants=consts,
        expected=[Push(reference_type.create_instance(jvm_object))])
Пример #7
0
def complex_machine():
    machine = Machine(FixedClassLoader({
        COMPLEX_CLASS_NAME: COMPLEX_CLASS,
        EXCEPTION_NAME: JvmClass(
            name=EXCEPTION_NAME,
            name_of_base=RootObjectType.refers_to,
            constants=ConstantPool()
        ),
        RootObjectType.refers_to: JvmClass(
            name=RootObjectType.refers_to,
            name_of_base=None,
            constants=ConstantPool()
        )
    }))
    for _ in range(2):
        frame = Frame.from_class_and_method(COMPLEX_CLASS, METHOD)
        machine.frames.push(frame)
    return machine
Пример #8
0
    def __init__(self, fio=None):
        # Default to J2SE_7
        self._version = ClassVersion(0x32, 0)
        self._constants = ConstantPool()
        self._access_flags = Flags('>H', {
            'acc_public': 0x0001,
            'acc_final': 0x0010,
            'acc_super': 0x0020,
            'acc_interface': 0x0200,
            'acc_abstract': 0x0400,
            'acc_synthetic': 0x1000,
            'acc_annotation': 0x2000,
            'acc_enum': 0x4000
        })
        self._this = 0
        self._super = 0
        self._interfaces = []
        self._fields = FieldTable(self)
        self._methods = MethodTable(self)
        self._attributes = AttributeTable(self)

        if fio:
            self._from_io(fio)
Пример #9
0
def test_printable_constants():
    # Ensure we can successfully repr valid constants without crashing.
    pool = ConstantPool()
    repr(pool.create_utf8('HelloWorld'))
    repr(pool.create_class('HelloWorld'))
    repr(pool.create_double(1))
    repr(pool.create_float(1))
    repr(pool.create_integer(1))
    repr(pool.create_long(1))
    repr(pool.create_name_and_type('HelloWorld', 'I'))
    repr(pool.create_field_ref('HelloWorld', 'test', 'I'))
    repr(pool.create_method_ref('HelloWorld', 'test', 'I)V'))
    repr(pool.create_interface_method_ref(
        'HelloWorld',
        'test',
        'I)V'
    ))
    repr(pool.create_string('HelloWorld'))
Пример #10
0
class ClassFile(object):
    """
    Implements the JVM ClassFile (files typically ending in ``.class``).

    To open an existing ClassFile::

        >>> with open('HelloWorld.class', 'rb') as fin:
        ...    cf = ClassFile(fin)

    To save a newly created or modified ClassFile::

        >>> cf = ClassFile.create('HelloWorld')
        >>> with open('HelloWorld.class', 'wb') as out:
        ...    cf.save(out)

    :meth:`~ClassFile.create` sets up some reasonable defaults equivalent to:

    .. code-block:: java

        public class HelloWorld extends java.lang.Object{
        }

    :param source: any file-like object providing ``.read()``.
    """

    #: The JVM ClassFile magic number.
    MAGIC = 0xCAFEBABE

    def __init__(self, source: IO = None):
        # Default to J2SE_7
        self._version = ClassVersion(0x32, 0)
        self._constants = ConstantPool()
        self.access_flags = Flags(
            '>H', {
                'acc_public': 0x0001,
                'acc_final': 0x0010,
                'acc_super': 0x0020,
                'acc_interface': 0x0200,
                'acc_abstract': 0x0400,
                'acc_synthetic': 0x1000,
                'acc_annotation': 0x2000,
                'acc_enum': 0x4000
            })
        self._this = 0
        self._super = 0
        self._interfaces = []
        self.fields = FieldTable(self)
        self.methods = MethodTable(self)
        self.attributes = AttributeTable(self)
        #: The ClassLoader bound to this ClassFile, if any.
        self.classloader = None

        if source:
            self._from_io(source)

    @classmethod
    def create(cls,
               this: str,
               super_: str = u'java/lang/Object') -> 'ClassFile':
        """
        A utility which sets up reasonable defaults for a new public class.

        :param this: The name of this class.
        :param super_: The name of this class's superclass.
        """
        cf = ClassFile()
        cf.access_flags.acc_public = True
        cf.access_flags.acc_super = True

        cf.this = cf.constants.create_class(this)
        cf.super_ = cf.constants.create_class(super_)

        return cf

    def save(self, source: IO):
        """
        Saves the class to the file-like object `source`.

        :param source: Any file-like object providing write().
        """
        write = source.write

        write(
            pack('>IHH', ClassFile.MAGIC, self.version.minor,
                 self.version.major))

        self._constants.pack(source)

        write(self.access_flags.pack())
        write(
            pack(f'>HHH{len(self._interfaces)}H', self._this, self._super,
                 len(self._interfaces), *self._interfaces))

        self.fields.pack(source)
        self.methods.pack(source)
        self.attributes.pack(source)

    def _from_io(self, source: IO):
        """
        Loads an existing JVM ClassFile from any file-like object.
        """
        read = source.read

        if unpack('>I', source.read(4))[0] != ClassFile.MAGIC:
            raise ValueError('invalid magic number')

        # The version is swapped on disk to (minor, major), so swap it back.
        self.version = unpack('>HH', source.read(4))[::-1]

        self._constants.unpack(source)

        # ClassFile access_flags, see section #4.1 of the JVM specs.
        self.access_flags.unpack(read(2))

        # The CONSTANT_Class indexes for "this" class and its superclass.
        # Interfaces are a simple list of CONSTANT_Class indexes.
        self._this, self._super, interfaces_count = unpack('>HHH', read(6))
        self._interfaces = unpack(f'>{interfaces_count}H',
                                  read(2 * interfaces_count))

        self.fields.unpack(source)
        self.methods.unpack(source)
        self.attributes.unpack(source)

    @property
    def version(self) -> ClassVersion:
        """
        The :class:`~jawa.cf.ClassVersion` for this class.

        Example::

            >>> cf = ClassFile.create('HelloWorld')
            >>> cf.version = 51, 0
            >>> print(cf.version)
            ClassVersion(major=51, minor=0)
            >>> print(cf.version.major)
            51
        """
        return self._version

    @version.setter
    def version(self, major_minor: Union[ClassVersion, Sequence]):
        self._version = ClassVersion(*major_minor)

    @property
    def constants(self) -> ConstantPool:
        """
        The :class:`~jawa.cp.ConstantPool` for this class.
        """
        return self._constants

    @property
    def this(self) -> ConstantClass:
        """
        The :class:`~jawa.constants.ConstantClass` which represents this class.
        """
        return self.constants.get(self._this)

    @this.setter
    def this(self, value):
        self._this = value.index

    @property
    def super_(self) -> ConstantClass:
        """
        The :class:`~jawa.constants.ConstantClass` which represents this
        class's superclass.
        """
        return self.constants.get(self._super)

    @super_.setter
    def super_(self, value: ConstantClass):
        self._super = value.index

    @property
    def interfaces(self) -> Iterable[ConstantClass]:
        """
        A list of direct superinterfaces of this class as indexes into
        the constant pool, in left-to-right order.
        """
        return [self._constants[idx] for idx in self._interfaces]

    @property
    def bootstrap_methods(self) -> BootstrapMethod:
        """
        Returns the bootstrap methods table from the BootstrapMethods attribute,
        if one exists. If it does not, one will be created.

        :returns: Table of `BootstrapMethod` objects.
        """
        bootstrap = self.attributes.find_one(name='BootstrapMethods')

        if bootstrap is None:
            bootstrap = self.attributes.create(
                ATTRIBUTE_CLASSES['BootstrapMethods'])

        return bootstrap.table

    def __repr__(self):
        return f'<ClassFile(this={self.this.name.value!r})>'
Пример #11
0
class ClassFile(object):
    """
    Implements the JVM ClassFile (files typically ending in ``.class``).

    To open an existing ClassFile::

        from jawa import ClassFile
        with open('HelloWorld.class') as fin:
            cf = ClassFile(fin)

    To save a newly created or modified ClassFile::

        with open('HelloWorld.class', 'wb') as fout:
            cf.save(fout)

    To create a new ClassFile, use the helper :meth:`~ClassFile.create`::

        from jawa import ClassFile
        cf = ClassFile.create('HelloWorld')
        with open('HelloWorld.class', 'wb') as fout:
            cf.save(fout)

    :meth:`~ClassFile.create` sets up some reasonable defaults equivelent to:

    .. code-block:: java

        public class HelloWorld extends java.lang.Object{
        }

    :param fio: any file-like object providing ``.read()``.
    """
    #: The JVM ClassFile magic number.
    MAGIC = 0xCAFEBABE

    def __init__(self, fio=None):
        # Default to J2SE_7
        self._version = ClassVersion(0x32, 0)
        self._constants = ConstantPool()
        self._access_flags = Flags('>H', {
            'acc_public': 0x0001,
            'acc_final': 0x0010,
            'acc_super': 0x0020,
            'acc_interface': 0x0200,
            'acc_abstract': 0x0400,
            'acc_synthetic': 0x1000,
            'acc_annotation': 0x2000,
            'acc_enum': 0x4000
        })
        self._this = 0
        self._super = 0
        self._interfaces = []
        self._fields = FieldTable(self)
        self._methods = MethodTable(self)
        self._attributes = AttributeTable(self)

        if fio:
            self._from_io(fio)

    @classmethod
    def create(cls, this, super_='java/lang/Object'):
        """
        A utility method which sets up reasonable defaults for a new public
        class.

        :param this: The name of this class.
        :param super_: The name of this class's superclass.
        """
        cf = ClassFile()
        cf.access_flags.acc_public = True
        cf.access_flags.acc_super = True

        cf._this = cf.constants.create_class(this).index
        cf._super = cf.constants.create_class(super_).index

        return cf

    def save(self, fout):
        """
        Saves the class to the file-like object `fout`.
        """
        write = fout.write

        write(pack('>IHH',
            ClassFile.MAGIC,
            self.version.minor,
            self.version.major
        ))

        self._constants._to_io(fout)

        write(self.access_flags.pack())
        write(pack('>HHH{0}H'.format(len(self._interfaces)),
            self._this,
            self._super,
            len(self._interfaces),
            *self._interfaces
        ))

        self._fields._to_io(fout)
        self._methods._to_io(fout)
        self._attributes._to_io(fout)

    # ------------
    # Internal
    # ------------

    def _from_io(self, fio):
        """
        Loads an existing JVM ClassFile from any file-like object.
        """
        read = fio.read

        if unpack('>I', fio.read(4))[0] != ClassFile.MAGIC:
            raise ValueError('invalid magic number')

        # The version is swapped on disk to (minor, major), so swap it back.
        self.version = unpack('>HH', fio.read(4))[::-1]

        self._constants._from_io(fio)

        # ClassFile access_flags, see section #4.1 of the JVM specs.
        self.access_flags.unpack(read(2))

        # The CONSTANT_Class indexes for "this" class and its superclass.
        # Interfaces are a simple list of CONSTANT_Class indexes.
        self._this, self._super, interfaces_count = unpack('>HHH', read(6))
        self._interfaces = unpack(
            '>{0}H'.format(interfaces_count),
            read(2 * interfaces_count)
        )

        self._fields._from_io(fio)
        self._methods._from_io(fio)
        self._attributes._from_io(fio)

    # -------------
    # Properties
    # -------------

    @property
    def version(self):
        """
        The :class:`~jawa.cf.ClassVersion` for this class.

        Example::

            >>> cf.version = 51, 0
            >>> print(cf.version)
            ClassVersion(major=51, minor=0)
            >>> print(cf.version.major)
            51
        """
        return self._version

    @version.setter
    def version(self, (major, minor)):
        self._version = ClassVersion(major, minor)
Пример #12
0
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={
        FIELD_NAME: Integer
    },
    methods={
        METHOD_KEY: METHOD
    }
)


def complex_machine():
    machine = Machine(FixedClassLoader({
        COMPLEX_CLASS_NAME: COMPLEX_CLASS,
Пример #13
0
def test_printable_constants():
    # Ensure we can successfully repr valid constants without crashing.
    pool = ConstantPool()
    repr(pool.create_utf8('HelloWorld'))
    repr(pool.create_class('HelloWorld'))
    repr(pool.create_double(1))
    repr(pool.create_float(1))
    repr(pool.create_integer(1))
    repr(pool.create_long(1))
    repr(pool.create_name_and_type('HelloWorld', 'I'))
    repr(pool.create_field_ref('HelloWorld', 'test', 'I'))
    repr(pool.create_method_ref('HelloWorld', 'test', 'I)V'))
    repr(pool.create_interface_method_ref('HelloWorld', 'test', 'I)V'))
    repr(pool.create_string('HelloWorld'))
Пример #14
0
Файл: cf.py Проект: TkTech/Jawa
class ClassFile(object):
    """
    Implements the JVM ClassFile (files typically ending in ``.class``).

    To open an existing ClassFile::

        >>> with open('HelloWorld.class', 'rb') as fin:
        ...    cf = ClassFile(fin)

    To save a newly created or modified ClassFile::

        >>> cf = ClassFile.create('HelloWorld')
        >>> with open('HelloWorld.class', 'wb') as out:
        ...    cf.save(out)

    :meth:`~ClassFile.create` sets up some reasonable defaults equivalent to:

    .. code-block:: java

        public class HelloWorld extends java.lang.Object{
        }

    :param source: any file-like object providing ``.read()``.
    """

    #: The JVM ClassFile magic number.
    MAGIC = 0xCAFEBABE

    def __init__(self, source: IO=None):
        # Default to J2SE_7
        self._version = ClassVersion(0x32, 0)
        self._constants = ConstantPool()
        self.access_flags = Flags('>H', {
            'acc_public': 0x0001,
            'acc_final': 0x0010,
            'acc_super': 0x0020,
            'acc_interface': 0x0200,
            'acc_abstract': 0x0400,
            'acc_synthetic': 0x1000,
            'acc_annotation': 0x2000,
            'acc_enum': 0x4000
        })
        self._this = 0
        self._super = 0
        self._interfaces = []
        self.fields = FieldTable(self)
        self.methods = MethodTable(self)
        self.attributes = AttributeTable(self)
        #: The ClassLoader bound to this ClassFile, if any.
        self.classloader = None

        if source:
            self._from_io(source)

    @classmethod
    def create(cls, this: str, super_: str=u'java/lang/Object') -> 'ClassFile':
        """
        A utility which sets up reasonable defaults for a new public class.

        :param this: The name of this class.
        :param super_: The name of this class's superclass.
        """
        cf = ClassFile()
        cf.access_flags.acc_public = True
        cf.access_flags.acc_super = True

        cf.this = cf.constants.create_class(this)
        cf.super_ = cf.constants.create_class(super_)

        return cf

    def save(self, source: IO):
        """
        Saves the class to the file-like object `source`.

        :param source: Any file-like object providing write().
        """
        write = source.write

        write(pack(
            '>IHH',
            ClassFile.MAGIC,
            self.version.minor,
            self.version.major
        ))

        self._constants.pack(source)

        write(self.access_flags.pack())
        write(pack(
            f'>HHH{len(self._interfaces)}H',
            self._this,
            self._super,
            len(self._interfaces),
            *self._interfaces
        ))

        self.fields.pack(source)
        self.methods.pack(source)
        self.attributes.pack(source)

    def _from_io(self, source: IO):
        """
        Loads an existing JVM ClassFile from any file-like object.
        """
        read = source.read

        if unpack('>I', source.read(4))[0] != ClassFile.MAGIC:
            raise ValueError('invalid magic number')

        # The version is swapped on disk to (minor, major), so swap it back.
        self.version = unpack('>HH', source.read(4))[::-1]

        self._constants.unpack(source)

        # ClassFile access_flags, see section #4.1 of the JVM specs.
        self.access_flags.unpack(read(2))

        # The CONSTANT_Class indexes for "this" class and its superclass.
        # Interfaces are a simple list of CONSTANT_Class indexes.
        self._this, self._super, interfaces_count = unpack('>HHH', read(6))
        self._interfaces = unpack(
            f'>{interfaces_count}H',
            read(2 * interfaces_count)
        )

        self.fields.unpack(source)
        self.methods.unpack(source)
        self.attributes.unpack(source)

    @property
    def version(self) -> ClassVersion:
        """
        The :class:`~jawa.cf.ClassVersion` for this class.

        Example::

            >>> cf = ClassFile.create('HelloWorld')
            >>> cf.version = 51, 0
            >>> print(cf.version)
            ClassVersion(major=51, minor=0)
            >>> print(cf.version.major)
            51
        """
        return self._version

    @version.setter
    def version(self, major_minor: Union[ClassVersion, Sequence]):
        self._version = ClassVersion(*major_minor)

    @property
    def constants(self) -> ConstantPool:
        """
        The :class:`~jawa.cp.ConstantPool` for this class.
        """
        return self._constants

    @property
    def this(self) -> ConstantClass:
        """
        The :class:`~jawa.constants.ConstantClass` which represents this class.
        """
        return self.constants.get(self._this)

    @this.setter
    def this(self, value):
        self._this = value.index

    @property
    def super_(self) -> ConstantClass:
        """
        The :class:`~jawa.constants.ConstantClass` which represents this
        class's superclass.
        """
        return self.constants.get(self._super)

    @super_.setter
    def super_(self, value: ConstantClass):
        self._super = value.index

    @property
    def interfaces(self) -> Iterable[ConstantClass]:
        """
        A list of direct superinterfaces of this class as indexes into
        the constant pool, in left-to-right order.
        """
        return [self._constants[idx] for idx in self._interfaces]

    @property
    def bootstrap_methods(self) -> BootstrapMethod:
        """
        Returns the bootstrap methods table from the BootstrapMethods attribute,
        if one exists. If it does not, one will be created.

        :returns: Table of `BootstrapMethod` objects.
        """
        bootstrap = self.attributes.find_one(name='BootstrapMethods')

        if bootstrap is None:
            bootstrap = self.attributes.create(
                ATTRIBUTE_CLASSES['BootstrapMethods']
            )

        return bootstrap.table

    def __repr__(self):
        return f'<ClassFile(this={self.this.name.value!r})>'