JDK-8243940 : NullPointerException in TableCellSkin when automatically resizing cells
  • Type: Bug
  • Component: javafx
  • Sub-Component: controls
  • Affected Version: openjfx14
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: os_x
  • CPU: x86
  • Submitted: 2020-04-27
  • Updated: 2020-07-31
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.
Other
tbdUnresolved
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
I have reproduced this bug in versions 15, 14, and 12.
The TableCellSkin appears to be not entirely reliable. I have been able to reproduce the following NullPointerException by hiding and showing a column in a table view whenever a cell is selected, and then scrolling continuously (see attached source code). The bug appears to occur after scrolling down for 98 rows.

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
	at javafx.scene.control.skin.TableCellSkin.tableColumnProperty(TableCellSkin.java:97)
	at javafx.scene.control.skin.TableCellSkinBase.getTableColumn(TableCellSkinBase.java:123)
	at javafx.scene.control.skin.TableCellSkinBase.dispose(TableCellSkinBase.java:136)
	at javafx.scene.control.skin.TableCellSkin.dispose(TableCellSkin.java:88)
	at javafx.scene.control.Control$2.invalidated(Control.java:267)
	at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112)
	at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:147)
	at javafx.css.StyleableObjectProperty.set(StyleableObjectProperty.java:82)
	at javafx.scene.control.Control$2.set(Control.java:250)
	at javafx.scene.control.Control$2.set(Control.java:233)
	at javafx.scene.control.Control.setSkin(Control.java:230)
	at javafx.scene.control.skin.TableRowSkinBase.recreateCells(TableRowSkinBase.java:715)
	at javafx.scene.control.skin.TableRowSkinBase.updateCells(TableRowSkinBase.java:505)
	at javafx.scene.control.skin.TableRowSkinBase.checkState(TableRowSkinBase.java:649)
	at javafx.scene.control.skin.TableRowSkinBase.computePrefHeight(TableRowSkinBase.java:588)
	at javafx.scene.control.Control.computePrefHeight(Control.java:570)
	at javafx.scene.Parent.prefHeight(Parent.java:1040)
	at javafx.scene.layout.Region.prefHeight(Region.java:1559)
	at javafx.scene.control.skin.VirtualFlow.resizeCell(VirtualFlow.java:1923)
	at javafx.scene.control.skin.VirtualFlow.addLeadingCells(VirtualFlow.java:2030)
	at javafx.scene.control.skin.VirtualFlow.scrollPixels(VirtualFlow.java:1601)
	at javafx.scene.control.skin.VirtualFlow.tryScrollOneCell(VirtualFlow.java:1489)
	at javafx.scene.control.skin.VirtualFlow.scrollTo(VirtualFlow.java:1462)
	at javafx.scene.control.skin.TableViewSkinBase.onSelectBelowCell(TableViewSkinBase.java:629)
	at javafx.scene.control.skin.TableViewSkin.lambda$new$9(TableViewSkin.java:128)
	at com.sun.javafx.scene.control.behavior.TableViewBehaviorBase.selectNextRow(TableViewBehaviorBase.java:839)
	at com.sun.javafx.scene.control.behavior.TableViewBehaviorBase.lambda$new$12(TableViewBehaviorBase.java:156)
	at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
	at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
	at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
	at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
	at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
	at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
	at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
	at javafx.event.Event.fireEvent(Event.java:198)
	at javafx.scene.Scene$KeyHandler.process(Scene.java:4098)
	at javafx.scene.Scene.processKeyEvent(Scene.java:2157)
	at javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:2625)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:217)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:149)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleKeyEvent$1(GlassViewEventHandler.java:248)
	at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:412)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleKeyEvent(GlassViewEventHandler.java:247)
	at com.sun.glass.ui.View.handleKeyEvent(View.java:547)
	at com.sun.glass.ui.View.notifyKey(View.java:971)
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
	at javafx.scene.control.skin.TableCellSkin.tableColumnProperty(TableCellSkin.java:97)
	at javafx.scene.control.skin.TableCellSkinBase.getTableColumn(TableCellSkinBase.java:123)
	at javafx.scene.control.skin.TableCellSkinBase.dispose(TableCellSkinBase.java:136)
	at javafx.scene.control.skin.TableCellSkin.dispose(TableCellSkin.java:88)
	at javafx.scene.control.Control$2.invalidated(Control.java:267)
	at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112)
	at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:147)
	at javafx.css.StyleableObjectProperty.set(StyleableObjectProperty.java:82)
	at javafx.scene.control.Control$2.set(Control.java:250)
	at javafx.scene.control.Control$2.set(Control.java:233)
	at javafx.scene.control.Control.setSkin(Control.java:230)
	at javafx.scene.control.skin.TableRowSkinBase.recreateCells(TableRowSkinBase.java:715)
	at javafx.scene.control.skin.TableRowSkinBase.updateCells(TableRowSkinBase.java:505)
	at javafx.scene.control.skin.TableRowSkinBase.checkState(TableRowSkinBase.java:649)
	at javafx.scene.control.skin.TableRowSkinBase.computePrefHeight(TableRowSkinBase.java:588)
	at javafx.scene.control.Control.computePrefHeight(Control.java:570)
	at javafx.scene.Parent.prefHeight(Parent.java:1040)
	at javafx.scene.layout.Region.prefHeight(Region.java:1559)
	at javafx.scene.control.skin.VirtualFlow.resizeCell(VirtualFlow.java:1923)
	at javafx.scene.control.skin.VirtualFlow.addTrailingCells(VirtualFlow.java:2125)
	at javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1253)
	at javafx.scene.Parent.layout(Parent.java:1207)
	at javafx.scene.Parent.layout(Parent.java:1214)
	at javafx.scene.Parent.layout(Parent.java:1214)
	at javafx.scene.Scene.doLayoutPass(Scene.java:576)
	at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2510)
	at com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:412)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:411)
	at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:438)
	at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:563)
	at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:543)
	at com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:536)
	at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:342)
	at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
	at javafx.scene.control.skin.CellSkinBase$StyleableProperties$1.isSettable(CellSkinBase.java:166)
	at javafx.scene.control.skin.CellSkinBase$StyleableProperties$1.isSettable(CellSkinBase.java:161)
	at javafx.scene.CssStyleHelper.transitionToState(CssStyleHelper.java:666)
	at javafx.scene.Node.doProcessCSS(Node.java:9660)
	at javafx.scene.Node$1.doProcessCSS(Node.java:472)
	at com.sun.javafx.scene.NodeHelper.processCSSImpl(NodeHelper.java:192)
	at com.sun.javafx.scene.ParentHelper.superProcessCSSImpl(ParentHelper.java:93)
	at com.sun.javafx.scene.ParentHelper.superProcessCSS(ParentHelper.java:63)
	at javafx.scene.Parent.doProcessCSS(Parent.java:1369)
	at javafx.scene.Parent$1.doProcessCSS(Parent.java:125)
	at com.sun.javafx.scene.ParentHelper.processCSSImpl(ParentHelper.java:98)
	at com.sun.javafx.scene.control.ControlHelper.superProcessCSSImpl(ControlHelper.java:63)
	at com.sun.javafx.scene.control.ControlHelper.superProcessCSS(ControlHelper.java:55)
	at javafx.scene.control.Control.doProcessCSS(Control.java:886)
	at javafx.scene.control.Control$1.doProcessCSS(Control.java:89)
	at com.sun.javafx.scene.control.ControlHelper.processCSSImpl(ControlHelper.java:67)
	at com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
	at javafx.scene.Node.processCSS(Node.java:9542)
	at javafx.scene.Node.processCSS(Node.java:9535)
	at javafx.scene.Node.processCSS(Node.java:9535)
	at javafx.scene.Node.processCSS(Node.java:9535)
	at javafx.scene.Node.processCSS(Node.java:9535)
	at javafx.scene.Node.processCSS(Node.java:9535)
	at javafx.scene.Node.processCSS(Node.java:9535)
	at javafx.scene.Scene.doCSSPass(Scene.java:569)
	at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2505)
	at com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:412)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:411)
	at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:438)
	at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:563)
	at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:543)
	at com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:536)
	at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:342)
	at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)

