mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-11-21 01:00:54 +08:00
wasm gc: fully support exporting classes to JS
This commit is contained in:
parent
0dcc25d66b
commit
f61d893b6d
@ -93,17 +93,12 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
||||
private boolean exportClassInstanceMembers(ClassReader classReader) {
|
||||
var members = collectMembers(classReader, AliasCollector::isInstanceMember);
|
||||
|
||||
var isJsClassImpl = typeHelper.isJavaScriptImplementation(classReader.getName());
|
||||
if (members.methods.isEmpty() && members.properties.isEmpty() && !isJsClassImpl) {
|
||||
if (members.methods.isEmpty() && members.properties.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
writer.append("c").ws().append("=").ws().appendClass(classReader.getName()).append(".prototype;")
|
||||
.softNewLine();
|
||||
if (isJsClassImpl) {
|
||||
writer.append("c[").appendFunction("$rt_jso_marker").append("]").ws().append("=").ws().append("true;")
|
||||
.softNewLine();
|
||||
}
|
||||
|
||||
for (var aliasEntry : members.methods.entrySet()) {
|
||||
if (classReader.getMethod(aliasEntry.getValue().getDescriptor()) == null) {
|
||||
@ -253,9 +248,6 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
||||
private boolean hasClassesToExpose() {
|
||||
for (String className : classSource.getClassNames()) {
|
||||
ClassReader cls = classSource.get(className);
|
||||
if (typeHelper.isJavaScriptImplementation(className)) {
|
||||
return true;
|
||||
}
|
||||
for (var method : cls.getMethods()) {
|
||||
if (getPublicAlias(method) != null) {
|
||||
return true;
|
||||
@ -348,6 +340,4 @@ class JSAliasRenderer implements RendererListener, MethodContributor {
|
||||
MethodReader methodReader = classReader.getMethod(methodRef.getDescriptor());
|
||||
return methodReader != null && getPublicAlias(methodReader) != null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -98,7 +98,5 @@ public class JSOPlugin implements TeaVMPlugin {
|
||||
wrapperGenerator);
|
||||
jsHost.add(new MethodReference(JSWrapper.class, "jsToWrapper", JSObject.class, JSWrapper.class),
|
||||
wrapperGenerator);
|
||||
jsHost.add(new MethodReference(JSWrapper.class, "isJSImplementation", Object.class, boolean.class),
|
||||
wrapperGenerator);
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||
processor.setClassFilter(classFilter);
|
||||
}
|
||||
processor.processClass(cls);
|
||||
if (isJavaScriptClass(cls)) {
|
||||
if (isJavaScriptClass(cls) && !isJavaScriptImplementation(cls)) {
|
||||
processor.processMemberMethods(cls);
|
||||
}
|
||||
|
||||
@ -99,44 +99,48 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||
}
|
||||
processor.createJSMethods(cls);
|
||||
|
||||
if (cls.hasModifier(ElementModifier.ABSTRACT)
|
||||
|| cls.getAnnotations().get(JSClass.class.getName()) != null && isJavaScriptClass(cls)) {
|
||||
if (isJavaScriptClass(cls) && !isJavaScriptImplementation(cls)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MethodReference functorMethod = processor.isFunctor(cls.getName());
|
||||
if (functorMethod != null) {
|
||||
if (processor.isFunctor(cls.getParent()) != null) {
|
||||
functorMethod = null;
|
||||
var hasStaticMethods = false;
|
||||
var hasMemberMethods = false;
|
||||
|
||||
if (!cls.hasModifier(ElementModifier.ABSTRACT)) {
|
||||
MethodReference functorMethod = processor.isFunctor(cls.getName());
|
||||
if (functorMethod != null) {
|
||||
if (processor.isFunctor(cls.getParent()) != null) {
|
||||
functorMethod = null;
|
||||
}
|
||||
}
|
||||
|
||||
ClassReader originalClass = hierarchy.getClassSource().get(cls.getName());
|
||||
ExposedClass exposedClass;
|
||||
if (originalClass != null) {
|
||||
exposedClass = getExposedClass(cls.getName());
|
||||
} else {
|
||||
exposedClass = new ExposedClass();
|
||||
createExposedClass(cls, exposedClass);
|
||||
}
|
||||
|
||||
exposeMethods(cls, exposedClass, context.getDiagnostics(), functorMethod);
|
||||
if (!exposedClass.methods.isEmpty()) {
|
||||
hasMemberMethods = true;
|
||||
cls.getAnnotations().add(new AnnotationHolder(JSClassToExpose.class.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
ClassReader originalClass = hierarchy.getClassSource().get(cls.getName());
|
||||
ExposedClass exposedClass;
|
||||
if (originalClass != null) {
|
||||
exposedClass = getExposedClass(cls.getName());
|
||||
} else {
|
||||
exposedClass = new ExposedClass();
|
||||
createExposedClass(cls, exposedClass);
|
||||
}
|
||||
|
||||
exposeMethods(cls, exposedClass, context.getDiagnostics(), functorMethod);
|
||||
var hasStaticMethods = exportStaticMethods(cls, context.getDiagnostics());
|
||||
|
||||
if (isJavaScriptImplementation(cls) || !exposedClass.methods.isEmpty()) {
|
||||
cls.getAnnotations().add(new AnnotationHolder(JSClassToExpose.class.getName()));
|
||||
}
|
||||
if (isJavaScriptImplementation(cls) || !exposedClass.methods.isEmpty() || hasStaticMethods) {
|
||||
hasStaticMethods = exportStaticMethods(cls, context.getDiagnostics());
|
||||
if (hasMemberMethods || hasStaticMethods) {
|
||||
cls.getAnnotations().add(new AnnotationHolder(JSClassObjectToExpose.class.getName()));
|
||||
}
|
||||
|
||||
if (wasmGC && (!exposedClass.methods.isEmpty() || isJavaScriptClass(cls))) {
|
||||
if (wasmGC && hasMemberMethods) {
|
||||
var createWrapperMethod = new MethodHolder(JSMethods.MARSHALL_TO_JS);
|
||||
createWrapperMethod.setLevel(AccessLevel.PUBLIC);
|
||||
createWrapperMethod.getModifiers().add(ElementModifier.NATIVE);
|
||||
cls.addMethod(createWrapperMethod);
|
||||
|
||||
if (!isJavaScriptClass(cls) || isJavaScriptImplementation(cls)) {
|
||||
if (!isJavaScriptClass(cls)) {
|
||||
cls.getInterfaces().add(JSMethods.JS_MARSHALLABLE);
|
||||
}
|
||||
}
|
||||
@ -429,7 +433,7 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||
if (typeHelper.isJavaScriptImplementation(cls.getName())) {
|
||||
return true;
|
||||
}
|
||||
if (cls.getAnnotations().get(JSClass.class.getName()) != null) {
|
||||
if (cls.getAnnotations().get(JSClass.class.getName()) != null || cls.hasModifier(ElementModifier.ABSTRACT)) {
|
||||
return false;
|
||||
}
|
||||
if (cls.getParent() != null) {
|
||||
|
@ -84,7 +84,8 @@ public class JSTypeHelper {
|
||||
return false;
|
||||
}
|
||||
ClassReader cls = classSource.get(className);
|
||||
if (cls == null || cls.getAnnotations().get(JSClass.class.getName()) != null) {
|
||||
if (cls == null || cls.getAnnotations().get(JSClass.class.getName()) != null
|
||||
|| cls.hasModifier(ElementModifier.ABSTRACT)) {
|
||||
return false;
|
||||
}
|
||||
if (cls.getParent() != null) {
|
||||
|
@ -188,14 +188,11 @@ public final class JSWrapper {
|
||||
@NoSideEffects
|
||||
public static native boolean isJava(JSObject obj);
|
||||
|
||||
@NoSideEffects
|
||||
private static native boolean isJSImplementation(Object obj);
|
||||
|
||||
public static JSObject unwrap(Object o) {
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
return isJSImplementation(o) ? marshallJavaToJs(o) : ((JSWrapper) o).js;
|
||||
return (!(o instanceof JSWrapper)) ? marshallJavaToJs(o) : ((JSWrapper) o).js;
|
||||
}
|
||||
|
||||
public static JSObject maybeUnwrap(Object o) {
|
||||
|
@ -50,17 +50,6 @@ public class JSWrapperGenerator implements Injector, DependencyPlugin {
|
||||
context.getWriter().append(")");
|
||||
}
|
||||
break;
|
||||
case "isJSImplementation":
|
||||
if (context.getPrecedence().ordinal() >= Precedence.EQUALITY.ordinal()) {
|
||||
context.getWriter().append("(");
|
||||
}
|
||||
context.writeExpr(context.getArgument(0), Precedence.MEMBER_ACCESS);
|
||||
context.getWriter().append("[").appendFunction("$rt_jso_marker").append("]")
|
||||
.ws().append("===").ws().append("true");
|
||||
if (context.getPrecedence().ordinal() >= Precedence.EQUALITY.ordinal()) {
|
||||
context.getWriter().append(")");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,6 @@ import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.emit.ProgramEmitter;
|
||||
|
||||
class WasmGCJSWrapperTransformer implements ClassHolderTransformer {
|
||||
@ -35,8 +34,6 @@ class WasmGCJSWrapperTransformer implements ClassHolderTransformer {
|
||||
transformMarshallMethod(cls.getMethod(new MethodDescriptor("marshallJavaToJs", Object.class,
|
||||
JSObject.class)), context);
|
||||
transformWrapMethod(cls.getMethod(new MethodDescriptor("wrap", JSObject.class, Object.class)));
|
||||
transformIsJsImplementation(cls.getMethod(new MethodDescriptor("isJSImplementation",
|
||||
Object.class, boolean.class)), context);
|
||||
transformIsJava(cls.getMethod(new MethodDescriptor("isJava", Object.class, boolean.class)), context);
|
||||
addCreateWrapperMethod(cls, context);
|
||||
}
|
||||
@ -54,13 +51,6 @@ class WasmGCJSWrapperTransformer implements ClassHolderTransformer {
|
||||
method.setProgram(null);
|
||||
}
|
||||
|
||||
private void transformIsJsImplementation(MethodHolder method, ClassHolderTransformerContext context) {
|
||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
var pe = ProgramEmitter.create(method, context.getHierarchy());
|
||||
var obj = pe.var(1, JSObject.class);
|
||||
obj.instanceOf(ValueType.parse(JSMarshallable.class)).returnValue();
|
||||
}
|
||||
|
||||
private void addCreateWrapperMethod(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
var method = new MethodHolder(new MethodDescriptor("createWrapper", JSObject.class, Object.class));
|
||||
method.getModifiers().add(ElementModifier.STATIC);
|
||||
|
@ -97,7 +97,7 @@ public class ExportTest {
|
||||
|
||||
@Test
|
||||
public void exportClassMembers() {
|
||||
testExport("exportClassMembers", ModuleWithExportedClassMembers.class, true);
|
||||
testExport("exportClassMembers", ModuleWithExportedClassMembers.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -107,23 +107,19 @@ public class ExportTest {
|
||||
|
||||
@Test
|
||||
public void exportClasses() {
|
||||
testExport("exportClasses", ModuleWithExportedClasses.class, true);
|
||||
testExport("exportClasses", ModuleWithExportedClasses.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void varargs() {
|
||||
testExport("varargs", ModuleWithVararg.class, true);
|
||||
testExport("varargs", ModuleWithVararg.class);
|
||||
}
|
||||
|
||||
private void testExport(String name, Class<?> moduleClass) {
|
||||
testExport(name, moduleClass, false);
|
||||
}
|
||||
|
||||
private void testExport(String name, Class<?> moduleClass, boolean skipWasm) {
|
||||
if (jsNeeded) {
|
||||
testExportJs(name, moduleClass);
|
||||
}
|
||||
if (wasmGCNeeded && !skipWasm) {
|
||||
if (wasmGCNeeded) {
|
||||
testExportWasmGC(name, moduleClass);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user