001    /*
002     * Copyright 2010-2015 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.expression;
018    
019    import com.google.dart.compiler.backend.js.ast.*;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.descriptors.*;
023    import org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsKt;
024    import org.jetbrains.kotlin.js.translate.context.Namer;
025    import org.jetbrains.kotlin.js.translate.context.TranslationContext;
026    import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
027    import org.jetbrains.kotlin.js.translate.general.Translation;
028    import org.jetbrains.kotlin.js.patterns.NamePredicate;
029    import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
030    import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
031    import org.jetbrains.kotlin.name.Name;
032    import org.jetbrains.kotlin.psi.KtExpression;
033    import org.jetbrains.kotlin.psi.KtIsExpression;
034    import org.jetbrains.kotlin.psi.KtTypeReference;
035    import org.jetbrains.kotlin.resolve.DescriptorUtils;
036    import org.jetbrains.kotlin.types.KotlinType;
037    
038    import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getTypeByReference;
039    import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*;
040    
041    public final class PatternTranslator extends AbstractTranslator {
042    
043        @NotNull
044        public static PatternTranslator newInstance(@NotNull TranslationContext context) {
045            return new PatternTranslator(context);
046        }
047    
048        private PatternTranslator(@NotNull TranslationContext context) {
049            super(context);
050        }
051    
052        @NotNull
053        public JsExpression translateIsExpression(@NotNull KtIsExpression expression) {
054            JsExpression left = Translation.translateAsExpression(expression.getLeftHandSide(), context());
055            KtTypeReference typeReference = expression.getTypeReference();
056            assert typeReference != null;
057            JsExpression result = translateIsCheck(left, typeReference);
058            if (expression.isNegated()) {
059                return negated(result);
060            }
061            return result;
062        }
063    
064        @NotNull
065        public JsExpression translateIsCheck(@NotNull JsExpression subject, @NotNull KtTypeReference typeReference) {
066            KotlinType type = BindingUtils.getTypeByReference(bindingContext(), typeReference);
067            JsExpression checkFunReference = getIsTypeCheckCallable(type);
068            JsInvocation isCheck = new JsInvocation(checkFunReference, subject);
069    
070            if (isNullable(typeReference)) {
071                return addNullCheck(subject, isCheck);
072            }
073    
074            return isCheck;
075        }
076    
077        @NotNull
078        public JsExpression getIsTypeCheckCallable(@NotNull KotlinType type) {
079            JsExpression builtinCheck = getIsTypeCheckCallableForBuiltin(type);
080            if (builtinCheck != null) return builtinCheck;
081    
082            ClassifierDescriptor typeDescriptor = type.getConstructor().getDeclarationDescriptor();
083    
084            if (typeDescriptor instanceof TypeParameterDescriptor) {
085                TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) typeDescriptor;
086    
087                if (typeParameterDescriptor.isReified()) {
088                    return getIsTypeCheckCallableForReifiedType(typeParameterDescriptor);
089                }
090            }
091    
092            JsNameRef typeName = getClassNameReference(type);
093            return namer().isInstanceOf(typeName);
094        }
095    
096        @Nullable
097        private JsExpression getIsTypeCheckCallableForBuiltin(@NotNull KotlinType type) {
098            Name typeName = DescriptorUtilsKt.getNameIfStandardType(type);
099    
100            if (NamePredicate.STRING.apply(typeName)) {
101                return namer().isTypeOf(program().getStringLiteral("string"));
102            }
103    
104            if (NamePredicate.BOOLEAN.apply(typeName)) {
105                return namer().isTypeOf(program().getStringLiteral("boolean"));
106            }
107    
108            if (NamePredicate.LONG.apply(typeName)) {
109                return namer().isInstanceOf(Namer.KOTLIN_LONG_NAME_REF);
110            }
111    
112            if (NamePredicate.NUMBER.apply(typeName)) {
113                return namer().kotlin(Namer.IS_NUMBER);
114            }
115    
116            if (NamePredicate.CHAR.apply(typeName)) {
117                return namer().kotlin(Namer.IS_CHAR);
118            }
119    
120            if (NamePredicate.PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS.apply(typeName)) {
121                return namer().isTypeOf(program().getStringLiteral("number"));
122            }
123    
124            return null;
125        }
126    
127        @NotNull
128        private JsExpression getIsTypeCheckCallableForReifiedType(@NotNull TypeParameterDescriptor typeParameter) {
129            assert typeParameter.isReified(): "Expected reified type, actual: " + typeParameter;
130            DeclarationDescriptor containingDeclaration = typeParameter.getContainingDeclaration();
131            assert containingDeclaration instanceof CallableDescriptor:
132                    "Expected type parameter " + typeParameter +
133                    " to be contained in CallableDescriptor, actual: " + containingDeclaration.getClass();
134    
135            JsExpression alias = context().getAliasForDescriptor(typeParameter);
136            assert alias != null: "No alias found for reified type parameter: " + typeParameter;
137            return alias;
138        }
139    
140        @NotNull
141        private static JsExpression addNullCheck(@NotNull JsExpression expressionToMatch, @NotNull JsInvocation isCheck) {
142            return or(TranslationUtils.isNullCheck(expressionToMatch), isCheck);
143        }
144    
145        private boolean isNullable(KtTypeReference typeReference) {
146            return getTypeByReference(bindingContext(), typeReference).isMarkedNullable();
147        }
148    
149        @NotNull
150        private JsNameRef getClassNameReference(@NotNull KotlinType type) {
151            ClassDescriptor referencedClass = DescriptorUtils.getClassDescriptorForType(type);
152            return context().getQualifiedReference(referencedClass);
153        }
154    
155        @NotNull
156        public JsExpression translateExpressionPattern(@NotNull JsExpression expressionToMatch, @NotNull KtExpression patternExpression) {
157            JsExpression expressionToMatchAgainst = translateExpressionForExpressionPattern(patternExpression);
158            return equality(expressionToMatch, expressionToMatchAgainst);
159        }
160    
161        @NotNull
162        public JsExpression translateExpressionForExpressionPattern(@NotNull KtExpression patternExpression) {
163            return Translation.translateAsExpression(patternExpression, context());
164        }
165    }