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