diff --git a/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java b/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java index e58bb6fe5..f974bdffd 100644 --- a/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java +++ b/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java @@ -140,21 +140,101 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener { agent.linkMethod(JavaScriptConvGenerator.valueOfLongMethod, location).use(); } - private static MethodReader findMethod(ClassReaderSource classSource, String clsName, MethodDescriptor desc) { - while (clsName != null) { - ClassReader cls = classSource.get(clsName); - if (cls == null) { - return null; - } - for (MethodReader method : cls.getMethods()) { - if (method.getName().equals(desc.getName()) && sameParams(method.getDescriptor(), desc)) { - return method; - } - } - clsName = cls.getParent(); + class GeneratorJsCallback extends JsCallback { + private DependencyAgent agent; + private MethodDependency caller; + private CallLocation location; + + GeneratorJsCallback(DependencyAgent agent, MethodDependency caller, CallLocation location) { + this.agent = agent; + this.caller = caller; + this.location = location; } + + @Override + protected CharSequence callMethod(String ident, String fqn, String method, String params) { + MethodDescriptor desc = MethodDescriptor.parse(method + params + "V"); + MethodReference methodRef = new MethodReference(fqn, desc); + + MethodReader reader = findMethod(agent.getClassSource(), fqn, desc); + if (reader == null) { + agent.getDiagnostics().error(location, "Can't resolve method {{m0}}", methodRef); + } + + methodRef = reader.getReference(); + MethodDependency methodDep = agent.linkMethod(methodRef, location); + if (ident == null) { + reachedMethods.get(caller.getReference()).add(methodRef); + methodDep.use(); + for (int i = 0; i < methodDep.getParameterCount(); ++i) { + allClassesNode.connect(methodDep.getVariable(i)); + } + } else { + allClassesNode.addConsumer(new VirtualCallbackConsumer(agent, methodRef, location)); + } + + return ""; + } + } + + class VirtualCallbackConsumer implements DependencyConsumer { + private DependencyAgent agent; + private MethodReference superMethod; + private CallLocation location; + + VirtualCallbackConsumer(DependencyAgent agent, MethodReference superMethod, CallLocation location) { + this.agent = agent; + this.superMethod = superMethod; + this.location = location; + } + + @Override + public void consume(DependencyType type) { + if (!agent.getClassSource().isSuperType(superMethod.getClassName(), type.getName()).orElse(false)) { + return; + } + MethodReader method = agent.getClassSource().resolveImplementation(new MethodReference( + type.getName(), superMethod.getDescriptor())); + if (method == null) { + return; + } + MethodDependency methodDep = agent.linkMethod(method.getReference(), location); + methodDep.use(); + for (int i = 0; i < methodDep.getParameterCount(); ++i) { + allClassesNode.connect(methodDep.getVariable(i)); + } + } + } + + static MethodReader findMethod(ClassReaderSource classSource, String clsName, MethodDescriptor desc) { + ClassReader cls = classSource.get(clsName); + if (cls == null) { + return null; + } + + for (MethodReader method : cls.getMethods()) { + if (method.getName().equals(desc.getName()) && sameParams(method.getDescriptor(), desc)) { + return method; + } + } + + if (cls.getParent() != null) { + MethodReader result = findMethod(classSource, cls.getParent(), desc); + if (result != null) { + return result; + } + } + + for (String iface : cls.getInterfaces()) { + MethodReader result = findMethod(classSource, iface, desc); + if (result != null) { + return result; + } + } + return null; } + private static boolean sameParams(MethodDescriptor a, MethodDescriptor b) { if (a.parameterCount() != b.parameterCount()) { return false; @@ -166,57 +246,4 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener { } return true; } - - class GeneratorJsCallback extends JsCallback { - private DependencyAgent agent; - private MethodDependency caller; - private CallLocation location; - GeneratorJsCallback(DependencyAgent agent, MethodDependency caller, CallLocation location) { - this.agent = agent; - this.caller = caller; - this.location = location; - } - @Override protected CharSequence callMethod(String ident, String fqn, String method, String params) { - MethodDescriptor desc = MethodDescriptor.parse(method + params + "V"); - MethodReader reader = findMethod(agent.getClassSource(), fqn, desc); - MethodReference ref = reader != null ? reader.getReference() : new MethodReference(fqn, desc); - MethodDependency methodDep = agent.linkMethod(ref, location); - reachedMethods.get(caller.getReference()).add(ref); - if (!methodDep.isMissing()) { - if (reader.hasModifier(ElementModifier.STATIC) || reader.hasModifier(ElementModifier.FINAL)) { - methodDep.use(); - for (int i = 0; i < methodDep.getParameterCount(); ++i) { - allClassesNode.connect(methodDep.getVariable(i)); - } - } else { - allClassesNode.addConsumer(new VirtualCallbackConsumer(agent, reader, caller)); - } - } - return ""; - } - } - - class VirtualCallbackConsumer implements DependencyConsumer { - private DependencyAgent agent; - private MethodReader superMethod; - private ClassReader superClass; - private MethodDependency caller; - VirtualCallbackConsumer(DependencyAgent agent, MethodReader superMethod, MethodDependency caller) { - this.agent = agent; - this.superMethod = superMethod; - this.caller = caller; - this.superClass = agent.getClassSource().get(superMethod.getOwnerName()); - } - @Override public void consume(DependencyType type) { - if (!agent.getClassSource().isSuperType(superClass.getName(), type.getName()).orElse(false)) { - return; - } - MethodReference methodRef = new MethodReference(type.getName(), superMethod.getDescriptor()); - MethodDependency method = agent.linkMethod(methodRef, new CallLocation(caller.getReference())); - method.use(); - for (int i = 0; i < method.getParameterCount(); ++i) { - allClassesNode.connect(method.getVariable(i)); - } - } - } } diff --git a/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyGenerator.java b/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyGenerator.java index cc5db7a61..0455afe3b 100644 --- a/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyGenerator.java +++ b/html4j/src/main/java/org/teavm/html4j/JavaScriptBodyGenerator.java @@ -24,7 +24,6 @@ import org.teavm.backend.javascript.spi.Generator; import org.teavm.backend.javascript.spi.GeneratorContext; import org.teavm.model.AnnotationReader; import org.teavm.model.AnnotationValue; -import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.MethodDescriptor; @@ -63,7 +62,7 @@ public class JavaScriptBodyGenerator implements Generator { } writer.append(");").softNewLine(); writer.append("return "); - unwrapValue(context, writer, method.getResultType(), "result"); + unwrapValue(context, writer, method.getResultType()); writer.append(";").softNewLine(); } @@ -72,10 +71,10 @@ public class JavaScriptBodyGenerator implements Generator { writer.append("(").append(param).append(")"); } - private void unwrapValue(GeneratorContext context, SourceWriter writer, ValueType type, String param) + private void unwrapValue(GeneratorContext context, SourceWriter writer, ValueType type) throws IOException { writer.appendMethodBody(JavaScriptConvGenerator.fromJsMethod); - writer.append("(").append(param).append(",").ws().append(context.typeToClassString(type)) + writer.append("(").append("result").append(",").ws().append(context.typeToClassString(type)) .append(")"); } @@ -83,20 +82,28 @@ public class JavaScriptBodyGenerator implements Generator { private GeneratorContext context; private ClassReaderSource classSource; private NamingStrategy naming; - public GeneratorJsCallback(GeneratorContext context, ClassReaderSource classSource, NamingStrategy naming) { + + GeneratorJsCallback(GeneratorContext context, ClassReaderSource classSource, NamingStrategy naming) { this.context = context; this.classSource = classSource; this.naming = naming; } - @Override protected CharSequence callMethod(String ident, String fqn, String method, String params) { + + @Override + protected CharSequence callMethod(String ident, String fqn, String method, String params) { MethodDescriptor desc = MethodDescriptor.parse(method + params + "V"); - MethodReader reader = findMethod(fqn, desc); + MethodReader reader = JavaScriptBodyDependency.findMethod(classSource, fqn, desc); + if (reader == null) { + return ""; + } + desc = reader.getDescriptor(); + StringBuilder sb = new StringBuilder(); sb.append("(function("); if (ident != null) { sb.append("$this"); } - for (int i = 0; i < reader.parameterCount(); ++i) { + for (int i = 0; i < desc.parameterCount(); ++i) { if (ident != null || i > 0) { sb.append(", "); } @@ -106,14 +113,14 @@ public class JavaScriptBodyGenerator implements Generator { if (ident == null) { sb.append(naming.getFullNameFor(reader.getReference())); } else { - sb.append("$this.").append(naming.getNameFor(reader.getDescriptor())); + sb.append("$this.").append(naming.getNameFor(desc)); } sb.append("("); - for (int i = 0; i < reader.parameterCount(); ++i) { + for (int i = 0; i < desc.parameterCount(); ++i) { if (i > 0) { sb.append(", "); } - ValueType paramType = simplifyParamType(reader.parameterType(i)); + ValueType paramType = simplifyParamType(desc.parameterType(i)); sb.append(naming.getFullNameFor(JavaScriptConvGenerator.fromJsMethod)).append("(p").append(i) .append(", ") .append(context.typeToClassString(paramType)).append(")"); @@ -124,6 +131,7 @@ public class JavaScriptBodyGenerator implements Generator { } return sb.toString(); } + private ValueType simplifyParamType(ValueType type) { if (type instanceof ValueType.Object) { return ValueType.object("java.lang.Object"); @@ -134,28 +142,5 @@ public class JavaScriptBodyGenerator implements Generator { return type; } } - private MethodReader findMethod(String clsName, MethodDescriptor desc) { - while (clsName != null) { - ClassReader cls = classSource.get(clsName); - for (MethodReader method : cls.getMethods()) { - if (method.getName().equals(desc.getName()) && sameParams(method.getDescriptor(), desc)) { - return method; - } - } - clsName = cls.getParent(); - } - return null; - } - private boolean sameParams(MethodDescriptor a, MethodDescriptor b) { - if (a.parameterCount() != b.parameterCount()) { - return false; - } - for (int i = 0; i < a.parameterCount(); ++i) { - if (!a.parameterType(i).equals(b.parameterType(i))) { - return false; - } - } - return true; - } } } diff --git a/html4j/src/test/java/org/teavm/html4j/test/JavaScriptBodyTest.java b/html4j/src/test/java/org/teavm/html4j/test/JavaScriptBodyTest.java index 69077b7ee..727298153 100644 --- a/html4j/src/test/java/org/teavm/html4j/test/JavaScriptBodyTest.java +++ b/html4j/src/test/java/org/teavm/html4j/test/JavaScriptBodyTest.java @@ -21,7 +21,6 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.util.Calendar; import net.java.html.js.JavaScriptBody; import org.junit.Test; import org.junit.runner.RunWith; @@ -79,12 +78,9 @@ public class JavaScriptBodyTest { @Test public void unusedArgumentIgnored() { - final int[] array = new int[1]; - invokeCallback(new Callback() { - @Override - public void exec(Calendar input) { - array[0] = 23; - } + int[] array = new int[1]; + invokeCallback(input -> { + array[0] = 23; }); assertEquals(23, array[0]); }