Refactoring: extract methods about constructor codegen out
#KT-25614 In Progress
This commit is contained in:
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* 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 org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.codegen.binding.MutableClosure;
|
||||
import org.jetbrains.kotlin.codegen.context.ConstructorContext;
|
||||
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext;
|
||||
import org.jetbrains.kotlin.codegen.context.MethodContext;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
||||
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.org.objectweb.asm.Type;
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvmInterface;
|
||||
import static org.jetbrains.kotlin.codegen.MemberCodegen.markLineNumberForDescriptor;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall;
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
|
||||
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE;
|
||||
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE;
|
||||
import static org.jetbrains.org.objectweb.asm.Opcodes.RETURN;
|
||||
|
||||
public class ConstructorCodegen {
|
||||
private final ClassDescriptor descriptor;
|
||||
private final FieldOwnerContext<?> context;
|
||||
private final FunctionCodegen functionCodegen;
|
||||
private final MemberCodegen<?> memberCodegen;
|
||||
private final ClassBodyCodegen classBodyCodegen;
|
||||
private final GenerationState state;
|
||||
private final OwnerKind kind;
|
||||
private final ClassBuilder v;
|
||||
private final Type classAsmType;
|
||||
private final KtPureClassOrObject myClass;
|
||||
private final BindingContext bindingContext;
|
||||
private final KotlinTypeMapper typeMapper;
|
||||
|
||||
public ConstructorCodegen(
|
||||
@NotNull ClassDescriptor descriptor,
|
||||
@NotNull FieldOwnerContext<?> context,
|
||||
@NotNull FunctionCodegen codegen,
|
||||
@NotNull MemberCodegen<?> memberCodegen,
|
||||
@NotNull ClassBodyCodegen classBodyCodegen,
|
||||
@NotNull GenerationState state,
|
||||
@NotNull OwnerKind kind,
|
||||
@NotNull ClassBuilder v,
|
||||
@NotNull Type type,
|
||||
@NotNull KtPureClassOrObject myClass,
|
||||
@NotNull BindingContext bindingContext
|
||||
) {
|
||||
this.descriptor = descriptor;
|
||||
this.context = context;
|
||||
functionCodegen = codegen;
|
||||
this.memberCodegen = memberCodegen;
|
||||
this.classBodyCodegen = classBodyCodegen;
|
||||
this.state = state;
|
||||
this.kind = kind;
|
||||
this.v = v;
|
||||
this.classAsmType = type;
|
||||
this.myClass = myClass;
|
||||
this.bindingContext = bindingContext;
|
||||
this.typeMapper = state.getTypeMapper();
|
||||
}
|
||||
|
||||
public void generatePrimaryConstructor(DelegationFieldsInfo delegationFieldsInfo, Type superClassAsmType) {
|
||||
if (isJvmInterface(descriptor)) return;
|
||||
|
||||
ClassConstructorDescriptor constructorDescriptor = descriptor.getUnsubstitutedPrimaryConstructor();
|
||||
if (constructorDescriptor == null) return;
|
||||
|
||||
ConstructorContext constructorContext = context.intoConstructor(constructorDescriptor);
|
||||
|
||||
KtPrimaryConstructor primaryConstructor = myClass.getPrimaryConstructor();
|
||||
JvmDeclarationOrigin origin = JvmDeclarationOriginKt
|
||||
.OtherOrigin(primaryConstructor != null ? primaryConstructor : myClass.getPsiOrParent(), constructorDescriptor);
|
||||
functionCodegen.generateMethod(origin, constructorDescriptor, constructorContext,
|
||||
new FunctionGenerationStrategy.CodegenBased(state) {
|
||||
@Override
|
||||
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
|
||||
generatePrimaryConstructorImpl(
|
||||
constructorDescriptor,
|
||||
codegen,
|
||||
delegationFieldsInfo,
|
||||
primaryConstructor,
|
||||
superClassAsmType
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
functionCodegen.generateDefaultIfNeeded(constructorContext, constructorDescriptor, OwnerKind.IMPLEMENTATION,
|
||||
DefaultParameterValueLoader.DEFAULT, null);
|
||||
|
||||
new DefaultParameterValueSubstitutor(state).generatePrimaryConstructorOverloadsIfNeeded(constructorDescriptor, v, memberCodegen, kind, myClass);
|
||||
}
|
||||
|
||||
public void generateSecondaryConstructor(
|
||||
@NotNull ClassConstructorDescriptor constructorDescriptor,
|
||||
@NotNull Type superClassAsmType
|
||||
) {
|
||||
if (!canHaveDeclaredConstructors(descriptor)) return;
|
||||
|
||||
ConstructorContext constructorContext = context.intoConstructor(constructorDescriptor);
|
||||
|
||||
KtSecondaryConstructor constructor = (KtSecondaryConstructor) descriptorToDeclaration(constructorDescriptor);
|
||||
|
||||
functionCodegen.generateMethod(
|
||||
JvmDeclarationOriginKt.OtherOrigin(constructor, constructorDescriptor),
|
||||
constructorDescriptor, constructorContext,
|
||||
new FunctionGenerationStrategy.CodegenBased(state) {
|
||||
@Override
|
||||
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
|
||||
generateSecondaryConstructorImpl(constructorDescriptor, codegen, superClassAsmType);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
functionCodegen.generateDefaultIfNeeded(constructorContext, constructorDescriptor, OwnerKind.IMPLEMENTATION,
|
||||
DefaultParameterValueLoader.DEFAULT, null);
|
||||
|
||||
new DefaultParameterValueSubstitutor(state).generateOverloadsIfNeeded(
|
||||
constructor, constructorDescriptor, constructorDescriptor, kind, v, memberCodegen
|
||||
);
|
||||
}
|
||||
|
||||
private void generateDelegatorToConstructorCall(
|
||||
@NotNull InstructionAdapter iv,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull ClassConstructorDescriptor constructorDescriptor,
|
||||
@Nullable ResolvedCall<ConstructorDescriptor> delegationConstructorCall,
|
||||
@NotNull Type superClassAsmType
|
||||
) {
|
||||
MethodContext codegenContext = codegen.context;
|
||||
assert codegenContext instanceof ConstructorContext :
|
||||
"Constructor context expected: " + codegenContext;
|
||||
assert !((ConstructorContext) codegenContext).isThisInitialized() :
|
||||
"Delegating constructor call is already generated for " + ((ConstructorContext) codegenContext).getConstructorDescriptor();
|
||||
|
||||
if (delegationConstructorCall == null) {
|
||||
genSimpleSuperCall(iv, superClassAsmType);
|
||||
}
|
||||
else {
|
||||
generateDelegationConstructorCall(iv, codegen, constructorDescriptor, delegationConstructorCall);
|
||||
}
|
||||
|
||||
((ConstructorContext) codegenContext).setThisInitialized(true);
|
||||
}
|
||||
|
||||
private void generatePrimaryConstructorImpl(
|
||||
@NotNull ClassConstructorDescriptor constructorDescriptor,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull DelegationFieldsInfo fieldsInfo,
|
||||
@Nullable KtPrimaryConstructor primaryConstructor,
|
||||
@NotNull Type superClassAsmType
|
||||
) {
|
||||
InstructionAdapter iv = codegen.v;
|
||||
|
||||
markLineNumberForConstructor(constructorDescriptor, primaryConstructor, codegen);
|
||||
|
||||
generateClosureInitialization(iv);
|
||||
|
||||
generateDelegatorToConstructorCall(
|
||||
iv, codegen, constructorDescriptor, getDelegationConstructorCall(bindingContext, constructorDescriptor), superClassAsmType
|
||||
);
|
||||
|
||||
for (KtSuperTypeListEntry specifier : myClass.getSuperTypeListEntries()) {
|
||||
if (specifier instanceof KtDelegatedSuperTypeEntry) {
|
||||
genCallToDelegatorByExpressionSpecifier(iv, codegen, (KtDelegatedSuperTypeEntry) specifier, fieldsInfo);
|
||||
}
|
||||
}
|
||||
|
||||
int curParam = 0;
|
||||
List<ValueParameterDescriptor> parameters = constructorDescriptor.getValueParameters();
|
||||
for (KtParameter parameter : classBodyCodegen.getPrimaryConstructorParameters()) {
|
||||
if (parameter.hasValOrVar()) {
|
||||
VariableDescriptor descriptor = parameters.get(curParam);
|
||||
Type type = typeMapper.mapType(descriptor);
|
||||
iv.load(0, classAsmType);
|
||||
iv.load(codegen.myFrameMap.getIndex(descriptor), type);
|
||||
PropertyDescriptor propertyDescriptor = bindingContext.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter);
|
||||
assert propertyDescriptor != null : "Property descriptor is not found for primary constructor parameter: " + parameter;
|
||||
iv.putfield(classAsmType.getInternalName(), context.getFieldName(propertyDescriptor, false), type.getDescriptor());
|
||||
}
|
||||
curParam++;
|
||||
}
|
||||
|
||||
//object initialization was moved to initializeObjects()
|
||||
if (!isObject(descriptor)) {
|
||||
memberCodegen.generateInitializers(() -> codegen);
|
||||
}
|
||||
iv.visitInsn(RETURN);
|
||||
}
|
||||
|
||||
private void generateSecondaryConstructorImpl(
|
||||
@NotNull ClassConstructorDescriptor constructorDescriptor,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull Type superClassAsmType
|
||||
) {
|
||||
InstructionAdapter iv = codegen.v;
|
||||
|
||||
KtSecondaryConstructor constructor =
|
||||
(KtSecondaryConstructor) DescriptorToSourceUtils.descriptorToDeclaration(constructorDescriptor);
|
||||
|
||||
markLineNumberForConstructor(constructorDescriptor, constructor, codegen);
|
||||
|
||||
ResolvedCall<ConstructorDescriptor> constructorDelegationCall =
|
||||
getDelegationConstructorCall(bindingContext, constructorDescriptor);
|
||||
ConstructorDescriptor delegateConstructor = constructorDelegationCall == null ? null :
|
||||
constructorDelegationCall.getResultingDescriptor();
|
||||
|
||||
generateDelegatorToConstructorCall(iv, codegen, constructorDescriptor, constructorDelegationCall, superClassAsmType);
|
||||
if (!isSameClassConstructor(delegateConstructor)) {
|
||||
// Initialization happens only for constructors delegating to super
|
||||
generateClosureInitialization(iv);
|
||||
memberCodegen.generateInitializers(() -> codegen);
|
||||
}
|
||||
|
||||
assert constructor != null;
|
||||
if (constructor.hasBody()) {
|
||||
codegen.gen(constructor.getBodyExpression(), Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
iv.visitInsn(RETURN);
|
||||
}
|
||||
|
||||
private void genCallToDelegatorByExpressionSpecifier(
|
||||
InstructionAdapter iv,
|
||||
ExpressionCodegen codegen,
|
||||
KtDelegatedSuperTypeEntry specifier,
|
||||
DelegationFieldsInfo fieldsInfo
|
||||
) {
|
||||
KtExpression expression = specifier.getDelegateExpression();
|
||||
|
||||
DelegationFieldsInfo.Field fieldInfo = fieldsInfo.getInfo(specifier);
|
||||
if (fieldInfo == null) return;
|
||||
|
||||
if (fieldInfo.generateField) {
|
||||
iv.load(0, classAsmType);
|
||||
fieldInfo.getStackValue().store(codegen.gen(expression), iv);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSameClassConstructor(@Nullable ConstructorDescriptor delegatingConstructor) {
|
||||
return delegatingConstructor != null && delegatingConstructor.getContainingDeclaration() == descriptor;
|
||||
}
|
||||
|
||||
private void generateClosureInitialization(@NotNull InstructionAdapter iv) {
|
||||
MutableClosure closure = context.closure;
|
||||
if (closure != null) {
|
||||
List<FieldInfo> argsFromClosure = ClosureCodegen.calculateConstructorParameters(typeMapper, closure, classAsmType);
|
||||
int k = 1;
|
||||
for (FieldInfo info : argsFromClosure) {
|
||||
k = AsmUtil.genAssignInstanceFieldFromParam(info, k, iv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateDelegationConstructorCall(
|
||||
@NotNull InstructionAdapter iv,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull ClassConstructorDescriptor constructorDescriptor,
|
||||
@NotNull ResolvedCall<ConstructorDescriptor> delegationConstructorCall
|
||||
) {
|
||||
iv.load(0, OBJECT_TYPE);
|
||||
ConstructorDescriptor delegateConstructor = SamCodegenUtil.resolveSamAdapter(codegen.getConstructorDescriptor(delegationConstructorCall));
|
||||
|
||||
KotlinTypeMapper typeMapper = state.getTypeMapper();
|
||||
|
||||
CallableMethod delegateConstructorCallable = typeMapper.mapToCallableMethod(delegateConstructor, false);
|
||||
CallableMethod callable = typeMapper.mapToCallableMethod(constructorDescriptor, false);
|
||||
|
||||
List<JvmMethodParameterSignature> delegatingParameters = delegateConstructorCallable.getValueParameters();
|
||||
List<JvmMethodParameterSignature> parameters = callable.getValueParameters();
|
||||
|
||||
ArgumentGenerator argumentGenerator;
|
||||
if (isSameClassConstructor(delegateConstructor)) {
|
||||
// if it's the same class constructor we should just pass all synthetic parameters
|
||||
argumentGenerator =
|
||||
generateThisCallImplicitArguments(iv, codegen, delegateConstructor, delegateConstructorCallable, delegatingParameters,
|
||||
parameters);
|
||||
}
|
||||
else {
|
||||
argumentGenerator =
|
||||
generateSuperCallImplicitArguments(iv, codegen, constructorDescriptor, delegateConstructor, delegationConstructorCall,
|
||||
delegateConstructorCallable,
|
||||
delegatingParameters,
|
||||
parameters);
|
||||
}
|
||||
|
||||
codegen.invokeMethodWithArguments(
|
||||
delegateConstructorCallable, delegationConstructorCall, StackValue.none(), codegen.defaultCallGenerator, argumentGenerator);
|
||||
}
|
||||
|
||||
private void genSimpleSuperCall(InstructionAdapter iv, Type superClassAsmType) {
|
||||
iv.load(0, superClassAsmType);
|
||||
if (descriptor.getKind() == ClassKind.ENUM_CLASS || descriptor.getKind() == ClassKind.ENUM_ENTRY) {
|
||||
iv.load(1, JAVA_STRING_TYPE);
|
||||
iv.load(2, Type.INT_TYPE);
|
||||
iv.invokespecial(superClassAsmType.getInternalName(), "<init>", "(Ljava/lang/String;I)V", false);
|
||||
}
|
||||
else {
|
||||
iv.invokespecial(superClassAsmType.getInternalName(), "<init>", "()V", false);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static ArgumentGenerator generateThisCallImplicitArguments(
|
||||
@NotNull InstructionAdapter iv,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull ConstructorDescriptor delegatingConstructor,
|
||||
@NotNull CallableMethod delegatingCallable,
|
||||
@NotNull List<JvmMethodParameterSignature> delegatingParameters,
|
||||
@NotNull List<JvmMethodParameterSignature> parameters
|
||||
) {
|
||||
int offset = 1;
|
||||
int index = 0;
|
||||
for (; index < delegatingParameters.size(); index++) {
|
||||
JvmMethodParameterKind delegatingKind = delegatingParameters.get(index).getKind();
|
||||
if (delegatingKind == JvmMethodParameterKind.VALUE) {
|
||||
assert index == parameters.size() || parameters.get(index).getKind() == JvmMethodParameterKind.VALUE:
|
||||
"Delegating constructor has not enough implicit parameters";
|
||||
break;
|
||||
}
|
||||
assert index < parameters.size() && parameters.get(index).getKind() == delegatingKind :
|
||||
"Constructors of the same class should have the same set of implicit arguments";
|
||||
JvmMethodParameterSignature parameter = parameters.get(index);
|
||||
|
||||
iv.load(offset, parameter.getAsmType());
|
||||
offset += parameter.getAsmType().getSize();
|
||||
}
|
||||
|
||||
assert index == parameters.size() || parameters.get(index).getKind() == JvmMethodParameterKind.VALUE :
|
||||
"Delegating constructor has not enough parameters";
|
||||
|
||||
return new CallBasedArgumentGenerator(codegen, codegen.defaultCallGenerator, delegatingConstructor.getValueParameters(),
|
||||
delegatingCallable.getValueParameterTypes());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ArgumentGenerator generateSuperCallImplicitArguments(
|
||||
@NotNull InstructionAdapter iv,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull ConstructorDescriptor constructorDescriptor,
|
||||
@NotNull ConstructorDescriptor superConstructor,
|
||||
@NotNull ResolvedCall<ConstructorDescriptor> superConstructorCall,
|
||||
@NotNull CallableMethod superCallable,
|
||||
@NotNull List<JvmMethodParameterSignature> superParameters,
|
||||
@NotNull List<JvmMethodParameterSignature> parameters
|
||||
) {
|
||||
int offset = 1;
|
||||
int superIndex = 0;
|
||||
|
||||
// Here we match all the super constructor parameters except those with kind VALUE to the derived constructor parameters, push
|
||||
// them all onto the stack and update "offset" variable so that in the end it points to the slot of the first VALUE argument
|
||||
for (JvmMethodParameterSignature parameter : parameters) {
|
||||
if (superIndex >= superParameters.size()) break;
|
||||
|
||||
JvmMethodParameterKind superKind = superParameters.get(superIndex).getKind();
|
||||
JvmMethodParameterKind kind = parameter.getKind();
|
||||
Type type = parameter.getAsmType();
|
||||
|
||||
if (superKind == JvmMethodParameterKind.VALUE && kind == JvmMethodParameterKind.SUPER_CALL_PARAM) {
|
||||
// Stop when we reach the actual value parameters present in the code; they will be generated via ResolvedCall below
|
||||
break;
|
||||
}
|
||||
|
||||
if (superKind == JvmMethodParameterKind.OUTER) {
|
||||
assert kind == JvmMethodParameterKind.OUTER || kind == JvmMethodParameterKind.SUPER_CALL_PARAM :
|
||||
String.format("Non-outer parameter incorrectly mapped to outer for %s: %s vs %s",
|
||||
constructorDescriptor, parameters, superParameters);
|
||||
// Super constructor requires OUTER parameter, but our OUTER instance may be different from what is expected by the super
|
||||
// constructor. We need to traverse our outer classes from the bottom up, to find the needed class. See innerExtendsOuter.kt
|
||||
ClassDescriptor outerForSuper = (ClassDescriptor) superConstructor.getContainingDeclaration().getContainingDeclaration();
|
||||
StackValue outer = codegen.generateThisOrOuter(outerForSuper, true, true);
|
||||
outer.put(outer.type, codegen.v);
|
||||
superIndex++;
|
||||
}
|
||||
else if (kind == JvmMethodParameterKind.SUPER_CALL_PARAM || kind == JvmMethodParameterKind.ENUM_NAME_OR_ORDINAL) {
|
||||
iv.load(offset, type);
|
||||
superIndex++;
|
||||
}
|
||||
|
||||
offset += type.getSize();
|
||||
}
|
||||
|
||||
if (isAnonymousObject(descriptor)) {
|
||||
List<JvmMethodParameterSignature> superValues = superParameters.subList(superIndex, superParameters.size());
|
||||
return new ObjectSuperCallArgumentGenerator(
|
||||
superValues, iv,
|
||||
superConstructor.getValueParameters(), codegen.typeMapper,
|
||||
offset, superConstructorCall
|
||||
);
|
||||
}
|
||||
else {
|
||||
return new CallBasedArgumentGenerator(codegen, codegen.defaultCallGenerator, superConstructor.getValueParameters(),
|
||||
superCallable.getValueParameterTypes());
|
||||
}
|
||||
}
|
||||
|
||||
private static void markLineNumberForConstructor(
|
||||
@NotNull ClassConstructorDescriptor descriptor,
|
||||
@Nullable KtConstructor constructor,
|
||||
@NotNull ExpressionCodegen codegen
|
||||
) {
|
||||
if (constructor == null) {
|
||||
markLineNumberForDescriptor(descriptor.getContainingDeclaration(), codegen.v);
|
||||
}
|
||||
else if (constructor.hasBody() && !(constructor instanceof KtSecondaryConstructor && !((KtSecondaryConstructor) constructor).hasImplicitDelegationCall())) {
|
||||
KtBlockExpression bodyExpression = constructor.getBodyExpression();
|
||||
List<KtExpression> statements = bodyExpression != null ? bodyExpression.getStatements() : Collections.emptyList();
|
||||
if (!statements.isEmpty()) {
|
||||
codegen.markStartLineNumber(statements.iterator().next());
|
||||
}
|
||||
else {
|
||||
codegen.markStartLineNumber(bodyExpression != null ? bodyExpression : constructor);
|
||||
}
|
||||
}
|
||||
else {
|
||||
codegen.markStartLineNumber(constructor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
|
||||
import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry;
|
||||
import org.jetbrains.kotlin.psi.KtExpression;
|
||||
import org.jetbrains.kotlin.psi.KtSuperTypeListEntry;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jetbrains.kotlin.load.java.JvmAbi.DELEGATE_SUPER_FIELD_PREFIX;
|
||||
|
||||
public class DelegationFieldsInfo {
|
||||
private final Type classAsmType;
|
||||
private final ClassDescriptor classDescriptor;
|
||||
private final GenerationState state;
|
||||
private final KotlinTypeMapper typeMapper;
|
||||
private final BindingContext bindingContext;
|
||||
|
||||
public DelegationFieldsInfo(
|
||||
@NotNull Type type,
|
||||
@NotNull ClassDescriptor descriptor,
|
||||
@NotNull GenerationState state,
|
||||
@NotNull BindingContext context
|
||||
) {
|
||||
this.classAsmType = type;
|
||||
this.classDescriptor = descriptor;
|
||||
this.state = state;
|
||||
this.typeMapper = state.getTypeMapper();
|
||||
this.bindingContext = context;
|
||||
}
|
||||
|
||||
public class Field {
|
||||
public final Type type;
|
||||
public final String name;
|
||||
public final boolean generateField;
|
||||
|
||||
private Field(Type type, String name, boolean generateField) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.generateField = generateField;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public StackValue getStackValue() {
|
||||
return StackValue.field(type, classAsmType, name, false, StackValue.none());
|
||||
}
|
||||
}
|
||||
private final Map<KtDelegatedSuperTypeEntry, Field> fields = new HashMap<>();
|
||||
|
||||
@Nullable
|
||||
public DelegationFieldsInfo.Field getInfo(KtDelegatedSuperTypeEntry specifier) {
|
||||
DelegationFieldsInfo.Field field = fields.get(specifier);
|
||||
assert field != null || state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES : "No field for " + specifier.getText();
|
||||
return field;
|
||||
}
|
||||
|
||||
private void addField(KtDelegatedSuperTypeEntry specifier, PropertyDescriptor propertyDescriptor) {
|
||||
fields.put(specifier,
|
||||
new DelegationFieldsInfo.Field(typeMapper.mapType(propertyDescriptor), propertyDescriptor.getName().asString(), false));
|
||||
}
|
||||
|
||||
private void addField(KtDelegatedSuperTypeEntry specifier, Type type, String name) {
|
||||
fields.put(specifier, new DelegationFieldsInfo.Field(type, name, true));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DelegationFieldsInfo getDelegationFieldsInfo(@NotNull List<KtSuperTypeListEntry> delegationSpecifiers) {
|
||||
DelegationFieldsInfo result = new DelegationFieldsInfo(classAsmType, classDescriptor, state, bindingContext);
|
||||
int n = 0;
|
||||
for (KtSuperTypeListEntry specifier : delegationSpecifiers) {
|
||||
if (specifier instanceof KtDelegatedSuperTypeEntry) {
|
||||
KtExpression expression = ((KtDelegatedSuperTypeEntry) specifier).getDelegateExpression();
|
||||
if (expression == null) continue;
|
||||
|
||||
PropertyDescriptor propertyDescriptor = CodegenUtil.getDelegatePropertyIfAny(expression, classDescriptor, bindingContext);
|
||||
|
||||
|
||||
if (CodegenUtil.isFinalPropertyWithBackingField(propertyDescriptor, bindingContext)) {
|
||||
result.addField((KtDelegatedSuperTypeEntry) specifier, propertyDescriptor);
|
||||
}
|
||||
else {
|
||||
KotlinType expressionType = bindingContext.getType(expression);
|
||||
ClassDescriptor superClass = JvmCodegenUtil.getSuperClass(specifier, state, bindingContext);
|
||||
Type asmType =
|
||||
expressionType != null ? typeMapper.mapType(expressionType) :
|
||||
superClass != null ? typeMapper.mapType(superClass) : null;
|
||||
|
||||
if (asmType == null) continue;
|
||||
|
||||
result.addField((KtDelegatedSuperTypeEntry) specifier, asmType, DELEGATE_SUPER_FIELD_PREFIX + n);
|
||||
}
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||
import org.jetbrains.kotlin.config.LanguageFeature;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
|
||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode;
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader;
|
||||
@@ -46,8 +47,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature;
|
||||
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.resolve.scopes.DescriptorKindFilter;
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
||||
@@ -71,7 +70,6 @@ import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
|
||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.enumEntryNeedSubclass;
|
||||
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtils2Kt.initDefaultSourceMappingIfNeeded;
|
||||
import static org.jetbrains.kotlin.load.java.JvmAbi.*;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContextUtils.getNotNull;
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
|
||||
@@ -101,6 +99,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
|
||||
private final DescriptorSerializer serializer;
|
||||
|
||||
private final ConstructorCodegen constructorCodegen;
|
||||
|
||||
public ImplementationBodyCodegen(
|
||||
@NotNull KtPureClassOrObject aClass,
|
||||
@NotNull ClassContext context,
|
||||
@@ -112,7 +112,10 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
super(aClass, context, v, state, parentCodegen);
|
||||
this.classAsmType = getObjectType(typeMapper.classInternalName(descriptor));
|
||||
this.isLocal = isLocal;
|
||||
this.delegationFieldsInfo = getDelegationFieldsInfo(myClass.getSuperTypeListEntries());
|
||||
|
||||
this.delegationFieldsInfo =
|
||||
new DelegationFieldsInfo(classAsmType, descriptor, state, bindingContext)
|
||||
.getDelegationFieldsInfo(myClass.getSuperTypeListEntries());
|
||||
|
||||
JvmSerializerExtension extension = new JvmSerializerExtension(v.getSerializationBindings(), state);
|
||||
this.serializer = DescriptorSerializer.create(
|
||||
@@ -121,6 +124,11 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
? ((ImplementationBodyCodegen) parentCodegen).serializer
|
||||
: DescriptorSerializer.createTopLevel(extension)
|
||||
);
|
||||
|
||||
this.constructorCodegen = new ConstructorCodegen(
|
||||
descriptor, context, functionCodegen, this,
|
||||
this, state, kind, v, classAsmType, myClass, bindingContext
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -449,9 +457,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
protected void generateConstructors() {
|
||||
try {
|
||||
lookupConstructorExpressionsInClosureIfPresent();
|
||||
generatePrimaryConstructor(delegationFieldsInfo);
|
||||
constructorCodegen.generatePrimaryConstructor(delegationFieldsInfo, superClassAsmType);
|
||||
for (ClassConstructorDescriptor secondaryConstructor : DescriptorUtilsKt.getSecondaryConstructors(descriptor)) {
|
||||
generateSecondaryConstructor(secondaryConstructor);
|
||||
constructorCodegen.generateSecondaryConstructor(secondaryConstructor, superClassAsmType);
|
||||
}
|
||||
}
|
||||
catch (CompilationException | ProcessCanceledException e) {
|
||||
@@ -928,276 +936,10 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
field.store(property, codegen.v);
|
||||
}
|
||||
|
||||
private void generatePrimaryConstructor(DelegationFieldsInfo delegationFieldsInfo) {
|
||||
if (isJvmInterface(descriptor)) return;
|
||||
|
||||
ClassConstructorDescriptor constructorDescriptor = descriptor.getUnsubstitutedPrimaryConstructor();
|
||||
if (constructorDescriptor == null) return;
|
||||
|
||||
ConstructorContext constructorContext = context.intoConstructor(constructorDescriptor);
|
||||
|
||||
KtPrimaryConstructor primaryConstructor = myClass.getPrimaryConstructor();
|
||||
JvmDeclarationOrigin origin = JvmDeclarationOriginKt
|
||||
.OtherOrigin(primaryConstructor != null ? primaryConstructor : myClass.getPsiOrParent(), constructorDescriptor);
|
||||
functionCodegen.generateMethod(origin, constructorDescriptor, constructorContext,
|
||||
new FunctionGenerationStrategy.CodegenBased(state) {
|
||||
@Override
|
||||
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
|
||||
generatePrimaryConstructorImpl(constructorDescriptor, codegen, delegationFieldsInfo, primaryConstructor);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
functionCodegen.generateDefaultIfNeeded(constructorContext, constructorDescriptor, OwnerKind.IMPLEMENTATION,
|
||||
DefaultParameterValueLoader.DEFAULT, null);
|
||||
|
||||
new DefaultParameterValueSubstitutor(state).generatePrimaryConstructorOverloadsIfNeeded(constructorDescriptor, v, this, kind, myClass);
|
||||
}
|
||||
|
||||
private void generateSecondaryConstructor(@NotNull ClassConstructorDescriptor constructorDescriptor) {
|
||||
if (!canHaveDeclaredConstructors(descriptor)) return;
|
||||
|
||||
ConstructorContext constructorContext = context.intoConstructor(constructorDescriptor);
|
||||
|
||||
KtSecondaryConstructor constructor = (KtSecondaryConstructor) descriptorToDeclaration(constructorDescriptor);
|
||||
|
||||
functionCodegen.generateMethod(
|
||||
JvmDeclarationOriginKt.OtherOrigin(constructor, constructorDescriptor),
|
||||
constructorDescriptor, constructorContext,
|
||||
new FunctionGenerationStrategy.CodegenBased(state) {
|
||||
@Override
|
||||
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
|
||||
generateSecondaryConstructorImpl(constructorDescriptor, codegen);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
functionCodegen.generateDefaultIfNeeded(constructorContext, constructorDescriptor, OwnerKind.IMPLEMENTATION,
|
||||
DefaultParameterValueLoader.DEFAULT, null);
|
||||
|
||||
new DefaultParameterValueSubstitutor(state).generateOverloadsIfNeeded(
|
||||
constructor, constructorDescriptor, constructorDescriptor, kind, v, this
|
||||
);
|
||||
}
|
||||
|
||||
private void generatePrimaryConstructorImpl(
|
||||
@NotNull ClassConstructorDescriptor constructorDescriptor,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull DelegationFieldsInfo fieldsInfo,
|
||||
@Nullable KtPrimaryConstructor primaryConstructor
|
||||
) {
|
||||
InstructionAdapter iv = codegen.v;
|
||||
|
||||
markLineNumberForConstructor(constructorDescriptor, primaryConstructor, codegen);
|
||||
|
||||
generateClosureInitialization(iv);
|
||||
|
||||
generateDelegatorToConstructorCall(iv, codegen, constructorDescriptor,
|
||||
getDelegationConstructorCall(bindingContext, constructorDescriptor));
|
||||
|
||||
for (KtSuperTypeListEntry specifier : myClass.getSuperTypeListEntries()) {
|
||||
if (specifier instanceof KtDelegatedSuperTypeEntry) {
|
||||
genCallToDelegatorByExpressionSpecifier(iv, codegen, (KtDelegatedSuperTypeEntry) specifier, fieldsInfo);
|
||||
}
|
||||
}
|
||||
|
||||
int curParam = 0;
|
||||
List<ValueParameterDescriptor> parameters = constructorDescriptor.getValueParameters();
|
||||
for (KtParameter parameter : getPrimaryConstructorParameters()) {
|
||||
if (parameter.hasValOrVar()) {
|
||||
VariableDescriptor descriptor = parameters.get(curParam);
|
||||
Type type = typeMapper.mapType(descriptor);
|
||||
iv.load(0, classAsmType);
|
||||
iv.load(codegen.myFrameMap.getIndex(descriptor), type);
|
||||
PropertyDescriptor propertyDescriptor = bindingContext.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter);
|
||||
assert propertyDescriptor != null : "Property descriptor is not found for primary constructor parameter: " + parameter;
|
||||
iv.putfield(classAsmType.getInternalName(), context.getFieldName(propertyDescriptor, false), type.getDescriptor());
|
||||
}
|
||||
curParam++;
|
||||
}
|
||||
|
||||
//object initialization was moved to initializeObjects()
|
||||
if (!isObject(descriptor)) {
|
||||
generateInitializers(codegen);
|
||||
}
|
||||
iv.visitInsn(RETURN);
|
||||
}
|
||||
|
||||
private void generateSecondaryConstructorImpl(
|
||||
@NotNull ClassConstructorDescriptor constructorDescriptor,
|
||||
@NotNull ExpressionCodegen codegen
|
||||
) {
|
||||
InstructionAdapter iv = codegen.v;
|
||||
|
||||
KtSecondaryConstructor constructor =
|
||||
(KtSecondaryConstructor) DescriptorToSourceUtils.descriptorToDeclaration(constructorDescriptor);
|
||||
|
||||
markLineNumberForConstructor(constructorDescriptor, constructor, codegen);
|
||||
|
||||
ResolvedCall<ConstructorDescriptor> constructorDelegationCall =
|
||||
getDelegationConstructorCall(bindingContext, constructorDescriptor);
|
||||
ConstructorDescriptor delegateConstructor = constructorDelegationCall == null ? null :
|
||||
constructorDelegationCall.getResultingDescriptor();
|
||||
|
||||
generateDelegatorToConstructorCall(iv, codegen, constructorDescriptor, constructorDelegationCall);
|
||||
if (!isSameClassConstructor(delegateConstructor)) {
|
||||
// Initialization happens only for constructors delegating to super
|
||||
generateClosureInitialization(iv);
|
||||
generateInitializers(codegen);
|
||||
}
|
||||
|
||||
assert constructor != null;
|
||||
if (constructor.hasBody()) {
|
||||
codegen.gen(constructor.getBodyExpression(), Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
iv.visitInsn(RETURN);
|
||||
}
|
||||
|
||||
private static void markLineNumberForConstructor(
|
||||
@NotNull ClassConstructorDescriptor descriptor,
|
||||
@Nullable KtConstructor constructor,
|
||||
@NotNull ExpressionCodegen codegen
|
||||
) {
|
||||
if (constructor == null) {
|
||||
markLineNumberForDescriptor(descriptor.getContainingDeclaration(), codegen.v);
|
||||
}
|
||||
else if (constructor.hasBody() && !(constructor instanceof KtSecondaryConstructor && !((KtSecondaryConstructor) constructor).hasImplicitDelegationCall())) {
|
||||
KtBlockExpression bodyExpression = constructor.getBodyExpression();
|
||||
List<KtExpression> statements = bodyExpression != null ? bodyExpression.getStatements() : Collections.emptyList();
|
||||
if (!statements.isEmpty()) {
|
||||
codegen.markStartLineNumber(statements.iterator().next());
|
||||
}
|
||||
else {
|
||||
codegen.markStartLineNumber(bodyExpression != null ? bodyExpression : constructor);
|
||||
}
|
||||
}
|
||||
else {
|
||||
codegen.markStartLineNumber(constructor);
|
||||
}
|
||||
}
|
||||
|
||||
public void generateInitializers(@NotNull ExpressionCodegen codegen) {
|
||||
generateInitializers(() -> codegen);
|
||||
}
|
||||
|
||||
private void generateClosureInitialization(@NotNull InstructionAdapter iv) {
|
||||
MutableClosure closure = context.closure;
|
||||
if (closure != null) {
|
||||
List<FieldInfo> argsFromClosure = ClosureCodegen.calculateConstructorParameters(typeMapper, closure, classAsmType);
|
||||
int k = 1;
|
||||
for (FieldInfo info : argsFromClosure) {
|
||||
k = AsmUtil.genAssignInstanceFieldFromParam(info, k, iv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void genSimpleSuperCall(InstructionAdapter iv) {
|
||||
iv.load(0, superClassAsmType);
|
||||
if (descriptor.getKind() == ClassKind.ENUM_CLASS || descriptor.getKind() == ClassKind.ENUM_ENTRY) {
|
||||
iv.load(1, JAVA_STRING_TYPE);
|
||||
iv.load(2, Type.INT_TYPE);
|
||||
iv.invokespecial(superClassAsmType.getInternalName(), "<init>", "(Ljava/lang/String;I)V", false);
|
||||
}
|
||||
else {
|
||||
iv.invokespecial(superClassAsmType.getInternalName(), "<init>", "()V", false);
|
||||
}
|
||||
}
|
||||
|
||||
private class DelegationFieldsInfo {
|
||||
private class Field {
|
||||
public final Type type;
|
||||
public final String name;
|
||||
public final boolean generateField;
|
||||
|
||||
private Field(Type type, String name, boolean generateField) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.generateField = generateField;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public StackValue getStackValue() {
|
||||
return StackValue.field(type, classAsmType, name, false, StackValue.none());
|
||||
}
|
||||
}
|
||||
private final Map<KtDelegatedSuperTypeEntry, Field> fields = new HashMap<>();
|
||||
|
||||
@Nullable
|
||||
public Field getInfo(KtDelegatedSuperTypeEntry specifier) {
|
||||
Field field = fields.get(specifier);
|
||||
assert field != null || state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES : "No field for " + specifier.getText();
|
||||
return field;
|
||||
}
|
||||
|
||||
private void addField(KtDelegatedSuperTypeEntry specifier, PropertyDescriptor propertyDescriptor) {
|
||||
fields.put(specifier,
|
||||
new Field(typeMapper.mapType(propertyDescriptor), propertyDescriptor.getName().asString(), false));
|
||||
}
|
||||
|
||||
private void addField(KtDelegatedSuperTypeEntry specifier, Type type, String name) {
|
||||
fields.put(specifier, new Field(type, name, true));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private DelegationFieldsInfo getDelegationFieldsInfo(@NotNull List<KtSuperTypeListEntry> delegationSpecifiers) {
|
||||
DelegationFieldsInfo result = new DelegationFieldsInfo();
|
||||
int n = 0;
|
||||
for (KtSuperTypeListEntry specifier : delegationSpecifiers) {
|
||||
if (specifier instanceof KtDelegatedSuperTypeEntry) {
|
||||
KtExpression expression = ((KtDelegatedSuperTypeEntry) specifier).getDelegateExpression();
|
||||
if (expression == null) continue;
|
||||
|
||||
PropertyDescriptor propertyDescriptor = CodegenUtil.getDelegatePropertyIfAny(expression, descriptor, bindingContext);
|
||||
|
||||
|
||||
if (CodegenUtil.isFinalPropertyWithBackingField(propertyDescriptor, bindingContext)) {
|
||||
result.addField((KtDelegatedSuperTypeEntry) specifier, propertyDescriptor);
|
||||
}
|
||||
else {
|
||||
KotlinType expressionType = bindingContext.getType(expression);
|
||||
ClassDescriptor superClass = getSuperClass(specifier);
|
||||
Type asmType =
|
||||
expressionType != null ? typeMapper.mapType(expressionType) :
|
||||
superClass != null ? typeMapper.mapType(superClass) : null;
|
||||
|
||||
if (asmType == null) continue;
|
||||
|
||||
result.addField((KtDelegatedSuperTypeEntry) specifier, asmType, DELEGATE_SUPER_FIELD_PREFIX + n);
|
||||
}
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ClassDescriptor getSuperClass(@NotNull KtSuperTypeListEntry specifier) {
|
||||
ClassDescriptor superClass = CodegenUtil.getSuperClassBySuperTypeListEntry(specifier, bindingContext);
|
||||
|
||||
assert superClass != null || state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES
|
||||
: "ClassDescriptor should not be null:" + specifier.getText();
|
||||
return superClass;
|
||||
}
|
||||
|
||||
private void genCallToDelegatorByExpressionSpecifier(
|
||||
InstructionAdapter iv,
|
||||
ExpressionCodegen codegen,
|
||||
KtDelegatedSuperTypeEntry specifier,
|
||||
DelegationFieldsInfo fieldsInfo
|
||||
) {
|
||||
KtExpression expression = specifier.getDelegateExpression();
|
||||
|
||||
DelegationFieldsInfo.Field fieldInfo = fieldsInfo.getInfo(specifier);
|
||||
if (fieldInfo == null) return;
|
||||
|
||||
if (fieldInfo.generateField) {
|
||||
iv.load(0, classAsmType);
|
||||
fieldInfo.getStackValue().store(codegen.gen(expression), iv);
|
||||
}
|
||||
}
|
||||
|
||||
private void lookupConstructorExpressionsInClosureIfPresent() {
|
||||
if (!state.getClassBuilderMode().generateBodies || descriptor.getConstructors().isEmpty()) return;
|
||||
|
||||
@@ -1374,160 +1116,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
);
|
||||
}
|
||||
|
||||
private void generateDelegatorToConstructorCall(
|
||||
@NotNull InstructionAdapter iv,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull ClassConstructorDescriptor constructorDescriptor,
|
||||
@Nullable ResolvedCall<ConstructorDescriptor> delegationConstructorCall
|
||||
) {
|
||||
MethodContext codegenContext = codegen.context;
|
||||
assert codegenContext instanceof ConstructorContext :
|
||||
"Constructor context expected: " + codegenContext;
|
||||
assert !((ConstructorContext) codegenContext).isThisInitialized() :
|
||||
"Delegating constructor call is already generated for " + ((ConstructorContext) codegenContext).getConstructorDescriptor();
|
||||
|
||||
if (delegationConstructorCall == null) {
|
||||
genSimpleSuperCall(iv);
|
||||
}
|
||||
else {
|
||||
generateDelegationConstructorCall(iv, codegen, constructorDescriptor, delegationConstructorCall);
|
||||
}
|
||||
|
||||
((ConstructorContext) codegenContext).setThisInitialized(true);
|
||||
}
|
||||
|
||||
private void generateDelegationConstructorCall(
|
||||
@NotNull InstructionAdapter iv,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull ClassConstructorDescriptor constructorDescriptor,
|
||||
@NotNull ResolvedCall<ConstructorDescriptor> delegationConstructorCall
|
||||
) {
|
||||
iv.load(0, OBJECT_TYPE);
|
||||
ConstructorDescriptor delegateConstructor = SamCodegenUtil.resolveSamAdapter(codegen.getConstructorDescriptor(delegationConstructorCall));
|
||||
|
||||
CallableMethod delegateConstructorCallable = typeMapper.mapToCallableMethod(delegateConstructor, false);
|
||||
CallableMethod callable = typeMapper.mapToCallableMethod(constructorDescriptor, false);
|
||||
|
||||
List<JvmMethodParameterSignature> delegatingParameters = delegateConstructorCallable.getValueParameters();
|
||||
List<JvmMethodParameterSignature> parameters = callable.getValueParameters();
|
||||
|
||||
ArgumentGenerator argumentGenerator;
|
||||
if (isSameClassConstructor(delegateConstructor)) {
|
||||
// if it's the same class constructor we should just pass all synthetic parameters
|
||||
argumentGenerator =
|
||||
generateThisCallImplicitArguments(iv, codegen, delegateConstructor, delegateConstructorCallable, delegatingParameters,
|
||||
parameters);
|
||||
}
|
||||
else {
|
||||
argumentGenerator =
|
||||
generateSuperCallImplicitArguments(iv, codegen, constructorDescriptor, delegateConstructor, delegationConstructorCall,
|
||||
delegateConstructorCallable,
|
||||
delegatingParameters,
|
||||
parameters);
|
||||
}
|
||||
|
||||
codegen.invokeMethodWithArguments(
|
||||
delegateConstructorCallable, delegationConstructorCall, StackValue.none(), codegen.defaultCallGenerator, argumentGenerator);
|
||||
}
|
||||
|
||||
private boolean isSameClassConstructor(@Nullable ConstructorDescriptor delegatingConstructor) {
|
||||
return delegatingConstructor != null && delegatingConstructor.getContainingDeclaration() == descriptor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ArgumentGenerator generateSuperCallImplicitArguments(
|
||||
@NotNull InstructionAdapter iv,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull ConstructorDescriptor constructorDescriptor,
|
||||
@NotNull ConstructorDescriptor superConstructor,
|
||||
@NotNull ResolvedCall<ConstructorDescriptor> superConstructorCall,
|
||||
@NotNull CallableMethod superCallable,
|
||||
@NotNull List<JvmMethodParameterSignature> superParameters,
|
||||
@NotNull List<JvmMethodParameterSignature> parameters
|
||||
) {
|
||||
int offset = 1;
|
||||
int superIndex = 0;
|
||||
|
||||
// Here we match all the super constructor parameters except those with kind VALUE to the derived constructor parameters, push
|
||||
// them all onto the stack and update "offset" variable so that in the end it points to the slot of the first VALUE argument
|
||||
for (JvmMethodParameterSignature parameter : parameters) {
|
||||
if (superIndex >= superParameters.size()) break;
|
||||
|
||||
JvmMethodParameterKind superKind = superParameters.get(superIndex).getKind();
|
||||
JvmMethodParameterKind kind = parameter.getKind();
|
||||
Type type = parameter.getAsmType();
|
||||
|
||||
if (superKind == JvmMethodParameterKind.VALUE && kind == JvmMethodParameterKind.SUPER_CALL_PARAM) {
|
||||
// Stop when we reach the actual value parameters present in the code; they will be generated via ResolvedCall below
|
||||
break;
|
||||
}
|
||||
|
||||
if (superKind == JvmMethodParameterKind.OUTER) {
|
||||
assert kind == JvmMethodParameterKind.OUTER || kind == JvmMethodParameterKind.SUPER_CALL_PARAM :
|
||||
String.format("Non-outer parameter incorrectly mapped to outer for %s: %s vs %s",
|
||||
constructorDescriptor, parameters, superParameters);
|
||||
// Super constructor requires OUTER parameter, but our OUTER instance may be different from what is expected by the super
|
||||
// constructor. We need to traverse our outer classes from the bottom up, to find the needed class. See innerExtendsOuter.kt
|
||||
ClassDescriptor outerForSuper = (ClassDescriptor) superConstructor.getContainingDeclaration().getContainingDeclaration();
|
||||
StackValue outer = codegen.generateThisOrOuter(outerForSuper, true, true);
|
||||
outer.put(outer.type, codegen.v);
|
||||
superIndex++;
|
||||
}
|
||||
else if (kind == JvmMethodParameterKind.SUPER_CALL_PARAM || kind == JvmMethodParameterKind.ENUM_NAME_OR_ORDINAL) {
|
||||
iv.load(offset, type);
|
||||
superIndex++;
|
||||
}
|
||||
|
||||
offset += type.getSize();
|
||||
}
|
||||
|
||||
if (isAnonymousObject(descriptor)) {
|
||||
List<JvmMethodParameterSignature> superValues = superParameters.subList(superIndex, superParameters.size());
|
||||
return new ObjectSuperCallArgumentGenerator(
|
||||
superValues, iv,
|
||||
superConstructor.getValueParameters(), codegen.typeMapper,
|
||||
offset, superConstructorCall
|
||||
);
|
||||
}
|
||||
else {
|
||||
return new CallBasedArgumentGenerator(codegen, codegen.defaultCallGenerator, superConstructor.getValueParameters(),
|
||||
superCallable.getValueParameterTypes());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static ArgumentGenerator generateThisCallImplicitArguments(
|
||||
@NotNull InstructionAdapter iv,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull ConstructorDescriptor delegatingConstructor,
|
||||
@NotNull CallableMethod delegatingCallable,
|
||||
@NotNull List<JvmMethodParameterSignature> delegatingParameters,
|
||||
@NotNull List<JvmMethodParameterSignature> parameters
|
||||
) {
|
||||
int offset = 1;
|
||||
int index = 0;
|
||||
for (; index < delegatingParameters.size(); index++) {
|
||||
JvmMethodParameterKind delegatingKind = delegatingParameters.get(index).getKind();
|
||||
if (delegatingKind == JvmMethodParameterKind.VALUE) {
|
||||
assert index == parameters.size() || parameters.get(index).getKind() == JvmMethodParameterKind.VALUE:
|
||||
"Delegating constructor has not enough implicit parameters";
|
||||
break;
|
||||
}
|
||||
assert index < parameters.size() && parameters.get(index).getKind() == delegatingKind :
|
||||
"Constructors of the same class should have the same set of implicit arguments";
|
||||
JvmMethodParameterSignature parameter = parameters.get(index);
|
||||
|
||||
iv.load(offset, parameter.getAsmType());
|
||||
offset += parameter.getAsmType().getSize();
|
||||
}
|
||||
|
||||
assert index == parameters.size() || parameters.get(index).getKind() == JvmMethodParameterKind.VALUE :
|
||||
"Delegating constructor has not enough parameters";
|
||||
|
||||
return new CallBasedArgumentGenerator(codegen, codegen.defaultCallGenerator, delegatingConstructor.getValueParameters(),
|
||||
delegatingCallable.getValueParameterTypes());
|
||||
}
|
||||
|
||||
private void generateEnumEntries() {
|
||||
if (descriptor.getKind() != ClassKind.ENUM_CLASS) return;
|
||||
|
||||
@@ -1615,7 +1203,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
generateDelegateField(field);
|
||||
KtExpression delegateExpression = ((KtDelegatedSuperTypeEntry) specifier).getDelegateExpression();
|
||||
KotlinType delegateExpressionType = delegateExpression != null ? bindingContext.getType(delegateExpression) : null;
|
||||
ClassDescriptor superClass = getSuperClass(specifier);
|
||||
ClassDescriptor superClass = JvmCodegenUtil.getSuperClass(specifier, state, bindingContext);
|
||||
if (superClass == null) continue;
|
||||
|
||||
generateDelegates(superClass, delegateExpressionType, field);
|
||||
|
||||
@@ -11,12 +11,14 @@ import kotlin.collections.CollectionsKt;
|
||||
import kotlin.text.StringsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor;
|
||||
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
|
||||
import org.jetbrains.kotlin.codegen.context.CodegenContext;
|
||||
import org.jetbrains.kotlin.codegen.context.FacadePartWithSourceFile;
|
||||
import org.jetbrains.kotlin.codegen.context.MethodContext;
|
||||
import org.jetbrains.kotlin.codegen.context.RootContext;
|
||||
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;
|
||||
@@ -29,6 +31,7 @@ import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.Call;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.psi.KtFunction;
|
||||
import org.jetbrains.kotlin.psi.KtSuperTypeListEntry;
|
||||
import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
||||
@@ -363,4 +366,17 @@ public class JvmCodegenUtil {
|
||||
JvmCodegenUtil::isDeclarationOfBigArityFunctionInvoke
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ClassDescriptor getSuperClass(
|
||||
@NotNull KtSuperTypeListEntry specifier,
|
||||
@NotNull GenerationState state,
|
||||
@NotNull BindingContext bindingContext
|
||||
) {
|
||||
ClassDescriptor superClass = CodegenUtil.getSuperClassBySuperTypeListEntry(specifier, bindingContext);
|
||||
|
||||
assert superClass != null || state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES
|
||||
: "ClassDescriptor should not be null:" + specifier.getText();
|
||||
return superClass;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user