From 6140f29818b2fd1a870af74eb2b256835e7d91d5 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Wed, 28 Jun 2017 15:32:49 +0300 Subject: [PATCH] Ranges: introduce RangeValue class The overall idea is to unify intrinsics for 'for-in' loop generation and for 'in'/'!in' expression generation. --- .../kotlin/codegen/ExpressionCodegen.java | 8 +- .../codegen/forLoop/ForLoopGenerators.kt | 99 ----------------- .../kotlin/codegen/range/RangeValue.kt | 85 +++++++++++++++ .../kotlin/codegen/range/RangeValues.kt | 102 ++++++++++++++++++ 4 files changed, 193 insertions(+), 101 deletions(-) delete mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForLoopGenerators.kt create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValue.kt create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValues.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index ba50c588031..85eceae50e5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -40,10 +40,11 @@ import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; import org.jetbrains.kotlin.codegen.coroutines.ResolvedCallWithRealDescriptor; import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension; import org.jetbrains.kotlin.codegen.forLoop.AbstractForLoopGenerator; -import org.jetbrains.kotlin.codegen.forLoop.ForLoopGeneratorsKt; import org.jetbrains.kotlin.codegen.inline.*; import org.jetbrains.kotlin.codegen.intrinsics.*; import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt; +import org.jetbrains.kotlin.codegen.range.RangeValue; +import org.jetbrains.kotlin.codegen.range.RangeValuesKt; import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter; import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter; import org.jetbrains.kotlin.codegen.state.GenerationState; @@ -599,7 +600,10 @@ public class ExpressionCodegen extends KtVisitor impleme } private void generateFor(@NotNull KtForExpression forExpression) { - generateForLoop(ForLoopGeneratorsKt.getForLoopGenerator(this, forExpression)); + KtExpression range = forExpression.getLoopRange(); + assert range != null : "No loop range in for expression"; + RangeValue rangeValue = RangeValuesKt.createRangeValueForExpression(this, range); + generateForLoop(rangeValue.createForLoopGenerator(this, forExpression)); } @NotNull diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForLoopGenerators.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForLoopGenerators.kt deleted file mode 100644 index f227d01f021..00000000000 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/forLoop/ForLoopGenerators.kt +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2010-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.codegen.forLoop - -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.codegen.ExpressionCodegen -import org.jetbrains.kotlin.codegen.RangeCodegenUtil -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.checker.KotlinTypeChecker -import org.jetbrains.org.objectweb.asm.Type - -fun ExpressionCodegen.getForLoopGenerator(forExpression: KtForExpression) : AbstractForLoopGenerator { - getLoopRangeResolvedCall(forExpression, bindingContext)?.let { loopRangeCall -> - createOptimizedForLoopGeneratorOrNull(forExpression, loopRangeCall)?.let { - return it - } - } - - val loopRange = forExpression.loopRange!! - val loopRangeType = bindingContext.getType(loopRange)!! - val asmLoopRangeType = asmType(loopRangeType) - - return when { - asmLoopRangeType.sort == Type.ARRAY -> - ForInArrayLoopGenerator(this, forExpression) - RangeCodegenUtil.isRange(loopRangeType) -> - ForInRangeInstanceLoopGenerator(this, forExpression) - RangeCodegenUtil.isProgression(loopRangeType) -> - ForInProgressionExpressionLoopGenerator(this, forExpression) - isSubtypeOfCharSequence(loopRangeType, state.module.builtIns) -> - ForInCharSequenceLoopGenerator(this, forExpression) - else -> - IteratorForLoopGenerator(this, forExpression) - } -} - -private fun isSubtypeOfCharSequence(type: KotlinType, builtIns: KotlinBuiltIns) = - KotlinTypeChecker.DEFAULT.isSubtypeOf(type, builtIns.getBuiltInClassByName(Name.identifier("CharSequence")).defaultType) - -private fun ExpressionCodegen.createOptimizedForLoopGeneratorOrNull( - forExpression: KtForExpression, - loopRangeCall: ResolvedCall -): AbstractForLoopGenerator? { - val loopRangeCallee = loopRangeCall.resultingDescriptor - - return when { - RangeCodegenUtil.isPrimitiveNumberRangeTo(loopRangeCallee) -> - ForInRangeLiteralLoopGenerator(this, forExpression, loopRangeCall) - RangeCodegenUtil.isPrimitiveNumberDownTo(loopRangeCallee) -> - ForInDownToProgressionLoopGenerator(this, forExpression, loopRangeCall) - RangeCodegenUtil.isPrimitiveNumberUntil(loopRangeCallee) -> - ForInUntilRangeLoopGenerator(this, forExpression, loopRangeCall) - RangeCodegenUtil.isArrayOrPrimitiveArrayIndices(loopRangeCallee) -> - ForInArrayIndicesRangeLoopGenerator(this, forExpression, loopRangeCall) - RangeCodegenUtil.isCollectionIndices(loopRangeCallee) -> - ForInCollectionIndicesRangeLoopGenerator(this, forExpression, loopRangeCall) - RangeCodegenUtil.isCharSequenceIndices(loopRangeCallee) -> - ForInCharSequenceIndicesRangeLoopGenerator(this, forExpression, loopRangeCall) - else -> null - } -} - -private fun getLoopRangeResolvedCall(forExpression: KtForExpression, bindingContext: BindingContext): ResolvedCall? { - val loopRange = KtPsiUtil.deparenthesize(forExpression.loopRange) - - when (loopRange) { - is KtQualifiedExpression -> { - val qualifiedExpression = loopRange as KtQualifiedExpression? - val selector = qualifiedExpression!!.selectorExpression - if (selector is KtCallExpression || selector is KtSimpleNameExpression) { - return selector.getResolvedCall(bindingContext) - } - } - is KtSimpleNameExpression, is KtCallExpression -> return loopRange.getResolvedCall(bindingContext) - is KtBinaryExpression -> return loopRange.operationReference.getResolvedCall(bindingContext) - } - - return null -} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValue.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValue.kt new file mode 100644 index 00000000000..99621f96040 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValue.kt @@ -0,0 +1,85 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.range + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.forLoop.* +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.psi.KtForExpression +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall + +interface RangeValue { + fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): AbstractForLoopGenerator +} + +class ArrayRangeValue : RangeValue { + override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) = + ForInArrayLoopGenerator(codegen, forExpression) +} + +class PrimitiveRangeRangeValue : RangeValue { + override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) = + ForInRangeInstanceLoopGenerator(codegen, forExpression) +} + +class PrimitiveProgressionRangeValue : RangeValue { + override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) = + ForInProgressionExpressionLoopGenerator(codegen, forExpression) +} + +class CharSequenceRangeValue : RangeValue { + override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) = + ForInCharSequenceLoopGenerator(codegen, forExpression) +} + +class IterableRangeValue : RangeValue { + override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) = + IteratorForLoopGenerator(codegen, forExpression) +} + +abstract class CallIntrinsicRangeValue(protected val loopRangeCall: ResolvedCall): RangeValue + +class PrimitiveNumberRangeToRangeValue(loopRangeCall: ResolvedCall): CallIntrinsicRangeValue(loopRangeCall) { + override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) = + ForInRangeLiteralLoopGenerator(codegen, forExpression, loopRangeCall) +} + +class DownToProgressionRangeValue(loopRangeCall: ResolvedCall): CallIntrinsicRangeValue(loopRangeCall) { + override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) = + ForInDownToProgressionLoopGenerator(codegen, forExpression, loopRangeCall) +} + +class PrimitiveNumberUntilRangeValue(loopRangeCall: ResolvedCall): CallIntrinsicRangeValue(loopRangeCall) { + override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) = + ForInUntilRangeLoopGenerator(codegen, forExpression, loopRangeCall) +} + +class ArrayIndicesRangeValue(loopRangeCall: ResolvedCall): CallIntrinsicRangeValue(loopRangeCall) { + override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) = + ForInArrayIndicesRangeLoopGenerator(codegen, forExpression, loopRangeCall) +} + +class CollectionIndicesRangeValue(loopRangeCall: ResolvedCall): CallIntrinsicRangeValue(loopRangeCall) { + override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) = + ForInCollectionIndicesRangeLoopGenerator(codegen, forExpression, loopRangeCall) +} + +class CharSequenceIndicesRangeValue(loopRangeCall: ResolvedCall): CallIntrinsicRangeValue(loopRangeCall) { + override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) = + ForInCharSequenceIndicesRangeLoopGenerator(codegen, forExpression, loopRangeCall) +} + diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValues.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValues.kt new file mode 100644 index 00000000000..6f4766d2292 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/RangeValues.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.range + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.RangeCodegenUtil +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.org.objectweb.asm.Type + +fun ExpressionCodegen.createRangeValueForExpression(rangeExpression: KtExpression): RangeValue { + getResolvedCallForRangeExpression(bindingContext, rangeExpression)?.let { + createIntrinsifiedRangeValueOrNull(it)?.let { + return it + } + } + + val rangeType = bindingContext.getType(rangeExpression)!! + val asmRangeType = asmType(rangeType) + + return when { + asmRangeType.sort == Type.ARRAY -> + ArrayRangeValue() + RangeCodegenUtil.isRange(rangeType) -> + PrimitiveRangeRangeValue() + RangeCodegenUtil.isProgression(rangeType) -> + PrimitiveProgressionRangeValue() + isSubtypeOfCharSequence(rangeType, state.module.builtIns) -> + CharSequenceRangeValue() + else -> + IterableRangeValue() + } +} + +private fun isSubtypeOfCharSequence(type: KotlinType, builtIns: KotlinBuiltIns) = + KotlinTypeChecker.DEFAULT.isSubtypeOf(type, builtIns.getBuiltInClassByName(Name.identifier("CharSequence")).defaultType) + +private fun getResolvedCallForRangeExpression( + bindingContext: BindingContext, + rangeExpression: KtExpression +): ResolvedCall? { + val expression = KtPsiUtil.deparenthesize(rangeExpression) ?: return null + + return when (expression) { + is KtQualifiedExpression -> + expression.selectorExpression.let { selector -> + if (selector is KtCallExpression || selector is KtSimpleNameExpression) + selector.getResolvedCall(bindingContext) + else + null + } + + is KtSimpleNameExpression, is KtCallExpression -> + expression.getResolvedCall(bindingContext) + is KtBinaryExpression -> + expression.operationReference.getResolvedCall(bindingContext) + else -> + null + } +} + +private fun createIntrinsifiedRangeValueOrNull(rangeCall: ResolvedCall): RangeValue? { + val rangeCallee = rangeCall.resultingDescriptor + + return when { + RangeCodegenUtil.isPrimitiveNumberRangeTo(rangeCallee) -> + PrimitiveNumberRangeToRangeValue(rangeCall) + RangeCodegenUtil.isPrimitiveNumberDownTo(rangeCallee) -> + DownToProgressionRangeValue(rangeCall) + RangeCodegenUtil.isPrimitiveNumberUntil(rangeCallee) -> + PrimitiveNumberUntilRangeValue(rangeCall) + RangeCodegenUtil.isArrayOrPrimitiveArrayIndices(rangeCallee) -> + ArrayIndicesRangeValue(rangeCall) + RangeCodegenUtil.isCollectionIndices(rangeCallee) -> + CollectionIndicesRangeValue(rangeCall) + RangeCodegenUtil.isCharSequenceIndices(rangeCallee) -> + CharSequenceIndicesRangeValue(rangeCall) + else -> + null + } +} \ No newline at end of file