package yalp.classloading.enhancers;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;
import org.apache.commons.javaflow.bytecode.transformation.asm.AsmClassTransformer;
import yalp.Yalp;
import yalp.classloading.ApplicationClasses.ApplicationClass;
public class ContinuationEnhancer extends Enhancer {
static final List<String> continuationMethods = new ArrayList<String>();
static {
continuationMethods.add("yalp.mvc.Controller.await(java.lang.String)");
continuationMethods.add("yalp.mvc.Controller.await(int)");
continuationMethods.add("yalp.mvc.Controller.await(java.util.concurrent.Future)");
continuationMethods.add("yalp.mvc.WebSocketController.await(java.lang.String)");
continuationMethods.add("yalp.mvc.WebSocketController.await(int)");
continuationMethods.add("yalp.mvc.WebSocketController.await(java.util.concurrent.Future)");
}
public static boolean isEnhanced(String appClassName) {
ApplicationClass appClass = Yalp.classes.getApplicationClass(appClassName);
if (appClass == null) {
return false;
}
// All classes enhanced for Continuations are implementing the interface EnhancedForContinuations
return EnhancedForContinuations.class.isAssignableFrom(appClass.javaClass);
}
@Override
public void enhanceThisClass(ApplicationClass applicationClass) throws Exception {
if (isScala(applicationClass)) {
return;
}
CtClass ctClass = makeClass(applicationClass);
if (!ctClass.subtypeOf(classPool.get(ControllersEnhancer.ControllerSupport.class.getName()))) {
return;
}
boolean needsContinuations = shouldEnhance(ctClass);
if (!needsContinuations) {
return;
}
// To be able to runtime detect if a class is enhanced for Continuations,
// we add the interface EnhancedForContinuations to the class
CtClass enhancedForContinuationsInterface;
try {
InputStream in = getClass().getClassLoader().getResourceAsStream("yalp/classloading/enhancers/EnhancedForContinuations.class");
enhancedForContinuationsInterface = classPool.makeClass(in);
in.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
ctClass.addInterface(enhancedForContinuationsInterface);
// Apply continuations
applicationClass.enhancedByteCode = new AsmClassTransformer().transform(ctClass.toBytecode());
ctClass.defrost();
enhancedForContinuationsInterface.defrost();
}
private boolean shouldEnhance(CtClass ctClass) throws Exception {
if (ctClass == null || ctClass.getPackageName().startsWith("yalp.")) {
// If we have not found any await-usage yet, we return false..
return false;
}
boolean needsContinuations = false;
final boolean[] _needsContinuations = new boolean[]{false};
for (CtMethod m : ctClass.getDeclaredMethods()) {
m.instrument(new ExprEditor() {
@Override
public void edit(MethodCall m) throws CannotCompileException {
try {
if (continuationMethods.contains(m.getMethod().getLongName())) {
_needsContinuations[0] = true;
}
} catch (Exception e) {
}
}
});
if (_needsContinuations[0]) {
break;
}
}
if (!_needsContinuations[0]) {
// Check parent class
_needsContinuations[0] = shouldEnhance(ctClass.getSuperclass());
}
return _needsContinuations[0];
}
}