mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
wasm gc: support passing arguments to main method
This commit is contained in:
parent
e61301576b
commit
73edc0cf6e
|
@ -15,20 +15,21 @@
|
|||
*/
|
||||
package org.teavm.backend.wasm;
|
||||
|
||||
import org.teavm.backend.wasm.generate.TemporaryVariablePool;
|
||||
import org.teavm.backend.wasm.generate.gc.WasmGCDeclarationsGenerator;
|
||||
import org.teavm.backend.wasm.generate.gc.methods.WasmGCGenerationUtil;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmGlobal;
|
||||
import org.teavm.backend.wasm.model.WasmLocal;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCallReference;
|
||||
import org.teavm.backend.wasm.model.expression.WasmDrop;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmReturn;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
|
||||
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
|
@ -56,25 +57,92 @@ public class WasmGCModuleGenerator {
|
|||
public WasmFunction generateMainFunction(String entryPoint) {
|
||||
var mainFunction = declarationsGenerator.functions().forStaticMethod(new MethodReference(entryPoint,
|
||||
"main", ValueType.parse(String[].class), ValueType.VOID));
|
||||
var mainFunctionCaller = new WasmFunction(declarationsGenerator.functionTypes.of(null));
|
||||
var stringArrayType = declarationsGenerator.typeMapper()
|
||||
.mapType(ValueType.parse(String[].class));
|
||||
var mainFunctionCaller = new WasmFunction(declarationsGenerator.functionTypes.of(null, stringArrayType));
|
||||
var argsLocal = new WasmLocal(stringArrayType, "args");
|
||||
declarationsGenerator.module.functions.add(mainFunctionCaller);
|
||||
mainFunctionCaller.getBody().add(callInitializer());
|
||||
|
||||
var tempVars = new TemporaryVariablePool(mainFunctionCaller);
|
||||
var genUtil = new WasmGCGenerationUtil(declarationsGenerator.classInfoProvider(), tempVars);
|
||||
var stringArrayType = declarationsGenerator.typeMapper()
|
||||
.mapType(ValueType.parse(String[].class));
|
||||
var arrayVar = tempVars.acquire(stringArrayType);
|
||||
genUtil.allocateArray(ValueType.parse(String.class), new WasmInt32Constant(0), null,
|
||||
arrayVar, mainFunctionCaller.getBody());
|
||||
var callToMainFunction = new WasmCall(mainFunction, new WasmGetLocal(arrayVar));
|
||||
var callToMainFunction = new WasmCall(mainFunction, new WasmGetLocal(argsLocal));
|
||||
mainFunctionCaller.getBody().add(callToMainFunction);
|
||||
mainFunctionCaller.getBody().add(new WasmReturn());
|
||||
tempVars.release(arrayVar);
|
||||
|
||||
return mainFunctionCaller;
|
||||
}
|
||||
|
||||
public WasmFunction generateCreateStringBuilderFunction() {
|
||||
var function = declarationsGenerator.functions().forStaticMethod(new MethodReference(
|
||||
WasmGCSupport.class, "createStringBuilder", StringBuilder.class));
|
||||
var caller = new WasmFunction(function.getType());
|
||||
caller.getBody().add(callInitializer());
|
||||
caller.getBody().add(new WasmReturn(new WasmCall(function)));
|
||||
declarationsGenerator.module.functions.add(caller);
|
||||
return caller;
|
||||
}
|
||||
|
||||
public WasmFunction generateCreateStringArrayFunction() {
|
||||
var function = declarationsGenerator.functions().forStaticMethod(new MethodReference(
|
||||
WasmGCSupport.class, "createStringArray", int.class, String[].class));
|
||||
var caller = new WasmFunction(function.getType());
|
||||
var sizeLocal = new WasmLocal(WasmType.INT32);
|
||||
caller.add(sizeLocal);
|
||||
caller.getBody().add(callInitializer());
|
||||
caller.getBody().add(new WasmReturn(new WasmCall(function, new WasmGetLocal(sizeLocal))));
|
||||
declarationsGenerator.module.functions.add(caller);
|
||||
return caller;
|
||||
}
|
||||
|
||||
public WasmFunction generateAppendCharFunction() {
|
||||
var function = declarationsGenerator.functions().forInstanceMethod(new MethodReference(
|
||||
StringBuilder.class, "append", char.class, StringBuilder.class));
|
||||
var stringBuilderType = declarationsGenerator.typeMapper().mapType(ValueType.parse(StringBuilder.class));
|
||||
var caller = new WasmFunction(declarationsGenerator.functionTypes.of(null, stringBuilderType, WasmType.INT32));
|
||||
var stringBuilderLocal = new WasmLocal(stringBuilderType);
|
||||
var codeLocal = new WasmLocal(WasmType.INT32);
|
||||
caller.add(stringBuilderLocal);
|
||||
caller.add(codeLocal);
|
||||
caller.getBody().add(callInitializer());
|
||||
caller.getBody().add(new WasmDrop(new WasmCall(function, new WasmGetLocal(stringBuilderLocal),
|
||||
new WasmGetLocal(codeLocal))));
|
||||
caller.getBody().add(new WasmReturn());
|
||||
declarationsGenerator.module.functions.add(caller);
|
||||
return caller;
|
||||
}
|
||||
|
||||
public WasmFunction generateBuildStringFunction() {
|
||||
var function = declarationsGenerator.functions().forInstanceMethod(new MethodReference(
|
||||
StringBuilder.class, "toString", String.class));
|
||||
var stringBuilderType = declarationsGenerator.typeMapper().mapType(ValueType.parse(StringBuilder.class));
|
||||
var stringType = declarationsGenerator.typeMapper().mapType(ValueType.parse(String.class));
|
||||
var caller = new WasmFunction(declarationsGenerator.functionTypes.of(stringType, stringBuilderType));
|
||||
var stringBuilderLocal = new WasmLocal(stringBuilderType);
|
||||
caller.add(stringBuilderLocal);
|
||||
caller.getBody().add(callInitializer());
|
||||
caller.getBody().add(new WasmReturn(new WasmCall(function, new WasmGetLocal(stringBuilderLocal))));
|
||||
declarationsGenerator.module.functions.add(caller);
|
||||
return caller;
|
||||
}
|
||||
|
||||
public WasmFunction generateSetToStringArrayFunction() {
|
||||
var function = declarationsGenerator.functions().forStaticMethod(new MethodReference(
|
||||
WasmGCSupport.class, "setToStringArray", String[].class, int.class, String.class, void.class));
|
||||
var stringArrayType = declarationsGenerator.typeMapper().mapType(ValueType.parse(String[].class));
|
||||
var stringType = declarationsGenerator.typeMapper().mapType(ValueType.parse(String.class));
|
||||
var caller = new WasmFunction(function.getType());
|
||||
var arrayLocal = new WasmLocal(stringArrayType);
|
||||
var indexLocal = new WasmLocal(WasmType.INT32);
|
||||
var valueLocal = new WasmLocal(stringType);
|
||||
caller.add(arrayLocal);
|
||||
caller.add(indexLocal);
|
||||
caller.add(valueLocal);
|
||||
caller.getBody().add(callInitializer());
|
||||
caller.getBody().add(new WasmReturn(new WasmCall(function, new WasmGetLocal(arrayLocal),
|
||||
new WasmGetLocal(indexLocal), new WasmGetLocal(valueLocal))));
|
||||
declarationsGenerator.module.functions.add(caller);
|
||||
return caller;
|
||||
}
|
||||
|
||||
private void createInitializer() {
|
||||
if (initializer != null) {
|
||||
return;
|
||||
|
|
|
@ -75,7 +75,9 @@ public class WasmGCTarget implements TeaVMTarget {
|
|||
|
||||
@Override
|
||||
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
|
||||
new WasmGCDependencies(dependencyAnalyzer).contribute();
|
||||
var deps = new WasmGCDependencies(dependencyAnalyzer);
|
||||
deps.contribute();
|
||||
deps.contributeStandardExports();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -122,9 +124,26 @@ public class WasmGCTarget implements TeaVMTarget {
|
|||
);
|
||||
declarationsGenerator.setFriendlyToDebugger(controller.isFriendlyToDebugger());
|
||||
var moduleGenerator = new WasmGCModuleGenerator(declarationsGenerator);
|
||||
|
||||
var mainFunction = moduleGenerator.generateMainFunction(controller.getEntryPoint());
|
||||
mainFunction.setExportName(controller.getEntryPointName());
|
||||
mainFunction.setName(controller.getEntryPointName());
|
||||
|
||||
var stringBuilderFunction = moduleGenerator.generateCreateStringBuilderFunction();
|
||||
stringBuilderFunction.setExportName("createStringBuilder");
|
||||
|
||||
var createStringArrayFunction = moduleGenerator.generateCreateStringArrayFunction();
|
||||
createStringArrayFunction.setExportName("createStringArray");
|
||||
|
||||
var appendCharFunction = moduleGenerator.generateAppendCharFunction();
|
||||
appendCharFunction.setExportName("appendChar");
|
||||
|
||||
var buildStringFunction = moduleGenerator.generateBuildStringFunction();
|
||||
buildStringFunction.setExportName("buildString");
|
||||
|
||||
var setArrayFunction = moduleGenerator.generateSetToStringArrayFunction();
|
||||
setArrayFunction.setExportName("setToStringArray");
|
||||
|
||||
moduleGenerator.generate();
|
||||
adjustModuleMemory(module);
|
||||
|
||||
|
|
|
@ -34,6 +34,24 @@ public class WasmGCDependencies {
|
|||
contributeInitializerUtils();
|
||||
}
|
||||
|
||||
public void contributeStandardExports() {
|
||||
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "createStringArray", int.class, String[].class))
|
||||
.use();
|
||||
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "createStringBuilder", StringBuilder.class))
|
||||
.use();
|
||||
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "setToStringArray", String[].class,
|
||||
int.class, String.class, void.class))
|
||||
.propagate(1, analyzer.getType("[java/lang/String;"))
|
||||
.propagate(3, analyzer.getType("java.lang.String"))
|
||||
.use();
|
||||
analyzer.linkMethod(new MethodReference(StringBuilder.class, "append", char.class, StringBuilder.class))
|
||||
.propagate(0, analyzer.getType("java.lang.StringBuilder"))
|
||||
.use();
|
||||
analyzer.linkMethod(new MethodReference(StringBuilder.class, "toString", String.class))
|
||||
.propagate(0, analyzer.getType("java.lang.StringBuilder"))
|
||||
.use();
|
||||
}
|
||||
|
||||
private void contributeMathUtils() {
|
||||
for (var type : Arrays.asList(int.class, long.class, float.class, double.class)) {
|
||||
var method = new MethodReference(WasmRuntime.class, "compare", type, type, int.class);
|
||||
|
|
|
@ -1105,6 +1105,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
|||
public void visit(ReturnStatement statement) {
|
||||
if (statement.getResult() != null) {
|
||||
acceptWithType(statement.getResult(), currentMethod.getReturnType());
|
||||
result = forceType(result, currentMethod.getReturnType());
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
|
@ -1113,6 +1114,10 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
|||
resultConsumer.add(wasmStatement);
|
||||
}
|
||||
|
||||
protected WasmExpression forceType(WasmExpression expression, ValueType type) {
|
||||
return expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InstanceOfExpr expr) {
|
||||
acceptWithType(expr.getExpr(), expr.getType());
|
||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.backend.wasm.generate.gc.methods;
|
|||
|
||||
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
|
||||
import org.teavm.backend.wasm.WasmFunctionTypes;
|
||||
import org.teavm.backend.wasm.gc.WasmGCMethodReturnTypes;
|
||||
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationContext;
|
||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
|
||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
|
||||
|
@ -48,6 +49,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
|||
private WasmGCSupertypeFunctionProvider supertypeFunctions;
|
||||
private WasmGCCustomGeneratorProvider customGenerators;
|
||||
private WasmGCIntrinsicProvider intrinsics;
|
||||
private WasmGCMethodReturnTypes returnTypes;
|
||||
private WasmFunction npeMethod;
|
||||
private WasmFunction aaiobeMethod;
|
||||
private WasmFunction cceMethod;
|
||||
|
@ -59,7 +61,8 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
|||
ClassHierarchy hierarchy, BaseWasmFunctionRepository functions,
|
||||
WasmGCSupertypeFunctionProvider supertypeFunctions, WasmGCClassInfoProvider classInfoProvider,
|
||||
WasmGCStandardClasses standardClasses, WasmGCStringProvider strings,
|
||||
WasmGCCustomGeneratorProvider customGenerators, WasmGCIntrinsicProvider intrinsics) {
|
||||
WasmGCCustomGeneratorProvider customGenerators, WasmGCIntrinsicProvider intrinsics,
|
||||
WasmGCMethodReturnTypes returnTypes) {
|
||||
this.module = module;
|
||||
this.virtualTables = virtualTables;
|
||||
this.typeMapper = typeMapper;
|
||||
|
@ -73,6 +76,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
|||
this.strings = strings;
|
||||
this.customGenerators = customGenerators;
|
||||
this.intrinsics = intrinsics;
|
||||
this.returnTypes = returnTypes;
|
||||
}
|
||||
|
||||
public WasmGCClassInfoProvider classInfoProvider() {
|
||||
|
@ -172,4 +176,8 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
|||
public WasmGCIntrinsicProvider intrinsics() {
|
||||
return intrinsics;
|
||||
}
|
||||
|
||||
public WasmGCMethodReturnTypes returnTypes() {
|
||||
return returnTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -408,6 +408,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
|||
result = invocation(expr, null, false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected WasmExpression invocation(InvocationExpr expr, List<WasmExpression> resultConsumer, boolean willDrop) {
|
||||
if (expr.getType() == InvocationType.SPECIAL || expr.getType() == InvocationType.STATIC) {
|
||||
|
@ -435,26 +436,34 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
|||
@Override
|
||||
protected WasmExpression mapFirstArgumentForCall(WasmExpression argument, WasmFunction function,
|
||||
MethodReference method) {
|
||||
argument.acceptVisitor(typeInference);
|
||||
return forceType(argument, function.getType().getParameterTypes().get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WasmExpression forceType(WasmExpression expression, ValueType type) {
|
||||
return forceType(expression, mapType(context.returnTypes().returnTypeOf(currentMethod)));
|
||||
}
|
||||
|
||||
private WasmExpression forceType(WasmExpression expression, WasmType expectedType) {
|
||||
expression.acceptVisitor(typeInference);
|
||||
var actualType = typeInference.getResult();
|
||||
var expectedType = function.getType().getParameterTypes().get(0);
|
||||
if (actualType == expectedType || !(actualType instanceof WasmType.CompositeReference)
|
||||
|| !(expectedType instanceof WasmType.CompositeReference)) {
|
||||
return argument;
|
||||
return expression;
|
||||
}
|
||||
var actualComposite = ((WasmType.CompositeReference) actualType).composite;
|
||||
var expectedComposite = ((WasmType.CompositeReference) expectedType).composite;
|
||||
if (!(actualComposite instanceof WasmStructure) || !(expectedComposite instanceof WasmStructure)) {
|
||||
return argument;
|
||||
return expression;
|
||||
}
|
||||
|
||||
var actualStruct = (WasmStructure) actualComposite;
|
||||
var expectedStruct = (WasmStructure) expectedComposite;
|
||||
if (!actualStruct.isSupertypeOf(expectedStruct)) {
|
||||
return argument;
|
||||
return expression;
|
||||
}
|
||||
|
||||
return new WasmCast(argument, expectedComposite.getReference());
|
||||
return new WasmCast(expression, expectedComposite.getReference());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -302,7 +302,8 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
|||
standardClasses,
|
||||
strings,
|
||||
customGenerators,
|
||||
intrinsics
|
||||
intrinsics,
|
||||
returnTypes
|
||||
);
|
||||
}
|
||||
return context;
|
||||
|
|
|
@ -85,4 +85,16 @@ public class WasmGCSupport {
|
|||
private static native byte nextByte();
|
||||
|
||||
private static native void error();
|
||||
|
||||
public static StringBuilder createStringBuilder() {
|
||||
return new StringBuilder();
|
||||
}
|
||||
|
||||
public static String[] createStringArray(int size) {
|
||||
return new String[size];
|
||||
}
|
||||
|
||||
public static void setToStringArray(String[] array, int index, String value) {
|
||||
array[index] = value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2024 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() {
|
||||
function defaults(imports) {
|
||||
let stderr = "";
|
||||
let stdout = "";
|
||||
imports.teavm = {
|
||||
putcharStderr(c) {
|
||||
if (c === 10) {
|
||||
console.error(stderr);
|
||||
stderr = "";
|
||||
} else {
|
||||
stderr += String.fromCharCode(c);
|
||||
}
|
||||
},
|
||||
putcharStdout(c) {
|
||||
if (c === 10) {
|
||||
console.log(stdout);
|
||||
stdout = "";
|
||||
} else {
|
||||
stdout += String.fromCharCode(c);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function load(path, options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
const importObj = {};
|
||||
defaults(importObj);
|
||||
if (typeof options.installImports !== "undefined") {
|
||||
options.installImports(importObj);
|
||||
}
|
||||
|
||||
return WebAssembly.instantiateStreaming(fetch(path), importObj).then((obj => {
|
||||
let teavm = {};
|
||||
teavm.main = createMain(obj.instance);
|
||||
return teavm;
|
||||
}));
|
||||
}
|
||||
|
||||
function createMain(instance) {
|
||||
return args => {
|
||||
if (typeof args === "undefined") {
|
||||
args = [];
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let exports = instance.exports;
|
||||
let javaArgs = exports.createStringArray(args.length);
|
||||
for (let i = 0; i < args.length; ++i) {
|
||||
let arg = args[i];
|
||||
let javaArg = exports.createStringBuilder();
|
||||
for (let j = 0; j < arg.length; ++j) {
|
||||
exports.appendChar(javaArg, arg.charCodeAt(j));
|
||||
}
|
||||
exports.setToStringArray(javaArgs, i, exports.buildString(javaArg));
|
||||
}
|
||||
try {
|
||||
exports.main(javaArgs);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { load };
|
||||
}();
|
Loading…
Reference in New Issue
Block a user