mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-11-27 01:30:35 +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 || {};
|
||||
TeaVM.wasm = function() {
|
||||
let exports;
|
||||
let globalsCache = new Map();
|
||||
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) {
|
||||
new Function("value", name + " = value;")(value);
|
||||
@ -237,13 +243,20 @@ TeaVM.wasm = function() {
|
||||
return fn(javaObjectSymbol, functionsSymbol, functionOriginSymbol);
|
||||
},
|
||||
defineMethod(cls, name, fn) {
|
||||
cls.prototype[name] = function(...args) {
|
||||
try {
|
||||
return fn(this, ...args);
|
||||
} catch (e) {
|
||||
rethrowJavaAsJs(e);
|
||||
}
|
||||
let params = [];
|
||||
for (let i = 1; i < fn.length; ++i) {
|
||||
params.push("p" + i);
|
||||
}
|
||||
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) {
|
||||
let descriptor = {
|
||||
@ -390,30 +403,47 @@ TeaVM.wasm = function() {
|
||||
"unwrapShort", "unwrapChar", "unwrapInt", "unwrapFloat", "unwrapDouble"]) {
|
||||
imports.teavmJso[name] = identity;
|
||||
}
|
||||
function wrapCallFromJavaToJs(call) {
|
||||
try {
|
||||
return call();
|
||||
} catch (e) {
|
||||
rethrowJsAsJava(e);
|
||||
}
|
||||
}
|
||||
let argumentList = [];
|
||||
for (let i = 0; i < 32; ++i) {
|
||||
imports.teavmJso["createFunction" + i] = (...args) => new Function(...args);
|
||||
imports.teavmJso["callFunction" + i] = (fn, ...args) => {
|
||||
let args = argumentList.length === 0 ? "" : argumentList.join(", ");
|
||||
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 {
|
||||
return fn(...args);
|
||||
return fn(${args});
|
||||
} catch (e) {
|
||||
rethrowJsAsJava(e);
|
||||
}
|
||||
};
|
||||
imports.teavmJso["callMethod" + i] = (instance, method, ...args) => {
|
||||
`).bind(null, rethrowJsAsJava);
|
||||
imports.teavmJso["callMethod" + i] = new Function("rethrowJsAsJava", "getGlobalName", "instance",
|
||||
"method", ...argumentList, `
|
||||
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) {
|
||||
rethrowJsAsJava(e);
|
||||
}
|
||||
}
|
||||
imports.teavmJso["construct" + i] = (constructor, ...args) => {
|
||||
try {
|
||||
return new constructor(...args);
|
||||
} catch (e) {
|
||||
rethrowJsAsJava(e);
|
||||
}
|
||||
}
|
||||
imports.teavmJso["arrayOf" + i] = (...args) => args
|
||||
`).bind(null, rethrowJsAsJava);
|
||||
imports.teavmJso["arrayOf" + i] = new Function(...argumentList, "return [" + args + "]");
|
||||
|
||||
let param = "p" + (i + 1);
|
||||
argumentList.push(param);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.STRING_TO_JS;
|
||||
import org.teavm.ast.ConstantExpr;
|
||||
import org.teavm.ast.Expr;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext;
|
||||
@ -37,21 +39,23 @@ import org.teavm.model.ValueType;
|
||||
|
||||
class WasmGCJSIntrinsic implements WasmGCIntrinsic {
|
||||
private WasmFunction globalFunction;
|
||||
private WasmGCJsoCommonGenerator commonGen;
|
||||
|
||||
WasmGCJSIntrinsic(WasmGCJsoCommonGenerator commonGen) {
|
||||
this.commonGen = commonGen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "wrap": {
|
||||
var function = context.functions().forStaticMethod(STRING_TO_JS);
|
||||
return new WasmCall(function, context.generate(invocation.getArguments().get(0)));
|
||||
}
|
||||
case "wrap":
|
||||
return wrapString(invocation.getArguments().get(0), context);
|
||||
case "unwrapString": {
|
||||
var function = context.functions().forStaticMethod(JS_TO_STRING);
|
||||
return new WasmCall(function, context.generate(invocation.getArguments().get(0)));
|
||||
}
|
||||
case "global": {
|
||||
var stringToJs = context.functions().forStaticMethod(STRING_TO_JS);
|
||||
var name = new WasmCall(stringToJs, context.generate(invocation.getArguments().get(0)));
|
||||
var name = wrapString(invocation.getArguments().get(0), context);
|
||||
return new WasmCall(getGlobalFunction(context), name);
|
||||
}
|
||||
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) {
|
||||
if (globalFunction == null) {
|
||||
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.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, "unwrapString", JSObject.class, String.class),
|
||||
jsIntrinsic);
|
||||
|
@ -17,9 +17,12 @@ package org.teavm.jso.impl.wasmgc;
|
||||
|
||||
import static org.teavm.jso.impl.wasmgc.WasmGCJSConstants.STRING_TO_JS;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
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.WasmGlobal;
|
||||
import org.teavm.backend.wasm.model.WasmLocal;
|
||||
@ -46,6 +49,7 @@ class WasmGCJsoCommonGenerator {
|
||||
private boolean initialized;
|
||||
private List<Consumer<WasmFunction>> initializerParts = new ArrayList<>();
|
||||
private boolean rethrowExported;
|
||||
private Map<String, WasmGlobal> stringsConstants = new HashMap<>();
|
||||
|
||||
WasmGCJsoCommonGenerator(WasmGCJSFunctions jsFunctions) {
|
||||
this.jsFunctions = jsFunctions;
|
||||
@ -159,4 +163,24 @@ class WasmGCJsoCommonGenerator {
|
||||
throwExpr.getArguments().add(asThrowable);
|
||||
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