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 FunctionIntrinsicFactory INSTANCE = new TopLevelFIF();
128
129 private TopLevelFIF() {
130 add(pattern("jet", "toString").receiverExists(), new KotlinFunctionIntrinsic("toString"));
131 add(pattern("jet", "equals").receiverExists(), EQUALS);
132 add(pattern("jet", "identityEquals").receiverExists(), IDENTITY_EQUALS);
133 add(pattern(NamePredicate.PRIMITIVE_NUMBERS, "equals"), EQUALS);
134 add(pattern("String|Boolean|Char|Number.equals"), EQUALS);
135 add(pattern("jet", "arrayOfNulls"), new KotlinFunctionIntrinsic("nullArray"));
136 add(pattern("jet", "iterator").receiverExists(), RETURN_RECEIVER_INTRINSIC);
137 add(new DescriptorPredicate() {
138 @Override
139 public boolean apply(@NotNull FunctionDescriptor descriptor) {
140 if (!descriptor.getName().asString().equals("invoke")) {
141 return false;
142 }
143 int parameterCount = descriptor.getValueParameters().size();
144 DeclarationDescriptor fun = descriptor.getContainingDeclaration();
145 return fun == (descriptor.getReceiverParameter() == null
146 ? KotlinBuiltIns.getInstance().getFunction(parameterCount)
147 : KotlinBuiltIns.getInstance().getExtensionFunction(parameterCount));
148 }
149 }, new CallParametersAwareFunctionIntrinsic() {
150 @NotNull
151 @Override
152 public JsExpression apply(
153 @NotNull CallTranslator callTranslator,
154 @NotNull List<JsExpression> arguments,
155 @NotNull TranslationContext context
156 ) {
157 JsExpression thisExpression = callTranslator.getCallParameters().getThisObject();
158 if (thisExpression == null) {
159 return new JsInvocation(callTranslator.getCallParameters().getFunctionReference(), arguments);
160 }
161 else if (callTranslator.getResolvedCall().getReceiverArgument().exists()) {
162 return callTranslator.extensionFunctionCall(false);
163 }
164 else {
165 return new JsInvocation(new JsNameRef("call", callTranslator.getCallParameters().getFunctionReference()),
166 generateInvocationArguments(thisExpression, arguments));
167 }
168 }
169 }
170 );
171
172 add(pattern("jet", "Map", "get").checkOverridden(), NATIVE_MAP_GET);
173 add(pattern("js", "set").receiverExists(), NATIVE_MAP_SET);
174
175 String[] javaUtil = {"java", "util"};
176 add(pattern(javaUtil, "HashMap", "<init>"), new MapSelectImplementationIntrinsic(false));
177 add(pattern(javaUtil, "HashSet", "<init>"), new MapSelectImplementationIntrinsic(true));
178
179 add(pattern("js", "Json", "get"), ArrayFIF.GET_INTRINSIC);
180 add(pattern("js", "Json", "set"), ArrayFIF.SET_INTRINSIC);
181 }
182
183 private abstract static class NativeMapGetSet extends CallParametersAwareFunctionIntrinsic {
184 @NotNull
185 protected abstract String operation();
186
187 @Nullable
188 protected abstract ExpressionReceiver getExpressionReceiver(@NotNull ResolvedCall<?> resolvedCall);
189
190 protected abstract JsExpression asArrayAccess(
191 @NotNull JsExpression receiver,
192 @NotNull List<JsExpression> arguments,
193 @NotNull TranslationContext context
194 );
195
196 @NotNull
197 @Override
198 public JsExpression apply(@NotNull CallTranslator callTranslator, @NotNull List<JsExpression> arguments, @NotNull TranslationContext context) {
199 ExpressionReceiver expressionReceiver = getExpressionReceiver(callTranslator.getResolvedCall());
200 JsExpression thisOrReceiver = callTranslator.getCallParameters().getThisOrReceiverOrNull();
201 assert thisOrReceiver != null;
202 if (expressionReceiver != null) {
203 JetExpression expression = expressionReceiver.getExpression();
204 JetReferenceExpression referenceExpression = null;
205 if (expression instanceof JetReferenceExpression) {
206 referenceExpression = (JetReferenceExpression) expression;
207 }
208 else if (expression instanceof JetQualifiedExpression) {
209 JetExpression candidate = ((JetQualifiedExpression) expression).getReceiverExpression();
210 if (candidate instanceof JetReferenceExpression) {
211 referenceExpression = (JetReferenceExpression) candidate;
212 }
213 }
214
215 if (referenceExpression != null) {
216 DeclarationDescriptor candidate = BindingUtils.getDescriptorForReferenceExpression(context.bindingContext(),
217 referenceExpression);
218 if (candidate instanceof PropertyDescriptor && AnnotationsUtils.isNativeObject(candidate)) {
219 return asArrayAccess(thisOrReceiver, arguments, context);
220 }
221 }
222 }
223
224 return new JsInvocation(new JsNameRef(operation(), thisOrReceiver), arguments);
225 }
226 }
227
228 private static class MapSelectImplementationIntrinsic extends CallParametersAwareFunctionIntrinsic {
229 private final boolean isSet;
230
231 private MapSelectImplementationIntrinsic(boolean isSet) {
232 this.isSet = isSet;
233 }
234
235 @NotNull
236 @Override
237 public JsExpression apply(
238 @NotNull CallTranslator callTranslator,
239 @NotNull List<JsExpression> arguments,
240 @NotNull TranslationContext context
241 ) {
242 JetType keyType = callTranslator.getResolvedCall().getTypeArguments().values().iterator().next();
243 Name keyTypeName = JsDescriptorUtils.getNameIfStandardType(keyType);
244 String collectionClassName;
245 if (keyTypeName != null &&
246 (NamePredicate.PRIMITIVE_NUMBERS.apply(keyTypeName) ||
247 keyTypeName.asString().equals("String") ||
248 PrimitiveType.BOOLEAN.getTypeName().equals(keyTypeName))) {
249 collectionClassName = isSet ? "PrimitiveHashSet" : "PrimitiveHashMap";
250 }
251 else {
252 collectionClassName = isSet ? "ComplexHashSet" : "ComplexHashMap";
253 }
254
255 return callTranslator.createConstructorCallExpression(context.namer().kotlin(collectionClassName));
256 }
257 }
258 }