Files
PJVM/pjvm/vm.py

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