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