undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
359
test/langtools/jdk/jshell/UITesting.java
Normal file
359
test/langtools/jdk/jshell/UITesting.java
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
/*
|
||||
* Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintStream;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jdk.jshell.JShell;
|
||||
import jdk.jshell.tool.JavaShellToolBuilder;
|
||||
|
||||
public class UITesting {
|
||||
|
||||
protected static final String TAB = "\011";
|
||||
protected static final String INTERRUPT = "\u0003";
|
||||
protected static final String BELL = "\u0007";
|
||||
protected static final String PROMPT = " \u0005";
|
||||
protected static final String CONTINUATION_PROMPT = " \u0006";
|
||||
protected static final String REDRAW_PROMPT = "\n\r?" + PROMPT;
|
||||
protected static final String UP = "\033[A";
|
||||
protected static final String DOWN = "\033[B";
|
||||
protected static final String CTRL_D = "\u0004";
|
||||
protected static final String ESC_DEL = "\u001B\u007F"; // ESC + DEL (common Alt+Backspace)
|
||||
protected static final String ESC_B = "\u001Bb"; // ESC + b (common Alt+b)
|
||||
protected static final String ESC_F = "\u001Bf"; // ESC + f (common Alt+f)
|
||||
protected static final String ESC_D = "\u001Bd"; // ESC + d (common Alt+d)
|
||||
private final boolean laxLineEndings;
|
||||
|
||||
public UITesting() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public UITesting(boolean laxLineEndings) {
|
||||
this.laxLineEndings = laxLineEndings;
|
||||
}
|
||||
|
||||
protected void doRunTest(UITest test) throws Exception {
|
||||
doRunTest(test, true);
|
||||
}
|
||||
|
||||
protected void doRunTest(UITest test, boolean setUserInput) throws Exception {
|
||||
// turn on logging of launch failures
|
||||
Logger.getLogger("jdk.jshell.execution").setLevel(Level.ALL);
|
||||
|
||||
PipeInputStream input = new PipeInputStream();
|
||||
StringBuilder out = new StringBuilder();
|
||||
PrintStream outS = new PrintStream(new OutputStream() {
|
||||
@Override public void write(int b) throws IOException {
|
||||
synchronized (out) {
|
||||
System.out.print((char) b);
|
||||
out.append((char) b);
|
||||
out.notifyAll();
|
||||
}
|
||||
}
|
||||
@Override public void write(byte[] b, int off, int len) throws IOException {
|
||||
synchronized (out) {
|
||||
String data = new String(b, off, len);
|
||||
System.out.print(data);
|
||||
out.append(data);
|
||||
out.notifyAll();
|
||||
}
|
||||
}
|
||||
});
|
||||
Thread runner = new Thread(() -> {
|
||||
try {
|
||||
JavaShellToolBuilder.builder()
|
||||
.in(input, setUserInput ? input : null)
|
||||
.out(outS)
|
||||
.err(outS)
|
||||
.promptCapture(true)
|
||||
.persistence(new HashMap<>())
|
||||
.locale(Locale.US)
|
||||
.run("--no-startup",
|
||||
"--execution", Presets.TEST_DEFAULT_EXECUTION);
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
});
|
||||
|
||||
Writer inputSink = new OutputStreamWriter(input.createOutput(), StandardCharsets.UTF_8) {
|
||||
boolean closed = false;
|
||||
@Override
|
||||
public void write(String str) throws IOException {
|
||||
if (closed) return; // prevents exception thrown due to closed writer
|
||||
super.write(str);
|
||||
flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
closed = true;
|
||||
}
|
||||
};
|
||||
|
||||
runner.start();
|
||||
|
||||
try {
|
||||
Class<?> jshellToolClass = Class.forName("jdk.internal.jshell.tool.JShellTool");
|
||||
Field promptField = jshellToolClass.getDeclaredField("PROMPT");
|
||||
promptField.setAccessible(true);
|
||||
promptField.set(null, PROMPT);
|
||||
Field continuationPromptField = jshellToolClass.getDeclaredField("CONTINUATION_PROMPT");
|
||||
continuationPromptField.setAccessible(true);
|
||||
continuationPromptField.set(null, CONTINUATION_PROMPT);
|
||||
waitOutput(out, PROMPT);
|
||||
test.test(inputSink, out);
|
||||
} finally {
|
||||
inputSink.write(INTERRUPT + INTERRUPT + "/exit\n");
|
||||
|
||||
runner.join();
|
||||
}
|
||||
}
|
||||
|
||||
protected interface UITest {
|
||||
public void test(Writer inputSink, StringBuilder out) throws Exception;
|
||||
}
|
||||
|
||||
private static final long TIMEOUT = (long) (60_000 * Double.parseDouble(System.getProperty("test.timeout.factor", "1.0")));
|
||||
|
||||
protected void waitOutput(StringBuilder out, String expected) {
|
||||
waitOutput(out, expected, null);
|
||||
}
|
||||
|
||||
// Return true if expected is found, false if secondary is found,
|
||||
// otherwise, time out with an IllegalStateException
|
||||
protected boolean waitOutput(StringBuilder out, String expected, String secondary) {
|
||||
expected = expected.replaceAll("\n", laxLineEndings ? "\r*\n" : System.getProperty("line.separator"));
|
||||
Pattern expectedPattern = Pattern.compile(expected, Pattern.DOTALL);
|
||||
Pattern secondaryPattern = null;
|
||||
if (secondary != null) {
|
||||
secondary = secondary.replaceAll("\n", laxLineEndings ? "\r*\n" : System.getProperty("line.separator"));
|
||||
secondaryPattern = Pattern.compile(secondary, Pattern.DOTALL);
|
||||
}
|
||||
synchronized (out) {
|
||||
long s = System.currentTimeMillis();
|
||||
|
||||
while (true) {
|
||||
Matcher m = expectedPattern.matcher(out);
|
||||
if (m.find()) {
|
||||
out.delete(0, m.end());
|
||||
return true;
|
||||
}
|
||||
if (secondaryPattern != null) {
|
||||
m = secondaryPattern.matcher(out);
|
||||
if (m.find()) {
|
||||
out.delete(0, m.end());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
long e = System.currentTimeMillis();
|
||||
if ((e - s) > TIMEOUT) {
|
||||
throw new IllegalStateException("Timeout waiting for: " + quote(expected) + ", actual output so far: " + quote(out.toString()));
|
||||
}
|
||||
try {
|
||||
out.wait(TIMEOUT);
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String quote(String original) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
for (char c : original.toCharArray()) {
|
||||
if (c < 32) {
|
||||
output.append(String.format("\\u%04X", (int) c));
|
||||
} else {
|
||||
output.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
protected String clearOut(String what) {
|
||||
return backspace(what.length()) + "\\u001B\\[K";
|
||||
}
|
||||
|
||||
protected String backspace(int n) {
|
||||
return fill(n, '\010');
|
||||
}
|
||||
|
||||
protected String space(int n) {
|
||||
return fill(n, ' ');
|
||||
}
|
||||
|
||||
private String fill(int n, char c) {
|
||||
StringBuilder result = new StringBuilder(n);
|
||||
|
||||
while (n-- > 0)
|
||||
result.append(c);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private final ResourceBundle resources;
|
||||
{
|
||||
resources = ResourceBundle.getBundle("jdk.internal.jshell.tool.resources.l10n", Locale.US, JShell.class.getModule());
|
||||
}
|
||||
|
||||
protected String getResource(String key) {
|
||||
return resources.getString(key);
|
||||
}
|
||||
|
||||
protected String resource(String key) {
|
||||
return patternQuote(getResource(key));
|
||||
}
|
||||
|
||||
protected String patternQuote(String str) {
|
||||
//from JDK-6507804:
|
||||
return str.replaceAll("([\\\\\\[\\].^$?*+{}()|])", "\\\\$1");
|
||||
}
|
||||
|
||||
protected String getMessage(String key, Object... args) {
|
||||
return MessageFormat.format(resources.getString(key), args);
|
||||
}
|
||||
private static class PipeInputStream extends InputStream {
|
||||
|
||||
private static final int INITIAL_SIZE = 128;
|
||||
private int[] buffer = new int[INITIAL_SIZE];
|
||||
private int start;
|
||||
private int end;
|
||||
private boolean closed;
|
||||
|
||||
@Override
|
||||
public synchronized int read() throws IOException {
|
||||
if (start == end && !closed) {
|
||||
inputNeeded();
|
||||
}
|
||||
while (start == end) {
|
||||
if (closed) {
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
try {
|
||||
return buffer[start];
|
||||
} finally {
|
||||
start = (start + 1) % buffer.length;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int read(byte[] b, int off, int len) throws IOException {
|
||||
if (b == null) {
|
||||
throw new NullPointerException();
|
||||
} else if (off < 0 || len < 0 || len > b.length - off) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int c = read();
|
||||
if (c == -1) {
|
||||
return -1;
|
||||
}
|
||||
b[off] = (byte)c;
|
||||
|
||||
int totalRead = 1;
|
||||
while (totalRead < len && start != end) {
|
||||
int r = read();
|
||||
if (r == (-1))
|
||||
break;
|
||||
b[off + totalRead++] = (byte) r;
|
||||
}
|
||||
return totalRead;
|
||||
}
|
||||
|
||||
protected void inputNeeded() throws IOException {}
|
||||
|
||||
private synchronized void write(int b) {
|
||||
if (closed) {
|
||||
throw new IllegalStateException("Already closed.");
|
||||
}
|
||||
int newEnd = (end + 1) % buffer.length;
|
||||
if (newEnd == start) {
|
||||
//overflow:
|
||||
int[] newBuffer = new int[buffer.length * 2];
|
||||
int rightPart = (end > start ? end : buffer.length) - start;
|
||||
int leftPart = end > start ? 0 : start - 1;
|
||||
System.arraycopy(buffer, start, newBuffer, 0, rightPart);
|
||||
System.arraycopy(buffer, 0, newBuffer, rightPart, leftPart);
|
||||
buffer = newBuffer;
|
||||
start = 0;
|
||||
end = rightPart + leftPart;
|
||||
newEnd = end + 1;
|
||||
}
|
||||
buffer[end] = b;
|
||||
end = newEnd;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
closed = true;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
public OutputStream createOutput() {
|
||||
return new OutputStream() {
|
||||
@Override public void write(int b) throws IOException {
|
||||
PipeInputStream.this.write(b);
|
||||
}
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
for (int i = 0 ; i < len ; i++) {
|
||||
write(Byte.toUnsignedInt(b[off + i]));
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
PipeInputStream.this.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue