aboutsummaryrefslogtreecommitdiff
path: root/org.jacoco.core/src/org/jacoco/core/internal/analysis
diff options
context:
space:
mode:
authorEvgeny Mandrikov <Godin@users.noreply.github.com>2018-02-12 13:45:50 +0100
committerMarc R. Hoffmann <hoffmann@mountainminds.com>2018-02-12 13:45:50 +0100
commit06e8f36dee7bd3f19efa91b486af78eb4d375b46 (patch)
tree15c62cfb452f076406584f23bdba61e5fc78275a /org.jacoco.core/src/org/jacoco/core/internal/analysis
parent65595d05530621a240fc7511a041e2f3909057ed (diff)
downloadjacoco-06e8f36dee7bd3f19efa91b486af78eb4d375b46.tar.gz
Add filter for empty constructor without parameters in enum (#649)
Diffstat (limited to 'org.jacoco.core/src/org/jacoco/core/internal/analysis')
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AbstractMatcher.java32
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/EnumEmptyConstructorFilter.java59
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java2
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/PrivateEmptyNoArgConstructorFilter.java27
4 files changed, 100 insertions, 20 deletions
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AbstractMatcher.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AbstractMatcher.java
index b5aea723..fcd1c888 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AbstractMatcher.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/AbstractMatcher.java
@@ -17,6 +17,7 @@ import java.util.Map;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
abstract class AbstractMatcher {
@@ -25,6 +26,35 @@ abstract class AbstractMatcher {
AbstractInsnNode cursor;
+ /**
+ * Sets {@link #cursor} to first instruction of method if it is
+ * <code>ALOAD 0</code>, otherwise sets it to <code>null</code>.
+ */
+ final void firstIsALoad0(final MethodNode methodNode) {
+ cursor = methodNode.instructions.getFirst();
+ skipNonOpcodes();
+ if (cursor.getOpcode() == Opcodes.ALOAD
+ && ((VarInsnNode) cursor).var == 0) {
+ return;
+ }
+ cursor = null;
+ }
+
+ /**
+ * Moves {@link #cursor} to next instruction if it is
+ * <code>INVOKESPECIAL &lt;init&gt;</code> with given owner and descriptor,
+ * otherwise sets it to <code>null</code>.
+ */
+ final void nextIsInvokeSuper(final String owner, final String desc) {
+ nextIs(Opcodes.INVOKESPECIAL);
+ MethodInsnNode m = (MethodInsnNode) cursor;
+ if (m != null && owner.equals(m.owner) && "<init>".equals(m.name)
+ && desc.equals(m.desc)) {
+ return;
+ }
+ cursor = null;
+ }
+
final void nextIsInvokeVirtual(final String owner, final String name) {
nextIs(Opcodes.INVOKEVIRTUAL);
if (cursor == null) {
@@ -76,7 +106,7 @@ abstract class AbstractMatcher {
skipNonOpcodes();
}
- final void skipNonOpcodes() {
+ private void skipNonOpcodes() {
while (cursor != null && (cursor.getType() == AbstractInsnNode.FRAME
|| cursor.getType() == AbstractInsnNode.LABEL
|| cursor.getType() == AbstractInsnNode.LINE)) {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/EnumEmptyConstructorFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/EnumEmptyConstructorFilter.java
new file mode 100644
index 00000000..9bdc5077
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/EnumEmptyConstructorFilter.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Evgeny Mandrikov - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.internal.analysis.filter;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * Filters empty enum constructors.
+ *
+ * Constructor of enum is invoked from static initialization block to create
+ * instance of each enum constant. So it won't be executed if number of enum
+ * constants is zero. Such enums are sometimes used as alternative to classes
+ * with static utilities and private empty constructor. Implicit constructor of
+ * enum created by compiler doesn't have a synthetic flag and refers to a line
+ * of enum definition. Therefore in order to not have partial coverage of enum
+ * definition line in enums without enum constants and similarly to
+ * {@link PrivateEmptyNoArgConstructorFilter filter of private empty
+ * constructors} - empty constructor in enums without additional parameters
+ * should be filtered out even if it is not implicit.
+ */
+public final class EnumEmptyConstructorFilter implements IFilter {
+
+ private static final String CONSTRUCTOR_NAME = "<init>";
+ private static final String CONSTRUCTOR_DESC = "(Ljava/lang/String;I)V";
+
+ public void filter(String className, String superClassName,
+ MethodNode methodNode, IFilterOutput output) {
+ if ("java/lang/Enum".equals(superClassName)
+ && CONSTRUCTOR_NAME.equals(methodNode.name)
+ && CONSTRUCTOR_DESC.equals(methodNode.desc)
+ && new Matcher().match(methodNode, superClassName)) {
+ output.ignore(methodNode.instructions.getFirst(),
+ methodNode.instructions.getLast());
+ }
+ }
+
+ private static class Matcher extends AbstractMatcher {
+ private boolean match(final MethodNode methodNode,
+ final String superClassName) {
+ firstIsALoad0(methodNode);
+ nextIs(Opcodes.ALOAD);
+ nextIs(Opcodes.ILOAD);
+ nextIsInvokeSuper(superClassName, CONSTRUCTOR_DESC);
+ nextIs(Opcodes.RETURN);
+ return cursor != null;
+ }
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java
index 0f344180..40c0dfc3 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/Filters.java
@@ -31,7 +31,7 @@ public final class Filters implements IFilter {
new TryWithResourcesJavacFilter(), new TryWithResourcesEcjFilter(),
new FinallyFilter(), new PrivateEmptyNoArgConstructorFilter(),
new StringSwitchJavacFilter(), new LombokGeneratedFilter(),
- new GroovyGeneratedFilter());
+ new GroovyGeneratedFilter(), new EnumEmptyConstructorFilter());
private final IFilter[] filters;
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/PrivateEmptyNoArgConstructorFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/PrivateEmptyNoArgConstructorFilter.java
index 08c654e3..29214c5b 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/PrivateEmptyNoArgConstructorFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/PrivateEmptyNoArgConstructorFilter.java
@@ -12,20 +12,21 @@
package org.jacoco.core.internal.analysis.filter;
import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
-import org.objectweb.asm.tree.VarInsnNode;
/**
* Filters private empty constructors that do not have arguments.
*/
public final class PrivateEmptyNoArgConstructorFilter implements IFilter {
+ private static final String CONSTRUCTOR_NAME = "<init>";
+ private static final String CONSTRUCTOR_DESC = "()V";
+
public void filter(final String className, final String superClassName,
final MethodNode methodNode, final IFilterOutput output) {
if ((methodNode.access & Opcodes.ACC_PRIVATE) != 0
- && "<init>".equals(methodNode.name)
- && "()V".equals(methodNode.desc)
+ && CONSTRUCTOR_NAME.equals(methodNode.name)
+ && CONSTRUCTOR_DESC.equals(methodNode.desc)
&& new Matcher().match(methodNode, superClassName)) {
output.ignore(methodNode.instructions.getFirst(),
methodNode.instructions.getLast());
@@ -35,20 +36,10 @@ public final class PrivateEmptyNoArgConstructorFilter implements IFilter {
private static class Matcher extends AbstractMatcher {
private boolean match(final MethodNode methodNode,
final String superClassName) {
- cursor = methodNode.instructions.getFirst();
- skipNonOpcodes();
- if (cursor.getOpcode() != Opcodes.ALOAD
- || ((VarInsnNode) cursor).var != 0) {
- return false;
- }
- nextIs(Opcodes.INVOKESPECIAL);
- MethodInsnNode m = (MethodInsnNode) cursor;
- if (m != null && superClassName.equals(m.owner)
- && "<init>".equals(m.name) && ("()V").equals(m.desc)) {
- nextIs(Opcodes.RETURN);
- return cursor != null;
- }
- return false;
+ firstIsALoad0(methodNode);
+ nextIsInvokeSuper(superClassName, CONSTRUCTOR_DESC);
+ nextIs(Opcodes.RETURN);
+ return cursor != null;
}
}