Fix bugs in method resolution in html4j

This commit is contained in:
Alexey Andreev 2017-10-29 21:40:48 +03:00
parent c54b2b9e9e
commit 680f0dabb5
3 changed files with 114 additions and 106 deletions

View File

@ -140,21 +140,101 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener {
agent.linkMethod(JavaScriptConvGenerator.valueOfLongMethod, location).use(); agent.linkMethod(JavaScriptConvGenerator.valueOfLongMethod, location).use();
} }
private static MethodReader findMethod(ClassReaderSource classSource, String clsName, MethodDescriptor desc) { class GeneratorJsCallback extends JsCallback {
while (clsName != null) { private DependencyAgent agent;
ClassReader cls = classSource.get(clsName); private MethodDependency caller;
if (cls == null) { private CallLocation location;
return null;
} GeneratorJsCallback(DependencyAgent agent, MethodDependency caller, CallLocation location) {
for (MethodReader method : cls.getMethods()) { this.agent = agent;
if (method.getName().equals(desc.getName()) && sameParams(method.getDescriptor(), desc)) { this.caller = caller;
return method; this.location = location;
}
}
clsName = cls.getParent();
} }
@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; return null;
} }
private static boolean sameParams(MethodDescriptor a, MethodDescriptor b) { private static boolean sameParams(MethodDescriptor a, MethodDescriptor b) {
if (a.parameterCount() != b.parameterCount()) { if (a.parameterCount() != b.parameterCount()) {
return false; return false;
@ -166,57 +246,4 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener {
} }
return true; 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));
}
}
}
} }

View File

@ -24,7 +24,6 @@ import org.teavm.backend.javascript.spi.Generator;
import org.teavm.backend.javascript.spi.GeneratorContext; import org.teavm.backend.javascript.spi.GeneratorContext;
import org.teavm.model.AnnotationReader; import org.teavm.model.AnnotationReader;
import org.teavm.model.AnnotationValue; import org.teavm.model.AnnotationValue;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
@ -63,7 +62,7 @@ public class JavaScriptBodyGenerator implements Generator {
} }
writer.append(");").softNewLine(); writer.append(");").softNewLine();
writer.append("return "); writer.append("return ");
unwrapValue(context, writer, method.getResultType(), "result"); unwrapValue(context, writer, method.getResultType());
writer.append(";").softNewLine(); writer.append(";").softNewLine();
} }
@ -72,10 +71,10 @@ public class JavaScriptBodyGenerator implements Generator {
writer.append("(").append(param).append(")"); 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 { throws IOException {
writer.appendMethodBody(JavaScriptConvGenerator.fromJsMethod); 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(")"); .append(")");
} }
@ -83,20 +82,28 @@ public class JavaScriptBodyGenerator implements Generator {
private GeneratorContext context; private GeneratorContext context;
private ClassReaderSource classSource; private ClassReaderSource classSource;
private NamingStrategy naming; private NamingStrategy naming;
public GeneratorJsCallback(GeneratorContext context, ClassReaderSource classSource, NamingStrategy naming) {
GeneratorJsCallback(GeneratorContext context, ClassReaderSource classSource, NamingStrategy naming) {
this.context = context; this.context = context;
this.classSource = classSource; this.classSource = classSource;
this.naming = naming; 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"); 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(); StringBuilder sb = new StringBuilder();
sb.append("(function("); sb.append("(function(");
if (ident != null) { if (ident != null) {
sb.append("$this"); sb.append("$this");
} }
for (int i = 0; i < reader.parameterCount(); ++i) { for (int i = 0; i < desc.parameterCount(); ++i) {
if (ident != null || i > 0) { if (ident != null || i > 0) {
sb.append(", "); sb.append(", ");
} }
@ -106,14 +113,14 @@ public class JavaScriptBodyGenerator implements Generator {
if (ident == null) { if (ident == null) {
sb.append(naming.getFullNameFor(reader.getReference())); sb.append(naming.getFullNameFor(reader.getReference()));
} else { } else {
sb.append("$this.").append(naming.getNameFor(reader.getDescriptor())); sb.append("$this.").append(naming.getNameFor(desc));
} }
sb.append("("); sb.append("(");
for (int i = 0; i < reader.parameterCount(); ++i) { for (int i = 0; i < desc.parameterCount(); ++i) {
if (i > 0) { if (i > 0) {
sb.append(", "); sb.append(", ");
} }
ValueType paramType = simplifyParamType(reader.parameterType(i)); ValueType paramType = simplifyParamType(desc.parameterType(i));
sb.append(naming.getFullNameFor(JavaScriptConvGenerator.fromJsMethod)).append("(p").append(i) sb.append(naming.getFullNameFor(JavaScriptConvGenerator.fromJsMethod)).append("(p").append(i)
.append(", ") .append(", ")
.append(context.typeToClassString(paramType)).append(")"); .append(context.typeToClassString(paramType)).append(")");
@ -124,6 +131,7 @@ public class JavaScriptBodyGenerator implements Generator {
} }
return sb.toString(); return sb.toString();
} }
private ValueType simplifyParamType(ValueType type) { private ValueType simplifyParamType(ValueType type) {
if (type instanceof ValueType.Object) { if (type instanceof ValueType.Object) {
return ValueType.object("java.lang.Object"); return ValueType.object("java.lang.Object");
@ -134,28 +142,5 @@ public class JavaScriptBodyGenerator implements Generator {
return type; 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;
}
} }
} }

View File

@ -21,7 +21,6 @@ import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.Calendar;
import net.java.html.js.JavaScriptBody; import net.java.html.js.JavaScriptBody;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -79,12 +78,9 @@ public class JavaScriptBodyTest {
@Test @Test
public void unusedArgumentIgnored() { public void unusedArgumentIgnored() {
final int[] array = new int[1]; int[] array = new int[1];
invokeCallback(new Callback() { invokeCallback(input -> {
@Override array[0] = 23;
public void exec(Calendar input) {
array[0] = 23;
}
}); });
assertEquals(23, array[0]); assertEquals(23, array[0]);
} }