001 /*
002 * Copyright 2010-2016 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.js.translate.operation;
018
019 import org.jetbrains.kotlin.js.backend.ast.JsExpression;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.kotlin.descriptors.CallableDescriptor;
023 import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
024 import org.jetbrains.kotlin.js.patterns.DescriptorPredicate;
025 import org.jetbrains.kotlin.js.patterns.PatternBuilder;
026 import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
027 import org.jetbrains.kotlin.js.translate.context.TranslationContext;
028 import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
029 import org.jetbrains.kotlin.js.translate.general.Translation;
030 import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
031 import org.jetbrains.kotlin.psi.*;
032 import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
033 import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
034 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
035 import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
036
037 /**
038 * Translates 'A in B' expression applying specialization if possible
039 */
040 public class InOperationTranslator extends AbstractTranslator {
041 private static final DescriptorPredicate INT_SPECIALIZATION_TEST = PatternBuilder.pattern("ranges.IntRange.contains");
042 private static final DescriptorPredicate INT_RANGE_TEST = PatternBuilder.pattern("Int.rangeTo");
043 private final JsExpression left;
044 private final KtExpression right;
045 private final KtSimpleNameExpression operation;
046 private final boolean negated;
047
048 public InOperationTranslator(@NotNull TranslationContext context, @NotNull JsExpression left, @NotNull KtExpression right,
049 @NotNull KtSimpleNameExpression operation, boolean negated) {
050 super(context);
051 this.left = left;
052 this.right = right;
053 this.operation = operation;
054 this.negated = negated;
055 }
056
057 @NotNull
058 public JsExpression translate() {
059 ResolvedCall<? extends FunctionDescriptor> call = CallUtilKt.getFunctionResolvedCallWithAssert(operation, bindingContext());
060 if (INT_SPECIALIZATION_TEST.apply(call.getResultingDescriptor())) {
061 JsExpression candidate = translateInt();
062 if (candidate != null) {
063 return candidate;
064 }
065 }
066 JsExpression rightTranslated = Translation.translateAsExpression(right, context());
067 return translateGeneral(call, rightTranslated);
068 }
069
070 @NotNull
071 private JsExpression translateGeneral(@NotNull ResolvedCall<? extends FunctionDescriptor> call, @NotNull JsExpression rightTranslated) {
072 JsExpression result = CallTranslator.translate(context(), call, rightTranslated);
073 if (negated) {
074 result = JsAstUtils.not(result);
075 }
076 return result;
077 }
078
079 @Nullable
080 private JsExpression translateInt() {
081 ResolvedCall<? extends CallableDescriptor> rightCall = CallUtilKt.getResolvedCallWithAssert(right, bindingContext());
082 if (!(rightCall.getResultingDescriptor() instanceof FunctionDescriptor)) {
083 return null;
084 }
085 FunctionDescriptor callDescriptor = (FunctionDescriptor) rightCall.getResultingDescriptor();
086 if (!INT_RANGE_TEST.apply(callDescriptor)) {
087 return null;
088 }
089 if (!(rightCall.getDispatchReceiver() instanceof ExpressionReceiver)) {
090 return null;
091 }
092
093 KtExpression lower = ((ExpressionReceiver) rightCall.getDispatchReceiver()).getExpression();
094 KtExpression upper = rightCall.getCall().getValueArguments().get(0).getArgumentExpression();
095 assert upper != null : "Parse error occurred: " + PsiUtilsKt.getTextWithLocation(right);
096 return translateInt(lower, upper);
097 }
098
099 @NotNull
100 private JsExpression translateInt(@NotNull KtExpression lowerExpression, @NotNull KtExpression upperExpression) {
101 JsExpression lower = Translation.translateAsExpression(lowerExpression, context());
102 JsExpression upper = Translation.translateAsExpression(upperExpression, context());
103 if (!negated) {
104 JsExpression first = JsAstUtils.greaterThanEq(left, lower);
105 JsExpression second = JsAstUtils.lessThanEq(left, upper);
106 return JsAstUtils.and(first, second);
107 }
108 else {
109 JsExpression first = JsAstUtils.lessThan(left, lower);
110 JsExpression second = JsAstUtils.greaterThan(left, upper);
111 return JsAstUtils.or(first, second);
112 }
113 }
114 }