109 lines
3.6 KiB
Python
109 lines
3.6 KiB
Python
from logging import getLogger
|
|
from typing import Dict, List, Optional
|
|
|
|
from pjvm.classloader import load_class
|
|
from pjvm.clazz import Class, Method
|
|
from .exceptions import *
|
|
from .mockimpl import mock_instance_methods, mock_static_objects
|
|
from .old_jtypes import JArray, JObj, JString
|
|
from .opcodes import OPCODE_TYPE, Opcode
|
|
from .opcode_names import INSTRUCTIONS
|
|
from .stackframe import Frame
|
|
|
|
LOGGER = getLogger(__name__)
|
|
|
|
|
|
class PJVirtualMachine:
|
|
instructions: Dict[int, OPCODE_TYPE]
|
|
|
|
stack: List[Frame] = []
|
|
|
|
classes: Dict[str, Class] = {}
|
|
|
|
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 = 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 = 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}")
|