mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Wasm backend: make JUnit tests work
This commit is contained in:
parent
f532801f38
commit
b087610c2c
|
@ -242,7 +242,7 @@ public class TCharacter extends TObject implements TComparable<TCharacter> {
|
||||||
return toLowerCaseSystem(codePoint);
|
return toLowerCaseSystem(codePoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Import(module = "runtime", name = "towlower")
|
@Import(module = "teavm", name = "towlower")
|
||||||
private static native int toLowerCaseSystem(int codePoint);
|
private static native int toLowerCaseSystem(int codePoint);
|
||||||
|
|
||||||
public static char toUpperCase(char ch) {
|
public static char toUpperCase(char ch) {
|
||||||
|
@ -258,7 +258,7 @@ public class TCharacter extends TObject implements TComparable<TCharacter> {
|
||||||
return toUpperCaseSystem(codePoint);
|
return toUpperCaseSystem(codePoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Import(module = "runtime", name = "towupper")
|
@Import(module = "teavm", name = "towupper")
|
||||||
private static native int toUpperCaseSystem(int codePoint);
|
private static native int toUpperCaseSystem(int codePoint);
|
||||||
|
|
||||||
public static int digit(char ch, int radix) {
|
public static int digit(char ch, int radix) {
|
||||||
|
|
|
@ -32,6 +32,6 @@ class TConsoleOutputStreamStderr extends TOutputStream {
|
||||||
writeImpl(b);
|
writeImpl(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Import(name = "putwchar", module = "runtime")
|
@Import(name = "putwchar", module = "teavm")
|
||||||
static native void writeImpl(int b);
|
static native void writeImpl(int b);
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,15 +222,15 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@JSBody(params = "v", script = "return isNaN(v);")
|
@JSBody(params = "v", script = "return isNaN(v);")
|
||||||
@Import(module = "runtime", name = "isnan")
|
@Import(module = "teavm", name = "isnan")
|
||||||
public static native boolean isNaN(double v);
|
public static native boolean isNaN(double v);
|
||||||
|
|
||||||
@JSBody(script = "return NaN;")
|
@JSBody(script = "return NaN;")
|
||||||
@Import(module = "runtime", name = "TeaVM_getNaN")
|
@Import(module = "teavm", name = "TeaVM_getNaN")
|
||||||
private static native double getNaN();
|
private static native double getNaN();
|
||||||
|
|
||||||
@JSBody(params = "v", script = "return !isFinite(v);")
|
@JSBody(params = "v", script = "return !isFinite(v);")
|
||||||
@Import(module = "runtime", name = "isinf")
|
@Import(module = "teavm", name = "isinf")
|
||||||
public static native boolean isInfinite(double v);
|
public static native boolean isInfinite(double v);
|
||||||
|
|
||||||
public static long doubleToRawLongBits(double value) {
|
public static long doubleToRawLongBits(double value) {
|
||||||
|
|
|
@ -90,7 +90,7 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@JSBody(params = "v", script = "return isNaN(v);")
|
@JSBody(params = "v", script = "return isNaN(v);")
|
||||||
@Import(module = "runtime", name = "isnan")
|
@Import(module = "teavm", name = "isnan")
|
||||||
public static native boolean isNaN(float v);
|
public static native boolean isNaN(float v);
|
||||||
|
|
||||||
public static boolean isInfinite(float v) {
|
public static boolean isInfinite(float v) {
|
||||||
|
@ -98,11 +98,11 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@JSBody(params = "v", script = "return isFinite(v);")
|
@JSBody(params = "v", script = "return isFinite(v);")
|
||||||
@Import(module = "runtime", name = "isfinite")
|
@Import(module = "teavm", name = "isfinite")
|
||||||
private static native boolean isFinite(float v);
|
private static native boolean isFinite(float v);
|
||||||
|
|
||||||
@JSBody(script = "return NaN;")
|
@JSBody(script = "return NaN;")
|
||||||
@Import(module = "runtime", name = "TeaVM_getNaN")
|
@Import(module = "teavm", name = "TeaVM_getNaN")
|
||||||
private static native float getNaN();
|
private static native float getNaN();
|
||||||
|
|
||||||
public static float parseFloat(TString string) throws TNumberFormatException {
|
public static float parseFloat(TString string) throws TNumberFormatException {
|
||||||
|
|
|
@ -26,27 +26,27 @@ public final class TMath extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GeneratedBy(MathNativeGenerator.class)
|
@GeneratedBy(MathNativeGenerator.class)
|
||||||
@Import(module = "math", name = "sin")
|
@Import(module = "teavmMath", name = "sin")
|
||||||
public static native double sin(double a);
|
public static native double sin(double a);
|
||||||
|
|
||||||
@GeneratedBy(MathNativeGenerator.class)
|
@GeneratedBy(MathNativeGenerator.class)
|
||||||
@Import(module = "math", name = "cos")
|
@Import(module = "teavmMath", name = "cos")
|
||||||
public static native double cos(double a);
|
public static native double cos(double a);
|
||||||
|
|
||||||
@GeneratedBy(MathNativeGenerator.class)
|
@GeneratedBy(MathNativeGenerator.class)
|
||||||
@Import(module = "math", name = "tan")
|
@Import(module = "teavmMath", name = "tan")
|
||||||
public static native double tan(double a);
|
public static native double tan(double a);
|
||||||
|
|
||||||
@GeneratedBy(MathNativeGenerator.class)
|
@GeneratedBy(MathNativeGenerator.class)
|
||||||
@Import(module = "math", name = "asin")
|
@Import(module = "teavmMath", name = "asin")
|
||||||
public static native double asin(double a);
|
public static native double asin(double a);
|
||||||
|
|
||||||
@GeneratedBy(MathNativeGenerator.class)
|
@GeneratedBy(MathNativeGenerator.class)
|
||||||
@Import(module = "math", name = "acos")
|
@Import(module = "teavmMath", name = "acos")
|
||||||
public static native double acos(double a);
|
public static native double acos(double a);
|
||||||
|
|
||||||
@GeneratedBy(MathNativeGenerator.class)
|
@GeneratedBy(MathNativeGenerator.class)
|
||||||
@Import(module = "math", name = "atan")
|
@Import(module = "teavmMath", name = "atan")
|
||||||
public static native double atan(double a);
|
public static native double atan(double a);
|
||||||
|
|
||||||
public static double toRadians(double angdeg) {
|
public static double toRadians(double angdeg) {
|
||||||
|
@ -58,11 +58,11 @@ public final class TMath extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GeneratedBy(MathNativeGenerator.class)
|
@GeneratedBy(MathNativeGenerator.class)
|
||||||
@Import(module = "math", name = "exp")
|
@Import(module = "teavmMath", name = "exp")
|
||||||
public static native double exp(double a);
|
public static native double exp(double a);
|
||||||
|
|
||||||
@GeneratedBy(MathNativeGenerator.class)
|
@GeneratedBy(MathNativeGenerator.class)
|
||||||
@Import(module = "math", name = "log")
|
@Import(module = "teavmMath", name = "log")
|
||||||
public static native double log(double a);
|
public static native double log(double a);
|
||||||
|
|
||||||
public static double log10(double a) {
|
public static double log10(double a) {
|
||||||
|
@ -70,7 +70,7 @@ public final class TMath extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GeneratedBy(MathNativeGenerator.class)
|
@GeneratedBy(MathNativeGenerator.class)
|
||||||
@Import(module = "math", name = "sqrt")
|
@Import(module = "teavmMath", name = "sqrt")
|
||||||
public static native double sqrt(double a);
|
public static native double sqrt(double a);
|
||||||
|
|
||||||
public static double cbrt(double a) {
|
public static double cbrt(double a) {
|
||||||
|
@ -83,15 +83,15 @@ public final class TMath extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GeneratedBy(MathNativeGenerator.class)
|
@GeneratedBy(MathNativeGenerator.class)
|
||||||
@Import(module = "math", name = "ceil")
|
@Import(module = "teavmMath", name = "ceil")
|
||||||
public static native double ceil(double a);
|
public static native double ceil(double a);
|
||||||
|
|
||||||
@GeneratedBy(MathNativeGenerator.class)
|
@GeneratedBy(MathNativeGenerator.class)
|
||||||
@Import(module = "math", name = "floor")
|
@Import(module = "teavmMath", name = "floor")
|
||||||
public static native double floor(double a);
|
public static native double floor(double a);
|
||||||
|
|
||||||
@GeneratedBy(MathNativeGenerator.class)
|
@GeneratedBy(MathNativeGenerator.class)
|
||||||
@Import(module = "math", name = "pow")
|
@Import(module = "teavmMath", name = "pow")
|
||||||
public static native double pow(double x, double y);
|
public static native double pow(double x, double y);
|
||||||
|
|
||||||
public static double rint(double a) {
|
public static double rint(double a) {
|
||||||
|
@ -99,7 +99,7 @@ public final class TMath extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GeneratedBy(MathNativeGenerator.class)
|
@GeneratedBy(MathNativeGenerator.class)
|
||||||
@Import(module = "math", name = "atan2")
|
@Import(module = "teavmMath", name = "atan2")
|
||||||
public static native double atan2(double y, double x);
|
public static native double atan2(double y, double x);
|
||||||
|
|
||||||
public static int round(float a) {
|
public static int round(float a) {
|
||||||
|
@ -111,7 +111,7 @@ public final class TMath extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GeneratedBy(MathNativeGenerator.class)
|
@GeneratedBy(MathNativeGenerator.class)
|
||||||
@Import(module = "math", name = "random")
|
@Import(module = "teavmMath", name = "random")
|
||||||
public static native double random();
|
public static native double random();
|
||||||
|
|
||||||
public static int min(int a, int b) {
|
public static int min(int a, int b) {
|
||||||
|
|
|
@ -114,7 +114,7 @@ public final class TSystem extends TObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Import(name = "currentTimeMillis", module = "runtime")
|
@Import(name = "currentTimeMillis", module = "teavm")
|
||||||
private static native double currentTimeMillisWasm();
|
private static native double currentTimeMillisWasm();
|
||||||
|
|
||||||
@Import(name = "currentTimeMillis")
|
@Import(name = "currentTimeMillis")
|
||||||
|
|
|
@ -114,6 +114,6 @@ public class TRandom extends TObject implements TSerializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@JSBody(script = "return Math.random();")
|
@JSBody(script = "return Math.random();")
|
||||||
@Import(module = "math", name = "random")
|
@Import(module = "teavmMath", name = "random")
|
||||||
private static native double random();
|
private static native double random();
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,9 +73,11 @@ public class WasmClassGenerator {
|
||||||
DataPrimitives.ADDRESS, /* item type */
|
DataPrimitives.ADDRESS, /* item type */
|
||||||
DataPrimitives.ADDRESS, /* array type */
|
DataPrimitives.ADDRESS, /* array type */
|
||||||
DataPrimitives.INT, /* isInstance function */
|
DataPrimitives.INT, /* isInstance function */
|
||||||
|
DataPrimitives.INT, /* init function */
|
||||||
DataPrimitives.ADDRESS, /* parent */
|
DataPrimitives.ADDRESS, /* parent */
|
||||||
DataPrimitives.ADDRESS, /* enum values */
|
DataPrimitives.ADDRESS, /* enum values */
|
||||||
DataPrimitives.ADDRESS /* layout */);
|
DataPrimitives.ADDRESS, /* layout */
|
||||||
|
DataPrimitives.ADDRESS /* simple name */);
|
||||||
private IntegerArray staticGcRoots = new IntegerArray(1);
|
private IntegerArray staticGcRoots = new IntegerArray(1);
|
||||||
private int staticGcRootsAddress;
|
private int staticGcRootsAddress;
|
||||||
|
|
||||||
|
@ -87,9 +89,11 @@ public class WasmClassGenerator {
|
||||||
private static final int CLASS_ITEM_TYPE = 6;
|
private static final int CLASS_ITEM_TYPE = 6;
|
||||||
private static final int CLASS_ARRAY_TYPE = 7;
|
private static final int CLASS_ARRAY_TYPE = 7;
|
||||||
private static final int CLASS_IS_INSTANCE = 8;
|
private static final int CLASS_IS_INSTANCE = 8;
|
||||||
private static final int CLASS_PARENT = 9;
|
private static final int CLASS_INIT = 9;
|
||||||
private static final int CLASS_ENUM_VALUES = 10;
|
private static final int CLASS_PARENT = 10;
|
||||||
private static final int CLASS_LAYOUT = 11;
|
private static final int CLASS_ENUM_VALUES = 11;
|
||||||
|
private static final int CLASS_LAYOUT = 12;
|
||||||
|
private static final int CLASS_SIMPLE_NAME = 13;
|
||||||
|
|
||||||
public WasmClassGenerator(ClassReaderSource classSource, VirtualTableProvider vtableProvider,
|
public WasmClassGenerator(ClassReaderSource classSource, VirtualTableProvider vtableProvider,
|
||||||
TagRegistry tagRegistry, BinaryWriter binaryWriter) {
|
TagRegistry tagRegistry, BinaryWriter binaryWriter) {
|
||||||
|
@ -170,6 +174,8 @@ public class WasmClassGenerator {
|
||||||
binaryData.data.setInt(CLASS_IS_INSTANCE, functionTable.size());
|
binaryData.data.setInt(CLASS_IS_INSTANCE, functionTable.size());
|
||||||
binaryData.data.setInt(CLASS_CANARY, RuntimeClass.computeCanary(4, 0));
|
binaryData.data.setInt(CLASS_CANARY, RuntimeClass.computeCanary(4, 0));
|
||||||
functionTable.add(Mangling.mangleIsSupertype(type));
|
functionTable.add(Mangling.mangleIsSupertype(type));
|
||||||
|
binaryData.data.setAddress(CLASS_SIMPLE_NAME, 0);
|
||||||
|
binaryData.data.setInt(CLASS_INIT, -1);
|
||||||
binaryData.start = binaryWriter.append(vtableSize > 0 ? wrapper : binaryData.data);
|
binaryData.start = binaryWriter.append(vtableSize > 0 ? wrapper : binaryData.data);
|
||||||
|
|
||||||
itemBinaryData.data.setAddress(CLASS_ARRAY_TYPE, binaryData.start);
|
itemBinaryData.data.setAddress(CLASS_ARRAY_TYPE, binaryData.start);
|
||||||
|
@ -181,6 +187,8 @@ public class WasmClassGenerator {
|
||||||
value.setInt(CLASS_SIZE, size);
|
value.setInt(CLASS_SIZE, size);
|
||||||
value.setInt(CLASS_FLAGS, RuntimeClass.PRIMITIVE);
|
value.setInt(CLASS_FLAGS, RuntimeClass.PRIMITIVE);
|
||||||
value.setInt(CLASS_IS_INSTANCE, functionTable.size());
|
value.setInt(CLASS_IS_INSTANCE, functionTable.size());
|
||||||
|
value.setAddress(CLASS_SIMPLE_NAME, 0);
|
||||||
|
value.setInt(CLASS_INIT, -1);
|
||||||
functionTable.add(Mangling.mangleIsSupertype(type));
|
functionTable.add(Mangling.mangleIsSupertype(type));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -248,7 +256,16 @@ public class WasmClassGenerator {
|
||||||
flags |= RuntimeClass.ENUM;
|
flags |= RuntimeClass.ENUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cls != null && binaryData.start >= 0
|
||||||
|
&& cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)) != null) {
|
||||||
|
header.setInt(CLASS_INIT, functionTable.size());
|
||||||
|
functionTable.add(Mangling.mangleInitializer(name));
|
||||||
|
} else {
|
||||||
|
header.setInt(CLASS_INIT, -1);
|
||||||
|
}
|
||||||
|
|
||||||
header.setInt(CLASS_FLAGS, flags);
|
header.setInt(CLASS_FLAGS, flags);
|
||||||
|
header.setAddress(CLASS_SIMPLE_NAME, 0);
|
||||||
|
|
||||||
return vtable != null ? wrapper : header;
|
return vtable != null ? wrapper : header;
|
||||||
}
|
}
|
||||||
|
@ -500,7 +517,7 @@ public class WasmClassGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasClinit(String className) {
|
public boolean hasClinit(String className) {
|
||||||
if (isStructure(className)) {
|
if (isStructure(className) || className.equals(Address.class.getName())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ClassReader cls = classSource.get(className);
|
ClassReader cls = classSource.get(className);
|
||||||
|
|
|
@ -35,6 +35,7 @@ public class PlatformIntrinsic implements WasmIntrinsic {
|
||||||
case "getPlatformObject":
|
case "getPlatformObject":
|
||||||
case "asJavaClass":
|
case "asJavaClass":
|
||||||
case "getName":
|
case "getName":
|
||||||
|
case "createQueue":
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -48,6 +49,7 @@ public class PlatformIntrinsic implements WasmIntrinsic {
|
||||||
case "asJavaClass":
|
case "asJavaClass":
|
||||||
return manager.generate(invocation.getArguments().get(0));
|
return manager.generate(invocation.getArguments().get(0));
|
||||||
case "getName":
|
case "getName":
|
||||||
|
case "createQueue":
|
||||||
return new WasmInt32Constant(0);
|
return new WasmInt32Constant(0);
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException(invocation.getMethod().toString());
|
throw new IllegalArgumentException(invocation.getMethod().toString());
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var TeaVM = TeaVM || {};
|
||||||
|
TeaVM.wasm = function() {
|
||||||
|
let lineBuffer = "";
|
||||||
|
function putwchar(charCode) {
|
||||||
|
if (charCode === 10) {
|
||||||
|
console.log(lineBuffer);
|
||||||
|
lineBuffer = "";
|
||||||
|
} else {
|
||||||
|
lineBuffer += String.fromCharCode(charCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function towlower(code) {
|
||||||
|
return String.fromCharCode(code).toLowerCase().charCodeAt(0);
|
||||||
|
}
|
||||||
|
function towupper(code) {
|
||||||
|
return String.fromCharCode(code).toUpperCase().charCodeAt(0);
|
||||||
|
}
|
||||||
|
function currentTimeMillis() {
|
||||||
|
return new Date().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
function importDefaults(obj) {
|
||||||
|
obj.teavm = {
|
||||||
|
currentTimeMillis: currentTimeMillis,
|
||||||
|
isnan: isNaN,
|
||||||
|
TeaVM_getNaN: function() { return NaN; },
|
||||||
|
isinf: function(n) { return !isFinite(n) },
|
||||||
|
isfinite: isFinite,
|
||||||
|
putwchar: putwchar,
|
||||||
|
towlower: towlower,
|
||||||
|
towupper: towupper
|
||||||
|
};
|
||||||
|
|
||||||
|
obj.teavmMath = Math;
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(path, options) {
|
||||||
|
if (!options) {
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
let callback = typeof options.callback !== "undefined" ? options.callback : function() {};
|
||||||
|
let errorCallback = typeof options.errorCallback !== "undefined" ? options.errorCallback : function() {};
|
||||||
|
|
||||||
|
let importObj = {};
|
||||||
|
importDefaults(importObj);
|
||||||
|
if (typeof options.installImports !== "undefined") {
|
||||||
|
options.installImports(importObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
let xhr = new XMLHttpRequest();
|
||||||
|
xhr.responseType = "arraybuffer";
|
||||||
|
xhr.open("GET", path);
|
||||||
|
|
||||||
|
xhr.onload = function() {
|
||||||
|
let response = xhr.response;
|
||||||
|
if (!response) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebAssembly.instantiate(response, importObj).then(function(resultObject) {
|
||||||
|
resultObject.instance.exports.main();
|
||||||
|
callback(resultObject);
|
||||||
|
}).catch(function(error) {
|
||||||
|
console.log("Error loading WebAssembly %o", error);
|
||||||
|
errorCallback(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
xhr.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
return { importDefaults: importDefaults, run: run };
|
||||||
|
}();
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
window.addEventListener("message", event => {
|
window.addEventListener("message", event => {
|
||||||
let request = event.data;
|
let request = event.data;
|
||||||
|
switch (request.type) {
|
||||||
|
case "js":
|
||||||
appendFiles(request.files, 0, () => {
|
appendFiles(request.files, 0, () => {
|
||||||
launchTest(response => {
|
launchTest(response => {
|
||||||
event.source.postMessage(response, "*");
|
event.source.postMessage(response, "*");
|
||||||
|
@ -25,19 +27,30 @@ window.addEventListener("message", event => {
|
||||||
}, error => {
|
}, error => {
|
||||||
event.source.postMessage({ status: "failed", errorMessage: error }, "*");
|
event.source.postMessage({ status: "failed", errorMessage: error }, "*");
|
||||||
});
|
});
|
||||||
|
break;
|
||||||
|
case "wasm":
|
||||||
|
appendFiles(request.files.filter(f => f.endsWith(".js")), 0, () => {
|
||||||
|
launchWasmTest(request.files.filter(f => f.endsWith(".wasm"))[0], response => {
|
||||||
|
event.source.postMessage(response, "*");
|
||||||
|
});
|
||||||
|
}, error => {
|
||||||
|
event.source.postMessage({ status: "failed", errorMessage: error }, "*");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function appendFiles(files, index, callback, errorCallback) {
|
function appendFiles(files, index, callback, errorCallback) {
|
||||||
if (index === files.length) {
|
if (index === files.length) {
|
||||||
callback();
|
callback();
|
||||||
} else {
|
} else {
|
||||||
let fileName = "file://" + files[index];
|
let fileName = files[index];
|
||||||
let script = document.createElement("script");
|
let script = document.createElement("script");
|
||||||
script.onload = () => {
|
script.onload = () => {
|
||||||
appendFiles(files, index + 1, callback, errorCallback);
|
appendFiles(files, index + 1, callback, errorCallback);
|
||||||
};
|
};
|
||||||
script.onerror = () => {
|
script.onerror = () => {
|
||||||
errorCallback("failed to load script" + fileName);
|
errorCallback("failed to load script " + fileName);
|
||||||
};
|
};
|
||||||
script.src = fileName;
|
script.src = fileName;
|
||||||
document.body.appendChild(script);
|
document.body.appendChild(script);
|
||||||
|
@ -81,6 +94,44 @@ function launchTest(callback) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function launchWasmTest(path, callback) {
|
||||||
|
var output = [];
|
||||||
|
var outputBuffer = "";
|
||||||
|
|
||||||
|
function putwchar(charCode) {
|
||||||
|
if (charCode === 10) {
|
||||||
|
switch (outputBuffer) {
|
||||||
|
case "SUCCESS":
|
||||||
|
callback({status: "OK"});
|
||||||
|
break;
|
||||||
|
case "FAILED":
|
||||||
|
callback({
|
||||||
|
status: "failed",
|
||||||
|
errorMessage: output.join("\n")
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
output.push(TeaVM_outputBuffer);
|
||||||
|
outputBuffer = "";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outputBuffer += String.fromCharCode(charCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TeaVM.wasm.run(path, {
|
||||||
|
installImports: function(o) {
|
||||||
|
o.teavm.putwchar = putwchar;
|
||||||
|
},
|
||||||
|
errorCallback: function(err) {
|
||||||
|
callback({
|
||||||
|
status: "failed",
|
||||||
|
errorMessage: err.message + '\n' + err.stack
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
window.parent.postMessage("ready", "*");
|
window.parent.postMessage("ready", "*");
|
||||||
}
|
}
|
|
@ -16,17 +16,20 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
import * as fs from "./promise-fs.js";
|
import * as fs from "./promise-fs.js";
|
||||||
import * as nodePath from "path";
|
|
||||||
import * as http from "http";
|
import * as http from "http";
|
||||||
import {server as WebSocketServer} from "websocket";
|
import {server as WebSocketServer} from "websocket";
|
||||||
|
|
||||||
const TEST_FILE_NAME = "test.js";
|
const TEST_FILE_NAME = "test.js";
|
||||||
const RUNTIME_FILE_NAME = "runtime.js";
|
const RUNTIME_FILE_NAME = "runtime.js";
|
||||||
|
const WASM_RUNTIME_FILE_NAME = "test.wasm-runtime.js";
|
||||||
const TEST_FILES = [
|
const TEST_FILES = [
|
||||||
{ file: TEST_FILE_NAME, name: "simple" },
|
{ file: TEST_FILE_NAME, name: "simple", type: "js" },
|
||||||
{ file: "test-min.js", name: "minified" },
|
{ file: "test-min.js", name: "minified", type: "js" },
|
||||||
{ file: "test-optimized.js", name: "optimized" }
|
{ file: "test-optimized.js", name: "optimized", type: "js" },
|
||||||
|
{ file: "test.wasm", name: "wasm", type: "wasm" },
|
||||||
|
{ file: "test-optimized.wasm", name: "wasm-optimized", type: "wasm" }
|
||||||
];
|
];
|
||||||
|
const SERVER_PREFIX = "http://localhost:9090/";
|
||||||
let totalTests = 0;
|
let totalTests = 0;
|
||||||
|
|
||||||
class TestSuite {
|
class TestSuite {
|
||||||
|
@ -37,20 +40,30 @@ class TestSuite {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class TestCase {
|
class TestCase {
|
||||||
constructor(name, files) {
|
constructor(type, name, files) {
|
||||||
|
this.type = type;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.files = files;
|
this.files = files;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let rootDir = process.argv[2];
|
||||||
|
if (rootDir.endsWith("/")) {
|
||||||
|
rootDir = rootDir.substring(0, rootDir.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
async function runAll() {
|
async function runAll() {
|
||||||
const rootSuite = new TestSuite("root");
|
const rootSuite = new TestSuite("root");
|
||||||
console.log("Searching tests");
|
console.log("Searching tests");
|
||||||
await walkDir(process.argv[2], "root", rootSuite);
|
await walkDir("", "root", rootSuite);
|
||||||
|
|
||||||
console.log("Running tests");
|
console.log("Running tests");
|
||||||
|
|
||||||
const server = http.createServer((request, response) => {
|
const server = http.createServer((request, response) => {
|
||||||
|
if (request.url.endsWith(".js") || request.url.endsWith(".wasm")) {
|
||||||
|
serveFile(rootDir + "/" + request.url, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
response.writeHead(404);
|
response.writeHead(404);
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
|
@ -94,14 +107,37 @@ async function runAll() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function serveFile(path, response) {
|
||||||
|
const stat = await fs.stat(path);
|
||||||
|
const contentType = path.endsWith(".wasm") ? "application/octet-stream" : "text/javascript";
|
||||||
|
if (stat.isFile()) {
|
||||||
|
const content = await fs.readFile(path);
|
||||||
|
response.writeHead(200, { 'Content-Type': contentType, 'Access-Control-Allow-Origin': "*" });
|
||||||
|
response.end(content, 'utf-8');
|
||||||
|
} else {
|
||||||
|
response.writeHead(404);
|
||||||
|
response.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function walkDir(path, name, suite) {
|
async function walkDir(path, name, suite) {
|
||||||
const files = await fs.readdir(path);
|
const files = await fs.readdir(rootDir + "/" + path);
|
||||||
if (files.includes(TEST_FILE_NAME) && files.includes(RUNTIME_FILE_NAME)) {
|
if (files.includes(WASM_RUNTIME_FILE_NAME) || files.includes(RUNTIME_FILE_NAME)) {
|
||||||
for (const { file: fileName, name: profileName } of TEST_FILES) {
|
for (const { file: fileName, name: profileName, type: type } of TEST_FILES) {
|
||||||
if (files.includes(fileName)) {
|
if (files.includes(fileName)) {
|
||||||
|
switch (type) {
|
||||||
|
case "js":
|
||||||
suite.testCases.push(new TestCase(
|
suite.testCases.push(new TestCase(
|
||||||
name + " " + profileName,
|
"js", name + " " + profileName,
|
||||||
[path + "/" + RUNTIME_FILE_NAME, path + "/" + fileName]));
|
[SERVER_PREFIX + path + "/" + RUNTIME_FILE_NAME, SERVER_PREFIX + path + "/" + fileName]));
|
||||||
|
break;
|
||||||
|
case "wasm":
|
||||||
|
suite.testCases.push(new TestCase(
|
||||||
|
"wasm", name + " " + profileName,
|
||||||
|
[SERVER_PREFIX + path + "/" + WASM_RUNTIME_FILE_NAME,
|
||||||
|
SERVER_PREFIX + path + "/" + fileName]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
totalTests++;
|
totalTests++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +146,7 @@ async function walkDir(path, name, suite) {
|
||||||
suite.testSuites.push(childSuite);
|
suite.testSuites.push(childSuite);
|
||||||
await Promise.all(files.map(async file => {
|
await Promise.all(files.map(async file => {
|
||||||
const filePath = path + "/" + file;
|
const filePath = path + "/" + file;
|
||||||
const stat = await fs.stat(filePath);
|
const stat = await fs.stat(rootDir + "/" + filePath);
|
||||||
if (stat.isDirectory()) {
|
if (stat.isDirectory()) {
|
||||||
await walkDir(filePath, file, childSuite);
|
await walkDir(filePath, file, childSuite);
|
||||||
}
|
}
|
||||||
|
@ -148,8 +184,9 @@ class TestRunner {
|
||||||
let request = { id: this.requestIdGen++ };
|
let request = { id: this.requestIdGen++ };
|
||||||
request.tests = suite.testCases.map(testCase => {
|
request.tests = suite.testCases.map(testCase => {
|
||||||
return {
|
return {
|
||||||
|
type: testCase.type,
|
||||||
name: testCase.name,
|
name: testCase.name,
|
||||||
files: testCase.files.map(fileName => nodePath.resolve(process.cwd(), fileName))
|
files: testCase.files
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
this.testsRun += suite.testCases.length;
|
this.testsRun += suite.testCases.length;
|
||||||
|
|
|
@ -17,5 +17,6 @@ package org.teavm.junit;
|
||||||
|
|
||||||
enum RunKind {
|
enum RunKind {
|
||||||
JAVASCRIPT,
|
JAVASCRIPT,
|
||||||
C
|
C,
|
||||||
|
WASM
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.junit;
|
||||||
|
|
||||||
import org.teavm.backend.c.CTarget;
|
import org.teavm.backend.c.CTarget;
|
||||||
import org.teavm.backend.javascript.JavaScriptTarget;
|
import org.teavm.backend.javascript.JavaScriptTarget;
|
||||||
|
import org.teavm.backend.wasm.WasmTarget;
|
||||||
import org.teavm.vm.TeaVM;
|
import org.teavm.vm.TeaVM;
|
||||||
import org.teavm.vm.TeaVMOptimizationLevel;
|
import org.teavm.vm.TeaVMOptimizationLevel;
|
||||||
import org.teavm.vm.TeaVMTarget;
|
import org.teavm.vm.TeaVMTarget;
|
||||||
|
@ -79,6 +80,40 @@ interface TeaVMTestConfiguration<T extends TeaVMTarget> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TeaVMTestConfiguration<WasmTarget> WASM_DEFAULT = new TeaVMTestConfiguration<WasmTarget>() {
|
||||||
|
@Override
|
||||||
|
public String getSuffix() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(TeaVM vm) {
|
||||||
|
vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(WasmTarget target) {
|
||||||
|
target.setMinHeapSize(32 * 1024 * 1024);
|
||||||
|
target.setWastEmitted(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TeaVMTestConfiguration<WasmTarget> WASM_OPTIMIZED = new TeaVMTestConfiguration<WasmTarget>() {
|
||||||
|
@Override
|
||||||
|
public String getSuffix() {
|
||||||
|
return "optimized";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(TeaVM vm) {
|
||||||
|
vm.setOptimizationLevel(TeaVMOptimizationLevel.FULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(WasmTarget target) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
TeaVMTestConfiguration<CTarget> C_DEFAULT = new TeaVMTestConfiguration<CTarget>() {
|
TeaVMTestConfiguration<CTarget> C_DEFAULT = new TeaVMTestConfiguration<CTarget>() {
|
||||||
@Override
|
@Override
|
||||||
public String getSuffix() {
|
public String getSuffix() {
|
||||||
|
@ -95,11 +130,10 @@ interface TeaVMTestConfiguration<T extends TeaVMTarget> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
TeaVMTestConfiguration<CTarget> C_OPTIMIZED = new TeaVMTestConfiguration<CTarget>() {
|
TeaVMTestConfiguration<CTarget> C_OPTIMIZED = new TeaVMTestConfiguration<CTarget>() {
|
||||||
@Override
|
@Override
|
||||||
public String getSuffix() {
|
public String getSuffix() {
|
||||||
return "";
|
return "optimized";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -52,6 +52,7 @@ import org.junit.runner.notification.RunNotifier;
|
||||||
import org.junit.runners.model.InitializationError;
|
import org.junit.runners.model.InitializationError;
|
||||||
import org.teavm.backend.c.CTarget;
|
import org.teavm.backend.c.CTarget;
|
||||||
import org.teavm.backend.javascript.JavaScriptTarget;
|
import org.teavm.backend.javascript.JavaScriptTarget;
|
||||||
|
import org.teavm.backend.wasm.WasmTarget;
|
||||||
import org.teavm.callgraph.CallGraph;
|
import org.teavm.callgraph.CallGraph;
|
||||||
import org.teavm.diagnostics.DefaultProblemTextConsumer;
|
import org.teavm.diagnostics.DefaultProblemTextConsumer;
|
||||||
import org.teavm.diagnostics.Problem;
|
import org.teavm.diagnostics.Problem;
|
||||||
|
@ -78,7 +79,8 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
private static final String SELENIUM_URL = "teavm.junit.js.selenium.url";
|
private static final String SELENIUM_URL = "teavm.junit.js.selenium.url";
|
||||||
private static final String JS_ENABLED = "teavm.junit.js";
|
private static final String JS_ENABLED = "teavm.junit.js";
|
||||||
private static final String C_ENABLED = "teavm.junit.c";
|
private static final String C_ENABLED = "teavm.junit.c";
|
||||||
private static final String C_COMPILER = "teavm.junit.c-compiler";
|
private static final String WASM_ENABLED = "teavm.junit.wasm";
|
||||||
|
private static final String C_COMPILER = "teavm.junit.c.compiler";
|
||||||
private static final String MINIFIED = "teavm.junit.minified";
|
private static final String MINIFIED = "teavm.junit.minified";
|
||||||
private static final String OPTIMIZED = "teavm.junit.optimized";
|
private static final String OPTIMIZED = "teavm.junit.optimized";
|
||||||
|
|
||||||
|
@ -275,6 +277,15 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
runs.add(run);
|
runs.add(run);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (TeaVMTestConfiguration<WasmTarget> configuration : getWasmConfigurations()) {
|
||||||
|
TestRun run = compile(child, notifier, RunKind.WASM,
|
||||||
|
m -> compileToWasm(m, configuration, outputPath), onSuccess.get(0));
|
||||||
|
if (run != null) {
|
||||||
|
runs.add(run);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
notifier.fireTestFailure(new Failure(description, e));
|
notifier.fireTestFailure(new Failure(description, e));
|
||||||
notifier.fireTestFinished(description);
|
notifier.fireTestFinished(description);
|
||||||
|
@ -414,7 +425,9 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
|
|
||||||
private void copyJsFilesTo(File path) throws IOException {
|
private void copyJsFilesTo(File path) throws IOException {
|
||||||
resourceToFile("org/teavm/backend/javascript/runtime.js", new File(path, "runtime.js"));
|
resourceToFile("org/teavm/backend/javascript/runtime.js", new File(path, "runtime.js"));
|
||||||
|
resourceToFile("org/teavm/backend/wasm/wasm-runtime.js", new File(path, "test.wasm-runtime.js"));
|
||||||
resourceToFile("teavm-run-test.html", new File(path, "run-test.html"));
|
resourceToFile("teavm-run-test.html", new File(path, "run-test.html"));
|
||||||
|
resourceToFile("teavm-run-test-wasm.html", new File(path, "run-test-wasm.html"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompileResult compileToJs(Method method, TeaVMTestConfiguration<JavaScriptTarget> configuration,
|
private CompileResult compileToJs(Method method, TeaVMTestConfiguration<JavaScriptTarget> configuration,
|
||||||
|
@ -434,6 +447,13 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
}, path, ".c");
|
}, path, ".c");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CompileResult compileToWasm(Method method, TeaVMTestConfiguration<WasmTarget> configuration,
|
||||||
|
File path) {
|
||||||
|
return compileTest(method, configuration, WasmTarget::new, vm -> {
|
||||||
|
vm.entryPoint("main", new MethodReference(TestEntryPoint.class, "main", String[].class, void.class));
|
||||||
|
}, path, ".wasm");
|
||||||
|
}
|
||||||
|
|
||||||
private <T extends TeaVMTarget> CompileResult compileTest(Method method, TeaVMTestConfiguration<T> configuration,
|
private <T extends TeaVMTarget> CompileResult compileTest(Method method, TeaVMTestConfiguration<T> configuration,
|
||||||
Supplier<T> targetSupplier, Consumer<TeaVM> preBuild, File path, String extension) {
|
Supplier<T> targetSupplier, Consumer<TeaVM> preBuild, File path, String extension) {
|
||||||
CompileResult result = new CompileResult();
|
CompileResult result = new CompileResult();
|
||||||
|
@ -497,6 +517,17 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
||||||
return configurations;
|
return configurations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<TeaVMTestConfiguration<WasmTarget>> getWasmConfigurations() {
|
||||||
|
List<TeaVMTestConfiguration<WasmTarget>> configurations = new ArrayList<>();
|
||||||
|
if (Boolean.getBoolean(WASM_ENABLED)) {
|
||||||
|
configurations.add(TeaVMTestConfiguration.WASM_DEFAULT);
|
||||||
|
if (Boolean.getBoolean(OPTIMIZED)) {
|
||||||
|
configurations.add(TeaVMTestConfiguration.WASM_OPTIMIZED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return configurations;
|
||||||
|
}
|
||||||
|
|
||||||
private List<TeaVMTestConfiguration<CTarget>> getCConfigurations() {
|
private List<TeaVMTestConfiguration<CTarget>> getCConfigurations() {
|
||||||
List<TeaVMTestConfiguration<CTarget>> configurations = new ArrayList<>();
|
List<TeaVMTestConfiguration<CTarget>> configurations = new ArrayList<>();
|
||||||
if (Boolean.getBoolean(C_ENABLED)) {
|
if (Boolean.getBoolean(C_ENABLED)) {
|
||||||
|
|
|
@ -39,6 +39,7 @@ final class TestEntryPoint {
|
||||||
System.out.println("SUCCESS");
|
System.out.println("SUCCESS");
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace(System.out);
|
e.printStackTrace(System.out);
|
||||||
|
System.out.println("FAILURE");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
29
tools/junit/src/main/resources/teavm-run-test-wasm.html
Normal file
29
tools/junit/src/main/resources/teavm-run-test-wasm.html
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2018 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>TeaVM JUnit test</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="text/javascript" src="test.wasm-runtime.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
TeaVM.wasm.run("test.wasm");
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user