JDK-8173028 : Incorrect processing of supplementary-plane characters in text fields
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 9
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2017-01-13
  • Updated: 2017-02-23
  • Resolved: 2017-02-13
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 10 JDK 9
10Fixed 9 b158Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "9-ea"
Java(TM) SE Runtime Environment (build 9-ea+152)
Java HotSpot(TM) 64-Bit Server VM (build 9-ea+152, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.14393]

A DESCRIPTION OF THE PROBLEM :
 Supplementary-plane characters in text fields are sometimes treated as two characters - it must be caused by their internal representation as surrogate pairs.

REGRESSION.  Last worked in version 8u112

ADDITIONAL REGRESSION INFORMATION: 
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run sample program given below. In opened text field, press Left first, then Backspace.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
First character is deleted (completely), second remains.
ACTUAL -
First character is replaced with some undisplayable character, probably because only a part of surrogate pair was deleted.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javax.swing.*;

public class JTextFieldTest {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame();
            JTextField field = new JTextField(20);
            field.setText(new String(new int[]{0x1d400, 0x61}, 0, 2));
            frame.add(field);
            frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}

---------- END SOURCE ----------


Comments
A test case that illustrates the change in behaviour more precisely is this import java.awt.Font; import java.awt.font.*; public class HitTest { public static void main(String args[]) { String s = new String(new int[]{0x1d400, 0x61}, 0, 2); Font font = new Font("Dialog", Font.PLAIN, 12); FontRenderContext frc = new FontRenderContext(null, false, false); TextLayout tl = new TextLayout(s, font, frc); TextHitInfo currHit = TextHitInfo.beforeOffset(3); TextHitInfo prevHit = tl.getNextLeftHit(currHit); System.out.println(prevHit.getCharIndex()+" "+ prevHit.isLeadingEdge()); } } JDK 8 : prints "2 true" JDK 9 : prints "1 true"
03-02-2017

For a text field containing 0x1d400, 0x61 When we press "left" key from last position of the field ie 0x61, the caret/dot position obtained is 1 http://hg.openjdk.java.net/jdk9/client/jdk/file/2c0a514ebdfb/src/java.desktop/share/classes/javax/swing/text/DefaultEditorKit.java#l1056 because textlayout getNextLeftHit() returns 1 [http://hg.openjdk.java.net/jdk9/client/jdk/file/2c0a514ebdfb/src/java.desktop/share/classes/javax/swing/text/GlyphPainter2.java#l348] which prevents "delChars" from being 2 but the first character is a unicode with 2 bytes so doc.remove() is called with 1 so character is not removed. It seems to be a layouting issue. If we have 0x61, 0x1d400 then caret/dot position is 3 which causes doc.remove() to be called with 2 chars and the unicode is removed. So, it seems java/awt/font/TextLayout.java#getNextLeftHit() has some problem.
20-01-2017

This test started failing in b96 because of our migration to harfbuzz done in fix of JDK-8143177. If we run the same test with b96 till b136 with icu fontlayout engine, it passes. But starting from b137 build fails even with icu fontlayout. The AIOOB exception was also introduced in b96 and subsequently fixed via JDK-8041480 in b137. Seems to be a regression of JDK-8143177.
20-01-2017

