KT-12985 Do not create range instances for 'for' loop in CharSequence.indices

This commit is contained in:
Dmitry Petrov
2016-07-05 16:06:18 +03:00
parent 3dc23a0e02
commit 3445fe0d30
6 changed files with 71 additions and 1 deletions
@@ -656,6 +656,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
assert extensionReceiver != null : "Extension receiver should be non-null for optimizable 'Collection.indices' call";
return new ForInCollectionIndicesRangeLoopGenerator(forExpression, extensionReceiver);
}
else if (RangeCodegenUtil.isCharSequenceIndices(loopRangeCallee)) {
ReceiverValue extensionReceiver = loopRangeCall.getExtensionReceiver();
assert extensionReceiver != null : "Extension receiver should be non-null for optimizable 'CharSequence.indices' call";
return new ForInCharSequenceIndicesRangeLoopGenerator(forExpression, extensionReceiver);
}
return null;
}
@@ -1198,7 +1203,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
StackValue receiver = generateReceiverValue(receiverValue, false);
Type receiverType = asmType(receiverValue.getType());
receiver.put(receiverType, v); // NB receiverType is a collection or an array
receiver.put(receiverType, v);
getReceiverSizeAsInt();
v.iconst(1);
v.sub(Type.INT_TYPE);
@@ -1233,6 +1238,17 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
}
private class ForInCharSequenceIndicesRangeLoopGenerator extends ForInOptimizedIndicesLoopGenerator {
private ForInCharSequenceIndicesRangeLoopGenerator(@NotNull KtForExpression forExpression, @NotNull ReceiverValue receiverValue) {
super(forExpression, receiverValue);
}
@Override
protected void getReceiverSizeAsInt() {
v.invokeinterface("java/lang/CharSequence", "length", "()I");
}
}
private class ForInProgressionExpressionLoopGenerator extends AbstractForInProgressionOrRangeLoopGenerator {
private int incrementVar;
private Type incrementType;
@@ -165,6 +165,17 @@ public class RangeCodegenUtil {
return true;
}
public static boolean isCharSequenceIndices(@NotNull CallableDescriptor descriptor) {
if (!isTopLevelInPackage(descriptor, "indices", "kotlin.text")) return false;
ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
if (extensionReceiver == null) return false;
KotlinType extensionReceiverType = extensionReceiver.getType();
if (!KotlinBuiltIns.isCharSequenceOrNullableCharSequence(extensionReceiverType)) return false;
return true;
}
private static boolean isTopLevelInPackage(@NotNull CallableDescriptor descriptor, @NotNull String name, @NotNull String packageName) {
if (!name.equals(descriptor.getName().asString())) return false;
@@ -0,0 +1,16 @@
// WITH_RUNTIME
fun test(s: CharSequence): Int {
var result = 0
for (i in s.indices) {
result = result * 10 + (i + 1)
}
return result
}
fun box(): String {
val test = test("abcd")
if (test != 1234) return "Fail: $test"
return "OK"
}
@@ -0,0 +1,15 @@
// WITH_RUNTIME
fun test(s: CharSequence): Int {
var result = 0
for (i in s.indices) {
result = result * 10 + (i + 1)
}
return result
}
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
@@ -10884,6 +10884,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInIndices"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("forInCharSequenceIndices.kt")
public void testForInCharSequenceIndices() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInIndices/forInCharSequenceIndices.kt");
doTest(fileName);
}
@TestMetadata("forInCollectionImplicitReceiverIndices.kt")
public void testForInCollectionImplicitReceiverIndices() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInIndices/forInCollectionImplicitReceiverIndices.kt");
@@ -909,6 +909,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/forLoop/forInIndices"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("forInCharSequenceIndices.kt")
public void testForInCharSequenceIndices() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInIndices/forInCharSequenceIndices.kt");
doTest(fileName);
}
@TestMetadata("forInCollectionImplicitReceiverIndices.kt")
public void testForInCollectionImplicitReceiverIndices() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInIndices/forInCollectionImplicitReceiverIndices.kt");