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