blob: c91ac80a23d2abfcd737c0241f6c1f9a20df59f6 [file] [log] [blame]
/*
* 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.js.translate.callTranslator
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.js.backend.ast.JsExpression
import org.jetbrains.kotlin.js.backend.ast.JsInvocation
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
import org.jetbrains.kotlin.js.backend.ast.metadata.*
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
class RangeCheckTranslator(private val context: TranslationContext) {
fun translateAsRangeCheck(resolvedCall: ResolvedCall<out FunctionDescriptor>, receivers: ExplicitReceivers): JsExpression? {
val calledFunction = resolvedCall.resultingDescriptor
if (calledFunction.name.asString() != "contains" || calledFunction.containingDeclaration !is ClassDescriptor) return null
val invocation = (receivers.extensionOrDispatchReceiver as? JsExpression.JsExpressionHasArguments) ?: return null
val (rangeType, rangeKind) = invocation.range ?: return null
var (lower, upper) = when {
rangeKind == RangeKind.UNTIL || rangeType == RangeType.INT -> {
if (invocation.arguments.size != 2) return null
invocation.arguments
}
else -> {
if (invocation !is JsInvocation) return null
val qualifier = invocation.qualifier as? JsNameRef ?: return null
val receiver = qualifier.qualifier ?: return null
if (invocation.arguments.size != 1) return null
listOf(receiver, invocation.arguments.single())
}
}
lower = context.cacheExpressionIfNeeded(lower)
upper = context.cacheExpressionIfNeeded(upper)
val subjectPsi = resolvedCall.valueArguments.values.singleOrNull()?.arguments?.singleOrNull()?.getArgumentExpression() ?: return null
val subject = context.cacheExpressionIfNeeded(Translation.translateAsExpression(subjectPsi, context))
return when (rangeType) {
RangeType.INT -> translateAsIntRangeCheck(lower, upper, rangeKind, subject)
RangeType.LONG -> translateAsLongRangeCheck(lower, upper, rangeKind, subject)
}
}
private fun translateAsIntRangeCheck(lower: JsExpression, upper: JsExpression, kind: RangeKind, subject: JsExpression): JsExpression {
val lowerCheck = JsAstUtils.lessThanEq(lower, subject)
val upperCheck = when (kind) {
RangeKind.RANGE_TO -> JsAstUtils.lessThanEq(subject, upper)
RangeKind.UNTIL -> JsAstUtils.lessThan(subject, upper)
}
lowerCheck.synthetic = true
upperCheck.synthetic = true
return JsAstUtils.and(lowerCheck, upperCheck).apply { synthetic = true }
}
private fun translateAsLongRangeCheck(lower: JsExpression, upper: JsExpression, kind: RangeKind, subject: JsExpression): JsExpression {
val lowerCheck = JsAstUtils.invokeMethod(lower, "lessThanOrEqual", subject).apply { sideEffects = SideEffectKind.PURE }
val upperCheck = when (kind) {
RangeKind.RANGE_TO -> JsAstUtils.invokeMethod(subject, "lessThanOrEqual", upper)
RangeKind.UNTIL -> JsAstUtils.invokeMethod(subject, "lessThan", upper)
}
upperCheck.sideEffects = SideEffectKind.PURE
return JsAstUtils.and(lowerCheck, upperCheck)
}
}