JS: a better fix for wrapping/unwrapping JS objects implemented in Java

This commit is contained in:
Alexey Andreev 2023-11-27 13:05:51 +01:00
parent b3e889fa11
commit 067f7453fe
4 changed files with 37 additions and 15 deletions

View File

@ -35,11 +35,13 @@ class JSAliasRenderer implements RendererListener, VirtualMethodContributor {
private static String variableChars = "abcdefghijklmnopqrstuvwxyz"; private static String variableChars = "abcdefghijklmnopqrstuvwxyz";
private SourceWriter writer; private SourceWriter writer;
private ListableClassReaderSource classSource; private ListableClassReaderSource classSource;
private JSTypeHelper typeHelper;
@Override @Override
public void begin(RenderingManager context, BuildTarget buildTarget) { public void begin(RenderingManager context, BuildTarget buildTarget) {
writer = context.getWriter(); writer = context.getWriter();
classSource = context.getClassSource(); classSource = context.getClassSource();
typeHelper = new JSTypeHelper(context.getClassSource());
} }
@Override @Override
@ -48,6 +50,8 @@ class JSAliasRenderer implements RendererListener, VirtualMethodContributor {
return; return;
} }
writer.append("let ").appendFunction("$rt_jso_marker").ws().append("=").ws()
.appendGlobal("Symbol").append("('jsoClass');").newLine();
writer.append("(function()").ws().append("{").softNewLine().indent(); writer.append("(function()").ws().append("{").softNewLine().indent();
writer.append("var c;").softNewLine(); writer.append("var c;").softNewLine();
for (String className : classSource.getClassNames()) { for (String className : classSource.getClassNames()) {
@ -74,20 +78,23 @@ class JSAliasRenderer implements RendererListener, VirtualMethodContributor {
} }
} }
} }
if (methods.isEmpty() && properties.isEmpty()) {
var isJsClassImpl = typeHelper.isJavaScriptImplementation(className);
if (methods.isEmpty() && properties.isEmpty() && !isJsClassImpl) {
continue; continue;
} }
boolean first = true; writer.append("c").ws().append("=").ws().appendClass(className).append(".prototype;")
.softNewLine();
if (isJsClassImpl) {
writer.append("c[").appendFunction("$rt_jso_marker").append("]").ws().append("=").ws().append("true;")
.softNewLine();
}
for (var aliasEntry : methods.entrySet()) { for (var aliasEntry : methods.entrySet()) {
if (classReader.getMethod(aliasEntry.getValue()) == null) { if (classReader.getMethod(aliasEntry.getValue()) == null) {
continue; continue;
} }
if (first) {
writer.append("c").ws().append("=").ws().appendClass(className).append(".prototype;")
.softNewLine();
first = false;
}
if (isKeyword(aliasEntry.getKey())) { if (isKeyword(aliasEntry.getKey())) {
writer.append("c[\"").append(aliasEntry.getKey()).append("\"]"); writer.append("c[\"").append(aliasEntry.getKey()).append("\"]");
} else { } else {
@ -101,11 +108,6 @@ class JSAliasRenderer implements RendererListener, VirtualMethodContributor {
if (propInfo.getter == null || classReader.getMethod(propInfo.getter) == null) { if (propInfo.getter == null || classReader.getMethod(propInfo.getter) == null) {
continue; continue;
} }
if (first) {
writer.append("c").ws().append("=").ws().appendClass(className).append(".prototype;")
.softNewLine();
first = false;
}
writer.append("Object.defineProperty(c,") writer.append("Object.defineProperty(c,")
.ws().append("\"").append(aliasEntry.getKey()).append("\",") .ws().append("\"").append(aliasEntry.getKey()).append("\",")
.ws().append("{").indent().softNewLine(); .ws().append("{").indent().softNewLine();
@ -128,7 +130,8 @@ class JSAliasRenderer implements RendererListener, VirtualMethodContributor {
private boolean hasClassesToExpose() { private boolean hasClassesToExpose() {
for (String className : classSource.getClassNames()) { for (String className : classSource.getClassNames()) {
ClassReader cls = classSource.get(className); ClassReader cls = classSource.get(className);
if (cls.getMethods().stream().anyMatch(method -> getPublicAlias(method) != null)) { if (cls.getMethods().stream().anyMatch(method -> getPublicAlias(method) != null)
|| typeHelper.isJavaScriptImplementation(className)) {
return true; return true;
} }
} }

View File

@ -72,6 +72,8 @@ public class JSOPlugin implements TeaVMPlugin {
wrapperGenerator); wrapperGenerator);
jsHost.add(new MethodReference(JSWrapper.class, "jsToWrapper", JSObject.class, JSWrapper.class), jsHost.add(new MethodReference(JSWrapper.class, "jsToWrapper", JSObject.class, JSWrapper.class),
wrapperGenerator); wrapperGenerator);
jsHost.add(new MethodReference(JSWrapper.class, "isJSImplementation", Object.class, boolean.class),
wrapperGenerator);
host.add(new MethodReference(JSWrapper.class, "jsToWrapper", JSObject.class, JSWrapper.class), host.add(new MethodReference(JSWrapper.class, "jsToWrapper", JSObject.class, JSWrapper.class),
wrapperGenerator); wrapperGenerator);

View File

@ -59,6 +59,9 @@ public final class JSWrapper {
return null; return null;
} }
var js = directJavaToJs(o); var js = directJavaToJs(o);
if (isJSImplementation(o)) {
return o;
}
if (wrappers != null) { if (wrappers != null) {
var type = JSObjects.typeOf(js); var type = JSObjects.typeOf(js);
if (type.equals("object") || type.equals("function")) { if (type.equals("object") || type.equals("function")) {
@ -161,18 +164,21 @@ public final class JSWrapper {
@NoSideEffects @NoSideEffects
public static native boolean isJava(JSObject obj); public static native boolean isJava(JSObject obj);
@NoSideEffects
private static native boolean isJSImplementation(Object obj);
public static JSObject unwrap(Object o) { public static JSObject unwrap(Object o) {
if (o == null) { if (o == null) {
return null; return null;
} }
return o instanceof JSWrapper ? ((JSWrapper) o).js : directJavaToJs(o); return isJSImplementation(o) ? directJavaToJs(o) : ((JSWrapper) o).js;
} }
public static JSObject maybeUnwrap(Object o) { public static JSObject maybeUnwrap(Object o) {
if (o == null) { if (o == null) {
return null; return null;
} }
return o instanceof JSWrapper ? unwrap(o) : directJavaToJs(o); return isJava(o) ? unwrap(o) : directJavaToJs(o);
} }
public static JSObject javaToJs(Object o) { public static JSObject javaToJs(Object o) {

View File

@ -48,6 +48,17 @@ public class JSWrapperGenerator implements Injector, DependencyPlugin {
context.getWriter().append(")"); context.getWriter().append(")");
} }
break; 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;
} }
} }