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 com.google.dart.compiler.backend.js.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    
047        public InOperationTranslator(@NotNull TranslationContext context, @NotNull JsExpression left, @NotNull KtExpression right,
048                @NotNull KtSimpleNameExpression operation) {
049            super(context);
050            this.left = left;
051            this.right = right;
052            this.operation = operation;
053        }
054    
055        @NotNull
056        public JsExpression translate() {
057            ResolvedCall<? extends FunctionDescriptor> call = CallUtilKt.getFunctionResolvedCallWithAssert(operation, bindingContext());
058            if (INT_SPECIALIZATION_TEST.apply(call.getResultingDescriptor())) {
059                JsExpression candidate = translateInt();
060                if (candidate != null) {
061                    return candidate;
062                }
063            }
064            JsExpression rightTranslated = Translation.translateAsExpression(right, context());
065            return translateGeneral(call, rightTranslated);
066        }
067    
068        @NotNull
069        private JsExpression translateGeneral(@NotNull ResolvedCall<? extends FunctionDescriptor> call, @NotNull JsExpression rightTranslated) {
070            return CallTranslator.translate(context(), call, rightTranslated);
071        }
072    
073        @Nullable
074        private JsExpression translateInt() {
075            ResolvedCall<? extends CallableDescriptor> rightCall = CallUtilKt.getResolvedCallWithAssert(right, bindingContext());
076            if (!(rightCall.getResultingDescriptor() instanceof FunctionDescriptor)) {
077                return null;
078            }
079            FunctionDescriptor callDescriptor = (FunctionDescriptor) rightCall.getResultingDescriptor();
080            if (!INT_RANGE_TEST.apply(callDescriptor)) {
081                return null;
082            }
083            if (!(rightCall.getDispatchReceiver() instanceof ExpressionReceiver)) {
084                return null;
085            }
086    
087            KtExpression lower = ((ExpressionReceiver) rightCall.getDispatchReceiver()).getExpression();
088            KtExpression upper = rightCall.getCall().getValueArguments().get(0).getArgumentExpression();
089            assert upper != null : "Parse error occurred: " + PsiUtilsKt.getTextWithLocation(right);
090            return translateInt(lower, upper);
091        }
092    
093        @NotNull
094        private JsExpression translateInt(@NotNull KtExpression lowerExpression, @NotNull KtExpression upperExpression) {
095            JsExpression lower = Translation.translateAsExpression(lowerExpression, context());
096            JsExpression upper = Translation.translateAsExpression(upperExpression, context());
097            JsExpression first = JsAstUtils.greaterThanEq(left, lower);
098            JsExpression second = JsAstUtils.lessThanEq(left, upper);
099            return JsAstUtils.and(first, second);
100        }
101    }