[JEWEL-1387] Fix native window lookup on JBR 25

On JBR 25 the AWT window peer is sun.lwawt.macosx.LWCWindowPeer, a
subclass of sun.lwawt.LWWindowPeer that inherits getPlatformWindow()
without declaring it. The getDeclaredMethod lookup on the runtime class
threw NoSuchMethodException, so getWindowFromJavaWindow returned ID.NIL
and native window chrome updates (updateColors, updateFullScreenButtons)
silently became no-ops on macOS.

Search the whole class hierarchy for both the getPlatformWindow() method
and the ptr field (the latter had the same latent hardcoded-depth
assumption), and add regression tests for the hierarchy-walking lookups.

closes https://github.com/JetBrains/intellij-community/pull/3598

(cherry picked from commit 60e96be3384981411db33eccf5bdb5150283656a)


(cherry picked from commit bdc19ad03ffc1fd053354bdc5f80e52b462545c2)

IJ-MR-220560

GitOrigin-RevId: b2fb4e8b2776d561a526ebbcabde3a061c6cd54c
This commit is contained in:
Sebastiano Poggi
2026-09-07 22:29:09 +00:00
committed by intellij-monorepo-bot
parent 82443b5357
commit 0aa073b7ba
2 changed files with 125 additions and 7 deletions
@@ -0,0 +1,71 @@
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jewel.intui.standalone.window.macos
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import org.junit.jupiter.api.Test
/**
* Regression test for JEWEL-1387: the reflective lookups used to resolve the native window must search the whole class
* hierarchy, not just the runtime class.
*
* On JBR 25 the AWT window peer is `sun.lwawt.macosx.LWCWindowPeer`, a subclass of `sun.lwawt.LWWindowPeer` that
* inherits (but does not declare) `getPlatformWindow()`. A `getDeclaredMethod` call on the runtime class alone throws
* [NoSuchMethodException] and silently breaks native window chrome updates on macOS.
*/
internal class MacPlatformServicesReflectionTest {
@Suppress("UnusedPrivateMember", "FunctionOnlyReturningConstant")
private open class BasePeer {
@JvmField internal val ptr: Long = 42L
fun getPlatformWindow(): Any = this
}
// Mimics JBR 25's LWCWindowPeer: inherits getPlatformWindow() and ptr without declaring them
private class SubclassPeer : BasePeer()
@Test
fun `findMethodInHierarchy finds method declared on the class itself`() {
val method = MacPlatformServicesDefaultImpl.findMethodInHierarchy(BasePeer::class.java, "getPlatformWindow")
assertNotNull(method, "Should find a method declared directly on the class")
}
@Test
fun `findMethodInHierarchy finds method inherited from a superclass`() {
val method = MacPlatformServicesDefaultImpl.findMethodInHierarchy(SubclassPeer::class.java, "getPlatformWindow")
assertNotNull(method, "Should find a method declared on a superclass (JBR 25 LWCWindowPeer scenario)")
assertEquals(BasePeer::class.java, method.declaringClass, "Method should be resolved from the superclass")
}
@Test
fun `findMethodInHierarchy returns null for a missing method`() {
val method = MacPlatformServicesDefaultImpl.findMethodInHierarchy(SubclassPeer::class.java, "doesNotExist")
assertNull(method, "Should return null instead of throwing for missing methods")
}
@Test
fun `findFieldInHierarchy finds field declared on the class itself`() {
val field = MacPlatformServicesDefaultImpl.findFieldInHierarchy(BasePeer::class.java, "ptr")
assertNotNull(field, "Should find a field declared directly on the class")
}
@Test
fun `findFieldInHierarchy finds field inherited from a superclass`() {
val field = MacPlatformServicesDefaultImpl.findFieldInHierarchy(SubclassPeer::class.java, "ptr")
assertNotNull(field, "Should find a field declared on a superclass")
assertEquals(BasePeer::class.java, field.declaringClass, "Field should be resolved from the superclass")
}
@Test
fun `findFieldInHierarchy returns null for a missing field`() {
val field = MacPlatformServicesDefaultImpl.findFieldInHierarchy(SubclassPeer::class.java, "doesNotExist")
assertNull(field, "Should return null instead of throwing for missing fields")
}
}
@@ -7,9 +7,12 @@ import com.sun.jna.Callback
import com.sun.jna.Pointer
import java.awt.Component
import java.awt.Window
import java.lang.reflect.Field
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import javax.swing.SwingUtilities
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.VisibleForTesting
import org.jetbrains.jewel.foundation.InternalJewelApi
import org.jetbrains.jewel.foundation.util.JewelLogger
import org.jetbrains.jewel.intui.standalone.styling.default
@@ -60,14 +63,16 @@ public object MacPlatformServicesDefaultImpl : MacPlatformServices {
try {
val cPlatformWindow = getPlatformWindow(w)
if (cPlatformWindow != null) {
val ptr = cPlatformWindow.javaClass.superclass.getDeclaredField("ptr")
val ptr = findFieldInHierarchy(cPlatformWindow.javaClass, "ptr")
if (ptr == null) {
logger.warn("Fail to get cPlatformWindow from awt window: no 'ptr' field found.")
return ID.NIL
}
ptr.setAccessible(true)
return ID(ptr.getLong(cPlatformWindow))
}
} catch (e: IllegalAccessException) {
logger.warn("Fail to get cPlatformWindow from awt window.", e)
} catch (e: NoSuchFieldException) {
logger.warn("Fail to get cPlatformWindow from awt window.", e)
}
return ID.NIL
}
@@ -79,15 +84,19 @@ public object MacPlatformServicesDefaultImpl : MacPlatformServices {
val getPeer = componentAccessor.javaClass.getMethod("getPeer", Component::class.java).accessible()
val peer = getPeer.invoke(componentAccessor, w)
if (peer != null) {
val cWindowPeerClass: Class<*> = peer.javaClass
val getPlatformWindowMethod = cWindowPeerClass.getDeclaredMethod("getPlatformWindow")
// The method may be declared on a superclass of the runtime peer class (e.g., on JBR 25 the peer
// is sun.lwawt.macosx.LWCWindowPeer, which inherits getPlatformWindow() from sun.lwawt.LWWindowPeer),
// so we must search the whole class hierarchy rather than just the runtime class.
val getPlatformWindowMethod = findMethodInHierarchy(peer.javaClass, "getPlatformWindow")
if (getPlatformWindowMethod == null) {
logger.warn("Fail to get cPlatformWindow from awt window: no getPlatformWindow() method found.")
return null
}
val cPlatformWindow = getPlatformWindowMethod.invoke(peer)
if (cPlatformWindow != null) {
return cPlatformWindow
}
}
} catch (e: NoSuchMethodException) {
logger.warn("Fail to get cPlatformWindow from awt window.", e)
} catch (e: IllegalAccessException) {
logger.warn("Fail to get cPlatformWindow from awt window.", e)
} catch (e: InvocationTargetException) {
@@ -98,6 +107,44 @@ public object MacPlatformServicesDefaultImpl : MacPlatformServices {
return null
}
/**
* Finds a method with the given [name] and no parameters declared on [clazz] or any of its superclasses, or `null`
* if no such method exists.
*/
@VisibleForTesting
@ApiStatus.Internal
@InternalJewelApi
public fun findMethodInHierarchy(clazz: Class<*>, name: String): Method? {
var current: Class<*>? = clazz
while (current != null) {
try {
return current.getDeclaredMethod(name)
} catch (_: NoSuchMethodException) {
current = current.superclass
}
}
return null
}
/**
* Finds a field with the given [name] declared on [clazz] or any of its superclasses, or `null` if no such field
* exists.
*/
@VisibleForTesting
@ApiStatus.Internal
@InternalJewelApi
public fun findFieldInHierarchy(clazz: Class<*>, name: String): Field? {
var current: Class<*>? = clazz
while (current != null) {
try {
return current.getDeclaredField(name)
} catch (_: NoSuchFieldException) {
current = current.superclass
}
}
return null
}
public override fun updateColors(w: Window) {
SwingUtilities.invokeLater {
val window = getWindowFromJavaWindow(w)