diff options
author | Evgeny Mandrikov <Godin@users.noreply.github.com> | 2018-02-12 13:45:50 +0100 |
---|---|---|
committer | Marc R. Hoffmann <hoffmann@mountainminds.com> | 2018-02-12 13:45:50 +0100 |
commit | 06e8f36dee7bd3f19efa91b486af78eb4d375b46 (patch) | |
tree | 15c62cfb452f076406584f23bdba61e5fc78275a /org.jacoco.core/src/org/jacoco/core/internal/analysis | |
parent | 65595d05530621a240fc7511a041e2f3909057ed (diff) | |
download | jacoco-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')
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 <init></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; } } |