/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen;
import com.google.common.collect.
Lists;
import com.intellij.util.
ArrayUtil;
import kotlin.
Pair;
import kotlin.
Unit;
import kotlin.collections.
CollectionsKt;
import org.jetbrains.annotations.
NotNull;
import org.jetbrains.annotations.
Nullable;
import org.jetbrains.kotlin.codegen.binding.
CalculatedClosure;
import org.jetbrains.kotlin.codegen.binding.
CodegenBinding;
import org.jetbrains.kotlin.codegen.context.
ClosureContext;
import org.jetbrains.kotlin.codegen.context.
EnclosedValueDescriptor;
import org.jetbrains.kotlin.codegen.coroutines.
CoroutineCodegenUtilKt;
import org.jetbrains.kotlin.codegen.serialization.
JvmSerializerExtension;
import org.jetbrains.kotlin.codegen.signature.
BothSignatureWriter;
import org.jetbrains.kotlin.codegen.signature.
JvmSignatureWriter;
import org.jetbrains.kotlin.codegen.state.
GenerationState;
import org.jetbrains.kotlin.codegen.state.
KotlinTypeMapper;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.
LocalVariableDescriptor;
import org.jetbrains.kotlin.descriptors.impl.
SimpleFunctionDescriptorImpl;
import org.jetbrains.kotlin.incremental.components.
NoLookupLocation;
import org.jetbrains.kotlin.load.java.
JvmAbi;
import org.jetbrains.kotlin.load.kotlin.header.
KotlinClassHeader;
import org.jetbrains.kotlin.metadata.
ProtoBuf;
import org.jetbrains.kotlin.psi.
KtElement;
import org.jetbrains.kotlin.resolve.
DescriptorUtils;
import org.jetbrains.kotlin.resolve.descriptorUtil.
DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.
JvmDeclarationOriginKt;
import org.jetbrains.kotlin.resolve.scopes.
MemberScope;
import org.jetbrains.kotlin.serialization.
DescriptorSerializer;
import org.jetbrains.kotlin.types.
KotlinType;
import org.jetbrains.kotlin.types.expressions.
ExpressionTypingUtils;
import org.jetbrains.kotlin.util.
OperatorNameConventions;
import org.jetbrains.org.objectweb.asm.
MethodVisitor;
import org.jetbrains.org.objectweb.asm.
Type;
import org.jetbrains.org.objectweb.asm.commons.
InstructionAdapter;
import org.jetbrains.org.objectweb.asm.commons.
Method;
import java.util.
ArrayList;
import java.util.
Collections;
import java.util.
List;
import static org.jetbrains.kotlin.codegen.
AsmUtil.*;
import static org.jetbrains.kotlin.codegen.
JvmCodegenUtil.isConst;
import static org.jetbrains.kotlin.codegen.binding.
CodegenBinding.
CLOSURE;
import static org.jetbrains.kotlin.codegen.inline.
InlineCodegenUtils2Kt.initDefaultSourceMappingIfNeeded;
import static org.jetbrains.kotlin.codegen.serialization.
JvmSerializationBindings.
METHOD_FOR_FUNCTION;
import static org.jetbrains.kotlin.resolve.jvm.
AsmTypes.*;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.
JvmDeclarationOrigin.
NO_ORIGIN;
import static org.jetbrains.org.objectweb.asm.
Opcodes.*;
public class
ClosureCodegen extends
MemberCodegen<
KtElement> {
protected final
FunctionDescriptor funDescriptor;
private final
ClassDescriptor classDescriptor;
private final
SamType samType;
private final
KotlinType superClassType;
private final
List<
KotlinType>
superInterfaceTypes;
private final
FunctionDescriptor functionReferenceTarget;
private final
FunctionGenerationStrategy strategy;
protected final
CalculatedClosure closure;
protected final
Type asmType;
protected final int
visibilityFlag;
private final boolean
shouldHaveBoundReferenceReceiver;
private
Method constructor;
protected
Type superClassAsmType;
public
ClosureCodegen(
@
NotNull GenerationState state,
@
NotNull KtElement element,
@
Nullable SamType samType,
@
NotNull ClosureContext context,
@
Nullable FunctionDescriptor functionReferenceTarget,
@
NotNull FunctionGenerationStrategy strategy,
@
NotNull MemberCodegen<?>
parentCodegen,
@
NotNull ClassBuilder classBuilder
) {
super(
state,
parentCodegen,
context,
element,
classBuilder);
this.
funDescriptor =
context.
getFunctionDescriptor();
this.
classDescriptor =
context.
getContextDescriptor();
this.
samType =
samType;
this.
functionReferenceTarget =
functionReferenceTarget;
this.
strategy =
strategy;
if (
samType == null) {
this.
superInterfaceTypes = new
ArrayList<>();
KotlinType superClassType = null;
for (
KotlinType supertype :
classDescriptor.
getTypeConstructor().
getSupertypes()) {
ClassifierDescriptor classifier =
supertype.
getConstructor().
getDeclarationDescriptor();
if (
DescriptorUtils.
isInterface(
classifier)) {
superInterfaceTypes.
add(
supertype);
}
else {
assert
superClassType == null : "Closure class can't have more than one superclass: " +
funDescriptor;
superClassType =
supertype;
}
}
assert
superClassType != null : "Closure class should have a superclass: " +
funDescriptor;
this.
superClassType =
superClassType;
}
else {
this.
superInterfaceTypes =
Collections.
singletonList(
samType.
getType());
this.
superClassType =
DescriptorUtilsKt.
getBuiltIns(
funDescriptor).
getAnyType();
}
this.
closure =
bindingContext.
get(
CLOSURE,
classDescriptor);
assert
closure != null : "Closure must be calculated for class: " +
classDescriptor;
this.
shouldHaveBoundReferenceReceiver =
CallableReferenceUtilKt.
isForBoundCallableReference(
closure);
this.
asmType =
typeMapper.
mapClass(
classDescriptor);
visibilityFlag =
AsmUtil.
getVisibilityAccessFlagForClass(
classDescriptor);
}
@
Override
protected void
generateDeclaration() {
JvmSignatureWriter sw = new
BothSignatureWriter(
BothSignatureWriter.
Mode.
CLASS);
if (
samType != null) {
typeMapper.
writeFormalTypeParameters(
samType.
getType().
getConstructor().
getParameters(),
sw);
}
sw.
writeSuperclass();
superClassAsmType =
typeMapper.
mapSupertype(
superClassType,
sw);
sw.
writeSuperclassEnd();
String[]
superInterfaceAsmTypes = new
String[
superInterfaceTypes.
size()];
for (int
i = 0;
i <
superInterfaceTypes.
size();
i++) {
KotlinType superInterfaceType =
superInterfaceTypes.
get(
i);
sw.
writeInterface();
superInterfaceAsmTypes[
i] =
typeMapper.
mapSupertype(
superInterfaceType,
sw).
getInternalName();
sw.
writeInterfaceEnd();
}
v.
defineClass(
element,
state.
getClassFileVersion(),
ACC_FINAL |
ACC_SUPER |
visibilityFlag,
asmType.
getInternalName(),
sw.
makeJavaGenericSignature(),
superClassAsmType.
getInternalName(),
superInterfaceAsmTypes
);
initDefaultSourceMappingIfNeeded(
context, this,
state);
v.
visitSource(
element.
getContainingFile().
getName(), null);
}
@
Nullable
@
Override
protected
ClassDescriptor classForInnerClassRecord() {
return
JvmCodegenUtil.
isArgumentWhichWillBeInlined(
bindingContext,
funDescriptor) ? null :
classDescriptor;
}
@
Override
protected void
generateBody() {
generateBridges();
generateClosureBody();
this.
constructor =
generateConstructor();
if (
isConst(
closure)) {
generateConstInstance(
asmType,
asmType);
}
genClosureFields(
closure,
v,
typeMapper);
}
protected void
generateClosureBody() {
functionCodegen.
generateMethod(
JvmDeclarationOriginKt.
OtherOrigin(
element,
funDescriptor),
funDescriptor,
strategy);
if (
functionReferenceTarget != null) {
generateFunctionReferenceMethods(
functionReferenceTarget);
}
functionCodegen.
generateDefaultIfNeeded(
context.
intoFunction(
funDescriptor),
funDescriptor,
context.
getContextKind(),
DefaultParameterValueLoader.
DEFAULT, null
);
}
protected void
generateBridges() {
FunctionDescriptor erasedInterfaceFunction;
if (
samType == null) {
erasedInterfaceFunction =
getErasedInvokeFunction(
funDescriptor);
}
else {
erasedInterfaceFunction =
samType.
getOriginalAbstractMethod();
}
List<
KotlinType>
bridgeParameterKotlinTypes =
CollectionsKt.
map(
erasedInterfaceFunction.
getValueParameters(),
ValueDescriptor::getType);
generateBridge(
typeMapper.
mapAsmMethod(
erasedInterfaceFunction),
bridgeParameterKotlinTypes,
erasedInterfaceFunction.
getReturnType(),
typeMapper.
mapAsmMethod(
funDescriptor),
funDescriptor.
getReturnType(),
JvmCodegenUtil.
isDeclarationOfBigArityFunctionInvoke(
erasedInterfaceFunction)
);
//TODO: rewrite cause ugly hack
if (
samType != null) {
SimpleFunctionDescriptorImpl descriptorForBridges =
SimpleFunctionDescriptorImpl
.
create(
funDescriptor.
getContainingDeclaration(),
funDescriptor.
getAnnotations(),
erasedInterfaceFunction.
getName(),
CallableMemberDescriptor.
Kind.
DECLARATION,
funDescriptor.
getSource());
descriptorForBridges
.
initialize(null,
erasedInterfaceFunction.
getDispatchReceiverParameter(),
erasedInterfaceFunction.
getTypeParameters(),
erasedInterfaceFunction.
getValueParameters(),
erasedInterfaceFunction.
getReturnType(),
Modality.
OPEN,
erasedInterfaceFunction.
getVisibility());
DescriptorUtilsKt.
setSingleOverridden(
descriptorForBridges,
erasedInterfaceFunction);
functionCodegen.
generateBridges(
descriptorForBridges);
}
}
@
Override
protected void
generateKotlinMetadataAnnotation() {
FunctionDescriptor frontendFunDescriptor =
CodegenUtilKt.
unwrapFrontendVersion(
funDescriptor);
Method method =
v.
getSerializationBindings().
get(
METHOD_FOR_FUNCTION,
frontendFunDescriptor);
assert
method != null : "No method for " +
frontendFunDescriptor;
FunctionDescriptor freeLambdaDescriptor =
FakeDescriptorsForReferencesKt.
createFreeFakeLambdaDescriptor(
frontendFunDescriptor);
v.
getSerializationBindings().
put(
METHOD_FOR_FUNCTION,
freeLambdaDescriptor,
method);
DescriptorSerializer serializer =
DescriptorSerializer.
createForLambda(new
JvmSerializerExtension(
v.
getSerializationBindings(),
state));
ProtoBuf.
Function functionProto =
serializer.
functionProto(
freeLambdaDescriptor).
build();
WriteAnnotationUtilKt.
writeKotlinMetadata(
v,
state,
KotlinClassHeader.
Kind.
SYNTHETIC_CLASS, 0,
av -> {
writeAnnotationData(
av,
serializer,
functionProto);
return
Unit.
INSTANCE;
});
}
@
Override
protected void
done() {
writeOuterClassAndEnclosingMethod();
super.done();
}
@
NotNull
public
StackValue putInstanceOnStack(@
NotNull ExpressionCodegen codegen, @
Nullable StackValue functionReferenceReceiver) {
return
StackValue.
operation(
functionReferenceTarget != null ?
K_FUNCTION :
asmType,
v -> {
if (
isConst(
closure)) {
v.
getstatic(
asmType.
getInternalName(),
JvmAbi.
INSTANCE_FIELD,
asmType.
getDescriptor());
}
else {
v.
anew(
asmType);
v.
dup();
codegen.
pushClosureOnStack(
classDescriptor, true,
codegen.
defaultCallGenerator,
functionReferenceReceiver);
v.
invokespecial(
asmType.
getInternalName(), "<init>",
constructor.
getDescriptor(), false);
}
return
Unit.
INSTANCE;
}
);
}
protected void
generateBridge(
@
NotNull Method bridge,
@
NotNull List<
KotlinType>
bridgeParameterKotlinTypes,
@
Nullable KotlinType bridgeReturnType,
@
NotNull Method delegate,
@
Nullable KotlinType delegateReturnType,
boolean
isVarargInvoke
) {
if (
bridge.
equals(
delegate)) return;
MethodVisitor mv =
v.
newMethod(
JvmDeclarationOriginKt.
OtherOrigin(
element,
funDescriptor),
ACC_PUBLIC |
ACC_BRIDGE |
ACC_SYNTHETIC,
bridge.
getName(),
bridge.
getDescriptor(), null,
ArrayUtil.
EMPTY_STRING_ARRAY);
if (!
state.
getClassBuilderMode().
generateBodies) return;
mv.
visitCode();
InstructionAdapter iv = new
InstructionAdapter(
mv);
MemberCodegen.
markLineNumberForDescriptor(
DescriptorUtils.
getParentOfType(
funDescriptor,
ClassDescriptor.class),
iv);
Type[]
bridgeParameterTypes =
bridge.
getArgumentTypes();
if (
isVarargInvoke) {
assert
bridgeParameterTypes.length == 1 &&
bridgeParameterTypes[0].
equals(
AsmUtil.
getArrayType(
OBJECT_TYPE)) :
"Vararg invoke must have one parameter of type [Ljava/lang/Object;: " +
bridge;
generateVarargInvokeArityAssert(
iv,
delegate.
getArgumentTypes().length);
}
else {
assert
bridgeParameterTypes.length ==
bridgeParameterKotlinTypes.
size() :
"Asm parameter types should be the same length as Kotlin parameter types";
}
iv.
load(0,
asmType);
List<
ParameterDescriptor>
calleeParameters =
CollectionsKt.
plus(
CollectionsKt.
listOfNotNull(
funDescriptor.
getExtensionReceiverParameter()),
funDescriptor.
getValueParameters()
);
int
slot = 1;
for (int
i = 0;
i <
calleeParameters.
size();
i++) {
ParameterDescriptor calleeParameter =
calleeParameters.
get(
i);
KotlinType parameterType =
calleeParameter.
getType();
StackValue value;
if (
isVarargInvoke) {
value =
StackValue.
arrayElement(
OBJECT_TYPE, null,
StackValue.
local(1,
bridgeParameterTypes[0],
bridgeParameterKotlinTypes.
get(0)),
StackValue.
constant(
i)
);
}
else {
Type type =
bridgeParameterTypes[
i];
value =
StackValue.
local(
slot,
type,
bridgeParameterKotlinTypes.
get(
i));
slot +=
type.
getSize();
}
value.
put(
typeMapper.
mapType(
calleeParameter),
parameterType,
iv);
}
iv.
invokevirtual(
asmType.
getInternalName(),
delegate.
getName(),
delegate.
getDescriptor(), false);
StackValue
.
onStack(
delegate.
getReturnType(),
delegateReturnType)
.
put(
bridge.
getReturnType(),
bridgeReturnType,
iv);
iv.
areturn(
bridge.
getReturnType());
FunctionCodegen.
endVisit(
mv, "bridge",
element);
}
// TODO: ImplementationBodyCodegen.markLineNumberForSyntheticFunction?
private void
generateFunctionReferenceMethods(@
NotNull FunctionDescriptor descriptor) {
int
flags =
ACC_PUBLIC |
ACC_FINAL;
boolean
generateBody =
state.
getClassBuilderMode().
generateBodies;
{
MethodVisitor mv =
v.
newMethod(
NO_ORIGIN,
flags, "getOwner",
Type.
getMethodDescriptor(
K_DECLARATION_CONTAINER_TYPE), null, null);
if (
generateBody) {
mv.
visitCode();
InstructionAdapter iv = new
InstructionAdapter(
mv);
generateCallableReferenceDeclarationContainer(
iv,
descriptor,
state);
iv.
areturn(
K_DECLARATION_CONTAINER_TYPE);
FunctionCodegen.
endVisit(
iv, "function reference getOwner",
element);
}
}
{
MethodVisitor mv =
v.
newMethod(
NO_ORIGIN,
flags, "getName",
Type.
getMethodDescriptor(
JAVA_STRING_TYPE), null, null);
if (
generateBody) {
mv.
visitCode();
InstructionAdapter iv = new
InstructionAdapter(
mv);
iv.
aconst(
descriptor.
getName().
asString());
iv.
areturn(
JAVA_STRING_TYPE);
FunctionCodegen.
endVisit(
iv, "function reference getName",
element);
}
}
{
MethodVisitor mv =
v.
newMethod(
NO_ORIGIN,
flags, "getSignature",
Type.
getMethodDescriptor(
JAVA_STRING_TYPE), null, null);
if (
generateBody) {
mv.
visitCode();
InstructionAdapter iv = new
InstructionAdapter(
mv);
PropertyReferenceCodegen.
generateCallableReferenceSignature(
iv,
descriptor,
state);
iv.
areturn(
JAVA_STRING_TYPE);
FunctionCodegen.
endVisit(
iv, "function reference getSignature",
element);
}
}
}
public static void
generateCallableReferenceDeclarationContainer(
@
NotNull InstructionAdapter iv,
@
NotNull CallableDescriptor descriptor,
@
NotNull GenerationState state
) {
DeclarationDescriptor container =
descriptor.
getContainingDeclaration();
if (
container instanceof
ClassDescriptor) {
// TODO: getDefaultType() here is wrong and won't work for arrays
putJavaLangClassInstance(
iv,
state.
getTypeMapper().
mapType(((
ClassDescriptor)
container).
getDefaultType()));
}
else if (
container instanceof
PackageFragmentDescriptor) {
iv.
aconst(
state.
getTypeMapper().
mapOwner(
descriptor));
}
else if (
descriptor instanceof
VariableDescriptorWithAccessors) {
iv.
aconst(
state.
getBindingContext().
get(
CodegenBinding.
DELEGATED_PROPERTY_METADATA_OWNER, ((
VariableDescriptorWithAccessors)
descriptor)
));
}
else {
iv.
aconst(null);
return;
}
boolean
isContainerPackage =
descriptor instanceof
LocalVariableDescriptor
?
DescriptorUtils.
getParentOfType(
descriptor,
ClassDescriptor.class) == null
:
container instanceof
PackageFragmentDescriptor;
if (
isContainerPackage) {
// Note that this name is not used in reflection. There should be the name of the referenced declaration's module instead,
// but there's no nice API to obtain that name here yet
// TODO: write the referenced declaration's module name and use it in reflection
iv.
aconst(
state.
getModuleName());
iv.
invokestatic(
REFLECTION, "getOrCreateKotlinPackage",
Type.
getMethodDescriptor(
K_DECLARATION_CONTAINER_TYPE,
getType(
Class.class),
getType(
String.class)), false);
}
else {
wrapJavaClassIntoKClass(
iv);
}
}
@
NotNull
protected
Method generateConstructor() {
List<
FieldInfo>
args =
calculateConstructorParameters(
typeMapper,
closure,
asmType);
Type[]
argTypes =
fieldListToTypeArray(
args);
Method constructor = new
Method("<init>",
Type.
VOID_TYPE,
argTypes);
MethodVisitor mv =
v.
newMethod(
JvmDeclarationOriginKt.
OtherOrigin(
element,
funDescriptor),
visibilityFlag, "<init>",
constructor.
getDescriptor(), null,
ArrayUtil.
EMPTY_STRING_ARRAY);
if (
state.
getClassBuilderMode().
generateBodies) {
mv.
visitCode();
InstructionAdapter iv = new
InstructionAdapter(
mv);
Pair<
Integer,
Type>
receiverIndexAndType =
CallableReferenceUtilKt.
generateClosureFieldsInitializationFromParameters(
iv,
closure,
args);
if (
shouldHaveBoundReferenceReceiver &&
receiverIndexAndType == null) {
throw new
AssertionError("No bound reference receiver in constructor parameters: " +
args);
}
int
boundReferenceReceiverParameterIndex =
shouldHaveBoundReferenceReceiver ?
receiverIndexAndType.
getFirst() : -1;
Type boundReferenceReceiverType =
shouldHaveBoundReferenceReceiver ?
receiverIndexAndType.
getSecond() : null;
iv.
load(0,
superClassAsmType);
String superClassConstructorDescriptor;
if (
superClassAsmType.
equals(
LAMBDA) ||
superClassAsmType.
equals(
FUNCTION_REFERENCE) ||
CoroutineCodegenUtilKt.
isCoroutineSuperClass(
state.
getLanguageVersionSettings(),
superClassAsmType.
getInternalName())) {
int
arity =
calculateArity();
iv.
iconst(
arity);
if (
shouldHaveBoundReferenceReceiver) {
CallableReferenceUtilKt.
loadBoundReferenceReceiverParameter(
iv,
boundReferenceReceiverParameterIndex,
boundReferenceReceiverType);
superClassConstructorDescriptor = "(ILjava/lang/Object;)V";
}
else {
superClassConstructorDescriptor = "(I)V";
}
}
else {
assert !
shouldHaveBoundReferenceReceiver : "Unexpected bound reference with supertype " +
superClassAsmType;
superClassConstructorDescriptor = "()V";
}
iv.
invokespecial(
superClassAsmType.
getInternalName(), "<init>",
superClassConstructorDescriptor, false);
iv.
visitInsn(
RETURN);
FunctionCodegen.
endVisit(
iv, "constructor",
element);
}
return
constructor;
}
protected int
calculateArity() {
int
arity =
funDescriptor.
getValueParameters().
size();
if (
funDescriptor.
getExtensionReceiverParameter() != null)
arity++;
if (
funDescriptor.
getDispatchReceiverParameter() != null)
arity++;
return
arity;
}
@
NotNull
public static
List<
FieldInfo>
calculateConstructorParameters(
@
NotNull KotlinTypeMapper typeMapper,
@
NotNull CalculatedClosure closure,
@
NotNull Type ownerType
) {
List<
FieldInfo>
args =
Lists.
newArrayList();
ClassDescriptor captureThis =
closure.
getCaptureThis();
if (
captureThis != null) {
Type type =
typeMapper.
mapType(
captureThis);
args.
add(
FieldInfo.
createForHiddenField(
ownerType,
type,
CAPTURED_THIS_FIELD));
}
KotlinType captureReceiverType =
closure.
getCaptureReceiverType();
if (
captureReceiverType != null) {
args.
add(
FieldInfo.
createForHiddenField(
ownerType,
typeMapper.
mapType(
captureReceiverType),
CAPTURED_RECEIVER_FIELD));
}
for (
EnclosedValueDescriptor enclosedValueDescriptor :
closure.
getCaptureVariables().
values()) {
DeclarationDescriptor descriptor =
enclosedValueDescriptor.
getDescriptor();
if ((
descriptor instanceof
VariableDescriptor && !(
descriptor instanceof
PropertyDescriptor)) ||
ExpressionTypingUtils.
isLocalFunction(
descriptor)) {
args.
add(
FieldInfo.
createForHiddenField(
ownerType,
enclosedValueDescriptor.
getType(),
enclosedValueDescriptor.
getFieldName()
)
);
}
else if (
descriptor instanceof
FunctionDescriptor) {
assert
captureReceiverType != null;
}
}
return
args;
}
private static
Type[]
fieldListToTypeArray(
List<
FieldInfo>
args) {
Type[]
argTypes = new
Type[
args.
size()];
for (int
i = 0;
i !=
argTypes.length; ++
i) {
argTypes[
i] =
args.
get(
i).
getFieldType();
}
return
argTypes;
}
@
NotNull
public static
FunctionDescriptor getErasedInvokeFunction(@
NotNull FunctionDescriptor function) {
ClassDescriptor functionClass =
DescriptorUtilsKt.
getBuiltIns(
function).
getFunction(
function.
getValueParameters().
size() + (
function.
getExtensionReceiverParameter() != null ? 1 : 0)
);
MemberScope scope =
functionClass.
getDefaultType().
getMemberScope();
return
scope.
getContributedFunctions(
OperatorNameConventions.
INVOKE,
NoLookupLocation.
FROM_BACKEND).
iterator().
next();
}
}