clazz) {
return clazz.newInstance();
}
// added bridge method
public FooSubType createFoo(Class clazz) {
invokevirtual this.createFoo(java/lang/Class)LFoo
checkcast FooSubType
areturn
}
```
# Bridge Methods and Interfaces
You can use `@WithBridgeMethods` with interfaces, too. However, making this work correctly is tricky,
as you have to ensure that bridge methods are implemented on all the classes that implement the interface,
for example by adding `@WithBridgeMethods` on every implementation of the method in question,
or by introducing a base class that provides a bridge method.
# Integration into Your Build
Add the following dependency in your POM. (This dependency is not needed at runtime, but it is necessary
for compilation of source code that transitively depend on this, so it is the simplest to just treat
this like a regular library dependency)
```xml
com.infradna.tool
bridge-method-annotation
1.24
```
Then put the following fragment in your build to have the byte-code post processor kick in to inject the necessary bridge methods.
```xml
com.infradna.tool
bridge-method-injector
1.24
process
```
bridge-method-injector-bridge-method-injector-parent-1.25/annotation/ 0000775 0000000 0000000 00000000000 14335561456 0025766 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/annotation/pom.xml 0000664 0000000 0000000 00000001341 14335561456 0027302 0 ustar 00root root 0000000 0000000
4.0.0
com.infradna.tool
bridge-method-injector-parent
1.25
bridge-method-annotation
Bridge method injection annotations
org.jenkins-ci
annotation-indexer
1.16
bridge-method-injector-bridge-method-injector-parent-1.25/annotation/src/ 0000775 0000000 0000000 00000000000 14335561456 0026555 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/annotation/src/main/ 0000775 0000000 0000000 00000000000 14335561456 0027501 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/annotation/src/main/java/ 0000775 0000000 0000000 00000000000 14335561456 0030422 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/annotation/src/main/java/com/ 0000775 0000000 0000000 00000000000 14335561456 0031200 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/annotation/src/main/java/com/infradna/ 0000775 0000000 0000000 00000000000 14335561456 0032762 5 ustar 00root root 0000000 0000000 tool/ 0000775 0000000 0000000 00000000000 14335561456 0033660 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/annotation/src/main/java/com/infradna bridge_method_injector/ 0000775 0000000 0000000 00000000000 14335561456 0040351 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/annotation/src/main/java/com/infradna/tool BridgeMethodsAdded.java 0000664 0000000 0000000 00000003264 14335561456 0044663 0 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/annotation/src/main/java/com/infradna/tool/bridge_method_injector /*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.infradna.tool.bridge_method_injector;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation is added after the class transformation to indicate that
* the class has already been processed by bridge method injector.
*
*
* Used for up-to-date check to avoid unnecessary transformations.
*
* @author Kohsuke Kawaguchi
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface BridgeMethodsAdded {
}
WithBridgeMethods.java 0000664 0000000 0000000 00000010733 14335561456 0044574 0 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/annotation/src/main/java/com/infradna/tool/bridge_method_injector /*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.infradna.tool.bridge_method_injector;
import org.jvnet.hudson.annotation_indexer.Indexed;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Request that bridge methods of the same name and same arguments be generated
* with each specified type as the return type. This helps you maintain binary compatibility
* as you evolve your classes.
*
*
* For example, if you have the following code:
*
*
* @WithBridgeMethods(Foo.class)
* public FooSubType getFoo() { ... }
*
*
*
* The Maven mojo will insert the following bridge method:
*
*
* public Foo getFoo() {
* return getFoo(); // invokevirtual to getFoo() that returns FooSubType
* }
*
*
*
* In some cases, it's necessary to widen the return type of a method, but in a way that legacy
* calls would still return instances of the original type. In this case, add
* {@link #castRequired() castRequired=true} to the annotation. For example, if you have the
* following code:
*
* @WithBridgeMethods(value=FooSubType.class, castRequired=true)
* public <T extends Foo> createFoo(Class<T> clazz) {
* return clazz.newInstance();
* }
*
*
* The Maven mojo will insert the following bridge method:
*
*
* public FooSubType createFoo(Class clazz) {
* return (FooSubType) createFoo(clazz); // invokeVirtual to createFoo that returns Foo
* }
*
*
*
* In extreme cases, this method can add a method whose return type has nothing to do
* with the return type of the declared method. For example, if you have the following code:
*
*
* @WithBridgeMethods(value=String.class, adapterMethod="convert")
* public URL getURL() {
* URL url = ....
* return url;
* }
*
* private Object convert(URL url, Class targetType) { return url.toString(); }
*
*
*
* The Maven mojo will insert the following bridge method:
*
*
* public String getURL() {
* return (String)convert(getURL(),String.class); // invokeVirtual to getURL that returns URL
* }
*
*
*
* The specified adapter method must be a method specified on the current class
* or its ancestors. It cannot be a static method.
*
* @author Kohsuke Kawaguchi
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
@Documented
@Indexed
public @interface WithBridgeMethods {
/**
* Specifies the return types. These types must be assignable from the actual
* method return type, or {@link #castRequired()} should be set to true.
*/
Class>[] value();
/**
* Specifies whether the injected bridge methods should perform a cast prior to returning. Only
* set this to true when it is known that calls to the bridge methods will in fact return
* objects assignable to {@linkplain #value() the bridge method return type}, even though
* the declared method return type is not assignable to them.
*
* @since 1.4
*/
boolean castRequired() default false;
/**
* Specifies the method to convert return value. This lets bridge methods to return
* any types, even if it's unrelated to the return type of the declared method.
*
* @since 1.14
*/
String adapterMethod() default "";
}
bridge-method-injector-bridge-method-injector-parent-1.25/injector/ 0000775 0000000 0000000 00000000000 14335561456 0025431 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/pom.xml 0000664 0000000 0000000 00000015107 14335561456 0026752 0 ustar 00root root 0000000 0000000
4.0.0
com.infradna.tool
bridge-method-injector-parent
1.25
bridge-method-injector
maven-plugin
bridge-method-injector
Evolve your classes without breaking compatibility
9.4
3.7.0
org.apache.maven.plugins
maven-plugin-plugin
${maven-plugin-tools.version}
maven-antrun-plugin
run
test
${project.groupId}
bridge-method-annotation
${project.version}
com.github.spotbugs
spotbugs-annotations
true
com.google.code.findbugs
jsr305
org.ow2.asm
asm
${asm.version}
org.ow2.asm
asm-commons
${asm.version}
org.apache.maven
maven-plugin-api
3.8.6
provided
org.apache.maven.plugin-tools
maven-plugin-annotations
${maven-plugin-tools.version}
provided
junit
junit
test
bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/ 0000775 0000000 0000000 00000000000 14335561456 0026220 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/main/ 0000775 0000000 0000000 00000000000 14335561456 0027144 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/main/java/ 0000775 0000000 0000000 00000000000 14335561456 0030065 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/main/java/com/ 0000775 0000000 0000000 00000000000 14335561456 0030643 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/main/java/com/infradna/ 0000775 0000000 0000000 00000000000 14335561456 0032425 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/main/java/com/infradna/tool/ 0000775 0000000 0000000 00000000000 14335561456 0033402 5 ustar 00root root 0000000 0000000 bridge_method_injector/ 0000775 0000000 0000000 00000000000 14335561456 0040014 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/main/java/com/infradna/tool ClassAnnotationInjector.java 0000664 0000000 0000000 00000004722 14335561456 0045462 0 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/main/java/com/infradna/tool/bridge_method_injector /*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.infradna.tool.bridge_method_injector;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* Adds class annotations.
*
* @author Kohsuke Kawaguchi
*/
abstract class ClassAnnotationInjector extends ClassVisitor {
ClassAnnotationInjector(ClassVisitor cv) {
super(Opcodes.ASM9, cv);
}
private boolean emitted = false;
private void maybeEmit() {
if (!emitted) {
emitted = true;
emit();
}
}
protected abstract void emit();
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
maybeEmit();
super.visitInnerClass(name, outerName, innerName, access);
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
maybeEmit();
return super.visitField(access, name, desc, signature, value);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
maybeEmit();
return super.visitMethod(access, name, desc, signature, exceptions);
}
@Override
public void visitEnd() {
maybeEmit();
super.visitEnd();
}
}
MethodInjector.java 0000664 0000000 0000000 00000032115 14335561456 0043577 0 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/main/java/com/infradna/tool/bridge_method_injector /*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.infradna.tool.bridge_method_injector;
import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_BRIDGE;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.POP;
import static org.objectweb.asm.Opcodes.POP2;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
/**
* Injects bridge methods as per {@link WithBridgeMethods}.
*
* @author Kohsuke Kawaguchi
*/
public class MethodInjector {
public void handleRecursively(File f) throws IOException {
if (f.isDirectory()) {
File[] files = f.listFiles();
if (files != null) {
for (File c : files) {
handleRecursively(c);
}
}
} else if (f.getName().endsWith(".class")) {
handle(f);
}
}
public void handle(File classFile) throws IOException {
byte[] image;
try (FileInputStream in = new FileInputStream(classFile); BufferedInputStream bis = new BufferedInputStream(in)) {
ClassReader cr = new ClassReader(bis);
ClassWriter cw = new ClassWriter(cr,COMPUTE_MAXS);
cr.accept(new Transformer(new ClassAnnotationInjectorImpl(cw)),0);
image = cw.toByteArray();
} catch (AlreadyUpToDate unused) {
// no need to process this class. it's already up-to-date.
return;
} catch (IOException | RuntimeException e) {
throw new IOException("Failed to process " + classFile, e);
}
// write it back
try (FileOutputStream out = new FileOutputStream(classFile)) {
out.write(image);
}
}
/**
* Thrown to indicate that there's no need to re-process this class file.
*/
static class AlreadyUpToDate extends RuntimeException {
private static final long serialVersionUID = 1L;
}
static class ClassAnnotationInjectorImpl extends ClassAnnotationInjector {
ClassAnnotationInjectorImpl(ClassVisitor cv) {
super(cv);
}
@Override
protected void emit() {
AnnotationVisitor av = cv.visitAnnotation(SYNTHETIC_METHODS_ADDED, false);
av.visitEnd();
}
}
private static class WithBridgeMethodsAnnotationVisitor extends AnnotationVisitor {
protected boolean castRequired = false;
protected String adapterMethod = null;
protected final List types = new ArrayList<>();
public WithBridgeMethodsAnnotationVisitor(AnnotationVisitor av) {
super(Opcodes.ASM9, av);
}
@Override
public AnnotationVisitor visitArray(String name) {
return new AnnotationVisitor(Opcodes.ASM9, super.visitArray(name)) {
@Override
public void visit(String name, Object value) {
if (value instanceof Type) {
// assume this is a member of the array of classes named "value" in WithBridgeMethods
types.add((Type) value);
}
super.visit(name, value);
}
};
}
@Override
public void visit(String name, Object value) {
if ("castRequired".equals(name) && value instanceof Boolean) {
castRequired = (Boolean) value;
}
if ("adapterMethod".equals(name) && value instanceof String) {
adapterMethod = (String) value;
}
super.visit(name, value);
}
}
static class Transformer extends ClassVisitor {
private String internalClassName;
/**
* Synthetic methods to be generated.
*/
private final List syntheticMethods = new ArrayList<>();
class SyntheticMethod {
final int access;
final String name;
final String desc;
final String originalSignature;
final String[] exceptions;
final boolean castRequired;
final String adapterMethod;
/**
* Return type of the bridge method to be inserted.
*/
final Type returnType;
/**
* Return type of the declared method written in the source code.
*/
final Type originalReturnType;
SyntheticMethod(int access, String name, String desc, String originalSignature, String[] exceptions, Type returnType, boolean castRequired, String adapterMethod) {
this.access = access;
this.name = name;
this.desc = desc;
this.originalSignature = originalSignature;
this.exceptions = exceptions;
this.returnType = returnType;
this.castRequired = castRequired;
this.adapterMethod = adapterMethod;
originalReturnType = Type.getReturnType(desc);
}
/**
* Injects a synthetic method and send it to cv.
*/
public void inject(ClassVisitor cv) {
Type[] paramTypes = Type.getArgumentTypes(desc);
int access = this.access | ACC_SYNTHETIC | ACC_BRIDGE;
String methodDescriptor = Type.getMethodDescriptor(returnType, paramTypes);
MethodVisitor mv = cv.visitMethod(access, name,
methodDescriptor, null/*TODO:is this really correct?*/, exceptions);
if ((access&ACC_ABSTRACT)==0) {
GeneratorAdapter ga = new GeneratorAdapter(mv, access, name, methodDescriptor);
mv.visitCode();
int sz = 0;
if (hasAdapterMethod()) {
// the LHS of the adapter method invocation
ga.loadThis();
sz++;
}
boolean isStatic = (access & ACC_STATIC) != 0;
if (!isStatic) {
ga.loadThis();
sz++;
}
for (Type p : paramTypes) {
mv.visitVarInsn(p.getOpcode(ILOAD), sz);
sz += p.getSize();
}
mv.visitMethodInsn(
isStatic ? INVOKESTATIC : INVOKEVIRTUAL,internalClassName,name,desc,false);
if (hasAdapterMethod()) {
insertAdapterMethod(ga);
} else
if (castRequired || returnType.equals(Type.VOID_TYPE)) {
ga.unbox(returnType);
} else {
ga.box(originalReturnType);
}
if (returnType.equals(Type.VOID_TYPE) || returnType.getClassName().equals("java.lang.Void")) {
// bridge to void, which means disregard the return value from the original method
switch (originalReturnType.getSize()) {
case 0:
throw new IllegalArgumentException("Cannot bridge " + name + " from void to void; did you mean to use a different type?");
case 1:
mv.visitInsn(POP);
break;
case 2:
mv.visitInsn(POP2);
break;
default:
throw new AssertionError("Unexpected operand size: "+originalReturnType);
}
}
mv.visitInsn(returnType.getOpcode(IRETURN));
mv.visitMaxs(sz,0);
}
mv.visitEnd();
}
private boolean hasAdapterMethod() {
return adapterMethod!=null && adapterMethod.length()>0;
}
private void insertAdapterMethod(GeneratorAdapter ga) {
ga.push(returnType);
ga.visitMethodInsn(INVOKEVIRTUAL, internalClassName, adapterMethod,
Type.getMethodDescriptor(
Type.getType(Object.class), // return type
originalReturnType,
Type.getType(Class.class)
), false);
ga.unbox(returnType);
}
}
Transformer(ClassVisitor cv) {
super(Opcodes.ASM9, cv);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.internalClassName = name;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (desc.equals(SYNTHETIC_METHODS_ADDED)) {
throw new AlreadyUpToDate(); // no need to process this class
}
return super.visitAnnotation(desc, visible);
}
/**
* Look for methods annotated with {@link WithBridgeMethods}.
*/
@Override
public MethodVisitor visitMethod(final int access, final String name, final String mdesc, final String signature, final String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, mdesc, signature, exceptions);
return new MethodVisitor(Opcodes.ASM9, mv) {
@Override
public AnnotationVisitor visitAnnotation(String adesc, boolean visible) {
AnnotationVisitor av = super.visitAnnotation(adesc, visible);
if (adesc.equals(WITH_SYNTHETIC_METHODS) && (access & ACC_SYNTHETIC) == 0) {
return new WithBridgeMethodsAnnotationVisitor(av) {
@Override
public void visitEnd() {
super.visitEnd();
for (Type type : this.types) {
syntheticMethods.add(new SyntheticMethod(
access,name,mdesc,signature,exceptions,type, this.castRequired, this.adapterMethod
));
}
}
};
}
return av;
}
};
}
/**
* Inject methods at the end.
*/
@Override
public void visitEnd() {
for (SyntheticMethod m : syntheticMethods) {
m.inject(cv);
}
super.visitEnd();
}
}
@SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "user-provided value for running the program")
public static void main(String[] args) throws IOException {
MethodInjector mi = new MethodInjector();
for (String a : args) {
mi.handleRecursively(new File(a));
}
}
private static final String SYNTHETIC_METHODS_ADDED = Type.getDescriptor(BridgeMethodsAdded.class);
private static final String WITH_SYNTHETIC_METHODS = Type.getDescriptor(WithBridgeMethods.class);
}
ProcessMojo.java 0000664 0000000 0000000 00000005701 14335561456 0043125 0 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/main/java/com/infradna/tool/bridge_method_injector /*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.infradna.tool.bridge_method_injector;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import org.jvnet.hudson.annotation_indexer.Index;
/**
* @author Kohsuke Kawaguchi
*/
@Mojo(name = "process", requiresDependencyResolution = ResolutionScope.RUNTIME, defaultPhase = LifecyclePhase.PROCESS_CLASSES, threadSafe = true)
public class ProcessMojo extends AbstractMojo {
/**
* The directory containing generated classes.
*/
@Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
private File classesDirectory;
@Override
@SuppressFBWarnings(
value = {"DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED", "PATH_TRAVERSAL_IN"},
justification = "irrelevant without SecurityManager; user-provided value for running the program")
public void execute() throws MojoExecutionException {
try {
for (String line : Index.listClassNames(WithBridgeMethods.class, new URLClassLoader(new URL[] {classesDirectory.toURI().toURL()}, ClassLoader.getSystemClassLoader().getParent()))) {
File classFile = new File(classesDirectory,line.replace('.','/')+".class");
getLog().debug("Processing "+line);
new MethodInjector().handle(classFile);
}
} catch (IOException e) {
throw new MojoExecutionException("Failed to process @WithBridgeMethods",e);
}
}
}
bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/ 0000775 0000000 0000000 00000000000 14335561456 0027177 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/client/ 0000775 0000000 0000000 00000000000 14335561456 0030455 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/client/Main.java 0000664 0000000 0000000 00000003603 14335561456 0032206 0 ustar 00root root 0000000 0000000 public class Main {
public static void main(String[] args) throws Exception {
// invocation of void method. verify that it runs without error
Foo.toggle = false;
Foo.hello();
assertEquals(true, Foo.toggle);
Foo.toggle = false;
Foo.hello2();
assertEquals(true, Foo.toggle);
Foo.toggle = false;
new Foo().hello3();
assertEquals(true, Foo.toggle);
Foo.toggle = false;
new Foo().hello4();
assertEquals(true, Foo.toggle);
Foo.unbox();
Foo.box();
Object o = new Foo().getMessage();
assertEquals(args[0],o);
String n = new Foo().getString();
assertEquals(args[0],n);
Object s = Foo.getStaticMessage();
assertEquals(args[0],s);
Object w = Foo.methodToWiden(String.class);
assertEquals(args[0],w);
// using reflection to ensure that JIT isn't doing inlining
check((Foo)Foo.class.newInstance(),args[0]);
// still a work in progress
// check((Bar)Bar.class.newInstance(),args[0]);
Adapter a = new Adapter();
assertEquals(1,a.i());
assertEquals("http://kohsuke.org/",a.o());
new Adapter.SomeClass().someMethod();
assertEquals(1,a.l());
}
private static void assertEquals(Object expected, Object actual) {
System.out.println("We got "+actual+", expecting "+expected);
if (!actual.equals(expected))
System.exit(1);
}
private static void check(IFoo f, String expected) {
Object o = f.getMessage();
assertEquals(expected,o);
String n = f.getString();
assertEquals(expected,n);
}
private static void check(IBar f, String expected) {
Object o = f.narrow();
assertEquals(expected,o);
String n = f.widen();
assertEquals(expected,n);
}
}
bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/java/ 0000775 0000000 0000000 00000000000 14335561456 0030120 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/java/com/ 0000775 0000000 0000000 00000000000 14335561456 0030676 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/java/com/infradna/ 0000775 0000000 0000000 00000000000 14335561456 0032460 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/java/com/infradna/tool/ 0000775 0000000 0000000 00000000000 14335561456 0033435 5 ustar 00root root 0000000 0000000 bridge_method_injector/ 0000775 0000000 0000000 00000000000 14335561456 0040047 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/java/com/infradna/tool Junit4TestsRanTest.java 0000664 0000000 0000000 00000000470 14335561456 0044414 0 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/java/com/infradna/tool/bridge_method_injector package com.infradna.tool.bridge_method_injector;
import org.junit.Test;
public class Junit4TestsRanTest {
@Test
public void anything() {
/*
* Intentionally blank. We just want a test that runs with JUnit so that buildPlugin() works
* in the Jenkinsfile.
*/
}
}
bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/synthetics/ 0000775 0000000 0000000 00000000000 14335561456 0031374 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/synthetics/A.java 0000664 0000000 0000000 00000000117 14335561456 0032416 0 ustar 00root root 0000000 0000000 public class A {
public Object getProperty() {
return null;
}
} bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/synthetics/B.java 0000664 0000000 0000000 00000000341 14335561456 0032416 0 ustar 00root root 0000000 0000000 import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
public class B extends A {
@WithBridgeMethods(value=String.class,castRequired=true)
public CharSequence getProperty() {
return null;
}
} bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/v1/ 0000775 0000000 0000000 00000000000 14335561456 0027525 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/v1/Adapter.java 0000664 0000000 0000000 00000000760 14335561456 0031753 0 ustar 00root root 0000000 0000000 public class Adapter {
public int i() { return 1; }
public String o() { return "http://kohsuke.org/"; }
// Just making sure we do not barf on Java 8 constructs:
interface SomeInterface {
default void someMethod() {}
}
static class SomeClass implements SomeInterface {
@Override
public void someMethod() {
SomeInterface.super.someMethod();
}
}
// we will evolve this from int -> long
public int l() { return 1; }
}
bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/v1/Bar.java 0000664 0000000 0000000 00000000176 14335561456 0031100 0 ustar 00root root 0000000 0000000 public class Bar implements IBar {
public String widen() { return "foo"; }
public Object narrow() { return "foo"; }
} bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/v1/Foo.java 0000664 0000000 0000000 00000001670 14335561456 0031117 0 ustar 00root root 0000000 0000000 public class Foo implements IFoo {
/**
* Testing narrowing. In v2, we'll narrow this to return the String type. This is type safe.
*/
public Object getMessage() {
return "foo";
}
/**
* Testing widening. In v2, we'll widen this to Object. Potentially type unsafe.
*/
public String getString() {
return "foo";
}
public static Object getStaticMessage() {
return "foo";
}
public static T methodToWiden(Class clazz) {
return clazz.cast("foo");
}
static boolean toggle;
public static void hello() {
toggle = true;
}
public static void hello2() {
toggle = true;
}
public void hello3() {
toggle = true;
}
public void hello4() {
toggle = true;
}
public static int unbox() {return Integer.MIN_VALUE;}
public static int box() {return Integer.MAX_VALUE;}
} bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/v1/IBar.java 0000664 0000000 0000000 00000000102 14335561456 0031176 0 ustar 00root root 0000000 0000000 public interface IBar {
String widen();
Object narrow();
} bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/v1/IFoo.java 0000664 0000000 0000000 00000000130 14335561456 0031216 0 ustar 00root root 0000000 0000000 public interface IFoo {
public Object getMessage();
public String getString();
} bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/v2/ 0000775 0000000 0000000 00000000000 14335561456 0027526 5 ustar 00root root 0000000 0000000 bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/v2/Adapter.java 0000664 0000000 0000000 00000001665 14335561456 0031761 0 ustar 00root root 0000000 0000000 import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import java.io.IOException;
import java.net.URL;
public class Adapter {
@WithBridgeMethods(value=int.class, adapterMethod="_i")
public String i() { return "1"; }
private Object _i(String o, Class type) {
return Integer.parseInt(o);
}
@WithBridgeMethods(value=String.class, adapterMethod="_o")
URL o() throws IOException { return new URL("http://kohsuke.org/"); }
private Object _o(URL o, Class type) {
return o.toString();
}
interface SomeInterface {
default void someMethod() {}
}
static class SomeClass implements SomeInterface {
@Override
public void someMethod() {
SomeInterface.super.someMethod();
}
}
@WithBridgeMethods(value=int.class, adapterMethod="l2i")
public long l() { return 1L; }
private Object l2i(long v, Class type) { return (int)v; }
} bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/v2/Bar.java 0000664 0000000 0000000 00000000176 14335561456 0031101 0 ustar 00root root 0000000 0000000 public class Bar implements IBar {
public Object widen() { return "bar"; }
public String narrow() { return "bar"; }
} bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/v2/Foo.java 0000664 0000000 0000000 00000002603 14335561456 0031115 0 ustar 00root root 0000000 0000000 import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
public class Foo implements IFoo {
@WithBridgeMethods(Object.class)
public String getMessage() {
return "bar";
}
/**
* Widening String to Object.
*/
@WithBridgeMethods(value=String.class,castRequired=true)
public Object getString() {
return "bar";
}
@WithBridgeMethods(Object.class)
public static String getStaticMessage() {
return "bar";
}
@WithBridgeMethods(value=String.class, castRequired=true)
public static T methodToWiden(Class clazz) {
return clazz.cast("bar");
}
static boolean toggle;
@WithBridgeMethods(void.class)
public static boolean hello() {
toggle = true;
return true;
}
@WithBridgeMethods(void.class)
public static String hello2() {
toggle = true;
return "hello2";
}
@WithBridgeMethods(void.class)
public boolean hello3() {
toggle = true;
return true;
}
@WithBridgeMethods(void.class)
public String hello4() {
toggle = true;
return "hello4";
}
@WithBridgeMethods(value=int.class, castRequired=true)
public static Integer unbox() {
return Integer.MIN_VALUE;
}
@WithBridgeMethods(value=Integer.class)
public static int box() {
return Integer.MAX_VALUE;
}
} bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/v2/IBar.java 0000664 0000000 0000000 00000000350 14335561456 0031204 0 ustar 00root root 0000000 0000000 import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
public interface IBar {
@WithBridgeMethods(value=String.class,castRequired=true)
Object widen();
@WithBridgeMethods(Object.class)
String narrow();
} bridge-method-injector-bridge-method-injector-parent-1.25/injector/src/test/v2/IFoo.java 0000664 0000000 0000000 00000000377 14335561456 0031234 0 ustar 00root root 0000000 0000000 import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
public interface IFoo {
@WithBridgeMethods(Object.class)
public String getMessage();
@WithBridgeMethods(value=String.class,castRequired=true)
public Object getString();
} bridge-method-injector-bridge-method-injector-parent-1.25/pom.xml 0000664 0000000 0000000 00000004110 14335561456 0025125 0 ustar 00root root 0000000 0000000
4.0.0
org.jenkins-ci
jenkins
1.91
com.infradna.tool
bridge-method-injector-parent
1.25
pom
Bridge Method Injection Parent POM
Evolve your classes without breaking compatibility
https://github.com/jenkinsci/bridge-method-injector
annotation
injector
1.25
-SNAPSHOT
jenkinsci/bridge-method-injector
repo.jenkins-ci.org
https://repo.jenkins-ci.org/public/
repo.jenkins-ci.org
https://repo.jenkins-ci.org/public/
scm:git:https://github.com/${gitHubRepo}.git
scm:git:git@github.com:${gitHubRepo}.git
https://github.com/${gitHubRepo}
bridge-method-injector-parent-1.25
kohsuke
Kohsuke Kawaguchi
MIT License
repository
https://opensource.org/licenses/MIT