From 72e09c2578c82619d6c748da4ab221faeb125e82 Mon Sep 17 00:00:00 2001 From: Alex Cook Date: Tue, 27 Jan 2026 16:29:46 -0500 Subject: [PATCH 1/4] refactor: introduce bytecode hierarchy resolver to move away from reflection --- .../resolution/BytecodeHierarchyResolver.java | 85 +++++++++++++++++++ .../resolution/HierarchyResolver.java | 7 +- .../resolution/ParentMethod.java | 10 +++ 3 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 framework/src/main/java/io/github/eisop/runtimeframework/resolution/BytecodeHierarchyResolver.java create mode 100644 framework/src/main/java/io/github/eisop/runtimeframework/resolution/ParentMethod.java diff --git a/framework/src/main/java/io/github/eisop/runtimeframework/resolution/BytecodeHierarchyResolver.java b/framework/src/main/java/io/github/eisop/runtimeframework/resolution/BytecodeHierarchyResolver.java new file mode 100644 index 0000000..ace7aa4 --- /dev/null +++ b/framework/src/main/java/io/github/eisop/runtimeframework/resolution/BytecodeHierarchyResolver.java @@ -0,0 +1,85 @@ +package io.github.eisop.runtimeframework.resolution; + +import io.github.eisop.runtimeframework.filter.Filter; +import java.io.IOException; +import java.io.InputStream; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.classfile.MethodModel; +import java.lang.reflect.Modifier; +import java.util.HashSet; +import java.util.Set; + +public class BytecodeHierarchyResolver implements HierarchyResolver { + + private final Filter safetyFilter; + + public BytecodeHierarchyResolver(Filter safetyFilter) { + this.safetyFilter = safetyFilter; + } + + @Override + public Set resolveUncheckedMethods(ClassModel model, ClassLoader loader) { + Set bridgesNeeded = new HashSet<>(); + Set implementedSignatures = new HashSet<>(); + + // Record signatures implemented in the current class + for (MethodModel mm : model.methods()) { + implementedSignatures.add( + mm.methodName().stringValue() + mm.methodTypeSymbol().descriptorString()); + } + + String superName = + model + .superclass() + .map(sc -> sc.asInternalName().replace('/', '.')) + .orElse("java.lang.Object"); + + if ("java.lang.Object".equals(superName)) return bridgesNeeded; + + // Traverse up + String currentName = superName; + while (currentName != null && !currentName.equals("java.lang.Object")) { + // If the parent is "Checked" (safe), we stop bridging at this boundary. + if (safetyFilter.test(currentName)) { + break; + } + + try (InputStream is = loader.getResourceAsStream(currentName.replace('.', '/') + ".class")) { + if (is == null) { + // If we can't find the class resource, we can't analyze it. + break; + } + + ClassModel parentModel = ClassFile.of().parse(is.readAllBytes()); + + for (MethodModel m : parentModel.methods()) { + int flags = m.flags().flagsMask(); + + // Check flags: Not Private, Not Static, Not Final, Not Synthetic, Not Bridge + if (Modifier.isPrivate(flags) + || Modifier.isStatic(flags) + || Modifier.isFinal(flags) + || (flags & 0x1000) != 0 /* SYNTHETIC */ + || (flags & 0x0040) != 0 /* BRIDGE */) { + continue; + } + + String sig = m.methodName().stringValue() + m.methodTypeSymbol().descriptorString(); + if (implementedSignatures.contains(sig)) continue; + + implementedSignatures.add(sig); + bridgesNeeded.add(new ParentMethod(parentModel, m)); + } + + currentName = + parentModel.superclass().map(sc -> sc.asInternalName().replace('/', '.')).orElse(null); + + } catch (IOException e) { + System.err.println("[RuntimeFramework] Error reading bytecode for: " + currentName); + break; + } + } + return bridgesNeeded; + } +} diff --git a/framework/src/main/java/io/github/eisop/runtimeframework/resolution/HierarchyResolver.java b/framework/src/main/java/io/github/eisop/runtimeframework/resolution/HierarchyResolver.java index 32a757c..c1ddeda 100644 --- a/framework/src/main/java/io/github/eisop/runtimeframework/resolution/HierarchyResolver.java +++ b/framework/src/main/java/io/github/eisop/runtimeframework/resolution/HierarchyResolver.java @@ -1,7 +1,6 @@ package io.github.eisop.runtimeframework.resolution; import java.lang.classfile.ClassModel; -import java.lang.reflect.Method; import java.util.Set; /** @@ -15,8 +14,8 @@ public interface HierarchyResolver { * class. 2. Are NOT final/private/static. 3. Come from an "Unchecked" (unsafe) ancestor. * * @param model The class currently being instrumented. - * @param loader The ClassLoader to use for loading parent classes. - * @return A set of java.lang.reflect.Method objects representing the targets for bridging. + * @param loader The ClassLoader to use for loading parent classes (as resources). + * @return A set of ParentMethod objects representing the targets for bridging. */ - Set resolveUncheckedMethods(ClassModel model, ClassLoader loader); + Set resolveUncheckedMethods(ClassModel model, ClassLoader loader); } diff --git a/framework/src/main/java/io/github/eisop/runtimeframework/resolution/ParentMethod.java b/framework/src/main/java/io/github/eisop/runtimeframework/resolution/ParentMethod.java new file mode 100644 index 0000000..2c40a5a --- /dev/null +++ b/framework/src/main/java/io/github/eisop/runtimeframework/resolution/ParentMethod.java @@ -0,0 +1,10 @@ +package io.github.eisop.runtimeframework.resolution; + +import java.lang.classfile.ClassModel; +import java.lang.classfile.MethodModel; + +/** + * Represents a method found in a parent class during hierarchy resolution. Wraps the ClassModel of + * the parent and the MethodModel of the method. + */ +public record ParentMethod(ClassModel owner, MethodModel method) {} From c2ad073159c97c06576ceeabe6875cb24962025f Mon Sep 17 00:00:00 2001 From: Alex Cook Date: Tue, 27 Jan 2026 16:45:15 -0500 Subject: [PATCH 2/4] refactor: update code base to use new bytecode resolver --- .../nullness/NullnessRuntimeChecker.java | 4 +- .../core/AnnotationInstrumenter.java | 57 +++++++------- .../policy/EnforcementPolicy.java | 6 +- .../policy/StandardEnforcementPolicy.java | 67 +++++++++-------- .../resolution/BytecodeHierarchyResolver.java | 5 -- .../ReflectionHierarchyResolver.java | 74 ------------------- 6 files changed, 74 insertions(+), 139 deletions(-) delete mode 100644 framework/src/main/java/io/github/eisop/runtimeframework/resolution/ReflectionHierarchyResolver.java diff --git a/checker/src/main/java/io/github/eisop/runtimeframework/checker/nullness/NullnessRuntimeChecker.java b/checker/src/main/java/io/github/eisop/runtimeframework/checker/nullness/NullnessRuntimeChecker.java index 87fd2e2..184b0cc 100644 --- a/checker/src/main/java/io/github/eisop/runtimeframework/checker/nullness/NullnessRuntimeChecker.java +++ b/checker/src/main/java/io/github/eisop/runtimeframework/checker/nullness/NullnessRuntimeChecker.java @@ -8,8 +8,8 @@ import io.github.eisop.runtimeframework.filter.ClassInfo; import io.github.eisop.runtimeframework.filter.Filter; import io.github.eisop.runtimeframework.policy.EnforcementPolicy; +import io.github.eisop.runtimeframework.resolution.BytecodeHierarchyResolver; import io.github.eisop.runtimeframework.resolution.HierarchyResolver; -import io.github.eisop.runtimeframework.resolution.ReflectionHierarchyResolver; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -33,7 +33,7 @@ public RuntimeInstrumenter getInstrumenter(Filter filter) { EnforcementPolicy policy = createPolicy(config, filter); HierarchyResolver resolver = - new ReflectionHierarchyResolver( + new BytecodeHierarchyResolver( className -> filter.test(new ClassInfo(className.replace('.', '/'), null, null))); return new AnnotationInstrumenter(policy, resolver, filter); diff --git a/framework/src/main/java/io/github/eisop/runtimeframework/core/AnnotationInstrumenter.java b/framework/src/main/java/io/github/eisop/runtimeframework/core/AnnotationInstrumenter.java index af620f7..9983d3e 100644 --- a/framework/src/main/java/io/github/eisop/runtimeframework/core/AnnotationInstrumenter.java +++ b/framework/src/main/java/io/github/eisop/runtimeframework/core/AnnotationInstrumenter.java @@ -19,8 +19,7 @@ import java.lang.classfile.instruction.StoreInstruction; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; -import java.lang.reflect.Method; -import java.util.Arrays; +import java.util.List; public class AnnotationInstrumenter extends RuntimeInstrumenter { @@ -159,7 +158,8 @@ protected void generateMethodCallCheck(CodeBuilder b, InvokeInstruction invoke) @Override protected void generateBridgeMethods(ClassBuilder builder, ClassModel model, ClassLoader loader) { - for (Method parentMethod : hierarchyResolver.resolveUncheckedMethods(model, loader)) { + for (io.github.eisop.runtimeframework.resolution.ParentMethod parentMethod : + hierarchyResolver.resolveUncheckedMethods(model, loader)) { if (policy.shouldGenerateBridge(parentMethod)) { emitBridge(builder, parentMethod); } @@ -186,28 +186,25 @@ protected void generateStoreCheck( } } - private void emitBridge(ClassBuilder builder, Method parentMethod) { - String methodName = parentMethod.getName(); - MethodTypeDesc desc = - MethodTypeDesc.of( - ClassDesc.ofDescriptor(parentMethod.getReturnType().descriptorString()), - Arrays.stream(parentMethod.getParameterTypes()) - .map(c -> ClassDesc.ofDescriptor(c.descriptorString())) - .toArray(ClassDesc[]::new)); + private void emitBridge( + ClassBuilder builder, io.github.eisop.runtimeframework.resolution.ParentMethod parentMethod) { + MethodModel method = parentMethod.method(); + String methodName = method.methodName().stringValue(); + MethodTypeDesc desc = method.methodTypeSymbol(); builder.withMethod( methodName, desc, - java.lang.reflect.Modifier.PUBLIC, + java.lang.reflect.Modifier.PUBLIC, // Bridges are usually public methodBuilder -> { methodBuilder.withCode( codeBuilder -> { int slotIndex = 1; - Class[] paramTypes = parentMethod.getParameterTypes(); + // Parse parameter types from descriptor + List paramTypes = desc.parameterList(); - for (int i = 0; i < paramTypes.length; i++) { - TypeKind type = - TypeKind.from(ClassDesc.ofDescriptor(paramTypes[i].descriptorString())); + for (int i = 0; i < paramTypes.size(); i++) { + TypeKind type = TypeKind.from(paramTypes.get(i)); RuntimeVerifier target = policy.getBridgeParameterCheck(parentMethod, i); if (target != null) { codeBuilder.aload(slotIndex); @@ -219,15 +216,18 @@ private void emitBridge(ClassBuilder builder, Method parentMethod) { codeBuilder.aload(0); slotIndex = 1; - for (Class pType : paramTypes) { - TypeKind type = TypeKind.from(ClassDesc.ofDescriptor(pType.descriptorString())); + for (java.lang.constant.ClassDesc pType : paramTypes) { + TypeKind type = TypeKind.from(pType); loadLocal(codeBuilder, type, slotIndex); slotIndex += type.slotSize(); } - ClassDesc parentDesc = ClassDesc.of(parentMethod.getDeclaringClass().getName()); + ClassDesc parentDesc = + ClassDesc.of( + parentMethod.owner().thisClass().asInternalName().replace('/', '.')); codeBuilder.invokespecial(parentDesc, methodName, desc); - returnResult(codeBuilder, parentMethod.getReturnType()); + returnResult( + codeBuilder, ClassDesc.ofDescriptor(desc.returnType().descriptorString())); }); }); } @@ -253,12 +253,17 @@ private void loadLocal(CodeBuilder b, TypeKind type, int slot) { } } - private void returnResult(CodeBuilder b, Class returnType) { - if (returnType == void.class) b.return_(); - else if (returnType == int.class || returnType == boolean.class) b.ireturn(); - else if (returnType == long.class) b.lreturn(); - else if (returnType == float.class) b.freturn(); - else if (returnType == double.class) b.dreturn(); + private void returnResult(CodeBuilder b, ClassDesc returnType) { + String desc = returnType.descriptorString(); + if (desc.equals("V")) b.return_(); + else if (desc.equals("I") + || desc.equals("Z") + || desc.equals("B") + || desc.equals("S") + || desc.equals("C")) b.ireturn(); + else if (desc.equals("J")) b.lreturn(); + else if (desc.equals("F")) b.freturn(); + else if (desc.equals("D")) b.dreturn(); else b.areturn(); } } diff --git a/framework/src/main/java/io/github/eisop/runtimeframework/policy/EnforcementPolicy.java b/framework/src/main/java/io/github/eisop/runtimeframework/policy/EnforcementPolicy.java index 0b8a3a4..c43a62a 100644 --- a/framework/src/main/java/io/github/eisop/runtimeframework/policy/EnforcementPolicy.java +++ b/framework/src/main/java/io/github/eisop/runtimeframework/policy/EnforcementPolicy.java @@ -1,12 +1,12 @@ package io.github.eisop.runtimeframework.policy; import io.github.eisop.runtimeframework.core.RuntimeVerifier; +import io.github.eisop.runtimeframework.resolution.ParentMethod; import java.lang.classfile.ClassModel; import java.lang.classfile.FieldModel; import java.lang.classfile.MethodModel; import java.lang.classfile.TypeKind; import java.lang.constant.MethodTypeDesc; -import java.lang.reflect.Method; /** Defines the rules for WHEN to inject a runtime check. */ public interface EnforcementPolicy { @@ -39,10 +39,10 @@ default RuntimeVerifier getBoundaryFieldWriteCheck( RuntimeVerifier getBoundaryFieldReadCheck(String owner, String fieldName, TypeKind type); /** Should we generate a bridge for this inherited method? */ - boolean shouldGenerateBridge(Method parentMethod); + boolean shouldGenerateBridge(ParentMethod parentMethod); /** For a bridge we are generating, what check applies to this parameter? */ - RuntimeVerifier getBridgeParameterCheck(Method parentMethod, int paramIndex); + RuntimeVerifier getBridgeParameterCheck(ParentMethod parentMethod, int paramIndex); /** Should we check an value being stored into an array? */ RuntimeVerifier getArrayStoreCheck(TypeKind componentType); diff --git a/framework/src/main/java/io/github/eisop/runtimeframework/policy/StandardEnforcementPolicy.java b/framework/src/main/java/io/github/eisop/runtimeframework/policy/StandardEnforcementPolicy.java index 897fa83..5f21c31 100644 --- a/framework/src/main/java/io/github/eisop/runtimeframework/policy/StandardEnforcementPolicy.java +++ b/framework/src/main/java/io/github/eisop/runtimeframework/policy/StandardEnforcementPolicy.java @@ -5,15 +5,14 @@ import io.github.eisop.runtimeframework.core.ValidationKind; import io.github.eisop.runtimeframework.filter.ClassInfo; import io.github.eisop.runtimeframework.filter.Filter; +import io.github.eisop.runtimeframework.resolution.ParentMethod; import java.lang.classfile.Annotation; import java.lang.classfile.Attributes; import java.lang.classfile.FieldModel; import java.lang.classfile.MethodModel; import java.lang.classfile.TypeAnnotation; import java.lang.classfile.TypeKind; -import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @@ -137,18 +136,21 @@ public RuntimeVerifier getBoundaryFieldReadCheck(String owner, String fieldName, // --- 3. Inheritance Logic --- @Override - public boolean shouldGenerateBridge(Method parentMethod) { - if (parentMethod.getDeclaringClass() == Object.class) return false; - Class[] paramTypes = parentMethod.getParameterTypes(); - java.lang.annotation.Annotation[][] paramAnnos = parentMethod.getParameterAnnotations(); + public boolean shouldGenerateBridge(ParentMethod parentMethod) { + if (parentMethod.owner().thisClass().asInternalName().equals("java/lang/Object")) return false; - for (int i = 0; i < paramTypes.length; i++) { - // Check specific parameter annotations + MethodModel method = parentMethod.method(); + // MethodTypeDesc param parsing + var paramTypes = method.methodTypeSymbol().parameterList(); + + for (int i = 0; i < paramTypes.size(); i++) { boolean explicitNoop = false; boolean explicitEnforce = false; - for (java.lang.annotation.Annotation anno : paramAnnos[i]) { - String desc = "L" + anno.annotationType().getName().replace('.', '/') + ";"; + List annos = getMethodParamAnnotations(method, i); + + for (Annotation anno : annos) { + String desc = anno.classSymbol().descriptorString(); TypeSystemConfiguration.ConfigEntry entry = configuration.find(desc); if (entry != null) { if (entry.kind() == ValidationKind.ENFORCE) explicitEnforce = true; @@ -159,8 +161,8 @@ public boolean shouldGenerateBridge(Method parentMethod) { if (explicitEnforce) return true; // If no explicit decision, check default if it's a reference type - ClassDesc pTypeDesc = ClassDesc.ofDescriptor(paramTypes[i].descriptorString()); - if (TypeKind.from(pTypeDesc) == TypeKind.REFERENCE && !explicitNoop) { + TypeKind pType = TypeKind.from(paramTypes.get(i)); + if (pType == TypeKind.REFERENCE && !explicitNoop) { TypeSystemConfiguration.ConfigEntry defaultEntry = configuration.getDefault(); if (defaultEntry != null && defaultEntry.kind() == ValidationKind.ENFORCE) { return true; @@ -171,26 +173,33 @@ public boolean shouldGenerateBridge(Method parentMethod) { } @Override - public RuntimeVerifier getBridgeParameterCheck(Method parentMethod, int paramIndex) { - java.lang.annotation.Annotation[] annos = parentMethod.getParameterAnnotations()[paramIndex]; - Class paramType = parentMethod.getParameterTypes()[paramIndex]; + public RuntimeVerifier getBridgeParameterCheck(ParentMethod parentMethod, int paramIndex) { + MethodModel method = parentMethod.method(); + List annos = getMethodParamAnnotations(method, paramIndex); - // 1. Explicit Configuration - for (java.lang.annotation.Annotation anno : annos) { - String desc = "L" + anno.annotationType().getName().replace('.', '/') + ";"; - TypeSystemConfiguration.ConfigEntry entry = configuration.find(desc); - if (entry != null) { - if (entry.kind() == ValidationKind.ENFORCE) return entry.verifier(); - if (entry.kind() == ValidationKind.NOOP) return null; + RuntimeVerifier verifier = resolveVerifier(annos); + if (verifier != null) return verifier; + + // Check default + var paramTypes = method.methodTypeSymbol().parameterList(); + TypeKind pType = TypeKind.from(paramTypes.get(paramIndex)); + + if (pType == TypeKind.REFERENCE) { + // Need to ensure we don't default if it was explicitly NOOPed (resolvedVerifier handles NOOP + // by returning null if found) + // Re-checking NOOP logic because resolveVerifier returns null for BOTH "Noop" and "Not Found" + boolean isExplicitNoop = false; + for (Annotation a : annos) { + TypeSystemConfiguration.ConfigEntry entry = + configuration.find(a.classSymbol().descriptorString()); + if (entry != null && entry.kind() == ValidationKind.NOOP) isExplicitNoop = true; } - } - // 2. Default - ClassDesc pTypeDesc = ClassDesc.ofDescriptor(paramType.descriptorString()); - if (TypeKind.from(pTypeDesc) == TypeKind.REFERENCE) { - TypeSystemConfiguration.ConfigEntry defaultEntry = configuration.getDefault(); - if (defaultEntry != null && defaultEntry.kind() == ValidationKind.ENFORCE) { - return defaultEntry.verifier(); + if (!isExplicitNoop) { + TypeSystemConfiguration.ConfigEntry defaultEntry = configuration.getDefault(); + if (defaultEntry != null && defaultEntry.kind() == ValidationKind.ENFORCE) { + return defaultEntry.verifier(); + } } } return null; diff --git a/framework/src/main/java/io/github/eisop/runtimeframework/resolution/BytecodeHierarchyResolver.java b/framework/src/main/java/io/github/eisop/runtimeframework/resolution/BytecodeHierarchyResolver.java index ace7aa4..d98fb62 100644 --- a/framework/src/main/java/io/github/eisop/runtimeframework/resolution/BytecodeHierarchyResolver.java +++ b/framework/src/main/java/io/github/eisop/runtimeframework/resolution/BytecodeHierarchyResolver.java @@ -23,7 +23,6 @@ public Set resolveUncheckedMethods(ClassModel model, ClassLoader l Set bridgesNeeded = new HashSet<>(); Set implementedSignatures = new HashSet<>(); - // Record signatures implemented in the current class for (MethodModel mm : model.methods()) { implementedSignatures.add( mm.methodName().stringValue() + mm.methodTypeSymbol().descriptorString()); @@ -37,17 +36,14 @@ public Set resolveUncheckedMethods(ClassModel model, ClassLoader l if ("java.lang.Object".equals(superName)) return bridgesNeeded; - // Traverse up String currentName = superName; while (currentName != null && !currentName.equals("java.lang.Object")) { - // If the parent is "Checked" (safe), we stop bridging at this boundary. if (safetyFilter.test(currentName)) { break; } try (InputStream is = loader.getResourceAsStream(currentName.replace('.', '/') + ".class")) { if (is == null) { - // If we can't find the class resource, we can't analyze it. break; } @@ -56,7 +52,6 @@ public Set resolveUncheckedMethods(ClassModel model, ClassLoader l for (MethodModel m : parentModel.methods()) { int flags = m.flags().flagsMask(); - // Check flags: Not Private, Not Static, Not Final, Not Synthetic, Not Bridge if (Modifier.isPrivate(flags) || Modifier.isStatic(flags) || Modifier.isFinal(flags) diff --git a/framework/src/main/java/io/github/eisop/runtimeframework/resolution/ReflectionHierarchyResolver.java b/framework/src/main/java/io/github/eisop/runtimeframework/resolution/ReflectionHierarchyResolver.java deleted file mode 100644 index 2b23e7f..0000000 --- a/framework/src/main/java/io/github/eisop/runtimeframework/resolution/ReflectionHierarchyResolver.java +++ /dev/null @@ -1,74 +0,0 @@ -package io.github.eisop.runtimeframework.resolution; - -import io.github.eisop.runtimeframework.filter.Filter; -import java.lang.classfile.ClassModel; -import java.lang.classfile.MethodModel; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.HashSet; -import java.util.Set; - -public class ReflectionHierarchyResolver implements HierarchyResolver { - - private final Filter safetyFilter; - - public ReflectionHierarchyResolver(Filter safetyFilter) { - this.safetyFilter = safetyFilter; - } - - @Override - public Set resolveUncheckedMethods(ClassModel model, ClassLoader loader) { - Set bridgesNeeded = new HashSet<>(); - Set implementedSignatures = new HashSet<>(); - - for (MethodModel mm : model.methods()) { - implementedSignatures.add( - mm.methodName().stringValue() + mm.methodTypeSymbol().descriptorString()); - } - - String superName = - model - .superclass() - .map(sc -> sc.asInternalName().replace('/', '.')) - .orElse("java.lang.Object"); - if ("java.lang.Object".equals(superName)) return bridgesNeeded; - - try { - Class currentAncestor = Class.forName(superName, false, loader); - - while (currentAncestor != null && currentAncestor != Object.class) { - if (safetyFilter.test(currentAncestor.getName())) { - break; - } - - for (Method m : currentAncestor.getDeclaredMethods()) { - int mods = m.getModifiers(); - if (Modifier.isFinal(mods) || Modifier.isStatic(mods) || Modifier.isPrivate(mods)) - continue; - if (m.isSynthetic() || m.isBridge()) continue; - String sig = m.getName() + getMethodDescriptor(m); - if (implementedSignatures.contains(sig)) continue; - - implementedSignatures.add(sig); - bridgesNeeded.add(m); - } - currentAncestor = currentAncestor.getSuperclass(); - } - } catch (ClassNotFoundException e) { - System.err.println( - "[RuntimeFramework] Could not resolve hierarchy for: " - + model.thisClass().asInternalName()); - } - return bridgesNeeded; - } - - private String getMethodDescriptor(Method m) { - StringBuilder sb = new StringBuilder("("); - for (Class p : m.getParameterTypes()) { - sb.append(p.descriptorString()); - } - sb.append(")"); - sb.append(m.getReturnType().descriptorString()); - return sb.toString(); - } -} From f65f6e317d47e617e3808cad03fed00ac6da0b4b Mon Sep 17 00:00:00 2001 From: Alex Cook Date: Wed, 28 Jan 2026 14:12:20 -0500 Subject: [PATCH 3/4] refactor: use proper imports --- .../runtimeframework/agent/RuntimeTransformer.java | 3 ++- .../core/AnnotationInstrumenter.java | 13 ++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/framework/src/main/java/io/github/eisop/runtimeframework/agent/RuntimeTransformer.java b/framework/src/main/java/io/github/eisop/runtimeframework/agent/RuntimeTransformer.java index dbb6f60..bdb2fe3 100644 --- a/framework/src/main/java/io/github/eisop/runtimeframework/agent/RuntimeTransformer.java +++ b/framework/src/main/java/io/github/eisop/runtimeframework/agent/RuntimeTransformer.java @@ -5,6 +5,7 @@ import io.github.eisop.runtimeframework.filter.ClassInfo; import io.github.eisop.runtimeframework.filter.Filter; import io.github.eisop.runtimeframework.qual.AnnotatedFor; +import java.io.InputStream; import java.lang.classfile.Annotation; import java.lang.classfile.AnnotationValue; import java.lang.classfile.Attributes; @@ -118,7 +119,7 @@ private boolean hasPackageLevelAnnotation(String className, ClassLoader loader, String packageInfoPath = packageName + "/package-info.class"; boolean found = false; - try (java.io.InputStream is = + try (InputStream is = (loader != null) ? loader.getResourceAsStream(packageInfoPath) : ClassLoader.getSystemResourceAsStream(packageInfoPath)) { diff --git a/framework/src/main/java/io/github/eisop/runtimeframework/core/AnnotationInstrumenter.java b/framework/src/main/java/io/github/eisop/runtimeframework/core/AnnotationInstrumenter.java index 9983d3e..edc32f2 100644 --- a/framework/src/main/java/io/github/eisop/runtimeframework/core/AnnotationInstrumenter.java +++ b/framework/src/main/java/io/github/eisop/runtimeframework/core/AnnotationInstrumenter.java @@ -4,6 +4,7 @@ import io.github.eisop.runtimeframework.filter.Filter; import io.github.eisop.runtimeframework.policy.EnforcementPolicy; import io.github.eisop.runtimeframework.resolution.HierarchyResolver; +import io.github.eisop.runtimeframework.resolution.ParentMethod; import java.lang.classfile.ClassBuilder; import java.lang.classfile.ClassModel; import java.lang.classfile.CodeBuilder; @@ -19,6 +20,7 @@ import java.lang.classfile.instruction.StoreInstruction; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; +import java.lang.reflect.Modifier; import java.util.List; public class AnnotationInstrumenter extends RuntimeInstrumenter { @@ -158,8 +160,7 @@ protected void generateMethodCallCheck(CodeBuilder b, InvokeInstruction invoke) @Override protected void generateBridgeMethods(ClassBuilder builder, ClassModel model, ClassLoader loader) { - for (io.github.eisop.runtimeframework.resolution.ParentMethod parentMethod : - hierarchyResolver.resolveUncheckedMethods(model, loader)) { + for (ParentMethod parentMethod : hierarchyResolver.resolveUncheckedMethods(model, loader)) { if (policy.shouldGenerateBridge(parentMethod)) { emitBridge(builder, parentMethod); } @@ -186,8 +187,7 @@ protected void generateStoreCheck( } } - private void emitBridge( - ClassBuilder builder, io.github.eisop.runtimeframework.resolution.ParentMethod parentMethod) { + private void emitBridge(ClassBuilder builder, ParentMethod parentMethod) { MethodModel method = parentMethod.method(); String methodName = method.methodName().stringValue(); MethodTypeDesc desc = method.methodTypeSymbol(); @@ -195,13 +195,12 @@ private void emitBridge( builder.withMethod( methodName, desc, - java.lang.reflect.Modifier.PUBLIC, // Bridges are usually public + Modifier.PUBLIC, methodBuilder -> { methodBuilder.withCode( codeBuilder -> { int slotIndex = 1; - // Parse parameter types from descriptor - List paramTypes = desc.parameterList(); + List paramTypes = desc.parameterList(); for (int i = 0; i < paramTypes.size(); i++) { TypeKind type = TypeKind.from(paramTypes.get(i)); From ffc19499c481fb5ec9a4fe047da61aa9026602ae Mon Sep 17 00:00:00 2001 From: Alex Cook Date: Wed, 28 Jan 2026 14:17:33 -0500 Subject: [PATCH 4/4] refactor: fix quailfied name --- .../eisop/runtimeframework/core/AnnotationInstrumenter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/main/java/io/github/eisop/runtimeframework/core/AnnotationInstrumenter.java b/framework/src/main/java/io/github/eisop/runtimeframework/core/AnnotationInstrumenter.java index edc32f2..efd39f9 100644 --- a/framework/src/main/java/io/github/eisop/runtimeframework/core/AnnotationInstrumenter.java +++ b/framework/src/main/java/io/github/eisop/runtimeframework/core/AnnotationInstrumenter.java @@ -215,7 +215,7 @@ private void emitBridge(ClassBuilder builder, ParentMethod parentMethod) { codeBuilder.aload(0); slotIndex = 1; - for (java.lang.constant.ClassDesc pType : paramTypes) { + for (ClassDesc pType : paramTypes) { TypeKind type = TypeKind.from(pType); loadLocal(codeBuilder, type, slotIndex); slotIndex += type.slotSize();