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 }