Refactor TeaVM to use new default methods of ClassReaderSource instead

of manual traversing through class hierarchy
This commit is contained in:
Alexey Andreev 2015-07-31 23:12:38 +03:00
parent 57a39a156a
commit fe940bc084
7 changed files with 38 additions and 163 deletions

View File

@ -284,9 +284,10 @@ class DependencyGraphBuilder {
@Override
public void consume(DependencyType type) {
ClassReaderSource classSource = checker.getClassSource();
for (int i = 0; i < exceptions.length; ++i) {
if (exceptions[i] == null || isAssignableFrom(checker.getClassSource(), exceptions[i].getName(),
type.getName())) {
if (exceptions[i] == null || classSource.isSuperType(exceptions[i].getName(), type.getName())
.orElse(false)) {
if (vars[i] != null) {
vars[i].propagate(type);
}
@ -334,7 +335,9 @@ class DependencyGraphBuilder {
if (className.startsWith("[")) {
className = "java.lang.Object";
}
if (!isAssignableFrom(checker.getClassSource(), filterClass.getName(), className)) {
ClassReaderSource classSource = checker.getClassSource();
if (classSource.isSuperType(filterClass.getName(), className).orElse(false)) {
return;
}
MethodReference methodRef = new MethodReference(className, methodDesc);
@ -344,8 +347,8 @@ class DependencyGraphBuilder {
methodDep.use();
DependencyNode[] targetParams = methodDep.getVariables();
if (parameters[0] != null && targetParams[0] != null) {
parameters[0].connect(targetParams[0], thisType -> isAssignableFrom(checker.getClassSource(),
methodDep.getMethod().getOwnerName(), thisType.getName()));
parameters[0].connect(targetParams[0], thisType -> classSource.isSuperType(
methodDep.getMethod().getOwnerName(), thisType.getName()).orElse(false));
}
for (int i = 1; i < parameters.length; ++i) {
if (parameters[i] != null && targetParams[i] != null) {
@ -360,23 +363,6 @@ class DependencyGraphBuilder {
}
}
private static boolean isAssignableFrom(ClassReaderSource classSource, String supertype, String subtypeName) {
if (supertype.equals(subtypeName)) {
return true;
}
ClassReader subtype = classSource.get(subtypeName);
if (subtype == null) {
return false;
}
if (subtype.getParent() != null && isAssignableFrom(classSource, supertype, subtype.getParent())) {
return true;
}
if (subtype.getInterfaces().stream().anyMatch(iface -> isAssignableFrom(classSource, supertype, iface))) {
return true;
}
return false;
}
private InstructionReader reader = new InstructionReader() {
@Override
public void location(InstructionLocation location) {
@ -455,17 +441,17 @@ class DependencyGraphBuilder {
public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
DependencyNode valueNode = nodes[value.getIndex()];
DependencyNode receiverNode = nodes[receiver.getIndex()];
ClassReaderSource classSource = dependencyChecker.getClassSource();
if (targetType instanceof ValueType.Object) {
String targetClsName = ((ValueType.Object) targetType).getClassName();
final ClassReader targetClass = dependencyChecker.getClassSource().get(targetClsName);
final ClassReader targetClass = classSource.get(targetClsName);
if (targetClass != null) {
if (valueNode != null && receiverNode != null) {
valueNode.connect(receiverNode, type -> {
if (targetClass.getName().equals("java.lang.Object")) {
return true;
}
return isAssignableFrom(dependencyChecker.getClassSource(), targetClass.getName(),
type.getName());
return classSource.isSuperType(targetClass.getName(), type.getName()).orElse(false);
});
}
return;

View File

@ -244,6 +244,10 @@ public final class ProgramEmitter {
return this;
}
public ProgramEmitter invoke(Class<?> cls, String methodName, ValueEmitter... arguments) {
return invoke(cls.getName(), methodName, arguments);
}
public ValueEmitter construct(String className, ValueEmitter... arguments) {
Variable var = program.createVariable();
ConstructInstruction insn = new ConstructInstruction();

View File

@ -71,7 +71,7 @@ public class Devirtualization {
className = "java.lang.Object";
}
ClassReader cls = classSource.get(className);
if (cls == null || !isAssignable(ref.getClassName(), cls)) {
if (cls == null || !classSource.isSuperType(ref.getClassName(), cls.getName()).orElse(false)) {
continue;
}
MethodDependencyInfo methodDep = dependency.getMethodImplementation(new MethodReference(
@ -82,23 +82,4 @@ public class Devirtualization {
}
return methods;
}
private boolean isAssignable(String target, ClassReader cls) {
if (cls.getName().equals(target)) {
return true;
}
if (cls.getParent() != null) {
ClassReader parent = classSource.get(cls.getParent());
if (parent != null && isAssignable(target, parent)) {
return true;
}
}
for (String ifaceName : cls.getInterfaces()) {
ClassReader iface = classSource.get(ifaceName);
if (iface != null && isAssignable(target, iface)) {
return true;
}
}
return false;
}
}

View File

@ -198,7 +198,7 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener {
this.superClass = agent.getClassSource().get(superMethod.getOwnerName());
}
@Override public void consume(DependencyType type) {
if (!isAssignableFrom(superClass, type.getName())) {
if (!agent.getClassSource().isSuperType(superClass.getName(), type.getName()).orElse(false)) {
return;
}
MethodReference methodRef = new MethodReference(type.getName(), superMethod.getDescriptor());
@ -208,24 +208,5 @@ public class JavaScriptBodyDependency extends AbstractDependencyListener {
allClassesNode.connect(method.getVariable(i));
}
}
private boolean isAssignableFrom(ClassReader supertype, String subtypeName) {
ClassReaderSource classSource = agent.getClassSource();
if (supertype.getName().equals(subtypeName)) {
return true;
}
ClassReader subtype = classSource.get(subtypeName);
if (subtype == null) {
return false;
}
if (subtype.getParent() != null && isAssignableFrom(supertype, subtype.getParent())) {
return true;
}
for (String iface : subtype.getInterfaces()) {
if (isAssignableFrom(supertype, iface)) {
return true;
}
}
return false;
}
}
}

View File

@ -115,37 +115,22 @@ class JavascriptNativeProcessor {
return null;
}
Map<MethodDescriptor, MethodReference> methods = new HashMap<>();
getFunctorMethods(className, new HashSet<>(), methods);
getFunctorMethods(className, methods);
if (methods.size() == 1) {
return methods.values().iterator().next();
}
return null;
}
private void getFunctorMethods(String className, Set<String> visited,
Map<MethodDescriptor, MethodReference> methods) {
if (!visited.add(className)) {
return;
}
ClassReader cls = classSource.get(className);
if (cls == null) {
return;
}
if (cls.getAnnotations().get(JSFunctor.class.getName()) != null && isProperFunctor(cls)) {
MethodReference method = cls.getMethods().iterator().next().getReference();
if (!methods.containsKey(method.getDescriptor())) {
methods.put(method.getDescriptor(), method);
private void getFunctorMethods(String className, Map<MethodDescriptor, MethodReference> methods) {
classSource.getAncestors(className).forEach(cls -> {
if (cls.getAnnotations().get(JSFunctor.class.getName()) != null && isProperFunctor(cls)) {
MethodReference method = cls.getMethods().iterator().next().getReference();
if (!methods.containsKey(method.getDescriptor())) {
methods.put(method.getDescriptor(), method);
}
}
}
if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) {
getFunctorMethods(cls.getParent(), visited, methods);
}
for (String iface : cls.getInterfaces()) {
getFunctorMethods(iface, visited, methods);
}
});
}
public void processClass(ClassHolder cls) {
@ -223,31 +208,12 @@ class JavascriptNativeProcessor {
}
private MethodReader findOverridenMethod(String className, MethodReader finalMethod) {
ClassReader cls = classSource.get(className);
if (cls == null) {
return null;
}
MethodReader method = cls.getMethod(finalMethod.getDescriptor());
if (method != null && !method.getOwnerName().equals(finalMethod.getOwnerName())) {
return method;
}
if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) {
method = findOverridenMethod(cls.getParent(), finalMethod);
if (method != null) {
return method;
}
}
for (String iface : cls.getInterfaces()) {
method = findOverridenMethod(iface, finalMethod);
if (method != null) {
return method;
}
}
return null;
return classSource.getAncestors(className)
.skip(1)
.map(cls -> cls.getMethod(finalMethod.getDescriptor()))
.filter(method -> method != null)
.findFirst()
.orElse(null);
}
public void addFunctorField(ClassHolder cls, MethodReference method) {

View File

@ -24,7 +24,6 @@ import org.teavm.javascript.spi.Generator;
import org.teavm.javascript.spi.GeneratorContext;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
@ -100,9 +99,10 @@ public class AsyncMethodGenerator implements Generator, DependencyPlugin {
@Override
public void methodAchieved(DependencyAgent checker, MethodDependency method, CallLocation location) {
MethodReference asyncRef = getAsyncReference(method.getReference());
MethodReference ref = method.getReference();
MethodReference asyncRef = getAsyncReference(ref);
MethodDependency asyncMethod = checker.linkMethod(asyncRef, location);
int paramCount = method.getReference().parameterCount();
int paramCount = ref.parameterCount();
for (int i = 0; i <= paramCount; ++i) {
method.getVariable(i).connect(asyncMethod.getVariable(i));
}
@ -111,8 +111,8 @@ public class AsyncMethodGenerator implements Generator, DependencyPlugin {
MethodDependency completeMethod = checker.linkMethod(
new MethodReference(AsyncCallbackWrapper.class, "complete", Object.class, void.class), null);
if (method.getResult() != null) {
completeMethod.getVariable(1).connect(method.getResult(), type -> isSubtype(checker.getClassSource(),
type.getName(), method.getReference().getReturnType()));
completeMethod.getVariable(1).connect(method.getResult(), type -> checker.getClassSource()
.isSuperType(ref.getReturnType(), ValueType.object(type.getName())).orElse(false));
}
completeMethod.use();
@ -126,30 +126,4 @@ public class AsyncMethodGenerator implements Generator, DependencyPlugin {
asyncMethod.use();
}
private boolean isSubtype(ClassReaderSource classSource, String className, ValueType returnType) {
if (returnType instanceof ValueType.Primitive) {
return false;
} else if (returnType instanceof ValueType.Array) {
return className.startsWith("[");
} else {
return isSubclass(classSource, className, ((ValueType.Object) returnType).getClassName());
}
}
private boolean isSubclass(ClassReaderSource classSource, String className, String baseClass) {
if (className.equals(baseClass)) {
return true;
}
ClassReader cls = classSource.get(className);
if (cls == null) {
return false;
}
if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) {
if (isSubclass(classSource, cls.getParent(), baseClass)) {
return true;
}
}
return cls.getInterfaces().stream().anyMatch(iface -> isSubclass(classSource, iface, baseClass));
}
}

View File

@ -80,7 +80,7 @@ class ResourceProgramTransformer {
return Arrays.<Instruction>asList(accessInsn);
}
ClassReader iface = innerSource.get(method.getClassName());
if (iface == null || !isSubclass(iface, Resource.class.getName())) {
if (iface == null || !innerSource.isSuperType(Resource.class.getName(), iface.getName()).orElse(false)) {
return null;
}
if (method.getName().startsWith("get")) {
@ -118,23 +118,6 @@ class ResourceProgramTransformer {
return Arrays.<Instruction>asList(keysInsn, transformInsn);
}
private boolean isSubclass(ClassReader cls, String superClass) {
if (cls.getName().equals(superClass)) {
return true;
}
ClassReader parent = cls.getParent() != null ? innerSource.get(cls.getParent()) : null;
if (parent != null && isSubclass(parent, superClass)) {
return true;
}
for (String ifaceName : cls.getInterfaces()) {
ClassReader iface = innerSource.get(ifaceName);
if (iface != null) {
return isSubclass(iface, superClass);
}
}
return false;
}
private List<Instruction> transformGetterInvocation(InvokeInstruction insn, String property) {
if (insn.getReceiver() == null) {
return Collections.emptyList();