001 /*
002 * Copyright 2010-2013 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.k2js.translate.expression;
018
019 import com.google.dart.compiler.backend.js.ast.JsExpression;
020 import com.google.dart.compiler.backend.js.ast.JsInvocation;
021 import com.google.dart.compiler.backend.js.ast.JsNameRef;
022 import org.jetbrains.annotations.NotNull;
023 import org.jetbrains.annotations.Nullable;
024 import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
025 import org.jetbrains.jet.lang.psi.JetExpression;
026 import org.jetbrains.jet.lang.psi.JetIsExpression;
027 import org.jetbrains.jet.lang.psi.JetTypeReference;
028 import org.jetbrains.jet.lang.resolve.name.Name;
029 import org.jetbrains.k2js.translate.context.TranslationContext;
030 import org.jetbrains.k2js.translate.general.AbstractTranslator;
031 import org.jetbrains.k2js.translate.general.Translation;
032 import org.jetbrains.k2js.translate.intrinsic.functions.patterns.NamePredicate;
033 import org.jetbrains.k2js.translate.utils.BindingUtils;
034 import org.jetbrains.k2js.translate.utils.JsAstUtils;
035 import org.jetbrains.k2js.translate.utils.TranslationUtils;
036
037 import static org.jetbrains.k2js.translate.utils.BindingUtils.getTypeByReference;
038 import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
039 import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getNameIfStandardType;
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 JetIsExpression expression) {
054 JsExpression left = Translation.translateAsExpression(expression.getLeftHandSide(), context());
055 JetTypeReference 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 JetTypeReference typeReference) {
066 JsExpression result = translateAsIntrinsicTypeCheck(subject, typeReference);
067 if (result != null) {
068 return result;
069 }
070 return translateAsIsCheck(subject, typeReference);
071 }
072
073 @NotNull
074 private JsExpression translateAsIsCheck(@NotNull JsExpression expressionToMatch,
075 @NotNull JetTypeReference typeReference) {
076 JsInvocation isCheck = new JsInvocation(context().namer().isOperationReference(),
077 expressionToMatch, getClassNameReference(typeReference));
078 if (isNullable(typeReference)) {
079 return addNullCheck(expressionToMatch, isCheck);
080 }
081 return isCheck;
082 }
083
084 @Nullable
085 private JsExpression translateAsIntrinsicTypeCheck(@NotNull JsExpression expressionToMatch,
086 @NotNull JetTypeReference typeReference) {
087 Name typeName = getNameIfStandardType(getTypeByReference(bindingContext(), typeReference));
088 if (typeName == null) {
089 return null;
090 }
091
092 String jsSTypeName;
093 if (NamePredicate.STRING.apply(typeName)) {
094 jsSTypeName = "string";
095 }
096 else if (NamePredicate.LONG.apply(typeName)) {
097 return JsAstUtils.isLong(expressionToMatch);
098 }
099 else if (NamePredicate.NUMBER.apply(typeName)) {
100 return JsAstUtils.isNumber(expressionToMatch);
101 }
102 else if (NamePredicate.CHAR.apply(typeName)) {
103 return JsAstUtils.isChar(expressionToMatch);
104 }
105 else if (NamePredicate.PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS.apply(typeName)) {
106 jsSTypeName = "number";
107 }
108 else {
109 return null;
110 }
111 return typeof(expressionToMatch, program().getStringLiteral(jsSTypeName));
112 }
113
114 @NotNull
115 private static JsExpression addNullCheck(@NotNull JsExpression expressionToMatch, @NotNull JsInvocation isCheck) {
116 return or(TranslationUtils.isNullCheck(expressionToMatch), isCheck);
117 }
118
119 private boolean isNullable(JetTypeReference typeReference) {
120 return getTypeByReference(bindingContext(), typeReference).isNullable();
121 }
122
123 @NotNull
124 private JsNameRef getClassNameReference(@NotNull JetTypeReference typeReference) {
125 ClassDescriptor referencedClass = BindingUtils.getClassDescriptorForTypeReference
126 (bindingContext(), typeReference);
127 return context().getQualifiedReference(referencedClass);
128 }
129
130 @NotNull
131 public JsExpression translateExpressionPattern(@NotNull JsExpression expressionToMatch, @NotNull JetExpression patternExpression) {
132 JsExpression expressionToMatchAgainst = translateExpressionForExpressionPattern(patternExpression);
133 return equality(expressionToMatch, expressionToMatchAgainst);
134 }
135
136 @NotNull
137 public JsExpression translateExpressionForExpressionPattern(@NotNull JetExpression patternExpression) {
138 return Translation.translateAsExpression(patternExpression, context());
139 }
140 }