mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
classlib: optimize console output in JS backend
This commit is contained in:
parent
0ee994e913
commit
9c6f23d280
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.classlib.impl.console;
|
||||||
|
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
|
||||||
|
public class JSStderrPrintStream extends JsConsolePrintStream {
|
||||||
|
@Override
|
||||||
|
public void print(String s) {
|
||||||
|
writeJs(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSBody(params = "b", script = "$rt_putStderr(b);")
|
||||||
|
private static native void writeJs(String s);
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.classlib.impl.console;
|
||||||
|
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
|
||||||
|
public class JSStdoutPrintStream extends JsConsolePrintStream {
|
||||||
|
@Override
|
||||||
|
public void print(String s) {
|
||||||
|
writeJs(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSBody(params = "b", script = "$rt_putStdout(b);")
|
||||||
|
private static native void writeJs(String s);
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.classlib.impl.console;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.charset.CharsetDecoder;
|
||||||
|
import java.nio.charset.CoderResult;
|
||||||
|
import java.nio.charset.CodingErrorAction;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public abstract class JsConsolePrintStream extends PrintStream {
|
||||||
|
private ByteEncoder byteEncoder;
|
||||||
|
private Runnable flushAction;
|
||||||
|
|
||||||
|
public JsConsolePrintStream() {
|
||||||
|
super(new ByteArrayOutputStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void println(String s) {
|
||||||
|
print(s);
|
||||||
|
print("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void print(int i) {
|
||||||
|
print(Integer.toString(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void print(char c) {
|
||||||
|
print(Character.toString(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void print(long l) {
|
||||||
|
print(Long.toString(l));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void print(double d) {
|
||||||
|
super.print(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void print(Object s) {
|
||||||
|
print(Objects.toString(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void print(char[] s) {
|
||||||
|
print(new String(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void println() {
|
||||||
|
print("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void println(int i) {
|
||||||
|
println(Integer.toString(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void println(char c) {
|
||||||
|
println(Character.toString(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void println(long l) {
|
||||||
|
println(Long.toString(l));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void println(float d) {
|
||||||
|
println(Float.toString(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void println(double d) {
|
||||||
|
println(Double.toString(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void println(boolean b) {
|
||||||
|
println(Boolean.toString(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void println(Object s) {
|
||||||
|
println(Objects.toString(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) {
|
||||||
|
ensureByteEncoder().write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b, int off, int len) {
|
||||||
|
ensureByteEncoder().write(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() {
|
||||||
|
if (flushAction != null) {
|
||||||
|
flushAction = null;
|
||||||
|
flushAction.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ByteEncoder ensureByteEncoder() {
|
||||||
|
if (byteEncoder == null) {
|
||||||
|
byteEncoder = new ByteEncoder();
|
||||||
|
}
|
||||||
|
return byteEncoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract void print(String s);
|
||||||
|
|
||||||
|
private class ByteEncoder {
|
||||||
|
private ByteBuffer buffer = ByteBuffer.wrap(new byte[32]);
|
||||||
|
private char[] outChars = new char[32];
|
||||||
|
private CharBuffer outBuffer = CharBuffer.wrap(outChars);
|
||||||
|
private CharsetDecoder decoder = StandardCharsets.UTF_8
|
||||||
|
.newDecoder()
|
||||||
|
.onMalformedInput(CodingErrorAction.REPLACE)
|
||||||
|
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||||
|
|
||||||
|
void write(int b) {
|
||||||
|
postponeFlush();
|
||||||
|
if (!buffer.hasRemaining()) {
|
||||||
|
output();
|
||||||
|
}
|
||||||
|
buffer.put((byte) b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(byte[] b, int off, int len) {
|
||||||
|
postponeFlush();
|
||||||
|
while (len > 0) {
|
||||||
|
var count = Math.min(len, buffer.remaining());
|
||||||
|
buffer.put(b, off, count);
|
||||||
|
len -= count;
|
||||||
|
output();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void postponeFlush() {
|
||||||
|
if (flushAction == null) {
|
||||||
|
flushAction = this::flush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flush() {
|
||||||
|
if (buffer.position() > 0) {
|
||||||
|
output(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void output() {
|
||||||
|
output(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void output(boolean endOfInput) {
|
||||||
|
buffer.flip();
|
||||||
|
while (true) {
|
||||||
|
var result = decoder.decode(buffer, outBuffer, endOfInput);
|
||||||
|
print(new String(outChars, 0, outBuffer.position()));
|
||||||
|
outBuffer.rewind();
|
||||||
|
if (result != CoderResult.OVERFLOW) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.compact();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,8 @@ import org.teavm.backend.c.runtime.fs.CFileSystem;
|
||||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
import org.teavm.backend.wasm.runtime.WasmSupport;
|
import org.teavm.backend.wasm.runtime.WasmSupport;
|
||||||
import org.teavm.classlib.PlatformDetector;
|
import org.teavm.classlib.PlatformDetector;
|
||||||
|
import org.teavm.classlib.impl.console.JSStderrPrintStream;
|
||||||
|
import org.teavm.classlib.impl.console.JSStdoutPrintStream;
|
||||||
import org.teavm.classlib.impl.console.StderrOutputStream;
|
import org.teavm.classlib.impl.console.StderrOutputStream;
|
||||||
import org.teavm.classlib.impl.console.StdoutOutputStream;
|
import org.teavm.classlib.impl.console.StdoutOutputStream;
|
||||||
import org.teavm.classlib.java.io.TConsole;
|
import org.teavm.classlib.java.io.TConsole;
|
||||||
|
@ -55,14 +57,22 @@ public final class TSystem extends TObject {
|
||||||
|
|
||||||
public static TPrintStream out() {
|
public static TPrintStream out() {
|
||||||
if (outCache == null) {
|
if (outCache == null) {
|
||||||
outCache = new TPrintStream((TOutputStream) (Object) StdoutOutputStream.INSTANCE, false);
|
if (PlatformDetector.isJavaScript()) {
|
||||||
|
outCache = (TPrintStream) (Object) new JSStdoutPrintStream();
|
||||||
|
} else {
|
||||||
|
outCache = new TPrintStream((TOutputStream) (Object) StdoutOutputStream.INSTANCE, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return outCache;
|
return outCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TPrintStream err() {
|
public static TPrintStream err() {
|
||||||
if (errCache == null) {
|
if (errCache == null) {
|
||||||
errCache = new TPrintStream((TOutputStream) (Object) StderrOutputStream.INSTANCE, false);
|
if (PlatformDetector.isJavaScript()) {
|
||||||
|
errCache = (TPrintStream) (Object) new JSStderrPrintStream();
|
||||||
|
} else {
|
||||||
|
errCache = new TPrintStream((TOutputStream) (Object) StderrOutputStream.INSTANCE, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return errCache;
|
return errCache;
|
||||||
}
|
}
|
||||||
|
|
|
@ -397,55 +397,30 @@ let $rt_assertNotNaN = value => {
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
let $rt_createOutputFunction = printFunction => {
|
|
||||||
|
let $rt_createOutputFunction = outputFunction => {
|
||||||
let buffer = "";
|
let buffer = "";
|
||||||
let utf8Buffer = 0;
|
return msg => {
|
||||||
let utf8Remaining = 0;
|
let index = 0;
|
||||||
|
while (true) {
|
||||||
let putCodePoint = ch =>{
|
let next = msg.indexOf('\n', index);
|
||||||
if (ch === 0xA) {
|
if (next < 0) {
|
||||||
printFunction(buffer);
|
break;
|
||||||
buffer = "";
|
|
||||||
} else if (ch < 0x10000) {
|
|
||||||
buffer += String.fromCharCode(ch);
|
|
||||||
} else {
|
|
||||||
ch = (ch - 0x10000) | 0;
|
|
||||||
let hi = (ch >> 10) + 0xD800;
|
|
||||||
let lo = (ch & 0x3FF) + 0xDC00;
|
|
||||||
buffer += String.fromCharCode(hi, lo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ch => {
|
|
||||||
if ((ch & 0x80) === 0) {
|
|
||||||
putCodePoint(ch);
|
|
||||||
} else if ((ch & 0xC0) === 0x80) {
|
|
||||||
if (utf8Buffer > 0) {
|
|
||||||
utf8Remaining <<= 6;
|
|
||||||
utf8Remaining |= ch & 0x3F;
|
|
||||||
if (--utf8Buffer === 0) {
|
|
||||||
putCodePoint(utf8Remaining);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if ((ch & 0xE0) === 0xC0) {
|
outputFunction(buffer + msg.substring(index, next));
|
||||||
utf8Remaining = ch & 0x1F;
|
buffer = "";
|
||||||
utf8Buffer = 1;
|
index = next + 1;
|
||||||
} else if ((ch & 0xF0) === 0xE0) {
|
|
||||||
utf8Remaining = ch & 0x0F;
|
|
||||||
utf8Buffer = 2;
|
|
||||||
} else if ((ch & 0xF8) === 0xF0) {
|
|
||||||
utf8Remaining = ch & 0x07;
|
|
||||||
utf8Buffer = 3;
|
|
||||||
}
|
}
|
||||||
};
|
buffer += msg.substring(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let $rt_putStdout = typeof teavm_globals.$rt_putStdoutCustom === "function"
|
let $rt_putStdout = typeof teavm_globals.$rt_putStdoutCustom === "function"
|
||||||
? teavm_globals.$rt_putStdoutCustom
|
? teavm_globals.$rt_putStdoutCustom
|
||||||
: typeof console === "object" ? $rt_createOutputFunction(function(msg) { console.info(msg); }) : function() {};
|
: typeof console === "object" ? $rt_createOutputFunction(msg => console.info(msg)) : () => {};
|
||||||
let $rt_putStderr = typeof teavm_globals.$rt_putStderrCustom === "function"
|
let $rt_putStderr = typeof teavm_globals.$rt_putStderrCustom === "function"
|
||||||
? teavm_globals.$rt_putStderrCustom
|
? teavm_globals.$rt_putStderrCustom
|
||||||
: typeof console === "object" ? $rt_createOutputFunction(function(msg) { console.error(msg); }) : function() {};
|
: typeof console === "object" ? $rt_createOutputFunction(msg => console.error(msg)) : () => {};
|
||||||
|
|
||||||
let $rt_packageData = null;
|
let $rt_packageData = null;
|
||||||
let $rt_packages = data => {
|
let $rt_packages = data => {
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
let $rt_stdoutBuffer = "";
|
let $rt_stdoutBuffer = "";
|
||||||
function $rt_putStdoutCustom(ch) {
|
function $rt_putStdoutCustom(str) {
|
||||||
if (ch === 0xA) {
|
let index = 0;
|
||||||
var lineElem = document.createElement("div");
|
while (true) {
|
||||||
var stdoutElem = document.getElementById("stdout");
|
let next = msg.indexOf('\n', index);
|
||||||
lineElem.appendChild(document.createTextNode($rt_stdoutBuffer));
|
if (next < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let line = buffer + msg.substring(index, next);
|
||||||
|
let lineElem = document.createElement("div");
|
||||||
|
let stdoutElem = document.getElementById("stdout");
|
||||||
|
lineElem.appendChild(document.createTextNode(line));
|
||||||
stdoutElem.appendChild(lineElem);
|
stdoutElem.appendChild(lineElem);
|
||||||
$rt_stdoutBuffer = "";
|
buffer = "";
|
||||||
stdoutElem.scrollTop = stdoutElem.scrollHeight;
|
index = next + 1;
|
||||||
} else {
|
|
||||||
$rt_stdoutBuffer += String.fromCharCode(ch);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.$rt_putStdoutCustom = $rt_putStdoutCustom;
|
this.$rt_putStdoutCustom = $rt_putStdoutCustom;
|
|
@ -188,43 +188,17 @@ let $rt_putStderrCustom = createOutputFunction(msg => {
|
||||||
|
|
||||||
function createOutputFunction(printFunction) {
|
function createOutputFunction(printFunction) {
|
||||||
let buffer = "";
|
let buffer = "";
|
||||||
let utf8Buffer = 0;
|
return msg => {
|
||||||
let utf8Remaining = 0;
|
let index = 0;
|
||||||
|
while (true) {
|
||||||
function putCodePoint(ch) {
|
let next = msg.indexOf('\n', index);
|
||||||
if (ch === 0xA) {
|
if (next < 0) {
|
||||||
printFunction(buffer);
|
break;
|
||||||
buffer = "";
|
|
||||||
} else if (ch < 0x10000) {
|
|
||||||
buffer += String.fromCharCode(ch);
|
|
||||||
} else {
|
|
||||||
ch = (ch - 0x10000) | 0;
|
|
||||||
var hi = (ch >> 10) + 0xD800;
|
|
||||||
var lo = (ch & 0x3FF) + 0xDC00;
|
|
||||||
buffer += String.fromCharCode(hi, lo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ch => {
|
|
||||||
if ((ch & 0x80) === 0) {
|
|
||||||
putCodePoint(ch);
|
|
||||||
} else if ((ch & 0xC0) === 0x80) {
|
|
||||||
if (utf8Buffer > 0) {
|
|
||||||
utf8Remaining <<= 6;
|
|
||||||
utf8Remaining |= ch & 0x3F;
|
|
||||||
if (--utf8Buffer === 0) {
|
|
||||||
putCodePoint(utf8Remaining);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if ((ch & 0xE0) === 0xC0) {
|
printFunction(buffer + msg.substring(index, next));
|
||||||
utf8Remaining = ch & 0x1F;
|
buffer = "";
|
||||||
utf8Buffer = 1;
|
index = next + 1;
|
||||||
} else if ((ch & 0xF0) === 0xE0) {
|
|
||||||
utf8Remaining = ch & 0x0F;
|
|
||||||
utf8Buffer = 2;
|
|
||||||
} else if ((ch & 0xF8) === 0xF0) {
|
|
||||||
utf8Remaining = ch & 0x07;
|
|
||||||
utf8Buffer = 3;
|
|
||||||
}
|
}
|
||||||
};
|
buffer += msg.substring(index);
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user