/*******************************************************************************
* 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.state.migrator;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import rubah.Rubah;
import rubah.bytecode.transformers.AddTraverseMethod;
import rubah.bytecode.transformers.ProcessUpdateClass;
import rubah.bytecode.transformers.ProxyGenerator;
import rubah.framework.Clazz;
import rubah.framework.Type;
import rubah.runtime.Version;
import rubah.runtime.state.migrator.UnsafeUtils.ClassOffsets;
import rubah.runtime.state.strategy.MigrationStrategy;
public class StaticFieldsMigratorFactory extends MigratorSubFactory {
private Version v1;
public StaticFieldsMigratorFactory(MigrationStrategy strategy, Version v1) {
super(strategy);
this.v1 = v1;
}
@Override
public boolean canMigrate(Class<?> c) {
if (c.isInterface() || !AddTraverseMethod.isAllowed(c.getName()))
return false;
if (c.getClassLoader() != Rubah.getLoader()) {
return false;
}
if (!Rubah.getLoader().isResolved(c.getName())) {
return false;
}
//TODO this method is a mess, rewrite it carefully
String originalName = this.v1.getPrevious().getOriginalName(
c.getName());
if (originalName == null) {
// Class belongs the new version, doesn't need to be
// converted
return false;
}
if (this.v1.getPrevious().getPrevious() != null
&& this.v1.getPrevious().getPrevious()
.getOriginalName(c.getName()) != null) {
// Class was redefined earlier than last version
return false;
}
originalName = v1.getOriginalName(c.getName());
if (originalName != null) {
// Class is updatable
Clazz c1 = v1.getNamespace().getClass(
Type.getObjectType(originalName.replace('.', '/')));
Clazz c0 = v1.getUpdate().getV0(c1);
if (c0 == null) {
// Class was introduced in this version
return false;
}
}
return true;
}
@Override
public Migrator buildMigrator() {
return new StaticFieldsMigrator();
}
private class StaticFieldsMigrator extends Migrator {
private boolean traverse = true;
@Override
protected Object doMigrate(Object pre) {
Class<?> c = (Class<?>)pre;
String originalName = v1.getOriginalName(c.getName());
Clazz c1 = v1.getNamespace().getClass(
Type.getObjectType(originalName.replace('.', '/')));
Clazz c0 = v1.getUpdate().getV0(c1);
Method conversionMethod;
try {
// Map the old class to a new class
Class<?> conversionOwner;
Class<?> post = Class.forName(
v1.getUpdatableName(originalName), true,
Rubah.getLoader());
if (!post.isEnum()) {
// if (v1.getUpdate().isConverted(c0)) {
// if (v1.getUpdate().isUpdated(c0)) {
// Run the class conversion method for the static
// fields
conversionOwner =
Class.forName(
ProxyGenerator.generateProxyName(post.getName()),
true,
Rubah.getLoader());
conversionMethod = conversionOwner.getMethod(
ProcessUpdateClass.METHOD_NAME_STATIC, c);
// } else {
// conversionOwner = Class.forName(
// PureConversionClassLoader.PURE_CONVERSION_PREFFIX
// + v1.getNumber(), true, Rubah.getLoader());
// // Run the class conversion method for the static fields
// conversionMethod = conversionOwner.getMethod(
// ProcessUpdateClass.METHOD_NAME_STATIC, c);
// conversionOwner.getMethod(
// ProcessUpdateClass.METHOD_NAME_STATIC, c);
// }
if (Rubah.getLoader().isResolved(c.getName())) {
// Migrate static fields if resolved
conversionMethod.invoke(null, new Object[] { null });
}
}
return post;
} catch (ClassNotFoundException e) {
throw new Error(e);
} catch (NoSuchMethodException e) {
throw new Error(e);
} catch (SecurityException e) {
throw new Error(e);
} catch (IllegalAccessException e) {
throw new Error(e);
} catch (IllegalArgumentException e) {
throw new Error(e);
} catch (InvocationTargetException e) {
throw new Error(e);
}
}
@Override
public void followReferences(Object post) {
if (!this.traverse)
return;
Class<?> c = (Class<?>) post;
if (!AddTraverseMethod.isAllowed(c.getName()))
return;
ClassOffsets offsets = UnsafeUtils.getInstance().getOffsets(c);
Object staticBase = offsets.getStaticBase();
for (long offset : offsets.getStaticOffsets())
strategy.migrate(staticBase, offset, staticBase, offset);
}
}
}