wasm gc: fix exporting classes to JS

This commit is contained in:
Alexey Andreev 2024-10-10 14:18:32 +02:00
parent f61d893b6d
commit 14a4a99fa5
10 changed files with 201 additions and 144 deletions

View File

@ -263,15 +263,23 @@ TeaVM.wasmGC = TeaVM.wasmGC || function() {
params.push("p" + i); params.push("p" + i);
} }
let paramsAsString = params.length === 0 ? "" : params.join(", "); let paramsAsString = params.length === 0 ? "" : params.join(", ");
return new Function("rethrowJavaAsJs", "fn", ` return new Function("rethrowJavaAsJs", "fn",
return function(${paramsAsString}) { `return function(${paramsAsString}) {\n` +
try { ` try {\n` +
return fn(${paramsAsString}); ` return fn(${paramsAsString});\n` +
} catch (e) { ` } catch (e) {\n` +
rethrowJavaAsJs(e); ` rethrowJavaAsJs(e);\n` +
` }\n` +
`};`
)(rethrowJavaAsJs, fn);
} }
}; function renameConstructor(name, c) {
`)(rethrowJavaAsJs, fn); return new Function(
"constructor",
`return function ${name}(marker, javaObject) {\n` +
` return constructor.call(this, marker, javaObject);\n` +
`}\n`
)(c);
} }
imports.teavmJso = { imports.teavmJso = {
emptyString: () => "", emptyString: () => "",
@ -295,64 +303,41 @@ TeaVM.wasmGC = TeaVM.wasmGC || function() {
} }
}, },
createClass(name, parent, constructor) { createClass(name, parent, constructor) {
name = sanitizeName(name); name = sanitizeName(name || "JavaObject");
let action;
if (parent === null) { if (parent === null) {
let fn = new Function( action = function (javaObject) {
"javaObjectSymbol",
"functionsSymbol",
"wrapperCallMarker",
"constructor",
"rethrowJavaAsJs",
`let fn;
fn = function ${name}(marker, javaObject) {
if (marker === wrapperCallMarker) {
this[javaObjectSymbol] = javaObject; this[javaObjectSymbol] = javaObject;
this[functionsSymbol] = null; this[functionsSymbol] = null;
} else if (constructor === null) {
throw new Error("This class can't be instantiated directly");
} else {
try {
return fn(wrapperCallMarker, constructor(arguments));
} catch (e) {
rethrowJavaAsJs(e);
}
}
}; };
let boundFn = function(javaObject) { return fn.call(this, wrapperCallMarker, javaObject); };
boundFn[wrapperCallMarker] = fn;
boundFn.prototype = fn.prototype;
return boundFn;`
);
return fn(javaObjectSymbol, functionsSymbol, wrapperCallMarkerSymbol, constructor, rethrowJavaAsJs);
} else { } else {
let fn = new Function( action = function (javaObject) {
"parent",
"wrapperCallMarker",
"constructor",
"rethrowJavaAsJs",
`let fn
fn = function ${name}(marker, javaObject) {
if (marker === wrapperCallMarker) {
parent.call(this, javaObject); parent.call(this, javaObject);
} else if (constructor === null) {
throw new Error("This class can't be instantiated directly");
} else {
try {
return fn(wrapperCallMarker, constructor(arguments));
} catch (e) {
rethrowJavaAsJs(e);
}
}
}; };
fn.prototype = Object.create(parent); fn.prototype = Object.create(parent);
fn.prototype.constructor = parent; fn.prototype.constructor = parent;
let boundFn = function(javaObject) { return fn.call(this, wrapperCallMarker, javaObject); };
boundFn[wrapperCallMarker] = fn;
boundFn.prototype = fn.prototype;
return fn;`
);
return fn(parent, wrapperCallMarkerSymbol, constructor, rethrowJavaAsJs);
} }
let fn = renameConstructor(name, function (marker, javaObject) {
if (marker === wrapperCallMarkerSymbol) {
action.call(this, javaObject);
} else if (constructor === null) {
throw new Error("This class can't be instantiated directly");
} else {
try {
return constructor.apply(null, arguments);
} catch (e) {
rethrowJavaAsJs(e);
}
}
});
fn.prototype = Object.create(parent || Object.prototype);
fn.prototype.constructor = fn;
let boundFn = renameConstructor(name, function(javaObject) {
return fn.call(this, wrapperCallMarkerSymbol, javaObject);
});
boundFn[wrapperCallMarkerSymbol] = fn;
boundFn.prototype = fn.prototype;
return boundFn;
}, },
exportClass(cls) { exportClass(cls) {
return cls[wrapperCallMarkerSymbol]; return cls[wrapperCallMarkerSymbol];
@ -363,15 +348,15 @@ TeaVM.wasmGC = TeaVM.wasmGC || function() {
params.push("p" + i); params.push("p" + i);
} }
let paramsAsString = params.length === 0 ? "" : params.join(", "); let paramsAsString = params.length === 0 ? "" : params.join(", ");
cls.prototype[name] = new Function("rethrowJavaAsJs", "fn", ` cls.prototype[name] = new Function("rethrowJavaAsJs", "fn",
return function(${paramsAsString}) { `return function(${paramsAsString}) {\n` +
try { ` try {\n` +
return fn(${['this', params].join(", ")}); ` return fn(${['this', params].join(", ")});\n` +
} catch (e) { ` } catch (e) {\n` +
rethrowJavaAsJs(e); ` rethrowJavaAsJs(e);\n` +
} ` }\n` +
}; `};`
`)(rethrowJavaAsJs, fn); )(rethrowJavaAsJs, fn);
}, },
defineStaticMethod(cls, name, fn) { defineStaticMethod(cls, name, fn) {
cls[name] = defineFunction(fn); cls[name] = defineFunction(fn);
@ -554,32 +539,33 @@ TeaVM.wasmGC = TeaVM.wasmGC || function() {
for (let i = 0; i < 32; ++i) { for (let i = 0; i < 32; ++i) {
let args = argumentList.length === 0 ? "" : argumentList.join(", "); let args = argumentList.length === 0 ? "" : argumentList.join(", ");
let argsAndBody = [...argumentList, "body"].join(", "); let argsAndBody = [...argumentList, "body"].join(", ");
imports.teavmJso["createFunction" + i] = new Function("wrapCallFromJavaToJs", ...argumentList, "body", ` imports.teavmJso["createFunction" + i] = new Function("wrapCallFromJavaToJs", ...argumentList, "body",
return new Function('wrapCallFromJavaToJs', ${argsAndBody}).bind(this, wrapCallFromJavaToJs); `return new Function('wrapCallFromJavaToJs', ${argsAndBody}).bind(this, wrapCallFromJavaToJs);`
`).bind(null, wrapCallFromJavaToJs); ).bind(null, wrapCallFromJavaToJs);
imports.teavmJso["callFunction" + i] = new Function("rethrowJsAsJava", "fn", ...argumentList, ` imports.teavmJso["callFunction" + i] = new Function("rethrowJsAsJava", "fn", ...argumentList,
try { `try {\n` +
return fn(${args}); ` return fn(${args});\n` +
} catch (e) { `} catch (e) {\n` +
rethrowJsAsJava(e); ` rethrowJsAsJava(e);\n` +
} `}`
`).bind(null, rethrowJsAsJava); ).bind(null, rethrowJsAsJava);
imports.teavmJso["callMethod" + i] = new Function("rethrowJsAsJava", "getGlobalName", "instance", imports.teavmJso["callMethod" + i] = new Function("rethrowJsAsJava", "getGlobalName", "instance",
"method", ...argumentList, ` "method", ...argumentList,
try { `try {\n`+
return instance !== null ` return instance !== null\n` +
? instance[method](${args}) ` ? instance[method](${args})\n` +
: getGlobalName(method)(${args}); ` : getGlobalName(method)(${args});\n` +
} catch (e) { `} catch (e) {\n` +
rethrowJsAsJava(e); ` rethrowJsAsJava(e);\n` +
}`).bind(null, rethrowJsAsJava, getGlobalName); `}`
imports.teavmJso["construct" + i] = new Function("rethrowJsAsJava", "constructor", ...argumentList, ` ).bind(null, rethrowJsAsJava, getGlobalName);
try { imports.teavmJso["construct" + i] = new Function("rethrowJsAsJava", "constructor", ...argumentList,
return new constructor(${args}); `try {\n` +
} catch (e) { ` return new constructor(${args});\n` +
rethrowJsAsJava(e); `} catch (e) {\n` +
} ` rethrowJsAsJava(e);\n` +
`).bind(null, rethrowJsAsJava); `}`
).bind(null, rethrowJsAsJava);
imports.teavmJso["arrayOf" + i] = new Function(...argumentList, "return [" + args + "]"); imports.teavmJso["arrayOf" + i] = new Function(...argumentList, "return [" + args + "]");
let param = "p" + (i + 1); let param = "p" + (i + 1);

View File

@ -139,12 +139,9 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
createWrapperMethod.setLevel(AccessLevel.PUBLIC); createWrapperMethod.setLevel(AccessLevel.PUBLIC);
createWrapperMethod.getModifiers().add(ElementModifier.NATIVE); createWrapperMethod.getModifiers().add(ElementModifier.NATIVE);
cls.addMethod(createWrapperMethod); cls.addMethod(createWrapperMethod);
if (!isJavaScriptClass(cls)) {
cls.getInterfaces().add(JSMethods.JS_MARSHALLABLE); cls.getInterfaces().add(JSMethods.JS_MARSHALLABLE);
} }
} }
}
private void exposeMethods(ClassHolder classHolder, ExposedClass classToExpose, Diagnostics diagnostics, private void exposeMethods(ClassHolder classHolder, ExposedClass classToExpose, Diagnostics diagnostics,
MethodReference functorMethod) { MethodReference functorMethod) {

View File

@ -66,6 +66,8 @@ final class WasmGCJSRuntime {
@Import(name = "charAt", module = "teavmJso") @Import(name = "charAt", module = "teavmJso")
static native char charAt(JSObject str, int index); static native char charAt(JSObject str, int index);
static native JSObject wrapObject(Object obj);
static Throwable wrapException(JSObject obj) { static Throwable wrapException(JSObject obj) {
return new WasmGCExceptionWrapper(obj); return new WasmGCExceptionWrapper(obj);
} }

View File

@ -0,0 +1,40 @@
/*
* Copyright 2024 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.jso.impl.wasmgc;
import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic;
import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
class WasmGCJSRuntimeIntrinsic implements WasmGCIntrinsic {
private WasmGCJsoCommonGenerator commonGen;
WasmGCJSRuntimeIntrinsic(WasmGCJsoCommonGenerator commonGen) {
this.commonGen = commonGen;
}
@Override
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
var jsoContext = WasmGCJsoContext.wrap(context);
var wrapperClass = commonGen.getDefaultWrapperClass(jsoContext);
var wrapperFunction = commonGen.javaObjectToJSFunction(jsoContext);
return new WasmCall(wrapperFunction, context.generate(invocation.getArguments().get(0)),
new WasmGetGlobal(wrapperClass));
}
}

View File

@ -21,7 +21,6 @@ import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmCast;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmExternConversion; import org.teavm.backend.wasm.model.expression.WasmExternConversion;
import org.teavm.backend.wasm.model.expression.WasmExternConversionType; import org.teavm.backend.wasm.model.expression.WasmExternConversionType;
@ -41,17 +40,6 @@ class WasmGCJSWrapperIntrinsic implements WasmGCIntrinsic {
var function = getWrapFunction(context); var function = getWrapFunction(context);
return new WasmCall(function, context.generate(invocation.getArguments().get(0))); return new WasmCall(function, context.generate(invocation.getArguments().get(0)));
} }
case "dependencyJavaToJs":
case "directJavaToJs":
return new WasmExternConversion(WasmExternConversionType.ANY_TO_EXTERN,
context.generate(invocation.getArguments().get(0)));
case "dependencyJsToJava":
case "directJsToJava": {
var any = new WasmExternConversion(WasmExternConversionType.EXTERN_TO_ANY,
context.generate(invocation.getArguments().get(0)));
var objectType = context.typeMapper().mapType(ValueType.parse(Object.class));
return new WasmCast(any, (WasmType.Reference) objectType);
}
case "isJava": { case "isJava": {
var convert = new WasmExternConversion(WasmExternConversionType.EXTERN_TO_ANY, var convert = new WasmExternConversion(WasmExternConversionType.EXTERN_TO_ANY,
context.generate(invocation.getArguments().get(0))); context.generate(invocation.getArguments().get(0)));

View File

@ -25,6 +25,7 @@ import org.teavm.model.ClassHolderTransformerContext;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.ValueType;
import org.teavm.model.emit.ProgramEmitter; import org.teavm.model.emit.ProgramEmitter;
class WasmGCJSWrapperTransformer implements ClassHolderTransformer { class WasmGCJSWrapperTransformer implements ClassHolderTransformer {
@ -33,6 +34,12 @@ class WasmGCJSWrapperTransformer implements ClassHolderTransformer {
if (cls.getName().equals(JSWrapper.class.getName())) { if (cls.getName().equals(JSWrapper.class.getName())) {
transformMarshallMethod(cls.getMethod(new MethodDescriptor("marshallJavaToJs", Object.class, transformMarshallMethod(cls.getMethod(new MethodDescriptor("marshallJavaToJs", Object.class,
JSObject.class)), context); JSObject.class)), context);
transformDirectJavaToJs(cls.getMethod(new MethodDescriptor("directJavaToJs", Object.class,
JSObject.class)), context);
transformDirectJavaToJs(cls.getMethod(new MethodDescriptor("dependencyJavaToJs", Object.class,
JSObject.class)), context);
transformDirectJsToJava(cls.getMethod(new MethodDescriptor("dependencyJsToJava", JSObject.class,
Object.class)), context);
transformWrapMethod(cls.getMethod(new MethodDescriptor("wrap", JSObject.class, Object.class))); transformWrapMethod(cls.getMethod(new MethodDescriptor("wrap", JSObject.class, Object.class)));
transformIsJava(cls.getMethod(new MethodDescriptor("isJava", Object.class, boolean.class)), context); transformIsJava(cls.getMethod(new MethodDescriptor("isJava", Object.class, boolean.class)), context);
addCreateWrapperMethod(cls, context); addCreateWrapperMethod(cls, context);
@ -43,9 +50,26 @@ class WasmGCJSWrapperTransformer implements ClassHolderTransformer {
method.getModifiers().remove(ElementModifier.NATIVE); method.getModifiers().remove(ElementModifier.NATIVE);
var pe = ProgramEmitter.create(method, context.getHierarchy()); var pe = ProgramEmitter.create(method, context.getHierarchy());
var obj = pe.var(1, Object.class); var obj = pe.var(1, Object.class);
pe.when(obj.instanceOf(ValueType.parse(JSMarshallable.class)).isFalse()).thenDo(() -> {
pe.invoke(WasmGCJSRuntime.class, "wrapObject", JSObject.class, obj).returnValue();
});
obj.cast(JSMarshallable.class).invokeVirtual("marshallToJs", JSObject.class).returnValue(); obj.cast(JSMarshallable.class).invokeVirtual("marshallToJs", JSObject.class).returnValue();
} }
private void transformDirectJavaToJs(MethodHolder method, ClassHolderTransformerContext context) {
method.getModifiers().remove(ElementModifier.NATIVE);
var pe = ProgramEmitter.create(method, context.getHierarchy());
var obj = pe.var(1, Object.class);
pe.invoke(JSWrapper.class, "marshallJavaToJs", JSObject.class, obj).returnValue();
}
private void transformDirectJsToJava(MethodHolder method, ClassHolderTransformerContext context) {
method.getModifiers().remove(ElementModifier.NATIVE);
var pe = ProgramEmitter.create(method, context.getHierarchy());
var obj = pe.var(1, JSObject.class);
pe.invoke(JSWrapper.class, "unmarshallJavaFromJs", Object.class, obj).returnValue();
}
private void transformWrapMethod(MethodHolder method) { private void transformWrapMethod(MethodHolder method) {
method.getModifiers().add(ElementModifier.NATIVE); method.getModifiers().add(ElementModifier.NATIVE);
method.setProgram(null); method.setProgram(null);

View File

@ -57,15 +57,10 @@ public final class WasmGCJso {
var wrapperIntrinsic = new WasmGCJSWrapperIntrinsic(); var wrapperIntrinsic = new WasmGCJSWrapperIntrinsic();
wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "wrap", JSObject.class, Object.class), wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "wrap", JSObject.class, Object.class),
wrapperIntrinsic); wrapperIntrinsic);
wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "dependencyJavaToJs", Object.class,
JSObject.class), wrapperIntrinsic);
wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "directJavaToJs", Object.class, JSObject.class),
wrapperIntrinsic);
wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "dependencyJsToJava", JSObject.class,
Object.class), wrapperIntrinsic);
wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "directJsToJava", JSObject.class, Object.class),
wrapperIntrinsic);
wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "isJava", JSObject.class, boolean.class), wasmGCHost.addIntrinsic(new MethodReference(JSWrapper.class, "isJava", JSObject.class, boolean.class),
wrapperIntrinsic); wrapperIntrinsic);
wasmGCHost.addIntrinsic(new MethodReference(WasmGCJSRuntime.class, "wrapObject", Object.class,
JSObject.class), new WasmGCJSRuntimeIntrinsic(commonGen));
} }
} }

View File

@ -63,6 +63,8 @@ class WasmGCJsoCommonGenerator {
private WasmFunction definePropertyFunction; private WasmFunction definePropertyFunction;
private WasmFunction defineStaticPropertyFunction; private WasmFunction defineStaticPropertyFunction;
private WasmFunction exportClassFunction; private WasmFunction exportClassFunction;
private WasmFunction javaObjectToJSFunction;
private WasmGlobal defaultWrapperClass;
private Map<String, WasmGlobal> definedClasses = new HashMap<>(); private Map<String, WasmGlobal> definedClasses = new HashMap<>();
WasmGCJsoCommonGenerator(WasmGCJSFunctions jsFunctions) { WasmGCJsoCommonGenerator(WasmGCJSFunctions jsFunctions) {
@ -198,6 +200,23 @@ class WasmGCJsoCommonGenerator {
return new WasmGetGlobal(global); return new WasmGetGlobal(global);
} }
WasmGlobal getDefaultWrapperClass(WasmGCJsoContext context) {
if (defaultWrapperClass == null) {
var name = context.names().topLevel("teavm.js@defaultWrapperClass");
defaultWrapperClass = new WasmGlobal(name, WasmType.Reference.EXTERN,
new WasmNullConstant(WasmType.Reference.EXTERN));
context.module().globals.add(defaultWrapperClass);
addInitializerPart(context, initializer -> {
var createClass = new WasmCall(createClassFunction(context),
new WasmNullConstant(WasmType.Reference.EXTERN),
new WasmNullConstant(WasmType.Reference.EXTERN),
new WasmNullConstant(WasmType.Reference.FUNC));
initializer.getBody().add(new WasmSetGlobal(defaultWrapperClass, createClass));
});
}
return defaultWrapperClass;
}
WasmGlobal getDefinedClass(WasmGCJsoContext context, String className) { WasmGlobal getDefinedClass(WasmGCJsoContext context, String className) {
return definedClasses.computeIfAbsent(className, n -> defineClass(context, n)); return definedClasses.computeIfAbsent(className, n -> defineClass(context, n));
} }
@ -216,8 +235,6 @@ class WasmGCJsoCommonGenerator {
defineProperties(context, members, cls, global, expressions); defineProperties(context, members, cls, global, expressions);
var staticMembers = AliasCollector.collectMembers(cls, AliasCollector::isStaticMember); var staticMembers = AliasCollector.collectMembers(cls, AliasCollector::isStaticMember);
defineStaticMethods(context, staticMembers, cls, global, expressions, isModule);
defineStaticProperties(context, staticMembers, cls, global, expressions);
var simpleName = className.substring(className.lastIndexOf('.') + 1); var simpleName = className.substring(className.lastIndexOf('.') + 1);
var javaClassName = context.strings().getStringConstant(simpleName); var javaClassName = context.strings().getStringConstant(simpleName);
@ -233,6 +250,7 @@ class WasmGCJsoCommonGenerator {
WasmExpression constructor; WasmExpression constructor;
if (members.constructor != null) { if (members.constructor != null) {
var function = context.functions().forStaticMethod(members.constructor); var function = context.functions().forStaticMethod(members.constructor);
function.setReferenced(true);
constructor = new WasmFunctionReference(function); constructor = new WasmFunctionReference(function);
needsExport = true; needsExport = true;
} else { } else {
@ -240,10 +258,14 @@ class WasmGCJsoCommonGenerator {
} }
var createClass = new WasmCall(createClassFunction(context), jsClassName, jsExportedParent, constructor); var createClass = new WasmCall(createClassFunction(context), jsClassName, jsExportedParent, constructor);
expressions.add(0, new WasmSetGlobal(global, createClass)); expressions.add(0, new WasmSetGlobal(global, createClass));
var globalForStatic = global;
if (needsExport) { if (needsExport) {
exportClass(context, cls, global, expressions); globalForStatic = exportClass(context, cls, global, expressions);
} }
defineStaticMethods(context, staticMembers, cls, globalForStatic, expressions, isModule);
defineStaticProperties(context, staticMembers, cls, globalForStatic, expressions);
context.addToInitializer(f -> f.getBody().addAll(expressions)); context.addToInitializer(f -> f.getBody().addAll(expressions));
return global; return global;
} }
@ -340,7 +362,7 @@ class WasmGCJsoCommonGenerator {
} }
} }
private void exportClass(WasmGCJsoContext context, ClassReader cls, WasmGlobal global, private WasmGlobal exportClass(WasmGCJsoContext context, ClassReader cls, WasmGlobal global,
List<WasmExpression> expressions) { List<WasmExpression> expressions) {
var exportName = getClassAliasName(cls); var exportName = getClassAliasName(cls);
var globalName = context.names().topLevel("teavm.js.export.class@" + exportName); var globalName = context.names().topLevel("teavm.js.export.class@" + exportName);
@ -351,6 +373,8 @@ class WasmGCJsoCommonGenerator {
var exported = new WasmCall(exportClassFunction(context), new WasmGetGlobal(global)); var exported = new WasmCall(exportClassFunction(context), new WasmGetGlobal(global));
expressions.add(new WasmSetGlobal(exportGlobal, exported)); expressions.add(new WasmSetGlobal(exportGlobal, exported));
return exportGlobal;
} }
private String parentExportedClass(WasmGCJsoContext context, String className) { private String parentExportedClass(WasmGCJsoContext context, String className) {
@ -453,6 +477,18 @@ class WasmGCJsoCommonGenerator {
return exportClassFunction; return exportClassFunction;
} }
WasmFunction javaObjectToJSFunction(WasmGCJsoContext context) {
if (javaObjectToJSFunction == null) {
javaObjectToJSFunction = new WasmFunction(context.functionTypes().of(WasmType.Reference.EXTERN,
context.typeMapper().mapType(ValueType.parse(Object.class)), WasmType.Reference.EXTERN));
javaObjectToJSFunction.setName(context.names().topLevel("teavm.jso@javaObjectToJS"));
javaObjectToJSFunction.setImportName("javaObjectToJS");
javaObjectToJSFunction.setImportModule("teavmJso");
context.module().functions.add(javaObjectToJSFunction);
}
return javaObjectToJSFunction;
}
private String getClassAliasName(ClassReader cls) { private String getClassAliasName(ClassReader cls) {
var name = cls.getSimpleName(); var name = cls.getSimpleName();
if (name == null) { if (name == null) {

View File

@ -19,7 +19,6 @@ import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerator;
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorContext; import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorContext;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal; 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.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal; import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
import org.teavm.backend.wasm.model.expression.WasmGetLocal; import org.teavm.backend.wasm.model.expression.WasmGetLocal;
@ -28,7 +27,6 @@ import org.teavm.model.ValueType;
class WasmGCMarshallMethodGenerator implements WasmGCCustomGenerator { class WasmGCMarshallMethodGenerator implements WasmGCCustomGenerator {
private WasmGCJsoCommonGenerator commonGen; private WasmGCJsoCommonGenerator commonGen;
private WasmFunction javaObjectToJSFunction;
WasmGCMarshallMethodGenerator(WasmGCJsoCommonGenerator commonGen) { WasmGCMarshallMethodGenerator(WasmGCJsoCommonGenerator commonGen) {
this.commonGen = commonGen; this.commonGen = commonGen;
@ -42,21 +40,8 @@ class WasmGCMarshallMethodGenerator implements WasmGCCustomGenerator {
function.add(thisLocal); function.add(thisLocal);
var jsClassGlobal = commonGen.getDefinedClass(jsoContext, method.getClassName()); var jsClassGlobal = commonGen.getDefinedClass(jsoContext, method.getClassName());
var wrapperFunction = javaObjectToJSFunction(context); var wrapperFunction = commonGen.javaObjectToJSFunction(jsoContext);
function.getBody().add(new WasmCall(wrapperFunction, new WasmGetLocal(thisLocal), function.getBody().add(new WasmCall(wrapperFunction, new WasmGetLocal(thisLocal),
new WasmGetGlobal(jsClassGlobal))); new WasmGetGlobal(jsClassGlobal)));
} }
private WasmFunction javaObjectToJSFunction(WasmGCCustomGeneratorContext context) {
if (javaObjectToJSFunction == null) {
javaObjectToJSFunction = new WasmFunction(context.functionTypes().of(WasmType.Reference.EXTERN,
context.typeMapper().mapType(ValueType.parse(Object.class)), WasmType.Reference.EXTERN));
javaObjectToJSFunction.setName(context.names().topLevel("teavm.jso@javaObjectToJS"));
javaObjectToJSFunction.setImportName("javaObjectToJS");
javaObjectToJSFunction.setImportModule("teavmJso");
context.module().functions.add(javaObjectToJSFunction);
}
return javaObjectToJSFunction;
}
} }

View File

@ -112,14 +112,18 @@ public class ExportTest {
@Test @Test
public void varargs() { public void varargs() {
testExport("varargs", ModuleWithVararg.class); testExport("varargs", ModuleWithVararg.class, true);
} }
private void testExport(String name, Class<?> moduleClass) { private void testExport(String name, Class<?> moduleClass) {
testExport(name, moduleClass, false);
}
private void testExport(String name, Class<?> moduleClass, boolean skipWasmGC) {
if (jsNeeded) { if (jsNeeded) {
testExportJs(name, moduleClass); testExportJs(name, moduleClass);
} }
if (wasmGCNeeded) { if (wasmGCNeeded && !skipWasmGC) {
testExportWasmGC(name, moduleClass); testExportWasmGC(name, moduleClass);
} }
} }