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