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        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.negated(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    }