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.wasm.runtime.WasmSupport;
|
||||
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.StdoutOutputStream;
|
||||
import org.teavm.classlib.java.io.TConsole;
|
||||
|
@ -55,14 +57,22 @@ public final class TSystem extends TObject {
|
|||
|
||||
public static TPrintStream out() {
|
||||
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;
|
||||
}
|
||||
|
||||
public static TPrintStream err() {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -397,55 +397,30 @@ let $rt_assertNotNaN = value => {
|
|||
}
|
||||
return value;
|
||||
}
|
||||
let $rt_createOutputFunction = printFunction => {
|
||||
|
||||
let $rt_createOutputFunction = outputFunction => {
|
||||
let buffer = "";
|
||||
let utf8Buffer = 0;
|
||||
let utf8Remaining = 0;
|
||||
|
||||
let putCodePoint = ch =>{
|
||||
if (ch === 0xA) {
|
||||
printFunction(buffer);
|
||||
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);
|
||||
}
|
||||
return msg => {
|
||||
let index = 0;
|
||||
while (true) {
|
||||
let next = msg.indexOf('\n', index);
|
||||
if (next < 0) {
|
||||
break;
|
||||
}
|
||||
} else if ((ch & 0xE0) === 0xC0) {
|
||||
utf8Remaining = ch & 0x1F;
|
||||
utf8Buffer = 1;
|
||||
} else if ((ch & 0xF0) === 0xE0) {
|
||||
utf8Remaining = ch & 0x0F;
|
||||
utf8Buffer = 2;
|
||||
} else if ((ch & 0xF8) === 0xF0) {
|
||||
utf8Remaining = ch & 0x07;
|
||||
utf8Buffer = 3;
|
||||
outputFunction(buffer + msg.substring(index, next));
|
||||
buffer = "";
|
||||
index = next + 1;
|
||||
}
|
||||
};
|
||||
buffer += msg.substring(index);
|
||||
}
|
||||
}
|
||||
|
||||
let $rt_putStdout = typeof teavm_globals.$rt_putStdoutCustom === "function"
|
||||
? 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"
|
||||
? 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_packages = data => {
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
let $rt_stdoutBuffer = "";
|
||||
function $rt_putStdoutCustom(ch) {
|
||||
if (ch === 0xA) {
|
||||
var lineElem = document.createElement("div");
|
||||
var stdoutElem = document.getElementById("stdout");
|
||||
lineElem.appendChild(document.createTextNode($rt_stdoutBuffer));
|
||||
function $rt_putStdoutCustom(str) {
|
||||
let index = 0;
|
||||
while (true) {
|
||||
let next = msg.indexOf('\n', index);
|
||||
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);
|
||||
$rt_stdoutBuffer = "";
|
||||
stdoutElem.scrollTop = stdoutElem.scrollHeight;
|
||||
} else {
|
||||
$rt_stdoutBuffer += String.fromCharCode(ch);
|
||||
buffer = "";
|
||||
index = next + 1;
|
||||
}
|
||||
}
|
||||
this.$rt_putStdoutCustom = $rt_putStdoutCustom;
|
|
@ -188,43 +188,17 @@ let $rt_putStderrCustom = createOutputFunction(msg => {
|
|||
|
||||
function createOutputFunction(printFunction) {
|
||||
let buffer = "";
|
||||
let utf8Buffer = 0;
|
||||
let utf8Remaining = 0;
|
||||
|
||||
function putCodePoint(ch) {
|
||||
if (ch === 0xA) {
|
||||
printFunction(buffer);
|
||||
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);
|
||||
}
|
||||
return msg => {
|
||||
let index = 0;
|
||||
while (true) {
|
||||
let next = msg.indexOf('\n', index);
|
||||
if (next < 0) {
|
||||
break;
|
||||
}
|
||||
} else if ((ch & 0xE0) === 0xC0) {
|
||||
utf8Remaining = ch & 0x1F;
|
||||
utf8Buffer = 1;
|
||||
} else if ((ch & 0xF0) === 0xE0) {
|
||||
utf8Remaining = ch & 0x0F;
|
||||
utf8Buffer = 2;
|
||||
} else if ((ch & 0xF8) === 0xF0) {
|
||||
utf8Remaining = ch & 0x07;
|
||||
utf8Buffer = 3;
|
||||
printFunction(buffer + msg.substring(index, next));
|
||||
buffer = "";
|
||||
index = next + 1;
|
||||
}
|
||||
};
|
||||
buffer += msg.substring(index);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user