Skip to content

Commit

Permalink
Fixing the FfmTerminal to run on JDK 22 and on Linux. (#945)
Browse files Browse the repository at this point in the history
* Fixing the FffmTerminal to run on JDK 22 and on Linux.
* Ensuring control characters are not re-written inadvertedly.
* Update terminal-ffm/src/main/java/org/jline/terminal/impl/ffm/FfmTerminalProvider.java

Co-authored-by: ExE Boss <3889017+ExE-Boss@users.noreply.github.com>
Co-authored-by: Guillaume Nodet <gnodet@gmail.com>
  • Loading branch information
3 people committed Apr 17, 2024
1 parent 8775eb2 commit 9393ed6
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 49 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/master-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-latest, ubuntu-20.04, windows-latest, macos-latest ]
java: [ '21' ]
java: [ '22' ]
steps:
- uses: actions/checkout@v4

Expand Down
7 changes: 3 additions & 4 deletions jline/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -400,19 +400,18 @@
</configuration>
</execution>
<execution>
<id>jdk21</id>
<id>jdk22</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<includes>
<include>**/ffm/*.java</include>
</includes>
<release>21</release>
<release>22</release>
<compilerArgs>
<arg>-Xlint:all,-options,-preview</arg>
<arg>-Xlint:all,-options</arg>
<arg>-Werror</arg>
<arg>--enable-preview</arg>
</compilerArgs>
</configuration>
</execution>
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.outputTimestamp>2024-01-23T12:21:59Z</project.build.outputTimestamp>

<java.build.version>21</java.build.version>
<java.build.version>22</java.build.version>
<java.release.version>8</java.release.version>
<maven.version>3.9.6</maven.version>

Expand Down
11 changes: 4 additions & 7 deletions terminal-ffm/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<name>JLine FFM Terminal</name>

<properties>
<java.release.version>21</java.release.version>
<java.release.version>22</java.release.version>
<automatic.module.name>org.jline.terminal.ffm</automatic.module.name>
</properties>

Expand Down Expand Up @@ -56,24 +56,21 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>21</release>
<compilerArgs>
<arg>--enable-preview</arg>
</compilerArgs>
<release>${java.release.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>--enable-preview --enable-native-access=ALL-UNNAMED</argLine>
<argLine>--enable-native-access=ALL-UNNAMED</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<additionalOptions>--enable-preview --release ${java.release.version}</additionalOptions>
<additionalOptions>--release ${java.release.version}</additionalOptions>
</configuration>
</plugin>
</plugins>
Expand Down
106 changes: 80 additions & 26 deletions terminal-ffm/src/main/java/org/jline/terminal/impl/ffm/CLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import java.io.InputStream;
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -33,7 +35,7 @@
import org.jline.terminal.spi.TerminalProvider;
import org.jline.utils.OSUtils;

@SuppressWarnings("preview")
@SuppressWarnings("restricted")
class CLibrary {

private static final Logger logger = Logger.getLogger("org.jline");
Expand All @@ -51,8 +53,8 @@ static class winsize {
ValueLayout.JAVA_SHORT.withName("ws_col"),
ValueLayout.JAVA_SHORT,
ValueLayout.JAVA_SHORT);
ws_row = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ws_row"));
ws_col = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ws_col"));
ws_row = FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("ws_row"));
ws_col = FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("ws_col"));
}

private final java.lang.foreign.MemorySegment seg;
Expand Down Expand Up @@ -97,24 +99,59 @@ static class termios {
private static final VarHandle c_oflag;
private static final VarHandle c_cflag;
private static final VarHandle c_lflag;
private static final long c_cc_offset;
private static final VarHandle c_ispeed;
private static final VarHandle c_ospeed;

static {
LAYOUT = MemoryLayout.structLayout(
ValueLayout.JAVA_LONG.withName("c_iflag"),
ValueLayout.JAVA_LONG.withName("c_oflag"),
ValueLayout.JAVA_LONG.withName("c_cflag"),
ValueLayout.JAVA_LONG.withName("c_lflag"),
MemoryLayout.sequenceLayout(32, ValueLayout.JAVA_BYTE).withName("c_cc"),
ValueLayout.JAVA_LONG.withName("c_ispeed"),
ValueLayout.JAVA_LONG.withName("c_ospeed"));
c_iflag = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c_iflag"));
c_oflag = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c_oflag"));
c_cflag = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c_cflag"));
c_lflag = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c_lflag"));
c_ispeed = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c_ispeed"));
c_ospeed = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c_ospeed"));
if (OSUtils.IS_OSX) {
LAYOUT = MemoryLayout.structLayout(
ValueLayout.JAVA_LONG.withName("c_iflag"),
ValueLayout.JAVA_LONG.withName("c_oflag"),
ValueLayout.JAVA_LONG.withName("c_cflag"),
ValueLayout.JAVA_LONG.withName("c_lflag"),
MemoryLayout.sequenceLayout(32, ValueLayout.JAVA_BYTE).withName("c_cc"),
ValueLayout.JAVA_LONG.withName("c_ispeed"),
ValueLayout.JAVA_LONG.withName("c_ospeed"));
} else if (OSUtils.IS_LINUX) {
LAYOUT = MemoryLayout.structLayout(
ValueLayout.JAVA_INT.withName("c_iflag"),
ValueLayout.JAVA_INT.withName("c_oflag"),
ValueLayout.JAVA_INT.withName("c_cflag"),
ValueLayout.JAVA_INT.withName("c_lflag"),
ValueLayout.JAVA_BYTE.withName("c_line"),
MemoryLayout.sequenceLayout(32, ValueLayout.JAVA_BYTE).withName("c_cc"),
MemoryLayout.paddingLayout(3),
ValueLayout.JAVA_INT.withName("c_ispeed"),
ValueLayout.JAVA_INT.withName("c_ospeed"));
} else {
throw new IllegalStateException("Unsupported system!");
}
c_iflag = adjust2LinuxHandle(
FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("c_iflag")));
c_oflag = adjust2LinuxHandle(
FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("c_oflag")));
c_cflag = adjust2LinuxHandle(
FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("c_cflag")));
c_lflag = adjust2LinuxHandle(
FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("c_lflag")));
c_cc_offset = LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("c_cc"));
c_ispeed = adjust2LinuxHandle(
FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("c_ispeed")));
c_ospeed = adjust2LinuxHandle(
FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("c_ospeed")));
}

private static VarHandle adjust2LinuxHandle(VarHandle v) {
if (OSUtils.IS_LINUX) {
MethodHandle id = MethodHandles.identity(int.class);
v = MethodHandles.filterValue(
v,
MethodHandles.explicitCastArguments(id, MethodType.methodType(int.class, long.class)),
MethodHandles.explicitCastArguments(id, MethodType.methodType(long.class, int.class)));
}

return v;
}

private final java.lang.foreign.MemorySegment seg;
Expand Down Expand Up @@ -211,14 +248,18 @@ static class termios {
c_cc[VINTR] = (byte) t.getControlChar(Attributes.ControlChar.VINTR);
c_cc[VQUIT] = (byte) t.getControlChar(Attributes.ControlChar.VQUIT);
c_cc[VSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VSUSP);
c_cc[VDSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VDSUSP);
if (VDSUSP != (-1)) {
c_cc[VDSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VDSUSP);
}
c_cc[VSTART] = (byte) t.getControlChar(Attributes.ControlChar.VSTART);
c_cc[VSTOP] = (byte) t.getControlChar(Attributes.ControlChar.VSTOP);
c_cc[VLNEXT] = (byte) t.getControlChar(Attributes.ControlChar.VLNEXT);
c_cc[VDISCARD] = (byte) t.getControlChar(Attributes.ControlChar.VDISCARD);
c_cc[VMIN] = (byte) t.getControlChar(Attributes.ControlChar.VMIN);
c_cc[VTIME] = (byte) t.getControlChar(Attributes.ControlChar.VTIME);
c_cc[VSTATUS] = (byte) t.getControlChar(Attributes.ControlChar.VSTATUS);
if (VSTATUS != (-1)) {
c_cc[VSTATUS] = (byte) t.getControlChar(Attributes.ControlChar.VSTATUS);
}
c_cc().copyFrom(java.lang.foreign.MemorySegment.ofArray(c_cc));
}

Expand Down Expand Up @@ -259,7 +300,7 @@ void c_lflag(long f) {
}

java.lang.foreign.MemorySegment c_cc() {
return seg.asSlice(32, 20);
return seg.asSlice(c_cc_offset, 20);
}

long c_ispeed() {
Expand Down Expand Up @@ -377,14 +418,18 @@ public Attributes asAttributes() {
cc.put(Attributes.ControlChar.VINTR, (int) c_cc[VINTR]);
cc.put(Attributes.ControlChar.VQUIT, (int) c_cc[VQUIT]);
cc.put(Attributes.ControlChar.VSUSP, (int) c_cc[VSUSP]);
cc.put(Attributes.ControlChar.VDSUSP, (int) c_cc[VDSUSP]);
if (VDSUSP != (-1)) {
cc.put(Attributes.ControlChar.VDSUSP, (int) c_cc[VDSUSP]);
}
cc.put(Attributes.ControlChar.VSTART, (int) c_cc[VSTART]);
cc.put(Attributes.ControlChar.VSTOP, (int) c_cc[VSTOP]);
cc.put(Attributes.ControlChar.VLNEXT, (int) c_cc[VLNEXT]);
cc.put(Attributes.ControlChar.VDISCARD, (int) c_cc[VDISCARD]);
cc.put(Attributes.ControlChar.VMIN, (int) c_cc[VMIN]);
cc.put(Attributes.ControlChar.VTIME, (int) c_cc[VTIME]);
cc.put(Attributes.ControlChar.VSTATUS, (int) c_cc[VSTATUS]);
if (VSTATUS != (-1)) {
cc.put(Attributes.ControlChar.VSTATUS, (int) c_cc[VSTATUS]);
}
// Return
return attr;
}
Expand Down Expand Up @@ -630,19 +675,19 @@ static Pty openpty(TerminalProvider provider, Attributes attr, Size size) {
private static final int VWERASE;
private static final int VKILL;
private static final int VREPRINT;
private static int VERASE2;
private static final int VERASE2;
private static final int VINTR;
private static final int VQUIT;
private static final int VSUSP;
private static int VDSUSP;
private static final int VDSUSP;
private static final int VSTART;
private static final int VSTOP;
private static final int VLNEXT;
private static final int VDISCARD;
private static final int VMIN;
private static int VSWTC;
private static final int VSWTC;
private static final int VTIME;
private static int VSTATUS;
private static final int VSTATUS;

private static final int IGNBRK;
private static final int BRKINT;
Expand Down Expand Up @@ -784,6 +829,9 @@ static Pty openpty(TerminalProvider provider, Attributes attr, Size size) {
VWERASE = 14;
VLNEXT = 15;
VEOL2 = 16;
VERASE2 = -1;
VDSUSP = -1;
VSTATUS = -1;

IGNBRK = 0x0000001;
BRKINT = 0x0000002;
Expand Down Expand Up @@ -906,6 +954,9 @@ static Pty openpty(TerminalProvider provider, Attributes attr, Size size) {
VWERASE = 14;
VLNEXT = 15;
VEOL2 = 16;
VERASE2 = -1;
VDSUSP = -1;
VSTATUS = -1;

IGNBRK = 0x0000001;
BRKINT = 0x0000002;
Expand Down Expand Up @@ -1026,6 +1077,8 @@ static Pty openpty(TerminalProvider provider, Attributes attr, Size size) {
VMIN = 16;
VTIME = 17;
VSTATUS = 18;
VERASE2 = -1;
VSWTC = -1;

IGNBRK = 0x00000001;
BRKINT = 0x00000002;
Expand Down Expand Up @@ -1119,6 +1172,7 @@ static Pty openpty(TerminalProvider provider, Attributes attr, Size size) {
VMIN = 16;
VTIME = 17;
VSTATUS = 18;
VSWTC = -1;

IGNBRK = 0x0000001;
BRKINT = 0x0000002;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemoryLayout.PathElement;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.charset.Charset;

import org.jline.terminal.Attributes;
Expand Down Expand Up @@ -115,4 +119,13 @@ public int systemStreamWidth(SystemStream stream) {
public String toString() {
return "TerminalProvider[" + name() + "]";
}

static VarHandle lookupVarHandle(MemoryLayout layout, PathElement... element) {
VarHandle h = layout.varHandle(element);

// the last parameter of the VarHandle is additional offset, hardcode zero:
h = MethodHandles.insertCoordinates(h, h.coordinateTypes().size() - 1, 0L);

return h;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import java.nio.charset.StandardCharsets;
import java.util.Objects;

@SuppressWarnings({"unused", "preview"})
@SuppressWarnings({"unused", "restricted"})
final class Kernel32 {

public static final int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
Expand Down Expand Up @@ -277,8 +277,8 @@ public static INPUT_RECORD[] readConsoleInputHelper(java.lang.foreign.MemorySegm
public static INPUT_RECORD[] readConsoleInputHelper(
java.lang.foreign.Arena arena, java.lang.foreign.MemorySegment handle, int count, boolean peek)
throws IOException {
java.lang.foreign.MemorySegment inputRecordPtr = arena.allocateArray(INPUT_RECORD.LAYOUT, count);
java.lang.foreign.MemorySegment length = arena.allocate(java.lang.foreign.ValueLayout.JAVA_INT, 0);
java.lang.foreign.MemorySegment inputRecordPtr = arena.allocate(INPUT_RECORD.LAYOUT, count);
java.lang.foreign.MemorySegment length = arena.allocate(java.lang.foreign.ValueLayout.JAVA_INT, 1);
int res = peek
? PeekConsoleInputW(handle, inputRecordPtr, count, length)
: ReadConsoleInputW(handle, inputRecordPtr, count, length);
Expand Down Expand Up @@ -910,11 +910,13 @@ static <T> T requireNonNull(T obj, String symbolName) {
}

static VarHandle varHandle(java.lang.foreign.MemoryLayout layout, String name) {
return layout.varHandle(java.lang.foreign.MemoryLayout.PathElement.groupElement(name));
return FfmTerminalProvider.lookupVarHandle(
layout, java.lang.foreign.MemoryLayout.PathElement.groupElement(name));
}

static VarHandle varHandle(java.lang.foreign.MemoryLayout layout, String e1, String name) {
return layout.varHandle(
return FfmTerminalProvider.lookupVarHandle(
layout,
java.lang.foreign.MemoryLayout.PathElement.groupElement(e1),
java.lang.foreign.MemoryLayout.PathElement.groupElement(name));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,14 @@
import static org.jline.terminal.impl.ffm.Kernel32.WriteConsoleW;
import static org.jline.terminal.impl.ffm.Kernel32.getLastErrorMessage;

@SuppressWarnings("preview")
class NativeWinConsoleWriter extends AbstractWindowsConsoleWriter {

private final java.lang.foreign.MemorySegment console = GetStdHandle(STD_OUTPUT_HANDLE);

@Override
protected void writeConsole(char[] text, int len) throws IOException {
try (java.lang.foreign.Arena arena = java.lang.foreign.Arena.ofConfined()) {
java.lang.foreign.MemorySegment txt = arena.allocateArray(ValueLayout.JAVA_CHAR, text);
java.lang.foreign.MemorySegment txt = arena.allocateFrom(ValueLayout.JAVA_CHAR, text);
if (WriteConsoleW(console, txt, len, MemorySegment.NULL, MemorySegment.NULL) == 0) {
throw new IOException("Failed to write to console: " + getLastErrorMessage());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import static org.jline.terminal.impl.ffm.Kernel32.getLastErrorMessage;
import static org.jline.terminal.impl.ffm.Kernel32.readConsoleInputHelper;

@SuppressWarnings("preview")
public class NativeWinSysTerminal extends AbstractWindowsTerminal<java.lang.foreign.MemorySegment> {

public static NativeWinSysTerminal createTerminal(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import static org.jline.terminal.impl.ffm.Kernel32.SetConsoleTitleW;
import static org.jline.terminal.impl.ffm.Kernel32.getLastErrorMessage;

@SuppressWarnings("preview")
class WindowsAnsiWriter extends AnsiWriter {

private static final java.lang.foreign.MemorySegment console = GetStdHandle(STD_OUTPUT_HANDLE);
Expand Down Expand Up @@ -401,7 +400,7 @@ protected void processDeleteLine(int optionInt) throws IOException {
@Override
protected void processChangeWindowTitle(String title) {
try (java.lang.foreign.Arena session = java.lang.foreign.Arena.ofConfined()) {
java.lang.foreign.MemorySegment str = session.allocateUtf8String(title);
java.lang.foreign.MemorySegment str = session.allocateFrom(title);
SetConsoleTitleW(str);
}
}
Expand Down

0 comments on commit 9393ed6

Please sign in to comment.