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            List<ResolvedValueArgument> arguments = resolvedCall.getValueArgumentsByIndex();
073            if (arguments == null) {
074                throw new IllegalStateException("Failed to arrange value arguments by index");
075            }
076            assignParameterValues(fd, callable, arguments);
077            if (callable.getReceiverClass() != null) {
078                if (resolvedCall.getReceiverArgument() != fd.getReceiverParameter().getValue()) {
079                    StackValue expression = context.getReceiverExpression(codegen.typeMapper);
080                    expression.store(callable.getReceiverClass(), v);
081                }
082                else {
083                    AsmUtil.pop(v, callable.getReceiverClass());
084                }
085            }
086    
087            if (callable.getThisType() != null) {
088                AsmUtil.pop(v, callable.getThisType());
089            }
090    
091            v.goTo(context.getMethodStartLabel());
092        }
093    
094        private void assignParameterValues(
095                CallableDescriptor fd,
096                CallableMethod callableMethod,
097                List<ResolvedValueArgument> valueArguments
098        ) {
099            List<Type> types = callableMethod.getValueParameterTypes();
100            for (ValueParameterDescriptor parameterDescriptor : Lists.reverse(fd.getValueParameters())) {
101                ResolvedValueArgument arg = valueArguments.get(parameterDescriptor.getIndex());
102                Type type = types.get(parameterDescriptor.getIndex());
103    
104                if (arg instanceof ExpressionValueArgument) {
105                    ExpressionValueArgument ev = (ExpressionValueArgument) arg;
106                    ValueArgument argument = ev.getValueArgument();
107                    JetExpression argumentExpression = argument == null ? null : argument.getArgumentExpression();
108    
109                    if (argumentExpression instanceof JetSimpleNameExpression) {
110                        ResolvedCall<? extends CallableDescriptor> resolvedCall = state.getBindingContext().get(RESOLVED_CALL, argumentExpression);
111                        if (resolvedCall != null && resolvedCall.getResultingDescriptor().equals(parameterDescriptor.getOriginal())) {
112                            // do nothing: we shouldn't store argument to itself again
113                            AsmUtil.pop(v, type);
114                            continue;
115                        }
116                    }
117                    //assign the parameter below
118                }
119                else if (arg instanceof DefaultValueArgument) {
120                    AsmUtil.pop(v, type);
121                    DefaultParameterValueLoader.DEFAULT.putValueOnStack(parameterDescriptor, codegen);
122                }
123                else if (arg instanceof VarargValueArgument) {
124                    // assign the parameter below
125                }
126                else {
127                    throw new UnsupportedOperationException("Unknown argument type: " + arg + " in " + fd);
128                }
129    
130                store(parameterDescriptor, type);
131            }
132        }
133    
134        private void store(ValueParameterDescriptor parameterDescriptor, Type type) {
135            int index = getParameterVariableIndex(parameterDescriptor);
136            v.store(index, type);
137        }
138    
139        private int getParameterVariableIndex(ValueParameterDescriptor parameterDescriptor) {
140            int index = codegen.lookupLocalIndex(parameterDescriptor);
141            if (index == -1) {
142                // in the case of a generic function recursively calling itself, the parameters on the call site are substituted
143                index = codegen.lookupLocalIndex(parameterDescriptor.getOriginal());
144            }
145    
146            if (index == -1) {
147                throw new IllegalStateException("Failed to obtain parameter index: " + parameterDescriptor);
148            }
149    
150            return index;
151        }
152    }