mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
wasm gc: improve performance of JS interop
This commit is contained in:
parent
3218a00eb9
commit
753a028fc9
|
@ -17,8 +17,14 @@
|
||||||
var TeaVM = TeaVM || {};
|
var TeaVM = TeaVM || {};
|
||||||
TeaVM.wasm = function() {
|
TeaVM.wasm = function() {
|
||||||
let exports;
|
let exports;
|
||||||
|
let globalsCache = new Map();
|
||||||
let getGlobalName = function(name) {
|
let getGlobalName = function(name) {
|
||||||
return eval(name);
|
let result = globalsCache.get(name);
|
||||||
|
if (typeof result === "undefined") {
|
||||||
|
result = new Function("return " + name + ";");
|
||||||
|
globalsCache.set(name, result);
|
||||||
|
}
|
||||||
|
return result();
|
||||||
}
|
}
|
||||||
let setGlobalName = function(name, value) {
|
let setGlobalName = function(name, value) {
|
||||||
new Function("value", name + " = value;")(value);
|
new Function("value", name + " = value;")(value);
|
||||||
|
@ -237,13 +243,20 @@ TeaVM.wasm = function() {
|
||||||
return fn(javaObjectSymbol, functionsSymbol, functionOriginSymbol);
|
return fn(javaObjectSymbol, functionsSymbol, functionOriginSymbol);
|
||||||
},
|
},
|
||||||
defineMethod(cls, name, fn) {
|
defineMethod(cls, name, fn) {
|
||||||
cls.prototype[name] = function(...args) {
|
let params = [];
|
||||||
try {
|
for (let i = 1; i < fn.length; ++i) {
|
||||||
return fn(this, ...args);
|
params.push("p" + i);
|
||||||
} catch (e) {
|
|
||||||
rethrowJavaAsJs(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let paramsAsString = params.length === 0 ? "" : params.join(", ");
|
||||||
|
cls.prototype[name] = new Function("rethrowJavaAsJs", "fn", `
|
||||||
|
return function(${paramsAsString}) {
|
||||||
|
try {
|
||||||
|
return fn(${['this', params].join(", ")});
|
||||||
|
} catch (e) {
|
||||||
|
rethrowJavaAsJs(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
`)(rethrowJavaAsJs, fn);
|
||||||
},
|
},
|
||||||
defineProperty(cls, name, getFn, setFn) {
|
defineProperty(cls, name, getFn, setFn) {
|
||||||
let descriptor = {
|
let descriptor = {
|
||||||
|
@ -390,30 +403,47 @@ TeaVM.wasm = function() {
|
||||||
"unwrapShort", "unwrapChar", "unwrapInt", "unwrapFloat", "unwrapDouble"]) {
|
"unwrapShort", "unwrapChar", "unwrapInt", "unwrapFloat", "unwrapDouble"]) {
|
||||||
imports.teavmJso[name] = identity;
|
imports.teavmJso[name] = identity;
|
||||||
}
|
}
|
||||||
|
function wrapCallFromJavaToJs(call) {
|
||||||
|
try {
|
||||||
|
return call();
|
||||||
|
} catch (e) {
|
||||||
|
rethrowJsAsJava(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let argumentList = [];
|
||||||
for (let i = 0; i < 32; ++i) {
|
for (let i = 0; i < 32; ++i) {
|
||||||
imports.teavmJso["createFunction" + i] = (...args) => new Function(...args);
|
let args = argumentList.length === 0 ? "" : argumentList.join(", ");
|
||||||
imports.teavmJso["callFunction" + i] = (fn, ...args) => {
|
let argsAndBody = [...argumentList, "body"].join(", ");
|
||||||
|
imports.teavmJso["createFunction" + i] = new Function("wrapCallFromJavaToJs", ...argumentList, "body", `
|
||||||
|
return new Function('wrapCallFromJavaToJs', ${argsAndBody}).bind(this, wrapCallFromJavaToJs);
|
||||||
|
`).bind(null, wrapCallFromJavaToJs);
|
||||||
|
imports.teavmJso["callFunction" + i] = new Function("rethrowJsAsJava", "fn", ...argumentList, `
|
||||||
try {
|
try {
|
||||||
return fn(...args);
|
return fn(${args});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
rethrowJsAsJava(e);
|
rethrowJsAsJava(e);
|
||||||
}
|
}
|
||||||
};
|
`).bind(null, rethrowJsAsJava);
|
||||||
imports.teavmJso["callMethod" + i] = (instance, method, ...args) => {
|
imports.teavmJso["callMethod" + i] = new Function("rethrowJsAsJava", "getGlobalName", "instance",
|
||||||
|
"method", ...argumentList, `
|
||||||
try {
|
try {
|
||||||
return instance !== null ? instance[method](...args) : getGlobalName(method)(...args);
|
return instance !== null
|
||||||
|
? instance[method](${args})
|
||||||
|
: getGlobalName(method)(${args});
|
||||||
|
} catch (e) {
|
||||||
|
rethrowJsAsJava(e);
|
||||||
|
}`).bind(null, rethrowJsAsJava, getGlobalName);
|
||||||
|
imports.teavmJso["construct" + i] = new Function("rethrowJsAsJava", "constructor", ...argumentList, `
|
||||||
|
try {
|
||||||
|
return new constructor(${args});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
rethrowJsAsJava(e);
|
rethrowJsAsJava(e);
|
||||||
}
|
}
|
||||||
}
|
`).bind(null, rethrowJsAsJava);
|
||||||
imports.teavmJso["construct" + i] = (constructor, ...args) => {
|
imports.teavmJso["arrayOf" + i] = new Function(...argumentList, "return [" + args + "]");
|
||||||
try {
|
|
||||||
return new constructor(...args);
|
let param = "p" + (i + 1);
|
||||||
} catch (e) {
|
argumentList.push(param);
|
||||||
rethrowJsAsJava(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
imports.teavmJso["arrayOf" + i] = (...args) => args
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@ package org.teavm.jso.impl.wasmgc;
|
||||||
|
|
||||||
import static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.JS_TO_STRING;
|
import static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.JS_TO_STRING;
|
||||||
import static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.STRING_TO_JS;
|
import static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.STRING_TO_JS;
|
||||||
|
import org.teavm.ast.ConstantExpr;
|
||||||
|
import org.teavm.ast.Expr;
|
||||||
import org.teavm.ast.InvocationExpr;
|
import org.teavm.ast.InvocationExpr;
|
||||||
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic;
|
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext;
|
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext;
|
||||||
|
@ -37,21 +39,23 @@ import org.teavm.model.ValueType;
|
||||||
|
|
||||||
class WasmGCJSIntrinsic implements WasmGCIntrinsic {
|
class WasmGCJSIntrinsic implements WasmGCIntrinsic {
|
||||||
private WasmFunction globalFunction;
|
private WasmFunction globalFunction;
|
||||||
|
private WasmGCJsoCommonGenerator commonGen;
|
||||||
|
|
||||||
|
WasmGCJSIntrinsic(WasmGCJsoCommonGenerator commonGen) {
|
||||||
|
this.commonGen = commonGen;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
|
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
|
||||||
switch (invocation.getMethod().getName()) {
|
switch (invocation.getMethod().getName()) {
|
||||||
case "wrap": {
|
case "wrap":
|
||||||
var function = context.functions().forStaticMethod(STRING_TO_JS);
|
return wrapString(invocation.getArguments().get(0), context);
|
||||||
return new WasmCall(function, context.generate(invocation.getArguments().get(0)));
|
|
||||||
}
|
|
||||||
case "unwrapString": {
|
case "unwrapString": {
|
||||||
var function = context.functions().forStaticMethod(JS_TO_STRING);
|
var function = context.functions().forStaticMethod(JS_TO_STRING);
|
||||||
return new WasmCall(function, context.generate(invocation.getArguments().get(0)));
|
return new WasmCall(function, context.generate(invocation.getArguments().get(0)));
|
||||||
}
|
}
|
||||||
case "global": {
|
case "global": {
|
||||||
var stringToJs = context.functions().forStaticMethod(STRING_TO_JS);
|
var name = wrapString(invocation.getArguments().get(0), context);
|
||||||
var name = new WasmCall(stringToJs, context.generate(invocation.getArguments().get(0)));
|
|
||||||
return new WasmCall(getGlobalFunction(context), name);
|
return new WasmCall(getGlobalFunction(context), name);
|
||||||
}
|
}
|
||||||
case "throwCCEIfFalse":
|
case "throwCCEIfFalse":
|
||||||
|
@ -65,6 +69,17 @@ class WasmGCJSIntrinsic implements WasmGCIntrinsic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private WasmExpression wrapString(Expr stringExpr, WasmGCIntrinsicContext context) {
|
||||||
|
if (stringExpr instanceof ConstantExpr) {
|
||||||
|
var constantExpr = (ConstantExpr) stringExpr;
|
||||||
|
if (constantExpr.getValue() instanceof String) {
|
||||||
|
return commonGen.jsStringConstant(WasmGCJsoContext.wrap(context), (String) constantExpr.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var function = context.functions().forStaticMethod(STRING_TO_JS);
|
||||||
|
return new WasmCall(function, context.generate(stringExpr));
|
||||||
|
}
|
||||||
|
|
||||||
private WasmFunction getGlobalFunction(WasmGCIntrinsicContext context) {
|
private WasmFunction getGlobalFunction(WasmGCIntrinsicContext context) {
|
||||||
if (globalFunction == null) {
|
if (globalFunction == null) {
|
||||||
globalFunction = new WasmFunction(context.functionTypes().of(WasmType.Reference.EXTERN,
|
globalFunction = new WasmFunction(context.functionTypes().of(WasmType.Reference.EXTERN,
|
||||||
|
|
|
@ -36,7 +36,7 @@ public final class WasmGCJso {
|
||||||
wasmGCHost.addIntrinsicFactory(new WasmGCJSBodyRenderer(jsBodyRepository, jsFunctions, commonGen));
|
wasmGCHost.addIntrinsicFactory(new WasmGCJSBodyRenderer(jsBodyRepository, jsFunctions, commonGen));
|
||||||
wasmGCHost.addGeneratorFactory(new WasmGCMarshallMethodGeneratorFactory(commonGen));
|
wasmGCHost.addGeneratorFactory(new WasmGCMarshallMethodGeneratorFactory(commonGen));
|
||||||
|
|
||||||
var jsIntrinsic = new WasmGCJSIntrinsic();
|
var jsIntrinsic = new WasmGCJSIntrinsic(commonGen);
|
||||||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "wrap", String.class, JSObject.class), jsIntrinsic);
|
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "wrap", String.class, JSObject.class), jsIntrinsic);
|
||||||
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "unwrapString", JSObject.class, String.class),
|
wasmGCHost.addIntrinsic(new MethodReference(JS.class, "unwrapString", JSObject.class, String.class),
|
||||||
jsIntrinsic);
|
jsIntrinsic);
|
||||||
|
|
|
@ -17,9 +17,12 @@ package org.teavm.jso.impl.wasmgc;
|
||||||
|
|
||||||
import static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.STRING_TO_JS;
|
import static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.STRING_TO_JS;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.teavm.backend.javascript.rendering.AstWriter;
|
import org.teavm.backend.javascript.rendering.AstWriter;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider;
|
||||||
import org.teavm.backend.wasm.model.WasmFunction;
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
import org.teavm.backend.wasm.model.WasmGlobal;
|
import org.teavm.backend.wasm.model.WasmGlobal;
|
||||||
import org.teavm.backend.wasm.model.WasmLocal;
|
import org.teavm.backend.wasm.model.WasmLocal;
|
||||||
|
@ -46,6 +49,7 @@ class WasmGCJsoCommonGenerator {
|
||||||
private boolean initialized;
|
private boolean initialized;
|
||||||
private List<Consumer<WasmFunction>> initializerParts = new ArrayList<>();
|
private List<Consumer<WasmFunction>> initializerParts = new ArrayList<>();
|
||||||
private boolean rethrowExported;
|
private boolean rethrowExported;
|
||||||
|
private Map<String, WasmGlobal> stringsConstants = new HashMap<>();
|
||||||
|
|
||||||
WasmGCJsoCommonGenerator(WasmGCJSFunctions jsFunctions) {
|
WasmGCJsoCommonGenerator(WasmGCJSFunctions jsFunctions) {
|
||||||
this.jsFunctions = jsFunctions;
|
this.jsFunctions = jsFunctions;
|
||||||
|
@ -159,4 +163,24 @@ class WasmGCJsoCommonGenerator {
|
||||||
throwExpr.getArguments().add(asThrowable);
|
throwExpr.getArguments().add(asThrowable);
|
||||||
fn.getBody().add(throwExpr);
|
fn.getBody().add(throwExpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WasmExpression jsStringConstant(WasmGCJsoContext context, String str) {
|
||||||
|
var global = stringsConstants.computeIfAbsent(str, s -> {
|
||||||
|
var javaGlobal = context.strings().getStringConstant(s).global;
|
||||||
|
var function = context.functions().forStaticMethod(STRING_TO_JS);
|
||||||
|
var index = stringsConstants.size();
|
||||||
|
var brief = str.length() > 16 ? str.substring(0, 16) : str;
|
||||||
|
var name = context.names().topLevel("teavm.js.strings<" + index + ">:"
|
||||||
|
+ WasmGCNameProvider.sanitize(brief));
|
||||||
|
var jsGlobal = new WasmGlobal(name, WasmType.Reference.EXTERN,
|
||||||
|
new WasmNullConstant(WasmType.Reference.EXTERN));
|
||||||
|
context.module().globals.add(jsGlobal);
|
||||||
|
addInitializerPart(context, initializer -> {
|
||||||
|
var call = new WasmCall(function, new WasmGetGlobal(javaGlobal));
|
||||||
|
initializer.getBody().add(new WasmSetGlobal(jsGlobal, call));
|
||||||
|
});
|
||||||
|
return jsGlobal;
|
||||||
|
});
|
||||||
|
return new WasmGetGlobal(global);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user