feat: restructured code
This commit is contained in:
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from .opcodes import INSTRUCTIONS
|
from .opcodes_names import INSTRUCTIONS
|
||||||
|
|
||||||
|
|
||||||
class PJVMException(ValueError):
|
class PJVMException(ValueError):
|
||||||
|
|||||||
19
pjvm/mockimpl/__init__.py
Normal file
19
pjvm/mockimpl/__init__.py
Normal file
@@ -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']
|
||||||
31
pjvm/mockimpl/system.py
Normal file
31
pjvm/mockimpl/system.py
Normal file
@@ -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))
|
||||||
46
pjvm/old_jtypes.py
Normal file
46
pjvm/old_jtypes.py
Normal file
@@ -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
|
||||||
45
pjvm/stackframe.py
Normal file
45
pjvm/stackframe.py
Normal file
@@ -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
|
||||||
135
pjvm/vm.py
135
pjvm/vm.py
@@ -1,13 +1,14 @@
|
|||||||
import struct
|
|
||||||
import sys
|
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from typing import Callable, Dict, List, Optional, Tuple, Union
|
from typing import Callable, Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from pjvm.classloader import load_class
|
from pjvm.classloader import load_class
|
||||||
from pjvm.clazz import Class, Code, Method
|
from pjvm.clazz import Class, Code, Method
|
||||||
from . import utils
|
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 .exceptions import *
|
||||||
|
from .stackframe import Frame
|
||||||
|
|
||||||
OPCODE_TYPE = Callable[[int, "Frame"], Tuple[int, "JObj"]]
|
OPCODE_TYPE = Callable[[int, "Frame"], Tuple[int, "JObj"]]
|
||||||
OPCODE_BOUND_TYPE = Callable[["PJVirtualMachine", 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__)
|
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:
|
class Opcode:
|
||||||
opcodes: Dict[int, OPCODE_TYPE] = {}
|
opcodes: Dict[int, OPCODE_TYPE] = {}
|
||||||
vm: "PJVirtualMachine"
|
vm: "PJVirtualMachine"
|
||||||
@@ -287,22 +180,6 @@ class PJVirtualMachine:
|
|||||||
|
|
||||||
classes: Dict[str, Class] = {}
|
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: []):
|
def __init__(self, classpath: []):
|
||||||
self.classpath = classpath
|
self.classpath = classpath
|
||||||
self.instructions = Opcode.get_opcodes(self)
|
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[
|
def execute_instance_method(self, class_name: str, method_name: str, args: List[JObj], this: JObj) -> Optional[
|
||||||
JObj]:
|
JObj]:
|
||||||
mock_class = self.mock_instance_methods.get(class_name)
|
mock_class = mock_instance_methods.get(class_name)
|
||||||
if mock_class:
|
if mock_class:
|
||||||
mock_method = mock_class.get(method_name)
|
mock_method = mock_class.get(method_name)
|
||||||
if mock_method:
|
if mock_method:
|
||||||
@@ -382,7 +259,7 @@ class PJVirtualMachine:
|
|||||||
self.stack[-1].stack.append(return_value)
|
self.stack[-1].stack.append(return_value)
|
||||||
|
|
||||||
def get_static_field(self, class_name: str, field_name: str):
|
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:
|
if mock_type:
|
||||||
mock_field = mock_type.get(field_name)
|
mock_field = mock_type.get(field_name)
|
||||||
if mock_field:
|
if mock_field:
|
||||||
|
|||||||
Reference in New Issue
Block a user