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.reference;
018
019 import com.google.common.collect.Maps;
020 import com.google.dart.compiler.backend.js.ast.JsExpression;
021 import com.google.dart.compiler.backend.js.ast.JsLiteral;
022 import com.google.dart.compiler.backend.js.ast.JsNode;
023 import com.google.dart.compiler.backend.js.ast.JsReturn;
024 import com.intellij.util.SmartList;
025 import org.jetbrains.annotations.NotNull;
026 import org.jetbrains.annotations.Nullable;
027 import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
028 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
029 import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
030 import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
031 import org.jetbrains.jet.lang.psi.JetCallExpression;
032 import org.jetbrains.jet.lang.psi.JetFunction;
033 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
034 import org.jetbrains.k2js.translate.context.TemporaryVariable;
035 import org.jetbrains.k2js.translate.context.TranslationContext;
036 import org.jetbrains.k2js.translate.utils.JsAstUtils;
037 import org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator;
038 import org.jetbrains.k2js.translate.utils.mutator.Mutator;
039
040 import java.util.List;
041 import java.util.Map;
042
043 import static org.jetbrains.k2js.translate.reference.CallArgumentTranslator.translateSingleArgument;
044 import static org.jetbrains.k2js.translate.reference.CallParametersResolver.resolveCallParameters;
045 import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionForDescriptor;
046 import static org.jetbrains.k2js.translate.utils.FunctionBodyTranslator.translateFunctionBody;
047 import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getExpectedReceiverDescriptor;
048 import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getExpectedThisDescriptor;
049
050 public final class InlinedCallExpressionTranslator extends AbstractCallExpressionTranslator {
051
052 @SuppressWarnings("UnusedParameters")
053 public static boolean shouldBeInlined(@NotNull JetCallExpression expression, @NotNull TranslationContext context) {
054 //TODO: inlining turned off
055 //ResolvedCall<?> resolvedCall = getResolvedCallForCallExpression(context.bindingContext(), expression);
056 //CallableDescriptor descriptor = resolvedCall.getCandidateDescriptor();
057 //if (descriptor instanceof SimpleFunctionDescriptor) {
058 // return ((SimpleFunctionDescriptor)descriptor).isInline();
059 //}
060 return false;
061 }
062
063 @NotNull
064 public static JsExpression translate(@NotNull JetCallExpression expression,
065 @Nullable JsExpression receiver,
066 @NotNull CallType callType,
067 @NotNull TranslationContext context) {
068 return (new InlinedCallExpressionTranslator(expression, receiver, callType, context)).translate();
069 }
070
071 private InlinedCallExpressionTranslator(@NotNull JetCallExpression expression, @Nullable JsExpression receiver,
072 @NotNull CallType callType, @NotNull TranslationContext context) {
073 super(expression, receiver, callType, context);
074 }
075
076 @NotNull
077 private JsExpression translate() {
078 TranslationContext contextWithAllParametersAliased = createContextForInlining();
079 JsNode translatedBody = translateFunctionBody(getFunctionDescriptor(), getFunctionBody(), contextWithAllParametersAliased);
080 //TODO: declare uninitialized temporary
081 TemporaryVariable temporaryVariable = contextWithAllParametersAliased.declareTemporary(JsLiteral.NULL);
082 JsNode mutatedBody = LastExpressionMutator.mutateLastExpression(translatedBody, new InlineFunctionMutator(temporaryVariable));
083 context().addStatementToCurrentBlock(JsAstUtils.convertToBlock(mutatedBody));
084 return temporaryVariable.reference();
085 }
086
087 @NotNull
088 private JetFunction getFunctionBody() {
089 return getFunctionForDescriptor(bindingContext(), getFunctionDescriptor());
090 }
091
092 @NotNull
093 private SimpleFunctionDescriptor getFunctionDescriptor() {
094 CallableDescriptor descriptor = resolvedCall.getCandidateDescriptor().getOriginal();
095 assert descriptor instanceof SimpleFunctionDescriptor : "Inlined functions should have descriptor of type SimpleFunctionDescriptor";
096 return (SimpleFunctionDescriptor)descriptor;
097 }
098
099 @NotNull
100 private TranslationContext createContextForInlining() {
101 TranslationContext contextForInlining = context();
102 contextForInlining = createContextWithAliasForThisExpression(contextForInlining);
103 return createContextWithAliasesForParameters(contextForInlining);
104 }
105
106 @NotNull
107 private TranslationContext createContextWithAliasesForParameters(@NotNull TranslationContext contextForInlining) {
108 Map<DeclarationDescriptor, JsExpression> aliases = Maps.newHashMap();
109 for (ValueParameterDescriptor parameterDescriptor : resolvedCall.getResultingDescriptor().getValueParameters()) {
110 TemporaryVariable aliasForArgument = createAliasForArgument(parameterDescriptor);
111 aliases.put(parameterDescriptor, aliasForArgument.name().makeRef());
112 }
113 return contextForInlining.innerContextWithDescriptorsAliased(aliases);
114 }
115
116 @NotNull
117 private TranslationContext createContextWithAliasForThisExpression(@NotNull TranslationContext contextForInlining) {
118 TranslationContext contextWithAliasForThisExpression = contextForInlining;
119 SimpleFunctionDescriptor functionDescriptor = getFunctionDescriptor();
120 CallParameters callParameters = resolveCallParameters(receiver, null, functionDescriptor, resolvedCall, contextForInlining);
121 JsExpression receiver = callParameters.getReceiver();
122 if (receiver != null) {
123 contextWithAliasForThisExpression =
124 contextWithAlias(contextWithAliasForThisExpression, receiver, getExpectedReceiverDescriptor(functionDescriptor));
125 }
126 JsExpression thisObject = callParameters.getThisObject();
127 if (thisObject != null) {
128 contextWithAliasForThisExpression =
129 contextWithAlias(contextWithAliasForThisExpression, thisObject, getExpectedThisDescriptor(functionDescriptor));
130 }
131 return contextWithAliasForThisExpression;
132 }
133
134 @NotNull
135 private TranslationContext contextWithAlias(@NotNull TranslationContext contextWithAliasForThisExpression,
136 @NotNull JsExpression aliasExpression, @Nullable DeclarationDescriptor descriptorToAlias) {
137 TemporaryVariable aliasForReceiver = context().declareTemporary(aliasExpression);
138 assert descriptorToAlias != null;
139 TranslationContext newContext =
140 contextWithAliasForThisExpression.innerContextWithThisAliased(descriptorToAlias, aliasForReceiver.reference());
141 newContext.addStatementToCurrentBlock(aliasForReceiver.assignmentExpression().makeStmt());
142 return newContext;
143 }
144
145 @NotNull
146 private TemporaryVariable createAliasForArgument(@NotNull ValueParameterDescriptor parameterDescriptor) {
147 ResolvedValueArgument actualArgument = resolvedCall.getValueArgumentsByIndex().get(parameterDescriptor.getIndex());
148 JsExpression translatedArgument = translateArgument(actualArgument);
149 TemporaryVariable aliasForArgument = context().declareTemporary(translatedArgument);
150 context().addStatementToCurrentBlock(aliasForArgument.assignmentExpression().makeStmt());
151 return aliasForArgument;
152 }
153
154 @NotNull
155 private JsExpression translateArgument(@NotNull ResolvedValueArgument actualArgument) {
156 List<JsExpression> result = new SmartList<JsExpression>();
157 translateSingleArgument(actualArgument, result, context(), true);
158 assert result.size() == 1 : "We always wrap varargs in kotlin calls.";
159 return result.get(0);
160 }
161
162 private static final class InlineFunctionMutator implements Mutator {
163
164 @NotNull
165 private final TemporaryVariable toAssignTo;
166
167 private InlineFunctionMutator(@NotNull TemporaryVariable to) {
168 toAssignTo = to;
169 }
170
171 @NotNull
172 @Override
173 public JsNode mutate(@NotNull JsNode node) {
174 if (node instanceof JsReturn) {
175 JsExpression returnedExpression = ((JsReturn)node).getExpression();
176 return JsAstUtils.assignment(toAssignTo.name().makeRef(), returnedExpression);
177 }
178 return node;
179 }
180 }
181 }