diff --git a/teavm-jso/src/main/java/org/teavm/jso/plugin/JSObjectClassTransformer.java b/teavm-jso/src/main/java/org/teavm/jso/plugin/JSObjectClassTransformer.java index acc7fa125..8c023c08f 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/plugin/JSObjectClassTransformer.java +++ b/teavm-jso/src/main/java/org/teavm/jso/plugin/JSObjectClassTransformer.java @@ -28,12 +28,17 @@ public class JSObjectClassTransformer implements ClassHolderTransformer { @Override public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) { - processor = new JavascriptNativeProcessor(innerSource); + if (processor == null || processor.getClassSource() != innerSource) { + processor = new JavascriptNativeProcessor(innerSource); + } processor.setDiagnostics(diagnostics); processor.processClass(cls); if (processor.isNative(cls.getName())) { processor.processFinalMethods(cls); } + if (processor.isNativeImplementation(cls.getName())) { + processor.makeSync(cls); + } for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) { if (method.getAnnotations().get(JSBody.class.getName()) != null) { processor.processJSBody(cls, method); diff --git a/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java b/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java index 94ffa2b67..0a62d19cf 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java +++ b/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java @@ -18,6 +18,7 @@ package org.teavm.jso.plugin; import java.util.*; import org.teavm.diagnostics.Diagnostics; import org.teavm.javascript.spi.GeneratedBy; +import org.teavm.javascript.spi.Sync; import org.teavm.jso.*; import org.teavm.model.*; import org.teavm.model.instructions.*; @@ -41,10 +42,18 @@ class JavascriptNativeProcessor { nativeRepos = new NativeJavascriptClassRepository(classSource); } + public ClassReaderSource getClassSource() { + return classSource; + } + public boolean isNative(String className) { return nativeRepos.isJavaScriptClass(className); } + public boolean isNativeImplementation(String className) { + return nativeRepos.isJavaScriptImplementation(className); + } + public void setDiagnostics(Diagnostics diagnostics) { this.diagnostics = diagnostics; } @@ -111,6 +120,44 @@ class JavascriptNativeProcessor { } } + public void makeSync(ClassHolder cls) { + Set methods = new HashSet<>(); + findInheritedMethods(cls, methods, new HashSet()); + for (MethodHolder method : cls.getMethods()) { + if (methods.contains(method.getDescriptor()) && method.getAnnotations().get(Sync.class.getName()) == null) { + AnnotationHolder annot = new AnnotationHolder(Sync.class.getName()); + method.getAnnotations().add(annot); + } + } + } + + private void findInheritedMethods(ClassReader cls, Set methods, Set visited) { + if (!visited.add(cls.getName())) { + return; + } + if (isNative(cls.getName())) { + for (MethodReader method : cls.getMethods()) { + if (!method.hasModifier(ElementModifier.STATIC) && !method.hasModifier(ElementModifier.FINAL) && + method.getLevel() != AccessLevel.PRIVATE) { + methods.add(method.getDescriptor()); + } + } + } else if (isNativeImplementation(cls.getName())) { + if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) { + ClassReader parentCls = classSource.get(cls.getParent()); + if (parentCls != null) { + findInheritedMethods(parentCls, methods, visited); + } + } + for (String iface : cls.getInterfaces()) { + ClassReader parentCls = classSource.get(iface); + if (parentCls != null) { + findInheritedMethods(parentCls, methods, visited); + } + } + } + } + private static ValueType[] getStaticSignature(MethodReference method) { ValueType[] signature = method.getSignature(); ValueType[] staticSignature = new ValueType[signature.length + 1]; diff --git a/teavm-jso/src/main/java/org/teavm/jso/plugin/NativeJavascriptClassRepository.java b/teavm-jso/src/main/java/org/teavm/jso/plugin/NativeJavascriptClassRepository.java index a83959b8e..4b2483a1b 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/plugin/NativeJavascriptClassRepository.java +++ b/teavm-jso/src/main/java/org/teavm/jso/plugin/NativeJavascriptClassRepository.java @@ -29,6 +29,7 @@ import org.teavm.model.ElementModifier; class NativeJavascriptClassRepository { private ClassReaderSource classSource; private Map knownJavaScriptClasses = new HashMap<>(); + private Map knownJavaScriptImplementations = new HashMap<>(); public NativeJavascriptClassRepository(ClassReaderSource classSource) { this.classSource = classSource; @@ -38,13 +39,22 @@ class NativeJavascriptClassRepository { public boolean isJavaScriptClass(String className) { Boolean known = knownJavaScriptClasses.get(className); if (known == null) { - known = figureOutIfJavaScriptClass(className); + known = examineIfJavaScriptClass(className); knownJavaScriptClasses.put(className, known); } return known; } - private boolean figureOutIfJavaScriptClass(String className) { + public boolean isJavaScriptImplementation(String className) { + Boolean known = knownJavaScriptImplementations.get(className); + if (known == null) { + known = examineIfJavaScriptImplementation(className); + knownJavaScriptImplementations.put(className, known); + } + return known; + } + + private boolean examineIfJavaScriptClass(String className) { ClassReader cls = classSource.get(className); if (cls == null || !(cls.hasModifier(ElementModifier.INTERFACE) || cls.hasModifier(ElementModifier.ABSTRACT))) { return false; @@ -56,4 +66,25 @@ class NativeJavascriptClassRepository { } return false; } + + private boolean examineIfJavaScriptImplementation(String className) { + if (isJavaScriptClass(className)) { + return false; + } + ClassReader cls = classSource.get(className); + if (cls == null) { + return false; + } + if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) { + if (isJavaScriptClass(cls.getParent())) { + return true; + } + } + for (String iface : cls.getInterfaces()) { + if (isJavaScriptClass(iface)) { + return true; + } + } + return false; + } }