001 /*
002 * Copyright 2010-2015 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.kotlin.js.translate.utils;
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.kotlin.builtins.KotlinBuiltIns;
023 import org.jetbrains.kotlin.descriptors.*;
024 import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
025 import org.jetbrains.kotlin.js.translate.context.Namer;
026 import org.jetbrains.kotlin.js.translate.context.StaticContext;
027 import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable;
028 import org.jetbrains.kotlin.js.translate.context.TranslationContext;
029 import org.jetbrains.kotlin.js.translate.general.Translation;
030 import org.jetbrains.kotlin.psi.*;
031 import org.jetbrains.kotlin.resolve.DescriptorUtils;
032 import org.jetbrains.kotlin.types.KotlinType;
033
034 import java.util.ArrayList;
035 import java.util.List;
036
037 import static com.google.dart.compiler.backend.js.ast.JsBinaryOperator.*;
038 import static org.jetbrains.kotlin.js.translate.context.Namer.getKotlinBackingFieldName;
039 import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
040 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.assignment;
041 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.createDataDescriptor;
042 import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getMangledName;
043 import static org.jetbrains.kotlin.resolve.DescriptorUtils.isAnonymousObject;
044
045 public final class TranslationUtils {
046
047 private TranslationUtils() {
048 }
049
050 @NotNull
051 public static JsPropertyInitializer translateFunctionAsEcma5PropertyDescriptor(@NotNull JsFunction function,
052 @NotNull FunctionDescriptor descriptor,
053 @NotNull TranslationContext context) {
054 if (DescriptorUtils.isExtension(descriptor)) {
055 return translateExtensionFunctionAsEcma5DataDescriptor(function, descriptor, context);
056 }
057 else {
058 JsStringLiteral getOrSet = context.program().getStringLiteral(descriptor instanceof PropertyGetterDescriptor ? "get" : "set");
059 return new JsPropertyInitializer(getOrSet, function);
060 }
061 }
062
063 @NotNull
064 public static JsFunction simpleReturnFunction(@NotNull JsScope functionScope, @NotNull JsExpression returnExpression) {
065 return new JsFunction(functionScope, new JsBlock(new JsReturn(returnExpression)), "<simpleReturnFunction>");
066 }
067
068 @NotNull
069 private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsFunction function,
070 @NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) {
071 JsObjectLiteral meta = createDataDescriptor(function, ModalityKt.isOverridable(descriptor), false);
072 return new JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), meta);
073 }
074
075 @NotNull
076 public static JsExpression translateExclForBinaryEqualLikeExpr(@NotNull JsBinaryOperation baseBinaryExpression) {
077 return new JsBinaryOperation(notOperator(baseBinaryExpression.getOperator()), baseBinaryExpression.getArg1(),
078 baseBinaryExpression.getArg2());
079 }
080
081 public static boolean isEqualLikeOperator(@NotNull JsBinaryOperator operator) {
082 return notOperator(operator) != null;
083 }
084
085 @Nullable
086 private static JsBinaryOperator notOperator(@NotNull JsBinaryOperator operator) {
087 switch (operator) {
088 case REF_EQ:
089 return REF_NEQ;
090 case REF_NEQ:
091 return REF_EQ;
092 case EQ:
093 return NEQ;
094 case NEQ:
095 return EQ;
096 default:
097 return null;
098 }
099 }
100
101 @NotNull
102 public static JsBinaryOperation isNullCheck(@NotNull JsExpression expressionToCheck) {
103 return nullCheck(expressionToCheck, false);
104 }
105
106 @NotNull
107 public static JsBinaryOperation isNotNullCheck(@NotNull JsExpression expressionToCheck) {
108 return nullCheck(expressionToCheck, true);
109 }
110
111 @NotNull
112 public static JsBinaryOperation nullCheck(@NotNull JsExpression expressionToCheck, boolean isNegated) {
113 JsBinaryOperator operator = isNegated ? JsBinaryOperator.NEQ : JsBinaryOperator.EQ;
114 return new JsBinaryOperation(operator, expressionToCheck, JsLiteral.NULL);
115 }
116
117 @NotNull
118 public static JsConditional notNullConditional(
119 @NotNull JsExpression expression,
120 @NotNull JsExpression elseExpression,
121 @NotNull TranslationContext context
122 ) {
123 JsExpression testExpression;
124 JsExpression thenExpression;
125 if (isCacheNeeded(expression)) {
126 TemporaryConstVariable tempVar = context.getOrDeclareTemporaryConstVariable(expression);
127 testExpression = isNotNullCheck(tempVar.value());
128 thenExpression = tempVar.value();
129 }
130 else {
131 testExpression = isNotNullCheck(expression);
132 thenExpression = expression;
133 }
134
135 return new JsConditional(testExpression, thenExpression, elseExpression);
136 }
137
138 @NotNull
139 public static JsNameRef backingFieldReference(@NotNull TranslationContext context,
140 @NotNull PropertyDescriptor descriptor) {
141 JsName backingFieldName = context.getNameForDescriptor(descriptor);
142 if(!JsDescriptorUtils.isSimpleFinalProperty(descriptor)) {
143 String backingFieldMangledName;
144 if (!Visibilities.isPrivate(descriptor.getVisibility())) {
145 backingFieldMangledName = getMangledName(descriptor, getKotlinBackingFieldName(backingFieldName.getIdent()));
146 } else {
147 backingFieldMangledName = getKotlinBackingFieldName(backingFieldName.getIdent());
148 }
149 backingFieldName = context.declarePropertyOrPropertyAccessorName(descriptor, backingFieldMangledName, false);
150 }
151
152 DeclarationDescriptor containingDescriptor = descriptor.getContainingDeclaration();
153 JsExpression receiver;
154 if (containingDescriptor instanceof PackageFragmentDescriptor) {
155 // used inside package initializer
156 receiver = JsLiteral.THIS;
157 }
158 else {
159 receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor));
160 }
161 return new JsNameRef(backingFieldName, receiver);
162 }
163
164 @NotNull
165 public static JsExpression assignmentToBackingField(@NotNull TranslationContext context,
166 @NotNull PropertyDescriptor descriptor,
167 @NotNull JsExpression assignTo) {
168 JsNameRef backingFieldReference = backingFieldReference(context, descriptor);
169 return assignment(backingFieldReference, assignTo);
170 }
171
172 @Nullable
173 public static JsExpression translateInitializerForProperty(@NotNull KtProperty declaration,
174 @NotNull TranslationContext context) {
175 JsExpression jsInitExpression = null;
176 KtExpression initializer = declaration.getInitializer();
177 if (initializer != null) {
178 jsInitExpression = Translation.translateAsExpression(initializer, context);
179 }
180 return jsInitExpression;
181 }
182
183 @NotNull
184 public static JsExpression translateBaseExpression(@NotNull TranslationContext context,
185 @NotNull KtUnaryExpression expression) {
186 KtExpression baseExpression = PsiUtils.getBaseExpression(expression);
187 return Translation.translateAsExpression(baseExpression, context);
188 }
189
190 @NotNull
191 public static JsExpression translateLeftExpression(
192 @NotNull TranslationContext context,
193 @NotNull KtBinaryExpression expression,
194 @NotNull JsBlock block
195 ) {
196 KtExpression left = expression.getLeft();
197 assert left != null : "Binary expression should have a left expression: " + expression.getText();
198 return Translation.translateAsExpression(left, context, block);
199 }
200
201 @NotNull
202 public static JsExpression translateRightExpression(@NotNull TranslationContext context,
203 @NotNull KtBinaryExpression expression) {
204 return translateRightExpression(context, expression, context.dynamicContext().jsBlock());
205 }
206
207 @NotNull
208 public static JsExpression translateRightExpression(
209 @NotNull TranslationContext context,
210 @NotNull KtBinaryExpression expression,
211 @NotNull JsBlock block) {
212 KtExpression rightExpression = expression.getRight();
213 assert rightExpression != null : "Binary expression should have a right expression";
214 return Translation.translateAsExpression(rightExpression, context, block);
215 }
216
217 public static boolean hasCorrespondingFunctionIntrinsic(@NotNull TranslationContext context,
218 @NotNull KtOperationExpression expression) {
219 CallableDescriptor operationDescriptor = getCallableDescriptorForOperationExpression(context.bindingContext(), expression);
220
221 if (operationDescriptor == null || !(operationDescriptor instanceof FunctionDescriptor)) return true;
222
223 KotlinType returnType = operationDescriptor.getReturnType();
224 if (returnType != null && (KotlinBuiltIns.isChar(returnType) || KotlinBuiltIns.isLong(returnType))) return false;
225
226 if (context.intrinsics().getFunctionIntrinsic((FunctionDescriptor) operationDescriptor).exists()) return true;
227
228 return false;
229 }
230
231 @NotNull
232 public static List<JsExpression> generateInvocationArguments(@NotNull JsExpression receiver, @NotNull List<JsExpression> arguments) {
233 List<JsExpression> argumentList = new ArrayList<JsExpression>(1 + arguments.size());
234 argumentList.add(receiver);
235 argumentList.addAll(arguments);
236 return argumentList;
237 }
238
239 public static boolean isCacheNeeded(@NotNull JsExpression expression) {
240 return !(expression instanceof JsLiteral.JsValueLiteral) &&
241 (!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null);
242 }
243
244 @NotNull
245 public static JsConditional sure(@NotNull JsExpression expression, @NotNull TranslationContext context) {
246 JsInvocation throwNPE = new JsInvocation(Namer.throwNPEFunctionRef());
247 JsConditional ensureNotNull = notNullConditional(expression, throwNPE, context);
248
249 JsExpression thenExpression = ensureNotNull.getThenExpression();
250 if (thenExpression instanceof JsNameRef) {
251 JsName name = ((JsNameRef) thenExpression).getName();
252 if (name != null) {
253 // associate(cache) ensureNotNull expression to new TemporaryConstVariable with same name.
254 context.associateExpressionToLazyValue(ensureNotNull, new TemporaryConstVariable(name, ensureNotNull));
255 }
256 }
257
258 return ensureNotNull;
259 }
260
261 @NotNull
262 public static String getSuggestedNameForInnerDeclaration(StaticContext context, DeclarationDescriptor descriptor) {
263 String suggestedName = "";
264 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
265 //noinspection ConstantConditions
266 if (containingDeclaration != null &&
267 !(containingDeclaration instanceof ClassOrPackageFragmentDescriptor) &&
268 !(containingDeclaration instanceof AnonymousFunctionDescriptor) &&
269 !(containingDeclaration instanceof ConstructorDescriptor && isAnonymousObject(containingDeclaration.getContainingDeclaration()))) {
270 suggestedName = context.getNameForDescriptor(containingDeclaration).getIdent();
271 }
272
273 if (!suggestedName.isEmpty() && !suggestedName.endsWith("$")) {
274 suggestedName += "$";
275 }
276
277 if (descriptor.getName().isSpecial()) {
278 suggestedName += "f";
279 }
280 else {
281 suggestedName += context.getNameForDescriptor(descriptor).getIdent();
282 }
283 return suggestedName;
284 }
285 }