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