Skip to content

Wrong lookup behavior in super trampoline when subclasses override below injection #21

Description

@lukaskubanek

Originally pointed out in this discussion. When installing the super trampoline into a class hierarchy, if a subclass below the injection point overrides the method, the lookup logic in ITKReturnThreadSuper(…) misroutes to the nearest override, potentially causing infinite recursion.

Reproduction

import ITKSuperBuilder
import XCTest

final class TrampolineTests: XCTestCase {
    func testTrampolineEdgeCase() throws {
        // Inject our super trampoline into `Level2` for `sayHello()`.
        try ITKSuperBuilder.addSuperInstanceMethod(
            to: Level2.self,
            selector: #selector(Level2.sayHello)
        )
        
        // Create a `Level4` instance and call `sayHello()`. We expect this to eventually
        // call `Level1`’s implementation exactly once. However, due to the broken lookup
        // logic in `ITKReturnThreadSuper(…)`, it will loop forever between Level3 overrides.
        let object = Level4()
        object.sayHello()
    }
}

class Level1: NSObject {
    @objc dynamic func sayHello() {
        print("Level1 says hello")
    }
}

class Level2: Level1 {
    // No override here. This is where we install the trampoline.
}

class Level3: Level2 {
    @objc dynamic override func sayHello() {
        print("Level3 says hello")
        super.sayHello()
    }
}

class Level4: Level3 {
    @objc dynamic override func sayHello() {
        print("Level4 says hello")
        super.sayHello()
    }
}

Expected output:

Level4 says hello
Level3 says hello
Level1 says hello

Actual output:

Level4 says hello
Level3 says hello
Level3 says hello
Level3 says hello
… (infinite repetition)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions