JVM_IR: add accessors for protected members in divergent hierarchies

Class B : A cannot access a protected field of A through a reference to
an instance of C : A.
This commit is contained in:
pyos
2019-10-24 12:46:48 +02:00
parent 8c6916af52
commit 75aef4633a
4 changed files with 58 additions and 43 deletions
@@ -60,32 +60,36 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
return super.visitFunctionAccess(expression) return super.visitFunctionAccess(expression)
} }
fun makeFunctionAccessorSymbolWithSuper(functionSymbol: IrFunctionSymbol): IrFunctionSymbol = val withSuper = (expression as? IrCall)?.superQualifierSymbol != null
when (functionSymbol) { val thisSymbol = (expression as? IrCall)?.dispatchReceiver?.type?.classifierOrNull as? IrClassSymbol
is IrConstructorSymbol -> functionSymbol.owner.makeConstructorAccessor().symbol if (expression.symbol.isAccessible(withSuper, thisSymbol)) {
is IrSimpleFunctionSymbol -> functionSymbol.owner.makeSimpleFunctionAccessor(expression as IrCall).symbol return super.visitFunctionAccess(expression)
else -> error("Unknown subclass of IrFunctionSymbol") }
}
return super.visitExpression( return super.visitExpression(
handleAccess( modifyFunctionAccessExpression(expression, functionMap.getOrPut(expression.symbol) {
expression, when (val symbol = expression.symbol) {
expression.symbol, is IrConstructorSymbol -> symbol.owner.makeConstructorAccessor().symbol
functionMap, is IrSimpleFunctionSymbol -> symbol.owner.makeSimpleFunctionAccessor(expression as IrCall).symbol
::makeFunctionAccessorSymbolWithSuper, else -> error("Unknown subclass of IrFunctionSymbol")
::modifyFunctionAccessExpression, }
(expression as? IrCall)?.superQualifierSymbol, })
(expression as? IrCall)?.dispatchReceiver?.type?.classifierOrNull as? IrClassSymbol
)
) )
} }
override fun visitGetField(expression: IrGetField) = super.visitExpression( override fun visitGetField(expression: IrGetField) = super.visitExpression(
handleAccess(expression, expression.symbol, getterMap, ::makeGetterAccessorSymbol, ::modifyGetterExpression) if (!expression.symbol.isAccessible(false, expression.receiver?.type?.classifierOrNull as? IrClassSymbol)) {
modifyGetterExpression(expression, getterMap.getOrPut(expression.symbol) { makeGetterAccessorSymbol(expression.symbol) })
} else {
expression
}
) )
override fun visitSetField(expression: IrSetField) = super.visitExpression( override fun visitSetField(expression: IrSetField) = super.visitExpression(
handleAccess(expression, expression.symbol, setterMap, ::makeSetterAccessorSymbol, ::modifySetterExpression) if (!expression.symbol.isAccessible(false, expression.receiver?.type?.classifierOrNull as? IrClassSymbol)) {
modifySetterExpression(expression, setterMap.getOrPut(expression.symbol) { makeSetterAccessorSymbol(expression.symbol) })
} else {
expression
}
) )
override fun visitConstructor(declaration: IrConstructor): IrStatement { override fun visitConstructor(declaration: IrConstructor): IrStatement {
@@ -139,22 +143,6 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
}.symbol }.symbol
} }
private inline fun <ExprT : IrDeclarationReference, reified FromSyT : IrSymbol, ToSyT : IrSymbol> handleAccess(
expression: ExprT,
symbol: FromSyT,
accumMap: MutableMap<FromSyT, ToSyT>,
symbolConverter: (FromSyT) -> ToSyT,
exprConverter: (ExprT, ToSyT) -> IrDeclarationReference,
superQualifierSymbol: IrClassSymbol? = null,
thisSymbol: IrClassSymbol? = null
): IrExpression =
if (!symbol.isAccessible(superQualifierSymbol != null, thisSymbol)) {
val accessorSymbol = accumMap.getOrPut(symbol) { symbolConverter(symbol) }
exprConverter(expression, accessorSymbol)
} else {
expression
}
// In case of Java `protected static`, access could be done from a public inline function in the same package, // In case of Java `protected static`, access could be done from a public inline function in the same package,
// or a subclass of the Java class. Both cases require an accessor, which we cannot add to the Java class. // or a subclass of the Java class. Both cases require an accessor, which we cannot add to the Java class.
private fun IrDeclarationWithVisibility.accessorParent(parent: IrDeclarationParent = this.parent) = private fun IrDeclarationWithVisibility.accessorParent(parent: IrDeclarationParent = this.parent) =
@@ -520,17 +508,13 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
val contextDeclarationContainer = (currentScope!!.irElement as IrDeclaration).getAccessContext(withSuper) ?: return false val contextDeclarationContainer = (currentScope!!.irElement as IrDeclaration).getAccessContext(withSuper) ?: return false
val samePackage = declaration.getPackageFragment()?.fqName == contextDeclarationContainer.getPackageFragment()?.fqName val samePackage = declaration.getPackageFragment()?.fqName == contextDeclarationContainer.getPackageFragment()?.fqName
val fromSubclassOfReceiversClass = contextDeclarationContainer is IrClass && symbolDeclarationContainer is IrClass &&
((thisObjReference == null || contextDeclarationContainer.symbol.isSubtypeOfClass(thisObjReference)) &&
(contextDeclarationContainer.isSubclassOf(symbolDeclarationContainer)))
return when { return when {
declaration.visibility.isPrivate && symbolDeclarationContainer != contextDeclarationContainer -> false declaration.visibility.isPrivate && symbolDeclarationContainer != contextDeclarationContainer -> false
declaration.visibility.isProtected && !samePackage && declaration.visibility.isProtected && !samePackage && !fromSubclassOfReceiversClass -> false
!(symbolDeclarationContainer is IrClass && contextDeclarationContainer is IrClass && withSuper && !fromSubclassOfReceiversClass -> false
contextDeclarationContainer.isSubclassOf(symbolDeclarationContainer)) -> false
// Invoking with super qualifier is implemented by invokespecial, which requires
// 1. `this` to be assign compatible with current class.
// 2. the method is a member of a superclass of current class.
(withSuper && contextDeclarationContainer is IrClass && symbolDeclarationContainer is IrClass &&
((thisObjReference != null && !contextDeclarationContainer.symbol.isSubtypeOfClass(thisObjReference)) ||
!(contextDeclarationContainer.isSubclassOf(symbolDeclarationContainer)))) -> false
else -> true else -> true
} }
} }
@@ -0,0 +1,21 @@
// FILE: protectedPack/A.java
package protectedPack;
public class A {
protected final String field;
public A(String value) {
field = value;
}
}
// FILE: B.kt
import protectedPack.A
class B(value: String) : A(value) {
inner class C : A(field) {
val result = field
}
}
fun box(): String = B("OK").C().result
@@ -1087,6 +1087,11 @@ public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxAga
runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/overrideProtectedFunInPackage.kt"); runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/overrideProtectedFunInPackage.kt");
} }
@TestMetadata("protectedAccessor.kt")
public void testProtectedAccessor() throws Exception {
runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/protectedAccessor.kt");
}
@TestMetadata("protectedFunInPackage.kt") @TestMetadata("protectedFunInPackage.kt")
public void testProtectedFunInPackage() throws Exception { public void testProtectedFunInPackage() throws Exception {
runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/protectedFunInPackage.kt"); runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/protectedFunInPackage.kt");
@@ -1087,6 +1087,11 @@ public class IrBlackBoxAgainstJavaCodegenTestGenerated extends AbstractIrBlackBo
runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/overrideProtectedFunInPackage.kt"); runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/overrideProtectedFunInPackage.kt");
} }
@TestMetadata("protectedAccessor.kt")
public void testProtectedAccessor() throws Exception {
runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/protectedAccessor.kt");
}
@TestMetadata("protectedFunInPackage.kt") @TestMetadata("protectedFunInPackage.kt")
public void testProtectedFunInPackage() throws Exception { public void testProtectedFunInPackage() throws Exception {
runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/protectedFunInPackage.kt"); runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/protectedFunInPackage.kt");