Initial commit
This commit is contained in:
0
pjvm/__init__.py
Normal file
0
pjvm/__init__.py
Normal file
194
pjvm/classloader.py
Normal file
194
pjvm/classloader.py
Normal file
@@ -0,0 +1,194 @@
|
||||
import logging
|
||||
|
||||
from typing import BinaryIO, IO, Union
|
||||
|
||||
from .unpacker import Unpacker
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
formats = {
|
||||
'magic': '4s',
|
||||
'major_version': 'h',
|
||||
'minor_version': 'h',
|
||||
'constant_pool_count': 'h',
|
||||
'access_flags': 'h',
|
||||
'this_class': 'h',
|
||||
'super_class': 'h',
|
||||
'interfaces_count': 'h',
|
||||
'fields_count': 'h',
|
||||
'methods_count': 'h',
|
||||
'attributes_count': 'h',
|
||||
'byte': 'b',
|
||||
'short': 'h'
|
||||
}
|
||||
|
||||
|
||||
def create_constant_type(name, fmt, *fieldnames):
|
||||
if len(fmt) != len(fieldnames):
|
||||
raise ValueError("Length mismatch format and names")
|
||||
return {
|
||||
'name': name,
|
||||
'format': fmt,
|
||||
'fieldnames': fieldnames
|
||||
}
|
||||
|
||||
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4
|
||||
CONSTANT_POOL_TYPES = {
|
||||
7: create_constant_type('Class', 'h', 'name_index'),
|
||||
9: create_constant_type('Fieldref', 'hh', 'class_index', 'name_and_type_index'),
|
||||
10: create_constant_type('Methodref', 'hh', 'class_index', 'name_and_type_index'),
|
||||
11: create_constant_type('InterfaceMethodref', 'hh', 'class_index', 'name_and_type_index'),
|
||||
8: create_constant_type('String', 'h', 'string_index'),
|
||||
3: create_constant_type('Integer', 'i', 'value'), # divergence, actual value instead of raw bytes
|
||||
4: create_constant_type('Float', 'f', 'value'), # divergence, actual value instead of raw bytes
|
||||
5: create_constant_type('Long', 'q', 'value'), # divergence, actual value instead of raw bytes
|
||||
6: create_constant_type('Double', 'd', 'value'), # divergence, actual value instead of raw bytes
|
||||
12: create_constant_type('NameAndType', 'hh', 'name_index', 'descriptor_index'),
|
||||
1: None, # TODO: special format. Pascal coded but with short instead of byte
|
||||
15: create_constant_type('MethodHandle', 'bh', 'reference_kind', 'reference_index'),
|
||||
16: create_constant_type('MethodType', 'h', 'descriptor_index'),
|
||||
18: create_constant_type('InvokeDynamic', 'hh', 'bootstrap_method_attr_index', 'name_ant_type_index')
|
||||
}
|
||||
|
||||
JAVA_CLASS_MAGIC = b'\xca\xfe\xba\xbe'
|
||||
|
||||
|
||||
class ClassLoader:
|
||||
file: IO
|
||||
|
||||
magic: bytes
|
||||
major_version: int
|
||||
minor_version: int
|
||||
|
||||
constant_pool_count: int
|
||||
constant_pool: list
|
||||
|
||||
access_flags: int
|
||||
this_class: int
|
||||
super_class: int
|
||||
|
||||
interfaces_count: int
|
||||
interfaces: list
|
||||
|
||||
fields_count: int
|
||||
fields: list
|
||||
|
||||
methods_count: int
|
||||
methods: list
|
||||
|
||||
attributes_count: int
|
||||
attributes: list
|
||||
|
||||
def __init__(self, stream: BinaryIO):
|
||||
self.stream = stream
|
||||
self.unpacker = Unpacker(formats, self.stream)
|
||||
|
||||
def load(self):
|
||||
self.magic, = self.unpacker.magic
|
||||
|
||||
if self.magic != JAVA_CLASS_MAGIC:
|
||||
raise ValueError(f"Not a valid java class file! Magic: {self.magic!r}")
|
||||
|
||||
self.minor_version, = self.unpacker.minor_version
|
||||
self.major_version, = self.unpacker.major_version
|
||||
|
||||
LOGGER.info(f"Parsing class with version {self.major_version}.{self.minor_version}")
|
||||
|
||||
self.constant_pool_count, = self.unpacker.constant_pool_count
|
||||
|
||||
LOGGER.info(f"Parsing {self.constant_pool_count} constant pool items")
|
||||
|
||||
self.constant_pool = []
|
||||
for i in range(self.constant_pool_count - 1):
|
||||
tag, = self.unpacker.byte
|
||||
|
||||
if tag == 1:
|
||||
length, = self.unpacker.short
|
||||
utf8_str, = self.unpacker[f'{length}s']
|
||||
data = {
|
||||
'tag': tag,
|
||||
'type': 'Utf8',
|
||||
'value': utf8_str.decode()
|
||||
}
|
||||
else:
|
||||
constant_info = CONSTANT_POOL_TYPES.get(tag)
|
||||
data = {
|
||||
'tag': tag,
|
||||
'type': constant_info['name'],
|
||||
**dict(zip(constant_info['fieldnames'], self.unpacker[constant_info['format']]))
|
||||
}
|
||||
self.constant_pool.append(data)
|
||||
|
||||
self.access_flags, = self.unpacker.access_flags # todo enum.IntFlag
|
||||
self.this_class, = self.unpacker.this_class
|
||||
self.super_class, = self.unpacker.super_class
|
||||
|
||||
self.interfaces_count, = self.unpacker.interfaces_count
|
||||
self.interfaces = []
|
||||
|
||||
for i in range(self.interfaces_count):
|
||||
self.interfaces.append(self.unpacker.short[0])
|
||||
|
||||
self.fields_count, = self.unpacker.fields_count
|
||||
self.fields = []
|
||||
|
||||
for i in range(self.fields_count):
|
||||
access_flags, name_index, descriptor_index, attributes_count = \
|
||||
self.unpacker['hhhh']
|
||||
attributes = [self._parse_attribute() for _ in range(attributes_count)]
|
||||
self.fields.append({
|
||||
'access_flags': access_flags,
|
||||
'name_index': name_index,
|
||||
'descriptor_index': descriptor_index,
|
||||
'attributes_count': attributes_count,
|
||||
'attributes': attributes
|
||||
})
|
||||
|
||||
self.methods_count, = self.unpacker.methods_count
|
||||
self.methods = []
|
||||
|
||||
for i in range(self.methods_count):
|
||||
access_flags, name_index, descriptor_index, attributes_count = \
|
||||
self.unpacker['hhhh']
|
||||
attributes = [self._parse_attribute() for _ in range(attributes_count)]
|
||||
self.methods.append({
|
||||
'access_flags': access_flags,
|
||||
'name_index': name_index,
|
||||
'descriptor_index': descriptor_index,
|
||||
'attributes_count': attributes_count,
|
||||
'attributes': attributes
|
||||
})
|
||||
|
||||
self.attributes_count, = self.unpacker.attributes_count
|
||||
self.attributes = [self._parse_attribute() for _ in range(self.attributes_count)]
|
||||
|
||||
# cleanup
|
||||
del self.stream
|
||||
del self.unpacker
|
||||
|
||||
def _parse_attribute(self):
|
||||
attribute_name_index, attribute_length = self.unpacker['hi']
|
||||
data, = self.unpacker[f'{attribute_length}s']
|
||||
return {'attribute_name_index': attribute_name_index, 'attribute_length': attribute_length, 'info': data}
|
||||
|
||||
|
||||
def load_class(name_or_stream: Union[str, BinaryIO]) -> ClassLoader:
|
||||
"""
|
||||
Open a class file and parse it using the ClassLoader
|
||||
|
||||
Args:
|
||||
name_or_stream: A file name or a binary stream
|
||||
|
||||
Returns:
|
||||
A ClassLoader instance with a loaded class
|
||||
"""
|
||||
stream: BinaryIO
|
||||
if isinstance(name_or_stream, str):
|
||||
stream = open(name_or_stream, 'rb')
|
||||
else:
|
||||
stream = name_or_stream
|
||||
|
||||
loader = ClassLoader(stream)
|
||||
loader.load()
|
||||
return loader
|
||||
227
pjvm/clazz.py
Normal file
227
pjvm/clazz.py
Normal file
@@ -0,0 +1,227 @@
|
||||
import collections
|
||||
import enum
|
||||
import logging
|
||||
import struct
|
||||
from typing import Dict, List, Optional, Union, Tuple
|
||||
|
||||
from pjvm.unpacker import Unpacker
|
||||
from .classloader import ClassLoader
|
||||
from .expressions import METHOD_SIGNATURE
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
exception_table_tuple = collections.namedtuple('exception_table', ['start_pc', 'end_pc', 'handler_pc', 'catch_type'])
|
||||
|
||||
|
||||
class ClassAccessFlags(enum.IntFlag):
|
||||
ACC_PUBLIC = 0x0001 # Declared public; may be accessed from outside its package.
|
||||
ACC_FINAL = 0x0010 # Declared final; no subclasses allowed.
|
||||
ACC_SUPER = 0x0020 # Treat superclass methods specially when invoked by the invokespecial instruction.
|
||||
ACC_INTERFACE = 0x0200 # Is an interface, not a class.
|
||||
ACC_ABSTRACT = 0x0400 # Declared abstract; must not be instantiated.
|
||||
ACC_SYNTHETIC = 0x1000 # Declared synthetic; not present in the source code.
|
||||
ACC_ANNOTATION = 0x2000 # Declared as an annotation type.
|
||||
ACC_ENUM = 0x4000 # Declared as an enum type.
|
||||
|
||||
|
||||
class FieldAccessFlags(enum.IntFlag):
|
||||
ACC_PUBLIC = 0x0001 # Declared public; may be accessed from outside its package.
|
||||
ACC_PRIVATE = 0x0002 # Declared private; usable only within the defining class.
|
||||
ACC_PROTECTED = 0x0004 # Declared protected; may be accessed within subclasses.
|
||||
ACC_STATIC = 0x0008 # Declared static.
|
||||
ACC_FINAL = 0x0010 # Declared final; never directly assigned to after object construction (JLS §17.5).
|
||||
ACC_VOLATILE = 0x0040 # Declared volatile; cannot be cached.
|
||||
ACC_TRANSIENT = 0x0080 # Declared transient; not written or read by a persistent object manager.
|
||||
ACC_SYNTHETIC = 0x1000 # Declared synthetic; not present in the source code.
|
||||
ACC_ENUM = 0x4000 # Declared as an element of an enum.
|
||||
|
||||
|
||||
class MethodAccessFlags(enum.IntFlag):
|
||||
ACC_PUBLIC = 0x0001 # Declared public; may be accessed from outside its package.
|
||||
ACC_PRIVATE = 0x0002 # Declared private; accessible only within the defining class.
|
||||
ACC_PROTECTED = 0x0004 # Declared protected; may be accessed within subclasses.
|
||||
ACC_STATIC = 0x0008 # Declared static.
|
||||
ACC_FINAL = 0x0010 # Declared final; must not be overridden (§5.4.5).
|
||||
ACC_SYNCHRONIZED = 0x0020 # Declared synchronized; invocation is wrapped by a monitor use.
|
||||
ACC_BRIDGE = 0x0040 # A bridge method, generated by the compiler.
|
||||
ACC_VARARGS = 0x0080 # Declared with variable number of arguments.
|
||||
ACC_NATIVE = 0x0100 # Declared native; implemented in a language other than Java.
|
||||
ACC_ABSTRACT = 0x0400 # Declared abstract; no implementation is provided.
|
||||
ACC_STRICT = 0x0800 # Declared strictfp; floating-point mode is FP-strict.
|
||||
ACC_SYNTHETIC = 0x1000 # Declared synthetic; not present in the source code.
|
||||
|
||||
|
||||
KNOWN_ATTRIBUTES = ['SourceFile', 'Code']
|
||||
NOT_IMPLEMENTED_ATTRIBUTES = ['ConstantValue', 'StackMapTable', 'Exceptions', 'InnerClasses', 'EnclosingMethod',
|
||||
'Synthetic', 'Signature', 'SourceDebugExtension', 'LineNumberTable',
|
||||
'LocalVariableTable ', 'LocalVariableTypeTable', 'Deprecated',
|
||||
'RuntimeVisibleAnnotations', 'RuntimeInvisibleAnnotations',
|
||||
'RuntimeVisibleParameterAnnotations', 'RuntimeInvisibleParameterAnnotations',
|
||||
'AnnotationDefault', 'BootstrapMethods']
|
||||
|
||||
|
||||
class Class:
|
||||
methods: Dict[str, "Method"]
|
||||
fields: Dict[str, "Field"]
|
||||
|
||||
def __init__(self, loader: ClassLoader):
|
||||
self.loader = loader
|
||||
self.methods = {}
|
||||
self.attributes = {}
|
||||
self.fields = {}
|
||||
|
||||
self.access_flags: ClassAccessFlags = ClassAccessFlags(0)
|
||||
self.this_class: str = ""
|
||||
self.super_class: Optional[str] = None
|
||||
|
||||
# todo other info
|
||||
self.source_file: Optional[str] = None
|
||||
|
||||
self.initialize()
|
||||
|
||||
def initialize(self):
|
||||
self.access_flags = ClassAccessFlags(self.loader.access_flags)
|
||||
|
||||
self.this_class = self.cp_get_classname(self.loader.this_class)
|
||||
self.super_class = self.cp_get_classname(self.loader.super_class)
|
||||
|
||||
LOGGER.info(f"Parsing info for {self.access_flags!s} {self.this_class} : {self.super_class}")
|
||||
|
||||
self.initialize_fields()
|
||||
self.initialize_methods()
|
||||
self.initialize_attributes()
|
||||
|
||||
def initialize_fields(self):
|
||||
field: Dict[str, Union[int, list]]
|
||||
for field in self.loader.fields:
|
||||
acc = FieldAccessFlags(field['access_flags'])
|
||||
name = self.cp_get_utf8(field['name_index'])
|
||||
descriptor = self.cp_get_utf8(field['descriptor_index'])
|
||||
attributes = field['attributes']
|
||||
self.fields[name] = Field(acc, name, descriptor, attributes, self)
|
||||
|
||||
def initialize_methods(self):
|
||||
method: Dict[str, Union[int, list]]
|
||||
for method in self.loader.methods:
|
||||
acc = MethodAccessFlags(method['access_flags'])
|
||||
name = self.cp_get_utf8(method['name_index'])
|
||||
descriptor = self.cp_get_utf8(method['descriptor_index'])
|
||||
attributes = method['attributes']
|
||||
self.methods[name] = Method(acc, name, descriptor, attributes, self)
|
||||
|
||||
def initialize_attributes(self):
|
||||
for attr in self.loader.attributes:
|
||||
attr_name = self.cp_get_utf8(attr['attribute_name_index'])
|
||||
if attr_name in NOT_IMPLEMENTED_ATTRIBUTES:
|
||||
print(f"Attribute {attr_name} found but not implemented. {attr['info'][:10]}")
|
||||
elif attr_name in KNOWN_ATTRIBUTES:
|
||||
getattr(self, f'_attr_{attr_name}')(attr['info'])
|
||||
else:
|
||||
pass # ignore unknowns
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
def _attr_SourceFile(self, info: bytes):
|
||||
self.source_file = self.cp_get_utf8(struct.unpack('>h', info)[0])
|
||||
LOGGER.info(f"Found source file {self.source_file} for FULL_NAME")
|
||||
|
||||
def cp_get(self, index: int) -> dict:
|
||||
return self.loader.constant_pool[index - 1]
|
||||
|
||||
def cp_get_utf8(self, index: int) -> str:
|
||||
return self.cp_get(index)['value']
|
||||
|
||||
def cp_get_classname(self, index: int) -> str:
|
||||
return self.cp_get_utf8(self.cp_get(index)['name_index'])
|
||||
|
||||
def cp_get_name_type(self, index: int) -> Tuple[str, str]:
|
||||
nt = self.cp_get(index)
|
||||
return self.cp_get_utf8(nt['name_index']), self.cp_get_utf8(nt['descriptor_index'])
|
||||
|
||||
def cp_get_fieldref(self, index: int) -> Tuple[str, str, str]:
|
||||
# Exactly the same as methodref, only name differs
|
||||
return self.cp_get_methodref(index)
|
||||
|
||||
def cp_get_methodref(self, index: int) -> Tuple[str, str, str]:
|
||||
"""
|
||||
Get the class name, method name and descriptor
|
||||
Args:
|
||||
index: the index
|
||||
"""
|
||||
mr = self.cp_get(index)
|
||||
name, typename = self.cp_get_name_type(mr['name_and_type_index'])
|
||||
return self.cp_get_classname(mr['class_index']), name, typename
|
||||
|
||||
|
||||
class Code:
|
||||
def __init__(self, code: bytes):
|
||||
self.exception_handlers = []
|
||||
self.attributes = []
|
||||
unpacker = Unpacker.from_bytes(code)
|
||||
self.max_stack, = unpacker['h']
|
||||
self.max_locals, = unpacker['h']
|
||||
code_len, = unpacker['i']
|
||||
self.code, = unpacker[f'{code_len}s']
|
||||
exception_table_length, = unpacker['h']
|
||||
for i in range(exception_table_length):
|
||||
self.exception_handlers.append(exception_table_tuple(*unpacker['hhhh']))
|
||||
|
||||
attributes_count, = unpacker['h']
|
||||
for i in range(attributes_count):
|
||||
attribute_name_index, attribute_length = unpacker['hi']
|
||||
data, = unpacker[f'{attribute_length}s']
|
||||
self.attributes.append(
|
||||
{'attribute_name_index': attribute_name_index, 'attribute_length': attribute_length, 'info': data})
|
||||
|
||||
|
||||
class Field:
|
||||
def __init__(self, acc, name, descriptor, attributes, clazz: Class):
|
||||
self.acc = acc
|
||||
self.name = name
|
||||
self.descriptor = descriptor
|
||||
self.attributes = attributes
|
||||
self.clazz = clazz
|
||||
|
||||
self.parse_attributes()
|
||||
|
||||
LOGGER.info(f"New field {name} {descriptor}")
|
||||
# todo initialize
|
||||
|
||||
def parse_attributes(self):
|
||||
LOGGER.info("TODO parse field attributes")
|
||||
pass
|
||||
|
||||
|
||||
class Method:
|
||||
def __init__(self, acc, name, descriptor, attributes, clazz: Class):
|
||||
self.acc = acc
|
||||
self.name = name
|
||||
self.descriptor = descriptor
|
||||
self.attributes = attributes
|
||||
self.clazz = clazz
|
||||
|
||||
self.code: Optional[Code] = None
|
||||
|
||||
self.return_value: str = ''
|
||||
self.args: List[str] = []
|
||||
|
||||
self.parse_descriptor()
|
||||
|
||||
self.parse_attributes()
|
||||
|
||||
LOGGER.info(f"New method {self.return_value} {name}({', '.join(self.args)})")
|
||||
|
||||
def parse_descriptor(self):
|
||||
res = METHOD_SIGNATURE.search(self.descriptor)
|
||||
self.return_value = res.group('RET')
|
||||
self.args = res.group('ARGS').split(';')
|
||||
self.args = list(filter(None, self.args))
|
||||
|
||||
# todo initialize
|
||||
|
||||
def parse_attributes(self):
|
||||
# todo Exceptions, Synthetic, Signature, Deprecated, RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations, AnnotationDefault
|
||||
|
||||
for attr in self.attributes:
|
||||
name = self.clazz.cp_get_utf8(attr['attribute_name_index'])
|
||||
|
||||
if name == 'Code':
|
||||
self.code = Code(attr['info'])
|
||||
21
pjvm/exceptions.py
Normal file
21
pjvm/exceptions.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from .opcodes import INSTRUCTIONS
|
||||
|
||||
|
||||
class PJVMException(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
class PJVMUnknownOpcode(PJVMException):
|
||||
def __init__(self, opcode: int):
|
||||
super(PJVMUnknownOpcode, self).__init__(f"OPCODE {opcode} -> {INSTRUCTIONS[opcode]['name']} not implemented")
|
||||
|
||||
|
||||
class PJVMNotImplemented(PJVMException):
|
||||
pass
|
||||
|
||||
|
||||
class PJVMTypeError(PJVMException):
|
||||
pass
|
||||
|
||||
|
||||
__all__ = ['PJVMException', 'PJVMUnknownOpcode', 'PJVMNotImplemented', 'PJVMTypeError']
|
||||
4
pjvm/expressions.py
Normal file
4
pjvm/expressions.py
Normal file
@@ -0,0 +1,4 @@
|
||||
import re
|
||||
|
||||
METHOD_SIGNATURE = re.compile(r'\((?P<ARGS>\[?(?:[IFD]|L(?:\w+/)+\w+;)*)\)(?P<RET>[IFDV])')
|
||||
TYPE = re.compile(r'\[?(?:[IVDF]|L(?:[\w\d]+/)+[\w\d]+)')
|
||||
0
pjvm/jtypes/__init__.py
Normal file
0
pjvm/jtypes/__init__.py
Normal file
10
pjvm/jtypes/jarray.py
Normal file
10
pjvm/jtypes/jarray.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from .jtype import JType
|
||||
|
||||
|
||||
class JArray(JType):
|
||||
j_base_type = '['
|
||||
|
||||
values: []
|
||||
|
||||
def __init__(self):
|
||||
self.values = []
|
||||
70
pjvm/jtypes/jprimitives.py
Normal file
70
pjvm/jtypes/jprimitives.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import struct
|
||||
from typing import Union
|
||||
|
||||
from .jtype import JType
|
||||
|
||||
|
||||
class JPrimitive(JType):
|
||||
def __init__(self, value):
|
||||
self.j_type = self.j_base_type
|
||||
super(JPrimitive, self).__init__(value)
|
||||
|
||||
|
||||
class JChar(JPrimitive):
|
||||
j_base_type = 'C' # todo 16-bit unicode code point
|
||||
|
||||
|
||||
class JDouble(JPrimitive):
|
||||
j_base_type = 'D'
|
||||
|
||||
|
||||
class JFloat(JPrimitive):
|
||||
j_base_type = 'F'
|
||||
|
||||
|
||||
class JBool(JPrimitive):
|
||||
j_base_type = 'Z'
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
super(JBool, self).__init__(value)
|
||||
|
||||
|
||||
# Integer types
|
||||
|
||||
class JPrimitiveInteger(JPrimitive):
|
||||
pj_mask: int
|
||||
pj_struct: str
|
||||
|
||||
def __init__(self, value: Union[JType, bytes, int]):
|
||||
if isinstance(value, int):
|
||||
self.value = value & self.pj_mask
|
||||
elif isinstance(value, bytes):
|
||||
self.value, = struct.unpack(self.pj_struct, value)
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
super(JPrimitiveInteger, self).__init__(value)
|
||||
|
||||
|
||||
class JByte(JPrimitiveInteger):
|
||||
j_base_type = 'B'
|
||||
pj_mask = 0xff
|
||||
pj_struct = 'b'
|
||||
|
||||
|
||||
class JShort(JPrimitiveInteger):
|
||||
j_base_type = 'S'
|
||||
pj_mask = 0xffff
|
||||
pj_struct = 'h'
|
||||
|
||||
|
||||
class JInt(JPrimitiveInteger):
|
||||
j_base_type = 'I'
|
||||
pj_mask = 0xffffffff
|
||||
pj_struct = 'i'
|
||||
|
||||
|
||||
class JLong(JPrimitiveInteger):
|
||||
j_base_type = 'J'
|
||||
pj_mask = 0xffffffffffffffff
|
||||
pj_struct = 'q'
|
||||
13
pjvm/jtypes/jtype.py
Normal file
13
pjvm/jtypes/jtype.py
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
class JType:
|
||||
j_base_type: str
|
||||
j_type: str
|
||||
|
||||
def __init__(self, value):
|
||||
raise NotImplementedError()
|
||||
|
||||
def instance_of(self, other: "JType") -> bool:
|
||||
if self.j_type == other.j_type:
|
||||
return True
|
||||
# TODO inheritance check
|
||||
return False
|
||||
406
pjvm/opcodes.py
Normal file
406
pjvm/opcodes.py
Normal file
@@ -0,0 +1,406 @@
|
||||
INSTRUCTIONS = {
|
||||
0x32: {"name": 'aaload', "group": 'aaload', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.aaload
|
||||
0x53: {"name": 'aastore', "group": 'aastore', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.aastore
|
||||
0x1: {"name": 'aconst_null', "group": 'aconst_null', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.aconst_null
|
||||
0x19: {"name": 'aload', "group": 'aload', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.aload
|
||||
0x2a: {"name": 'aload_0', "group": 'aload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.aload_n
|
||||
0x2b: {"name": 'aload_1', "group": 'aload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.aload_n
|
||||
0x2c: {"name": 'aload_2', "group": 'aload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.aload_n
|
||||
0x2d: {"name": 'aload_3', "group": 'aload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.aload_n
|
||||
0xbd: {"name": 'anewarray', "group": 'anewarray', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.anewarray
|
||||
0xb0: {"name": 'areturn', "group": 'areturn', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.areturn
|
||||
0xbe: {"name": 'arraylength', "group": 'arraylength', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.arraylength
|
||||
0x3a: {"name": 'astore', "group": 'astore', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.astore
|
||||
0x4b: {"name": 'astore_0', "group": 'astore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.astore_n
|
||||
0x4c: {"name": 'astore_1', "group": 'astore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.astore_n
|
||||
0x4d: {"name": 'astore_2', "group": 'astore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.astore_n
|
||||
0x4e: {"name": 'astore_3', "group": 'astore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.astore_n
|
||||
0xbf: {"name": 'athrow', "group": 'athrow', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.athrow
|
||||
0x33: {"name": 'baload', "group": 'baload', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.baload
|
||||
0x54: {"name": 'bastore', "group": 'bastore', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.bastore
|
||||
0x10: {"name": 'bipush', "group": 'bipush', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.bipush
|
||||
0x34: {"name": 'caload', "group": 'caload', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.caload
|
||||
0x55: {"name": 'castore', "group": 'castore', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.castore
|
||||
0xc0: {"name": 'checkcast', "group": 'checkcast', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.checkcast
|
||||
0x90: {"name": 'd2f', "group": 'd2f', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.d2f
|
||||
0x8e: {"name": 'd2i', "group": 'd2i', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.d2i
|
||||
0x8f: {"name": 'd2l', "group": 'd2l', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.d2l
|
||||
0x63: {"name": 'dadd', "group": 'dadd', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dadd
|
||||
0x31: {"name": 'daload', "group": 'daload', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.daload
|
||||
0x52: {"name": 'dastore', "group": 'dastore', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dastore
|
||||
0x98: {"name": 'dcmpg', "group": 'dcmp<op>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dcmpop
|
||||
0x97: {"name": 'dcmpl', "group": 'dcmp<op>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dcmpop
|
||||
0xe: {"name": 'dconst_0', "group": 'dconst_<d>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dconst_d
|
||||
0xf: {"name": 'dconst_1', "group": 'dconst_<d>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dconst_d
|
||||
0x6f: {"name": 'ddiv', "group": 'ddiv', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ddiv
|
||||
0x18: {"name": 'dload', "group": 'dload', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dload
|
||||
0x26: {"name": 'dload_0', "group": 'dload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dload_n
|
||||
0x27: {"name": 'dload_1', "group": 'dload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dload_n
|
||||
0x28: {"name": 'dload_2', "group": 'dload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dload_n
|
||||
0x29: {"name": 'dload_3', "group": 'dload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dload_n
|
||||
0x6b: {"name": 'dmul', "group": 'dmul', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dmul
|
||||
0x77: {"name": 'dneg', "group": 'dneg', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dneg
|
||||
0x73: {"name": 'drem', "group": 'drem', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.drem
|
||||
0xaf: {"name": 'dreturn', "group": 'dreturn', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dreturn
|
||||
0x39: {"name": 'dstore', "group": 'dstore', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dstore
|
||||
0x47: {"name": 'dstore_0', "group": 'dstore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dstore_n
|
||||
0x48: {"name": 'dstore_1', "group": 'dstore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dstore_n
|
||||
0x49: {"name": 'dstore_2', "group": 'dstore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dstore_n
|
||||
0x4a: {"name": 'dstore_3', "group": 'dstore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dstore_n
|
||||
0x67: {"name": 'dsub', "group": 'dsub', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dsub
|
||||
0x59: {"name": 'dup', "group": 'dup', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dup
|
||||
0x5a: {"name": 'dup_x1', "group": 'dup_x1', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dup_x1
|
||||
0x5b: {"name": 'dup_x2', "group": 'dup_x2', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dup_x2
|
||||
0x5c: {"name": 'dup2', "group": 'dup2', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dup2
|
||||
0x5d: {"name": 'dup2_x1', "group": 'dup2_x1', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dup2_x1
|
||||
0x5e: {"name": 'dup2_x2', "group": 'dup2_x2', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dup2_x2
|
||||
0x8d: {"name": 'f2d', "group": 'f2d', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.f2d
|
||||
0x8b: {"name": 'f2i', "group": 'f2i', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.f2i
|
||||
0x8c: {"name": 'f2l', "group": 'f2l', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.f2l
|
||||
0x62: {"name": 'fadd', "group": 'fadd', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fadd
|
||||
0x30: {"name": 'faload', "group": 'faload', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.faload
|
||||
0x51: {"name": 'fastore', "group": 'fastore', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fastore
|
||||
0x96: {"name": 'fcmpg', "group": 'fcmp<op>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fcmpop
|
||||
0x95: {"name": 'fcmpl', "group": 'fcmp<op>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fcmpop
|
||||
0xb: {"name": 'fconst_0', "group": 'fconst_<f>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fconst_f
|
||||
0xc: {"name": 'fconst_1', "group": 'fconst_<f>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fconst_f
|
||||
0xd: {"name": 'fconst_2', "group": 'fconst_<f>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fconst_f
|
||||
0x6e: {"name": 'fdiv', "group": 'fdiv', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fdiv
|
||||
0x17: {"name": 'fload', "group": 'fload', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fload
|
||||
0x22: {"name": 'fload_0', "group": 'fload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fload_n
|
||||
0x23: {"name": 'fload_1', "group": 'fload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fload_n
|
||||
0x24: {"name": 'fload_2', "group": 'fload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fload_n
|
||||
0x25: {"name": 'fload_3', "group": 'fload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fload_n
|
||||
0x6a: {"name": 'fmul', "group": 'fmul', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fmul
|
||||
0x76: {"name": 'fneg', "group": 'fneg', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fneg
|
||||
0x72: {"name": 'frem', "group": 'frem', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.frem
|
||||
0xae: {"name": 'freturn', "group": 'freturn', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.freturn
|
||||
0x38: {"name": 'fstore', "group": 'fstore', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fstore
|
||||
0x43: {"name": 'fstore_0', "group": 'fstore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fstore_n
|
||||
0x44: {"name": 'fstore_1', "group": 'fstore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fstore_n
|
||||
0x45: {"name": 'fstore_2', "group": 'fstore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fstore_n
|
||||
0x46: {"name": 'fstore_3', "group": 'fstore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fstore_n
|
||||
0x66: {"name": 'fsub', "group": 'fsub', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fsub
|
||||
0xb4: {"name": 'getfield', "group": 'getfield', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.getfield
|
||||
0xb2: {"name": 'getstatic', "group": 'getstatic', "impl": 'op_getstatic'},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.getstatic
|
||||
0xa7: {"name": 'goto', "group": 'goto', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.goto
|
||||
0xc8: {"name": 'goto_w', "group": 'goto_w', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.goto_w
|
||||
0x91: {"name": 'i2b', "group": 'i2b', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.i2b
|
||||
0x92: {"name": 'i2c', "group": 'i2c', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.i2c
|
||||
0x87: {"name": 'i2d', "group": 'i2d', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.i2d
|
||||
0x86: {"name": 'i2f', "group": 'i2f', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.i2f
|
||||
0x85: {"name": 'i2l', "group": 'i2l', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.i2l
|
||||
0x93: {"name": 'i2s', "group": 'i2s', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.i2s
|
||||
0x60: {"name": 'iadd', "group": 'iadd', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iadd
|
||||
0x2e: {"name": 'iaload', "group": 'iaload', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iaload
|
||||
0x7e: {"name": 'iand', "group": 'iand', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iand
|
||||
0x4f: {"name": 'iastore', "group": 'iastore', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iastore
|
||||
0x2: {"name": 'iconst_m1', "group": 'iconst_<i>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iconst_i
|
||||
0x3: {"name": 'iconst_0', "group": 'iconst_<i>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iconst_i
|
||||
0x4: {"name": 'iconst_1', "group": 'iconst_<i>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iconst_i
|
||||
0x5: {"name": 'iconst_2', "group": 'iconst_<i>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iconst_i
|
||||
0x6: {"name": 'iconst_3', "group": 'iconst_<i>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iconst_i
|
||||
0x7: {"name": 'iconst_4', "group": 'iconst_<i>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iconst_i
|
||||
0x8: {"name": 'iconst_5', "group": 'iconst_<i>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iconst_i
|
||||
0x6c: {"name": 'idiv', "group": 'idiv', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.idiv
|
||||
0xa5: {"name": 'if_acmpeq', "group": 'if_acmp<cond>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.if_acmpcond
|
||||
0xa6: {"name": 'if_acmpne', "group": 'if_acmp<cond>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.if_acmpcond
|
||||
0x9f: {"name": 'if_icmpeq', "group": 'if_icmp<cond>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.if_icmpcond
|
||||
0xa0: {"name": 'if_icmpne', "group": 'if_icmp<cond>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.if_icmpcond
|
||||
0xa1: {"name": 'if_icmplt', "group": 'if_icmp<cond>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.if_icmpcond
|
||||
0xa2: {"name": 'if_icmpge', "group": 'if_icmp<cond>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.if_icmpcond
|
||||
0xa3: {"name": 'if_icmpgt', "group": 'if_icmp<cond>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.if_icmpcond
|
||||
0xa4: {"name": 'if_icmple', "group": 'if_icmp<cond>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.if_icmpcond
|
||||
0x99: {"name": 'ifeq', "group": 'if<cond>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ifcond
|
||||
0x9a: {"name": 'ifne', "group": 'if<cond>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ifcond
|
||||
0x9b: {"name": 'iflt', "group": 'if<cond>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ifcond
|
||||
0x9c: {"name": 'ifge', "group": 'if<cond>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ifcond
|
||||
0x9d: {"name": 'ifgt', "group": 'if<cond>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ifcond
|
||||
0x9e: {"name": 'ifle', "group": 'if<cond>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ifcond
|
||||
0xc7: {"name": 'ifnonnull', "group": 'ifnonnull', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ifnonnull
|
||||
0xc6: {"name": 'ifnull', "group": 'ifnull', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ifnull
|
||||
0x84: {"name": 'iinc', "group": 'iinc', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iinc
|
||||
0x15: {"name": 'iload', "group": 'iload', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iload
|
||||
0x1a: {"name": 'iload_0', "group": 'iload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iload_n
|
||||
0x1b: {"name": 'iload_1', "group": 'iload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iload_n
|
||||
0x1c: {"name": 'iload_2', "group": 'iload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iload_n
|
||||
0x1d: {"name": 'iload_3', "group": 'iload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iload_n
|
||||
0x68: {"name": 'imul', "group": 'imul', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.imul
|
||||
0x74: {"name": 'ineg', "group": 'ineg', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ineg
|
||||
0xc1: {"name": 'instanceof', "group": 'instanceof', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.instanceof
|
||||
0xba: {"name": 'invokedynamic', "group": 'invokedynamic', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic
|
||||
0xb9: {"name": 'invokeinterface', "group": 'invokeinterface', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokeinterface
|
||||
0xb7: {"name": 'invokespecial', "group": 'invokespecial', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokespecial
|
||||
0xb8: {"name": 'invokestatic', "group": 'invokestatic', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokestatic
|
||||
0xb6: {"name": 'invokevirtual', "group": 'invokevirtual', "impl": 'op_invokevirtual'},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokevirtual
|
||||
0x80: {"name": 'ior', "group": 'ior', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ior
|
||||
0x70: {"name": 'irem', "group": 'irem', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.irem
|
||||
0xac: {"name": 'ireturn', "group": 'ireturn', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ireturn
|
||||
0x78: {"name": 'ishl', "group": 'ishl', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ishl
|
||||
0x7a: {"name": 'ishr', "group": 'ishr', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ishr
|
||||
0x36: {"name": 'istore', "group": 'istore', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.istore
|
||||
0x3b: {"name": 'istore_0', "group": 'istore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.istore_n
|
||||
0x3c: {"name": 'istore_1', "group": 'istore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.istore_n
|
||||
0x3d: {"name": 'istore_2', "group": 'istore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.istore_n
|
||||
0x3e: {"name": 'istore_3', "group": 'istore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.istore_n
|
||||
0x64: {"name": 'isub', "group": 'isub', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.isub
|
||||
0x7c: {"name": 'iushr', "group": 'iushr', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iushr
|
||||
0x82: {"name": 'ixor', "group": 'ixor', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ixor
|
||||
0xa8: {"name": 'jsr', "group": 'jsr', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.jsr
|
||||
0xc9: {"name": 'jsr_w', "group": 'jsr_w', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.jsr_w
|
||||
0x8a: {"name": 'l2d', "group": 'l2d', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.l2d
|
||||
0x89: {"name": 'l2f', "group": 'l2f', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.l2f
|
||||
0x88: {"name": 'l2i', "group": 'l2i', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.l2i
|
||||
0x61: {"name": 'ladd', "group": 'ladd', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ladd
|
||||
0x2f: {"name": 'laload', "group": 'laload', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.laload
|
||||
0x7f: {"name": 'land', "group": 'land', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.land
|
||||
0x50: {"name": 'lastore', "group": 'lastore', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lastore
|
||||
0x94: {"name": 'lcmp', "group": 'lcmp', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lcmp
|
||||
0x9: {"name": 'lconst_0', "group": 'lconst_<l>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lconst_l
|
||||
0xa: {"name": 'lconst_1', "group": 'lconst_<l>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lconst_l
|
||||
0x12: {"name": 'ldc', "group": 'ldc', "impl": 'op_ldc'},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ldc
|
||||
0x13: {"name": 'ldc_w', "group": 'ldc_w', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ldc_w
|
||||
0x14: {"name": 'ldc2_w', "group": 'ldc2_w', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ldc2_w
|
||||
0x6d: {"name": 'ldiv', "group": 'ldiv', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ldiv
|
||||
0x16: {"name": 'lload', "group": 'lload', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lload
|
||||
0x1e: {"name": 'lload_0', "group": 'lload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lload_n
|
||||
0x1f: {"name": 'lload_1', "group": 'lload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lload_n
|
||||
0x20: {"name": 'lload_2', "group": 'lload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lload_n
|
||||
0x21: {"name": 'lload_3', "group": 'lload_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lload_n
|
||||
0x69: {"name": 'lmul', "group": 'lmul', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lmul
|
||||
0x75: {"name": 'lneg', "group": 'lneg', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lneg
|
||||
0xab: {"name": 'lookupswitch', "group": 'lookupswitch', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lookupswitch
|
||||
0x81: {"name": 'lor', "group": 'lor', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lor
|
||||
0x71: {"name": 'lrem', "group": 'lrem', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lrem
|
||||
0xad: {"name": 'lreturn', "group": 'lreturn', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lreturn
|
||||
0x79: {"name": 'lshl', "group": 'lshl', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lshl
|
||||
0x7b: {"name": 'lshr', "group": 'lshr', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lshr
|
||||
0x37: {"name": 'lstore', "group": 'lstore', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lstore
|
||||
0x3f: {"name": 'lstore_0', "group": 'lstore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lstore_n
|
||||
0x40: {"name": 'lstore_1', "group": 'lstore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lstore_n
|
||||
0x41: {"name": 'lstore_2', "group": 'lstore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lstore_n
|
||||
0x42: {"name": 'lstore_3', "group": 'lstore_<n>', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lstore_n
|
||||
0x65: {"name": 'lsub', "group": 'lsub', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lsub
|
||||
0x7d: {"name": 'lushr', "group": 'lushr', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lushr
|
||||
0x83: {"name": 'lxor', "group": 'lxor', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lxor
|
||||
0xc2: {"name": 'monitorenter', "group": 'monitorenter', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.monitorenter
|
||||
0xc3: {"name": 'monitorexit', "group": 'monitorexit', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.monitorexit
|
||||
0xc5: {"name": 'multianewarray', "group": 'multianewarray', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.multianewarray
|
||||
0xbb: {"name": 'new', "group": 'new', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.new
|
||||
0xbc: {"name": 'newarray', "group": 'newarray', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.newarray
|
||||
0x0: {"name": 'nop', "group": 'nop', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.nop
|
||||
0x57: {"name": 'pop', "group": 'pop', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.pop
|
||||
0x58: {"name": 'pop2', "group": 'pop2', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.pop2
|
||||
0xb5: {"name": 'putfield', "group": 'putfield', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.putfield
|
||||
0xb3: {"name": 'putstatic', "group": 'putstatic', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.putstatic
|
||||
0xa9: {"name": 'ret', "group": 'ret', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.ret
|
||||
0xb1: {"name": 'return', "group": 'return', "impl": 'op_return'},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.return
|
||||
0x35: {"name": 'saload', "group": 'saload', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.saload
|
||||
0x56: {"name": 'sastore', "group": 'sastore', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.sastore
|
||||
0x11: {"name": 'sipush', "group": 'sipush', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.sipush
|
||||
0x5f: {"name": 'swap', "group": 'swap', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.swap
|
||||
0xaa: {"name": 'tableswitch', "group": 'tableswitch', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.tableswitch
|
||||
0xc4: {"name": 'wide', "group": 'wide', "impl": None},
|
||||
# https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.wide
|
||||
}
|
||||
34
pjvm/unpacker.py
Normal file
34
pjvm/unpacker.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from io import BytesIO
|
||||
from struct import unpack, calcsize
|
||||
from typing import Dict, BinaryIO
|
||||
|
||||
|
||||
class Unpacker:
|
||||
@staticmethod
|
||||
def from_bytes(data: bytes):
|
||||
return Unpacker({}, BytesIO(data))
|
||||
|
||||
def __init__(self, formats: Dict[str, str], stream: BinaryIO, endianness: str = '>'):
|
||||
self.formats = formats
|
||||
self.stream = stream
|
||||
self.endianness = endianness
|
||||
|
||||
def __getattribute__(self, item):
|
||||
try:
|
||||
return super(Unpacker, self).__getattribute__(item)
|
||||
except AttributeError:
|
||||
return self.unpack(item)
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.unpack(item)
|
||||
|
||||
def unpack(self, item):
|
||||
if item in self.formats:
|
||||
fmt = self.formats.get(item)
|
||||
else:
|
||||
fmt = item
|
||||
|
||||
fmt = self.endianness + fmt
|
||||
|
||||
size = calcsize(fmt)
|
||||
return unpack(fmt, self.stream.read(size))
|
||||
12
pjvm/utils.py
Normal file
12
pjvm/utils.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from typing import List
|
||||
import re
|
||||
|
||||
|
||||
re_types = re.compile('([BCDFIJSZV]|L(?:[a-zA-Z0-9_]+/)+[a-zA-Z0-9_]+;)')
|
||||
|
||||
re_method_sig = re.compile(fr"\(({re_types.pattern}*)\){re_types.pattern}")
|
||||
|
||||
|
||||
def get_argument_count_types_descriptor(descriptor: str) -> List[str]:
|
||||
args = re_method_sig.search(descriptor).group(1)
|
||||
return re_types.findall(args)
|
||||
391
pjvm/vm.py
Normal file
391
pjvm/vm.py
Normal file
@@ -0,0 +1,391 @@
|
||||
import struct
|
||||
import sys
|
||||
from logging import getLogger
|
||||
from typing import Callable, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from pjvm.classloader import load_class
|
||||
from pjvm.clazz import Class, Code, Method
|
||||
from . import utils
|
||||
from .opcodes import INSTRUCTIONS
|
||||
from .exceptions import *
|
||||
|
||||
OPCODE_TYPE = Callable[[int, "Frame"], Tuple[int, "JObj"]]
|
||||
OPCODE_BOUND_TYPE = Callable[["PJVirtualMachine", int, "Frame"], Tuple[int, "JObj"]]
|
||||
|
||||
LOGGER = getLogger(__name__)
|
||||
|
||||
|
||||
class JObj:
|
||||
type = 'java/lang/Object'
|
||||
value = None
|
||||
|
||||
def instance_of(self, java_type: str):
|
||||
return self.type == java_type # todo inheritance
|
||||
|
||||
|
||||
class JInteger(JObj):
|
||||
type = "I"
|
||||
|
||||
def __init__(self, val: int):
|
||||
self.value = val
|
||||
|
||||
|
||||
class JCharacter(JObj):
|
||||
type = 'C'
|
||||
|
||||
def __init__(self, val: int):
|
||||
self.value = val
|
||||
|
||||
|
||||
class JGenericObj(JObj):
|
||||
generic_types: List[str]
|
||||
pass
|
||||
|
||||
|
||||
class JString(JObj):
|
||||
type = 'java/lang/String'
|
||||
value: str
|
||||
|
||||
def __init__(self, value: str):
|
||||
self.value = value
|
||||
|
||||
|
||||
class JArray(JGenericObj):
|
||||
type = 'java/lang/Array'
|
||||
generic_types: List[str]
|
||||
values: List
|
||||
|
||||
def __init__(self, generic_types: List[str], values: List):
|
||||
self.generic_types = generic_types
|
||||
self.values = values
|
||||
|
||||
|
||||
class JMockSystemOut(JObj):
|
||||
type = "Ljava/io/PrintStream;"
|
||||
value = None
|
||||
|
||||
|
||||
class JMockSystemIn(JObj):
|
||||
type = "Ljava/io/InputStream;"
|
||||
value = None
|
||||
|
||||
|
||||
def mock_printstream_println(self: JObj, args: List[JObj]):
|
||||
if len(args) != 1:
|
||||
raise ValueError("Argument length mismatch")
|
||||
if args[0].type != 'java/lang/String':
|
||||
raise ValueError("Argument type mismatch")
|
||||
|
||||
print(args[0].value)
|
||||
|
||||
|
||||
def mock_inputstream_read(self: JObj, args: List[JObj]):
|
||||
character = sys.stdin.read(1).strip()
|
||||
return JInteger(ord(character))
|
||||
|
||||
|
||||
class Frame:
|
||||
ip: int = 0
|
||||
clazz: Class
|
||||
method: Method
|
||||
code: Code
|
||||
stack: List[JObj]
|
||||
this: JObj
|
||||
local_variables: List
|
||||
|
||||
def __init__(self, clazz: Class, method: Method):
|
||||
self.clazz = clazz
|
||||
self.method = method
|
||||
self.code = self.method.code
|
||||
self.stack = []
|
||||
self.local_variables = [None for _ in range(self.code.max_locals)]
|
||||
|
||||
def get_byte(self, idx: int) -> int:
|
||||
return struct.unpack('>b', self.code.code[idx:idx + 1])[0]
|
||||
|
||||
def get_short(self, idx: int) -> int:
|
||||
return struct.unpack('>h', self.code.code[idx:idx + 2])[0]
|
||||
|
||||
def get_int(self, idx: int) -> int:
|
||||
return struct.unpack('>i', self.code.code[idx:idx + 4])[0]
|
||||
|
||||
def set_local(self, val: JObj, index: int):
|
||||
if index > self.code.max_locals:
|
||||
raise PJVMException("Local out of bound!")
|
||||
self.local_variables[index] = val
|
||||
|
||||
def get_local(self, index: int) ->JObj:
|
||||
if index > self.code.max_locals:
|
||||
raise PJVMException("Local out of bound!")
|
||||
val = self.local_variables[index]
|
||||
if val is None:
|
||||
raise PJVMException("Local not set")
|
||||
return val
|
||||
|
||||
|
||||
class Opcode:
|
||||
opcodes: Dict[int, OPCODE_TYPE] = {}
|
||||
vm: "PJVirtualMachine"
|
||||
func: OPCODE_BOUND_TYPE
|
||||
owner: object
|
||||
|
||||
@classmethod
|
||||
def register(cls, opcode: Union[int, List[int]], method: OPCODE_TYPE):
|
||||
if isinstance(opcode, int):
|
||||
cls.opcodes[opcode] = method
|
||||
else:
|
||||
for oc in opcode:
|
||||
cls.opcodes[oc] = method
|
||||
|
||||
@classmethod
|
||||
def get_opcodes(cls, vm: "PJVirtualMachine") -> Dict[int, OPCODE_TYPE]:
|
||||
cls.vm = vm
|
||||
return cls.opcodes
|
||||
|
||||
def __init__(self, opcode: int, size: int = 0, no_ret: bool = False):
|
||||
self.opcode = opcode
|
||||
self.size = size
|
||||
self.no_ret = no_ret
|
||||
|
||||
def wrapped(self, opcode: int, frame: Frame):
|
||||
retval = self.func(self.vm, opcode, frame)
|
||||
frame.ip += self.size
|
||||
if self.no_ret and retval is None:
|
||||
retval = 0, None
|
||||
return retval
|
||||
|
||||
def __call__(self, func: OPCODE_BOUND_TYPE):
|
||||
self.func = func
|
||||
self.register(self.opcode, self.wrapped)
|
||||
|
||||
|
||||
@Opcode(0xb2, size=3, no_ret=True)
|
||||
def op_getstatic(vm: "PJVirtualMachine", opcode: int, frame: Frame):
|
||||
"""
|
||||
Get static value from field
|
||||
"""
|
||||
index = frame.get_short(frame.ip + 1)
|
||||
owner, field_name, field_descriptor = frame.clazz.cp_get_fieldref(index)
|
||||
LOGGER.info(f"Fetching {owner} -> {field_name} of type {field_descriptor}")
|
||||
|
||||
field = vm.get_static_field(owner, field_name)
|
||||
if not field.instance_of(field_descriptor):
|
||||
raise PJVMTypeError(f"Type mismatch {field.type} != {field_descriptor}")
|
||||
|
||||
frame.stack.append(field)
|
||||
|
||||
|
||||
@Opcode(0x12, size=2, no_ret=True)
|
||||
def op_ldc(vm: "PJVirtualMachine", opcode: int, frame: Frame):
|
||||
"""
|
||||
Get constant from constant pool
|
||||
"""
|
||||
index = frame.get_byte(frame.ip + 1)
|
||||
|
||||
value = frame.clazz.cp_get(index)
|
||||
resulting_value: JObj
|
||||
|
||||
if value['type'] == 'String':
|
||||
resulting_value = JString(frame.clazz.cp_get_utf8(value['string_index']))
|
||||
else:
|
||||
raise PJVMNotImplemented("did not feel like implementing the other stuff")
|
||||
|
||||
frame.stack.append(resulting_value)
|
||||
|
||||
|
||||
@Opcode(0xb6, size=3, no_ret=True)
|
||||
def op_invokevirtual(vm: "PJVirtualMachine", opcode: int, frame: Frame):
|
||||
index = frame.get_short(frame.ip + 1)
|
||||
result: JObj
|
||||
|
||||
class_name, method_name, descriptor = frame.clazz.cp_get_methodref(index)
|
||||
LOGGER.info(f"Calling {class_name} -> {method_name} {descriptor}")
|
||||
args_types = utils.get_argument_count_types_descriptor(descriptor)
|
||||
# todo type check
|
||||
args = []
|
||||
for _ in args_types:
|
||||
args.append(frame.stack.pop())
|
||||
object_ref = frame.stack.pop()
|
||||
|
||||
result = vm.execute_instance_method(class_name, method_name, args, object_ref)
|
||||
|
||||
if descriptor[-1] != 'V':
|
||||
frame.stack.append(result)
|
||||
|
||||
|
||||
@Opcode(0xb1, size=1)
|
||||
def op_return(vm: "PJVirtualMachine", opcode: int, frame: Frame) -> Tuple[int, Optional[JObj]]:
|
||||
return 1, None
|
||||
|
||||
|
||||
@Opcode(0x92, size=1, no_ret=True)
|
||||
def op_i2c(vm: "PJVirtualMachine", opcode: int, frame: Frame):
|
||||
top = frame.stack.pop()
|
||||
if not top.instance_of('I'):
|
||||
raise ValueError("Type mismatch!")
|
||||
|
||||
char = top.value & 0xFFFF
|
||||
frame.stack.append(JInteger(char))
|
||||
|
||||
|
||||
@Opcode([0x03b, 0x3c, 0x3d, 0x3e], size=1, no_ret=True)
|
||||
def op_istore_n(vm: "PJVirtualMachine", opcode: int, frame: Frame):
|
||||
idx = (opcode + 1) & 0b11
|
||||
item = frame.stack.pop()
|
||||
if not item.instance_of('I'):
|
||||
raise PJVMTypeError("Must be integer")
|
||||
|
||||
frame.set_local(item, idx)
|
||||
|
||||
|
||||
@Opcode([0x1a, 0x1b, 0x1c, 0x1d], size=1, no_ret=True)
|
||||
def op_iload_n(vm: "PJVirtualMachine", opcode: int, frame: Frame):
|
||||
idx = (opcode + 2) & 0b11
|
||||
item = frame.get_local(idx)
|
||||
if not item.instance_of('I'):
|
||||
raise PJVMTypeError("Must be integer")
|
||||
frame.stack.append(item)
|
||||
|
||||
|
||||
@Opcode(0x10, size=2, no_ret=True)
|
||||
def op_bipush(vm: "PJVirtualMachine", opcode: int, frame: Frame):
|
||||
val = frame.get_byte(frame.ip+1)
|
||||
frame.stack.append(JInteger(val))
|
||||
|
||||
|
||||
@Opcode([0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4], no_ret=True)
|
||||
def op_icmp_cond(vm: "PJVirtualMachine", opcode: int, frame: Frame):
|
||||
opcode_map = {
|
||||
0x9f: int.__eq__,
|
||||
0xa0: int.__ne__,
|
||||
0xa1: int.__lt__,
|
||||
0xa2: int.__ge__,
|
||||
0xa3: int.__gt__,
|
||||
0xa4: int.__le__,
|
||||
}
|
||||
left = frame.stack.pop()
|
||||
right = frame.stack.pop()
|
||||
if not left.instance_of("I") or not right.instance_of("I"):
|
||||
raise PJVMTypeError("Must be of type integer")
|
||||
if opcode_map[opcode](left.value, right.value):
|
||||
target = frame.get_short(frame.ip + 1)
|
||||
frame.ip += target
|
||||
else:
|
||||
frame.ip += 3
|
||||
|
||||
|
||||
@Opcode(0xa7, no_ret=True)
|
||||
def op_goto(vm: "PJVirtualMachine", opcode: int, frame: Frame):
|
||||
target = frame.get_short(frame.ip + 1)
|
||||
frame.ip += target
|
||||
|
||||
|
||||
class PJVirtualMachine:
|
||||
instructions: Dict[int, OPCODE_TYPE]
|
||||
|
||||
stack: List[Frame] = []
|
||||
|
||||
classes: Dict[str, Class] = {}
|
||||
|
||||
mock_static_objects = {
|
||||
'java/lang/System': {
|
||||
'out': JMockSystemOut(),
|
||||
'in': JMockSystemIn()
|
||||
}
|
||||
}
|
||||
|
||||
mock_instance_methods = {
|
||||
'java/io/PrintStream': {
|
||||
'println': mock_printstream_println
|
||||
},
|
||||
'java/io/InputStream': {
|
||||
'read': mock_inputstream_read
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, classpath: []):
|
||||
self.classpath = classpath
|
||||
self.instructions = Opcode.get_opcodes(self)
|
||||
self.load_classes()
|
||||
|
||||
def load_classes(self):
|
||||
LOGGER.info(f"Loading {len(self.classpath)} classes")
|
||||
for class_file in self.classpath:
|
||||
LOGGER.info(f"Loading {class_file}")
|
||||
loader = load_class(class_file)
|
||||
clazz = Class(loader)
|
||||
self.classes[clazz.this_class] = clazz
|
||||
|
||||
LOGGER.info("Done loading")
|
||||
|
||||
@staticmethod
|
||||
def not_implemented_instruction(opcode: int, frame: Frame):
|
||||
LOGGER.error(f"OPCODE {opcode} -> {INSTRUCTIONS[opcode]['name']} is not yet implemented!")
|
||||
raise PJVMUnknownOpcode(opcode)
|
||||
|
||||
def run(self, main_class: str, args: List[str] = None):
|
||||
if args is None:
|
||||
args = []
|
||||
|
||||
j_args: JArray = JArray(['java/lang/String'], list(map(JString, args)))
|
||||
|
||||
if main_class not in self.classes:
|
||||
raise PJVMException("main_class not found!")
|
||||
|
||||
self.execute_static_method(main_class, 'Main', [j_args])
|
||||
|
||||
def execute_static_method(self, class_name: str, method_name: str, args: List[JObj]):
|
||||
clazz = self.classes[class_name]
|
||||
method = clazz.methods[method_name]
|
||||
self.execute(clazz, method, args, None)
|
||||
|
||||
def execute_instance_method(self, class_name: str, method_name: str, args: List[JObj], this: JObj) -> Optional[
|
||||
JObj]:
|
||||
mock_class = self.mock_instance_methods.get(class_name)
|
||||
if mock_class:
|
||||
mock_method = mock_class.get(method_name)
|
||||
if mock_method:
|
||||
return mock_method(this, args)
|
||||
|
||||
raise PJVMNotImplemented(f"Instance methods are not yet implemented {class_name}->{method_name}")
|
||||
|
||||
def execute(self, clazz: Class, method: Method, args: List[JObj], this: Optional[JObj]):
|
||||
frame = Frame(clazz, method)
|
||||
frame.stack.extend(args)
|
||||
frame.this = this
|
||||
return_value = None
|
||||
|
||||
self.stack.append(frame)
|
||||
|
||||
while True:
|
||||
instruction = frame.code.code[frame.ip]
|
||||
try:
|
||||
impl = self.instructions[instruction]
|
||||
except KeyError:
|
||||
self.not_implemented_instruction(instruction, frame)
|
||||
break # unreachable
|
||||
|
||||
result, meta = impl(instruction, frame)
|
||||
if result == 0:
|
||||
continue
|
||||
|
||||
if result == 1:
|
||||
return_value = meta
|
||||
break
|
||||
if result == 2:
|
||||
raise PJVMNotImplemented("Exceptions are not implemented")
|
||||
|
||||
self.stack.pop()
|
||||
|
||||
if frame.method.return_value != 'V':
|
||||
# add the return value to the stack of the caller if it is not a void call
|
||||
self.stack[-1].stack.append(return_value)
|
||||
|
||||
def get_static_field(self, class_name: str, field_name: str):
|
||||
mock_type = self.mock_static_objects.get(class_name, None)
|
||||
if mock_type:
|
||||
mock_field = mock_type.get(field_name)
|
||||
if mock_field:
|
||||
return mock_field
|
||||
|
||||
raise PJVMNotImplemented(f"Static fields are not implemented yet, only as mock {class_name}->{field_name}")
|
||||
Reference in New Issue
Block a user