diff --git a/src/main/java/me/coley/cafedude/classfile/ClassFile.java b/src/main/java/me/coley/cafedude/classfile/ClassFile.java index 767671f..5f403b1 100644 --- a/src/main/java/me/coley/cafedude/classfile/ClassFile.java +++ b/src/main/java/me/coley/cafedude/classfile/ClassFile.java @@ -2,19 +2,22 @@ import me.coley.cafedude.classfile.attribute.Attribute; import me.coley.cafedude.classfile.behavior.AttributeHolder; +import me.coley.cafedude.classfile.behavior.CpAccessor; import me.coley.cafedude.classfile.constant.ConstPoolEntry; import me.coley.cafedude.classfile.constant.CpClass; import me.coley.cafedude.classfile.constant.CpUtf8; import me.coley.cafedude.io.AttributeContext; import java.util.List; +import java.util.Set; +import java.util.TreeSet; /** * Class file format. * * @author Matt Coley */ -public class ClassFile implements AttributeHolder { +public class ClassFile implements AttributeHolder, CpAccessor { private final ConstPool pool; private List interfaceIndices; private List fields; @@ -251,4 +254,19 @@ public void setAttributes(List attributes) { public AttributeContext getHolderType() { return AttributeContext.CLASS; } + + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getClassIndex()); + set.add(getSuperIndex()); + set.addAll(getInterfaceIndices()); + for (Attribute attribute : getAttributes()) + set.addAll(attribute.cpAccesses()); + for (ClassMember field : getFields()) + set.addAll(field.cpAccesses()); + for (ClassMember method : getMethods()) + set.addAll(method.cpAccesses()); + return set; + } } diff --git a/src/main/java/me/coley/cafedude/classfile/ClassMember.java b/src/main/java/me/coley/cafedude/classfile/ClassMember.java index 1b78ba5..a973c51 100644 --- a/src/main/java/me/coley/cafedude/classfile/ClassMember.java +++ b/src/main/java/me/coley/cafedude/classfile/ClassMember.java @@ -2,15 +2,18 @@ import me.coley.cafedude.classfile.attribute.Attribute; import me.coley.cafedude.classfile.behavior.AttributeHolder; +import me.coley.cafedude.classfile.behavior.CpAccessor; import java.util.List; +import java.util.Set; +import java.util.TreeSet; /** * Base class member. * * @author Matt Coley */ -public abstract class ClassMember implements AttributeHolder { +public abstract class ClassMember implements AttributeHolder, CpAccessor { private List attributes; private int access; private int nameIndex; @@ -87,4 +90,14 @@ public List getAttributes() { public void setAttributes(List attributes) { this.attributes = attributes; } + + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getNameIndex()); + set.add(getTypeIndex()); + for (Attribute attribute : getAttributes()) + set.addAll(attribute.cpAccesses()); + return set; + } } diff --git a/src/main/java/me/coley/cafedude/classfile/annotation/Annotation.java b/src/main/java/me/coley/cafedude/classfile/annotation/Annotation.java index f0607be..23be729 100644 --- a/src/main/java/me/coley/cafedude/classfile/annotation/Annotation.java +++ b/src/main/java/me/coley/cafedude/classfile/annotation/Annotation.java @@ -2,8 +2,11 @@ import me.coley.cafedude.classfile.attribute.AnnotationsAttribute; import me.coley.cafedude.classfile.attribute.ParameterAnnotationsAttribute; +import me.coley.cafedude.classfile.behavior.CpAccessor; import java.util.Map; +import java.util.Set; +import java.util.TreeSet; /** * Annotation outline. Represents an annotation item to be contained in an annotation collection attribute such as: @@ -18,7 +21,7 @@ * @see AnnotationsAttribute * @see ParameterAnnotationsAttribute */ -public class Annotation { +public class Annotation implements CpAccessor { private final Map values; private final int typeIndex; @@ -51,6 +54,15 @@ public Map getValues() { return values; } + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getTypeIndex()); + for (ElementValue value : values.values()) + set.addAll(value.cpAccesses()); + return set; + } + /** * @return Computed size for the annotation. */ diff --git a/src/main/java/me/coley/cafedude/classfile/annotation/AnnotationElementValue.java b/src/main/java/me/coley/cafedude/classfile/annotation/AnnotationElementValue.java index de96d9a..e4fccb7 100644 --- a/src/main/java/me/coley/cafedude/classfile/annotation/AnnotationElementValue.java +++ b/src/main/java/me/coley/cafedude/classfile/annotation/AnnotationElementValue.java @@ -1,5 +1,8 @@ package me.coley.cafedude.classfile.annotation; +import java.util.Set; +import java.util.TreeSet; + /** * Nested annotation element value. * @@ -44,6 +47,13 @@ public char getTag() { return super.getTag(); } + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.addAll(annotation.cpAccesses()); + return set; + } + @Override public int computeLength() { // u1: tag diff --git a/src/main/java/me/coley/cafedude/classfile/annotation/ArrayElementValue.java b/src/main/java/me/coley/cafedude/classfile/annotation/ArrayElementValue.java index a59c1e7..e3ddb31 100644 --- a/src/main/java/me/coley/cafedude/classfile/annotation/ArrayElementValue.java +++ b/src/main/java/me/coley/cafedude/classfile/annotation/ArrayElementValue.java @@ -1,6 +1,8 @@ package me.coley.cafedude.classfile.annotation; import java.util.List; +import java.util.Set; +import java.util.TreeSet; /** * Array element value. @@ -46,6 +48,14 @@ public char getTag() { return super.getTag(); } + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + for (ElementValue value : getArray()) + set.addAll(value.cpAccesses()); + return set; + } + @Override public int computeLength() { // u1: tag diff --git a/src/main/java/me/coley/cafedude/classfile/annotation/ClassElementValue.java b/src/main/java/me/coley/cafedude/classfile/annotation/ClassElementValue.java index 2af7de0..8ff09a3 100644 --- a/src/main/java/me/coley/cafedude/classfile/annotation/ClassElementValue.java +++ b/src/main/java/me/coley/cafedude/classfile/annotation/ClassElementValue.java @@ -1,5 +1,8 @@ package me.coley.cafedude.classfile.annotation; +import java.util.Set; +import java.util.TreeSet; + /** * Class element value. * @@ -44,6 +47,13 @@ public char getTag() { return super.getTag(); } + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getClassIndex()); + return set; + } + @Override public int computeLength() { // u1: tag diff --git a/src/main/java/me/coley/cafedude/classfile/annotation/ElementValue.java b/src/main/java/me/coley/cafedude/classfile/annotation/ElementValue.java index 6205d4d..8dfe4b5 100644 --- a/src/main/java/me/coley/cafedude/classfile/annotation/ElementValue.java +++ b/src/main/java/me/coley/cafedude/classfile/annotation/ElementValue.java @@ -1,11 +1,13 @@ package me.coley.cafedude.classfile.annotation; +import me.coley.cafedude.classfile.behavior.CpAccessor; + /** * Base attribute element value. * * @author Matt Coley */ -public abstract class ElementValue { +public abstract class ElementValue implements CpAccessor { private final char tag; /** diff --git a/src/main/java/me/coley/cafedude/classfile/annotation/EnumElementValue.java b/src/main/java/me/coley/cafedude/classfile/annotation/EnumElementValue.java index fb8a9b2..e7282ac 100644 --- a/src/main/java/me/coley/cafedude/classfile/annotation/EnumElementValue.java +++ b/src/main/java/me/coley/cafedude/classfile/annotation/EnumElementValue.java @@ -1,5 +1,8 @@ package me.coley.cafedude.classfile.annotation; +import java.util.Set; +import java.util.TreeSet; + /** * Enum element value. * @@ -63,6 +66,14 @@ public char getTag() { return super.getTag(); } + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getNameIndex()); + set.add(getTypeIndex()); + return set; + } + @Override public int computeLength() { // u1: tag diff --git a/src/main/java/me/coley/cafedude/classfile/annotation/PrimitiveElementValue.java b/src/main/java/me/coley/cafedude/classfile/annotation/PrimitiveElementValue.java index a55025f..43f1a5a 100644 --- a/src/main/java/me/coley/cafedude/classfile/annotation/PrimitiveElementValue.java +++ b/src/main/java/me/coley/cafedude/classfile/annotation/PrimitiveElementValue.java @@ -1,5 +1,8 @@ package me.coley.cafedude.classfile.annotation; +import java.util.Set; +import java.util.TreeSet; + /** * Primitive value element value. * @@ -42,6 +45,13 @@ public char getTag() { return super.getTag(); } + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getValueIndex()); + return set; + } + @Override public int computeLength() { // u1: tag diff --git a/src/main/java/me/coley/cafedude/classfile/annotation/Utf8ElementValue.java b/src/main/java/me/coley/cafedude/classfile/annotation/Utf8ElementValue.java index 9e7a9d7..d160587 100644 --- a/src/main/java/me/coley/cafedude/classfile/annotation/Utf8ElementValue.java +++ b/src/main/java/me/coley/cafedude/classfile/annotation/Utf8ElementValue.java @@ -1,5 +1,8 @@ package me.coley.cafedude.classfile.annotation; +import java.util.Set; +import java.util.TreeSet; + /** * UTF8 string element value. * @@ -44,6 +47,13 @@ public char getTag() { return super.getTag(); } + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getUtfIndex()); + return set; + } + @Override public int computeLength() { // u1: tag diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/AnnotationDefaultAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/AnnotationDefaultAttribute.java index b3fe2ff..76518bc 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/AnnotationDefaultAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/AnnotationDefaultAttribute.java @@ -2,6 +2,8 @@ import me.coley.cafedude.classfile.annotation.ElementValue; +import java.util.Set; + /** * Represents the default value of a annotation field (Which are technically methods, but I digress). * @@ -24,12 +26,19 @@ public AnnotationDefaultAttribute(int nameIndex, ElementValue elementValue) { /** * @return Value of the annotation type element represented by the {@code method_info} structure - * enclosing this attribute. + * enclosing this attribute. */ public ElementValue getElementValue() { return elementValue; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + set.addAll(elementValue.cpAccesses()); + return set; + } + @Override public int computeInternalLength() { return getElementValue().computeLength(); diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/AnnotationsAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/AnnotationsAttribute.java index fa2f849..44e125d 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/AnnotationsAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/AnnotationsAttribute.java @@ -3,6 +3,7 @@ import me.coley.cafedude.classfile.annotation.Annotation; import java.util.List; +import java.util.Set; /** * Annotation collection attribute. Represents either: @@ -42,6 +43,14 @@ public void setAnnotations(List annotations) { this.annotations = annotations; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + for (Annotation annotation : getAnnotations()) + set.addAll(annotation.cpAccesses()); + return set; + } + @Override public int computeInternalLength() { // u2 num_annotations + annotations diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/Attribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/Attribute.java index ae59081..d02ca1c 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/Attribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/Attribute.java @@ -1,11 +1,16 @@ package me.coley.cafedude.classfile.attribute; +import me.coley.cafedude.classfile.behavior.CpAccessor; + +import java.util.Set; +import java.util.TreeSet; + /** * Base attribute. * * @author Matt Coley */ -public abstract class Attribute { +public abstract class Attribute implements CpAccessor { private final int nameIndex; /** @@ -41,4 +46,11 @@ public int computeCompleteLength() { // ??: Internal length return 6 + computeInternalLength(); } + + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getNameIndex()); + return set; + } } diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/BootstrapMethodsAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/BootstrapMethodsAttribute.java index 769bde0..f557ab6 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/BootstrapMethodsAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/BootstrapMethodsAttribute.java @@ -1,8 +1,11 @@ package me.coley.cafedude.classfile.attribute; +import me.coley.cafedude.classfile.behavior.CpAccessor; import me.coley.cafedude.classfile.constant.CpMethodHandle; import java.util.List; +import java.util.Set; +import java.util.TreeSet; /** * Bootstrap methods attribute. @@ -23,6 +26,14 @@ public BootstrapMethodsAttribute(int nameIndex, List bootstrapM this.bootstrapMethods = bootstrapMethods; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + for (BootstrapMethod bsm : bootstrapMethods) + set.addAll(bsm.cpAccesses()); + return set; + } + @Override public int computeInternalLength() { return 2 + bootstrapMethods.stream().mapToInt(BootstrapMethod::computeLength).sum(); @@ -48,7 +59,7 @@ public void setBootstrapMethods(List bootstrapMethods) { * * @author Matt Coley */ - public static class BootstrapMethod { + public static class BootstrapMethod implements CpAccessor { private int bsmMethodref; private List args; @@ -63,6 +74,14 @@ public BootstrapMethod(int bsmMethodref, List args) { this.args = args; } + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getBsmMethodref()); + set.addAll(getArgs()); + return set; + } + /** * @return Constant pool index of method reference, {@link CpMethodHandle}. */ diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/CodeAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/CodeAttribute.java index 56db8ce..78c49bb 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/CodeAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/CodeAttribute.java @@ -1,9 +1,12 @@ package me.coley.cafedude.classfile.attribute; import me.coley.cafedude.classfile.behavior.AttributeHolder; +import me.coley.cafedude.classfile.behavior.CpAccessor; import me.coley.cafedude.io.AttributeContext; +import java.util.Collections; import java.util.List; +import java.util.Set; /** * Method code attribute. @@ -117,6 +120,17 @@ public AttributeContext getHolderType() { return AttributeContext.ATTRIBUTE; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + for (Attribute attribute : getAttributes()) + set.addAll(attribute.cpAccesses()); + for (ExceptionTableEntry ex : getExceptionTable()) + set.addAll(ex.cpAccesses()); + // TODO: Instructions + return set; + } + @Override public int computeInternalLength() { // u2: max_stack @@ -143,7 +157,7 @@ public int computeInternalLength() { * * @author Matt Coley */ - public static class ExceptionTableEntry { + public static class ExceptionTableEntry implements CpAccessor { private int startPc; private int endPc; private int handlerPc; @@ -225,5 +239,10 @@ public int getCatchTypeIndex() { public void setCatchTypeIndex(int catchTypeIndex) { this.catchTypeIndex = catchTypeIndex; } + + @Override + public Set cpAccesses() { + return Collections.singleton(getCatchTypeIndex()); + } } } diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/ConstantValueAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/ConstantValueAttribute.java index 12f9dd8..4c359a8 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/ConstantValueAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/ConstantValueAttribute.java @@ -1,17 +1,18 @@ package me.coley.cafedude.classfile.attribute; +import java.util.Set; + /** * Constant value attribute - * - * @author JCWasmx86 * + * @author JCWasmx86 */ -public class ConstantValueAttribute extends Attribute{ +public class ConstantValueAttribute extends Attribute { private int constantValueIndex; /** * @param nameIndex - * Name index in constant pool. + * Name index in constant pool. * @param constantValueIndex * Index in the constant pool representing the value of this attribute. */ @@ -20,6 +21,13 @@ public ConstantValueAttribute(int nameIndex, int constantValueIndex) { this.constantValueIndex = constantValueIndex; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + set.add(getConstantValueIndex()); + return set; + } + @Override public int computeInternalLength() { return 2; diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/EnclosingMethodAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/EnclosingMethodAttribute.java index dce31ce..a9d3297 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/EnclosingMethodAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/EnclosingMethodAttribute.java @@ -1,15 +1,16 @@ package me.coley.cafedude.classfile.attribute; +import java.util.Set; + /** * Enclosing method attribute - * - * @author JCWasmx86 * + * @author JCWasmx86 */ -public class EnclosingMethodAttribute extends Attribute{ +public class EnclosingMethodAttribute extends Attribute { private int classIndex; private int methodIndex; - + /** * @param nameIndex * Name index in constant pool. @@ -47,7 +48,7 @@ public int getMethodIndex() { public void setClassIndex(int classIndex) { this.classIndex = classIndex; } - + /** * @param methodIndex * Set the enclosing method index. @@ -56,6 +57,14 @@ public void setMethodIndex(int methodIndex) { this.methodIndex = methodIndex; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + set.add(getClassIndex()); + set.add(getMethodIndex()); + return set; + } + @Override public int computeInternalLength() { return 4; diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/ExceptionsAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/ExceptionsAttribute.java index 957ad0b..4f11ff2 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/ExceptionsAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/ExceptionsAttribute.java @@ -1,12 +1,12 @@ package me.coley.cafedude.classfile.attribute; import java.util.List; +import java.util.Set; /** * Checked exceptions attribute. - * - * @author JCWasmx86 * + * @author JCWasmx86 */ public class ExceptionsAttribute extends Attribute { private List exceptionIndexTable; @@ -39,6 +39,13 @@ public void setExceptionIndexTable(List exceptionIndexTable) { this.exceptionIndexTable = exceptionIndexTable; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + set.addAll(getExceptionIndexTable()); + return set; + } + @Override public int computeInternalLength() { // Multiplying with two, as each index has two bytes. diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/InnerClassesAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/InnerClassesAttribute.java index e8e4c5f..d3d577a 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/InnerClassesAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/InnerClassesAttribute.java @@ -1,6 +1,10 @@ package me.coley.cafedude.classfile.attribute; +import me.coley.cafedude.classfile.behavior.CpAccessor; + import java.util.List; +import java.util.Set; +import java.util.TreeSet; /** * Attribute describing the inner classes of a class. @@ -36,6 +40,14 @@ public void setInnerClasses(List innerClasses) { this.innerClasses = innerClasses; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + for (InnerClass inner : getInnerClasses()) + set.addAll(inner.cpAccesses()); + return set; + } + @Override public int computeInternalLength() { return 2 + this.innerClasses.size() * 8; @@ -46,7 +58,7 @@ public int computeInternalLength() { * * @author JCWasmx86 */ - public static class InnerClass { + public static class InnerClass implements CpAccessor { private int innerClassInfoIndex; private int outerClassInfoIndex; private int innerNameIndex; @@ -135,5 +147,14 @@ public int getInnerClassAccessFlags() { public void setInnerClassAccessFlags(int innerClassAccessFlags) { this.innerClassAccessFlags = innerClassAccessFlags; } + + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getOuterClassInfoIndex()); + set.add(getInnerClassInfoIndex()); + set.add(getInnerNameIndex()); + return set; + } } } diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/LocalVariableTableAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/LocalVariableTableAttribute.java index 276a116..0f16605 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/LocalVariableTableAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/LocalVariableTableAttribute.java @@ -1,6 +1,10 @@ package me.coley.cafedude.classfile.attribute; +import me.coley.cafedude.classfile.behavior.CpAccessor; + import java.util.List; +import java.util.Set; +import java.util.TreeSet; /** * Variable table attribute. @@ -21,6 +25,14 @@ public LocalVariableTableAttribute(int nameIndex, List entries) { this.entries = entries; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + for (VarEntry entry : getEntries()) + set.addAll(entry.cpAccesses()); + return set; + } + @Override public int computeInternalLength() { // u2: line_number_table_length @@ -52,7 +64,7 @@ public void setEntries(List entries) { /** * Variable table entry. */ - public static class VarEntry { + public static class VarEntry implements CpAccessor { private final int startPc; private final int length; private final int nameIndex; @@ -113,5 +125,13 @@ public int getDescIndex() { public int getIndex() { return index; } + + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getNameIndex()); + set.add(getDescIndex()); + return set; + } } } diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/LocalVariableTypeTableAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/LocalVariableTypeTableAttribute.java index 20917f6..6236171 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/LocalVariableTypeTableAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/LocalVariableTypeTableAttribute.java @@ -1,6 +1,10 @@ package me.coley.cafedude.classfile.attribute; +import me.coley.cafedude.classfile.behavior.CpAccessor; + import java.util.List; +import java.util.Set; +import java.util.TreeSet; /** * Variable generic/type table attribute. @@ -21,6 +25,14 @@ public LocalVariableTypeTableAttribute(int nameIndex, List entries this.entries = entries; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + for (VarTypeEntry entry : getEntries()) + set.addAll(entry.cpAccesses()); + return set; + } + @Override public int computeInternalLength() { // u2: line_number_table_length @@ -52,7 +64,7 @@ public void setEntries(List entries) { /** * Variable table entry. */ - public static class VarTypeEntry { + public static class VarTypeEntry implements CpAccessor { private final int startPc; private final int length; private final int nameIndex; @@ -113,5 +125,13 @@ public int getSignatureIndex() { public int getIndex() { return index; } + + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getNameIndex()); + set.add(getSignatureIndex()); + return set; + } } } diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/ModuleAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/ModuleAttribute.java index 70b0e95..5e8b258 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/ModuleAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/ModuleAttribute.java @@ -1,11 +1,14 @@ package me.coley.cafedude.classfile.attribute; +import me.coley.cafedude.classfile.behavior.CpAccessor; import me.coley.cafedude.classfile.constant.CpClass; import me.coley.cafedude.classfile.constant.CpModule; import me.coley.cafedude.classfile.constant.CpPackage; import me.coley.cafedude.classfile.constant.CpUtf8; import java.util.List; +import java.util.Set; +import java.util.TreeSet; /** * Module attribute. @@ -184,6 +187,23 @@ public void setProvides(List provides) { this.provides = provides; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + set.add(getModuleIndex()); + set.add(getVersionIndex()); + set.addAll(getUses()); + for (Requires requires : getRequires()) + set.addAll(requires.cpAccesses()); + for (Exports exports : getExports()) + set.addAll(exports.cpAccesses()); + for (Opens opens : getOpens()) + set.addAll(opens.cpAccesses()); + for (Provides provides : getProvides()) + set.addAll(provides.cpAccesses()); + return set; + } + @Override public int computeInternalLength() { // 6 = module_name_index + module_flags + module_version_index @@ -206,7 +226,7 @@ public int computeInternalLength() { * * @author Matt Coley */ - public static class Requires { + public static class Requires implements CpAccessor { private int index; private int flags; private int versionIndex; @@ -279,6 +299,14 @@ public int getVersionIndex() { public void setVersionIndex(int versionIndex) { this.versionIndex = versionIndex; } + + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getVersionIndex()); + set.add(getIndex()); + return set; + } } /** @@ -286,7 +314,7 @@ public void setVersionIndex(int versionIndex) { * * @author Matt Coley */ - public static class Exports { + public static class Exports implements CpAccessor { private int index; private int flags; private List toIndices; @@ -361,6 +389,14 @@ public int length() { // 6 = index + flags + list.size() return 6 + 2 * toIndices.size(); } + + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getIndex()); + set.addAll(getToIndices()); + return set; + } } /** @@ -368,7 +404,7 @@ public int length() { * * @author Matt Coley */ - public static class Opens { + public static class Opens implements CpAccessor { private int index; private int flags; private List toIndices; @@ -443,6 +479,14 @@ public int length() { // 6 = index + flags + list.size() return 6 + 2 * toIndices.size(); } + + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getIndex()); + set.addAll(getToIndices()); + return set; + } } /** @@ -450,7 +494,7 @@ public int length() { * * @author Matt Coley */ - public static class Provides { + public static class Provides implements CpAccessor { private int index; private List withIndex; @@ -504,5 +548,13 @@ public int length() { // 4 = index + list.size() return 4 + 2 * withIndex.size(); } + + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getIndex()); + set.addAll(getWithIndices()); + return set; + } } } diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/NestHostAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/NestHostAttribute.java index f678f19..ec7277b 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/NestHostAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/NestHostAttribute.java @@ -1,5 +1,7 @@ package me.coley.cafedude.classfile.attribute; +import java.util.Set; + /** * Nest host attribute, points to host class. * @@ -34,6 +36,13 @@ public void setHostClassIndex(int hostClassIndex) { this.hostClassIndex = hostClassIndex; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + set.add(getHostClassIndex()); + return set; + } + @Override public int computeInternalLength() { // U2: hostClassIndex diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/NestMembersAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/NestMembersAttribute.java index e7a8c44..8e4edb2 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/NestMembersAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/NestMembersAttribute.java @@ -1,6 +1,7 @@ package me.coley.cafedude.classfile.attribute; import java.util.List; +import java.util.Set; /** * Nest members attribute, lists classes allowed to declare membership of the nest hosted by current class. @@ -39,6 +40,13 @@ public void setMemberClassIndices(List memberClassIndices) { this.memberClassIndices = memberClassIndices; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + set.addAll(getMemberClassIndices()); + return set; + } + @Override public int computeInternalLength() { // U2: classCount diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/ParameterAnnotationsAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/ParameterAnnotationsAttribute.java index 7eea191..1a45bd7 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/ParameterAnnotationsAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/ParameterAnnotationsAttribute.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Map; +import java.util.Set; /** * Annotation collection attribute on method parameters. Represents either: @@ -43,6 +44,15 @@ public void setParameterAnnotations(Map> parameterAnno this.parameterAnnotations = parameterAnnotations; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + for (List list : getParameterAnnotations().values()) + for (Annotation annotation : list) + set.addAll(annotation.cpAccesses()); + return set; + } + @Override public int computeInternalLength() { int length = 1; // u1: num_parameters diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/PermittedClassesAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/PermittedClassesAttribute.java index 8d5a67d..2799f5f 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/PermittedClassesAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/PermittedClassesAttribute.java @@ -1,6 +1,7 @@ package me.coley.cafedude.classfile.attribute; import java.util.List; +import java.util.Set; /** * Permitted classes attribute. @@ -36,6 +37,13 @@ public void setClasses(List classes) { this.classes = classes; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + set.addAll(getClasses()); + return set; + } + @Override public int computeInternalLength() { // u2: count diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/RecordAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/RecordAttribute.java index 72e0048..8f2abc5 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/RecordAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/RecordAttribute.java @@ -1,6 +1,10 @@ package me.coley.cafedude.classfile.attribute; +import me.coley.cafedude.classfile.behavior.CpAccessor; + import java.util.List; +import java.util.Set; +import java.util.TreeSet; /** * Permitted classes attribute. @@ -21,6 +25,14 @@ public RecordAttribute(int nameIndex, List components) { this.components = components; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + for (RecordComponent component : getComponents()) + set.addAll(component.cpAccesses()); + return set; + } + @Override public int computeInternalLength() { // u2: count @@ -36,7 +48,8 @@ public List getComponents() { } /** - * @param components New record components (fields). + * @param components + * New record components (fields). */ public void setComponents(List components) { this.components = components; @@ -45,7 +58,7 @@ public void setComponents(List components) { /** * Component entry. */ - public static class RecordComponent { + public static class RecordComponent implements CpAccessor { private int nameIndex; private int descIndex; private List attributes; @@ -109,6 +122,14 @@ public void setAttributes(List attributes) { this.attributes = attributes; } + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + set.add(getNameIndex()); + set.add(getDescIndex()); + return set; + } + /** * @return Component bytecode size. */ @@ -122,5 +143,7 @@ public int length() { len += attribute.computeCompleteLength(); return len; } + + } } diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/SignatureAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/SignatureAttribute.java index 3b95071..7dc7176 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/SignatureAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/SignatureAttribute.java @@ -1,5 +1,7 @@ package me.coley.cafedude.classfile.attribute; +import java.util.Set; + /** * Signature attribute, for generic types. * @@ -34,6 +36,13 @@ public void setSignatureIndex(int signatureIndex) { this.signatureIndex = signatureIndex; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + set.add(getSignatureIndex()); + return set; + } + @Override public int computeInternalLength() { // U2: signatureIndex diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/SourceFileAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/SourceFileAttribute.java index 1dedb6c..2e87c44 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/SourceFileAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/SourceFileAttribute.java @@ -1,5 +1,7 @@ package me.coley.cafedude.classfile.attribute; +import java.util.Set; + /** * Source file attribute. * @@ -34,6 +36,13 @@ public void setSourceFileNameIndex(int sourceFileNameIndex) { this.sourceFileNameIndex = sourceFileNameIndex; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + set.add(getSourceFileNameIndex()); + return set; + } + @Override public int computeInternalLength() { // U2: sourceFileNameIndex diff --git a/src/main/java/me/coley/cafedude/classfile/attribute/StackMapTableAttribute.java b/src/main/java/me/coley/cafedude/classfile/attribute/StackMapTableAttribute.java index 2a98ccc..cce83c1 100644 --- a/src/main/java/me/coley/cafedude/classfile/attribute/StackMapTableAttribute.java +++ b/src/main/java/me/coley/cafedude/classfile/attribute/StackMapTableAttribute.java @@ -1,8 +1,12 @@ package me.coley.cafedude.classfile.attribute; import me.coley.cafedude.Constants; +import me.coley.cafedude.classfile.behavior.CpAccessor; +import java.util.Collections; import java.util.List; +import java.util.Set; +import java.util.TreeSet; /** * Used during the process of verification by type checking. @@ -29,13 +33,21 @@ public class StackMapTableAttribute * @param nameIndex * Name index in constant pool. * @param frames - * A list of stack map frames. + * Stack map frames of a method. */ public StackMapTableAttribute(int nameIndex, List frames) { super(nameIndex); this.frames = frames; } + @Override + public Set cpAccesses() { + Set set = super.cpAccesses(); + for (StackMapFrame frame : frames) + set.addAll(frame.cpAccesses()); + return set; + } + @Override public int computeInternalLength() { // u2: number_of_entries @@ -54,7 +66,7 @@ public int computeInternalLength() { * which type is in use, followed by zero or more bytes, giving more * information about the tag. */ - public abstract static class TypeInfo { + public abstract static class TypeInfo implements CpAccessor { /** * @return The one byte tag representing this type. */ @@ -67,6 +79,11 @@ public int getLength() { // u1: tag return 1; } + + @Override + public Set cpAccesses() { + return Collections.emptySet(); + } } /** @@ -160,6 +177,11 @@ public ObjectVariableInfo(int classIndex) { this.classIndex = classIndex; } + @Override + public Set cpAccesses() { + return Collections.singleton(classIndex); + } + /** * @return Size in bytes of the serialized type info. */ @@ -246,7 +268,7 @@ public int getTag() { * initial frame of the method. In that case, the bytecode offset at which the * stack map frame applies is the value {@code offset_delta} specified in the frame. */ - public abstract static class StackMapFrame { + public abstract static class StackMapFrame implements CpAccessor { /** * The offset delta of this frame. */ @@ -272,6 +294,11 @@ public int getLength() { // u1 frame_type return 1; } + + @Override + public Set cpAccesses() { + return Collections.emptySet(); + } } /** @@ -320,6 +347,11 @@ public SameLocalsOneStackItem(int offsetDelta, TypeInfo stack) { this.stack = stack; } + @Override + public Set cpAccesses() { + return stack.cpAccesses(); + } + /** * @return Size in bytes of the serialized frame. */ @@ -358,6 +390,11 @@ public SameLocalsOneStackItemExtended(int offsetDelta, TypeInfo stack) { this.stack = stack; } + @Override + public Set cpAccesses() { + return stack.cpAccesses(); + } + /** * @return Size in bytes of the serialized frame. */ @@ -473,6 +510,14 @@ public AppendFrame(int offsetDelta, List additionalLocals) { this.additionalLocals = additionalLocals; } + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + for (TypeInfo info : additionalLocals) + set.addAll(info.cpAccesses()); + return set; + } + /** * @return Size in bytes of the serialized frame. */ @@ -523,6 +568,16 @@ public FullFrame(int offsetDelta, List locals, List stack) { this.stack = stack; } + @Override + public Set cpAccesses() { + Set set = new TreeSet<>(); + for (TypeInfo info : locals) + set.addAll(info.cpAccesses()); + for (TypeInfo info : stack) + set.addAll(info.cpAccesses()); + return set; + } + /** * @return Size in bytes of the serialized frame. */ diff --git a/src/main/java/me/coley/cafedude/classfile/behavior/CpAccessor.java b/src/main/java/me/coley/cafedude/classfile/behavior/CpAccessor.java new file mode 100644 index 0000000..e8338af --- /dev/null +++ b/src/main/java/me/coley/cafedude/classfile/behavior/CpAccessor.java @@ -0,0 +1,15 @@ +package me.coley.cafedude.classfile.behavior; + +import java.util.Set; + +/** + * Applied to a data type that requires access to the constant pool. + * + * @author Matt Coley + */ +public interface CpAccessor { + /** + * @return Indices accessed. + */ + Set cpAccesses(); +} diff --git a/src/main/java/me/coley/cafedude/io/AttributeReader.java b/src/main/java/me/coley/cafedude/io/AttributeReader.java index 52efd3b..a0b41f2 100644 --- a/src/main/java/me/coley/cafedude/io/AttributeReader.java +++ b/src/main/java/me/coley/cafedude/io/AttributeReader.java @@ -110,15 +110,16 @@ public Attribute readAttribute(AttributeContext context) throws IOException { int read = is.getIndex(); if (read != expectedContentLength) { String name = ((CpUtf8) builder.getPool().get(nameIndex)).getText(); - logger.debug("Invalid '{}', claimed to be {} bytes, but was {}", name, expectedContentLength, read); + logger.debug("Invalid '{}' on {}, claimed to be {} bytes, but was {}", + name, context.name(), expectedContentLength, read); return null; } return attribute; } catch (IOException ex) { if (reader.doDropEofAttributes()) { String name = ((CpUtf8) builder.getPool().get(nameIndex)).getText(); - logger.debug("Invalid '{}', EOF thrown when parsing attribute, expected {} bytes", - name, expectedContentLength); + logger.debug("Invalid '{}' on {}, EOF thrown when parsing attribute, expected {} bytes", + name, context.name(), expectedContentLength); return null; } else throw ex; @@ -132,8 +133,8 @@ private Attribute read(AttributeContext context) throws IOException { if (reader.doDropForwardVersioned()) { int introducedAt = AttributeVersions.getIntroducedVersion(name); if (introducedAt > builder.getVersionMajor()) { - logger.debug("Found '{}' in class version {}, min supported is {}", - name, builder.getVersionMajor(), introducedAt); + logger.debug("Found '{}' on {} in class version {}, min supported is {}", + name, context.name(), builder.getVersionMajor(), introducedAt); return null; } } @@ -229,7 +230,8 @@ private RecordAttribute readRecord() throws IOException { List attributes = new ArrayList<>(); for (int x = 0; x < numAttributes; x++) { Attribute attr = new AttributeReader(reader, builder, is).readAttribute(AttributeContext.ATTRIBUTE); - attributes.add(attr); + if (attr != null) + attributes.add(attr); } components.add(new RecordComponent(nameIndex, descIndex, attributes)); } @@ -610,7 +612,8 @@ private CodeAttribute readCode() throws IOException { int numAttributes = is.readUnsignedShort(); for (int i = 0; i < numAttributes; i++) { Attribute attr = new AttributeReader(reader, builder, is).readAttribute(AttributeContext.ATTRIBUTE); - attributes.add(attr); + if (attr != null) + attributes.add(attr); } return new CodeAttribute(nameIndex, maxStack, maxLocals, code, exceptions, attributes); } diff --git a/src/main/java/me/coley/cafedude/transform/IllegalStrippingTransformer.java b/src/main/java/me/coley/cafedude/transform/IllegalStrippingTransformer.java index 750dec0..2305164 100644 --- a/src/main/java/me/coley/cafedude/transform/IllegalStrippingTransformer.java +++ b/src/main/java/me/coley/cafedude/transform/IllegalStrippingTransformer.java @@ -47,6 +47,7 @@ import me.coley.cafedude.classfile.behavior.AttributeHolder; import me.coley.cafedude.classfile.constant.ConstPoolEntry; import me.coley.cafedude.classfile.constant.CpClass; +import me.coley.cafedude.classfile.constant.CpInt; import me.coley.cafedude.classfile.constant.CpUtf8; import me.coley.cafedude.io.AttributeContext; import org.slf4j.Logger; @@ -56,6 +57,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Predicate; import static me.coley.cafedude.Constants.Attributes.*; @@ -67,8 +69,6 @@ */ public class IllegalStrippingTransformer extends Transformer { private static final Logger logger = LoggerFactory.getLogger(IllegalStrippingTransformer.class); - private final Map> expectedTypeMasks = new HashMap<>(); - private final Map> cpEntryValidators = new HashMap<>(); /** * @param clazz @@ -80,11 +80,34 @@ public IllegalStrippingTransformer(ClassFile clazz) { @Override public void transform() { + // Record existing CP refs + Set cpAccesses = clazz.cpAccesses(); + // Strip attributes that are not valid clazz.getAttributes().removeIf(attribute -> !isValidWrapped(clazz, attribute)); for (Field field : clazz.getFields()) field.getAttributes().removeIf(attribute -> !isValidWrapped(field, attribute)); for (Method method : clazz.getMethods()) method.getAttributes().removeIf(attribute -> !isValidWrapped(method, attribute)); + // Record filtered CP refs, the difference of the sets are the indices that were referenced + // by removed attributes/data. + Set filteredCpAccesses = clazz.cpAccesses(); + cpAccesses.removeAll(filteredCpAccesses); + // Replace with dummy entries (Removing and re-indexing things would be computationally expensive) + int max = pool.size(); + for (int index : cpAccesses) { + if (index == 0 || index >= max - 1) + continue; + ConstPoolEntry cpe = pool.get(index); + switch (cpe.getTag()) { + case ConstantPool.DYNAMIC: + case ConstantPool.INVOKE_DYNAMIC: + logger.debug("Removing now unused CP entry: {}={}", index, cpe.getClass().getSimpleName()); + pool.set(index, new CpInt(0)); + break; + default: + break; + } + } } private boolean isValidWrapped(AttributeHolder holder, Attribute attribute) { @@ -99,6 +122,8 @@ private boolean isValidWrapped(AttributeHolder holder, Attribute attribute) { } private boolean isValid(AttributeHolder holder, Attribute attribute) { + Map> expectedTypeMasks = new HashMap<>(); + Map> cpEntryValidators = new HashMap<>(); // Check name index int maxCpIndex = pool.size(); if (attribute.getNameIndex() > maxCpIndex) @@ -129,7 +154,7 @@ private boolean isValid(AttributeHolder holder, Attribute attribute) { case RUNTIME_VISIBLE_TYPE_ANNOTATIONS: AnnotationsAttribute annotations = (AnnotationsAttribute) attribute; for (Annotation anno : annotations.getAnnotations()) - addAnnotationValidation(anno); + addAnnotationValidation(expectedTypeMasks, cpEntryValidators, anno); break; case RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS: case RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS: { @@ -151,13 +176,13 @@ private boolean isValid(AttributeHolder holder, Attribute attribute) { Collection> parameterAnnos = paramAnnotations.getParameterAnnotations().values(); for (List annotationList : parameterAnnos) for (Annotation anno : annotationList) - addAnnotationValidation(anno); + addAnnotationValidation(expectedTypeMasks, cpEntryValidators, anno); break; } case ANNOTATION_DEFAULT: AnnotationDefaultAttribute annotationDefault = (AnnotationDefaultAttribute) attribute; ElementValue elementValue = annotationDefault.getElementValue(); - addElementValueValidation(elementValue); + addElementValueValidation(expectedTypeMasks, cpEntryValidators, elementValue); break; case NEST_HOST: NestHostAttribute nestHost = (NestHostAttribute) attribute; @@ -215,7 +240,8 @@ private boolean isValid(AttributeHolder holder, Attribute attribute) { CodeAttribute code = (CodeAttribute) attribute; for (Attribute subAttr : code.getAttributes()) { if (!isValid(code, subAttr)) { - logger.info("Attribute '{}' will be removed since a sub-attribute is not legal!", name); + logger.info("Attribute '{}' on {} will be removed since a sub-attribute is not legal!", + name, "CODE-ATTRIBUTE"); return false; } } @@ -340,7 +366,8 @@ private boolean isValid(AttributeHolder holder, Attribute attribute) { // - Yes, the CP doesn't start at 0, but there are special cases where it is allowed. int cpIndex = entry.getKey(); if (cpIndex < min || cpIndex > maxCpIndex) { - logger.debug("Invalid '{}' attribute, contains CP reference to index out of CP bounds!", name); + logger.debug("Invalid '{}' attribute on {}, contains CP reference to index out of CP bounds!", + name, context.name()); return false; } // Referenced entry must match type @@ -349,12 +376,13 @@ private boolean isValid(AttributeHolder holder, Attribute attribute) { // cpEntryValidators ConstPoolEntry cpEntry = pool.get(cpIndex); if (cpEntry == null) { - logger.debug("No CP entry at index '{}' in Attribute '{}'", cpIndex, name); + logger.debug("No CP entry at index '{}' in Attribute '{}' on {}", cpIndex, name, context.name()); return false; } int tag = cpEntry.getTag(); if (!entry.getValue().test(tag)) { - logger.debug("Invalid '{}' attribute, contains CP reference to index with wrong type!", name); + logger.debug("Invalid '{}' attribute on {}, contains CP reference to index with wrong type!", + name, context.name()); return false; } if (cpEntryValidators.containsKey(cpIndex) && !cpEntryValidators.get(cpIndex).test(cpEntry)) { @@ -366,18 +394,22 @@ private boolean isValid(AttributeHolder holder, Attribute attribute) { return true; } - private void addAnnotationValidation(Annotation anno) { + private void addAnnotationValidation(Map> expectedTypeMasks, + Map> cpEntryValidators, + Annotation anno) { expectedTypeMasks.put(anno.getTypeIndex(), i -> i == ConstantPool.UTF8); cpEntryValidators.put(anno.getTypeIndex(), matchUtf8ClassType()); for (Map.Entry entry : anno.getValues().entrySet()) { int elementTypeIndex = entry.getKey(); expectedTypeMasks.put(elementTypeIndex, i -> i == ConstantPool.UTF8); cpEntryValidators.put(elementTypeIndex, matchUtf8ClassType()); - addElementValueValidation(entry.getValue()); + addElementValueValidation(expectedTypeMasks, cpEntryValidators, entry.getValue()); } } - private void addElementValueValidation(ElementValue elementValue) { + private void addElementValueValidation(Map> expectedTypeMasks, + Map> cpEntryValidators, + ElementValue elementValue) { if (elementValue instanceof ClassElementValue) { int classIndex = ((ClassElementValue) elementValue).getClassIndex(); expectedTypeMasks.put(classIndex, i -> i == ConstantPool.CLASS); diff --git a/src/test/resources/samples/crasher/sample24.class b/src/test/resources/samples/crasher/sample24.class new file mode 100644 index 0000000..cf6a7a7 Binary files /dev/null and b/src/test/resources/samples/crasher/sample24.class differ diff --git a/src/test/resources/samples/crasher/sample25.class b/src/test/resources/samples/crasher/sample25.class new file mode 100644 index 0000000..9c72fa9 Binary files /dev/null and b/src/test/resources/samples/crasher/sample25.class differ diff --git a/src/test/resources/samples/crasher/sample26.class b/src/test/resources/samples/crasher/sample26.class new file mode 100644 index 0000000..867df1f Binary files /dev/null and b/src/test/resources/samples/crasher/sample26.class differ diff --git a/src/test/resources/samples/crasher/sample27.class b/src/test/resources/samples/crasher/sample27.class new file mode 100644 index 0000000..ec74f16 Binary files /dev/null and b/src/test/resources/samples/crasher/sample27.class differ