}
@Override
public boolean visit(JMethod x, Context ctx) {
// Let's do it!
JClassType enclosingType = (JClassType) x.getEnclosingType();
JType returnType = x.getType();
SourceInfo sourceInfo = x.getSourceInfo();
int myIndexInClass = enclosingType.getMethods().indexOf(x);
assert (myIndexInClass > 0);
// Create the new static method
String newName = "$" + x.getName();
/*
* Don't use the JProgram helper because it auto-adds the new method to
* its enclosing class.
*/
JMethod newMethod = new JMethod(sourceInfo.makeChild(
CreateStaticImplsVisitor.class, "Devirtualized function"), newName,
enclosingType, returnType, false, true, true, x.isPrivate());
// Setup parameters; map from the old params to the new params
JParameter thisParam = JParameter.create(sourceInfo.makeChild(
CreateStaticImplsVisitor.class, "Instance parameter"), "this$static",
enclosingType, true, true, newMethod);
Map<JParameter, JParameter> varMap = new IdentityHashMap<JParameter, JParameter>();
for (int i = 0; i < x.getParams().size(); ++i) {
JParameter oldVar = x.getParams().get(i);
JParameter newVar = JParameter.create(oldVar.getSourceInfo(),
oldVar.getName(), oldVar.getType(), oldVar.isFinal(), false,
newMethod);
varMap.put(oldVar, newVar);
}
// Set the new original param types based on the old original param types
List<JType> originalParamTypes = new ArrayList<JType>();
originalParamTypes.add(enclosingType);
originalParamTypes.addAll(x.getOriginalParamTypes());
newMethod.setOriginalTypes(x.getOriginalReturnType(), originalParamTypes);
// Move the body of the instance method to the static method
JAbstractMethodBody movedBody = x.getBody();
newMethod.setBody(movedBody);
// Create a new body for the instance method that delegates to the static
SourceInfo delegateCallSourceInfo = sourceInfo.makeChild(
CreateStaticImplsVisitor.class, "Degelgating to devirtualized method");
JMethodBody newBody = new JMethodBody(delegateCallSourceInfo);
x.setBody(newBody);
JMethodCall newCall = new JMethodCall(delegateCallSourceInfo, null,
newMethod);
newCall.addArg(new JThisRef(delegateCallSourceInfo, enclosingType));
for (int i = 0; i < x.getParams().size(); ++i) {
JParameter param = x.getParams().get(i);
newCall.addArg(new JParameterRef(delegateCallSourceInfo, param));
}
JStatement statement;
if (returnType == program.getTypeVoid()) {
statement = newCall.makeStatement();
} else {
statement = new JReturnStatement(delegateCallSourceInfo, newCall);
}
newBody.getBlock().addStmt(statement);
/*
* Rewrite the method body. Update all thisRefs to paramRefs. Update
* paramRefs and localRefs to target the params/locals in the new method.
*/
if (newMethod.isNative()) {
// For natives, we also need to create the JsParameter for this$static,
// because the jsFunc already has parameters.
// TODO: Do we really need to do that in BuildTypeMap?
JsFunction jsFunc = ((JsniMethodBody) movedBody).getFunc();
JsName paramName = jsFunc.getScope().declareName("this$static");
jsFunc.getParameters().add(
0,
new JsParameter(sourceInfo.makeChild(
CreateStaticImplsVisitor.class, "Static accessor"), paramName));
RewriteJsniMethodBody rewriter = new RewriteJsniMethodBody(paramName);
// Accept the body to avoid the recursion blocker.
rewriter.accept(jsFunc.getBody());
} else {
RewriteMethodBody rewriter = new RewriteMethodBody(thisParam, varMap);
rewriter.accept(movedBody);
}
// Add the new method as a static impl of the old method
program.putStaticImpl(x, newMethod);
enclosingType.getMethods().add(myIndexInClass + 1, newMethod);
return false;
}