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