aboutsummaryrefslogtreecommitdiff
path: root/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java
diff options
context:
space:
mode:
Diffstat (limited to 'org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java')
-rw-r--r--org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java95
1 files changed, 74 insertions, 21 deletions
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java
index ef198447..b734d234 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinDefaultArgumentsFilter.java
@@ -1,9 +1,10 @@
/*******************************************************************************
- * Copyright (c) 2009, 2019 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
+ * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors
+ * This program and the accompanying materials are made available under
+ * the terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Evgeny Mandrikov - initial API and implementation
@@ -18,15 +19,16 @@ import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
+import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
/**
* Filters branches that Kotlin compiler generates for default arguments.
- *
+ *
* For each default argument Kotlin compiler generates following bytecode to
* determine if it should be used or not:
- *
+ *
* <pre>
* ILOAD maskVar
* ICONST_x, BIPUSH, SIPUSH, LDC or LDC_W
@@ -35,16 +37,30 @@ import org.objectweb.asm.tree.VarInsnNode;
* default argument
* label:
* </pre>
- *
+ *
* Where <code>maskVar</code> is penultimate argument of synthetic method with
- * suffix "$default". And its value can't be zero - invocation with all
- * arguments uses original non synthetic method, thus <code>IFEQ</code>
- * instructions should be ignored.
+ * suffix "$default" or of synthetic constructor with last argument
+ * "kotlin.jvm.internal.DefaultConstructorMarker". And its value can't be zero -
+ * invocation with all arguments uses original non synthetic method, thus
+ * <code>IFEQ</code> instructions should be ignored.
*/
public final class KotlinDefaultArgumentsFilter implements IFilter {
- static boolean isDefaultArgumentsMethodName(final String methodName) {
- return methodName.endsWith("$default");
+ static boolean isDefaultArgumentsMethod(final MethodNode methodNode) {
+ return methodNode.name.endsWith("$default");
+ }
+
+ static boolean isDefaultArgumentsConstructor(final MethodNode methodNode) {
+ if (!"<init>".equals(methodNode.name)) {
+ return false;
+ }
+ final Type[] argumentTypes = Type.getMethodType(methodNode.desc)
+ .getArgumentTypes();
+ if (argumentTypes.length < 2) {
+ return false;
+ }
+ return "kotlin.jvm.internal.DefaultConstructorMarker"
+ .equals(argumentTypes[argumentTypes.length - 1].getClassName());
}
public void filter(final MethodNode methodNode,
@@ -52,24 +68,45 @@ public final class KotlinDefaultArgumentsFilter implements IFilter {
if ((methodNode.access & Opcodes.ACC_SYNTHETIC) == 0) {
return;
}
- if (!isDefaultArgumentsMethodName(methodNode.name)) {
- return;
- }
if (!KotlinGeneratedFilter.isKotlinClass(context)) {
return;
}
- new Matcher().match(methodNode, output);
+ if (isDefaultArgumentsMethod(methodNode)) {
+ new Matcher().match(methodNode, output, false);
+ } else if (isDefaultArgumentsConstructor(methodNode)) {
+ new Matcher().match(methodNode, output, true);
+ }
}
private static class Matcher extends AbstractMatcher {
public void match(final MethodNode methodNode,
- final IFilterOutput output) {
- cursor = methodNode.instructions.getFirst();
+ final IFilterOutput output, final boolean constructor) {
+ cursor = skipNonOpcodes(methodNode.instructions.getFirst());
+
+ nextIs(Opcodes.IFNULL);
+ nextIsType(Opcodes.NEW, "java/lang/UnsupportedOperationException");
+ nextIs(Opcodes.DUP);
+ nextIs(Opcodes.LDC);
+ if (cursor == null
+ || !(((LdcInsnNode) cursor).cst instanceof String)
+ || !(((String) ((LdcInsnNode) cursor).cst).startsWith(
+ "Super calls with default arguments not supported in this target"))) {
+ cursor = null;
+ }
+ nextIsInvoke(Opcodes.INVOKESPECIAL,
+ "java/lang/UnsupportedOperationException", "<init>",
+ "(Ljava/lang/String;)V");
+ nextIs(Opcodes.ATHROW);
+ if (cursor != null) {
+ output.ignore(methodNode.instructions.getFirst(), cursor);
+ next();
+ } else {
+ cursor = skipNonOpcodes(methodNode.instructions.getFirst());
+ }
final Set<AbstractInsnNode> ignore = new HashSet<AbstractInsnNode>();
- final int maskVar = Type.getMethodType(methodNode.desc)
- .getArgumentTypes().length - 2;
+ final int maskVar = maskVar(methodNode.desc, constructor);
while (true) {
if (cursor.getOpcode() != Opcodes.ILOAD) {
break;
@@ -92,6 +129,22 @@ public final class KotlinDefaultArgumentsFilter implements IFilter {
output.ignore(i, i);
}
}
+
+ private static int maskVar(final String desc,
+ final boolean constructor) {
+ int slot = 0;
+ if (constructor) {
+ // one slot for reference to current object
+ slot++;
+ }
+ final Type[] argumentTypes = Type.getMethodType(desc)
+ .getArgumentTypes();
+ final int penultimateArgument = argumentTypes.length - 2;
+ for (int i = 0; i < penultimateArgument; i++) {
+ slot += argumentTypes[i].getSize();
+ }
+ return slot;
+ }
}
}