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}")