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