FunctorAnalyzer.java
package com.mackenziehigh.autumn.lang.compiler.utils;
import autumn.lang.DefinedFunctor;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IClassType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IMethod;
import java.util.Collections;
import java.util.List;
/**
* An instance of this class can be used to analyze a well-formed functor type.
*
* <p>
* Note: A functor-type never declares more than one invoke(*) method directly.
* However, a functor-type may inherit multiple invoke(*) methods.
* </p>
*
* @author Mackenzie High
*/
public final class FunctorAnalyzer
{
/**
* This is the functor-type being analyzed.
*/
private final IClassType functor;
/**
* This object is needed in order to sort methods.
*/
private final TypeSystemUtils utils;
/**
* There is no need to search for the invoke(*) methods multiple times.
*/
private List<IMethod> cache;
/**
* Sole Constructor.
*
* @param functor is the functor-type to analyze.
*/
public FunctorAnalyzer(final TypeSystemUtils utils,
final IClassType functor)
{
Preconditions.checkNotNull(utils);
Preconditions.checkNotNull(functor);
assert functor.isSubtypeOf(functor.getTypeFactory().fromClass(DefinedFunctor.class));
this.utils = utils;
this.functor = functor;
}
/**
* This method retrieves the invoke(*) methods from the type.
*
* <p>
* The functor-type and each of its superclasses should define one invoke methods
* </p>
*
* @return the methods sorted topologically based on their specificity.
*/
public List<IMethod> invokeMethods()
{
/**
* If we already found the methods, simply return them.
*/
if (cache != null)
{
return cache;
}
final List<IClassType> types = Lists.newLinkedList();
types.add(functor);
types.addAll(functor.getAllSuperclasses());
final List<IMethod> methods = Lists.newLinkedList();
/**
* Transverse the functor-type and its superclasses.
*/
for (IClassType type : types)
{
/**
* Find the invoke methods declared in the type, if any.
*/
for (IMethod method : type.getMethods())
{
if (method.getOwner().isClassType() && method.getName().equals("invoke"))
{
methods.add(method);
}
}
}
/**
* Sort the methods.
*/
cache = Collections.unmodifiableList(utils.sort(methods));
return cache;
}
/**
* This method retrieves the most-specific of the invoke(*) methods.
*
* @return the most-specific of invoke-method.
*/
public IMethod invokeMethod()
{
return invokeMethods().get(0);
}
/**
* Every invoke(*) method in a subclass must be compatible with those in its superclasses.
*
* <p>
* <ul>
* <li>Let M be an invoke(*) method in type P. </li>
* <li>Let W be an invoke(*) method in type Q. </li>
* <li>Assume P is a subclass of Q. </li>
* <li>Requirement: M.formals.length == W.formals.length </li>
* <li>Requirement: M.return-type is a subtype of W.return-type </li>
* <li>Requirement: M.formals[i].type is a subtype of W.formals[i].type </li>
* </ul>
* </p>
*
* @return a description of the violation, or null, if no violation exists.
*/
public FunctorSubtypingViolation detectSubtypeViolation()
{
// Note: This is an O(n^2) algorithm.
// However, the inputs should always be small.
// So, until a profiler says otherwise, this algorithm is acceptable.
final List<IMethod> invoke_methods = invokeMethods();
for (IMethod invoke1 : invoke_methods)
{
for (IMethod invoke2 : invoke_methods)
{
if (invoke1.getOwner().isSubtypeOf(invoke2.getReturnType()))
{
final FunctorSubtypingViolation potential_violation = new FunctorSubtypingViolation(invoke1, invoke2);
if (potential_violation.isParameterLengthViolation())
{
return potential_violation;
}
if (potential_violation.isReturnTypeViolation())
{
return potential_violation;
}
if (potential_violation.isParameterTypeViolation())
{
return potential_violation;
}
}
}
}
return null; // No Violation Detected
}
/**
* This method creates a list of the bridge methods that the functor-type must provide.
*
* @return a list of the bridge methods.
*/
public List<BridgeMethod> bridges()
{
final List<BridgeMethod> bridges = Lists.newLinkedList();
final List<IMethod> invoke_methods = invokeMethods();
/**
* This is the method that the bridge methods will invoke.
*/
final IMethod target = invokeMethod();
/**
* Create the bridge method objects.
*/
for (int i = 0; i < invoke_methods.size(); i++)
{
final IMethod bridge = invoke_methods.get(i);
/**
* No bridge is needed, if the descriptors are the same.
* Simple inheritance will work instead.
*/
if (target.getDescriptor().equals(target.getDescriptor()) == false)
{
final BridgeMethod object = new BridgeMethod(bridge, target);
bridges.add(object);
}
}
return Collections.unmodifiableList(bridges);
}
}