/*
* 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);
if (
OwnerKind.
ERASED_INLINE_CLASS ==
kind) {
Type t =
typeMapper.
mapType(
constructorDescriptor.
getContainingDeclaration());
iv.
load(0,
t);
iv.
areturn(
t);
return;
}
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);
}
if (
OwnerKind.
ERASED_INLINE_CLASS ==
kind) {
iv.
areturn(
typeMapper.
mapType(
constructorDescriptor.
getContainingDeclaration()));
}
else {
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
) {
if (
OwnerKind.
ERASED_INLINE_CLASS !=
kind) {
iv.
load(0,
OBJECT_TYPE);
}
ConstructorDescriptor delegateConstructor =
SamCodegenUtil.
resolveSamAdapter(
codegen.
getConstructorDescriptor(
delegationConstructorCall));
KotlinTypeMapper typeMapper =
state.
getTypeMapper();
CallableMethod delegateConstructorCallable =
typeMapper.
mapToCallableMethod(
delegateConstructor, false,
kind);
CallableMethod callable =
typeMapper.
mapToCallableMethod(
constructorDescriptor, false,
kind);
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);
}
}
}