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