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 }