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