mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
wasm: fix running tests, add in-browser test runners
This commit is contained in:
parent
b2b7a603b4
commit
c4c1408160
|
@ -114,6 +114,14 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
|
|||
this(sb.buffer, 0, sb.length());
|
||||
}
|
||||
|
||||
private TString(int length) {
|
||||
this.characters = new char[length];
|
||||
}
|
||||
|
||||
private static TString allocate(int size) {
|
||||
return new TString(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
if (index < 0 || index >= characters.length) {
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.teavm.backend.wasm.generate.WasmClassGenerator;
|
|||
import org.teavm.backend.wasm.generate.WasmDependencyListener;
|
||||
import org.teavm.backend.wasm.generate.WasmGenerationContext;
|
||||
import org.teavm.backend.wasm.generate.WasmGenerator;
|
||||
import org.teavm.backend.wasm.generate.WasmInteropFunctionGenerator;
|
||||
import org.teavm.backend.wasm.generate.WasmNameProvider;
|
||||
import org.teavm.backend.wasm.generate.WasmSpecialFunctionGenerator;
|
||||
import org.teavm.backend.wasm.generate.WasmStringPool;
|
||||
|
@ -341,6 +342,9 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
|
||||
dependencyAnalyzer.linkField(new FieldReference("java.lang.Object", "monitor"));
|
||||
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(String.class, "allocate", int.class, String.class))
|
||||
.use();
|
||||
|
||||
ClassDependency runtimeClassDep = dependencyAnalyzer.linkClass(RuntimeClass.class.getName());
|
||||
ClassDependency runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.class.getName());
|
||||
ClassDependency runtimeArrayDep = dependencyAnalyzer.linkClass(RuntimeArray.class.getName());
|
||||
|
@ -431,6 +435,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator, binaryWriter);
|
||||
|
||||
generateMethods(classes, context, generator, classGenerator, binaryWriter, module);
|
||||
new WasmInteropFunctionGenerator(classGenerator).generateFunctions(module);
|
||||
exceptionHandlingIntrinsic.postProcess(CallSiteDescriptor.extract(classes, classes.getClassNames()));
|
||||
generateIsSupertypeFunctions(tagRegistry, module, classGenerator);
|
||||
classGenerator.postProcess();
|
||||
|
@ -455,6 +460,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
module.add(initFunction);
|
||||
module.setStartFunction(initFunction);
|
||||
|
||||
|
||||
for (TeaVMEntryPoint entryPoint : controller.getEntryPoints().values()) {
|
||||
String mangledName = names.forMethod(entryPoint.getMethod());
|
||||
WasmFunction function = module.getFunctions().get(mangledName);
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright 2021 konsoletyper.
|
||||
*
|
||||
* 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.backend.wasm.generate;
|
||||
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmLocal;
|
||||
import org.teavm.backend.wasm.model.WasmModule;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmInt32Subtype;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
|
||||
import org.teavm.backend.wasm.model.expression.WasmReturn;
|
||||
import org.teavm.interop.Address;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.runtime.Allocator;
|
||||
import org.teavm.runtime.RuntimeArray;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
|
||||
public class WasmInteropFunctionGenerator {
|
||||
private WasmClassGenerator classGenerator;
|
||||
|
||||
public WasmInteropFunctionGenerator(WasmClassGenerator classGenerator) {
|
||||
this.classGenerator = classGenerator;
|
||||
}
|
||||
|
||||
public void generateFunctions(WasmModule module) {
|
||||
module.add(allocateString());
|
||||
module.add(stringData());
|
||||
|
||||
module.add(allocateArray("teavm_allocateObjectArray", ValueType.parse(Object.class)));
|
||||
module.add(allocateArray("teavm_allocateStringArray", ValueType.parse(String.class)));
|
||||
module.add(allocateArray("teavm_allocateByteArray", ValueType.parse(byte.class)));
|
||||
module.add(allocateArray("teavm_allocateShortArray", ValueType.parse(short.class)));
|
||||
module.add(allocateArray("teavm_allocateCharArray", ValueType.parse(char.class)));
|
||||
module.add(allocateArray("teavm_allocateIntArray", ValueType.parse(int.class)));
|
||||
module.add(allocateArray("teavm_allocateLongArray", ValueType.parse(long.class)));
|
||||
module.add(allocateArray("teavm_allocateFloatArray", ValueType.parse(float.class)));
|
||||
module.add(allocateArray("teavm_allocateDoubleArray", ValueType.parse(double.class)));
|
||||
module.add(arrayData("teavm_objectArrayData", 4));
|
||||
module.add(arrayData("teavm_byteArrayData", 1));
|
||||
module.add(arrayData("teavm_shortArrayData", 2));
|
||||
module.add(arrayData("teavm_charArrayData", 2));
|
||||
module.add(arrayData("teavm_intArrayData", 4));
|
||||
module.add(arrayData("teavm_longArrayData", 8));
|
||||
module.add(arrayData("teavm_floatArrayData", 4));
|
||||
module.add(arrayData("teavm_doubleArrayData", 8));
|
||||
module.add(arrayLength());
|
||||
}
|
||||
|
||||
|
||||
private WasmFunction allocateString() {
|
||||
WasmFunction function = new WasmFunction("teavm_allocateString");
|
||||
function.setExportName(function.getName());
|
||||
function.setResult(WasmType.INT32);
|
||||
function.getParameters().add(WasmType.INT32);
|
||||
|
||||
WasmLocal sizeLocal = new WasmLocal(WasmType.INT32, "size");
|
||||
function.add(sizeLocal);
|
||||
|
||||
String constructorName = classGenerator.names.forMethod(new MethodReference(String.class, "allocate",
|
||||
int.class, String.class));
|
||||
WasmCall constructorCall = new WasmCall(constructorName);
|
||||
constructorCall.getArguments().add(new WasmGetLocal(sizeLocal));
|
||||
function.getBody().add(constructorCall);
|
||||
|
||||
function.getBody().add(new WasmReturn(constructorCall));
|
||||
|
||||
return function;
|
||||
}
|
||||
|
||||
private WasmFunction allocateArray(String name, ValueType type) {
|
||||
WasmFunction function = new WasmFunction(name);
|
||||
function.setExportName(name);
|
||||
function.setResult(WasmType.INT32);
|
||||
function.getParameters().add(WasmType.INT32);
|
||||
|
||||
WasmLocal sizeLocal = new WasmLocal(WasmType.INT32, "size");
|
||||
function.add(sizeLocal);
|
||||
|
||||
int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type));
|
||||
String allocName = classGenerator.names.forMethod(new MethodReference(Allocator.class, "allocateArray",
|
||||
RuntimeClass.class, int.class, Address.class));
|
||||
WasmCall call = new WasmCall(allocName);
|
||||
call.getArguments().add(new WasmInt32Constant(classPointer));
|
||||
call.getArguments().add(new WasmGetLocal(sizeLocal));
|
||||
|
||||
function.getBody().add(new WasmReturn(call));
|
||||
|
||||
return function;
|
||||
}
|
||||
|
||||
private WasmFunction stringData() {
|
||||
WasmFunction function = new WasmFunction("teavm_stringData");
|
||||
function.setExportName(function.getName());
|
||||
function.setResult(WasmType.INT32);
|
||||
function.getParameters().add(WasmType.INT32);
|
||||
|
||||
WasmLocal stringLocal = new WasmLocal(WasmType.INT32, "string");
|
||||
function.add(stringLocal);
|
||||
|
||||
int offset = classGenerator.getFieldOffset(new FieldReference("java.lang.String", "characters"));
|
||||
WasmExpression chars = new WasmLoadInt32(4, new WasmGetLocal(stringLocal), WasmInt32Subtype.INT32, offset);
|
||||
|
||||
function.getBody().add(new WasmReturn(chars));
|
||||
|
||||
return function;
|
||||
}
|
||||
|
||||
private WasmFunction arrayData(String name, int alignment) {
|
||||
WasmFunction function = new WasmFunction(name);
|
||||
function.setExportName(function.getName());
|
||||
function.setResult(WasmType.INT32);
|
||||
function.getParameters().add(WasmType.INT32);
|
||||
|
||||
WasmLocal arrayLocal = new WasmLocal(WasmType.INT32, "array");
|
||||
function.add(arrayLocal);
|
||||
|
||||
int start = WasmClassGenerator.align(classGenerator.getClassSize(RuntimeArray.class.getName()),
|
||||
alignment);
|
||||
WasmExpression data = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
|
||||
new WasmGetLocal(arrayLocal), new WasmInt32Constant(start));
|
||||
|
||||
function.getBody().add(new WasmReturn(data));
|
||||
|
||||
return function;
|
||||
}
|
||||
|
||||
private WasmFunction arrayLength() {
|
||||
WasmFunction function = new WasmFunction("teavm_arrayLength");
|
||||
function.setExportName(function.getName());
|
||||
function.setResult(WasmType.INT32);
|
||||
function.getParameters().add(WasmType.INT32);
|
||||
|
||||
WasmLocal arrayLocal = new WasmLocal(WasmType.INT32, "array");
|
||||
function.add(arrayLocal);
|
||||
|
||||
int sizeOffset = classGenerator.getFieldOffset(new FieldReference(RuntimeArray.class.getName(), "size"));
|
||||
|
||||
WasmExpression ptr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
|
||||
new WasmGetLocal(arrayLocal), new WasmInt32Constant(sizeOffset));
|
||||
WasmExpression length = new WasmLoadInt32(4, ptr, WasmInt32Subtype.INT32);
|
||||
|
||||
function.getBody().add(new WasmReturn(length));
|
||||
|
||||
return function;
|
||||
}
|
||||
}
|
|
@ -225,7 +225,7 @@ public class WasmCRenderer {
|
|||
renderFunctionModifiers(sb, function);
|
||||
sb.append(WasmCRenderingVisitor.mapType(function.getResult())).append(' ');
|
||||
if (function.getImportName() != null) {
|
||||
sb.append(!function.getImportModule().isEmpty()
|
||||
sb.append(function.getImportModule() != null && !function.getImportModule().isEmpty()
|
||||
? function.getImportModule() + "_" + function.getImportName()
|
||||
: function.getImportName());
|
||||
} else {
|
||||
|
|
|
@ -16,6 +16,12 @@
|
|||
|
||||
var TeaVM = TeaVM || {};
|
||||
TeaVM.wasm = function() {
|
||||
class JavaError extends Error {
|
||||
constructor(message) {
|
||||
super(message)
|
||||
}
|
||||
}
|
||||
|
||||
let lineBuffer = "";
|
||||
function putwchar(charCode) {
|
||||
if (charCode === 10) {
|
||||
|
@ -87,7 +93,38 @@ TeaVM.wasm = function() {
|
|||
};
|
||||
}
|
||||
|
||||
function run(path, options) {
|
||||
function createTeaVM(instance) {
|
||||
let teavm = {
|
||||
memory: instance.exports.memory,
|
||||
instance,
|
||||
catchException: instance.exports.teavm_catchException
|
||||
}
|
||||
|
||||
for (const name of ["allocateString", "stringData", "allocateObjectArray", "allocateStringArray",
|
||||
"allocateByteArray", "allocateShortArray", "allocateCharArray", "allocateIntArray",
|
||||
"allocateLongArray", "allocateFloatArray", "allocateDoubleArray",
|
||||
"objectArrayData", "byteArrayData", "shortArrayData", "charArrayData", "intArrayData",
|
||||
"longArrayData", "floatArrayData", "doubleArrayData", "arrayLength"]) {
|
||||
teavm[name] = wrapExport(instance.exports["teavm_" + name], instance);
|
||||
}
|
||||
|
||||
teavm.main = createMain(teavm, instance.exports.main);
|
||||
|
||||
return teavm;
|
||||
}
|
||||
|
||||
function wrapExport(fn, instance) {
|
||||
return function() {
|
||||
let result = fn.apply(this, arguments);
|
||||
let ex = instance.exports.teavm_catchException();
|
||||
if (ex !== 0) {
|
||||
throw new JavaError("Uncaught exception occurred in java");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function load(path, options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
@ -105,23 +142,49 @@ TeaVM.wasm = function() {
|
|||
xhr.responseType = "arraybuffer";
|
||||
xhr.open("GET", path);
|
||||
|
||||
xhr.onload = function() {
|
||||
return new Promise((resolve, reject) => {
|
||||
xhr.onload = () => {
|
||||
let response = xhr.response;
|
||||
if (!response) {
|
||||
return;
|
||||
}
|
||||
|
||||
WebAssembly.instantiate(response, importObj).then(function(resultObject) {
|
||||
WebAssembly.instantiate(response, importObj).then(resultObject => {
|
||||
importObj.teavm.logString.memory = resultObject.instance.exports.memory;
|
||||
resultObject.instance.exports.main();
|
||||
callback(resultObject);
|
||||
}).catch(function(error) {
|
||||
console.log("Error loading WebAssembly %o", error);
|
||||
errorCallback(error);
|
||||
let teavm = createTeaVM(resultObject.instance);
|
||||
teavm.main = createMain(teavm, wrapExport, resultObject.instance.exports.main);
|
||||
resolve(teavm);
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
};
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
|
||||
return { importDefaults: importDefaults, run: run };
|
||||
function createMain(teavm, mainFunction) {
|
||||
return function(args) {
|
||||
if (typeof args === "undefined") {
|
||||
args = [];
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
let javaArgs = teavm.allocateStringArray(mainArgs.length);
|
||||
let javaArgsData = new Uint32Array(teavm.memory, teavm.objectArrayData(javaArgs), args.length);
|
||||
for (let i = 0; i < mainArgs.length; ++i) {
|
||||
let arg = args[i];
|
||||
let javaArg = teavm.allocateString(arg.length);
|
||||
let javaArgAddress = teavm.objectArrayData(teavm.stringData(javaArg));
|
||||
let javaArgData = new Uint16Array(teavm.memory, javaArgAddress, arg.length);
|
||||
for (let j = 0; j < arg.length; ++j) {
|
||||
javaArgData[j] = arg.charCodeAt(j);
|
||||
}
|
||||
javaArgsData[i] = javaArg;
|
||||
}
|
||||
|
||||
resolve(wrapExport(mainFunction, teavm.instance)(javaArgs));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { JavaError, importDefaults, load, wrapExport, createTeaVM, createMain };
|
||||
}();
|
||||
|
|
|
@ -21,12 +21,12 @@ var Benchmark = function() {
|
|||
this.resultTableBody = document.getElementById("result-table-body");
|
||||
}
|
||||
Benchmark.prototype.load = function() {
|
||||
TeaVM.wasm.run("teavm-wasm/classes.wasm", {
|
||||
TeaVM.wasm.load("teavm-wasm/classes.wasm", {
|
||||
installImports: installImports.bind(this),
|
||||
callback: function(result) {
|
||||
this.instance = result.instance;
|
||||
}.bind(this)
|
||||
});
|
||||
}).then(teavm => {
|
||||
this.instance = teavm;
|
||||
teavm.main();
|
||||
})
|
||||
};
|
||||
|
||||
function installImports(o) {
|
||||
|
|
|
@ -104,6 +104,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
static final String TESTNG_PROVIDER = "org.testng.annotations.DataProvider";
|
||||
private static final String PATH_PARAM = "teavm.junit.target";
|
||||
private static final String JS_RUNNER = "teavm.junit.js.runner";
|
||||
private static final String WASM_RUNNER = "teavm.junit.wasm.runner";
|
||||
private static final String THREAD_COUNT = "teavm.junit.threads";
|
||||
private static final String JS_ENABLED = "teavm.junit.js";
|
||||
static final String JS_DECODE_STACK = "teavm.junit.js.decodeStack";
|
||||
|
@ -191,6 +192,25 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
if (cCommand != null) {
|
||||
runners.get(RunKind.C).strategy = new CRunStrategy(cCommand);
|
||||
}
|
||||
|
||||
runStrategyName = System.getProperty(WASM_RUNNER);
|
||||
if (runStrategyName != null) {
|
||||
TestRunStrategy wasmRunStrategy;
|
||||
switch (runStrategyName) {
|
||||
case "browser":
|
||||
wasmRunStrategy = new BrowserRunStrategy(outputDir, "WASM", this::customBrowser);
|
||||
break;
|
||||
case "browser-chrome":
|
||||
wasmRunStrategy = new BrowserRunStrategy(outputDir, "WASM", this::chromeBrowser);
|
||||
break;
|
||||
case "browser-firefox":
|
||||
wasmRunStrategy = new BrowserRunStrategy(outputDir, "WASM", this::firefoxBrowser);
|
||||
break;
|
||||
default:
|
||||
throw new InitializationError("Unknown run strategy: " + runStrategyName);
|
||||
}
|
||||
runners.get(RunKind.WASM).strategy = wasmRunStrategy;
|
||||
}
|
||||
}
|
||||
|
||||
private Process customBrowser(String url) {
|
||||
|
@ -479,6 +499,14 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
File testPath = getOutputFile(outputPath, "classTest", configuration.getSuffix(), false, ".wasm");
|
||||
runs.add(createTestRun(configuration, testPath, child, RunKind.WASM, reference.toString(),
|
||||
notifier, onSuccess));
|
||||
File htmlPath = getOutputFile(outputPathForMethod, "test-wasm", configuration.getSuffix(), false, ".html");
|
||||
properties.put("SCRIPT", "../" + testPath.getName() + "-runtime.js");
|
||||
properties.put("IDENTIFIER", reference.toString());
|
||||
try {
|
||||
resourceToFile("teavm-run-test-wasm.html", htmlPath, properties);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
for (TeaVMTestConfiguration<CTarget> configuration : getCConfigurations()) {
|
||||
|
@ -526,6 +554,18 @@ public class TeaVMTestRunner extends Runner implements Filterable {
|
|||
TestRun run = prepareRun(configuration, child, compileResult, notifier, RunKind.WASM, onSuccess);
|
||||
if (run != null) {
|
||||
runs.add(run);
|
||||
|
||||
File testPath = getOutputFile(outputPath, "test", configuration.getSuffix(), false,
|
||||
".wasm-runtime.js");
|
||||
File htmlPath = getOutputFile(outputPath, "test", configuration.getSuffix(), false, ".html");
|
||||
properties.put("SCRIPT", testPath.getName());
|
||||
properties.put("IDENTIFIER", "");
|
||||
|
||||
try {
|
||||
resourceToFile("teavm-run-test-wasm.html", htmlPath, properties);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
<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" src="${SCRIPT}"></script>
|
||||
<script type="text/javascript">
|
||||
TeaVM.wasm.run("test.wasm");
|
||||
TeaVM.wasm.load("test.wasm").then(teavm => teavm.main(["${IDENTIFIER}"]));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -32,7 +32,7 @@ window.addEventListener("message", event => {
|
|||
case "WASM":
|
||||
const runtimeFile = request.file + "-runtime.js";
|
||||
appendFiles([runtimeFile], 0, () => {
|
||||
launchWasmTest(request.file, equest.argument, response => {
|
||||
launchWasmTest(request.file, request.argument, response => {
|
||||
event.source.postMessage(response, "*");
|
||||
});
|
||||
}, error => {
|
||||
|
@ -112,7 +112,7 @@ function launchWasmTest(path, argument, callback) {
|
|||
}
|
||||
}
|
||||
|
||||
TeaVM.wasm.run(path, {
|
||||
TeaVM.wasm.load(path, {
|
||||
installImports: function(o) {
|
||||
o.teavm.putwchar = putwchar;
|
||||
},
|
||||
|
@ -122,7 +122,18 @@ function launchWasmTest(path, argument, callback) {
|
|||
errorMessage: err.message + '\n' + err.stack
|
||||
}));
|
||||
}
|
||||
});
|
||||
}).then(teavm => {
|
||||
teavm.main(argument ? [argument] : []);
|
||||
})
|
||||
.then(() => {
|
||||
callback(wrapResponse({ status: "OK" }));
|
||||
})
|
||||
.catch(err => {
|
||||
callback(wrapResponse({
|
||||
status: "failed",
|
||||
errorMessage: err.message + '\n' + err.stack
|
||||
}));
|
||||
})
|
||||
}
|
||||
|
||||
function start() {
|
||||
|
|
Loading…
Reference in New Issue
Block a user