Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RecordsFilter should filter out accessors #1393

Merged
merged 20 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ public class RecordsTarget {
record WithoutFields() { // assertFullyCovered()
}

record WithFields( // assertPartlyCovered()
record WithFields( // assertFullyCovered()
int x // assertEmpty()
) {
}

record WithCustomMethods(int x) { // assertFullyCovered()
public int x() {
return x; // assertNotCovered()
return x; // assertEmpty()
}

public String toString() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@

import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.MethodNode;

import java.util.Arrays;
import java.util.List;

/**
* Filters methods <code>toString</code>, <code>hashCode</code> and
* <code>equals</code> that compiler generates for records.
Expand All @@ -30,7 +34,8 @@ public void filter(final MethodNode methodNode,
}
final Matcher matcher = new Matcher();
if (matcher.isEquals(methodNode) || matcher.isHashCode(methodNode)
|| matcher.isToString(methodNode)) {
|| matcher.isToString(methodNode)
|| matcher.isFieldAccessor(methodNode)) {
output.ignore(methodNode.instructions.getFirst(),
methodNode.instructions.getLast());
}
Expand Down Expand Up @@ -58,6 +63,40 @@ boolean isHashCode(final MethodNode m) {
return cursor != null;
}

public static final List<Integer> ret = Arrays.asList(Opcodes.IRETURN,
Opcodes.LRETURN, Opcodes.FRETURN, Opcodes.DRETURN,
Opcodes.ARETURN);
ice1000 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Criteria: method name == field name, only three instructions (aload0,
* getField, return), and note that this class only happens in a record,
* so it's safe to assume that this is the record field accessor
* generated. It may happen that the code is explicitly written by the
* developer and is intentionally kept the same as the default generated
* format, but that's just trivial code, and it still makes sense to
* filter them out anyway.
* <p>
* Exception: if the code is compiled within IntelliJ IDEA's Java
* instrumentation, there will be extra null-assertion instructions
* after the getField instruction. This case is <emph>ignored</emph>.
*
* @see #ret
*/
boolean isFieldAccessor(final MethodNode m) {
// No parameter
if (!m.desc.startsWith("()")) {
return false;
}
ice1000 marked this conversation as resolved.
Show resolved Hide resolved
firstIsALoad0(m);
nextIs(Opcodes.GETFIELD);
if (!(cursor instanceof FieldInsnNode))
return false;
if (!((FieldInsnNode) cursor).name.equals(m.name))
return false;
ice1000 marked this conversation as resolved.
Show resolved Hide resolved
next();
return ret.contains(cursor.getOpcode());
}

boolean isEquals(final MethodNode m) {
if (!"equals".equals(m.name)
|| !"(Ljava/lang/Object;)Z".equals(m.desc)) {
Expand Down