Verified the issue against 8,9ea on Windows,Linux and could reproduce the issue against 9ea b96 and onward, but could not on JDK 8 family. Steps to reproduce(javac): ************************** - Run the attached file(JTextFieldTest.java) with JDK. - move the cursor one character to the left - Click on the backspace button once Result: ********* OS : Windows 7 64 bit, Linux Ubuntu 14.04 LTS JDK: +++++ 8u112 b15 : Pass 9ea b01 : Pass 9ea b95 : Pass >> 9ea b96: Fail [exception Found] <<=============== Introduced in version 9ea+135 : Fail [exception Found] 9ea+146 : Fail 9ea+152 : Fail ================================================================ Output: [9ea b96,9ea+135]: abhijit@abhijit-VirtualBox:/media/sf_ora/ji/clientlibs$ java -version java version "9-ea" Java(TM) SE Runtime Environment (build 9-ea+135) Java HotSpot(TM) 64-Bit Server VM (build 9-ea+135, mixed mode) abhijit@abhijit-VirtualBox:/media/sf_ora/ji/clientlibs$ javac JTextFieldTest.java abhijit@abhijit-VirtualBox:/media/sf_ora/ji/clientlibs$ java JTextFieldTest Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 16 at sun.font.ExtendedTextSourceLabel.createCharinfo(java.desktop@9-ea/ExtendedTextSourceLabel.java:814) at sun.font.ExtendedTextSourceLabel.getCharinfo(java.desktop@9-ea/ExtendedTextSourceLabel.java:548) at sun.font.ExtendedTextSourceLabel.getLineBreakIndex(java.desktop@9-ea/ExtendedTextSourceLabel.java:480) at java.awt.font.TextMeasurer.calcLineBreak(java.desktop@9-ea/TextMeasurer.java:330) at java.awt.font.TextMeasurer.getLineBreakIndex(java.desktop@9-ea/TextMeasurer.java:566) at java.awt.font.LineBreakMeasurer.nextOffset(java.desktop@9-ea/LineBreakMeasurer.java:359) at java.awt.font.LineBreakMeasurer.nextLayout(java.desktop@9-ea/LineBreakMeasurer.java:440) at javax.swing.text.TextLayoutStrategy.sync(java.desktop@9-ea/TextLayoutStrategy.java:324) at javax.swing.text.TextLayoutStrategy.removeUpdate(java.desktop@9-ea/TextLayoutStrategy.java:83) at javax.swing.text.FlowView.removeUpdate(java.desktop@9-ea/FlowView.java:278) at javax.swing.plaf.basic.BasicTextFieldUI$I18nFieldView.removeUpdate(java.desktop@9-ea/BasicTextFieldUI.java:421) at javax.swing.plaf.basic.BasicTextUI$RootView.removeUpdate(java.desktop@9-ea/BasicTextUI.java:1667) at javax.swing.plaf.basic.BasicTextUI$UpdateHandler.removeUpdate(java.desktop@9-ea/BasicTextUI.java:1927) at javax.swing.text.AbstractDocument.fireRemoveUpdate(java.desktop@9-ea/AbstractDocument.java:261) at javax.swing.text.AbstractDocument.handleRemove(java.desktop@9-ea/AbstractDocument.java:628) at javax.swing.text.AbstractDocument.remove(java.desktop@9-ea/AbstractDocument.java:596) at javax.swing.text.DefaultEditorKit$DeletePrevCharAction.actionPerformed(java.desktop@9-ea/DefaultEditorKit.java:1075) at javax.swing.SwingUtilities.notifyAction(java.desktop@9-ea/SwingUtilities.java:1800) at javax.swing.JComponent.processKeyBinding(java.desktop@9-ea/JComponent.java:2897) at javax.swing.JComponent.processKeyBindings(java.desktop@9-ea/JComponent.java:2944) at javax.swing.JComponent.processKeyEvent(java.desktop@9-ea/JComponent.java:2860) at java.awt.Component.processEvent(java.desktop@9-ea/Component.java:6377) at java.awt.Container.processEvent(java.desktop@9-ea/Container.java:2259) at java.awt.Component.dispatchEventImpl(java.desktop@9-ea/Component.java:4984) at java.awt.Container.dispatchEventImpl(java.desktop@9-ea/Container.java:2317) at java.awt.Component.dispatchEvent(java.desktop@9-ea/Component.java:4816) at java.awt.KeyboardFocusManager.redispatchEvent(java.desktop@9-ea/KeyboardFocusManager.java:1949) at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(java.desktop@9-ea/DefaultKeyboardFocusManager.java:814) at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(java.desktop@9-ea/DefaultKeyboardFocusManager.java:1083) at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(java.desktop@9-ea/DefaultKeyboardFocusManager.java:953) at java.awt.DefaultKeyboardFocusManager.dispatchEvent(java.desktop@9-ea/DefaultKeyboardFocusManager.java:779) at java.awt.Component.dispatchEventImpl(java.desktop@9-ea/Component.java:4865) at java.awt.Container.dispatchEventImpl(java.desktop@9-ea/Container.java:2317) at java.awt.Window.dispatchEventImpl(java.desktop@9-ea/Window.java:2754) at java.awt.Component.dispatchEvent(java.desktop@9-ea/Component.java:4816) at java.awt.EventQueue.dispatchEventImpl(java.desktop@9-ea/EventQueue.java:761) at java.awt.EventQueue.access$500(java.desktop@9-ea/EventQueue.java:97) at java.awt.EventQueue$3.run(java.desktop@9-ea/EventQueue.java:712) at java.awt.EventQueue$3.run(java.desktop@9-ea/EventQueue.java:706) at java.security.AccessController.doPrivileged(java.base@9-ea/Native Method) at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(java.base@9-ea/ProtectionDomain.java:77) at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(java.base@9-ea/ProtectionDomain.java:87) at java.awt.EventQueue$4.run(java.desktop@9-ea/EventQueue.java:734) at java.awt.EventQueue$4.run(java.desktop@9-ea/EventQueue.java:732) at java.security.AccessController.doPrivileged(java.base@9-ea/Native Method) at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(java.base@9-ea/ProtectionDomain.java:77) at java.awt.EventQueue.dispatchEvent(java.desktop@9-ea/EventQueue.java:731) at java.awt.EventDispatchThread.pumpOneEventForFilters(java.desktop@9-ea/EventDispatchThread.java:199) at java.awt.EventDispatchThread.pumpEventsForFilter(java.desktop@9-ea/EventDispatchThread.java:124) at java.awt.EventDispatchThread.pumpEventsForHierarchy(java.desktop@9-ea/EventDispatchThread.java:113) at java.awt.EventDispatchThread.pumpEvents(java.desktop@9-ea/EventDispatchThread.java:109) at java.awt.EventDispatchThread.pumpEvents(java.desktop@9-ea/EventDispatchThread.java:101) at java.awt.EventDispatchThread.run(java.desktop@9-ea/EventDispatchThread.java:90) abhijit@abhijit-VirtualBox:/media/sf_ora/ji/clientlibs$
19-01-2017