{"record":{"id":"7fbff8df8cc1f615","repo":"oracle/graal","slug":"expected-s-got-s","errorCode":null,"errorMessage":"expected %s, got %s","messagePattern":"expected (.+?), got (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/AnnotationValueParser.java","lineNumber":308,"sourceCode":"        return error != null ? error : List.of(result);\n    }\n\n    private static String getUtf8At(ConstantPool cp, int cpi) {\n        try {\n            return cp.lookupUtf8(cpi);\n        } catch (IndexOutOfBoundsException e) {\n            // Translate to IllegalArgumentException to match\n            // jdk.internal.reflect.ConstantPool\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    private static Object getPrimitiveConstAt(JavaKind kind, ConstantPool cp, int cpi) {\n        try {\n            PrimitiveConstant o = (PrimitiveConstant) cp.lookupConstant(cpi);\n            JavaKind stackKind = kind.getStackKind();\n            if (o.getJavaKind() != stackKind) {\n                throw new IllegalArgumentException(\"expected \" + stackKind + \", got \" + o.getJavaKind());\n            }\n            return JavaConstant.forPrimitive(kind, o.getRawValue()).asBoxedPrimitive();\n        } catch (ClassCastException | IndexOutOfBoundsException e) {\n            // Translate to IllegalArgumentException to match\n            // jdk.internal.reflect.ConstantPool\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    private static Object parseArrayElements(int length,\n                    ByteBuffer buf,\n                    int expectedTag,\n                    Supplier<Object> parseElement) {\n        Object[] result = new Object[length];\n        Object invalidTag = null;\n        for (int i = 0; i < result.length; i++) {\n            int tag = buf.get();\n            if (tag == expectedTag) {","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/AnnotationValueParser.java#L290-L326","documentation":"HSObject wraps a JNI object handle and is managed through an intrusive free list: when the handle is reclaimed, its 'next' pointer is set to point at itself, marking it invalid. getHandle() throws IllegalArgumentException if it detects that self-loop, i.e. the JNI reference behind this HSObject has already been freed/reclaimed and must not be used again. Using the wrapper after reclamation is a native-memory use-after-free analogue.","triggerScenarios":"Calling getHandle() on an HSObject after HSObject.invalidate(...) reclaimed it (as done in JNIMethodScope.close()); keeping an HSObject obtained inside one JNIMethodScope and using it after that scope closed; holding an HSObject across JNI local-frame pops after cleanHandles() drained the cleaners queue.","commonSituations":"Caching JNI object wrappers beyond the lifetime of their scope/frame; exception paths that close a scope and then continue using objects created inside it; long-lived Espresso/JNI feature code reusing stale wrappers.","solutions":["Confine every HSObject use to the JNIMethodScope it was created in; never leak it past close().","If an object must outlive a scope, create a global reference (NewGlobalRef) and use that handle instead of the local wrapper.","Audit exception handlers that close scopes and make sure they null out cached HSObjects afterwards."],"exampleFix":"// before\ntry (JNIMethodScope scope = new JNIMethodScope(\"op\", env)) {\n    obj = someCallReturningHSObject(env);\n}\nuseHandle(obj.getHandle()); // scope already closed -> reclaimed\n\n// after\ntry (JNIMethodScope scope = new JNIMethodScope(\"op\", env)) {\n    handle = JNIUtil.newGlobalRef(env, someCallReturningHSObject(env), \"obj\");\n}\nuseHandle(handle); // global ref survives the scope","handlingStrategy":"validation","validationCode":"// HSObject marks reclaimed handles by pointing next at itself:\nstatic boolean isReclaimed(HSObject o) {\n    return o != null && o.next == o; // conceptually; in practice just never use HSObject past scope close()\n}","typeGuard":null,"tryCatchPattern":"try {\n    handle = obj.getHandle();\n} catch (IllegalArgumentException e) {\n    // wrapper was invalidated with its scope; treat as stale-reference bug, do not retry\n    throw new IllegalStateException(\"HSObject used outside its JNIMethodScope\", e);\n}","preventionTips":["Never let an HSObject escape the JNIMethodScope that produced it.","Promote objects that must survive a scope with NewGlobalRef and pass the global handle around instead.","Null out cached wrappers in finally blocks that close scopes."],"tags":["jni","use-after-free","lifetime","native-image"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}