/*******************************************************************************
* 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;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.javatuples.Pair;
import rubah.bytecode.transformers.UpdatableClassRenamer;
import rubah.framework.Clazz;
import rubah.framework.Method;
import rubah.framework.Namespace;
import rubah.framework.Type;
import rubah.tools.UpdatableJarAnalyzer.VersionDescriptor;
import rubah.update.ProgramUpdate;
import rubah.update.UpdateClass;
import rubah.update.V0V0UpdateClass;
public class Version {
private final int number;
private final Map<Pair<Clazz, Method>, Integer> overloads;
private Map<String, String> updatableToOriginalClassNames = new HashMap<String, String>();
private Map<String, String> originalToUpdatableClassNames = new HashMap<String, String>();
private ProgramUpdate update;
private final Namespace namespace;
private final Version previous;
public Version(Namespace namespace) {
this.namespace = namespace;
this.previous = null;
this.number = -1;
this.overloads = new HashMap<Pair<Clazz,Method>, Integer>();
this.update = new ProgramUpdate();
}
public Version(final int number, VersionDescriptor descriptor, Version previous) {
this.previous = previous;
this.number = number;
this.namespace = descriptor.namespace;
this.overloads =
Collections.unmodifiableMap(descriptor.overloads);
}
public void computeTraversal() {
this.computeProgramUpdate(new V0V0UpdateClass(), false, false);
}
public void computeV0V0Update(boolean isLazy) {
this.computeProgramUpdate(null, true, isLazy);
}
public void computeProgramUpdate(UpdateClass updateClass, boolean isLazy) {
this.computeProgramUpdate(updateClass, false, isLazy);
}
private void computeProgramUpdate(UpdateClass updateClass, boolean v0v0, boolean isLazy) {
this.update = new ProgramUpdate(
new ProgramUpdate.Callback() {
public void foundUpdatableClass(Clazz c1) {
Type newType =
Type.getObjectType(UpdatableClassRenamer.rename(c1.getFqn(), Version.this.number).replace('.', '/'));
Version.this.updatableToOriginalClassNames.put(newType.getClassName(), c1.getFqn());
Version.this.originalToUpdatableClassNames.put(c1.getFqn(), newType.getClassName());
}
},
updateClass,
this,
v0v0);
this.update.setLazy(isLazy);
}
public int getNumber() {
return this.number;
}
public Map<Pair<Clazz, Method>, Integer> getOverloads() {
return this.overloads;
}
public String getUpdatableName(String originalName) {
Version v = this;
String ret = null;
while (v != null && (ret = v.originalToUpdatableClassNames.get(originalName)) == null) {
v = v.previous;
}
return ret;
}
public String getOriginalName(String updatableName) {
Version v = this;
String ret = null;
while (v != null && (ret = v.updatableToOriginalClassNames.get(updatableName)) == null) {
v = v.previous;
}
return ret;
}
public Method eraseUpdatableTypes(Method m) {
Clazz retType = this.eraseUpdatableType(m.getRetType());
List<Clazz> argTypes = new LinkedList<Clazz>();
for (Clazz arg : m.getArgTypes()) {
argTypes.add(this.eraseUpdatableType(arg));
}
return new Method(m.getAccess(), m.getName(), retType, argTypes);
}
public Clazz eraseUpdatableType(Clazz c) {
if (c.getNamespace().equals(this.namespace)) {
return this.namespace.getClass(Type.getType(Object.class), c.getDimensions());
} else if (c.isArray() && c.getArrayType().getNamespace().equals(this.namespace)) {
return this.namespace.getClass(Object.class);
} else if (this.previous != null) {
return this.previous.eraseUpdatableType(c);
}
return c;
}
public Namespace getNamespace() {
return this.namespace;
}
public ProgramUpdate getUpdate() {
return this.update;
}
public Version getPrevious() {
return this.previous;
}
public Type renameIfUpdatable(Type type) {
if (type.getSort() == org.objectweb.asm.Type.ARRAY) {
Type renamed = this.renameIfUpdatable(type.getElementType());
if (renamed != type.getElementType()) {
return this.namespace.getClass(renamed, type.getDimensions()).getASMType();
}
} else {
String newName = this.getUpdatableName(type.getClassName());
if (newName != null) {
type = Type.getObjectType(newName.replace('.', '/'));
}
}
return type;
}
private Type[] renameIfUpdatable(Type[] types) {
Type[] ret = new Type[types.length];
int i = 0;
for (Type t : types) {
ret[i++] = this.renameIfUpdatable(t);
}
return ret;
}
public String renameInternalIfUpdatable(String internalName) {
return this.renameIfUpdatable(Type.getObjectType(internalName)).getInternalName();
}
public String renameDescIfUpdatable(String desc) {
return this.renameIfUpdatable(Type.getType(desc)).getDescriptor();
}
public String renameMethodDescIfUpdatable(String methodDesc) {
Type retType = this.renameIfUpdatable(Type.getReturnType(methodDesc));
Type[] argTypes = this.renameIfUpdatable(Type.getArgumentTypes(methodDesc));
return Type.getMethodDescriptor(retType, argTypes);
}
}