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