diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pjvm/exceptions.py b/pjvm/exceptions.py index a871c44..183540a 100644 --- a/pjvm/exceptions.py +++ b/pjvm/exceptions.py @@ -1,4 +1,4 @@ -from .opcodes import INSTRUCTIONS +from .opcodes_names import INSTRUCTIONS class PJVMException(ValueError): diff --git a/pjvm/mockimpl/__init__.py b/pjvm/mockimpl/__init__.py new file mode 100644 index 0000000..2c564cb --- /dev/null +++ b/pjvm/mockimpl/__init__.py @@ -0,0 +1,19 @@ +from .system import JMockSystemIn, JMockSystemOut, mock_inputstream_read, mock_printstream_println + +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 + } +} + +__all__ = ['mock_instance_methods', 'mock_static_objects'] diff --git a/pjvm/mockimpl/system.py b/pjvm/mockimpl/system.py new file mode 100644 index 0000000..5edce66 --- /dev/null +++ b/pjvm/mockimpl/system.py @@ -0,0 +1,31 @@ +""" +In here the System module is partially implemented +""" +import sys +from typing import List + +from ..old_jtypes import JObj, JInteger + + +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)) diff --git a/pjvm/old_jtypes.py b/pjvm/old_jtypes.py new file mode 100644 index 0000000..d51ef88 --- /dev/null +++ b/pjvm/old_jtypes.py @@ -0,0 +1,46 @@ +from typing import List + + +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 diff --git a/pjvm/opcodes.py b/pjvm/opcodes_names.py similarity index 100% rename from pjvm/opcodes.py rename to pjvm/opcodes_names.py diff --git a/pjvm/stackframe.py b/pjvm/stackframe.py new file mode 100644 index 0000000..140f1b5 --- /dev/null +++ b/pjvm/stackframe.py @@ -0,0 +1,45 @@ +import struct +from typing import List + +from .clazz import Class, Method, Code +from .exceptions import PJVMException +from .old_jtypes import JObj + + +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 diff --git a/pjvm/vm.py b/pjvm/vm.py index bf64992..8c77025 100644 --- a/pjvm/vm.py +++ b/pjvm/vm.py @@ -1,13 +1,14 @@ -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 .mockimpl import mock_instance_methods, mock_static_objects +from .old_jtypes import JObj, JInteger, JString, JArray +from .opcodes_names import INSTRUCTIONS from .exceptions import * +from .stackframe import Frame OPCODE_TYPE = Callable[[int, "Frame"], Tuple[int, "JObj"]] OPCODE_BOUND_TYPE = Callable[["PJVirtualMachine", int, "Frame"], Tuple[int, "JObj"]] @@ -15,114 +16,6 @@ OPCODE_BOUND_TYPE = Callable[["PJVirtualMachine", int, "Frame"], Tuple[int, "JOb 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" @@ -287,22 +180,6 @@ class PJVirtualMachine: 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) @@ -341,7 +218,7 @@ class PJVirtualMachine: 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) + mock_class = mock_instance_methods.get(class_name) if mock_class: mock_method = mock_class.get(method_name) if mock_method: @@ -382,7 +259,7 @@ class PJVirtualMachine: 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) + mock_type = mock_static_objects.get(class_name, None) if mock_type: mock_field = mock_type.get(field_name) if mock_field: