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(VariableRemapper(oldConstructorParameterToNew))
irClass.transformChildrenVoid(object : IrElementTransformerVoid() { irClass.transformChildrenVoid(object : IrElementTransformerVoid() {
private var enclosingConstructor: IrConstructor? = null
// TODO: maybe add another transformer that skips specified elements // TODO: maybe add another transformer that skips specified elements
override fun visitClass(declaration: IrClass): IrStatement = override fun visitClass(declaration: IrClass): IrStatement =
declaration declaration
override fun visitConstructor(declaration: IrConstructor): IrStatement =
try {
enclosingConstructor = declaration
super.visitConstructor(declaration)
} finally {
enclosingConstructor = null
}
override fun visitGetValue(expression: IrGetValue): IrExpression { override fun visitGetValue(expression: IrGetValue): IrExpression {
expression.transformChildrenVoid(this) expression.transformChildrenVoid(this)
@@ -100,8 +110,14 @@ class InnerClassesLowering(val context: BackendContext) : ClassLoweringPass {
return expression return expression
} }
val outerThisField = context.declarationFactory.getOuterThisField(innerClass) irThis = if (enclosingConstructor != null && irClass == innerClass) {
irThis = IrGetFieldImpl(startOffset, endOffset, outerThisField.symbol, outerThisField.type, irThis, origin) // 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 innerClass = innerClass.parentAsClass
} }
return irThis return irThis
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
class Outer() { class Outer() {
open inner class InnerBase() { open inner class InnerBase() {
} }
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class A() { open class A() {
open inner class InnerA open inner class InnerA
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
class Outer() { class Outer() {
val s = "xyzzy" val s = "xyzzy"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class Base(val fn: () -> String) open class Base(val fn: () -> String)
fun box(): String { fun box(): String {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class Base(val fn: () -> String) open class Base(val fn: () -> String)
fun box(): String { fun box(): String {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class Foo(val x: () -> String) open class Foo(val x: () -> String)
class Outer { class Outer {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
interface Test { interface Test {
fun test(): String fun test(): String
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class Base(val fn: () -> String) open class Base(val fn: () -> String)
fun box(): String { fun box(): String {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class Base(val callback: () -> String) open class Base(val callback: () -> String)
class Outer { class Outer {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class Base(val callback: () -> String) open class Base(val callback: () -> String)
class Outer { class Outer {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class Base(val callback: () -> String) open class Base(val callback: () -> String)
class Outer { class Outer {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class Base(val callback: () -> String) open class Base(val callback: () -> String)
class Outer { class Outer {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class Foo(val x: () -> String) open class Foo(val x: () -> String)
open class Foo2(val foo: Foo) open class Foo2(val foo: Foo)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class Base(val callback: () -> String) open class Base(val callback: () -> String)
class Outer { class Outer {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
interface Callback { interface Callback {
fun invoke(): String fun invoke(): String
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
interface Callback { interface Callback {
fun invoke(): String fun invoke(): String
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
interface Callback { interface Callback {
fun invoke(): String fun invoke(): String
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// FILE: JavaClass.java // FILE: JavaClass.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class A1(y: String) { open class A1(y: String) {
val x = "A1.x,$y" val x = "A1.x,$y"
} }
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class A(val s: String) { open class A(val s: String) {
open inner class B(s: String): A(s) open inner class B(s: String): A(s)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class Father(val param: String) { open class Father(val param: String) {
abstract inner class InClass { abstract inner class InClass {
fun work(): String { fun work(): String {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
open class Father(val param: String) { open class Father(val param: String) {
abstract inner class InClass { abstract inner class InClass {
fun work(): String { fun work(): String {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
fun String.bar(): String { fun String.bar(): String {
open class Local { open class Local {
fun result() = this@bar fun result() = this@bar
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
class Outer { class Outer {
val outerProp: String val outerProp: String
constructor(x: String) { constructor(x: String) {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
class Outer { class Outer {
val outerProp: String val outerProp: String
constructor(x: String) { constructor(x: String) {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
class Outer(val x: String) { class Outer(val x: String) {
abstract inner class InnerBase abstract inner class InnerBase