/******************************************************************************* * 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.tools; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.HashSet; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import rubah.bytecode.transformers.AddForwardField; import rubah.bytecode.transformers.AddGettersAndSetters; import rubah.bytecode.transformers.AddHashCodeField; import rubah.bytecode.transformers.AddHashCodeMethod; import rubah.bytecode.transformers.AddTraverseMethod; import rubah.bytecode.transformers.BasicClassInfoGatherer; import rubah.bytecode.transformers.DecreaseClassMethodsProtection; import rubah.bytecode.transformers.ProxyGenerator; import rubah.bytecode.transformers.RedirectFieldManipulation; import rubah.bytecode.transformers.RubahTransformer; import rubah.framework.Clazz; import rubah.framework.DelegatingNamespace; import rubah.framework.Field; import rubah.framework.Namespace; import rubah.framework.Type; import rubah.runtime.Version; import rubah.tools.UpdatableJarAnalyzer.ClassData; public class BootstrapJarProcessor extends ReadWriteTool implements Opcodes { public static final String TOOL_NAME = "bootstraper"; private static final String META_INFO_FILE = "metainfo.bin"; private Namespace namespace = new Namespace(); @SuppressWarnings("unchecked") public static void readBootstrapMetaInfo(Namespace namespace) throws IOException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); ObjectInputStream inStream = new ObjectInputStream(cl.getResourceAsStream(META_INFO_FILE)); HashSet<ClassData> bootstrapClasses; try { bootstrapClasses = (HashSet<ClassData>) inStream.readObject(); } catch (ClassNotFoundException e) { throw new Error(e); } for (ClassData cd : bootstrapClasses) { cd.toClass(namespace); } } private State state; @Override public void processJar() throws IOException { this.state = new BuildingMetaData(); super.processJar(); this.state = new TransformingClasses(); super.processJar(); } @Override protected void foundClassFile(String name, InputStream inputStream) throws IOException { this.state.foundClassFile(name, inputStream); } @Override protected void endProcess() throws IOException { this.state.endProcess(); } private static interface State { public void foundClassFile(String name, InputStream inputStream) throws IOException; public void endProcess() throws IOException; } private class BuildingMetaData implements State { @Override public void foundClassFile(String name, InputStream inputStream) throws IOException { ClassVisitor visitor = new BasicClassInfoGatherer(namespace); visitor = new DecreaseClassMethodsProtection(visitor); new ClassReader(inputStream).accept(visitor, ClassReader.SKIP_FRAMES); } @Override public void endProcess() throws IOException { // Empty } } private class TransformingClasses implements State { @Override public void foundClassFile(String name, InputStream inputStream) throws IOException { System.out.println(name); if (name.startsWith("sun/security")) return; ClassReader reader = new ClassReader(inputStream); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassVisitor visitor = writer; visitor = new AddHashCodeMethod(namespace, visitor); visitor = new AddForwardField(namespace, visitor); visitor = new AddGettersAndSetters(namespace, visitor); visitor = new AddHashCodeField(namespace, visitor); // visitor = new AddStaticTraverseMethod(null, this.namespace, visitor); // visitor = new AddTraverseMethod(null, this.namespace, visitor); visitor = new RubahTransformer(null, namespace, visitor){ @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces, false); // Add $hashCode to java.lang.Class if (this.thisClass.getFqn().equals(Class.class.getName())) { this.thisClass.getFields().add(new Field( AddHashCodeField.FIELD_MODIFIERS, AddHashCodeField.FIELD_NAME, this.namespace.getClass(Type.INT_TYPE), false)); } this.cv.visit(version, access, name, signature, superName, interfaces); } }; visitor = new DecreaseClassMethodsProtection(visitor); visitor = new RedirectFieldManipulation(namespace, visitor); reader.accept(visitor, ClassReader.EXPAND_FRAMES); addFileToOutJar(name, writer.toByteArray()); } @Override public void endProcess() throws IOException { ByteArrayOutputStream innerStream = new ByteArrayOutputStream(); ObjectOutputStream outStream = new ObjectOutputStream(innerStream); HashSet<ClassData> bootstrapClasses = new HashSet<ClassData>(); for (Clazz c : new HashSet<Clazz>(namespace.getAllClasses())) { if (c.getASMType().isPrimitive() || c.isArray()) continue; bootstrapClasses.add(new ClassData(c)); if (AddTraverseMethod.isAllowed(c.getFqn())) { // String frontierName = FrontierClassGenerator.generateFrontierName(c.getFqn()); // // // Generate frontier classes a-priori because namespaces are protected // System.out.println(frontierName); // addFileToOutJar( // frontierName.replace('.', '/') + ".class", // new FrontierClassGenerator(c, new Version(new DelegatingNamespace(namespace, new HashSet<String>()))).generateProxy()); String proxyName = ProxyGenerator.generateProxyName(c.getFqn()); System.out.println(proxyName); // Generate proxies a-priori because namespaces are protected addFileToOutJar( proxyName.replace('.', '/') + ".class", new ProxyGenerator(c, new Version(new DelegatingNamespace(namespace, new HashSet<String>()))).generateProxy()); } } outStream.writeObject(bootstrapClasses); foundResource(META_INFO_FILE, new ByteArrayInputStream(innerStream.toByteArray())); } } }