diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/InstructionUnwrapper.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/InstructionUnwrapper.java new file mode 100644 index 0000000..8d4d376 --- /dev/null +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/InstructionUnwrapper.java @@ -0,0 +1,102 @@ +package io.papermc.codebook.lvt; + +import com.google.inject.Injector; +import io.papermc.codebook.report.ReportType; +import io.papermc.codebook.report.Reports; +import io.papermc.codebook.report.type.CheckCastWraps; +import java.util.Set; +import java.util.stream.Collectors; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.VarInsnNode; + +public class InstructionUnwrapper { + + private final Reports reports; + private final Injector reportsInjector; + + private static final MethodMatcher BOX_METHODS = new MethodMatcher(Set.of( + new Method("java/lang/Byte", "byteValue", "()B"), + new Method("java/lang/Short", "shortValue", "()S"), + new Method("java/lang/Integer", "intValue", "()I"), + new Method("java/lang/Long", "longValue", "()J"), + new Method("java/lang/Float", "floatValue", "()F"), + new Method("java/lang/Double", "doubleValue", "()D"), + new Method("java/lang/Boolean", "booleanValue", "()Z"), + new Method("java/lang/Character", "charValue", "()C"))); + + private static final MethodMatcher UNWRAP_AFTER_CAST = new MethodMatcher(Set.of( + new Method("net/minecraft/world/level/block/state/BlockState", "getValue", "(Lnet/minecraft/world/level/block/state/properties/Property;)Ljava/lang/Comparable;"), + new Method("net/minecraft/world/level/storage/loot/LootContext", "getParamOrNull", "(Lnet/minecraft/world/level/storage/loot/parameters/LootContextParam;)Ljava/lang/Object;"), + new Method("net/minecraft/world/level/storage/loot/LootParams$Builder", "getOptionalParameter", "(Lnet/minecraft/world/level/storage/loot/parameters/LootContextParam;)Ljava/lang/Object;") + )); + + public InstructionUnwrapper(final Reports reports, final Injector reportsInjector) { + this.reports = reports; + this.reportsInjector = reportsInjector; + } + + public @Nullable AbstractInsnNode unwrapFromAssignment(final VarInsnNode assignment) { + @Nullable AbstractInsnNode prev = assignment.getPrevious(); + if (prev == null) { + return null; + } + + // unwrap unboxing methods and the subsequent checkcast to the boxed type + if (prev.getOpcode() == Opcodes.INVOKEVIRTUAL && BOX_METHODS.matches(prev)) { + prev = prev.getPrevious(); + if (prev != null && prev.getOpcode() == Opcodes.CHECKCAST) { + prev = prev.getPrevious(); + } + } + if (prev == null) { + return null; + } + + + if (prev.getOpcode() == Opcodes.CHECKCAST) { + final AbstractInsnNode tempPrev = prev.getPrevious(); + if (tempPrev.getOpcode() == Opcodes.INVOKEVIRTUAL || tempPrev.getOpcode() == Opcodes.INVOKEINTERFACE || tempPrev.getOpcode() == Opcodes.INVOKESTATIC) { + final MethodInsnNode methodInsn = (MethodInsnNode) tempPrev; + if (UNWRAP_AFTER_CAST.matches(methodInsn)) { + prev = methodInsn; + } else { + if (this.reports.shouldGenerate(ReportType.CHECK_CAST_WRAPS)) { + this.reportsInjector.getInstance(CheckCastWraps.class).report(methodInsn); + } + return null; + } + } + } + + return prev; + } + + private record MethodMatcher(Set methods, Set methodNames) { + + private MethodMatcher(final Set methods) { + this(methods, methods.stream().map(Method::name).collect(Collectors.toUnmodifiableSet())); + } + + boolean matches(final AbstractInsnNode insn) { + return insn instanceof final MethodInsnNode methodInsnNode && this.methodNames.contains(methodInsnNode.name) && this.methods.stream().anyMatch(m -> m.matches(methodInsnNode)); + } + + } + + private record Method(String owner, String name, String desc, boolean itf) { + + private Method(final String owner, final String name, final String desc) { + this(owner, name, desc, false); + } + + boolean matches(final MethodInsnNode insn) { + return this.owner.equals(insn.owner) + && this.name.equals(insn.name) + && this.desc.equals(insn.desc) + && this.itf == insn.itf; + } + } +} diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/LvtNamer.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/LvtNamer.java index 083ae87..b763299 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/LvtNamer.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/LvtNamer.java @@ -70,7 +70,7 @@ public LvtNamer(final HypoContext context, final MappingSet mappings, final Repo this.lvtTypeSuggester = new LvtTypeSuggester(context); this.reports = reports; this.reportsInjector = Guice.createInjector(reports); - this.lvtAssignSuggester = new RootLvtSuggester(context, this.lvtTypeSuggester, this.reportsInjector); + this.lvtAssignSuggester = new RootLvtSuggester(context, this.lvtTypeSuggester, this.reports, this.reportsInjector); } public void processClass(final AsmClassData classData) throws IOException { diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/RootLvtSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/RootLvtSuggester.java index 7589430..3ea9feb 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/RootLvtSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/RootLvtSuggester.java @@ -44,18 +44,20 @@ import io.papermc.codebook.lvt.suggestion.SingleVerbSuggester; import io.papermc.codebook.lvt.suggestion.StringSuggester; import io.papermc.codebook.lvt.suggestion.VerbPrefixBooleanSuggester; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.field.FieldCallContext; import io.papermc.codebook.lvt.suggestion.context.field.FieldInsnContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import io.papermc.codebook.lvt.suggestion.numbers.MthRandomSuggester; import io.papermc.codebook.lvt.suggestion.numbers.RandomSourceSuggester; +import io.papermc.codebook.report.Reports; import io.papermc.codebook.report.type.MissingMethodLvtSuggestion; import java.io.IOException; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.Nullable; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; @@ -85,16 +87,18 @@ public final class RootLvtSuggester extends AbstractModule implements LvtSuggest GenericSuggester.class); private final HypoContext hypoContext; - private final LvtTypeSuggester lvtTypeSuggester; + final LvtTypeSuggester lvtTypeSuggester; private final Injector injector; private final List suggesters; + private final InstructionUnwrapper unwrapper; public RootLvtSuggester( - final HypoContext hypoContext, final LvtTypeSuggester lvtTypeSuggester, final Injector reports) { + final HypoContext hypoContext, final LvtTypeSuggester lvtTypeSuggester, final Reports reports, final Injector reportsInjector) { this.hypoContext = hypoContext; this.lvtTypeSuggester = lvtTypeSuggester; - this.injector = reports.createChildInjector(this); + this.injector = reportsInjector.createChildInjector(this); this.suggesters = SUGGESTERS.stream().map(this.injector::getInstance).toList(); + this.unwrapper = new InstructionUnwrapper(reports, reportsInjector); } @Override @@ -139,7 +143,7 @@ public String suggestName( } if (assignmentNode != null) { - final @Nullable String suggestedName = this.suggestNameFromFirstAssignment(parent, assignmentNode); + final @Nullable String suggestedName = this.suggestNameFromFirstAssignment(ContainerContext.from(parent), new AssignmentContext(assignmentNode, lvt)); if (suggestedName != null) { return determineFinalName(suggestedName, scopedNames); } @@ -172,50 +176,9 @@ public static String determineFinalName(final String suggestedName, final Set BOX_METHODS = Set.of( - new BoxMethod("java/lang/Byte", "byteValue", "()B"), - new BoxMethod("java/lang/Short", "shortValue", "()S"), - new BoxMethod("java/lang/Integer", "intValue", "()I"), - new BoxMethod("java/lang/Long", "longValue", "()J"), - new BoxMethod("java/lang/Float", "floatValue", "()F"), - new BoxMethod("java/lang/Double", "doubleValue", "()D"), - new BoxMethod("java/lang/Boolean", "booleanValue", "()Z"), - new BoxMethod("java/lang/Character", "charValue", "()C")); - private static final Set BOX_METHOD_NAMES = - BOX_METHODS.stream().map(BoxMethod::name).collect(Collectors.toUnmodifiableSet()); - - private record BoxMethod(String owner, String name, String desc) { - boolean is(final MethodInsnNode node) { - return this.owner.equals(node.owner) - && this.name.equals(node.name) - && this.desc.equals(node.desc) - && !node.itf; - } - } - - private @Nullable AbstractInsnNode walkBack(final VarInsnNode assignmentNode) { - AbstractInsnNode prev = assignmentNode.getPrevious(); - if (prev != null) { - final int op = prev.getOpcode(); - if (op == Opcodes.INVOKEVIRTUAL) { - final MethodInsnNode methodInsnNode = (MethodInsnNode) prev; - if (BOX_METHOD_NAMES.contains(methodInsnNode.name) - && BOX_METHODS.stream().anyMatch(bm -> bm.is(methodInsnNode))) { - prev = prev.getPrevious(); - if (prev != null && prev.getOpcode() == Opcodes.CHECKCAST) { - return prev.getPrevious(); - } - return prev; - } - } - return prev; - } - return null; - } - - private @Nullable String suggestNameFromFirstAssignment(final MethodData parent, final VarInsnNode varInsn) + private @Nullable String suggestNameFromFirstAssignment(final ContainerContext container, final AssignmentContext assignment) throws IOException { - final @Nullable AbstractInsnNode prev = this.walkBack(varInsn); + final @Nullable AbstractInsnNode prev = this.unwrapper.unwrapFromAssignment(assignment.assignmentNode()); if (prev == null) { return null; } @@ -236,36 +199,39 @@ boolean is(final MethodInsnNode node) { return null; } - return this.suggestFromMethod( + final @Nullable String suggestion = this.suggestFromMethod( MethodCallContext.create(method), MethodInsnContext.create(owner, methodInsnNode), - ContainerContext.from(parent)); + container, assignment, new SuggesterContext(this.hypoContext, this.lvtTypeSuggester)); + if (suggestion == null) { + this.injector + .getInstance(MissingMethodLvtSuggestion.class) + .reportMissingMethodLvtSuggestion(method, methodInsnNode); + } + return suggestion; } @Override public @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) throws IOException { @Nullable String suggestion; for (final LvtSuggester delegate : this.suggesters) { - suggestion = delegate.suggestFromMethod(call, insn, container); + suggestion = delegate.suggestFromMethod(call, insn, container, assignment, suggester); if (suggestion != null) { return suggestion; } } - this.injector - .getInstance(MissingMethodLvtSuggestion.class) - .reportMissingMethodLvtSuggestion(call.data(), insn.node()); return null; } @Override public @Nullable String suggestFromField( - final FieldCallContext call, final FieldInsnContext insn, final ContainerContext container) + final FieldCallContext call, final FieldInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) throws IOException { @Nullable String suggestion; for (final LvtSuggester delegate : this.suggesters) { - suggestion = delegate.suggestFromField(call, insn, container); + suggestion = delegate.suggestFromField(call, insn, container, assignment, suggester); if (suggestion != null) { return suggestion; } diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/ComplexGetSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/ComplexGetSuggester.java index a1ef077..5671c66 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/ComplexGetSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/ComplexGetSuggester.java @@ -23,8 +23,11 @@ package io.papermc.codebook.lvt.suggestion; import static io.papermc.codebook.lvt.LvtUtil.staticFinalFieldNameToLocalName; +import static io.papermc.codebook.lvt.LvtUtil.toJvmType; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import java.io.IOException; @@ -49,16 +52,36 @@ public class ComplexGetSuggester implements LvtSuggester { "getValue", "(Lnet/minecraft/world/level/block/state/properties/Property;)Ljava/lang/Comparable;")), Set.of( "Lnet/minecraft/world/level/block/state/properties/IntegerProperty;", - "Lnet/minecraft/world/level/block/state/properties/BooleanProperty;"), + "Lnet/minecraft/world/level/block/state/properties/BooleanProperty;", + "Lnet/minecraft/world/level/block/state/properties/EnumProperty;", + "Lnet/minecraft/world/level/block/state/properties/DirectionProperty;"), "Value"); + private static final StaticFieldEntry LOOT_CONTEXT_PARAM = new StaticFieldEntry( + Set.of( + "net/minecraft/world/level/storage/loot/LootContext", + "net/minecraft/world/level/storage/loot/LootParams$Builder" + ), + Set.of( + Map.entry("getParamOrNull", "(Lnet/minecraft/world/level/storage/loot/parameters/LootContextParam;)Ljava/lang/Object;"), + Map.entry("getOptionalParameter", "(Lnet/minecraft/world/level/storage/loot/parameters/LootContextParam;)Ljava/lang/Object;") + ), + Set.of( + "Lnet/minecraft/world/level/storage/loot/parameters/LootContextParam;" + ), + "Param" + ); + @Override public @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) throws IOException { final MethodInsnNode node = insn.node(); if (BLOCK_STATE_PROPERTY.test(node)) { - return BLOCK_STATE_PROPERTY.transform(node); + return BLOCK_STATE_PROPERTY.transform(node, assignment, suggester); + } + if (LOOT_CONTEXT_PARAM.test(node)) { + return LOOT_CONTEXT_PARAM.transform(node, assignment, suggester); } return null; } @@ -67,21 +90,25 @@ private record StaticFieldEntry( Set owners, Set> methods, Set fieldTypes, @Nullable String suffix) { boolean test(final MethodInsnNode node) { - return this.owners.contains(node.owner) - && this.methods.stream() - .anyMatch(e -> - e.getKey().equals(node.name) && e.getValue().equals(node.desc)); + return matches(this.owners, this.methods, node); } - @Nullable - String transform(final MethodInsnNode node) { + String transform(final MethodInsnNode node, final AssignmentContext assignment, final SuggesterContext suggester) throws IOException { final AbstractInsnNode prev = node.getPrevious(); if (prev instanceof final FieldInsnNode fieldInsnNode && fieldInsnNode.getOpcode() == Opcodes.GETSTATIC && this.fieldTypes.contains(fieldInsnNode.desc)) { return staticFinalFieldNameToLocalName(fieldInsnNode.name) + (this.suffix == null ? "" : this.suffix); } - return null; + // always use the type instead of any other suggesters + return suggester.typeSuggester().suggestNameFromType(toJvmType(assignment.lvt().desc)) + (this.suffix == null ? "" : this.suffix); } } + + private static boolean matches(final Set owners, final Set> methods, final MethodInsnNode node) { + return owners.contains(node.owner) + && methods.stream() + .anyMatch(e -> + e.getKey().equals(node.name) && e.getValue().equals(node.desc)); + } } diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/FluentGetterSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/FluentGetterSuggester.java index 097eeec..7bf4958 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/FluentGetterSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/FluentGetterSuggester.java @@ -23,7 +23,9 @@ package io.papermc.codebook.lvt.suggestion; import dev.denwav.hypo.model.data.types.PrimitiveType; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import java.io.IOException; @@ -55,7 +57,7 @@ public class FluentGetterSuggester implements LvtSuggester { @Override public @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) throws IOException { // I think it's best to only work with primitive types here, as other types should already have names // and this dramatically cuts down on the number of methods analyzed because we aren't filtering by diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/GenericSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/GenericSuggester.java index 8678dc9..95238a1 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/GenericSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/GenericSuggester.java @@ -22,7 +22,9 @@ package io.papermc.codebook.lvt.suggestion; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import org.checkerframework.checker.nullness.qual.Nullable; @@ -31,7 +33,7 @@ public class GenericSuggester implements LvtSuggester { @Override public @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) { + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) { return switch (call.data().name()) { case "hashCode" -> "hashCode"; case "size" -> "size"; diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/LvtSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/LvtSuggester.java index f0720f3..b454108 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/LvtSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/LvtSuggester.java @@ -22,7 +22,9 @@ package io.papermc.codebook.lvt.suggestion; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.field.FieldCallContext; import io.papermc.codebook.lvt.suggestion.context.field.FieldInsnContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; @@ -33,13 +35,13 @@ public interface LvtSuggester { default @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) throws IOException { return null; } default @Nullable String suggestFromField( - final FieldCallContext call, final FieldInsnContext insn, final ContainerContext container) + final FieldCallContext call, final FieldInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) throws IOException { return null; } diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/MathSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/MathSuggester.java index 19817d9..a752afc 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/MathSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/MathSuggester.java @@ -22,7 +22,9 @@ package io.papermc.codebook.lvt.suggestion; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import java.io.IOException; @@ -32,7 +34,7 @@ public class MathSuggester implements LvtSuggester { @Override public @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) throws IOException { final String methodName = call.data().name(); diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/NewPrefixSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/NewPrefixSuggester.java index 2d5e93f..030e31a 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/NewPrefixSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/NewPrefixSuggester.java @@ -25,7 +25,9 @@ import static io.papermc.codebook.lvt.LvtUtil.hasPrefix; import static io.papermc.codebook.lvt.LvtUtil.parseSimpleTypeNameFromMethod; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import java.io.IOException; @@ -35,7 +37,7 @@ public class NewPrefixSuggester implements LvtSuggester { @Override public @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) throws IOException { final String methodName = call.data().name(); if (!hasPrefix(methodName, "new")) { diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/PositionsSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/PositionsSuggester.java index 35d7db4..1e0f387 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/PositionsSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/PositionsSuggester.java @@ -29,7 +29,9 @@ import dev.denwav.hypo.model.data.MethodData; import dev.denwav.hypo.model.data.types.PrimitiveType; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import java.io.IOException; @@ -52,7 +54,7 @@ public class PositionsSuggester implements LvtSuggester { @Override public @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) throws IOException { if ("net/minecraft/core/SectionPos".equals(insn.owner().name())) { return suggestNameForSectionPos(container.node(), call.data(), insn.node()); diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/RecordComponentSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/RecordComponentSuggester.java index 5e4996e..9f74135 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/RecordComponentSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/RecordComponentSuggester.java @@ -24,7 +24,9 @@ import dev.denwav.hypo.model.data.ClassKind; import dev.denwav.hypo.model.data.FieldData; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import java.util.List; @@ -34,7 +36,7 @@ public class RecordComponentSuggester implements LvtSuggester { @Override public @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) { + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) { if (insn.owner().is(ClassKind.RECORD)) { return null; } diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/SingleVerbBooleanSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/SingleVerbBooleanSuggester.java index 21d23e8..92ca81a 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/SingleVerbBooleanSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/SingleVerbBooleanSuggester.java @@ -31,7 +31,9 @@ import dev.denwav.hypo.model.data.types.JvmType; import dev.denwav.hypo.model.data.types.PrimitiveType; import io.papermc.codebook.lvt.LvtTypeSuggester; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import jakarta.inject.Inject; @@ -57,7 +59,7 @@ public class SingleVerbBooleanSuggester implements LvtSuggester { @Override public @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) throws IOException { if (call.data().returnType() != PrimitiveType.BOOLEAN) { return null; diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/SingleVerbSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/SingleVerbSuggester.java index 6448fc0..4b59d9d 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/SingleVerbSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/SingleVerbSuggester.java @@ -25,7 +25,9 @@ import static io.papermc.codebook.lvt.LvtUtil.parseSimpleTypeNameFromMethod; import static io.papermc.codebook.lvt.LvtUtil.tryMatchPrefix; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import java.util.List; @@ -44,7 +46,7 @@ public class SingleVerbSuggester implements LvtSuggester { @Override public @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) { + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) { final String methodName = call.data().name(); final @Nullable String prefix = tryMatchPrefix(methodName, SINGLE_VERB_PREFIXES); diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/StringSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/StringSuggester.java index 22f8123..b231b7f 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/StringSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/StringSuggester.java @@ -22,7 +22,9 @@ package io.papermc.codebook.lvt.suggestion; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import java.io.IOException; @@ -32,7 +34,7 @@ public class StringSuggester implements LvtSuggester { @Override public @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) throws IOException { final String methodName = call.data().name(); diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/VerbPrefixBooleanSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/VerbPrefixBooleanSuggester.java index 94f00a2..2c7b3e6 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/VerbPrefixBooleanSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/VerbPrefixBooleanSuggester.java @@ -25,7 +25,9 @@ import static io.papermc.codebook.lvt.LvtUtil.tryMatchPrefix; import dev.denwav.hypo.model.data.types.PrimitiveType; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import java.util.List; @@ -41,7 +43,7 @@ public class VerbPrefixBooleanSuggester implements LvtSuggester { @Override public @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) { + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) { if (call.data().returnType() != PrimitiveType.BOOLEAN) { return null; } diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/context/AssignmentContext.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/context/AssignmentContext.java new file mode 100644 index 0000000..511f881 --- /dev/null +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/context/AssignmentContext.java @@ -0,0 +1,7 @@ +package io.papermc.codebook.lvt.suggestion.context; + +import org.objectweb.asm.tree.LocalVariableNode; +import org.objectweb.asm.tree.VarInsnNode; + +public record AssignmentContext(VarInsnNode assignmentNode, LocalVariableNode lvt) { +} diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/context/SuggesterContext.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/context/SuggesterContext.java new file mode 100644 index 0000000..c2e3195 --- /dev/null +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/context/SuggesterContext.java @@ -0,0 +1,7 @@ +package io.papermc.codebook.lvt.suggestion.context; + +import dev.denwav.hypo.core.HypoContext; +import io.papermc.codebook.lvt.LvtTypeSuggester; + +public record SuggesterContext(HypoContext hypoContext, LvtTypeSuggester typeSuggester) { +} diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/numbers/MthRandomSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/numbers/MthRandomSuggester.java index c745566..25f3820 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/numbers/MthRandomSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/numbers/MthRandomSuggester.java @@ -27,7 +27,9 @@ import dev.denwav.hypo.model.data.types.JvmType; import io.papermc.codebook.lvt.suggestion.LvtSuggester; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import java.util.List; @@ -40,7 +42,7 @@ public class MthRandomSuggester implements LvtSuggester { @Override public @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) { + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) { final String methodName = call.data().name(); if (!insn.ownerEqualTo(MTH_NAME)) { return null; diff --git a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/numbers/RandomSourceSuggester.java b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/numbers/RandomSourceSuggester.java index cb8f33e..b001fec 100644 --- a/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/numbers/RandomSourceSuggester.java +++ b/codebook-lvt/src/main/java/io/papermc/codebook/lvt/suggestion/numbers/RandomSourceSuggester.java @@ -29,7 +29,9 @@ import dev.denwav.hypo.model.data.types.ClassType; import dev.denwav.hypo.model.data.types.JvmType; import io.papermc.codebook.lvt.suggestion.LvtSuggester; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import jakarta.inject.Inject; @@ -53,7 +55,7 @@ public class RandomSourceSuggester implements LvtSuggester { @Override public @Nullable String suggestFromMethod( - final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container) { + final MethodCallContext call, final MethodInsnContext insn, final ContainerContext container, final AssignmentContext assignment, final SuggesterContext suggester) { if (this.randomSourceClass == null) { return null; } diff --git a/codebook-reports/src/main/java/io/papermc/codebook/report/ReportType.java b/codebook-reports/src/main/java/io/papermc/codebook/report/ReportType.java index 52ac12f..02673d0 100644 --- a/codebook-reports/src/main/java/io/papermc/codebook/report/ReportType.java +++ b/codebook-reports/src/main/java/io/papermc/codebook/report/ReportType.java @@ -24,5 +24,7 @@ public enum ReportType { MISSING_METHOD_LVT_SUGGESTION, - MISSING_METHOD_PARAM; + MISSING_METHOD_PARAM, + CHECK_CAST_WRAPS + ; } diff --git a/codebook-reports/src/main/java/io/papermc/codebook/report/Reports.java b/codebook-reports/src/main/java/io/papermc/codebook/report/Reports.java index 9ff4cb6..4d34f8e 100644 --- a/codebook-reports/src/main/java/io/papermc/codebook/report/Reports.java +++ b/codebook-reports/src/main/java/io/papermc/codebook/report/Reports.java @@ -23,6 +23,7 @@ package io.papermc.codebook.report; import com.google.inject.AbstractModule; +import io.papermc.codebook.report.type.CheckCastWraps; import io.papermc.codebook.report.type.MissingMethodLvtSuggestion; import io.papermc.codebook.report.type.MissingMethodParam; import io.papermc.codebook.report.type.Report; @@ -58,7 +59,9 @@ public Reports(final Path reportsDir, final Set typesToGenerate) { this.typesToGenerate = typesToGenerate; this.reports = Map.of( ReportType.MISSING_METHOD_LVT_SUGGESTION, new MissingMethodLvtSuggestion(), - ReportType.MISSING_METHOD_PARAM, new MissingMethodParam()); + ReportType.MISSING_METHOD_PARAM, new MissingMethodParam(), + ReportType.CHECK_CAST_WRAPS, new CheckCastWraps() + ); } public void generateReports() throws IOException { diff --git a/codebook-reports/src/main/java/io/papermc/codebook/report/type/CheckCastWraps.java b/codebook-reports/src/main/java/io/papermc/codebook/report/type/CheckCastWraps.java new file mode 100644 index 0000000..b398a77 --- /dev/null +++ b/codebook-reports/src/main/java/io/papermc/codebook/report/type/CheckCastWraps.java @@ -0,0 +1,30 @@ +package io.papermc.codebook.report.type; + +import java.util.Comparator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.objectweb.asm.tree.MethodInsnNode; + +public class CheckCastWraps implements Report { + + private final Map cache = new ConcurrentHashMap<>(); + + @Override + public String generate() { + final StringBuilder sb = new StringBuilder(); + this.cache.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).forEachOrdered(entry -> { + sb.append(entry.getKey().className).append("#").append(entry.getKey().methodName).append(" ") + .append(entry.getKey().descriptor).append(" ").append(entry.getKey().itf).append(" ") + .append(entry.getValue()).append("\n"); + }); + return sb.toString(); + } + + public void report(final MethodInsnNode insn) { + final var key = new CacheKey(insn.owner, insn.name, insn.desc, insn.itf); + this.cache.compute(key, (k, v) -> v == null ? 1 : v + 1); + } + + private record CacheKey(String className, String methodName, String descriptor, boolean itf) { + } +} diff --git a/src/test/java/io/papermc/codebook/lvt/LvtAssignmentSuggesterTest.java b/src/test/java/io/papermc/codebook/lvt/LvtAssignmentSuggesterTest.java index eb9ea4c..7ae4d67 100644 --- a/src/test/java/io/papermc/codebook/lvt/LvtAssignmentSuggesterTest.java +++ b/src/test/java/io/papermc/codebook/lvt/LvtAssignmentSuggesterTest.java @@ -40,7 +40,9 @@ import dev.denwav.hypo.model.data.MethodDescriptor; import dev.denwav.hypo.model.data.types.ClassType; import dev.denwav.hypo.model.data.types.JvmType; +import io.papermc.codebook.lvt.suggestion.context.AssignmentContext; import io.papermc.codebook.lvt.suggestion.context.ContainerContext; +import io.papermc.codebook.lvt.suggestion.context.SuggesterContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodCallContext; import io.papermc.codebook.lvt.suggestion.context.method.MethodInsnContext; import io.papermc.codebook.report.Reports; @@ -68,6 +70,7 @@ class LvtAssignmentSuggesterTest { static final JvmType RANDOM_SOURCE_TYPE = new ClassType("net/minecraft/util/RandomSource"); private static final MockSettings LENIENT = withSettings().strictness(Strictness.LENIENT); + private HypoContext context; private RootLvtSuggester suggester; @Mock @@ -88,9 +91,12 @@ class LvtAssignmentSuggesterTest { @Mock private Reports reports; + @Mock + private AssignmentContext assignment; + @BeforeEach void setup() throws Exception { - final HypoContext context = + this.context = HypoContext.builder().withContextProviders(this.provider).build(); when(this.provider.findClass("java/util/List")).thenReturn(this.listClass); @@ -102,7 +108,7 @@ void setup() throws Exception { when(this.randomSourceClass.name()).thenReturn(RANDOM_SOURCE_TYPE.asInternalName()); this.suggester = - new RootLvtSuggester(context, new LvtTypeSuggester(context), Guice.createInjector(this.reports)); + new RootLvtSuggester(this.context, new LvtTypeSuggester(this.context), this.reports, Guice.createInjector(this.reports)); } @ParameterizedTest @@ -142,7 +148,7 @@ void testSuggester( final MethodInsnNode insn = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, methodOwner, methodName, methodDescriptor); final @Nullable String result = this.suggester.suggestFromMethod( - MethodCallContext.create(method), MethodInsnContext.create(owner, insn), context); + MethodCallContext.create(method), MethodInsnContext.create(owner, insn), context, this.assignment, new SuggesterContext(this.context, this.suggester.lvtTypeSuggester)); assertEquals(expectedName, result); }