In this stack trace, a call to VirtualFlow.addLeadingCells is made by VirtualFlow.scrollPixels. However, I have seen a similar crash where VirtualFlow.addLeadingCells is called by VirtualFlow.layoutChildren, so it is likely that the same bug can be exhibited via different actions.

The immediate cause of the crash is the access of a null skinnable in TableCellSkin.tableColumnProperty. On surface-level examination, a simple null check in TableCellSkin.tableColumnProperty - and in its caller TableCellSkinBase.getTableColumn - appears to alleviate the problem.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
- Launch test program
- Select cell 0 in table view
- Hold the down arrow key to scroll continuously

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Table view should scroll until the last cell is selected
ACTUAL -
NullPointerException is thrown after scrolling for some time; seemingly after scrolling for 98 cells

---------- BEGIN SOURCE ----------
import java.util.ArrayList;
import java.util.List;

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class MyApplication extends Application
{
    @Override
    public void start(Stage stage)
    {
        List<Obj> list = createList();
        ObservableList<Obj> rows = FXCollections.observableArrayList(list);
        TableView<Obj> tableView = new TableView<>();
        tableView.setItems(rows);

        TableColumn<Obj, String> myColumn = new TableColumn<>("Column");
        myColumn.setCellValueFactory(new PropertyValueFactory<>(list.get(0).stringProperty().getName()));
        tableView.getColumns().add(myColumn);

        tableView.selectionModelProperty().get().selectedItemProperty().addListener((obs, o, n) ->
        {
            myColumn.setVisible(false);
            myColumn.setVisible(true);
        });

        BorderPane root = new BorderPane(tableView);
        Scene scene = new Scene(root, 500, 200);
        stage.setScene(scene);
        stage.show();
    }

    private List<Obj> createList()
    {
        var list = new ArrayList<Obj>();
        for (int i = 0; i < 1000; i++)
        {
            list.add(new Obj(String.valueOf(i)));
        }
        return list;
    }

    public static class Obj
    {
        private StringProperty string;
        public void setString(String value) { stringProperty().set(value); }
        public StringProperty stringProperty() {
            if (string == null) string = new SimpleStringProperty(this, "string");
            return string;
        }

        public Obj(String string) {
            setString(string);
        }
    }
}

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

