From 1ab69e385098ab9188b801021ef99055826c0956 Mon Sep 17 00:00:00 2001 From: VerKWer <26789899+VerKWer@users.noreply.github.com> Date: Wed, 24 Apr 2024 08:06:44 +0200 Subject: [PATCH] Fix OutOfMemoryError when using TailTipWidget (fix #974) (#975) Co-authored-by: Kay Werndli --- .../org/jline/widget/TailTipWidgetsTest.java | 66 +++++++++++++++++++ .../main/java/org/jline/utils/Display.java | 2 +- .../src/main/java/org/jline/utils/Status.java | 3 +- 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 console/src/test/java/org/jline/widget/TailTipWidgetsTest.java diff --git a/console/src/test/java/org/jline/widget/TailTipWidgetsTest.java b/console/src/test/java/org/jline/widget/TailTipWidgetsTest.java new file mode 100644 index 000000000..2257484b2 --- /dev/null +++ b/console/src/test/java/org/jline/widget/TailTipWidgetsTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.widget; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.jline.reader.LineReader; +import org.jline.reader.impl.LineReaderImpl; +import org.jline.terminal.Terminal; +import org.jline.terminal.impl.DumbTerminal; +import org.jline.utils.InfoCmp.Capability; +import org.jline.utils.Status; +import org.jline.widget.TailTipWidgets.TipType; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public final class TailTipWidgetsTest { + + /** A simple extension of {@link DumbTerminal} doing the minimal amount of work so that a {@link Status} constructed + * from it is "supported". */ + private static final class SupportedDumbTerminal extends DumbTerminal { + private SupportedDumbTerminal() throws IOException { + super(new ByteArrayInputStream(new byte[0]), new ByteArrayOutputStream()); + strings.put(Capability.change_scroll_region, ""); + strings.put(Capability.save_cursor, ""); + strings.put(Capability.restore_cursor, ""); + strings.put(Capability.cursor_address, ""); + } + } + + /** Subclass of {@link Status} exposing the {@code supported} field. For testing only. */ + private static final class TestStatus extends Status { + private TestStatus(Terminal terminal) { + super(terminal); + } + + private boolean isSupported() { + return supported; + } + } + + /** A dummy {@link LineReader} that's immediately resized to 0x0. */ + private static final class ZeroSizeLineReader extends LineReaderImpl { + private ZeroSizeLineReader(Terminal terminal) throws IOException { + super(terminal); + display.resize(0, 0); + } + } + + @Test + public void enableTest() throws Exception { + Terminal terminal = new SupportedDumbTerminal(); + assertTrue(new TestStatus(terminal).isSupported()); + LineReader reader = new ZeroSizeLineReader(terminal); + new TailTipWidgets(reader, __ -> null, 1, TipType.COMBINED).enable(); + } +} diff --git a/terminal/src/main/java/org/jline/utils/Display.java b/terminal/src/main/java/org/jline/utils/Display.java index 16fc9e337..b13a1b965 100644 --- a/terminal/src/main/java/org/jline/utils/Display.java +++ b/terminal/src/main/java/org/jline/utils/Display.java @@ -68,7 +68,7 @@ public void setDelayLineWrap(boolean v) { public void resize(int rows, int columns) { if (rows == 0 || columns == 0) { - columns = Integer.MAX_VALUE - 1; + columns = 1; rows = 1; } if (this.rows != rows || this.columns != columns) { diff --git a/terminal/src/main/java/org/jline/utils/Status.java b/terminal/src/main/java/org/jline/utils/Status.java index 074d3b0df..c42b50d7e 100644 --- a/terminal/src/main/java/org/jline/utils/Status.java +++ b/terminal/src/main/java/org/jline/utils/Status.java @@ -117,8 +117,9 @@ public void update(List lines, boolean flush) { lines = new ArrayList<>(lines); // add border + int rows = display.rows; int columns = display.columns; - if (border == 1 && !lines.isEmpty()) { + if (border == 1 && !lines.isEmpty() && rows > 1) { lines.add(0, getBorderString(columns)); } // trim or complete lines to the full width