From 1701c3c58ce9ffeda5260b99eb7d9907fd2743e2 Mon Sep 17 00:00:00 2001 From: Sebastiaan Fernandez Date: Thu, 25 Jan 2024 19:17:50 +0100 Subject: [PATCH 1/2] Implement instanceof instruction for classes --- src/VM/Instructions/ReferenceInstructions.cpp | 40 +++++++++++++++++++ src/VM/Instructions/ReferenceInstructions.h | 1 + src/VM/JavaHeap.cpp | 9 ++--- src/VM/VM.h | 3 +- src/VM/VMThread.cpp | 6 +++ test/e2e/CMakeLists.txt | 5 ++- test/e2e/InstanceofTest.java | 21 ++++++++++ 7 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 test/e2e/InstanceofTest.java diff --git a/src/VM/Instructions/ReferenceInstructions.cpp b/src/VM/Instructions/ReferenceInstructions.cpp index b82a8a1..1ff2f76 100644 --- a/src/VM/Instructions/ReferenceInstructions.cpp +++ b/src/VM/Instructions/ReferenceInstructions.cpp @@ -420,6 +420,46 @@ void checkCast(INSTRUCTION_ARGS) { // TODO: Implement actual type check } +void instanceof(INSTRUCTION_ARGS) +{ + const u2 index = combineBytes(args[0], args[1]); + const CPClassInfo* cpClassInfo = thread->m_currentClass->constantPool->getClassInfo(index); + const std::string_view name = thread->m_currentClass->constantPool->getString(cpClassInfo->nameIndex); + + const Variable operand = thread->m_currentFrame->popOperand(); + VM->checkType(operand, VariableType_REFERENCE, thread); + + i4 returnVal = 0; + + if (operand.data == 0) + { + thread->m_currentFrame->pushInt(returnVal); + return; + } + + const Object* object = heap->getObject(operand.data); + std::string_view objectClassName = object->classInfo->getName(); + // TODO: Implement interface and array class checking + do + { + if (name == objectClassName) + { + returnVal = 1; + break; + } + if (object->superClassObject == 0) + { + break; + } + object = heap->getObject(object->superClassObject); + if (object != nullptr) + { + objectClassName = object->classInfo->getName(); + } + } while(object != nullptr); + thread->m_currentFrame->pushInt(returnVal); +} + void monitorenter(INSTRUCTION_ARGS) { // TODO: Implement when real threading is implemented diff --git a/src/VM/Instructions/ReferenceInstructions.h b/src/VM/Instructions/ReferenceInstructions.h index 9df85de..0697956 100644 --- a/src/VM/Instructions/ReferenceInstructions.h +++ b/src/VM/Instructions/ReferenceInstructions.h @@ -19,5 +19,6 @@ void newarray(INSTRUCTION_ARGS); void anewarray(INSTRUCTION_ARGS); void arraylength(INSTRUCTION_ARGS); void checkCast(INSTRUCTION_ARGS); +void instanceof(INSTRUCTION_ARGS); void monitorenter(INSTRUCTION_ARGS); void monitorexit(INSTRUCTION_ARGS); \ No newline at end of file diff --git a/src/VM/JavaHeap.cpp b/src/VM/JavaHeap.cpp index de06aec..c050780 100644 --- a/src/VM/JavaHeap.cpp +++ b/src/VM/JavaHeap.cpp @@ -104,12 +104,9 @@ u4 JavaHeap::createObject(ClassInfo* classInfo, VM* VM) if (classInfo->superClass != 0) { const char* superClassName = classInfo->constantPool->getString(classInfo->constantPool->getClassInfo(classInfo->superClass)->nameIndex); - if (strcmp(superClassName, "java/lang/Object") != 0) - { - ClassInfo* superClass = getClassByName(superClassName); - u4 superClassObject = createObject(superClass, VM); - object->superClassObject = superClassObject; - } + ClassInfo* superClass = getClassByName(superClassName); + const u4 superClassObject = createObject(superClass, VM); + object->superClassObject = superClassObject; } objects.push_back(object); diff --git a/src/VM/VM.h b/src/VM/VM.h index ad79c47..17cc7c6 100644 --- a/src/VM/VM.h +++ b/src/VM/VM.h @@ -48,7 +48,7 @@ class VM { void executeLoop(VMThread* thread); static void checkType(Variable var, VariableType type, VMThread *thread); private: - inline static constexpr std::array m_instructions{{ + inline static constexpr std::array m_instructions{{ // Constants {i_nop, 0, "nop", 0, nop}, {i_aconst_null, 0, "aconst_null", 0, aconst_null}, @@ -152,6 +152,7 @@ class VM { {i_anewarray, 0, "anewarray", 0, anewarray}, {i_arraylength, 0, "arraylength", 0, arraylength}, {i_checkcast, 2, "checkcast", 0, checkCast}, + {i_instanceof, 2, "instanceof", 0, instanceof}, {i_monitorenter, 0, "monitorenter", 0, monitorenter}, {i_monitorexit, 0, "monitorexit", 0, monitorexit}, // Control diff --git a/src/VM/VMThread.cpp b/src/VM/VMThread.cpp index 8967e61..56dc824 100644 --- a/src/VM/VMThread.cpp +++ b/src/VM/VMThread.cpp @@ -24,6 +24,12 @@ static constexpr std::string_view NoNonNativeStackFrameFound{"Can't return to pr void VMThread::pushStackFrameWithoutParams(ClassInfo* classInfo, const MethodInfo* methodInfo) { StackFrame stackFrame; + + if (methodInfo->code == nullptr) + { + internalError(std::string_view("No code found in method")); + } + stackFrame.localVariables.reserve(methodInfo->code->maxLocals); for (u2 currentLocal = 0; currentLocal < methodInfo->code->maxLocals; ++currentLocal) { diff --git a/test/e2e/CMakeLists.txt b/test/e2e/CMakeLists.txt index f8ea120..2e59b73 100644 --- a/test/e2e/CMakeLists.txt +++ b/test/e2e/CMakeLists.txt @@ -9,13 +9,16 @@ set(CMAKE_JAVA_COMPILE_FLAGS "-source" "1.8" "-target" "1.8") add_jar(e2e SmokeTest.java PrivilegedTest.java + InstanceofTest.java ) get_target_property(_classDir e2e CLASSDIR) add_test(NAME SmokeTest COMMAND VigurVM "-classpath" "${_classDir}" "SmokeTest" WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) add_test(NAME PrivilegedTest COMMAND VigurVM "-classpath" "${_classDir}" "PrivilegedTest" WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +add_test(NAME InstanceofTest COMMAND VigurVM "-classpath" "${_classDir}" "InstanceofTest" WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set (failRegex "^Error:") set_property(TEST SmokeTest PROPERTY FAIL_REGULAR_EXPRESSION "${failRegex}") -set_property(TEST PrivilegedTest PROPERTY FAIL_REGULAR_EXPRESSION "${failRegex}") \ No newline at end of file +set_property(TEST PrivilegedTest PROPERTY FAIL_REGULAR_EXPRESSION "${failRegex}") +set_property(TEST InstanceofTest PROPERTY FAIL_REGULAR_EXPRESSION "${failRegex}") \ No newline at end of file diff --git a/test/e2e/InstanceofTest.java b/test/e2e/InstanceofTest.java new file mode 100644 index 0000000..a791f3e --- /dev/null +++ b/test/e2e/InstanceofTest.java @@ -0,0 +1,21 @@ + +public class InstanceofTest { + public static void main(String[] args) { + String brol = ""; + Object o = new Object(); + Object oNull = null; +// char[] charArr = new char[2]; + if (!(brol instanceof String)) + throw new RuntimeException(); + if (!(brol instanceof Object)) + throw new RuntimeException(); + if (o instanceof String) + throw new RuntimeException(); + if (oNull instanceof String) + throw new RuntimeException(); +// if (!(brol instanceof Serializable)) +// throw new RuntimeException(); +// if (!(charArr instanceof char[])) +// throw new RuntimeException(); + } +} \ No newline at end of file From c741bd77e15145f8c8b693b1ea2f22a6f15bc323 Mon Sep 17 00:00:00 2001 From: Sebastiaan Fernandez Date: Sat, 27 Jan 2024 17:02:18 +0100 Subject: [PATCH 2/2] Improve some heap code and improve instanceof handling of different cases --- src/Data/Class.h | 14 ++++- src/Library/java/lang/Class.cpp | 4 +- .../java/security/AccessController.cpp | 2 +- src/VM/Instructions/ReferenceInstructions.cpp | 63 +++++++++++++------ src/VM/JavaHeap.cpp | 50 +++++++-------- src/VM/JavaHeap.h | 29 ++++----- test/e2e/InstanceofTest.java | 1 + 7 files changed, 94 insertions(+), 69 deletions(-) diff --git a/src/Data/Class.h b/src/Data/Class.h index c5df728..d33007f 100644 --- a/src/Data/Class.h +++ b/src/Data/Class.h @@ -7,6 +7,7 @@ #include "Data/Variable.h" #include +#include class FieldInfo { public: @@ -83,7 +84,18 @@ class ClassInfo { uint16_t staticFieldsCount; // Total amount of static fields public: [[nodiscard]] bool isPublic() const { - return ((accessFlags & ACC_PUBLIC) != 0); + return (accessFlags & ACC_PUBLIC) != 0; + } + + [[nodiscard]] bool isInterface() const + { + return (accessFlags & ACC_INTERFACE) != 0; + } + + [[nodiscard]] bool isArrayType() const + { + const std::string_view name = getName(); + return name.starts_with('['); } [[nodiscard]] MethodInfo* findMethodWithNameAndDescriptor(const char* name, const char* descriptor) const diff --git a/src/Library/java/lang/Class.cpp b/src/Library/java/lang/Class.cpp index f223c82..a2be590 100644 --- a/src/Library/java/lang/Class.cpp +++ b/src/Library/java/lang/Class.cpp @@ -26,9 +26,9 @@ JCALL void lib_java_lang_Class_getPrimitiveClass(NATIVE_ARGS) const StackFrame* currentFrame = thread->m_currentFrame; const Variable var = currentFrame->localVariables[0]; VM->checkType(var, VariableType_REFERENCE, thread); - Object* strObject = heap->getObject(var.data); + const Object* strObject = heap->getObject(var.data); u4 arrayRefId = strObject->fields[0].data->data; - Array* array = heap->getArray(arrayRefId); + const Array* array = heap->getArray(arrayRefId); auto typeString = std::string_view{reinterpret_cast(array->data), array->length}; u4 classRef = 0; if (typeString == "float") diff --git a/src/Library/java/security/AccessController.cpp b/src/Library/java/security/AccessController.cpp index 662bd2d..360fe0f 100644 --- a/src/Library/java/security/AccessController.cpp +++ b/src/Library/java/security/AccessController.cpp @@ -19,7 +19,7 @@ void lib_java_security_AccessController_doPriviliged(NATIVE_ARGS) { StackFrame* currentFrame = thread->m_currentFrame; Variable objectVar = currentFrame->localVariables[0]; - Object* method = heap->getObject(currentFrame->localVariables[0].data); + const Object* method = heap->getObject(currentFrame->localVariables[0].data); MethodInfo* methodInfo = method->classInfo->findMethodWithNameAndDescriptor("run", "()Ljava/lang/Object;"); ClassInfo* classInfo = method->classInfo; diff --git a/src/VM/Instructions/ReferenceInstructions.cpp b/src/VM/Instructions/ReferenceInstructions.cpp index 1ff2f76..9392ef7 100644 --- a/src/VM/Instructions/ReferenceInstructions.cpp +++ b/src/VM/Instructions/ReferenceInstructions.cpp @@ -109,7 +109,7 @@ void getfield(INSTRUCTION_ARGS) } else { - Object* object = heap->getChildObject(referencePointer.data, targetClass); + const Object* object = heap->getChildObject(referencePointer.data, targetClass); FieldData* field = object->getField(fieldName, fieldDescr, heap); if (field == 0) { @@ -143,7 +143,7 @@ void putfield(INSTRUCTION_ARGS) } Variable referencePointer = thread->m_currentFrame->popOperand(); - Object* targetObject = heap->getChildObject(referencePointer.data, targetClass); + const Object* targetObject = heap->getChildObject(referencePointer.data, targetClass); FieldData* fieldData = targetObject->getField(fieldName, fieldDescr, heap); @@ -185,7 +185,7 @@ static void invokeVirtual(ClassInfo* classInfo, MethodInfo* methodInfo, VMThread } // Look for method based on the object - Object* object = heap->getObject(ref.data); + const Object* object = heap->getObject(ref.data); targetClass = object->classInfo; MethodInfo* foundMethod = targetClass->findMethodWithNameAndDescriptor(methodInfo->name, classInfo->constantPool->getString(methodInfo->descriptorIndex)); @@ -437,27 +437,50 @@ void instanceof(INSTRUCTION_ARGS) return; } - const Object* object = heap->getObject(operand.data); - std::string_view objectClassName = object->classInfo->getName(); - // TODO: Implement interface and array class checking - do + const Reference* reference = heap->getReference(operand.data); + + if (reference->type == OBJECT || reference->type == CLASSOBJECT) { - if (name == objectClassName) - { - returnVal = 1; - break; - } - if (object->superClassObject == 0) + const Object* object = reference->getObject(); + const ClassInfo* classInfo = object->classInfo; + if (classInfo->isInterface()) { - break; - } - object = heap->getObject(object->superClassObject); - if (object != nullptr) + thread->internalError("Instanceof not implemented yet for interfaces"); + } else { - objectClassName = object->classInfo->getName(); + std::string_view objectClassName = classInfo->getName(); + + const ClassInfo* targetClassInfo = VM->getClass(name.data(), thread); + if (targetClassInfo->isInterface()) + { + thread->internalError("Instanceof not implemented yet for interface implementation checking"); + } else + // Check if it is a subclass + { + do + { + if (name == objectClassName) + { + returnVal = 1; + break; + } + if (object->superClassObject == 0) + { + break; + } + object = heap->getObject(object->superClassObject); + if (object != nullptr) + { + objectClassName = object->classInfo->getName(); + } + } while(object != nullptr); + thread->m_currentFrame->pushInt(returnVal); + } } - } while(object != nullptr); - thread->m_currentFrame->pushInt(returnVal); + } else + { + thread->internalError("Instanceof not implemented yet for arrays"); + } } void monitorenter(INSTRUCTION_ARGS) diff --git a/src/VM/JavaHeap.cpp b/src/VM/JavaHeap.cpp index c050780..18015f2 100644 --- a/src/VM/JavaHeap.cpp +++ b/src/VM/JavaHeap.cpp @@ -204,7 +204,7 @@ u4 JavaHeap::createString(const char* utf8String, VM* VM) { return strObjectId; } -Object* JavaHeap::getObject(const uint32_t id) const +const Object* JavaHeap::getObject(const uint32_t id) const { if (id == 0) { @@ -212,16 +212,18 @@ Object* JavaHeap::getObject(const uint32_t id) const fprintf(stderr, "Error: Null pointer exception!\n"); Platform::exitProgram(1); } - Reference* ref = objects[id]; - if (ref->type == OBJECT || ref->type == CLASSOBJECT) - { - return static_cast(objects[id]); - } else + return objects[id]->getObject(); +} + +Reference* JavaHeap::getReference(u4 id) const +{ + if (id == 0) { - fprintf(stderr, "Error: Array instead of Object\n"); - Platform::exitProgram(22); + // Nullpointer + fprintf(stderr, "Error: Null pointer exception!\n"); + Platform::exitProgram(1); } - return nullptr; + return objects[id]; } ClassObject* JavaHeap::getClassObject(uint32_t id) const @@ -244,24 +246,23 @@ ClassObject* JavaHeap::getClassObject(uint32_t id) const return nullptr; } -Object* JavaHeap::getChildObject(uint32_t id, ClassInfo* classInfo) +const Object* JavaHeap::getChildObject(const uint32_t id, ClassInfo* classInfo) { - Object* o = getObject(id); - if (o == 0 || strcmp(o->classInfo->getName(), classInfo->getName()) != 0) + const Object* o = getObject(id); + if (o == nullptr || strcmp(o->classInfo->getName(), classInfo->getName()) != 0) { - if (o->superClassObject != 0) + if (o != nullptr && o->superClassObject != 0) { return getChildObject(o->superClassObject, classInfo); - } else - { - fprintf(stderr, "Error: Object not found at reference with class: %s\n", classInfo->getName()); - Platform::exitProgram(22); } + + fprintf(stderr, "Error: Object not found at reference with class: %s\n", classInfo->getName()); + Platform::exitProgram(22); } return o; } -Array* JavaHeap::getArray(const uint32_t id) const +const Array* JavaHeap::getArray(const uint32_t id) const { if (id == 0) { @@ -269,16 +270,7 @@ Array* JavaHeap::getArray(const uint32_t id) const fprintf(stderr, "Error: Null pointer exception!"); Platform::exitProgram(1); } - Reference* ref = objects[id]; - if (ref->type == ARRAY) - { - return static_cast(objects[id]); - } else - { - fprintf(stderr, "Error: Array instead of Object"); - Platform::exitProgram(22); - } - return nullptr; + return objects[id]->getArray(); } u4 JavaHeap::getString(const char* utf8String) const @@ -290,7 +282,7 @@ u4 JavaHeap::getString(const char* utf8String) const const Object* obj = static_cast(ref); if (strcmp(obj->classInfo->getName(), "java/lang/String") == 0) { Variable charArrRef = obj->fields[0].data[0]; - Array* arr = getArray(charArrRef.data); + const Array* arr = getArray(charArrRef.data); if (strncmp((const char*)arr->data, utf8String, (arr->length > strlen(utf8String) ? arr->length : strlen(utf8String))) == 0) { return currentObj; diff --git a/src/VM/JavaHeap.h b/src/VM/JavaHeap.h index 768a1fc..4ac01b1 100644 --- a/src/VM/JavaHeap.h +++ b/src/VM/JavaHeap.h @@ -24,25 +24,21 @@ class Reference { public: ReferenceType type; - Array* getArray() { + [[nodiscard]] const Array* getArray() const { if (type == ARRAY) { - return reinterpret_cast(this); - } - else { - fprintf(stderr, "Error: Reference is not of type 'Array'"); - Platform::exitProgram(23); + return reinterpret_cast(this); } + fprintf(stderr, "Error: Reference is not of type 'Array'"); + Platform::exitProgram(23); return nullptr; }; - Object* getObject() { - if (type == OBJECT) { - return reinterpret_cast(this); - } - else { - fprintf(stderr, "Error: Reference is not of type 'Object'"); - Platform::exitProgram(23); + [[nodiscard]] const Object* getObject() const { + if (type == OBJECT || type == CLASSOBJECT) { + return reinterpret_cast(this); } + fprintf(stderr, "Error: Reference is not of type 'Object'"); + Platform::exitProgram(23); return nullptr; }; }; @@ -100,10 +96,11 @@ class JavaHeap { u4 createClassObject(ClassInfo* classInfo, VM* VM, std::string_view name); u4 createString(const char* utf8String, VM* VM); - [[nodiscard]] Object* getObject(uint32_t id) const; + [[nodiscard]] const Object* getObject(uint32_t id) const; + [[nodiscard]] Reference* getReference(u4 id) const; [[nodiscard]] ClassObject* getClassObject(uint32_t id) const; - [[nodiscard]] Object* getChildObject(uint32_t id, ClassInfo* classInfo); - [[nodiscard]] Array* getArray(u4 id) const; + [[nodiscard]] const Object* getChildObject(uint32_t id, ClassInfo* classInfo); + [[nodiscard]] const Array* getArray(u4 id) const; [[nodiscard]] u4 getString(const char* utf8String) const; [[nodiscard]] u4 getClassObjectByName(std::string_view name) const; diff --git a/test/e2e/InstanceofTest.java b/test/e2e/InstanceofTest.java index a791f3e..5c74365 100644 --- a/test/e2e/InstanceofTest.java +++ b/test/e2e/InstanceofTest.java @@ -1,3 +1,4 @@ +import java.io.Serializable; public class InstanceofTest { public static void main(String[] args) {