wasm gc: basic support for functors in JSO

This commit is contained in:
Alexey Andreev 2024-10-01 14:43:53 +02:00
parent 383fee67c5
commit 4b76396332
4 changed files with 37 additions and 6 deletions

View File

@ -22,6 +22,7 @@ TeaVM.wasm = function() {
} }
let javaObjectSymbol = Symbol("javaObject"); let javaObjectSymbol = Symbol("javaObject");
let functionsSymbol = Symbol("functions"); let functionsSymbol = Symbol("functions");
let functionOriginSymbol = Symbol("functionOrigin");
let javaWrappers = new WeakMap(); let javaWrappers = new WeakMap();
function defaults(imports) { function defaults(imports) {
let stderr = ""; let stderr = "";
@ -135,7 +136,7 @@ TeaVM.wasm = function() {
this[functionsSymbol] = null; this[functionsSymbol] = null;
};` };`
); );
return fn(javaObjectSymbol, functionsSymbol); return fn(javaObjectSymbol, functionsSymbol, functionOriginSymbol);
}, },
defineMethod(cls, name, fn) { defineMethod(cls, name, fn) {
cls.prototype[name] = function(...args) { cls.prototype[name] = function(...args) {
@ -181,9 +182,20 @@ TeaVM.wasm = function() {
result = function() { result = function() {
return instance[propertyName].apply(instance, arguments); return instance[propertyName].apply(instance, arguments);
} }
result[functionOriginSymbol] = instance;
functions[propertyName] = result; functions[propertyName] = result;
} }
return result; return result;
},
functionAsObject(fn, property) {
let origin = fn[functionOriginSymbol];
if (typeof origin !== 'undefined') {
let functions = origin[functionsSymbol];
if (functions !== void 0 && functions[property] === fn) {
return origin;
}
}
return { [property]: fn };
} }
}; };
for (let name of ["wrapByte", "wrapShort", "wrapChar", "wrapInt", "wrapFloat", "wrapDouble", "unwrapByte", for (let name of ["wrapByte", "wrapShort", "wrapChar", "wrapInt", "wrapFloat", "wrapDouble", "unwrapByte",

View File

@ -743,6 +743,7 @@ public final class JS {
@GeneratedBy(JSNativeGenerator.class) @GeneratedBy(JSNativeGenerator.class)
@PluggableDependency(JSNativeInjector.class) @PluggableDependency(JSNativeInjector.class)
@Import(name = "functionAsObject", module = "teavmJso")
public static native JSObject functionAsObject(JSObject instance, JSObject property); public static native JSObject functionAsObject(JSObject instance, JSObject property);
@InjectedBy(JSNativeInjector.class) @InjectedBy(JSNativeInjector.class)

View File

@ -87,7 +87,7 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
processor.setClassFilter(classFilter); processor.setClassFilter(classFilter);
} }
processor.processClass(cls); processor.processClass(cls);
if (typeHelper.isJavaScriptClass(cls.getName())) { if (isJavaScriptClass(cls)) {
processor.processMemberMethods(cls); processor.processMemberMethods(cls);
} }
@ -122,17 +122,17 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
exposeMethods(cls, exposedClass, context.getDiagnostics(), functorMethod); exposeMethods(cls, exposedClass, context.getDiagnostics(), functorMethod);
exportStaticMethods(cls, context.getDiagnostics()); exportStaticMethods(cls, context.getDiagnostics());
if (typeHelper.isJavaScriptImplementation(cls.getName()) || !exposedClass.methods.isEmpty()) { if (isJavaScriptImplementation(cls) || !exposedClass.methods.isEmpty()) {
cls.getAnnotations().add(new AnnotationHolder(JSClassToExpose.class.getName())); cls.getAnnotations().add(new AnnotationHolder(JSClassToExpose.class.getName()));
} }
if (wasmGC && (!exposedClass.methods.isEmpty() || typeHelper.isJavaScriptClass(cls.getName()))) { if (wasmGC && (!exposedClass.methods.isEmpty() || isJavaScriptClass(cls))) {
var createWrapperMethod = new MethodHolder(JSMethods.MARSHALL_TO_JS); var createWrapperMethod = new MethodHolder(JSMethods.MARSHALL_TO_JS);
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 (typeHelper.isJavaScriptImplementation(cls.getName())) { if (isJavaScriptImplementation(cls)) {
cls.getInterfaces().add(JSMethods.JS_MARSHALLABLE); cls.getInterfaces().add(JSMethods.JS_MARSHALLABLE);
} }
} }
@ -387,6 +387,21 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
return false; return false;
} }
private boolean isJavaScriptImplementation(ClassReader cls) {
if (typeHelper.isJavaScriptImplementation(cls.getName())) {
return true;
}
if (cls.getAnnotations().get(JSClass.class.getName()) != null) {
return false;
}
if (cls.getParent() != null) {
if (typeHelper.isJavaScriptClass(cls.getParent())) {
return true;
}
}
return cls.getInterfaces().stream().anyMatch(typeHelper::isJavaScriptClass);
}
private boolean addInterfaces(ExposedClass exposedCls, ClassReader cls) { private boolean addInterfaces(ExposedClass exposedCls, ClassReader cls) {
boolean added = false; boolean added = false;
for (String ifaceName : cls.getInterfaces()) { for (String ifaceName : cls.getInterfaces()) {

View File

@ -26,12 +26,13 @@ import org.teavm.jso.JSProperty;
import org.teavm.junit.EachTestCompiledSeparately; import org.teavm.junit.EachTestCompiledSeparately;
import org.teavm.junit.OnlyPlatform; import org.teavm.junit.OnlyPlatform;
import org.teavm.junit.SkipJVM; import org.teavm.junit.SkipJVM;
import org.teavm.junit.SkipPlatform;
import org.teavm.junit.TeaVMTestRunner; import org.teavm.junit.TeaVMTestRunner;
import org.teavm.junit.TestPlatform; import org.teavm.junit.TestPlatform;
@RunWith(TeaVMTestRunner.class) @RunWith(TeaVMTestRunner.class)
@SkipJVM @SkipJVM
@OnlyPlatform(TestPlatform.JAVASCRIPT) @OnlyPlatform({TestPlatform.JAVASCRIPT, TestPlatform.WEBASSEMBLY_GC})
@EachTestCompiledSeparately @EachTestCompiledSeparately
public class FunctorTest { public class FunctorTest {
@Test @Test
@ -45,6 +46,7 @@ public class FunctorTest {
} }
@Test @Test
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void functorIdentityPreserved() { public void functorIdentityPreserved() {
JSBiFunction javaFunction = (a, b) -> a + b; JSBiFunction javaFunction = (a, b) -> a + b;
JSObject firstRef = getFunction(javaFunction); JSObject firstRef = getFunction(javaFunction);
@ -89,6 +91,7 @@ public class FunctorTest {
} }
@Test @Test
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void castToFunctor() { public void castToFunctor() {
JSBiFunction f = getBiFunctionAsObject().cast(); JSBiFunction f = getBiFunctionAsObject().cast();
assertEquals(23042, f.foo(23, 42)); assertEquals(23042, f.foo(23, 42));