001 /*
002 * Copyright 2010-2016 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 org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
022 import org.jetbrains.kotlin.descriptors.*;
023 import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor;
024 import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
025 import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
026 import org.jetbrains.kotlin.js.backend.ast.*;
027 import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator;
028 import org.jetbrains.kotlin.js.translate.context.Namer;
029 import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable;
030 import org.jetbrains.kotlin.js.translate.context.TranslationContext;
031 import org.jetbrains.kotlin.js.translate.expression.InlineMetadata;
032 import org.jetbrains.kotlin.js.translate.general.Translation;
033 import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator;
034 import org.jetbrains.kotlin.name.ClassId;
035 import org.jetbrains.kotlin.name.FqName;
036 import org.jetbrains.kotlin.name.Name;
037 import org.jetbrains.kotlin.psi.*;
038 import org.jetbrains.kotlin.resolve.BindingContext;
039 import org.jetbrains.kotlin.resolve.BindingContextUtils;
040 import org.jetbrains.kotlin.resolve.DescriptorUtils;
041 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
042 import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
043 import org.jetbrains.kotlin.resolve.inline.InlineUtil;
044 import org.jetbrains.kotlin.serialization.deserialization.FindClassInModuleKt;
045 import org.jetbrains.kotlin.types.KotlinType;
046 import org.jetbrains.kotlin.types.TypeUtils;
047
048 import java.util.ArrayList;
049 import java.util.List;
050
051 import static org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator.*;
052 import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
053 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.assignment;
054 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.createDataDescriptor;
055
056 public final class TranslationUtils {
057
058 private TranslationUtils() {
059 }
060
061 @NotNull
062 public static JsPropertyInitializer translateFunctionAsEcma5PropertyDescriptor(@NotNull JsFunction function,
063 @NotNull FunctionDescriptor descriptor,
064 @NotNull TranslationContext context) {
065 JsExpression functionExpression = function;
066 if (InlineUtil.isInline(descriptor)) {
067 InlineMetadata metadata = InlineMetadata.compose(function, descriptor);
068 functionExpression = metadata.getFunctionWithMetadata();
069 }
070
071 if (DescriptorUtils.isExtension(descriptor) ||
072 descriptor instanceof PropertyAccessorDescriptor &&
073 shouldAccessViaFunctions(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty())
074 ) {
075 return translateExtensionFunctionAsEcma5DataDescriptor(functionExpression, descriptor, context);
076 }
077 else {
078 JsStringLiteral getOrSet = context.program().getStringLiteral(getAccessorFunctionName(descriptor));
079 return new JsPropertyInitializer(getOrSet, functionExpression);
080 }
081 }
082
083 @NotNull
084 public static String getAccessorFunctionName(@NotNull FunctionDescriptor descriptor) {
085 boolean isGetter = descriptor instanceof PropertyGetterDescriptor || descriptor instanceof LocalVariableAccessorDescriptor.Getter;
086 return isGetter ? "get" : "set";
087 }
088
089 @NotNull
090 public static JsFunction simpleReturnFunction(@NotNull JsScope functionScope, @NotNull JsExpression returnExpression) {
091 return new JsFunction(functionScope, new JsBlock(new JsReturn(returnExpression)), "<simpleReturnFunction>");
092 }
093
094 @NotNull
095 private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsExpression functionExpression,
096 @NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) {
097 JsObjectLiteral meta = createDataDescriptor(functionExpression, ModalityKt.isOverridable(descriptor), false);
098 return new JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), meta);
099 }
100
101 @NotNull
102 public static JsExpression translateExclForBinaryEqualLikeExpr(@NotNull JsBinaryOperation baseBinaryExpression) {
103 JsBinaryOperator negatedOperator = notOperator(baseBinaryExpression.getOperator());
104 assert negatedOperator != null : "Can't negate operator: " + baseBinaryExpression.getOperator();
105 return new JsBinaryOperation(negatedOperator, baseBinaryExpression.getArg1(), baseBinaryExpression.getArg2());
106 }
107
108 public static boolean isEqualLikeOperator(@NotNull JsBinaryOperator operator) {
109 return notOperator(operator) != null;
110 }
111
112 @Nullable
113 private static JsBinaryOperator notOperator(@NotNull JsBinaryOperator operator) {
114 switch (operator) {
115 case REF_EQ:
116 return REF_NEQ;
117 case REF_NEQ:
118 return REF_EQ;
119 case EQ:
120 return NEQ;
121 case NEQ:
122 return EQ;
123 default:
124 return null;
125 }
126 }
127
128 @NotNull
129 public static JsBinaryOperation isNullCheck(@NotNull JsExpression expressionToCheck) {
130 return nullCheck(expressionToCheck, false);
131 }
132
133 @NotNull
134 private static JsBinaryOperation isNotNullCheck(@NotNull JsExpression expressionToCheck) {
135 return nullCheck(expressionToCheck, true);
136 }
137
138 @NotNull
139 public static JsBinaryOperation nullCheck(@NotNull JsExpression expressionToCheck, boolean isNegated) {
140 JsBinaryOperator operator = isNegated ? JsBinaryOperator.NEQ : JsBinaryOperator.EQ;
141 return new JsBinaryOperation(operator, expressionToCheck, JsLiteral.NULL);
142 }
143
144 @NotNull
145 public static JsConditional notNullConditional(
146 @NotNull JsExpression expression,
147 @NotNull JsExpression elseExpression,
148 @NotNull TranslationContext context
149 ) {
150 JsExpression testExpression;
151 JsExpression thenExpression;
152 if (isCacheNeeded(expression)) {
153 TemporaryConstVariable tempVar = context.getOrDeclareTemporaryConstVariable(expression);
154 testExpression = isNotNullCheck(tempVar.value());
155 thenExpression = tempVar.value();
156 }
157 else {
158 testExpression = isNotNullCheck(expression);
159 thenExpression = expression;
160 }
161
162 return new JsConditional(testExpression, thenExpression, elseExpression);
163 }
164
165 @NotNull
166 public static JsNameRef backingFieldReference(@NotNull TranslationContext context, @NotNull PropertyDescriptor descriptor) {
167 DeclarationDescriptor containingDescriptor = descriptor.getContainingDeclaration();
168 JsName backingFieldName = containingDescriptor instanceof PackageFragmentDescriptor ?
169 context.getInnerNameForDescriptor(descriptor) :
170 context.getNameForDescriptor(descriptor);
171
172 if (!JsDescriptorUtils.isSimpleFinalProperty(descriptor) && !(containingDescriptor instanceof PackageFragmentDescriptor)) {
173 backingFieldName = context.getNameForBackingField(descriptor);
174 }
175
176 JsExpression receiver;
177 if (containingDescriptor instanceof PackageFragmentDescriptor) {
178 receiver = null;
179 }
180 else {
181 receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor));
182 }
183 return new JsNameRef(backingFieldName, receiver);
184 }
185
186 @NotNull
187 public static JsExpression assignmentToBackingField(@NotNull TranslationContext context,
188 @NotNull PropertyDescriptor descriptor,
189 @NotNull JsExpression assignTo) {
190 JsNameRef backingFieldReference = backingFieldReference(context, descriptor);
191 return assignment(backingFieldReference, assignTo);
192 }
193
194 @Nullable
195 public static JsExpression translateInitializerForProperty(@NotNull KtProperty declaration,
196 @NotNull TranslationContext context) {
197 JsExpression jsInitExpression = null;
198 KtExpression initializer = declaration.getInitializer();
199 if (initializer != null) {
200 jsInitExpression = Translation.translateAsExpression(initializer, context);
201
202 KotlinType propertyType = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.VARIABLE, declaration).getType();
203 KotlinType initType = context.bindingContext().getType(initializer);
204
205 if (initType != null && KotlinBuiltIns.isCharOrNullableChar(initType) && !KotlinBuiltIns.isCharOrNullableChar(propertyType)) {
206 jsInitExpression = JsAstUtils.charToBoxedChar(jsInitExpression);
207 }
208 }
209 return jsInitExpression;
210 }
211
212 @NotNull
213 public static JsExpression translateBaseExpression(@NotNull TranslationContext context,
214 @NotNull KtUnaryExpression expression) {
215 KtExpression baseExpression = PsiUtils.getBaseExpression(expression);
216 return Translation.translateAsExpression(baseExpression, context);
217 }
218
219 @NotNull
220 public static JsExpression translateLeftExpression(
221 @NotNull TranslationContext context,
222 @NotNull KtBinaryExpression expression,
223 @NotNull JsBlock block
224 ) {
225 KtExpression left = expression.getLeft();
226 assert left != null : "Binary expression should have a left expression: " + expression.getText();
227 return Translation.translateAsExpression(left, context, block);
228 }
229
230 @NotNull
231 public static JsExpression translateRightExpression(@NotNull TranslationContext context,
232 @NotNull KtBinaryExpression expression) {
233 return translateRightExpression(context, expression, context.dynamicContext().jsBlock());
234 }
235
236 @NotNull
237 public static JsExpression translateRightExpression(
238 @NotNull TranslationContext context,
239 @NotNull KtBinaryExpression expression,
240 @NotNull JsBlock block) {
241 KtExpression rightExpression = expression.getRight();
242 assert rightExpression != null : "Binary expression should have a right expression";
243 return Translation.translateAsExpression(rightExpression, context, block);
244 }
245
246 public static boolean hasCorrespondingFunctionIntrinsic(@NotNull TranslationContext context,
247 @NotNull KtOperationExpression expression) {
248 CallableDescriptor operationDescriptor = getCallableDescriptorForOperationExpression(context.bindingContext(), expression);
249
250 if (operationDescriptor == null || !(operationDescriptor instanceof FunctionDescriptor)) return true;
251
252 KotlinType returnType = operationDescriptor.getReturnType();
253 if (returnType != null &&
254 (KotlinBuiltIns.isChar(returnType) || KotlinBuiltIns.isLong(returnType) || KotlinBuiltIns.isInt(returnType))) {
255 return false;
256 }
257
258 if (context.intrinsics().getFunctionIntrinsic((FunctionDescriptor) operationDescriptor).exists()) return true;
259
260 return false;
261 }
262
263 @NotNull
264 public static List<JsExpression> generateInvocationArguments(
265 @NotNull JsExpression receiver,
266 @NotNull List<? extends JsExpression> arguments
267 ) {
268 List<JsExpression> argumentList = new ArrayList<JsExpression>(1 + arguments.size());
269 argumentList.add(receiver);
270 argumentList.addAll(arguments);
271 return argumentList;
272 }
273
274 public static boolean isCacheNeeded(@NotNull JsExpression expression) {
275 if (expression instanceof JsLiteral.JsValueLiteral) return false;
276 if (expression instanceof JsNameRef && ((JsNameRef) expression).getQualifier() == null) return false;
277 if (expression instanceof JsBinaryOperation) {
278 JsBinaryOperation operation = (JsBinaryOperation) expression;
279 JsBinaryOperator operator = operation.getOperator();
280 if (operator.isAssignment() || operator == COMMA) return true;
281 return isCacheNeeded(operation.getArg1()) || isCacheNeeded(operation.getArg2());
282 }
283 if (expression instanceof JsUnaryOperation) {
284 JsUnaryOperation operation = (JsUnaryOperation) expression;
285 JsUnaryOperator operator = operation.getOperator();
286 switch (operator) {
287 case BIT_NOT:
288 case NEG:
289 case POS:
290 case NOT:
291 case TYPEOF:
292 case VOID:
293 return isCacheNeeded(operation.getArg());
294 default:
295 return true;
296 }
297 }
298
299 return true;
300 }
301
302 @NotNull
303 public static JsConditional sure(@NotNull JsExpression expression, @NotNull TranslationContext context) {
304 JsInvocation throwNPE = new JsInvocation(Namer.throwNPEFunctionRef());
305 JsConditional ensureNotNull = notNullConditional(expression, throwNPE, context);
306
307 JsExpression thenExpression = ensureNotNull.getThenExpression();
308 if (thenExpression instanceof JsNameRef) {
309 JsName name = ((JsNameRef) thenExpression).getName();
310 if (name != null) {
311 // associate(cache) ensureNotNull expression to new TemporaryConstVariable with same name.
312 context.associateExpressionToLazyValue(ensureNotNull, new TemporaryConstVariable(name, ensureNotNull));
313 }
314 }
315
316 return ensureNotNull;
317 }
318
319 public static boolean isSimpleNameExpressionNotDelegatedLocalVar(@Nullable KtExpression expression, @NotNull TranslationContext context) {
320 if (!(expression instanceof KtSimpleNameExpression)) {
321 return false;
322 }
323 DeclarationDescriptor descriptor = context.bindingContext().get(BindingContext.REFERENCE_TARGET, ((KtSimpleNameExpression) expression));
324 return !((descriptor instanceof LocalVariableDescriptor) && ((LocalVariableDescriptor) descriptor).isDelegated()) &&
325 !((descriptor instanceof PropertyDescriptor) && propertyAccessedByFunctionsInternally((PropertyDescriptor) descriptor, context));
326 }
327
328 private static boolean propertyAccessedByFunctionsInternally(@NotNull PropertyDescriptor p, @NotNull TranslationContext context) {
329 return !JsDescriptorUtils.isSimpleFinalProperty(p) && context.isFromCurrentModule(p) || shouldAccessViaFunctions(p);
330 }
331
332 public static boolean shouldAccessViaFunctions(@NotNull CallableDescriptor descriptor) {
333 if (descriptor instanceof PropertyDescriptor) {
334 return shouldAccessViaFunctions((PropertyDescriptor) descriptor);
335 }
336 else if (descriptor instanceof PropertyAccessorDescriptor) {
337 return shouldAccessViaFunctions(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty());
338 }
339 else {
340 return false;
341 }
342 }
343
344 private static boolean shouldAccessViaFunctions(@NotNull PropertyDescriptor property) {
345 if (AnnotationsUtils.hasJsNameInAccessors(property)) return true;
346 for (PropertyDescriptor overriddenProperty : property.getOverriddenDescriptors()) {
347 if (shouldAccessViaFunctions(overriddenProperty)) return true;
348 }
349 return false;
350 }
351
352 @NotNull
353 public static JsExpression translateContinuationArgument(@NotNull TranslationContext context, @NotNull ResolvedCall<?> resolvedCall) {
354 CallableDescriptor continuationDescriptor =
355 context.bindingContext().get(BindingContext.ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, resolvedCall.getCall());
356
357 if (continuationDescriptor == null) {
358 continuationDescriptor = getEnclosingContinuationParameter(context);
359 }
360
361 return ReferenceTranslator.translateAsValueReference(continuationDescriptor, context);
362 }
363
364 @NotNull
365 public static VariableDescriptor getEnclosingContinuationParameter(@NotNull TranslationContext context) {
366 VariableDescriptor result = context.getContinuationParameterDescriptor();
367 if (result == null) {
368 assert context.getParent() != null;
369 result = getEnclosingContinuationParameter(context.getParent());
370 }
371 return result;
372 }
373
374 @NotNull
375 public static ClassDescriptor getCoroutineBaseClass(@NotNull TranslationContext context) {
376 FqName className = DescriptorUtils.COROUTINES_PACKAGE_FQ_NAME.child(Name.identifier("CoroutineImpl"));
377 ClassDescriptor descriptor = FindClassInModuleKt.findClassAcrossModuleDependencies(
378 context.getCurrentModule(), ClassId.topLevel(className));
379 assert descriptor != null;
380 return descriptor;
381 }
382
383 @NotNull
384 public static PropertyDescriptor getCoroutineProperty(@NotNull TranslationContext context, @NotNull String name) {
385 return getCoroutineBaseClass(context).getUnsubstitutedMemberScope()
386 .getContributedVariables(Name.identifier(name), NoLookupLocation.FROM_DESERIALIZATION)
387 .iterator().next();
388 }
389
390
391 @NotNull
392 public static FunctionDescriptor getCoroutineDoResumeFunction(@NotNull TranslationContext context) {
393 return getCoroutineBaseClass(context).getUnsubstitutedMemberScope()
394 .getContributedFunctions(Name.identifier("doResume"), NoLookupLocation.FROM_DESERIALIZATION)
395 .iterator().next();
396 }
397
398 @NotNull
399 public static FunctionDescriptor getCoroutineResumeFunction(@NotNull TranslationContext context) {
400 return getCoroutineBaseClass(context).getUnsubstitutedMemberScope()
401 .getContributedFunctions(Name.identifier("resume"), NoLookupLocation.FROM_DESERIALIZATION)
402 .iterator().next();
403 }
404
405 public static boolean isOverridableFunctionWithDefaultParameters(@NotNull FunctionDescriptor descriptor) {
406 return DescriptorUtilsKt.hasOrInheritsParametersWithDefaultValue(descriptor) &&
407 !(descriptor instanceof ConstructorDescriptor) &&
408 descriptor.getContainingDeclaration() instanceof ClassDescriptor &&
409 ModalityKt.isOverridable(descriptor);
410 }
411
412 private static boolean overridesReturnAny(CallableDescriptor c) {
413 KotlinType returnType = c.getOriginal().getReturnType();
414 assert returnType != null;
415 if (KotlinBuiltIns.isAnyOrNullableAny(returnType) || TypeUtils.isTypeParameter(returnType)) return true;
416 for (CallableDescriptor o : c.getOverriddenDescriptors()) {
417 if (overridesReturnAny(o)) return true;
418 }
419 return false;
420 }
421
422
423 public static boolean shouldBoxReturnValue(CallableDescriptor c) {
424 return overridesReturnAny(c) || c instanceof CallableMemberDescriptor && ModalityKt.isOverridable((CallableMemberDescriptor)c);
425 }
426 }