FREQUENCY : always



Comments
seems to be fixed with JDK-8244112 - not sure what to do with this? The code in TableRowSkinBase still looks fishy.
06-05-2020

there's fishy code in TableRowSkinBase recreateCells: if (cell != null) { cell.updateIndex(-1); cell.getSkin().dispose(); cell.setSkin(null); } disposing the skin manually nulls its skinnable, the automatic dispose that's triggerred by setSkin happens on an invalid state of the skin - that's definitely wrong: we must not do _anything_ with a disposed skin. Removing the manual dispose solves this NPE, might have side-effects, obviously (somebody added the line for whatever reason ;) Edit: doc states "Calling dispose twice has no effect" - that looks like the skin impl must protect itself against the skinnable being null .. Edit2: relevant changes to recreateCells - JDK-8122968 (was RT-31015) - introduced the manual dispose (looks like a cleanup round, not directly related to fixed bug) - JDK-8096057 (was RT-36117) - introduced the nulling of the skin after the manual dispose: that's an intentional addition that fixed some NPE while dragging. Not reproducible with reverting, too many changes since then (8u20). None has tests, but all tests in controls are passing ;) No idea why that happens so reliably with the 98th scroll .. So: we might have different interwoven issues - TableCellSkin et: definitely break contract in dispose (must do nothing if called twice) - TableRowSkinBase: murky to call dispose (nulling the skin should be enough) - VirtualFlow: npe on 98th key pressed is .. weird
28-04-2020