Fix super constructor calls of anonymous objects and local classes
If a class inherits from another class which captures something (outer class instance, receiver parameter, local variables, etc.), the constructor of the former class should contain all the parameters of the super constructor as its own parameters, so that it could make a proper super call. All such parameters are now replicated in the derived constructor with kind = SUPER_CALL_PARAM, except an instance of the outer class (kind = OUTER), which can be taken from the derived's own OUTER when it has one, to prevent multiple passing of the same argument. Previously it worked only when inheriting from inner classes via a special hack (ConstructorFrameMap). Also reuse recently introduced ArgumentGenerator to automatically take care of default and vararg arguments of super constructor #KT-3581 Fixed #KT-5342 Fixed #KT-5343 Fixed
This commit is contained in:
@@ -1328,7 +1328,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
|
||||
ConstructorDescriptor constructorDescriptor = bindingContext.get(CONSTRUCTOR, expression.getObjectDeclaration());
|
||||
assert constructorDescriptor != null : "Unresolved constructor: " + expression.getText();
|
||||
CallableMethod constructor = typeMapper.mapToCallableMethod(constructorDescriptor);
|
||||
JvmMethodSignature constructor = typeMapper.mapSignature(constructorDescriptor);
|
||||
|
||||
Type type = typeMapper.mapType(constructorDescriptor.getContainingDeclaration());
|
||||
|
||||
@@ -1339,12 +1339,27 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
|
||||
ResolvedCall<ConstructorDescriptor> superCall = closure.getSuperCall();
|
||||
if (superCall != null) {
|
||||
// For an anonymous object, we should also generate all non-default arguments that it captures for its super call
|
||||
ConstructorDescriptor superConstructor = superCall.getResultingDescriptor();
|
||||
List<ResolvedValueArgument> valueArguments = superCall.getValueArgumentsByIndex();
|
||||
assert valueArguments != null : "Failed to arrange value arguments by index: " + superConstructor;
|
||||
ArgumentGenerator argumentGenerator =
|
||||
new CallBasedArgumentGenerator(this, defaultCallGenerator, superConstructor.getValueParameters(),
|
||||
typeMapper.mapToCallableMethod(superConstructor).getValueParameterTypes());
|
||||
List<ValueParameterDescriptor> superValueParameters = superConstructor.getValueParameters();
|
||||
int params = superValueParameters.size();
|
||||
List<Type> superMappedTypes = typeMapper.mapToCallableMethod(superConstructor).getValueParameterTypes();
|
||||
assert superMappedTypes.size() >= params : String.format("Incorrect number of mapped parameters vs arguments: %d < %d for %s",
|
||||
superMappedTypes.size(), params, constructorDescriptor);
|
||||
|
||||
List<ResolvedValueArgument> valueArguments = new ArrayList<ResolvedValueArgument>(params);
|
||||
List<ValueParameterDescriptor> valueParameters = new ArrayList<ValueParameterDescriptor>(params);
|
||||
List<Type> mappedTypes = new ArrayList<Type>(params);
|
||||
for (ValueParameterDescriptor parameter : superValueParameters) {
|
||||
ResolvedValueArgument argument = superCall.getValueArguments().get(parameter);
|
||||
if (!(argument instanceof DefaultValueArgument)) {
|
||||
valueArguments.add(argument);
|
||||
valueParameters.add(parameter);
|
||||
mappedTypes.add(superMappedTypes.get(parameter.getIndex()));
|
||||
}
|
||||
}
|
||||
ArgumentGenerator argumentGenerator = new CallBasedArgumentGenerator(this, defaultCallGenerator, valueParameters, mappedTypes);
|
||||
|
||||
argumentGenerator.generate(valueArguments);
|
||||
}
|
||||
|
||||
@@ -1352,7 +1367,11 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
return StackValue.onStack(type);
|
||||
}
|
||||
|
||||
public void pushClosureOnStack(@Nullable CalculatedClosure closure, boolean ignoreThisAndReceiver, @NotNull CallGenerator callGenerator) {
|
||||
public void pushClosureOnStack(
|
||||
@Nullable CalculatedClosure closure,
|
||||
boolean ignoreThisAndReceiver,
|
||||
@NotNull CallGenerator callGenerator
|
||||
) {
|
||||
if (closure == null) return;
|
||||
|
||||
int paramIndex = 0;
|
||||
@@ -1381,6 +1400,12 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
StackValue capturedVar = entry.getValue().getOuterValue(this);
|
||||
callGenerator.putCapturedValueOnStack(capturedVar, sharedVarType, paramIndex++);
|
||||
}
|
||||
|
||||
ResolvedCall<ConstructorDescriptor> superCall = closure.getSuperCall();
|
||||
if (superCall != null) {
|
||||
MutableClosure superClosure = bindingContext.get(CLOSURE, superCall.getResultingDescriptor().getContainingDeclaration());
|
||||
pushClosureOnStack(superClosure, ignoreThisAndReceiver, callGenerator);
|
||||
}
|
||||
}
|
||||
|
||||
private StackValue generateBlock(List<JetElement> statements, boolean lastStatementIsExpression) {
|
||||
@@ -2133,7 +2158,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
||||
invokeMethodWithArguments(callableMethod, resolvedCall, receiver, callGenerator, argumentGenerator);
|
||||
}
|
||||
|
||||
private void invokeMethodWithArguments(
|
||||
public void invokeMethodWithArguments(
|
||||
@NotNull CallableMethod callableMethod,
|
||||
@NotNull ResolvedCall<?> resolvedCall,
|
||||
@NotNull StackValue receiver,
|
||||
|
||||
@@ -393,7 +393,7 @@ public class FunctionCodegen extends ParentCodegenAware {
|
||||
private static boolean needIndexForVar(JvmMethodParameterKind kind) {
|
||||
return kind == JvmMethodParameterKind.CAPTURED_LOCAL_VARIABLE ||
|
||||
kind == JvmMethodParameterKind.ENUM_NAME_OR_ORDINAL ||
|
||||
kind == JvmMethodParameterKind.SUPER_OF_ANONYMOUS_CALL_PARAM;
|
||||
kind == JvmMethodParameterKind.SUPER_CALL_PARAM;
|
||||
}
|
||||
|
||||
public static void endVisit(MethodVisitor mv, @Nullable String description, @Nullable PsiElement method) {
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.backend.common.CodegenUtil;
|
||||
import org.jetbrains.jet.backend.common.DataClassMethodGenerator;
|
||||
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
|
||||
import org.jetbrains.jet.codegen.binding.CodegenBinding;
|
||||
import org.jetbrains.jet.codegen.binding.MutableClosure;
|
||||
import org.jetbrains.jet.codegen.bridges.BridgesPackage;
|
||||
@@ -47,7 +46,10 @@ import org.jetbrains.jet.lang.resolve.DeclarationResolver;
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
|
||||
import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
|
||||
import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.DefaultValueArgument;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ExpressionValueArgument;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.VarargValueArgument;
|
||||
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
|
||||
@@ -73,6 +75,7 @@ import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
|
||||
import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver;
|
||||
import static org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
|
||||
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
|
||||
import static org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage.getResolvedCallWithAssert;
|
||||
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*;
|
||||
import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.KotlinSyntheticClass;
|
||||
import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.DelegationToTraitImpl;
|
||||
@@ -1446,20 +1449,17 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
}
|
||||
|
||||
for (JetDelegationSpecifier specifier : myClass.getDelegationSpecifiers()) {
|
||||
if (specifier != superCall) {
|
||||
if (specifier instanceof JetDelegatorByExpressionSpecifier) {
|
||||
JetExpression delegateExpression = ((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression();
|
||||
assert delegateExpression != null;
|
||||
delegateExpression.accept(visitor);
|
||||
}
|
||||
if (specifier instanceof JetDelegatorByExpressionSpecifier) {
|
||||
JetExpression delegateExpression = ((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression();
|
||||
assert delegateExpression != null;
|
||||
delegateExpression.accept(visitor);
|
||||
}
|
||||
else {
|
||||
if (superCall instanceof JetDelegatorToSuperCall) {
|
||||
JetValueArgumentList argumentList = ((JetDelegatorToSuperCall) superCall).getValueArgumentList();
|
||||
if (argumentList != null) {
|
||||
argumentList.accept(visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (superCall instanceof JetDelegatorToSuperCall && !isAnonymousObject(descriptor)) {
|
||||
JetValueArgumentList argumentList = ((JetDelegatorToSuperCall) superCall).getValueArgumentList();
|
||||
if (argumentList != null) {
|
||||
argumentList.accept(visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1550,51 +1550,96 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
}
|
||||
|
||||
private void generateDelegatorToConstructorCall(
|
||||
InstructionAdapter iv, ExpressionCodegen codegen,
|
||||
ConstructorDescriptor constructorDescriptor
|
||||
@NotNull InstructionAdapter iv,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull ConstructorDescriptor constructorDescriptor
|
||||
) {
|
||||
ClassDescriptor classDecl = constructorDescriptor.getContainingDeclaration();
|
||||
|
||||
iv.load(0, OBJECT_TYPE);
|
||||
|
||||
if (classDecl.getKind() == ClassKind.ENUM_CLASS || classDecl.getKind() == ClassKind.ENUM_ENTRY) {
|
||||
iv.load(1, OBJECT_TYPE);
|
||||
iv.load(2, Type.INT_TYPE);
|
||||
}
|
||||
|
||||
ResolvedCall<?> resolvedCall = BindingContextUtilPackage.getResolvedCallWithAssert(superCall, bindingContext);
|
||||
ResolvedCall<?> resolvedCall = getResolvedCallWithAssert(superCall, bindingContext);
|
||||
ConstructorDescriptor superConstructor = (ConstructorDescriptor) resolvedCall.getResultingDescriptor();
|
||||
|
||||
//noinspection SuspiciousMethodCalls
|
||||
CalculatedClosure closureForSuper = bindingContext.get(CLOSURE, superConstructor.getContainingDeclaration());
|
||||
if (closureForSuper != null && closureForSuper.getCaptureThis() != null) {
|
||||
iv.load(((ConstructorFrameMap) codegen.myFrameMap).getOuterThisIndex(), OBJECT_TYPE);
|
||||
CallableMethod superCallable = typeMapper.mapToCallableMethod(superConstructor);
|
||||
CallableMethod callable = typeMapper.mapToCallableMethod(constructorDescriptor);
|
||||
|
||||
List<JvmMethodParameterSignature> superParameters = superCallable.getValueParameters();
|
||||
List<JvmMethodParameterSignature> parameters = callable.getValueParameters();
|
||||
|
||||
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 kind = parameter.getKind();
|
||||
Type type = parameter.getAsmType();
|
||||
|
||||
// Stop when we reach the actual value parameters present in the code; they will be generated via ResolvedCall below
|
||||
if (superParameters.get(superIndex).getKind() == JvmMethodParameterKind.VALUE &&
|
||||
kind == JvmMethodParameterKind.SUPER_CALL_PARAM) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (kind == JvmMethodParameterKind.SUPER_CALL_PARAM || kind == JvmMethodParameterKind.ENUM_NAME_OR_ORDINAL ||
|
||||
(kind == JvmMethodParameterKind.OUTER && superConstructor.getContainingDeclaration().isInner())) {
|
||||
iv.load(offset, type);
|
||||
superIndex++;
|
||||
}
|
||||
|
||||
offset += type.getSize();
|
||||
}
|
||||
|
||||
CallableMethod superCallable = typeMapper.mapToCallableMethod(superConstructor);
|
||||
|
||||
ArgumentGenerator argumentGenerator;
|
||||
if (isAnonymousObject(descriptor) && superCall instanceof JetDelegatorToSuperCall) {
|
||||
int nextVar = findFirstSuperArgument(typeMapper.mapToCallableMethod(constructorDescriptor));
|
||||
for (Type t : superCallable.getAsmMethod().getArgumentTypes()) {
|
||||
iv.load(nextVar, t);
|
||||
nextVar += t.getSize();
|
||||
}
|
||||
superCallable.invokeWithNotNullAssertion(codegen.v, state, resolvedCall);
|
||||
List<JvmMethodParameterSignature> superValues = superParameters.subList(superIndex, superParameters.size());
|
||||
argumentGenerator = new ObjectSuperCallArgumentGenerator(superValues, iv, offset);
|
||||
}
|
||||
else {
|
||||
codegen.invokeMethodWithArguments(superCallable, resolvedCall, StackValue.none());
|
||||
argumentGenerator =
|
||||
new CallBasedArgumentGenerator(codegen, codegen.defaultCallGenerator, superConstructor.getValueParameters(),
|
||||
superCallable.getValueParameterTypes());
|
||||
}
|
||||
|
||||
codegen.invokeMethodWithArguments(superCallable, resolvedCall, StackValue.none(), codegen.defaultCallGenerator, argumentGenerator);
|
||||
}
|
||||
|
||||
private static int findFirstSuperArgument(@NotNull CallableMethod method) {
|
||||
int i = 0;
|
||||
for (JvmMethodParameterSignature type : method.getValueParameters()) {
|
||||
if (type.getKind() == JvmMethodParameterKind.SUPER_OF_ANONYMOUS_CALL_PARAM) {
|
||||
return i + 1; // because of this
|
||||
}
|
||||
i += type.getAsmType().getSize();
|
||||
private static class ObjectSuperCallArgumentGenerator extends ArgumentGenerator {
|
||||
private final List<JvmMethodParameterSignature> parameters;
|
||||
private final InstructionAdapter iv;
|
||||
private int offset;
|
||||
|
||||
public ObjectSuperCallArgumentGenerator(
|
||||
@NotNull List<JvmMethodParameterSignature> superParameters,
|
||||
@NotNull InstructionAdapter iv,
|
||||
int firstValueParamOffset
|
||||
) {
|
||||
this.parameters = superParameters;
|
||||
this.iv = iv;
|
||||
this.offset = firstValueParamOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateExpression(int i, @NotNull ExpressionValueArgument argument) {
|
||||
generateSuperCallArgument(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateDefault(int i, @NotNull DefaultValueArgument argument) {
|
||||
pushDefaultValueOnStack(parameters.get(i).getAsmType(), iv);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateVararg(int i, @NotNull VarargValueArgument argument) {
|
||||
generateSuperCallArgument(i);
|
||||
}
|
||||
|
||||
private void generateSuperCallArgument(int i) {
|
||||
Type type = parameters.get(i).getAsmType();
|
||||
iv.load(offset, type);
|
||||
offset += type.getSize();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1602,7 +1647,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
if (declaration instanceof JetEnumEntry) {
|
||||
String name = declaration.getName();
|
||||
assert name != null : "Enum entry has no name: " + declaration.getText();
|
||||
String desc = "L" + classAsmType.getInternalName() + ";";
|
||||
String desc = classAsmType.getDescriptor();
|
||||
ClassDescriptor entryDescriptor = bindingContext.get(BindingContext.CLASS, declaration);
|
||||
v.newField(OtherOrigin(declaration, entryDescriptor), ACC_PUBLIC | ACC_ENUM | ACC_STATIC | ACC_FINAL, name, desc, null, null);
|
||||
myEnumConstants.add((JetEnumEntry) declaration);
|
||||
@@ -1663,7 +1708,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
throw new UnsupportedOperationException("unsupported type of enum constant initializer: " + specifier);
|
||||
}
|
||||
|
||||
ResolvedCall<?> resolvedCall = BindingContextUtilPackage.getResolvedCallWithAssert(specifier, bindingContext);
|
||||
ResolvedCall<?> resolvedCall = getResolvedCallWithAssert(specifier, bindingContext);
|
||||
|
||||
CallableMethod method = typeMapper.mapToCallableMethod((ConstructorDescriptor) resolvedCall.getResultingDescriptor());
|
||||
|
||||
|
||||
@@ -34,7 +34,9 @@ import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils;
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
|
||||
import org.jetbrains.jet.lang.resolve.OverrideResolver;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.DefaultValueArgument;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
|
||||
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
|
||||
import org.jetbrains.jet.lang.resolve.constants.StringValue;
|
||||
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
|
||||
@@ -784,9 +786,50 @@ public class JetTypeMapper {
|
||||
}
|
||||
|
||||
ResolvedCall<ConstructorDescriptor> superCall = closure.getSuperCall();
|
||||
if (superCall != null && isAnonymousObject(descriptor.getContainingDeclaration())) {
|
||||
for (JvmMethodParameterSignature parameter : mapSignature(superCall.getResultingDescriptor()).getValueParameters()) {
|
||||
writeParameter(sw, JvmMethodParameterKind.SUPER_OF_ANONYMOUS_CALL_PARAM, parameter.getAsmType());
|
||||
// We may generate a slightly wrong signature for a local class / anonymous object in light classes mode but we don't care,
|
||||
// because such classes are not accessible from the outside world
|
||||
if (superCall != null && classBuilderMode == ClassBuilderMode.FULL) {
|
||||
writeSuperConstructorCallParameters(sw, descriptor, superCall, captureThis != null);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeSuperConstructorCallParameters(
|
||||
@NotNull BothSignatureWriter sw,
|
||||
@NotNull ConstructorDescriptor descriptor,
|
||||
@NotNull ResolvedCall<ConstructorDescriptor> superCall,
|
||||
boolean hasOuter
|
||||
) {
|
||||
ConstructorDescriptor superDescriptor = superCall.getResultingDescriptor();
|
||||
List<ResolvedValueArgument> valueArguments = superCall.getValueArgumentsByIndex();
|
||||
assert valueArguments != null : "Failed to arrange value arguments by index: " + superDescriptor;
|
||||
|
||||
List<JvmMethodParameterSignature> parameters = mapSignature(superDescriptor).getValueParameters();
|
||||
|
||||
int params = parameters.size();
|
||||
int args = valueArguments.size();
|
||||
|
||||
// Mapped parameters should consist of captured values plus all of valueArguments
|
||||
assert params >= args :
|
||||
String.format("Incorrect number of mapped parameters vs arguments: %d < %d for %s", params, args, descriptor);
|
||||
|
||||
// Include all captured values, i.e. those parameters for which there are no resolved value arguments
|
||||
for (int i = 0; i < params - args; i++) {
|
||||
JvmMethodParameterSignature parameter = parameters.get(i);
|
||||
JvmMethodParameterKind kind = parameter.getKind();
|
||||
if (kind == JvmMethodParameterKind.ENUM_NAME_OR_ORDINAL) continue;
|
||||
if (hasOuter && kind == JvmMethodParameterKind.OUTER) continue;
|
||||
|
||||
writeParameter(sw, JvmMethodParameterKind.SUPER_CALL_PARAM, parameter.getAsmType());
|
||||
}
|
||||
|
||||
if (isAnonymousObject(descriptor.getContainingDeclaration())) {
|
||||
// For anonymous objects, also add all real non-default value arguments passed to the super constructor
|
||||
for (int i = 0; i < args; i++) {
|
||||
ResolvedValueArgument valueArgument = valueArguments.get(i);
|
||||
JvmMethodParameterSignature parameter = parameters.get(params - args + i);
|
||||
if (!(valueArgument instanceof DefaultValueArgument)) {
|
||||
writeParameter(sw, JvmMethodParameterKind.SUPER_CALL_PARAM, parameter.getAsmType());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user