pax_global_header00006660000000000000000000000064132475475340014530gustar00rootroot0000000000000052 comment=0724889e8adc62258b610435b76793d4a5c10a40 bridger-1.5.Final/000077500000000000000000000000001324754753400140435ustar00rootroot00000000000000bridger-1.5.Final/.gitignore000066400000000000000000000000761324754753400160360ustar00rootroot00000000000000.project .classpath .settings *.iml target .idea *.ipr *~ out bridger-1.5.Final/README.md000066400000000000000000000040651324754753400153270ustar00rootroot00000000000000bridger ======= A bytecode mangler for creating your own synthetic bridge methods, which allows more dramatic source code changes while still maintaining binary compatibility. And probably some other ugly tricks too. Usage: Source code ------------------ In your Java source, create your bridge methods with `$$bridge` in the name. Everything before the `$$bridge` is retained, everything including and after it is discarded. The methods will additionally be tagged as `ACC_BRIDGE` and `ACC_SYNTHETIC`. Usage: Maven ------------ Add a snippet like this to your pom.xml: org.jboss.bridger bridger 1.5.Final weave process-classes transform weave-tests process-test-classes transform ${project.build.testOutputDirectory} _Note that a separate execution is needed if you want your test classes to be transformed._ Usage: Command Line ------------------- Execute like this: java -classpath path/to/bridger.jar:path/to/asm-6.0.jar org.jboss.bridger.Bridger path/of/class/files/ The class files will be transformed in place. bridger-1.5.Final/pom.xml000066400000000000000000000162041324754753400153630ustar00rootroot00000000000000 4.0.0 org.jboss.bridger bridger 1.5.Final maven-plugin org.jboss jboss-parent 23 4.11 2.2.1 3.0.5 3.0.5 3.2 3.5.1 3.2 2.6 1.1 3.0.10 1.7 1.7 org.apache.maven.plugins maven-plugin-plugin ${version.org.apache.maven.plugins.plugin} maven-jar-plugin org.jboss.bridger.Bridger ${project.version} ${project.artifactId} org.apache.maven.plugins maven-site-plugin ${version.org.apache.maven.plugins.site-plugin} false org.apache.maven.plugins maven-plugin-plugin ${version.org.apache.maven.plugins.plugin} true org.ow2.asm asm 5.2 default-descriptor descriptor help-goal helpmojo org.jboss.bridger org.ow2.asm asm 6.0 org.apache.maven maven-plugin-api ${version.org.apache.maven.plugin-api} org.apache.maven maven-project ${version.org.apache.maven.maven-plugin} org.apache.maven.shared maven-filtering ${version.org.apache.maven.shared.maven-filtering} org.apache.maven.plugin-tools maven-plugin-annotations ${version.org.apache.maven.plugin-tools} org.codehaus.plexus plexus-utils ${version.org.codehaus.plexus.plexus-utils} org.apache.maven maven-settings ${version.org.apache.maven.maven-settings} bridger-1.5.Final/src/000077500000000000000000000000001324754753400146325ustar00rootroot00000000000000bridger-1.5.Final/src/main/000077500000000000000000000000001324754753400155565ustar00rootroot00000000000000bridger-1.5.Final/src/main/java/000077500000000000000000000000001324754753400164775ustar00rootroot00000000000000bridger-1.5.Final/src/main/java/org/000077500000000000000000000000001324754753400172665ustar00rootroot00000000000000bridger-1.5.Final/src/main/java/org/jboss/000077500000000000000000000000001324754753400204065ustar00rootroot00000000000000bridger-1.5.Final/src/main/java/org/jboss/bridger/000077500000000000000000000000001324754753400220245ustar00rootroot00000000000000bridger-1.5.Final/src/main/java/org/jboss/bridger/Bridger.java000066400000000000000000000174251324754753400242560ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source. * Copyright 2014, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.bridger; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; import java.util.concurrent.atomic.AtomicInteger; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Handle; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; /** * @author David M. Lloyd */ public final class Bridger implements ClassFileTransformer { private final AtomicInteger transformedMethodCount = new AtomicInteger(); private final AtomicInteger transformedMethodCallCount = new AtomicInteger(); public Bridger() { } /** * Translate all {@code .class} files in the given list of files and directories. * * @param args the file and directory names */ public static void main(String[] args) { final Bridger bridger = new Bridger(); bridger.transformRecursive(args); System.out.printf("Translated %d methods and %d method calls%n", bridger.getTransformedMethodCount(), bridger.getTransformedMethodCallCount()); } /** * Translate all {@code .class} files in the given list of files and directories. * * @param names the file and directory names */ public void transformRecursive(String... names) { final File[] files = new File[names.length]; for (int i = 0; i < names.length; i++) { files[i] = new File(names[i]); } transformRecursive(files); } /** * Translate all {@code .class} files in the given list of files and directories. * * @param files the files and directories */ public void transformRecursive(File... files) { for (File file : files) { if (file.isDirectory()) { transformRecursive(file.listFiles()); } else if (file.getName().endsWith(".class")) { try { transform(new RandomAccessFile(file, "rw")); } catch (Exception e) { System.out.println("Failed to transform " + file + ": " + e); } } // else ignore } } public byte[] transform(final ClassLoader loader, final String className, final Class classBeingRedefined, final ProtectionDomain protectionDomain, final byte[] classfileBuffer) throws IllegalClassFormatException { ClassWriter classWriter = new ClassWriter(0); final ClassReader classReader = new ClassReader(classfileBuffer); doAccept(classWriter, classReader); return classWriter.toByteArray(); } public byte[] transform(final InputStream input) throws IllegalClassFormatException, IOException { ClassWriter classWriter = new ClassWriter(0); final ClassReader classReader = new ClassReader(input); doAccept(classWriter, classReader); return classWriter.toByteArray(); } public int getTransformedMethodCount() { return transformedMethodCount.get(); } public int getTransformedMethodCallCount() { return transformedMethodCallCount.get(); } private void doAccept(final ClassWriter classWriter, final ClassReader classReader) throws IllegalClassFormatException { try { classReader.accept(new TranslatingClassVisitor(classWriter), 0); } catch (RuntimeException e) { final Throwable cause = e.getCause(); if (cause instanceof IllegalClassFormatException) { throw (IllegalClassFormatException) cause; } throw e; } } public void transform(final InputStream input, final OutputStream output) throws IllegalClassFormatException, IOException { output.write(transform(input)); } public void transform(final RandomAccessFile file) throws IllegalClassFormatException, IOException { try { file.seek(0); try (InputStream is = new FileInputStream(file.getFD())) { final byte[] bytes = transform(is); file.seek(0); file.write(bytes); file.setLength(bytes.length); } file.close(); } finally { safeClose(file); } } private static void safeClose(final Closeable closeable) { if (closeable != null) try { closeable.close(); } catch (Throwable ignored) {} } private class TranslatingClassVisitor extends ClassVisitor { public TranslatingClassVisitor(final ClassWriter classWriter) { super(Opcodes.ASM4, classWriter); } public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) { final MethodVisitor defaultVisitor; final int idx = name.indexOf("$$bridge"); if (idx != -1) { transformedMethodCount.getAndIncrement(); defaultVisitor = super.visitMethod(access | Opcodes.ACC_BRIDGE | Opcodes.ACC_SYNTHETIC, name.substring(0, idx), desc, signature, exceptions); } else { defaultVisitor = super.visitMethod(access, name, desc, signature, exceptions); } return new MethodVisitor(Opcodes.ASM5, defaultVisitor) { public void visitInvokeDynamicInsn(final String name, final String desc, final Handle bsm, final Object... bsmArgs) { final int idx = name.indexOf("$$bridge"); if (idx != -1) { transformedMethodCallCount.getAndIncrement(); final String realName = name.substring(0, idx); super.visitInvokeDynamicInsn(realName, desc, bsm, bsmArgs); } else { super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); } } public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc, final boolean itf) { final int idx = name.indexOf("$$bridge"); if (idx != -1) { transformedMethodCallCount.getAndIncrement(); final String realName = name.substring(0, idx); super.visitMethodInsn(opcode, owner, realName, desc, itf); } else { super.visitMethodInsn(opcode, owner, name, desc, itf); } } }; } } }bridger-1.5.Final/src/main/java/org/jboss/bridger/BridgerMojo.java000066400000000000000000000076241324754753400251030ustar00rootroot00000000000000/* * JBoss, Home of Professional Open Source. * Copyright 2014, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.bridger; import java.io.File; import java.util.ArrayList; import java.util.List; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.codehaus.plexus.util.DirectoryScanner; /** * A plugin that translates bridge methods. * * @author James R. Perkins */ @Mojo(name = "transform", defaultPhase = LifecyclePhase.PROCESS_CLASSES) public class BridgerMojo extends AbstractMojo { /** * The output directory where resources should be processed */ @Parameter(defaultValue = "${project.build.outputDirectory}", required = true) private File outputDirectory; /** * File patterns to include when processing */ @Parameter(property = "bridger.excludes") private String[] excludes; /** * File patterns to exclude when processing */ @Parameter(defaultValue = "**/*.class", property = "bridger.includes") private String[] includes; @Parameter(defaultValue = "false", property = "bridger.transform.skip") private boolean skip; @Override public void execute() throws MojoExecutionException, MojoFailureException { final Log log = getLog(); if (skip) { log.info("Skipping bridger transform"); } else { final Bridger bridger = new Bridger(); final File[] files = getFiles(); if (log.isDebugEnabled()) { final String newLine = String.format("%n\t"); final StringBuilder sb = new StringBuilder("Transforming Files:"); sb.append(newLine); for (File file : files) { sb.append(file.getAbsolutePath()).append(newLine); } log.debug(sb); } bridger.transformRecursive(files); log.info(String.format("Translated %d methods and %d method calls%n", bridger.getTransformedMethodCount(), bridger.getTransformedMethodCallCount())); } } private File[] getFiles() { final List result = new ArrayList(); final DirectoryScanner scanner = new DirectoryScanner(); scanner.setBasedir(outputDirectory); scanner.setIncludes(includes); scanner.setExcludes(excludes); scanner.scan(); for (String filename : scanner.getIncludedFiles()) { // Only class files final File targetFile = new File(outputDirectory, filename); if (targetFile.exists()) { result.add(targetFile.getAbsoluteFile()); } } return result.toArray(new File[result.size()]); } }