org.jetbrains.kotlin.codegen.range.RangeValues.kt Maven / Gradle / Ivy
/*
* 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.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
fun ExpressionCodegen.createRangeValueForExpression(rangeExpression: KtExpression): RangeValue {
// NB when you implement a new intrinsic RangeValue,
// also look into org.jetbrains.kotlin.generators.tests and update related testData generators
// (e.g., GenerateInRangeExpressionTestData).
val resolvedCall = getResolvedCallForRangeExpression(bindingContext, rangeExpression)
if (resolvedCall != null) {
val intrinsicRangeValue = createIntrinsifiedRangeValueOrNull(resolvedCall)
if (intrinsicRangeValue != null) {
return intrinsicRangeValue
}
}
val rangeType = bindingContext.getType(rangeExpression)!!
val asmRangeType = asmType(rangeType)
val loopRangeIteratorResolvedCall = bindingContext[BindingContext.LOOP_RANGE_ITERATOR_RESOLVED_CALL, rangeExpression]
val builtIns = state.module.builtIns
return when {
asmRangeType.sort == Type.ARRAY -> {
val properForInArraySemantics =
state.languageVersionSettings.supportsFeature(LanguageFeature.ProperForInArrayLoopRangeVariableAssignmentSemantic)
ArrayRangeValue(
properForInArraySemantics || !isLocalVarReference(rangeExpression, bindingContext),
properForInArraySemantics
)
}
isPrimitiveRange(rangeType) || isUnsignedRange(rangeType) ->
PrimitiveRangeRangeValue(rangeExpression)
isPrimitiveProgression(rangeType) || isUnsignedProgression(rangeType) ->
PrimitiveProgressionRangeValue(rangeExpression)
isSubtypeOfString(rangeType, builtIns) && isCharSequenceIteratorCall(loopRangeIteratorResolvedCall) ->
CharSequenceRangeValue(true, AsmTypes.JAVA_STRING_TYPE)
isSubtypeOfCharSequence(rangeType, builtIns) && isCharSequenceIteratorCall(loopRangeIteratorResolvedCall) ->
CharSequenceRangeValue(false, null)
else ->
IterableRangeValue()
}
}
fun isLocalVarReference(rangeExpression: KtExpression, bindingContext: BindingContext): Boolean {
if (rangeExpression !is KtSimpleNameExpression) return false
val resultingDescriptor = rangeExpression.getResolvedCall(bindingContext)?.resultingDescriptor ?: return false
return resultingDescriptor is LocalVariableDescriptor &&
resultingDescriptor !is SyntheticFieldDescriptor &&
!resultingDescriptor.isDelegated &&
resultingDescriptor.isVar
}
private fun isSubtypeOfString(type: KotlinType, builtIns: KotlinBuiltIns) =
KotlinTypeChecker.DEFAULT.isSubtypeOf(type, builtIns.stringType)
private fun isSubtypeOfCharSequence(type: KotlinType, builtIns: KotlinBuiltIns) =
KotlinTypeChecker.DEFAULT.isSubtypeOf(type, builtIns.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 ExpressionCodegen.createIntrinsifiedRangeValueOrNull(rangeCall: ResolvedCall): RangeValue? {
val rangeCallee = rangeCall.resultingDescriptor
return when {
isPrimitiveNumberRangeTo(rangeCallee) || isUnsignedIntegerRangeTo(rangeCallee) ->
PrimitiveNumberRangeLiteralRangeValue(rangeCall)
isPrimitiveNumberDownTo(rangeCallee) || isUnsignedIntegerDownTo(rangeCallee) ->
DownToProgressionRangeValue(rangeCall)
isPrimitiveNumberUntil(rangeCallee) || isUnsignedIntegerUntil(rangeCallee) ->
PrimitiveNumberUntilRangeValue(rangeCall)
isArrayOrPrimitiveArrayIndices(rangeCallee) ->
ArrayIndicesRangeValue(rangeCall)
isCollectionIndices(rangeCallee) ->
CollectionIndicesRangeValue(rangeCall)
isCharSequenceIndices(rangeCallee) ->
CharSequenceIndicesRangeValue(rangeCall)
isArrayOrPrimitiveArrayWithIndex(rangeCallee) ->
ArrayWithIndexRangeValue(rangeCall)
isIterableWithIndex(rangeCallee) ->
IterableWithIndexRangeValue(rangeCall)
isSequenceWithIndex(rangeCallee) ->
SequenceWithIndexRangeValue(rangeCall)
isCharSequenceWithIndex(rangeCallee) ->
CharSequenceWithIndexRangeValue(rangeCall)
isComparableRangeTo(rangeCallee) ->
ComparableRangeLiteralRangeValue(this, rangeCall)
isPrimitiveProgressionReverse(rangeCallee) ->
createReversedRangeValueOrNull(rangeCall)
else ->
null
}
}
private fun ExpressionCodegen.createReversedRangeValueOrNull(rangeCall: ResolvedCall): RangeValue? {
val receiver = rangeCall.extensionReceiver as? ExpressionReceiver ?: return null
val receiverRangeValue = createRangeValueForExpression(receiver.expression) as? ReversableRangeValue ?: return null
return ReversedRangeValue(receiverRangeValue)
}
private fun isCharSequenceIteratorCall(iteratorCall: ResolvedCall<*>?) =
iteratorCall?.resultingDescriptor?.let { isCharSequenceIterator(it) } ?: false