Fix this remapping in inner class constructors

Inner class constructors should use the argument instead of reading
outer `this` from a field because if such an access happens before a
delegating constructor call, e.g. when evaluating an argument, a JVM
bytecode validation error will be thrown. (The only operation on `this`
allowed before a delegating constructor call is SETFIELD, and only if
the field in question is declared in the same class.)
This commit is contained in:
pyos
2019-03-15 10:51:48 +01:00
committed by max-kammerer
parent 7e8db4cc4a
commit 82ccf81da8
27 changed files with 35 additions and 45 deletions
@@ -77,10 +77,20 @@ class InnerClassesLowering(val context: BackendContext) : ClassLoweringPass {
irClass.transformChildrenVoid(VariableRemapper(oldConstructorParameterToNew))
irClass.transformChildrenVoid(object : IrElementTransformerVoid() {
private var enclosingConstructor: IrConstructor? = null
// TODO: maybe add another transformer that skips specified elements
override fun visitClass(declaration: IrClass): IrStatement =
declaration
override fun visitConstructor(declaration: IrConstructor): IrStatement =
try {
enclosingConstructor = declaration
super.visitConstructor(declaration)
} finally {
enclosingConstructor = null
}
override fun visitGetValue(expression: IrGetValue): IrExpression {
expression.transformChildrenVoid(this)
@@ -100,8 +110,14 @@ class InnerClassesLowering(val context: BackendContext) : ClassLoweringPass {
return expression
}
val outerThisField = context.declarationFactory.getOuterThisField(innerClass)
irThis = IrGetFieldImpl(startOffset, endOffset, outerThisField.symbol, outerThisField.type, irThis, origin)
irThis = if (enclosingConstructor != null && irClass == innerClass) {
// Might be before a super() call (e.g. an argument to one), in which case the JVM bytecode verifier will reject
// an attempt to access the field. Good thing we have a local variable as well.
IrGetValueImpl(startOffset, endOffset, enclosingConstructor!!.valueParameters[0].symbol, origin)
} else {
val outerThisField = context.declarationFactory.getOuterThisField(innerClass)
IrGetFieldImpl(startOffset, endOffset, outerThisField.symbol, outerThisField.type, irThis, origin)
}
innerClass = innerClass.parentAsClass
}
return irThis