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.intrinsic.functions.factories;
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.builtins.PrimitiveType;
023 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
024 import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
025 import org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsKt;
026 import org.jetbrains.kotlin.js.patterns.DescriptorPredicate;
027 import org.jetbrains.kotlin.js.patterns.NamePredicate;
028 import org.jetbrains.kotlin.js.resolve.JsPlatform;
029 import org.jetbrains.kotlin.js.translate.callTranslator.CallInfo;
030 import org.jetbrains.kotlin.js.translate.context.TranslationContext;
031 import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic;
032 import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
033 import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
034 import org.jetbrains.kotlin.name.Name;
035 import org.jetbrains.kotlin.psi.KtExpression;
036 import org.jetbrains.kotlin.psi.KtQualifiedExpression;
037 import org.jetbrains.kotlin.psi.KtReferenceExpression;
038 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
039 import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
040 import org.jetbrains.kotlin.resolve.scopes.receivers.Receiver;
041 import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
042 import org.jetbrains.kotlin.types.KotlinType;
043
044 import java.util.List;
045
046 import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES;
047 import static org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern;
048 import static org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic.CallParametersAwareFunctionIntrinsic;
049 import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getStableMangledNameForDescriptor;
050
051 public final class TopLevelFIF extends CompositeFIF {
052 public static final DescriptorPredicate EQUALS_IN_ANY = pattern("kotlin", "Any", "equals");
053 @NotNull
054 public static final KotlinFunctionIntrinsic KOTLIN_EQUALS = new KotlinFunctionIntrinsic("equals");
055
056 @NotNull
057 public static final DescriptorPredicate HASH_CODE_IN_ANY = pattern("kotlin", "Any", "hashCode");
058 @NotNull
059 public static final KotlinFunctionIntrinsic KOTLIN_HASH_CODE = new KotlinFunctionIntrinsic("hashCode");
060
061 @NotNull
062 private static final FunctionIntrinsic RETURN_RECEIVER_INTRINSIC = new FunctionIntrinsic() {
063 @NotNull
064 @Override
065 public JsExpression apply(
066 @Nullable JsExpression receiver,
067 @NotNull List<JsExpression> arguments,
068 @NotNull TranslationContext context
069 ) {
070 assert receiver != null;
071 return receiver;
072 }
073 };
074
075 private static final FunctionIntrinsic NATIVE_MAP_GET = new NativeMapGetSet() {
076 @NotNull
077 @Override
078 protected String operationName() {
079 return "get";
080 }
081
082 @Nullable
083 @Override
084 protected ExpressionReceiver getExpressionReceiver(@NotNull ResolvedCall<?> resolvedCall) {
085 ReceiverValue result = resolvedCall.getDispatchReceiver();
086 return result instanceof ExpressionReceiver ? (ExpressionReceiver) result : null;
087 }
088
089 @Override
090 protected JsExpression asArrayAccess(
091 @NotNull JsExpression receiver,
092 @NotNull List<JsExpression> arguments,
093 @NotNull TranslationContext context
094 ) {
095 return ArrayFIF.GET_INTRINSIC.apply(receiver, arguments, context);
096 }
097 };
098
099 private static final FunctionIntrinsic NATIVE_MAP_SET = new NativeMapGetSet() {
100 @NotNull
101 @Override
102 protected String operationName() {
103 return "put";
104 }
105
106 @Nullable
107 @Override
108 protected ExpressionReceiver getExpressionReceiver(@NotNull ResolvedCall<?> resolvedCall) {
109 Receiver result = resolvedCall.getExtensionReceiver();
110 return result instanceof ExpressionReceiver ? (ExpressionReceiver) result : null;
111 }
112
113 @Override
114 protected JsExpression asArrayAccess(
115 @NotNull JsExpression receiver,
116 @NotNull List<JsExpression> arguments,
117 @NotNull TranslationContext context
118 ) {
119 return ArrayFIF.SET_INTRINSIC.apply(receiver, arguments, context);
120 }
121 };
122
123 @NotNull
124 public static final KotlinFunctionIntrinsic TO_STRING = new KotlinFunctionIntrinsic("toString");
125
126 @NotNull
127 public static final FunctionIntrinsicFactory INSTANCE = new TopLevelFIF();
128
129 private TopLevelFIF() {
130 add(EQUALS_IN_ANY, KOTLIN_EQUALS);
131 add(pattern("kotlin", "toString").isExtensionOf(FQ_NAMES.any.asString()), TO_STRING);
132 add(pattern("kotlin", "equals").isExtensionOf(FQ_NAMES.any.asString()), KOTLIN_EQUALS);
133 add(HASH_CODE_IN_ANY, KOTLIN_HASH_CODE);
134 add(pattern(NamePredicate.PRIMITIVE_NUMBERS, "equals"), KOTLIN_EQUALS);
135 add(pattern("String|Boolean|Char|Number.equals"), KOTLIN_EQUALS);
136 add(pattern("kotlin", "arrayOfNulls"), new KotlinFunctionIntrinsic("nullArray"));
137 add(pattern("kotlin", "iterator").isExtensionOf(FQ_NAMES.iterator.asString()), RETURN_RECEIVER_INTRINSIC);
138
139 add(pattern("kotlin.collections", "Map", "get").checkOverridden(), NATIVE_MAP_GET);
140 add(pattern("kotlin.js", "set").isExtensionOf(FQ_NAMES.mutableMap.asString()), NATIVE_MAP_SET);
141
142 add(pattern("java.util", "HashMap", "<init>"), new MapSelectImplementationIntrinsic(false));
143 add(pattern("java.util", "HashSet", "<init>"), new MapSelectImplementationIntrinsic(true));
144
145 add(pattern("kotlin.js", "Json", "get"), ArrayFIF.GET_INTRINSIC);
146 add(pattern("kotlin.js", "Json", "set"), ArrayFIF.SET_INTRINSIC);
147
148 add(pattern("kotlin", "Throwable", "getMessage"), MESSAGE_PROPERTY_INTRINSIC);
149 }
150
151 private abstract static class NativeMapGetSet extends CallParametersAwareFunctionIntrinsic {
152 @NotNull
153 protected abstract String operationName();
154
155 @Nullable
156 protected abstract ExpressionReceiver getExpressionReceiver(@NotNull ResolvedCall<?> resolvedCall);
157
158 protected abstract JsExpression asArrayAccess(
159 @NotNull JsExpression receiver,
160 @NotNull List<JsExpression> arguments,
161 @NotNull TranslationContext context
162 );
163
164 @NotNull
165 @Override
166 public JsExpression apply(@NotNull CallInfo callInfo, @NotNull List<JsExpression> arguments, @NotNull TranslationContext context) {
167 ExpressionReceiver expressionReceiver = getExpressionReceiver(callInfo.getResolvedCall());
168 JsExpression thisOrReceiver = getThisOrReceiverOrNull(callInfo);
169 assert thisOrReceiver != null;
170 if (expressionReceiver != null) {
171 KtExpression expression = expressionReceiver.getExpression();
172 KtReferenceExpression referenceExpression = null;
173 if (expression instanceof KtReferenceExpression) {
174 referenceExpression = (KtReferenceExpression) expression;
175 }
176 else if (expression instanceof KtQualifiedExpression) {
177 KtExpression candidate = ((KtQualifiedExpression) expression).getReceiverExpression();
178 if (candidate instanceof KtReferenceExpression) {
179 referenceExpression = (KtReferenceExpression) candidate;
180 }
181 }
182
183 if (referenceExpression != null) {
184 DeclarationDescriptor candidate = BindingUtils.getDescriptorForReferenceExpression(context.bindingContext(),
185 referenceExpression);
186 if (candidate instanceof PropertyDescriptor && AnnotationsUtils.isNativeObject(candidate)) {
187 return asArrayAccess(thisOrReceiver, arguments, context);
188 }
189 }
190 }
191
192 String mangledName = getStableMangledNameForDescriptor(JsPlatform.INSTANCE.getBuiltIns().getMutableMap(), operationName());
193
194 return new JsInvocation(new JsNameRef(mangledName, thisOrReceiver), arguments);
195 }
196 }
197
198 private static class MapSelectImplementationIntrinsic extends CallParametersAwareFunctionIntrinsic {
199 private final boolean isSet;
200
201 private MapSelectImplementationIntrinsic(boolean isSet) {
202 this.isSet = isSet;
203 }
204
205 @NotNull
206 @Override
207 public JsExpression apply(
208 @NotNull CallInfo callInfo,
209 @NotNull List<JsExpression> arguments,
210 @NotNull TranslationContext context
211 ) {
212 KotlinType keyType = callInfo.getResolvedCall().getTypeArguments().values().iterator().next();
213 Name keyTypeName = DescriptorUtilsKt.getNameIfStandardType(keyType);
214 String collectionClassName = null;
215 if (keyTypeName != null) {
216 if (NamePredicate.PRIMITIVE_NUMBERS.apply(keyTypeName)) {
217 collectionClassName = isSet ? "PrimitiveNumberHashSet" : "PrimitiveNumberHashMap";
218 }
219 else if (PrimitiveType.BOOLEAN.getTypeName().equals(keyTypeName)) {
220 collectionClassName = isSet ? "PrimitiveBooleanHashSet" : "PrimitiveBooleanHashMap";
221 }
222 else if (keyTypeName.asString().equals("String")) {
223 collectionClassName = isSet ? "DefaultPrimitiveHashSet" : "DefaultPrimitiveHashMap";
224 }
225 }
226
227 if (collectionClassName == null ) {
228 collectionClassName = isSet ? "ComplexHashSet" : "ComplexHashMap";
229 }
230
231 return new JsNew(context.namer().kotlin(collectionClassName), arguments);
232 }
233 }
234 }