/*
* Copyright 2010-2016 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.operation;
import org.jetbrains.kotlin.js.backend.ast.
JsExpression;
import org.jetbrains.annotations.
NotNull;
import org.jetbrains.annotations.
Nullable;
import org.jetbrains.kotlin.descriptors.
CallableDescriptor;
import org.jetbrains.kotlin.descriptors.
FunctionDescriptor;
import org.jetbrains.kotlin.js.patterns.
DescriptorPredicate;
import org.jetbrains.kotlin.js.patterns.
PatternBuilder;
import org.jetbrains.kotlin.js.translate.callTranslator.
CallTranslator;
import org.jetbrains.kotlin.js.translate.context.
TranslationContext;
import org.jetbrains.kotlin.js.translate.general.
AbstractTranslator;
import org.jetbrains.kotlin.js.translate.general.
Translation;
import org.jetbrains.kotlin.js.translate.utils.
JsAstUtils;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.
PsiUtilsKt;
import org.jetbrains.kotlin.resolve.calls.callUtil.
CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.model.
ResolvedCall;
import org.jetbrains.kotlin.resolve.scopes.receivers.
ExpressionReceiver;
/**
* Translates 'A in B' expression applying specialization if possible
*/
public class
InOperationTranslator extends
AbstractTranslator {
private static final
DescriptorPredicate INT_SPECIALIZATION_TEST =
PatternBuilder.
pattern("ranges.IntRange.contains");
private static final
DescriptorPredicate INT_RANGE_TEST =
PatternBuilder.
pattern("Int.rangeTo");
private final
JsExpression left;
private final
KtExpression right;
private final
KtSimpleNameExpression operation;
private final boolean
negated;
public
InOperationTranslator(@
NotNull TranslationContext context, @
NotNull JsExpression left, @
NotNull KtExpression right,
@
NotNull KtSimpleNameExpression operation, boolean
negated) {
super(
context);
this.
left =
left;
this.
right =
right;
this.
operation =
operation;
this.
negated =
negated;
}
@
NotNull
public
JsExpression translate() {
ResolvedCall<? extends
FunctionDescriptor>
call =
CallUtilKt.
getFunctionResolvedCallWithAssert(
operation,
bindingContext());
if (
INT_SPECIALIZATION_TEST.
test(
call.
getResultingDescriptor())) {
JsExpression candidate =
translateInt();
if (
candidate != null) {
return
candidate;
}
}
JsExpression rightTranslated =
Translation.
translateAsExpression(
right,
context());
return
translateGeneral(
call,
rightTranslated);
}
@
NotNull
private
JsExpression translateGeneral(@
NotNull ResolvedCall<? extends
FunctionDescriptor>
call, @
NotNull JsExpression rightTranslated) {
JsExpression result =
CallTranslator.
translate(
context(),
call,
rightTranslated);
if (
negated) {
result =
JsAstUtils.
not(
result);
}
return
result;
}
@
Nullable
private
JsExpression translateInt() {
ResolvedCall<? extends
CallableDescriptor>
rightCall =
CallUtilKt.
getResolvedCallWithAssert(
right,
bindingContext());
if (!(
rightCall.
getResultingDescriptor() instanceof
FunctionDescriptor)) {
return null;
}
FunctionDescriptor callDescriptor = (
FunctionDescriptor)
rightCall.
getResultingDescriptor();
if (!
INT_RANGE_TEST.
test(
callDescriptor)) {
return null;
}
if (!(
rightCall.
getDispatchReceiver() instanceof
ExpressionReceiver)) {
return null;
}
KtExpression lower = ((
ExpressionReceiver)
rightCall.
getDispatchReceiver()).
getExpression();
KtExpression upper =
rightCall.
getCall().
getValueArguments().
get(0).
getArgumentExpression();
assert
upper != null : "Parse error occurred: " +
PsiUtilsKt.
getTextWithLocation(
right);
return
translateInt(
lower,
upper);
}
@
NotNull
private
JsExpression translateInt(@
NotNull KtExpression lowerExpression, @
NotNull KtExpression upperExpression) {
JsExpression lower =
Translation.
translateAsExpression(
lowerExpression,
context());
JsExpression upper =
Translation.
translateAsExpression(
upperExpression,
context());
if (!
negated) {
JsExpression first =
JsAstUtils.
greaterThanEq(
left,
lower);
JsExpression second =
JsAstUtils.
lessThanEq(
left,
upper);
return
JsAstUtils.
and(
first,
second);
}
else {
JsExpression first =
JsAstUtils.
lessThan(
left,
lower);
JsExpression second =
JsAstUtils.
greaterThan(
left,
upper);
return
JsAstUtils.
or(
first,
second);
}
}
}