/*
* 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.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.psi.PsiElement;
import com.intellij.util.ArrayUtil;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.bridges.Bridge;
import org.jetbrains.kotlin.backend.common.bridges.ImplKt;
import org.jetbrains.kotlin.codegen.annotation.AnnotatedWithOnlyTargetedAnnotations;
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
import org.jetbrains.kotlin.codegen.context.*;
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
import org.jetbrains.kotlin.codegen.coroutines.SuspendFunctionGenerationStrategy;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotated;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget;
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.java.SpecialBuiltinMembers;
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt;
import org.jetbrains.kotlin.resolve.constants.ArrayValue;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.KClassValue;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
import org.jetbrains.kotlin.resolve.jvm.RuntimeAssertionInfo;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.kotlin.utils.StringsKt;
import org.jetbrains.org.objectweb.asm.*;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.commons.Method;
import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableAny;
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isAnnotationOrJvmInterfaceWithoutDefaults;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvm8InterfaceWithDefaultsMember;
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION;
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION;
import static org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.*;
import static org.jetbrains.kotlin.descriptors.annotations.AnnotationUtilKt.isEffectivelyInlineOnly;
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.getSourceFromDescriptor;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE;
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.*;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public class FunctionCodegen {
public final GenerationState state;
private final KotlinTypeMapper typeMapper;
private final BindingContext bindingContext;
private final CodegenContext owner;
private final ClassBuilder v;
private final MemberCodegen<?> memberCodegen;
private final Function1<DeclarationDescriptor, Boolean> IS_PURE_INTERFACE_CHECKER = new Function1<DeclarationDescriptor, Boolean>() {
@Override
public Boolean invoke(DeclarationDescriptor descriptor) {
return JvmCodegenUtil.isAnnotationOrJvmInterfaceWithoutDefaults(descriptor, state);
}
};
public FunctionCodegen(
@NotNull CodegenContext owner,
@NotNull ClassBuilder v,
@NotNull GenerationState state,
@NotNull MemberCodegen<?> memberCodegen
) {
this.owner = owner;
this.v = v;
this.state = state;
this.typeMapper = state.getTypeMapper();
this.bindingContext = state.getBindingContext();
this.memberCodegen = memberCodegen;
}
public void gen(@NotNull KtNamedFunction function) {
SimpleFunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, function);
if (bindingContext.get(CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW, functionDescriptor) != null) {
functionDescriptor =
(SimpleFunctionDescriptor) bindingContext.get(CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW, functionDescriptor);
}
if (functionDescriptor == null) {
throw ExceptionLogger.logDescriptorNotFound("No descriptor for function " + function.getName(), function);
}
if (owner.getContextKind() != OwnerKind.DEFAULT_IMPLS || function.hasBody()) {
FunctionGenerationStrategy strategy;
if (functionDescriptor.isSuspend() && !functionDescriptor.isInline()) {
strategy = new SuspendFunctionGenerationStrategy(
state,
CoroutineCodegenUtilKt.<FunctionDescriptor>unwrapInitialDescriptorForSuspendFunction(functionDescriptor),
function,
v.getThisName()
);
}
else {
strategy = new FunctionGenerationStrategy.FunctionDefault(state, function);
}
generateMethod(JvmDeclarationOriginKt.OtherOrigin(function, functionDescriptor), functionDescriptor, strategy);
}
generateDefaultIfNeeded(owner.intoFunction(functionDescriptor, true), functionDescriptor, owner.getContextKind(),
DefaultParameterValueLoader.DEFAULT, function);
generateOverloadsWithDefaultValues(function, functionDescriptor, functionDescriptor);
}
public void generateOverloadsWithDefaultValues(
@Nullable KtNamedFunction function,
@NotNull FunctionDescriptor functionDescriptor,
@NotNull FunctionDescriptor delegateFunctionDescriptor
) {
new DefaultParameterValueSubstitutor(state).generateOverloadsIfNeeded(
function, functionDescriptor, delegateFunctionDescriptor, owner.getContextKind(), v, memberCodegen
);
}
public void generateMethod(
@NotNull JvmDeclarationOrigin origin,
@NotNull FunctionDescriptor descriptor,
@NotNull FunctionGenerationStrategy strategy
) {
if (CoroutineCodegenUtilKt.isSuspendFunctionNotSuspensionView(descriptor)) {
generateMethod(origin, CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(descriptor, bindingContext), strategy);
return;
}
generateMethod(origin, descriptor, owner.intoFunction(descriptor), strategy);
}
public void generateMethod(
@NotNull JvmDeclarationOrigin origin,
@NotNull FunctionDescriptor functionDescriptor,
@NotNull MethodContext methodContext,
@NotNull FunctionGenerationStrategy strategy
) {
OwnerKind contextKind = methodContext.getContextKind();
if (isInterface(functionDescriptor.getContainingDeclaration()) &&
functionDescriptor.getVisibility() == Visibilities.PRIVATE &&
!processInterfaceMember(functionDescriptor, contextKind, state)) {
return;
}
JvmMethodGenericSignature jvmSignature = typeMapper.mapSignatureWithGeneric(functionDescriptor, contextKind);
Method asmMethod = jvmSignature.getAsmMethod();
int flags = getMethodAsmFlags(functionDescriptor, contextKind, state);
if (origin.getOriginKind() == JvmDeclarationOriginKind.SAM_DELEGATION) {
flags |= ACC_SYNTHETIC;
}
if (functionDescriptor.isExternal() && owner instanceof MultifileClassFacadeContext) {
// Native methods are only defined in facades and do not need package part implementations
return;
}
MethodVisitor mv =
strategy.wrapMethodVisitor(
v.newMethod(origin,
flags,
asmMethod.getName(),
asmMethod.getDescriptor(),
jvmSignature.getGenericsSignature(),
getThrownExceptions(functionDescriptor, typeMapper)
),
flags, asmMethod.getName(),
asmMethod.getDescriptor()
);
if (CodegenContextUtil.isImplClassOwner(owner)) {
v.getSerializationBindings().put(METHOD_FOR_FUNCTION, CodegenUtilKt.unwrapFrontendVersion(functionDescriptor), asmMethod);
}
generateMethodAnnotations(functionDescriptor, asmMethod, mv);
JvmMethodSignature signature = typeMapper.mapSignatureSkipGeneric(functionDescriptor);
generateParameterAnnotations(functionDescriptor, mv, signature);
GenerateJava8ParameterNamesKt.generateParameterNames(functionDescriptor, mv, signature, state, (flags & ACC_SYNTHETIC) != 0);
generateBridges(functionDescriptor);
if (isJvm8InterfaceWithDefaultsMember(functionDescriptor, state) && contextKind != OwnerKind.DEFAULT_IMPLS && state.getGenerateDefaultImplsForJvm8()) {
generateDelegateForDefaultImpl(functionDescriptor, origin.getElement());
}
boolean staticInCompanionObject = CodegenUtilKt.isJvmStaticInCompanionObject(functionDescriptor);
if (staticInCompanionObject) {
ImplementationBodyCodegen parentBodyCodegen = (ImplementationBodyCodegen) memberCodegen.getParentCodegen();
parentBodyCodegen.addAdditionalTask(new JvmStaticInCompanionObjectGenerator(functionDescriptor, origin, state, parentBodyCodegen));
}
if (!state.getClassBuilderMode().generateBodies || isAbstractMethod(functionDescriptor, contextKind, state)) {
generateLocalVariableTable(
mv,
jvmSignature,
functionDescriptor,
getThisTypeForFunction(functionDescriptor, methodContext, typeMapper),
new Label(),
new Label(),
contextKind,
typeMapper,
0);
mv.visitEnd();
return;
}
if (!functionDescriptor.isExternal()) {
generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, memberCodegen);
}
else if (staticInCompanionObject) {
// native @JvmStatic foo() in companion object should delegate to the static native function moved to the outer class
mv.visitCode();
FunctionDescriptor staticFunctionDescriptor = JvmStaticInCompanionObjectGenerator
.createStaticFunctionDescriptor(functionDescriptor);
Method accessorMethod =
typeMapper.mapAsmMethod(memberCodegen.getContext().accessibleDescriptor(staticFunctionDescriptor, null));
Type owningType = typeMapper.mapClass((ClassifierDescriptor) staticFunctionDescriptor.getContainingDeclaration());
generateDelegateToStaticMethodBody(false, mv, accessorMethod, owningType.getInternalName());
}
endVisit(mv, null, origin.getElement());
}
private void generateDelegateForDefaultImpl(
@NotNull FunctionDescriptor functionDescriptor,
@Nullable PsiElement element
) {
Method defaultImplMethod = typeMapper.mapAsmMethod(functionDescriptor, OwnerKind.DEFAULT_IMPLS);
CodegenUtilKt.generateMethod(
v, "Default Impl delegate in interface", Opcodes.ACC_SYNTHETIC | Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC,
new Method(defaultImplMethod.getName() + JvmAbi.DEFAULT_IMPLS_DELEGATE_SUFFIX, defaultImplMethod.getDescriptor()),
element, JvmDeclarationOrigin.NO_ORIGIN,
state, adapter -> {
Method interfaceMethod = typeMapper.mapAsmMethod(functionDescriptor, OwnerKind.IMPLEMENTATION);
Type type = typeMapper.mapOwner(functionDescriptor);
generateDelegateToMethodBody(
-1, adapter,
interfaceMethod,
type.getInternalName(),
Opcodes.INVOKESPECIAL,
true
);
return null;
}
);
}
private void generateMethodAnnotations(
@NotNull FunctionDescriptor functionDescriptor,
Method asmMethod,
MethodVisitor mv
) {
generateMethodAnnotations(functionDescriptor, asmMethod, mv, memberCodegen, typeMapper);
}
public static void generateMethodAnnotations(
@NotNull FunctionDescriptor functionDescriptor,
Method asmMethod,
MethodVisitor mv,
@NotNull InnerClassConsumer consumer,
@NotNull KotlinTypeMapper typeMapper
) {
AnnotationCodegen annotationCodegen = AnnotationCodegen.forMethod(mv, consumer, typeMapper);
if (functionDescriptor instanceof PropertyAccessorDescriptor) {
AnnotationUseSiteTarget target = functionDescriptor instanceof PropertySetterDescriptor ? PROPERTY_SETTER : PROPERTY_GETTER;
annotationCodegen.genAnnotations(functionDescriptor, asmMethod.getReturnType(), target);
}
else {
annotationCodegen.genAnnotations(functionDescriptor, asmMethod.getReturnType());
}
}
private void generateParameterAnnotations(
@NotNull FunctionDescriptor functionDescriptor,
@NotNull MethodVisitor mv,
@NotNull JvmMethodSignature jvmSignature
) {
generateParameterAnnotations(functionDescriptor, mv, jvmSignature, memberCodegen, state);
}
public static void generateParameterAnnotations(
@NotNull FunctionDescriptor functionDescriptor,
@NotNull MethodVisitor mv,
@NotNull JvmMethodSignature jvmSignature,
@NotNull InnerClassConsumer innerClassConsumer,
@NotNull GenerationState state
) {
generateParameterAnnotations(
functionDescriptor, mv, jvmSignature, functionDescriptor.getValueParameters(), innerClassConsumer, state
);
}
public static void generateParameterAnnotations(
@NotNull FunctionDescriptor functionDescriptor,
@NotNull MethodVisitor mv,
@NotNull JvmMethodSignature jvmSignature,
@NotNull List<ValueParameterDescriptor> valueParameters,
@NotNull InnerClassConsumer innerClassConsumer,
@NotNull GenerationState state
) {
KotlinTypeMapper typeMapper = state.getTypeMapper();
Iterator<ValueParameterDescriptor> iterator = valueParameters.iterator();
List<JvmMethodParameterSignature> kotlinParameterTypes = jvmSignature.getValueParameters();
for (int i = 0; i < kotlinParameterTypes.size(); i++) {
JvmMethodParameterSignature parameterSignature = kotlinParameterTypes.get(i);
JvmMethodParameterKind kind = parameterSignature.getKind();
if (kind.isSkippedInGenericSignature()) {
markEnumOrInnerConstructorParameterAsSynthetic(mv, i, state.getClassBuilderMode());
continue;
}
if (kind == JvmMethodParameterKind.VALUE) {
ValueParameterDescriptor parameter = iterator.next();
AnnotationCodegen annotationCodegen = AnnotationCodegen.forParameter(i, mv, innerClassConsumer, typeMapper);
if (functionDescriptor instanceof PropertySetterDescriptor) {
PropertyDescriptor propertyDescriptor = ((PropertySetterDescriptor) functionDescriptor).getCorrespondingProperty();
Annotated targetedAnnotations = new AnnotatedWithOnlyTargetedAnnotations(propertyDescriptor);
annotationCodegen.genAnnotations(targetedAnnotations, parameterSignature.getAsmType(), SETTER_PARAMETER);
}
if (functionDescriptor instanceof ConstructorDescriptor) {
annotationCodegen.genAnnotations(parameter, parameterSignature.getAsmType(), CONSTRUCTOR_PARAMETER);
}
else {
annotationCodegen.genAnnotations(parameter, parameterSignature.getAsmType());
}
}
else if (kind == JvmMethodParameterKind.RECEIVER) {
ReceiverParameterDescriptor receiver = JvmCodegenUtil.getDirectMember(functionDescriptor).getExtensionReceiverParameter();
if (receiver != null) {
AnnotationCodegen annotationCodegen = AnnotationCodegen.forParameter(i, mv, innerClassConsumer, typeMapper);
Annotated targetedAnnotations = new AnnotatedWithOnlyTargetedAnnotations(receiver.getType());
annotationCodegen.genAnnotations(targetedAnnotations, parameterSignature.getAsmType(), RECEIVER);
annotationCodegen.genAnnotations(receiver, parameterSignature.getAsmType());
}
}
}
}
private static void markEnumOrInnerConstructorParameterAsSynthetic(MethodVisitor mv, int i, ClassBuilderMode mode) {
// IDEA's ClsPsi builder fails to annotate synthetic parameters
if (mode == ClassBuilderMode.LIGHT_CLASSES) return;
// This is needed to avoid RuntimeInvisibleParameterAnnotations error in javac:
// see MethodWriter.visitParameterAnnotation()
AnnotationVisitor av = mv.visitParameterAnnotation(i, "Ljava/lang/Synthetic;", true);
if (av != null) {
av.visitEnd();
}
}
@Nullable
private static Type getThisTypeForFunction(
@NotNull FunctionDescriptor functionDescriptor,
@NotNull MethodContext context,
@NotNull KotlinTypeMapper typeMapper
) {
ReceiverParameterDescriptor dispatchReceiver = functionDescriptor.getDispatchReceiverParameter();
if (functionDescriptor instanceof ConstructorDescriptor) {
return typeMapper.mapType(functionDescriptor);
}
else if (dispatchReceiver != null) {
return typeMapper.mapType(dispatchReceiver.getType());
}
else if (isFunctionLiteral(functionDescriptor) ||
isLocalFunction(functionDescriptor) ||
isFunctionExpression(functionDescriptor)) {
return typeMapper.mapType(context.getThisDescriptor());
}
else {
return null;
}
}
public static void generateMethodBody(
@NotNull MethodVisitor mv,
@NotNull FunctionDescriptor functionDescriptor,
@NotNull MethodContext context,
@NotNull JvmMethodSignature signature,
@NotNull FunctionGenerationStrategy strategy,
@NotNull MemberCodegen<?> parentCodegen
) {
mv.visitCode();
Label methodBegin = new Label();
mv.visitLabel(methodBegin);
KotlinTypeMapper typeMapper = parentCodegen.typeMapper;
if (BuiltinSpecialBridgesUtil.shouldHaveTypeSafeBarrier(functionDescriptor, typeMapper::mapAsmMethod)) {
generateTypeCheckBarrierIfNeeded(
new InstructionAdapter(mv), functionDescriptor, signature.getReturnType(), /* delegateParameterTypes = */null);
}
Label methodEnd;
int functionFakeIndex = -1;
int lambdaFakeIndex = -1;
if (context.getParentContext() instanceof MultifileClassFacadeContext) {
generateFacadeDelegateMethodBody(mv, signature.getAsmMethod(), (MultifileClassFacadeContext) context.getParentContext());
methodEnd = new Label();
}
else if (OwnerKind.DEFAULT_IMPLS == context.getContextKind() && isJvm8InterfaceWithDefaultsMember(functionDescriptor, parentCodegen.state)) {
int flags = AsmUtil.getMethodAsmFlags(functionDescriptor, OwnerKind.DEFAULT_IMPLS, context.getState());
assert (flags & Opcodes.ACC_ABSTRACT) == 0 : "Interface method with body should be non-abstract" + functionDescriptor;
Type type = typeMapper.mapOwner(functionDescriptor);
Method asmMethod = typeMapper.mapAsmMethod(functionDescriptor, OwnerKind.DEFAULT_IMPLS);
generateDelegateToStaticMethodBody(
true, mv,
new Method(asmMethod.getName() + JvmAbi.DEFAULT_IMPLS_DELEGATE_SUFFIX, asmMethod.getDescriptor()),
type.getInternalName()
);
methodEnd = new Label();
}
else {
FrameMap frameMap = createFrameMap(parentCodegen.state, functionDescriptor, signature, isStaticMethod(context.getContextKind(),
functionDescriptor));
if (context.isInlineMethodContext()) {
functionFakeIndex = frameMap.enterTemp(Type.INT_TYPE);
}
if (context instanceof InlineLambdaContext) {
lambdaFakeIndex = frameMap.enterTemp(Type.INT_TYPE);
}
Label methodEntry = new Label();
mv.visitLabel(methodEntry);
context.setMethodStartLabel(methodEntry);
if (!KotlinTypeMapper.isAccessor(functionDescriptor)) {
genNotNullAssertionsForParameters(new InstructionAdapter(mv), parentCodegen.state, functionDescriptor, frameMap);
}
parentCodegen.beforeMethodBody(mv);
methodEnd = new Label();
context.setMethodEndLabel(methodEnd);
strategy.generateBody(mv, frameMap, signature, context, parentCodegen);
}
mv.visitLabel(methodEnd);
Type thisType = getThisTypeForFunction(functionDescriptor, context, typeMapper);
generateLocalVariableTable(
mv, signature, functionDescriptor, thisType, methodBegin, methodEnd, context.getContextKind(), typeMapper,
(functionFakeIndex >= 0 ? 1 : 0) + (lambdaFakeIndex >= 0 ? 1 : 0)
);
//TODO: it's best to move all below logic to 'generateLocalVariableTable' method
if (context.isInlineMethodContext() && functionFakeIndex != -1) {
mv.visitLocalVariable(
JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION + typeMapper.mapAsmMethod(functionDescriptor).getName(),
Type.INT_TYPE.getDescriptor(), null,
methodBegin, methodEnd,
functionFakeIndex);
}
if (context instanceof InlineLambdaContext && thisType != null && lambdaFakeIndex != -1) {
String name = thisType.getClassName();
int indexOfLambdaOrdinal = name.lastIndexOf("$");
if (indexOfLambdaOrdinal > 0) {
int lambdaOrdinal = Integer.parseInt(name.substring(indexOfLambdaOrdinal + 1));
KtPureElement functionArgument = parentCodegen.element;
String functionName = "unknown";
if (functionArgument instanceof KtFunction) {
ValueParameterDescriptor inlineArgumentDescriptor =
InlineUtil.getInlineArgumentDescriptor((KtFunction) functionArgument, parentCodegen.bindingContext);
if (inlineArgumentDescriptor != null) {
functionName = inlineArgumentDescriptor.getContainingDeclaration().getName().asString();
}
}
mv.visitLocalVariable(
JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT + lambdaOrdinal + "$" + functionName,
Type.INT_TYPE.getDescriptor(), null,
methodBegin, methodEnd,
lambdaFakeIndex);
}
}
}
private static void generateLocalVariableTable(
@NotNull MethodVisitor mv,
@NotNull JvmMethodSignature jvmMethodSignature,
@NotNull FunctionDescriptor functionDescriptor,
@Nullable Type thisType,
@NotNull Label methodBegin,
@NotNull Label methodEnd,
@NotNull OwnerKind ownerKind,
@NotNull KotlinTypeMapper typeMapper,
int shiftForDestructuringVariables
) {
generateLocalVariablesForParameters(mv, jvmMethodSignature, thisType, methodBegin, methodEnd,
functionDescriptor.getValueParameters(),
AsmUtil.isStaticMethod(ownerKind, functionDescriptor), typeMapper, shiftForDestructuringVariables
);
}
public static void generateLocalVariablesForParameters(
@NotNull MethodVisitor mv,
@NotNull JvmMethodSignature jvmMethodSignature,
@Nullable Type thisType,
@NotNull Label methodBegin,
@NotNull Label methodEnd,
Collection<ValueParameterDescriptor> valueParameters,
boolean isStatic,
KotlinTypeMapper typeMapper
) {
generateLocalVariablesForParameters(
mv, jvmMethodSignature, thisType, methodBegin, methodEnd, valueParameters, isStatic, typeMapper, 0
);
}
private static void generateLocalVariablesForParameters(
@NotNull MethodVisitor mv,
@NotNull JvmMethodSignature jvmMethodSignature,
@Nullable Type thisType,
@NotNull Label methodBegin,
@NotNull Label methodEnd,
Collection<ValueParameterDescriptor> valueParameters,
boolean isStatic,
KotlinTypeMapper typeMapper,
int shiftForDestructuringVariables
) {
Iterator<ValueParameterDescriptor> valueParameterIterator = valueParameters.iterator();
List<JvmMethodParameterSignature> params = jvmMethodSignature.getValueParameters();
int shift = 0;
if (!isStatic) {
//add this
if (thisType != null) {
mv.visitLocalVariable("this", thisType.getDescriptor(), null, methodBegin, methodEnd, shift);
}
else {
//TODO: provide thisType for callable reference
}
shift++;
}
for (int i = 0; i < params.size(); i++) {
JvmMethodParameterSignature param = params.get(i);
JvmMethodParameterKind kind = param.getKind();
String parameterName;
if (kind == JvmMethodParameterKind.VALUE) {
ValueParameterDescriptor parameter = valueParameterIterator.next();
List<VariableDescriptor> destructuringVariables = ValueParameterDescriptorImpl.getDestructuringVariablesOrNull(parameter);
parameterName =
destructuringVariables == null
? computeParameterName(i, parameter)
: "$" + joinParameterNames(destructuringVariables);
}
else {
String lowercaseKind = kind.name().toLowerCase();
parameterName = needIndexForVar(kind)
? "$" + lowercaseKind + "$" + i
: "$" + lowercaseKind;
}
Type type = param.getAsmType();
mv.visitLocalVariable(parameterName, type.getDescriptor(), null, methodBegin, methodEnd, shift);
shift += type.getSize();
}
shift += shiftForDestructuringVariables;
for (ValueParameterDescriptor parameter : valueParameters) {
List<VariableDescriptor> destructuringVariables = ValueParameterDescriptorImpl.getDestructuringVariablesOrNull(parameter);
if (destructuringVariables == null) continue;
for (VariableDescriptor entry : CodegenUtilKt.filterOutDescriptorsWithSpecialNames(destructuringVariables)) {
Type type = typeMapper.mapType(entry.getType());
mv.visitLocalVariable(entry.getName().asString(), type.getDescriptor(), null, methodBegin, methodEnd, shift);
shift += type.getSize();
}
}
}
private static String computeParameterName(int i, ValueParameterDescriptor parameter) {
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(parameter);
if (element instanceof KtParameter && UnderscoreUtilKt.isSingleUnderscore((KtParameter) element)) {
return "$noName_" + i;
}
return parameter.getName().asString();
}
private static String joinParameterNames(@NotNull List<VariableDescriptor> variables) {
// stub for anonymous destructuring declaration entry
return StringsKt.join(
CollectionsKt.map(variables, descriptor -> descriptor.getName().isSpecial() ? "$_$" : descriptor.getName().asString()),
"_"
);
}
private static void generateFacadeDelegateMethodBody(
@NotNull MethodVisitor mv,
@NotNull Method asmMethod,
@NotNull MultifileClassFacadeContext context
) {
generateDelegateToStaticMethodBody(true, mv, asmMethod, context.getFilePartType().getInternalName());
}
private static void generateDelegateToMethodBody(
// -1 means to add additional this parameter on stack
int firstParamIndex,
@NotNull MethodVisitor mv,
@NotNull Method asmMethod,
@NotNull String classToDelegateTo,
int opcode,
boolean isInterface
) {
InstructionAdapter iv = new InstructionAdapter(mv);
Type[] argTypes = asmMethod.getArgumentTypes();
// The first line of some package file is written to the line number attribute of a static delegate to allow to 'step into' it
// This is similar to what javac does with bridge methods
Label label = new Label();
iv.visitLabel(label);
iv.visitLineNumber(1, label);
int paramIndex = firstParamIndex;
if (paramIndex == -1) {
iv.load(0, AsmTypes.OBJECT_TYPE);
paramIndex = 1;
}
for (Type argType : argTypes) {
iv.load(paramIndex, argType);
paramIndex += argType.getSize();
}
iv.visitMethodInsn(opcode, classToDelegateTo, asmMethod.getName(), asmMethod.getDescriptor(), isInterface);
iv.areturn(asmMethod.getReturnType());
}
private static void generateDelegateToStaticMethodBody(
boolean isStatic,
@NotNull MethodVisitor mv,
@NotNull Method asmMethod,
@NotNull String classToDelegateTo
) {
generateDelegateToMethodBody(isStatic ? 0 : 1, mv, asmMethod, classToDelegateTo, Opcodes.INVOKESTATIC, false);
}
private static boolean needIndexForVar(JvmMethodParameterKind kind) {
return kind == JvmMethodParameterKind.CAPTURED_LOCAL_VARIABLE ||
kind == JvmMethodParameterKind.ENUM_NAME_OR_ORDINAL ||
kind == JvmMethodParameterKind.SUPER_CALL_PARAM;
}
public static void endVisit(MethodVisitor mv, @Nullable String description) {
endVisit(mv, description, (PsiElement)null);
}
public static void endVisit(MethodVisitor mv, @Nullable String description, @Nullable KtPureElement method) {
endVisit(mv, description, (PsiElement)(method == null ? null : method.getPsiOrParent()));
}
public static void endVisit(MethodVisitor mv, @Nullable String description, @NotNull KtElement method) {
endVisit(mv, description, (PsiElement)method);
}
public static void endVisit(MethodVisitor mv, @Nullable String description, @Nullable PsiElement method) {
try {
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
catch (ProcessCanceledException e) {
throw e;
}
catch (Throwable t) {
String bytecode = renderByteCodeIfAvailable(mv);
throw new CompilationException(
"wrong code generated\n" +
(description != null ? " for " + description : "") +
t.getClass().getName() +
" " +
t.getMessage() +
(bytecode != null ? "\nbytecode:\n" + bytecode : ""),
t, method);
}
}
private static String renderByteCodeIfAvailable(MethodVisitor mv) {
String bytecode = null;
if (mv instanceof TransformationMethodVisitor) {
mv = ((TransformationMethodVisitor) mv).getTraceMethodVisitorIfPossible();
}
if (mv instanceof TraceMethodVisitor) {
TraceMethodVisitor traceMethodVisitor = (TraceMethodVisitor) mv;
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
traceMethodVisitor.p.print(pw);
pw.close();
bytecode = sw.toString();
}
return bytecode;
}
public void generateBridges(@NotNull FunctionDescriptor descriptor) {
if (descriptor instanceof ConstructorDescriptor) return;
if (owner.getContextKind() == OwnerKind.DEFAULT_IMPLS) return;
if (isAnnotationOrJvmInterfaceWithoutDefaults(descriptor.getContainingDeclaration(), state)) return;
// equals(Any?), hashCode(), toString() never need bridges
if (isMethodOfAny(descriptor)) return;
boolean isSpecial = SpecialBuiltinMembers.getOverriddenBuiltinReflectingJvmDescriptor(descriptor) != null;
Set<Bridge<Method>> bridgesToGenerate;
if (!isSpecial) {
bridgesToGenerate =
ImplKt.generateBridgesForFunctionDescriptor(descriptor, typeMapper::mapAsmMethod, IS_PURE_INTERFACE_CHECKER);
if (!bridgesToGenerate.isEmpty()) {
PsiElement origin = descriptor.getKind() == DECLARATION ? getSourceFromDescriptor(descriptor) : null;
boolean isSpecialBridge =
BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(descriptor) != null;
for (Bridge<Method> bridge : bridgesToGenerate) {
generateBridge(origin, descriptor, bridge.getFrom(), bridge.getTo(), isSpecialBridge, false);
}
}
}
else {
Set<BridgeForBuiltinSpecial<Method>> specials = BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial(
descriptor, typeMapper::mapAsmMethod, IS_PURE_INTERFACE_CHECKER
);
if (!specials.isEmpty()) {
PsiElement origin = descriptor.getKind() == DECLARATION ? getSourceFromDescriptor(descriptor) : null;
for (BridgeForBuiltinSpecial<Method> bridge : specials) {
generateBridge(
origin, descriptor, bridge.getFrom(), bridge.getTo(),
bridge.isSpecial(), bridge.isDelegateToSuper());
}
}
if (!descriptor.getKind().isReal() && isAbstractMethod(descriptor, OwnerKind.IMPLEMENTATION, state)) {
CallableDescriptor overridden = SpecialBuiltinMembers.getOverriddenBuiltinReflectingJvmDescriptor(descriptor);
assert overridden != null;
if (!isThereOverriddenInKotlinClass(descriptor)) {
Method method = typeMapper.mapAsmMethod(descriptor);
int flags = ACC_ABSTRACT | getVisibilityAccessFlag(descriptor);
v.newMethod(JvmDeclarationOriginKt.OtherOrigin(overridden), flags, method.getName(), method.getDescriptor(), null, null);
}
}
}
}
public static boolean isThereOverriddenInKotlinClass(@NotNull CallableMemberDescriptor descriptor) {
return CollectionsKt.any(
getAllOverriddenDescriptors(descriptor),
overridden -> !(overridden.getContainingDeclaration() instanceof JavaClassDescriptor) &&
isClass(overridden.getContainingDeclaration())
);
}
public static boolean isMethodOfAny(@NotNull FunctionDescriptor descriptor) {
String name = descriptor.getName().asString();
List<ValueParameterDescriptor> parameters = descriptor.getValueParameters();
if (parameters.isEmpty()) {
return name.equals("hashCode") || name.equals("toString");
}
else if (parameters.size() == 1 && name.equals("equals")) {
return isNullableAny(parameters.get(0).getType());
}
return false;
}
@NotNull
public static String[] getThrownExceptions(@NotNull FunctionDescriptor function, @NotNull KotlinTypeMapper mapper) {
AnnotationDescriptor annotation = function.getAnnotations().findAnnotation(new FqName("kotlin.throws"));
if (annotation == null) {
annotation = function.getAnnotations().findAnnotation(new FqName("kotlin.jvm.Throws"));
}
if (annotation == null) return ArrayUtil.EMPTY_STRING_ARRAY;
Collection<ConstantValue<?>> values = annotation.getAllValueArguments().values();
if (values.isEmpty()) return ArrayUtil.EMPTY_STRING_ARRAY;
Object value = values.iterator().next();
if (!(value instanceof ArrayValue)) return ArrayUtil.EMPTY_STRING_ARRAY;
ArrayValue arrayValue = (ArrayValue) value;
List<String> strings = CollectionsKt.mapNotNull(
arrayValue.getValue(),
(ConstantValue<?> constant) -> {
if (constant instanceof KClassValue) {
KClassValue classValue = (KClassValue) constant;
ClassDescriptor classDescriptor = DescriptorUtils.getClassDescriptorForType(classValue.getValue());
return mapper.mapClass(classDescriptor).getInternalName();
}
return null;
}
);
return ArrayUtil.toStringArray(strings);
}
void generateDefaultIfNeeded(
@NotNull MethodContext owner,
@NotNull FunctionDescriptor functionDescriptor,
@NotNull OwnerKind kind,
@NotNull DefaultParameterValueLoader loadStrategy,
@Nullable KtNamedFunction function
) {
DeclarationDescriptor contextClass = owner.getContextDescriptor().getContainingDeclaration();
if (isInterface(contextClass) && !processInterface(contextClass, kind, state)) {
return;
}
if (!isDefaultNeeded(functionDescriptor)) {
return;
}
// $default methods are never private to be accessible from other class files (e.g. inner) without the need of synthetic accessors
// $default methods are never protected to be accessible from subclass nested classes
int visibilityFlag = Visibilities.isPrivate(functionDescriptor.getVisibility()) ||
isEffectivelyInlineOnly(functionDescriptor) ?
AsmUtil.NO_FLAG_PACKAGE_PRIVATE : Opcodes.ACC_PUBLIC;
int flags = visibilityFlag | getDeprecatedAccessFlag(functionDescriptor) | ACC_SYNTHETIC;
if (!(functionDescriptor instanceof ConstructorDescriptor)) {
flags |= ACC_STATIC | ACC_BRIDGE;
}
Method defaultMethod = typeMapper.mapDefaultMethod(functionDescriptor, kind);
MethodVisitor mv = v.newMethod(
JvmDeclarationOriginKt.Synthetic(function, functionDescriptor),
flags,
defaultMethod.getName(),
defaultMethod.getDescriptor(), null,
getThrownExceptions(functionDescriptor, typeMapper)
);
// Only method annotations are copied to the $default method. Parameter annotations are not copied until there are valid use cases;
// enum constructors have two additional synthetic parameters which somewhat complicate this task
AnnotationCodegen.forMethod(mv, memberCodegen, typeMapper).genAnnotations(functionDescriptor, defaultMethod.getReturnType());
if (!state.getClassBuilderMode().generateBodies) {
if (this.owner instanceof MultifileClassFacadeContext)
endVisit(mv, "default method delegation", getSourceFromDescriptor(functionDescriptor));
else
endVisit(mv, "default method", getSourceFromDescriptor(functionDescriptor));
return;
}
if (this.owner instanceof MultifileClassFacadeContext) {
mv.visitCode();
generateFacadeDelegateMethodBody(mv, defaultMethod, (MultifileClassFacadeContext) this.owner);
endVisit(mv, "default method delegation", getSourceFromDescriptor(functionDescriptor));
}
else {
mv.visitCode();
generateDefaultImplBody(owner, functionDescriptor, mv, loadStrategy, function, memberCodegen, defaultMethod);
endVisit(mv, "default method", getSourceFromDescriptor(functionDescriptor));
}
}
public static void generateDefaultImplBody(
@NotNull MethodContext methodContext,
@NotNull FunctionDescriptor functionDescriptor,
@NotNull MethodVisitor mv,
@NotNull DefaultParameterValueLoader loadStrategy,
@Nullable KtNamedFunction function,
@NotNull MemberCodegen<?> parentCodegen,
@NotNull Method defaultMethod
) {
GenerationState state = parentCodegen.state;
JvmMethodSignature signature = state.getTypeMapper().mapSignatureWithGeneric(functionDescriptor, methodContext.getContextKind());
boolean isStatic = isStaticMethod(methodContext.getContextKind(), functionDescriptor);
FrameMap frameMap = createFrameMap(state, functionDescriptor, signature, isStatic);
ExpressionCodegen codegen = new ExpressionCodegen(mv, frameMap, signature.getReturnType(), methodContext, state, parentCodegen);
CallGenerator generator = codegen.getOrCreateCallGeneratorForDefaultImplBody(functionDescriptor, function);
InstructionAdapter iv = new InstructionAdapter(mv);
genDefaultSuperCallCheckIfNeeded(iv, functionDescriptor, defaultMethod);
List<JvmMethodParameterSignature> mappedParameters = signature.getValueParameters();
int capturedArgumentsCount = 0;
while (capturedArgumentsCount < mappedParameters.size() &&
mappedParameters.get(capturedArgumentsCount).getKind() != JvmMethodParameterKind.VALUE) {
capturedArgumentsCount++;
}
int maskIndex = 0;
List<ValueParameterDescriptor> valueParameters = functionDescriptor.getValueParameters();
for (int index = 0; index < valueParameters.size(); index++) {
if (index % Integer.SIZE == 0) {
maskIndex = frameMap.enterTemp(Type.INT_TYPE);
}
ValueParameterDescriptor parameterDescriptor = valueParameters.get(index);
Type type = mappedParameters.get(capturedArgumentsCount + index).getAsmType();
int parameterIndex = frameMap.getIndex(parameterDescriptor);
if (parameterDescriptor.declaresDefaultValue()) {
iv.load(maskIndex, Type.INT_TYPE);
iv.iconst(1 << (index % Integer.SIZE));
iv.and(Type.INT_TYPE);
Label loadArg = new Label();
iv.ifeq(loadArg);
StackValue.local(parameterIndex, type).store(loadStrategy.genValue(parameterDescriptor, codegen), iv);
iv.mark(loadArg);
}
}
// load arguments after defaults generation to avoid redundant stack normalization operations
loadExplicitArgumentsOnStack(OBJECT_TYPE, isStatic, signature, generator);
for (int index = 0; index < valueParameters.size(); index++) {
ValueParameterDescriptor parameterDescriptor = valueParameters.get(index);
Type type = mappedParameters.get(capturedArgumentsCount + index).getAsmType();
int parameterIndex = frameMap.getIndex(parameterDescriptor);
generator.putValueIfNeeded(type, StackValue.local(parameterIndex, type));
}
CallableMethod method = state.getTypeMapper().mapToCallableMethod(functionDescriptor, false);
generator.genCall(method, null, false, codegen);
iv.areturn(signature.getReturnType());
}
private static void genDefaultSuperCallCheckIfNeeded(
@NotNull InstructionAdapter iv, @NotNull FunctionDescriptor descriptor, @NotNull Method defaultMethod
) {
if (descriptor instanceof ConstructorDescriptor) return;
DeclarationDescriptor container = descriptor.getContainingDeclaration();
if (!(container instanceof ClassDescriptor)) return;
if (((ClassDescriptor) container).getModality() == Modality.FINAL) return;
Label end = new Label();
int handleIndex = (Type.getArgumentsAndReturnSizes(defaultMethod.getDescriptor()) >> 2) - 2; /*-1 for this, and -1 for handle*/
iv.load(handleIndex, OBJECT_TYPE);
iv.ifnull(end);
AsmUtil.genThrow(
iv, "java/lang/UnsupportedOperationException",
"Super calls with default arguments not supported in this target, function: " + descriptor.getName().asString()
);
iv.visitLabel(end);
}
@NotNull
public static FrameMap createFrameMap(
@NotNull GenerationState state,
@NotNull FunctionDescriptor function,
@NotNull JvmMethodSignature signature,
boolean isStatic
) {
FrameMap frameMap = new FrameMap();
if (!isStatic) {
frameMap.enterTemp(OBJECT_TYPE);
}
for (JvmMethodParameterSignature parameter : signature.getValueParameters()) {
if (parameter.getKind() == JvmMethodParameterKind.RECEIVER) {
ReceiverParameterDescriptor receiverParameter = function.getExtensionReceiverParameter();
if (receiverParameter != null) {
frameMap.enter(receiverParameter, state.getTypeMapper().mapType(receiverParameter));
}
else {
frameMap.enterTemp(parameter.getAsmType());
}
}
else if (parameter.getKind() != JvmMethodParameterKind.VALUE) {
frameMap.enterTemp(parameter.getAsmType());
}
}
for (ValueParameterDescriptor parameter : function.getValueParameters()) {
frameMap.enter(parameter, state.getTypeMapper().mapType(parameter));
}
return frameMap;
}
private static void loadExplicitArgumentsOnStack(
@NotNull Type ownerType,
boolean isStatic,
@NotNull JvmMethodSignature signature,
@NotNull CallGenerator callGenerator
) {
int var = 0;
if (!isStatic) {
callGenerator.putValueIfNeeded(ownerType, StackValue.local(var, ownerType));
var += ownerType.getSize();
}
for (JvmMethodParameterSignature parameterSignature : signature.getValueParameters()) {
if (parameterSignature.getKind() != JvmMethodParameterKind.VALUE) {
Type type = parameterSignature.getAsmType();
callGenerator.putValueIfNeeded(type, StackValue.local(var, type));
var += type.getSize();
}
}
}
private static boolean isDefaultNeeded(FunctionDescriptor functionDescriptor) {
boolean needed = false;
if (functionDescriptor != null) {
for (ValueParameterDescriptor parameterDescriptor : functionDescriptor.getValueParameters()) {
if (parameterDescriptor.declaresDefaultValue()) {
needed = true;
break;
}
}
}
return needed;
}
private void generateBridge(
@Nullable PsiElement origin,
@NotNull FunctionDescriptor descriptor,
@NotNull Method bridge,
@NotNull Method delegateTo,
boolean isSpecialBridge,
boolean isStubDeclarationWithDelegationToSuper
) {
boolean isSpecialOrDelegationToSuper = isSpecialBridge || isStubDeclarationWithDelegationToSuper;
int flags = ACC_PUBLIC | ACC_BRIDGE | (!isSpecialOrDelegationToSuper ? ACC_SYNTHETIC : 0) | (isSpecialBridge ? ACC_FINAL : 0); // TODO.
MethodVisitor mv =
v.newMethod(JvmDeclarationOriginKt.Bridge(descriptor, origin), flags, bridge.getName(), bridge.getDescriptor(), null, null);
if (!state.getClassBuilderMode().generateBodies) return;
mv.visitCode();
Type[] argTypes = bridge.getArgumentTypes();
Type[] originalArgTypes = delegateTo.getArgumentTypes();
InstructionAdapter iv = new InstructionAdapter(mv);
MemberCodegen.markLineNumberForDescriptor(owner.getThisDescriptor(), iv);
if (delegateTo.getArgumentTypes().length > 0 && isSpecialBridge) {
generateTypeCheckBarrierIfNeeded(iv, descriptor, bridge.getReturnType(), delegateTo.getArgumentTypes());
}
iv.load(0, OBJECT_TYPE);
for (int i = 0, reg = 1; i < argTypes.length; i++) {
StackValue.local(reg, argTypes[i]).put(originalArgTypes[i], iv);
//noinspection AssignmentToForLoopParameter
reg += argTypes[i].getSize();
}
if (isStubDeclarationWithDelegationToSuper) {
ClassDescriptor parentClass = getSuperClassDescriptor((ClassDescriptor) descriptor.getContainingDeclaration());
assert parentClass != null;
String parentInternalName = typeMapper.mapClass(parentClass).getInternalName();
iv.invokespecial(parentInternalName, delegateTo.getName(), delegateTo.getDescriptor(), false);
}
else {
if (isJvm8InterfaceWithDefaultsMember(descriptor, state)) {
iv.invokeinterface(v.getThisName(), delegateTo.getName(), delegateTo.getDescriptor());
}
else {
iv.invokevirtual(v.getThisName(), delegateTo.getName(), delegateTo.getDescriptor(), false);
}
}
StackValue.coerce(delegateTo.getReturnType(), bridge.getReturnType(), iv);
iv.areturn(bridge.getReturnType());
endVisit(mv, "bridge method", origin);
}
private static void generateTypeCheckBarrierIfNeeded(
@NotNull InstructionAdapter iv,
@NotNull FunctionDescriptor descriptor,
@NotNull Type returnType,
@Nullable Type[] delegateParameterTypes
) {
BuiltinMethodsWithSpecialGenericSignature.TypeSafeBarrierDescription typeSafeBarrierDescription =
BuiltinMethodsWithSpecialGenericSignature.getDefaultValueForOverriddenBuiltinFunction(descriptor);
if (typeSafeBarrierDescription == null) return;
FunctionDescriptor overriddenBuiltin =
BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(descriptor);
assert overriddenBuiltin != null : "Overridden built-in method should not be null for " + descriptor;
Label defaultBranch = new Label();
for (int i = 0; i < descriptor.getValueParameters().size(); i++) {
if (!typeSafeBarrierDescription.checkParameter(i)) continue;
boolean isCheckForAny = delegateParameterTypes == null || OBJECT_TYPE.equals(delegateParameterTypes[i]);
KotlinType kotlinType = descriptor.getValueParameters().get(i).getType();
if (isCheckForAny && TypeUtils.isNullableType(kotlinType)) continue;
iv.load(1 + i, OBJECT_TYPE);
if (isCheckForAny) {
assert !TypeUtils.isNullableType(kotlinType) : "Only bridges for not-nullable types are necessary";
iv.ifnull(defaultBranch);
}
else {
CodegenUtilKt.generateIsCheck(iv, kotlinType, boxType(delegateParameterTypes[i]));
iv.ifeq(defaultBranch);
}
}
Label afterDefaultBranch = new Label();
iv.goTo(afterDefaultBranch);
iv.visitLabel(defaultBranch);
if (typeSafeBarrierDescription.equals(BuiltinMethodsWithSpecialGenericSignature.TypeSafeBarrierDescription.MAP_GET_OR_DEFAULT)) {
iv.load(2, returnType);
}
else {
StackValue.constant(typeSafeBarrierDescription.getDefaultValue(), returnType).put(returnType, iv);
}
iv.areturn(returnType);
iv.visitLabel(afterDefaultBranch);
}
public void genSamDelegate(@NotNull FunctionDescriptor functionDescriptor, FunctionDescriptor overriddenDescriptor, StackValue field) {
FunctionDescriptor delegatedTo = overriddenDescriptor.getOriginal();
JvmDeclarationOrigin declarationOrigin =
JvmDeclarationOriginKt.SamDelegation(functionDescriptor);
genDelegate(
functionDescriptor, delegatedTo,
declarationOrigin,
(ClassDescriptor) overriddenDescriptor.getContainingDeclaration(),
field);
}
public void genDelegate(@NotNull FunctionDescriptor functionDescriptor, FunctionDescriptor overriddenDescriptor, StackValue field) {
genDelegate(functionDescriptor, overriddenDescriptor.getOriginal(),
(ClassDescriptor) overriddenDescriptor.getContainingDeclaration(), field);
}
public void genDelegate(
@NotNull FunctionDescriptor delegateFunction,
FunctionDescriptor delegatedTo,
ClassDescriptor toClass,
StackValue field
) {
JvmDeclarationOrigin declarationOrigin =
JvmDeclarationOriginKt.Delegation(DescriptorToSourceUtils.descriptorToDeclaration(delegatedTo), delegateFunction);
genDelegate(delegateFunction, delegatedTo, declarationOrigin, toClass, field);
}
private void genDelegate(
@NotNull FunctionDescriptor delegateFunction,
FunctionDescriptor delegatedTo,
@NotNull JvmDeclarationOrigin declarationOrigin,
ClassDescriptor toClass,
StackValue field
) {
generateMethod(
declarationOrigin, delegateFunction,
new FunctionGenerationStrategy() {
@Override
public void generateBody(
@NotNull MethodVisitor mv,
@NotNull FrameMap frameMap,
@NotNull JvmMethodSignature signature,
@NotNull MethodContext context,
@NotNull MemberCodegen<?> parentCodegen
) {
Method delegateToMethod = typeMapper.mapToCallableMethod(delegatedTo, /* superCall = */ false).getAsmMethod();
Method delegateMethod = typeMapper.mapAsmMethod(delegateFunction);
Type[] argTypes = delegateMethod.getArgumentTypes();
Type[] originalArgTypes = delegateToMethod.getArgumentTypes();
InstructionAdapter iv = new InstructionAdapter(mv);
iv.load(0, OBJECT_TYPE);
field.put(field.type, iv);
for (int i = 0, reg = 1; i < argTypes.length; i++) {
StackValue.local(reg, argTypes[i]).put(originalArgTypes[i], iv);
//noinspection AssignmentToForLoopParameter
reg += argTypes[i].getSize();
}
String internalName = typeMapper.mapType(toClass).getInternalName();
if (toClass.getKind() == ClassKind.INTERFACE) {
iv.invokeinterface(internalName, delegateToMethod.getName(), delegateToMethod.getDescriptor());
}
else {
iv.invokevirtual(internalName, delegateToMethod.getName(), delegateToMethod.getDescriptor(), false);
}
StackValue stackValue = AsmUtil.genNotNullAssertions(
state,
StackValue.onStack(delegateToMethod.getReturnType()),
RuntimeAssertionInfo.create(
delegateFunction.getReturnType(),
delegatedTo.getReturnType(),
new RuntimeAssertionInfo.DataFlowExtras.OnlyMessage(delegatedTo.getName() + "(...)")
)
);
stackValue.put(delegateMethod.getReturnType(), iv);
iv.areturn(delegateMethod.getReturnType());
}
}
);
}
public static boolean processInterfaceMember(
@NotNull CallableMemberDescriptor function,
@NotNull OwnerKind kind,
@NotNull GenerationState state
) {
return processInterface(function.getContainingDeclaration(), kind, state);
}
public static boolean processInterface(
@NotNull DeclarationDescriptor contextClass,
@NotNull OwnerKind kind,
@NotNull GenerationState state
) {
assert isInterface(contextClass) : "'processInterface' method should be called only for interfaces, but: " + contextClass;
return JvmCodegenUtil.isJvm8InterfaceWithDefaults(contextClass, state) ? kind != OwnerKind.DEFAULT_IMPLS : kind == OwnerKind.DEFAULT_IMPLS;
}
}