/*******************************************************************************
* 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;
import java.io.File;
import java.util.LinkedList;
import java.util.Set;
import org.objectweb.asm.Opcodes;
import rubah.Rubah;
import rubah.framework.Clazz;
import rubah.runtime.Version;
import rubah.runtime.VersionManager;
import rubah.runtime.classloader.RubahClassloader;
import rubah.runtime.state.migrator.UnsafeUtils;
public class ComputingUpdateMetadata extends RubahState implements Opcodes {
private static final String UPDATE_PACKAGE_DUMP_FILE_PROPERTY = "dumpUpdatePackage";
private static final File UPDATE_PACKAGE_DUMP_FILE;
static {
String fileName = System.getProperty(UPDATE_PACKAGE_DUMP_FILE_PROPERTY);
if (fileName != null)
UPDATE_PACKAGE_DUMP_FILE = new File(fileName);
else
UPDATE_PACKAGE_DUMP_FILE = null;
}
public ComputingUpdateMetadata(UpdateState state) {
super(state);
}
@Override
public void doStart() {
long time = System.currentTimeMillis();
Version v1 = VersionManager.getInstance().getLatestVersion();
LinkedList<Class<?>> loadedClasses = null;
synchronized (RubahClassloader.class) {
loadedClasses = new LinkedList<Class<?>>(RubahClassloader.getLoadedClasses());
}
// Map<String, ClassRedefinition> redefinitions = null;
// File updatePackage = this.state.getOptions().getUpdatePackage();
// ObjectInputStream ois = null;
//
// try {
// if (updatePackage != null) {
// ois = new ObjectInputStream(new FileInputStream(updatePackage));
// redefinitions = (Map<String, ClassRedefinition>) ois.readObject();
// }
// } catch (FileNotFoundException e) {
// throw new Error(e);
// } catch (IOException e) {
// throw new Error(e);
// } catch (ClassNotFoundException e) {
// throw new Error(e);
// } finally {
// try {
// if (ois != null)
// ois.close();
// } catch (IOException e) {
// // Don't care
// }
// }
// if (redefinitions == null) {
// ObjectOutputStream oos = null;
// try {
// redefinitions = computeBytecode(v1, loadedClasses);
//
// if (UPDATE_PACKAGE_DUMP_FILE != null) {
// oos = new ObjectOutputStream(new FileOutputStream(UPDATE_PACKAGE_DUMP_FILE));
// oos.writeObject(redefinitions);
// }
//
// } catch (IOException e) {
// throw new Error(e);
// } finally {
// if (oos != null)
// try {
// oos.close();
// } catch (IOException e) {
// // Don't care
// }
// }
// }
//
// state.setRedefinitions(redefinitions);
for (Class<?> c0 : loadedClasses) {
String originalName = v1.getOriginalName(c0.getName());
if (originalName == null)
continue;
Class<?> c1;
try {
c1 = Class.forName(v1.getUpdatableName(originalName), false, Rubah.getLoader());
} catch (ClassNotFoundException e) {
throw new Error(e);
}
UnsafeUtils.getInstance().setOffsets(c0);
UnsafeUtils.getInstance().setOffsets(c1);
}
time = System.currentTimeMillis() - time;
System.out.println("Time spent computing update metadata " + time + "ms");
}
@Override
public void update(String updatePoint) {
/* Empty */
}
@Override
public boolean isUpdating() {
return false;
}
@Override
public boolean isUpdateRequested() {
return false;
}
private static void computeInterestingAndFailfastClasses(Set<String> interestingClassNames, Set<String> failfastClassNames, Version version) {
for (Clazz c1 : version.getNamespace().getDefinedClasses()) {
for (Version v = version; v != null ; v = v.getPrevious()) {
Clazz c0 = v.getUpdate().getV0(c1);
if (c0 == null) {
// Class introduced in version v
interestingClassNames.add(v.getUpdatableName(c1.getFqn()));
break;
}
if (v == version && v.getUpdate().isUpdated(c0)) {
failfastClassNames.add(v.getPrevious().getUpdatableName(c1.getFqn()));
interestingClassNames.add(v.getUpdatableName(c1.getFqn()));
break;
}
if (v.getPrevious() == null) {
interestingClassNames.add(v.getUpdatableName(c1.getFqn()));
break;
}
}
}
}
// public Map<String, ClassRedefinition> computeBytecode(Version version, Collection<Class<?>> loadedClasses) throws IOException {
// Set<String> interestingClassNames = new HashSet<String>();
// Set<String> failfastClassNames = new HashSet<String>();
//
// computeInterestingAndFailfastClasses(interestingClassNames, failfastClassNames, version);
//
// Map<String, ClassRedefinition> redefinitions = new HashMap<String, ClassRedefinition>();
//
// for (Class<?> loadedClass : loadedClasses) {
// String className = loadedClass.getName();
// byte[] scaffoldedBytecodes = null;
// byte[] newBytecodes = null;
// boolean classChanged = false;
// boolean isFailfast = false;
//
// if (!AddTraverseMethod.isAllowed(className) || loadedClass.equals(Object.class))
// continue;
//
// if (className.startsWith("org.apache.commons.io")) {
// // Loaded by Rubah and bypassed bytecode processing, disregard
// continue;
// }
//
// String originalName = version.getOriginalName(className);
//
// if (originalName == null) {
// // Non-updatable, update refs to old code
//
// GetMostRecentUsedFactory f = this.getMostRecentUsedFactory();
// scaffoldedBytecodes = UpdateManager.getInstance().getClassBytes(className, version, f);
//
// classChanged = (f.mostRecentVersionUsed == UpdateManager.getInstance().getLatestVersion());
// if (classChanged)
// newBytecodes = UpdateManager.getInstance().getClassBytes(className, version);
//
// } else if (failfastClassNames.contains(className)) {
// // TODO: Compute failfast bytecode
// classChanged = true;
// isFailfast = true;
// }
//
// ClassRedefinition bytecode = new ClassRedefinition(scaffoldedBytecodes, newBytecodes, isFailfast, classChanged);
// redefinitions.put(className, bytecode);
// }
//
// for (String className : interestingClassNames) {
// GetMostRecentUsedFactory f = this.getMostRecentUsedFactory();
// byte[] scaffoldedBytecodes = UpdateManager.getInstance().getClassBytes(className, version, f);
// boolean classChanged = (f.mostRecentVersionUsed == UpdateManager.getInstance().getLatestVersion());
// byte[] newBytecodes = UpdateManager.getInstance().getClassBytes(className, version);
// ClassRedefinition bytecode = new ClassRedefinition(scaffoldedBytecodes, newBytecodes, false, classChanged);
// redefinitions.put(className, bytecode);
// }
//
// return redefinitions;
// }
//
// protected GetMostRecentUsedFactory getMostRecentUsedFactory() {
// return new GetMostRecentUsedFactory();
// }
//
// protected class GetMostRecentUsedFactory extends TransformerFactory {
// protected Version mostRecentVersionUsed;
//
// @Override
// public ClassVisitor getUpdatableClassRenamer(
// HashMap<String, Object> objectsMap,
// Version version,
// Namespace namespace,
// ClassVisitor visitor) {
// return new UpdatableClassRenamerVersionGetter(version, objectsMap, namespace, visitor);
// }
//
// protected void registerVersion(Type type) {
// Version v = UpdateManager.getInstance().getIntroducedVersion(type.getClassName());
//
// if (this.mostRecentVersionUsed == null || v.getNumber() > this.mostRecentVersionUsed.getNumber()) {
// this.mostRecentVersionUsed = v;
// }
// }
//
// protected class UpdatableClassRenamerVersionGetter extends UpdatableClassRenamer {
// public UpdatableClassRenamerVersionGetter(Version version,
// HashMap<String, Object> objectsMap, Namespace namespace,
// ClassVisitor cv) {
// super(version, objectsMap, namespace, cv);
// }
//
// @Override
// protected Type renameIfUpdatable(Type type) {
// Type ret = super.renameIfUpdatable(type);
//
// if (ret != type)
// registerVersion(ret);
//
// return ret;
// }
// }
// }
}