/*******************************************************************************
* Copyright 2014,
* Luis Pina <luis@luispina.me>,
* Michael Hicks <mwh@cs.umd.edu>
*
* This file is part of Rubah.
*
* Rubah is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Rubah is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Rubah. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package rubah.runtime.classloader;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.MethodNode;
import rubah.bytecode.transformers.ProcessUpdateClass;
import rubah.framework.Clazz;
import rubah.framework.Type;
import rubah.runtime.Version;
import rubah.tools.UpdateClassGenerator;
import rubah.update.ProgramUpdate;
import rubah.update.UpdateClass;
public class PureConversionClassLoader extends DefaultClassLoader implements Opcodes {
public static final String PURE_CONVERSION_PREFFIX = "conversion.Conversion";
private static final String CONSTRUCTOR_NAME = "<init>";
private static final String OBJECT_INTERNAL_NAME =
Type.getType(Object.class).getInternalName();
private Version version;
private Map<String, MethodNode> methodNodeIndex;
public PureConversionClassLoader(Version version, UpdateClass updateClass) {
super(version.getNamespace(), new TransformerFactory());
this.version = version;
this.methodNodeIndex = ProcessUpdateClass.getMethodNodeIndex(updateClass.getBytes());
}
@Override
public byte[] getClass(String className) throws IOException {
if (!className.equals(PURE_CONVERSION_PREFFIX + this.version.getNumber())) {
return null;
}
return this.generatePureConversionClass(className);
}
private static boolean isPureConversion(Clazz c0, ProgramUpdate update) {
return update.isConverted(c0) && // is converted
!update.isUpdated(c0); // but not updated
}
private static boolean isUpdatedWithUnchangedSuper(Clazz c0, ProgramUpdate update) {
return update.isUpdated(c0) && // is updated
c0.getParent().getNamespace().equals(c0.getNamespace()) && // parent is updatable
!update.isUpdated(c0.getParent()); // but not updated
}
private byte[] generatePureConversionClass(String className) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(
V1_5,
ACC_PUBLIC,
className.replace('.', '/'),
null,
OBJECT_INTERNAL_NAME, null);
MethodVisitor mv = cw.visitMethod(
ACC_PRIVATE,
CONSTRUCTOR_NAME,
Type.getMethodDescriptor(Type.VOID_TYPE),
null,
null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(
INVOKESPECIAL,
OBJECT_INTERNAL_NAME,
CONSTRUCTOR_NAME,
Type.getMethodDescriptor(Type.VOID_TYPE),
false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
Set<Clazz> conversionMethodsToGenerate = new HashSet<Clazz>();
ProgramUpdate update = this.version.getUpdate();
for (Clazz c0 : this.version.getPrevious().getNamespace().getDefinedClasses()) {
Clazz c1 = update.getV1(c0);
if (c1 == null) {
continue;
}
if (isPureConversion(c0, update)) {
conversionMethodsToGenerate.add(c0);
} else if (isUpdatedWithUnchangedSuper(c0, update)) {
Clazz tmp = c0.getParent();
while (tmp.getNamespace().equals(c0.getNamespace())) {
conversionMethodsToGenerate.add(tmp);
tmp = tmp.getParent();
}
}
}
for (Clazz c0 : conversionMethodsToGenerate) {
Clazz c1 = update.getV1(c0);
String oldTypeName =
UpdateClassGenerator.V0_PREFFIX + "." + c0.getFqn();
Type oldDummyType = Type.getObjectType(oldTypeName.replace('.', '/'));
String newTypeName =
UpdateClassGenerator.V1_PREFFIX + "." + c0.getFqn();
Type newDummyType = Type.getObjectType(newTypeName.replace('.', '/'));
ProcessUpdateClass.generateConversionMethod(
this.version,
c1,
new ProcessUpdateClass.ConvertMethodGenerator[]{
new ProcessUpdateClass.NormalConvertMethodGenerator(
oldDummyType,
newDummyType,
this.version,
cw,
this.methodNodeIndex),
new ProcessUpdateClass.StaticConvertMethodGenerator(
c1,
oldDummyType,
newDummyType,
this.version,
cw,
this.methodNodeIndex)});
}
cw.visitEnd();
return cw.toByteArray();
}
@Override
protected String getOriginalClassName(String className) {
return className;
}
@Override
protected void analyzeClass(byte[] classBytes) {
// No need to analyze overload helper classes
}
}