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.jet.codegen;
018
019 import com.google.common.collect.Lists;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.asm4.Type;
022 import org.jetbrains.asm4.commons.InstructionAdapter;
023 import org.jetbrains.jet.codegen.context.MethodContext;
024 import org.jetbrains.jet.codegen.state.GenerationState;
025 import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
026 import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
027 import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
028 import org.jetbrains.jet.lang.psi.JetExpression;
029 import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
030 import org.jetbrains.jet.lang.psi.ValueArgument;
031 import org.jetbrains.jet.lang.resolve.calls.TailRecursionKind;
032 import org.jetbrains.jet.lang.resolve.calls.model.*;
033
034 import java.util.List;
035
036 import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL;
037 import static org.jetbrains.jet.lang.resolve.BindingContext.TAIL_RECURSION_CALL;
038
039 public class TailRecursionCodegen {
040
041 @NotNull
042 private final MethodContext context;
043 @NotNull
044 private final ExpressionCodegen codegen;
045 @NotNull
046 private final InstructionAdapter v;
047 @NotNull
048 private final GenerationState state;
049
050 public TailRecursionCodegen(
051 @NotNull MethodContext context,
052 @NotNull ExpressionCodegen codegen,
053 @NotNull InstructionAdapter v,
054 @NotNull GenerationState state
055 ) {
056 this.context = context;
057 this.codegen = codegen;
058 this.v = v;
059 this.state = state;
060 }
061
062 public boolean isTailRecursion(@NotNull ResolvedCall<?> resolvedCall) {
063 TailRecursionKind status = state.getBindingContext().get(TAIL_RECURSION_CALL, resolvedCall);
064 return status != null && status.isDoGenerateTailRecursion();
065 }
066
067 public void generateTailRecursion(ResolvedCall<? extends CallableDescriptor> resolvedCall) {
068 CallableDescriptor fd = resolvedCall.getResultingDescriptor();
069 assert fd instanceof FunctionDescriptor : "the resolved call is not refer to the function descriptor so why do we use generateTailRecursion for something strange?";
070 CallableMethod callable = (CallableMethod) codegen.resolveToCallable((FunctionDescriptor) fd, false);
071
072 assignParameterValues(fd, callable, resolvedCall.getValueArgumentsByIndex());
073 if (callable.getReceiverClass() != null) {
074 if (resolvedCall.getReceiverArgument() != fd.getReceiverParameter().getValue()) {
075 StackValue expression = context.getReceiverExpression(codegen.typeMapper);
076 expression.store(callable.getReceiverClass(), v);
077 }
078 else {
079 AsmUtil.pop(v, callable.getReceiverClass());
080 }
081 }
082
083 if (callable.getThisType() != null) {
084 AsmUtil.pop(v, callable.getThisType());
085 }
086
087 v.goTo(context.getMethodStartLabel());
088 }
089
090 private void assignParameterValues(
091 CallableDescriptor fd,
092 CallableMethod callableMethod,
093 List<ResolvedValueArgument> valueArguments
094 ) {
095 List<Type> types = callableMethod.getValueParameterTypes();
096 for (ValueParameterDescriptor parameterDescriptor : Lists.reverse(fd.getValueParameters())) {
097 ResolvedValueArgument arg = valueArguments.get(parameterDescriptor.getIndex());
098 Type type = types.get(parameterDescriptor.getIndex());
099
100 if (arg instanceof ExpressionValueArgument) {
101 ExpressionValueArgument ev = (ExpressionValueArgument) arg;
102 ValueArgument argument = ev.getValueArgument();
103 JetExpression argumentExpression = argument == null ? null : argument.getArgumentExpression();
104
105 if (argumentExpression instanceof JetSimpleNameExpression) {
106 ResolvedCall<? extends CallableDescriptor> resolvedCall = state.getBindingContext().get(RESOLVED_CALL, argumentExpression);
107 if (resolvedCall != null && resolvedCall.getResultingDescriptor().equals(parameterDescriptor.getOriginal())) {
108 // do nothing: we shouldn't store argument to itself again
109 AsmUtil.pop(v, type);
110 continue;
111 }
112 }
113 //assign the parameter below
114 }
115 else if (arg instanceof DefaultValueArgument) {
116 AsmUtil.pop(v, type);
117 DefaultParameterValueLoader.DEFAULT.putValueOnStack(parameterDescriptor, codegen);
118 }
119 else if (arg instanceof VarargValueArgument) {
120 // assign the parameter below
121 }
122 else {
123 throw new UnsupportedOperationException("Unknown argument type: " + arg + " in " + fd);
124 }
125
126 store(parameterDescriptor, type);
127 }
128 }
129
130 private void store(ValueParameterDescriptor parameterDescriptor, Type type) {
131 int index = getParameterVariableIndex(parameterDescriptor);
132 v.store(index, type);
133 }
134
135 private int getParameterVariableIndex(ValueParameterDescriptor parameterDescriptor) {
136 int index = codegen.lookupLocalIndex(parameterDescriptor);
137 if (index == -1) {
138 // in the case of a generic function recursively calling itself, the parameters on the call site are substituted
139 index = codegen.lookupLocalIndex(parameterDescriptor.getOriginal());
140 }
141
142 if (index == -1) {
143 throw new IllegalStateException("Failed to obtain parameter index: " + parameterDescriptor);
144 }
145
146 return index;
147 }
148 }