JVM JVM_IR hide sealed class constructors

This commit is contained in:
Dmitry Petrov
2021-01-22 15:31:56 +03:00
parent ca3bb02897
commit 708e6914bd
27 changed files with 555 additions and 97 deletions
@@ -19,7 +19,6 @@ import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.jvm.InlineClassManglingRulesKt;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
@@ -118,8 +117,9 @@ public class ConstructorCodegen {
}
private void registerAccessorForHiddenConstructorIfNeeded(ClassConstructorDescriptor descriptor) {
if (!InlineClassManglingRulesKt.shouldHideConstructorDueToInlineClassTypeValueParameters(descriptor)) return;
context.getAccessor(descriptor, AccessorKind.NORMAL, null, null);
if (DescriptorAsmUtil.isHiddenConstructor(descriptor)) {
context.getAccessor(descriptor, AccessorKind.NORMAL, null, null);
}
}
public void generateSecondaryConstructor(
@@ -371,11 +371,13 @@ public class ConstructorCodegen {
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";
"Delegating constructor has not enough implicit parameters: " + delegatingConstructor;
break;
}
assert index < parameters.size() && parameters.get(index).getKind() == delegatingKind :
"Constructors of the same class should have the same set of implicit arguments";
if (index >= parameters.size() || parameters.get(index).getKind() != delegatingKind) {
throw new AssertionError(
"Constructors of the same class should have the same set of implicit arguments: " + delegatingConstructor);
}
JvmMethodParameterSignature parameter = parameters.get(index);
iv.load(offset, parameter.getAsmType());
@@ -383,7 +385,7 @@ public class ConstructorCodegen {
}
assert index == parameters.size() || parameters.get(index).getKind() == JvmMethodParameterKind.VALUE :
"Delegating constructor has not enough parameters";
"Delegating constructor has not enough parameters: " + delegatingConstructor;
return new CallBasedArgumentGenerator(codegen, codegen.defaultCallGenerator, delegatingConstructor.getValueParameters(),
delegatingCallable.getValueParameterTypes());
@@ -61,7 +61,8 @@ import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvmInterface;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
import static org.jetbrains.kotlin.resolve.inline.InlineOnlyKt.isInlineOnlyPrivateInBytecode;
import static org.jetbrains.kotlin.resolve.inline.InlineOnlyKt.isInlineWithReified;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
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.kotlin.resolve.jvm.annotations.JvmAnnotationUtilKt.hasJvmSyntheticAnnotation;
import static org.jetbrains.kotlin.types.TypeUtils.isNullableType;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
@@ -368,6 +369,18 @@ public class DescriptorAsmUtil {
return ACC_PRIVATE;
}
// Sealed class constructors should be ACC_PRIVATE.
// In 1.4 and before, sealed class constructors had PRIVATE visibility, and were represented as private methods in bytecode.
// In 1.5 (+AllowSealedInheritorsInDifferentFilesOfSamePackage), sealed class constructors became INTERNAL,
// but still should be represented as private methods in bytecode in order to prevent inheriting from sealed classes on JVM.
if (memberDescriptor instanceof ConstructorDescriptor &&
!(memberDescriptor instanceof AccessorForConstructorDescriptor) &&
isSealedClass(((ConstructorDescriptor) memberDescriptor).getConstructedClass()) &&
memberDescriptor.getVisibility() != DescriptorVisibilities.PUBLIC
) {
return ACC_PRIVATE;
}
if (isInlineOnlyPrivateInBytecode(memberDescriptor)) {
return ACC_PRIVATE;
}
@@ -893,4 +906,19 @@ public class DescriptorAsmUtil {
//Trait always should have this descriptor
return kind != OwnerKind.DEFAULT_IMPLS && isStaticMethod(kind, descriptor) ? 0 : 1;
}
public static boolean isHiddenConstructor(FunctionDescriptor descriptor) {
if (!(descriptor instanceof ClassConstructorDescriptor)) return false;
ClassConstructorDescriptor classConstructorDescriptor = (ClassConstructorDescriptor) descriptor;
if (InlineClassManglingRulesKt.shouldHideConstructorDueToInlineClassTypeValueParameters(descriptor)) {
return true;
}
if (isSealedClass(classConstructorDescriptor.getConstructedClass()) &&
classConstructorDescriptor.getVisibility() != DescriptorVisibilities.PUBLIC
) {
return true;
}
return false;
}
}
@@ -2523,21 +2523,39 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
// $default method is not private, so you need no accessor to call it
return descriptor;
}
else if (InlineClassManglingRulesKt.shouldHideConstructorDueToInlineClassTypeValueParameters(descriptor.getOriginal())) {
// Constructors with inline class type value parameters should always be called using an accessor.
// NB this will require accessors even if the constructor itself is in a different module.
return new AccessorForConstructorDescriptor(
(ClassConstructorDescriptor) descriptor.getOriginal(),
descriptor.getContainingDeclaration(),
getSuperCallTarget(resolvedCall.getCall()),
AccessorKind.NORMAL
);
else if (shouldForceAccessorForConstructor(descriptor.getOriginal())) {
return createAccessorForHiddenConstructor(resolvedCall, descriptor);
}
else {
return context.accessibleDescriptor(descriptor, getSuperCallTarget(resolvedCall.getCall()));
}
}
private boolean shouldForceAccessorForConstructor(FunctionDescriptor descriptor) {
// Force using accessors on hidden constructors only
if (!isHiddenConstructor(descriptor)) {
return false;
}
// Don't use accessor when calling hidden constructor from the same class.
if (descriptor.getContainingDeclaration() == context.getContextDescriptor().getContainingDeclaration()) {
return false;
}
return true;
}
@NotNull
private AccessorForConstructorDescriptor createAccessorForHiddenConstructor(
@NotNull ResolvedCall<?> resolvedCall,
FunctionDescriptor descriptor
) {
return new AccessorForConstructorDescriptor(
(ClassConstructorDescriptor) descriptor.getOriginal(),
descriptor.getContainingDeclaration(),
getSuperCallTarget(resolvedCall.getCall()),
AccessorKind.NORMAL
);
}
@NotNull
public StackValue invokeFunction(@NotNull ResolvedCall<?> resolvedCall, @NotNull StackValue receiver) {
return invokeFunction(resolvedCall.getCall(), resolvedCall, receiver);