Revising parameter usage info.
This CL extends the range of supported cases for class inlining,
mostly for benefit of kotlin classes and lambdas inlining.
Adds 3 new cases of class inlining on tachoyomi (-300 bytes) and
3 new cases on gmscore10 (-1K).
Bug:
Change-Id: I79c9b63d2edd06d7e298fdd1c548b30fcaab6fc9
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 845eecd..69126be 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -25,10 +25,9 @@
import com.android.tools.r8.dex.JumboStringRewriter;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.graph.AppInfo.ResolutionResult;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo.ParameterUsage;
+import com.android.tools.r8.graph.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.ValueNumberGenerator;
import com.android.tools.r8.ir.code.ValueType;
@@ -45,13 +44,11 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
-import java.util.stream.Collectors;
public class DexEncodedMethod extends KeyedDexItem<DexMethod> implements ResolutionResult {
@@ -621,51 +618,6 @@
}
}
- public static final class ParameterUsagesInfo {
- private ImmutableList<ParameterUsage> parametersUsages;
-
- public ParameterUsagesInfo(List<ParameterUsage> usages) {
- assert !usages.isEmpty();
- parametersUsages = ImmutableList.copyOf(usages);
- assert parametersUsages.size() ==
- parametersUsages.stream().map(usage -> usage.index).collect(Collectors.toSet()).size();
- }
-
- public static abstract class ParameterUsage {
- public final int index;
-
- ParameterUsage(int index) {
- this.index = index;
- }
- }
-
- public static class SingleCallOfArgumentMethod extends ParameterUsage {
- public final Invoke.Type type;
- public final DexMethod method;
-
- public SingleCallOfArgumentMethod(int index, Type type, DexMethod method) {
- super(index);
- this.type = type;
- this.method = method;
- }
- }
-
- public static class NotUsed extends ParameterUsage {
- public NotUsed(int index) {
- super(index);
- }
- }
-
- ParameterUsage getParameterUsage(int parameter) {
- for (ParameterUsage usage : parametersUsages) {
- if (usage.index == parameter) {
- return usage;
- }
- }
- return null;
- }
- }
-
public static class OptimizationInfo {
private int returnedArgument = -1;
diff --git a/src/main/java/com/android/tools/r8/graph/ParameterUsagesInfo.java b/src/main/java/com/android/tools/r8/graph/ParameterUsagesInfo.java
new file mode 100644
index 0000000..84cf331
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ParameterUsagesInfo.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.Pair;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public final class ParameterUsagesInfo {
+ private ImmutableList<ParameterUsage> parametersUsages;
+
+ public ParameterUsagesInfo(List<ParameterUsage> usages) {
+ assert !usages.isEmpty();
+ parametersUsages = ImmutableList.copyOf(usages);
+ assert parametersUsages.size() ==
+ parametersUsages.stream().map(usage -> usage.index).collect(Collectors.toSet()).size();
+ }
+
+ ParameterUsage getParameterUsage(int parameter) {
+ for (ParameterUsage usage : parametersUsages) {
+ if (usage.index == parameter) {
+ return usage;
+ }
+ }
+ return null;
+ }
+
+ public final static class ParameterUsage {
+ public final int index;
+ public final Set<Type> ifZeroTest;
+ public final List<Pair<Invoke.Type, DexMethod>> callsReceiver;
+ public final boolean returnValue;
+
+ ParameterUsage(int index, Set<Type> ifZeroTest,
+ List<Pair<Invoke.Type, DexMethod>> callsReceiver, boolean returnValue) {
+ this.index = index;
+ this.ifZeroTest = ifZeroTest.isEmpty()
+ ? Collections.emptySet() : ImmutableSet.copyOf(ifZeroTest);
+ this.callsReceiver = callsReceiver.isEmpty()
+ ? Collections.emptyList() : ImmutableList.copyOf(callsReceiver);
+ this.returnValue = returnValue;
+ }
+
+ public boolean notUsed() {
+ return ifZeroTest == null && callsReceiver == null && !returnValue;
+ }
+ }
+
+ public static class ParameterUsageBuilder {
+ private final int index;
+ private final Value arg;
+ private final Set<Type> ifZeroTestTypes = new HashSet<>();
+ private final List<Pair<Invoke.Type, DexMethod>> callsOnReceiver = new ArrayList<>();
+ private boolean returnValue = false;
+
+ public ParameterUsageBuilder(Value arg, int index) {
+ this.arg = arg;
+ this.index = index;
+ }
+
+ // Returns false if the instruction is not supported.
+ public boolean note(Instruction instruction) {
+ if (instruction.isInvokeMethodWithReceiver() &&
+ instruction.inValues().lastIndexOf(arg) == 0) {
+ callsOnReceiver.add(new Pair<>(
+ instruction.asInvokeMethodWithReceiver().getType(),
+ instruction.asInvokeMethodWithReceiver().getInvokedMethod()));
+ return true;
+ }
+
+ if (instruction.isIf() && instruction.asIf().isZeroTest()) {
+ assert instruction.inValues().size() == 1 && instruction.inValues().get(0) == arg;
+ ifZeroTestTypes.add(instruction.asIf().getType());
+ return true;
+ }
+
+ if (instruction.isReturn()) {
+ assert instruction.inValues().size() == 1 && instruction.inValues().get(0) == arg;
+ returnValue = true;
+ return true;
+ }
+
+ return false;
+ }
+
+ public ParameterUsage build() {
+ return new ParameterUsage(index, ifZeroTestTypes, callsOnReceiver, returnValue);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 590b0c5..3288aa9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -745,7 +745,7 @@
Suppliers.memoize(() -> inliner.createDefaultOracle(
method, code, effectivelyFinalTypeEnvironment,
isProcessedConcurrently, callSiteInformation,
- Integer.MAX_VALUE, Integer.MAX_VALUE)
+ Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2)
)
);
assert code.isConsistentSSA();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
index ad6d66d..c26aecf 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedback.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo;
+import com.android.tools.r8.graph.ParameterUsagesInfo;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import java.util.BitSet;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDirect.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDirect.java
index 326fa03..2b1a6db 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDirect.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo;
+import com.android.tools.r8.graph.ParameterUsagesInfo;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import java.util.BitSet;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
index 359c90d..a547b36 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackIgnore.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo;
+import com.android.tools.r8.graph.ParameterUsagesInfo;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import java.util.BitSet;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 5066f87..ee64165 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -13,10 +13,6 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo.NotUsed;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo.ParameterUsage;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo.SingleCallOfArgumentMethod;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer.TrivialClassInitializer;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer.TrivialInstanceInitializer;
@@ -37,6 +33,9 @@
import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.DexValue.DexValueShort;
import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.graph.ParameterUsagesInfo;
+import com.android.tools.r8.graph.ParameterUsagesInfo.ParameterUsage;
+import com.android.tools.r8.graph.ParameterUsagesInfo.ParameterUsageBuilder;
import com.android.tools.r8.ir.analysis.type.TypeEnvironment;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.ArrayPut;
@@ -851,28 +850,27 @@
List<Value> values = code.collectArguments(true);
for (int i = 0; i < values.size(); i++) {
Value value = values.get(i);
-
- int numberOfAllUsages = value.numberOfAllUsers();
- if (numberOfAllUsages == 0) {
- usages.add(new NotUsed(i));
+ if (value.numberOfPhiUsers() > 0) {
continue;
}
-
- if (numberOfAllUsages == 1 && value.numberOfUsers() == 1) {
- Instruction instruction = value.singleUniqueUser();
- if (instruction.isInvokeMethodWithReceiver() &&
- instruction.inValues().lastIndexOf(value) == 0) {
- usages.add(new SingleCallOfArgumentMethod(i,
- instruction.asInvokeMethodWithReceiver().getType(),
- instruction.asInvokeMethodWithReceiver().getInvokedMethod()));
- }
- continue;
+ ParameterUsage usage = collectParameterUsages(i, value);
+ if (usage != null) {
+ usages.add(usage);
}
}
-
feedback.setParameterUsages(method, usages.isEmpty() ? null : new ParameterUsagesInfo(usages));
}
+ private ParameterUsage collectParameterUsages(int i, Value value) {
+ ParameterUsageBuilder builder = new ParameterUsageBuilder(value, i);
+ for (Instruction user : value.uniqueUsers()) {
+ if (!builder.note(user)) {
+ return null;
+ }
+ }
+ return builder.build();
+ }
+
// This method defines trivial instance initializer as follows:
//
// ** The initializer may call the initializer of the base class, which
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 4d6e3b1..8556905 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -9,9 +9,6 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
import com.android.tools.r8.graph.DexEncodedMethod.OptimizationInfo;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo.NotUsed;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo.ParameterUsage;
-import com.android.tools.r8.graph.DexEncodedMethod.ParameterUsagesInfo.SingleCallOfArgumentMethod;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer.TrivialClassInitializer;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer.TrivialInstanceInitializer;
@@ -19,6 +16,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
@@ -38,7 +36,9 @@
import com.android.tools.r8.kotlin.KotlinInfo;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.Pair;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.LinkedList;
@@ -48,6 +48,9 @@
import java.util.function.Supplier;
final class InlineCandidateProcessor {
+ private static final ImmutableSet<If.Type> ALLOWED_ZERO_TEST_TYPES =
+ ImmutableSet.of(If.Type.EQ, If.Type.NE);
+
private final DexItemFactory factory;
private final AppInfoWithLiveness appInfo;
private final Predicate<DexType> isClassEligible;
@@ -532,13 +535,24 @@
// eligible according to the same rules defined for direct method call eligibility
// (except we require the method receiver to not be used in return instruction)
//
+ // -- eligible instance is used in zero-test 'if' instructions testing if the value
+ // is null/not-null (since we know the instance is not null, those checks can
+ // be rewritten)
+ //
// -- method itself can be inlined
//
- private synchronized boolean isExtraMethodCallEligible(
+ private boolean isExtraMethodCallEligible(
Supplier<InliningOracle> defaultOracle, InvokeMethod invokeMethod) {
List<Value> arguments = Lists.newArrayList(invokeMethod.inValues());
+ // Don't consider constructor invocations and super calls, since
+ // we don't want to forcibly inline them.
+ if (invokeMethod.isInvokeSuper() ||
+ (invokeMethod.isInvokeDirect() && factory.isConstructor(invokeMethod.getInvokedMethod()))) {
+ return false;
+ }
+
// Remove receiver from arguments.
if (invokeMethod.isInvokeMethodWithReceiver()) {
if (arguments.get(0) == eligibleInstance) {
@@ -569,35 +583,44 @@
return false; // Don't know anything.
}
- if (parameterUsage instanceof NotUsed) {
+ if (parameterUsage.notUsed()) {
// Reference can be removed since it's not used.
unusedArguments.add(new Pair<>(invokeMethod, argIndex));
continue;
}
- if (parameterUsage instanceof SingleCallOfArgumentMethod) {
- // Method exactly one time calls a method on passed eligibleInstance.
- SingleCallOfArgumentMethod info = (SingleCallOfArgumentMethod) parameterUsage;
- if (info.type != Type.VIRTUAL && info.type != Type.INTERFACE) {
- return false; // Don't support direct and super calls yet.
- }
-
- // Is the method called indirectly still eligible?
- InliningInfo potentialInliningInfo = isEligibleIndirectMethodCall(info.method);
- if (potentialInliningInfo != null) {
- // Check if the method is inline-able by standard inliner.
- InlineAction inlineAction =
- invokeMethod.computeInlining(defaultOracle.get(), method.method.holder);
- if (inlineAction != null) {
- extraMethodCalls.put(invokeMethod, new InliningInfo(singleTarget, null));
- continue;
- }
- }
-
+ if (parameterUsage.returnValue &&
+ !(invokeMethod.outValue() == null || invokeMethod.outValue().numberOfAllUsers() == 0)) {
+ // Used as return value which is not ignored.
return false;
}
- return false; // All other argument usages are not eligible.
+ if (!Sets.difference(parameterUsage.ifZeroTest, ALLOWED_ZERO_TEST_TYPES).isEmpty()) {
+ // Used in unsupported zero-check-if kinds.
+ return false;
+ }
+
+ for (Pair<Type, DexMethod> call : parameterUsage.callsReceiver) {
+ if (call.getFirst() != Type.VIRTUAL && call.getFirst() != Type.INTERFACE) {
+ // Don't support direct and super calls yet.
+ return false;
+ }
+
+ // Is the method called indirectly still eligible?
+ InliningInfo potentialInliningInfo = isEligibleIndirectMethodCall(call.getSecond());
+ if (potentialInliningInfo == null) {
+ return false;
+ }
+
+ // Check if the method is inline-able by standard inliner.
+ InlineAction inlineAction =
+ invokeMethod.computeInlining(defaultOracle.get(), method.method.holder);
+ if (inlineAction == null) {
+ return false;
+ }
+ }
+
+ extraMethodCalls.put(invokeMethod, new InliningInfo(singleTarget, null));
}
// Looks good.
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index a690429..81e12a1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -114,6 +114,21 @@
"class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateless$1").isPresent());
assertTrue(inspector.clazz(
"class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1").isPresent());
+ assertTrue(inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1").isPresent());
+ assertTrue(inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethod2$1").isPresent());
+ assertTrue(inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethod3$1").isPresent());
+ assertTrue(inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1")
+ .isPresent());
+ assertTrue(inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda2$1")
+ .isPresent());
+ assertTrue(inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda3$1")
+ .isPresent());
});
runTest("class_inliner_lambda_k_style", mainClassName, true, (app) -> {
@@ -136,6 +151,31 @@
assertFalse(inspector.clazz(
"class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1").isPresent());
+
+ assertEquals(
+ Sets.newHashSet(),
+ collectAccessedLambdaTypes(lambdaCheck, clazz, "testBigExtraMethod"));
+
+ assertFalse(inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1").isPresent());
+ assertFalse(inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethod2$1").isPresent());
+ assertFalse(inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethod3$1").isPresent());
+
+ assertEquals(
+ Sets.newHashSet(),
+ collectAccessedLambdaTypes(lambdaCheck, clazz, "testBigExtraMethodReturningLambda"));
+
+ assertFalse(inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1")
+ .isPresent());
+ assertFalse(inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda2$1")
+ .isPresent());
+ assertFalse(inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda3$1")
+ .isPresent());
});
}
diff --git a/src/test/kotlinR8TestResources/class_inliner_lambda_k_style/main.kt b/src/test/kotlinR8TestResources/class_inliner_lambda_k_style/main.kt
index 136f72d..06b937e 100644
--- a/src/test/kotlinR8TestResources/class_inliner_lambda_k_style/main.kt
+++ b/src/test/kotlinR8TestResources/class_inliner_lambda_k_style/main.kt
@@ -11,6 +11,8 @@
fun main(args: Array<String>) {
testKotlinSequencesStateless(produceSequence(10))
testKotlinSequencesStateful(5, 2, produceSequence(10))
+ testBigExtraMethod()
+ testBigExtraMethodReturningLambda()
}
data class Record(val foo: String, val good: Boolean)
@@ -38,12 +40,57 @@
}
}
-private fun produceSequence(size: Int): Sequence<String> {
+@Synchronized
+fun testBigExtraMethod() {
+ useRecord()
+ bigUserWithNotNullChecksAndTwoCalls(next()) { next() }
+ testBigExtraMethod2()
+ testBigExtraMethod3()
+}
+
+fun testBigExtraMethod2() {
+ bigUserWithNotNullChecksAndTwoCalls(next()) { next() }
+}
+
+fun testBigExtraMethod3() {
+ bigUserWithNotNullChecksAndTwoCalls(next()) { next() }
+}
+
+fun bigUserWithNotNullChecksAndTwoCalls(id: String, lambda: () -> String): String {
+ useRecord()
+ println("[A] logging call#$id returning ${lambda()}")
+ return "$id: ${lambda()}"
+}
+
+@Synchronized
+fun testBigExtraMethodReturningLambda() {
+ useRecord()
+ bigUserReturningLambda(next()) { next() } // Not used
+ testBigExtraMethodReturningLambda2()
+ testBigExtraMethodReturningLambda3()
+}
+
+fun testBigExtraMethodReturningLambda2() {
+ bigUserReturningLambda(next()) { next() } // Not used
+}
+
+fun testBigExtraMethodReturningLambda3() {
+ bigUserReturningLambda(next()) { next() } // Not used
+}
+
+fun bigUserReturningLambda(id: String, lambda: () -> String): () -> String {
+ useRecord()
+ println("[B] logging call#$id returning ${lambda()}")
+ println("[C] logging call#$id returning ${lambda()}")
+ return lambda
+}
+
+fun produceSequence(size: Int): Sequence<String> {
var count = size
return generateSequence { if (count-- > 0) next() else null }
}
// Need this to make sure testKotlinSequenceXXX is not processed
// concurrently with invoke() on lambdas.
-fun useRecord() = useRecord2()
-fun useRecord2() = Record("", true)
+@Synchronized fun useRecord() = useRecord2()
+@Synchronized fun useRecord2() = Record("", true)