/* * Copyright 2010-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jetbrains.kotlin.codegen; import com.google.common.collect.Lists; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.cfg.TailRecursionKind; import org.jetbrains.kotlin.codegen.context.MethodContext; import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor; import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtSimpleNameExpression; import org.jetbrains.kotlin.psi.ValueArgument; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.calls.model.*; import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; import java.util.List; import static org.jetbrains.kotlin.resolve.BindingContext.TAIL_RECURSION_CALL; public class TailRecursionCodegen { @NotNull private final MethodContext context; @NotNull private final ExpressionCodegen codegen; @NotNull private final InstructionAdapter v; @NotNull private final GenerationState state; public TailRecursionCodegen( @NotNull MethodContext context, @NotNull ExpressionCodegen codegen, @NotNull InstructionAdapter v, @NotNull GenerationState state ) { this.context = context; this.codegen = codegen; this.v = v; this.state = state; } public boolean isTailRecursion(@NotNull ResolvedCall<?> resolvedCall) { TailRecursionKind status = state.getBindingContext().get(TAIL_RECURSION_CALL, resolvedCall.getCall()); return status != null && status.isDoGenerateTailRecursion(); } public void generateTailRecursion(ResolvedCall<?> resolvedCall) { CallableDescriptor fd = CoroutineCodegenUtilKt.unwrapInitialDescriptorForSuspendFunction(resolvedCall.getResultingDescriptor()); assert fd instanceof FunctionDescriptor : "Resolved call doesn't refer to the function descriptor: " + fd; CallableMethod callable = (CallableMethod) codegen.resolveToCallable((FunctionDescriptor) fd, false, resolvedCall); List<ResolvedValueArgument> arguments = resolvedCall.getValueArgumentsByIndex(); if (arguments == null) { throw new IllegalStateException("Failed to arrange value arguments by index: " + fd); } if (((FunctionDescriptor) fd).isSuspend()) { AsmUtil.pop(v, callable.getValueParameters().get(callable.getValueParameters().size() - 1).getAsmType()); } assignParameterValues(fd, callable, arguments); if (callable.getExtensionReceiverType() != null) { if (resolvedCall.getExtensionReceiver() != fd.getExtensionReceiverParameter().getValue()) { StackValue expression = context.getReceiverExpression(codegen.typeMapper); expression.store(StackValue.onStack(callable.getExtensionReceiverType()), v, true); } else { AsmUtil.pop(v, callable.getExtensionReceiverType()); } } if (callable.getDispatchReceiverType() != null) { AsmUtil.pop(v, callable.getDispatchReceiverType()); } v.goTo(context.getMethodStartLabel()); } private void assignParameterValues( CallableDescriptor fd, CallableMethod callableMethod, List<ResolvedValueArgument> valueArguments ) { List<Type> types = callableMethod.getValueParameterTypes(); for (ValueParameterDescriptor parameterDescriptor : Lists.reverse(fd.getValueParameters())) { ResolvedValueArgument arg = valueArguments.get(parameterDescriptor.getIndex()); Type type = types.get(parameterDescriptor.getIndex()); if (arg instanceof ExpressionValueArgument) { ExpressionValueArgument ev = (ExpressionValueArgument) arg; ValueArgument argument = ev.getValueArgument(); KtExpression argumentExpression = argument == null ? null : argument.getArgumentExpression(); if (argumentExpression instanceof KtSimpleNameExpression) { ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(argumentExpression, state.getBindingContext()); if (resolvedCall != null && resolvedCall.getResultingDescriptor().equals(parameterDescriptor.getOriginal())) { // do nothing: we shouldn't store argument to itself again AsmUtil.pop(v, type); continue; } } //assign the parameter below } else if (arg instanceof DefaultValueArgument) { AsmUtil.pop(v, type); DefaultParameterValueLoader.DEFAULT.genValue(parameterDescriptor, codegen).put(type, v); } else if (arg instanceof VarargValueArgument) { // assign the parameter below } else { throw new UnsupportedOperationException("Unknown argument type: " + arg + " in " + fd); } store(parameterDescriptor, type); } } private void store(ValueParameterDescriptor parameterDescriptor, Type type) { int index = getParameterVariableIndex(parameterDescriptor); v.store(index, type); } private int getParameterVariableIndex(ValueParameterDescriptor parameterDescriptor) { int index = codegen.lookupLocalIndex(parameterDescriptor); if (index == -1) { // in the case of a generic function recursively calling itself, the parameters on the call site are substituted index = codegen.lookupLocalIndex(parameterDescriptor.getOriginal()); } if (index == -1) { throw new IllegalStateException("Failed to obtain parameter index: " + parameterDescriptor); } return index; } }