pax_global_header 0000666 0000000 0000000 00000000064 13010176641 0014511 g ustar 00root root 0000000 0000000 52 comment=661f0f7cc54f9c30b4fb477da152825a8c2be080
lombok.patcher-0.22/ 0000775 0000000 0000000 00000000000 13010176641 0014344 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/.gitignore 0000664 0000000 0000000 00000000124 13010176641 0016331 0 ustar 00root root 0000000 0000000 .classpath
.project
.settings
bin
build
dist
escudo-upload.key
lib
contrib
ivyCache
lombok.patcher-0.22/README.markdown 0000664 0000000 0000000 00000001255 13010176641 0017050 0 ustar 00root root 0000000 0000000 #lombok.patcher
Lombok Patcher gives you the ability to live-rewrite classes as a JVM runs, either by loading as an agent during JVM bootup or by injecting the agent 'live' during execution.
To make this easier than fiddling with classes directly, Lombok Patcher offers a few 'patch scripts' to do common tasks, such as wrap your own code around any method call,
replace methods entirely with your own, or add fields.
lombok.patcher also includes support for getting around the Eclipse OSGi container's classloader separation.
An example can be found in [Project Lombok's eclipse agent code](http://github.com/rzwitserloot/lombok/tree/master/src/eclipseAgent/lombok/eclipse/agent/) lombok.patcher-0.22/build.xml 0000664 0000000 0000000 00000020561 13010176641 0016171 0 ustar 00root root 0000000 0000000
A new version of ivyplusplus was required and has been downloaded. Rerun the script to continue.
Lombok patcher version: ${lombok.patcher.version}
All tests successful.
automated uploading and deployment temporarily disabled. Upload dist/lombok.patcher-${lombok.patcher.version}.jar to the server and deploy manually.
lombok.patcher-0.22/buildScripts/ 0000775 0000000 0000000 00000000000 13010176641 0017013 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/buildScripts/ivy-repo/ 0000775 0000000 0000000 00000000000 13010176641 0020565 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/buildScripts/ivy-repo/projectlombok.org-jsch-ant-fixed-0.1.45.xml 0000664 0000000 0000000 00000000734 13010176641 0030300 0 ustar 00root root 0000000 0000000
lombok.patcher-0.22/buildScripts/ivy-repo/projectlombok.org-junit-4.8.1.xml 0000664 0000000 0000000 00000001073 13010176641 0026545 0 ustar 00root root 0000000 0000000
lombok.patcher-0.22/buildScripts/ivy.xml 0000664 0000000 0000000 00000002127 13010176641 0020346 0 ustar 00root root 0000000 0000000
lombok.patcher-0.22/buildScripts/ivysettings.xml 0000664 0000000 0000000 00000000662 13010176641 0022131 0 ustar 00root root 0000000 0000000
lombok.patcher-0.22/propagateDistToLombok 0000775 0000000 0000000 00000001465 13010176641 0020555 0 ustar 00root root 0000000 0000000 #!/bin/bash
if [ "$1" == "" ]; then
echo 'supply the version as first argument, such as 0.22'
exit 1
fi
VERSION="$1"
if [ ! -d ../lombok ]; then
echo 'Expected ../lombok to refer to a directory containing the lombok project.'
exit 1
fi
if [ ! -d ../lombok/ivyCache/org.projectlombok/lombok.patcher/jars ]; then
echo 'Expected ivy cache to already exist at: ../lombok/ivyCache/org.projectlombok/lombok.patcher/jars'
exit 1
fi
ant dist || exit 0
if [ -d ../lombok/lib/runtime ]; then
cp dist/lombok.patcher.jar ../lombok/lib/runtime/org.projectlombok-lombok.patcher.jar
fi
if [ -d ../lombok/lib/build ]; then
cp dist/lombok.patcher.jar ../lombok/lib/build/org.projectlombok-lombok.patcher.jar
fi
cp dist/lombok.patcher.jar ../lombok/ivyCache/org.projectlombok/lombok.patcher/jars/lombok.patcher-$VERSION.jar
lombok.patcher-0.22/runAsm 0000775 0000000 0000000 00000001147 13010176641 0015542 0 ustar 00root root 0000000 0000000 #!/bin/bash
if [ "$1" == "check" ]; then
APP="CheckClassAdapter"
elif [ "$1" == "asmify" ]; then
APP="ASMifierClassVisitor"
elif [ "$1" == "trace" ]; then
APP="TraceClassVisitor"
else
echo "Runs ASM's various utilities."
echo "usage: runAsm AppName ClassName"
echo "legal appnames:"
echo "check - runs CheckClassAdapter - like the java class verifier, but more verbose."
echo "asmify - runs ASMifier - prints the ASM code needed to recreate a given class file."
echo "trace - Runs Trace - decompiles a given class."
exit 0
fi
shift
java -cp "lib/*" org.objectweb.asm.util.$APP $@
lombok.patcher-0.22/src/ 0000775 0000000 0000000 00000000000 13010176641 0015133 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/src/injector/ 0000775 0000000 0000000 00000000000 13010176641 0016750 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/src/injector/lombok/ 0000775 0000000 0000000 00000000000 13010176641 0020233 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/src/injector/lombok/patcher/ 0000775 0000000 0000000 00000000000 13010176641 0021661 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/src/injector/lombok/patcher/inject/ 0000775 0000000 0000000 00000000000 13010176641 0023135 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/src/injector/lombok/patcher/inject/LiveInjector.java 0000664 0000000 0000000 00000014101 13010176641 0026372 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2014 The Project Lombok Authors.
*
* 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 lombok.patcher.inject;
import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.reflect.InvocationTargetException;
import lombok.patcher.ClassRootFinder;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
/**
* This class allows you to inject a java agent 'on the fly' into your VM. This feature only works on sun-derived VMs, such as the openJDK, sun JVMs,
* and apple's VMs.
*
* While an agent can be injected in both 1.5 and 1.6 VMs, class reloading is only supported on 1.6 VMs, so if you want to be 1.5 compatible,
* and want to transform classes, you'll need to ensure you inject the agent before the class you're interested in gets loaded. Otherwise, you can
* just reload these classes from within the injected agent.
*
* As a convenience, you can choose to inject this class (which is also an agent), but as a java agent has to come in a jar file, this only
* works if this class has been loaded from a jar file on the local file system. If it has indeed been loaded that way, then this class can take care
* of injecting itself. A common scenario is to distribute your patching application together with {@code lombok.patcher} in a single jar file.
* That way, you can use the live injector, and your injected agent can then run patchscripts that call into your own code, and all your code's dependencies
* will be available because they, too, are in this unified jar file.
*/
public class LiveInjector {
/**
* This interface is used internally by the {@code LiveInjector} to interface with the VM core.
*/
public static interface LibInstrument extends Library {
void Agent_OnAttach(Pointer vm, String name, Pointer Reserved);
}
/**
* This interface is used internally by the {@code LiveInjector} to interface with the VM core.
*/
public interface LibJVM extends Library {
int JNI_GetCreatedJavaVMs(PointerByReference vms, int count, IntByReference found);
}
/**
* Injects the jar file that contains the code for {@code LiveInjector} as an agent. Its your job to make sure this jar is in fact an agent jar.
*
* @throws IllegalStateException If this is not a sun-derived v1.6 VM.
*/
public void injectSelf() throws IllegalStateException {
inject(ClassRootFinder.findClassRootOfSelf());
}
/**
* Injects a jar file into the current VM as a live-loaded agent. The provided jar will be loaded into its own separate class loading context,
* and its manifest is checked for an {@code Agent-Class} to load. That class should have a static method named {@code agentmain} which will
* be called, with an {@link java.lang.instrument.Instrumentation} object that you're probably after.
*
* @throws IllegalStateException If this is not a sun-derived v1.6 VM.
*/
public void inject(String jarFile) throws IllegalStateException {
File f = new File(jarFile);
if (!f.isFile()) throw new IllegalArgumentException("Live Injection is not possible unless the classpath root to inject is a jar file.");
if (System.getProperty("lombok.patcher.safeInject", null) != null) {
slowInject(jarFile);
} else {
fastInject(jarFile);
}
}
private void fastInject(String jarFile) throws IllegalStateException {
try {
Class.forName("sun.instrument.InstrumentationImpl");
} catch (ClassNotFoundException e) {
throw new IllegalStateException("agent injection only works on a sun-derived 1.6 or higher VM");
}
LibJVM libjvm = (LibJVM) Native.loadLibrary(LibJVM.class);
PointerByReference vms = new PointerByReference();
IntByReference found = new IntByReference();
libjvm.JNI_GetCreatedJavaVMs(vms, 1, found);
LibInstrument libinstrument = (LibInstrument)Native.loadLibrary(LibInstrument.class);
Pointer vm = vms.getValue();
libinstrument.Agent_OnAttach(vm, jarFile, null);
}
private void slowInject(String jarFile) throws IllegalStateException {
String ownPidS = ManagementFactory.getRuntimeMXBean().getName();
ownPidS = ownPidS.substring(0, ownPidS.indexOf('@'));
int ownPid = Integer.parseInt(ownPidS);
boolean unsupportedEnvironment = false;
Throwable exception = null;
try {
Class> vmClass = Class.forName("com.sun.tools.attach.VirtualMachine");
Object vm = vmClass.getMethod("attach", String.class).invoke(null, String.valueOf(ownPid));
vmClass.getMethod("loadAgent", String.class).invoke(vm, jarFile);
} catch (ClassNotFoundException e) {
unsupportedEnvironment = true;
} catch (NoSuchMethodException e) {
unsupportedEnvironment = true;
} catch (InvocationTargetException e) {
exception = e.getCause();
if (exception == null) exception = e;
} catch (Throwable t) {
exception = t;
}
if (unsupportedEnvironment) throw new IllegalStateException("agent injection only works on a sun-derived 1.6 or higher VM");
if (exception != null) throw new IllegalStateException("agent injection not supported on this platform due to unknown reason", exception);
}
}
lombok.patcher-0.22/src/patcher/ 0000775 0000000 0000000 00000000000 13010176641 0016561 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/src/patcher/lombok/ 0000775 0000000 0000000 00000000000 13010176641 0020044 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/src/patcher/lombok/patcher/ 0000775 0000000 0000000 00000000000 13010176641 0021472 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/src/patcher/lombok/patcher/ClassRootFinder.java 0000664 0000000 0000000 00000006330 13010176641 0025400 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2014 The Project Lombok Authors.
*
* 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 lombok.patcher;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.Charset;
public class ClassRootFinder {
private static String urlDecode(String in, boolean forceUtf8) {
try {
return URLDecoder.decode(in, forceUtf8 ? "UTF-8" : Charset.defaultCharset().name());
} catch (UnsupportedEncodingException e) {
try {
return URLDecoder.decode(in, "UTF-8");
} catch (UnsupportedEncodingException e1) {
return in;
}
}
}
public static String findClassRootOfSelf() {
return findClassRootOfClass(ClassRootFinder.class);
}
public static String findClassRootOfClass(Class> context) {
String name = context.getName();
int idx = name.lastIndexOf('.');
String packageBase;
if (idx > -1) {
packageBase = name.substring(0, idx);
name = name.substring(idx + 1);
} else {
packageBase = "";
}
URL selfURL = context.getResource(name + ".class");
String self = selfURL.toString();
if (self.startsWith("file:/")) {
String path = urlDecode(self.substring(5), false);
if (!new File(path).exists()) path = urlDecode(self.substring(5), true);
String suffix = "/" + packageBase.replace('.', '/') + "/" + name + ".class";
if (!path.endsWith(suffix)) throw new IllegalArgumentException("Unknown path structure: " + path);
self = path.substring(0, path.length() - suffix.length());
} else if (self.startsWith("jar:")) {
int sep = self.indexOf('!');
if (sep == -1) throw new IllegalArgumentException("No separator in jar protocol: " + self);
String jarLoc = self.substring(4, sep);
if (jarLoc.startsWith("file:/")) {
String path = urlDecode(jarLoc.substring(5), false);
if (!new File(path).exists()) path = urlDecode(jarLoc.substring(5), true);
self = path;
} else throw new IllegalArgumentException("Unknown path structure: " + self);
} else {
throw new IllegalArgumentException("Unknown protocol: " + self);
}
if (self.isEmpty()) self = "/";
return self;
}
public static void main(String[] args) {
System.out.println(findClassRootOfSelf());
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/Filter.java 0000664 0000000 0000000 00000003101 13010176641 0023555 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2016 The Project Lombok Authors.
*
* 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 lombok.patcher;
import java.security.ProtectionDomain;
public interface Filter {
Filter ALWAYS = new Filter() {
public boolean shouldTransform(ClassLoader loader, String className, Class> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
return true;
}
};
boolean shouldTransform(ClassLoader loader, String className, Class> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer);
}
lombok.patcher-0.22/src/patcher/lombok/patcher/Hook.java 0000664 0000000 0000000 00000010124 13010176641 0023233 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2010 The Project Lombok Authors.
*
* 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 lombok.patcher;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
/**
* Represents a method you write yourself; calls to it will be inserted into
* code-to-be-patched by a {@code PatchScript}.
*
* For inner classes, use a dollar and not a dot to separate
* The {@code classSpec} property should be written in JVM style, such as
* {@code java/lang/String}.
*
* @see JVM Spec on method names and descriptors
*/
@ToString
@EqualsAndHashCode
public class Hook {
@Getter @NonNull
private final String className;
@Getter @NonNull
private final String methodName;
@Getter @NonNull
private final String returnType;
@Getter @NonNull
private final List parameterTypes;
public boolean isConstructor() {
return "".equals(methodName);
}
public Hook(String className, String methodName, String returnType, String... parameterTypes) {
if (className == null) throw new NullPointerException("classSpec");
if (methodName == null) throw new NullPointerException("methodName");
if (returnType == null) throw new NullPointerException("returnType");
if (parameterTypes == null) throw new NullPointerException("parameterTypes");
this.className = className;
this.methodName = methodName;
this.returnType = returnType;
List params = new ArrayList();
for (String param : parameterTypes) params.add(param);
this.parameterTypes = Collections.unmodifiableList(params);
}
public String getClassSpec() {
return convertType(className);
}
public String getMethodDescriptor() {
StringBuilder out = new StringBuilder();
out.append("(");
for (String p : parameterTypes) out.append(toSpec(p));
out.append(")");
out.append(toSpec(returnType));
return out.toString();
}
private static final Map PRIMITIVES; static {
Map m = new HashMap();
m.put("int", "I");
m.put("long", "J");
m.put("short", "S");
m.put("byte", "B");
m.put("char", "C");
m.put("double", "D");
m.put("float", "F");
m.put("void", "V");
m.put("boolean", "Z");
PRIMITIVES = Collections.unmodifiableMap(m);
}
public static String toSpec(String type) {
StringBuilder out = new StringBuilder();
while (type.endsWith("[]")) {
type = type.substring(0, type.length() - 2);
out.append("[");
}
String p = PRIMITIVES.get(type);
if (p != null) {
out.append(p);
return out.toString();
}
out.append("L");
out.append(convertType(type));
out.append(';');
return out.toString();
}
public static String convertType(String type) {
StringBuilder out = new StringBuilder();
for (String part : type.split("\\.")) {
if (out.length() > 0) out.append('/');
out.append(part);
}
return out.toString();
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/MethodLogistics.java 0000664 0000000 0000000 00000015515 13010176641 0025445 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009 The Project Lombok Authors.
*
* 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 lombok.patcher;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import lombok.Getter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* Method rewriting support class.
*
* MethodLogistics tracks the return type as well as all parameter types, and can tell you exactly where a given parameter
* exists in the local method space. This tracking is non-trivial, as doubles and longs take up 2 spaces, both on the stack
* and in the local method space.
*/
public class MethodLogistics {
private final int staticOffset;
@Getter
private final int returnOpcode;
private final int returnSize;
private final List loadOpcodes;
private final List paramSizes;
private final List paramIndices;
/**
* Creates a new logistics support class given a method descriptor and the access flags.
*
* These can be copied verbatim from what ASM gives you.
*
* @see org.objectweb.asm.ClassVisitor#visitMethod(int, String, String, String, String[])
*/
public MethodLogistics(int accessFlags, String descriptor) {
this.staticOffset = ((accessFlags & Opcodes.ACC_STATIC) != 0) ? 0 : 1;
List specs = MethodTarget.decomposeFullDesc(descriptor);
Iterator it = specs.iterator();
String returnSpec = it.next();
returnSize = sizeOf(returnSpec);
returnOpcode = returnOpcodeFor(returnSpec);
int index = staticOffset;
List paramSizes = new ArrayList();
List paramIndices = new ArrayList();
List loadOpcodes = new ArrayList();
while (it.hasNext()) {
String spec = it.next();
int size = sizeOf(spec);
paramSizes.add(size);
paramIndices.add(index);
loadOpcodes.add(loadOpcodeFor(spec));
index += size;
}
this.paramSizes = Collections.unmodifiableList(paramSizes);
this.paramIndices = Collections.unmodifiableList(paramIndices);
this.loadOpcodes = Collections.unmodifiableList(loadOpcodes);
}
/**
* @return {@code true} if the method is static.
*/
public boolean isStatic() {
return staticOffset == 0;
}
public int getParamCount() {
return this.paramSizes.size();
}
/**
* Writes the opcode to load the i-th parameter to the supplied {@code MethodVisitor}.
*
* @param index 0-based index into the method parameter (the index-th parameter will be loaded).
* @param mv The opcode will be generated in this MethodVisitor object.
*/
public void generateLoadOpcodeForParam(int index, MethodVisitor mv) {
mv.visitVarInsn(loadOpcodes.get(index), paramIndices.get(index));
}
/**
* Writes the opcode to load 'this', or, for static methods, 'null'.
*
* @param mv The opcode will be generated in this MethodVisitor object.
*/
public void generateLoadOpcodeForThis(MethodVisitor mv) {
if (isStatic()) mv.visitInsn(Opcodes.ACONST_NULL);
else mv.visitVarInsn(Opcodes.ALOAD, 0);
}
/**
* Writes the opcode to return from this method.
* Writes the appropriate opcode for this method's return type, so RETURN, ARETURN, IRETURN, LRETURN, FRETURN, or DRETURN.
*
* @param mv The opcode will be generated in this MethodVisitor object.
*/
public void generateReturnOpcode(MethodVisitor mv) {
mv.visitInsn(returnOpcode);
}
/**
* Generates either POP or POP2 depending on the size of the return type - if a value of the return type is on the stack,
* it'll be removed exactly by the generated POP.
*
* @param mv The opcode will be generated in this MethodVisitor object.
*/
public void generatePopForReturn(MethodVisitor mv) {
mv.visitInsn(returnSize == 2 ? Opcodes.POP2 : Opcodes.POP);
}
/**
* Generates either DUP or DUP2 depending on the size of the return type - if a value of the return type is on the stack,
* it'll be duplicated exactly by the generated POP.
*
* @param mv The opcode will be generated in this MethodVisitor object.
*/
public void generateDupForReturn(MethodVisitor mv) {
mv.visitInsn(returnSize == 2 ? Opcodes.DUP2 : Opcodes.DUP);
}
/**
* Generates an instruction to duplicate an object on the stack. The object is of the stated type, in JVM typespec, so
* {@code I} refers to an integer, and {@code Ljava/lang/Object;} would refer to an object.
*
* @param type A type spec in JVM format.
* @param mv This visitor will be given the call to visit DUP2, DUP, or nothing dependent on the type name.
*/
public static void generateDupForType(String type, MethodVisitor mv) {
switch (sizeOf(type)) {
default:
case 1:
mv.visitInsn(Opcodes.DUP);
break;
case 2:
mv.visitInsn(Opcodes.DUP2);
break;
case 0:
//Do nothing
break;
}
}
private static int loadOpcodeFor(String spec) {
switch (spec.charAt(0)) {
case 'D':
return Opcodes.DLOAD;
case 'J':
return Opcodes.LLOAD;
case 'F':
return Opcodes.FLOAD;
case 'I':
case 'S':
case 'B':
case 'Z':
return Opcodes.ILOAD;
case 'V':
throw new IllegalArgumentException("There's no load opcode for 'void'");
case 'L':
case '[':
return Opcodes.ALOAD;
}
throw new IllegalStateException("Uhoh - bug - unrecognized JVM type: " + spec);
}
private static int returnOpcodeFor(String returnSpec) {
switch (returnSpec.charAt(0)) {
case 'D':
return Opcodes.DRETURN;
case 'J':
return Opcodes.LRETURN;
case 'F':
return Opcodes.FRETURN;
case 'I':
case 'S':
case 'B':
case 'Z':
return Opcodes.IRETURN;
case 'V':
return Opcodes.RETURN;
case 'L':
case '[':
return Opcodes.ARETURN;
}
throw new IllegalStateException("Uhoh - bug - unrecognized JVM type: " + returnSpec);
}
private static int sizeOf(String spec) {
switch (spec.charAt(0)) {
case 'D':
case 'J':
return 2;
case 'V':
return 0;
default:
return 1;
}
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/MethodTarget.java 0000664 0000000 0000000 00000020335 13010176641 0024727 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009 The Project Lombok Authors.
*
* 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 lombok.patcher;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
/**
* Represents a target method that you want to transform.
*
* A target method is represented by at least a class name and a method name, which may match multiple methods if you've
* overloaded the method name. You can choose to focus on just one of a set of overloaded methods by also specifying return
* type and parameter types.
*/
@ToString
@EqualsAndHashCode
public final class MethodTarget implements TargetMatcher {
@Getter
private final String classSpec;
@Getter
private final String methodName;
@Getter
private final String returnSpec;
@Getter
private final List parameterSpec;
@Getter
private boolean hasDescription;
/**
* Target any method with the provided name that appears in the provided class, regardless of return type and parameter types.
*
* @param classSpec the class name in binary form (separate package names by dots, separate inner classes with dollars).
* @param methodName the method name (e.g.: {@code toLowerCase}).
*/
public MethodTarget(String classSpec, String methodName) {
this(classSpec, methodName, false, null, null);
}
/**
* Target any method with the provided name, the provided return type, and the provided parameter list, appearing in the provided class.
*
* @param classSpec the class name in binary form (separate package names by dots, separate inner classes with dollars).
* @param methodName The method name (e.g.: {@code toLowerCase}).
* @param returnSpec The return type, in the same style as {@code classSpec}. For primitives,
* use the primitive type name, such as 'int', and suffix 1 pair of array brackets per array dimension.
* @param parameterSpecs A list of parameter types, in the same style as {@code returnSpec}.
*/
public MethodTarget(String classSpec, String methodName, String returnSpec, String... parameterSpecs) {
this(classSpec, methodName, true, returnSpec, parameterSpecs);
}
public Boolean returnTypeIsVoid() {
if (hasDescription) return returnSpec.equals("void");
return null;
}
private MethodTarget(String classSpec, String methodName, boolean hasDescription, String returnSpec, String[] parameterSpecs) {
if (classSpec == null) throw new NullPointerException("classSpec");
if (methodName == null) throw new NullPointerException("methodName");
if (hasDescription && returnSpec == null) throw new NullPointerException("returnSpec");
if (hasDescription && parameterSpecs == null) throw new NullPointerException("parameterSpecs");
if (methodName.contains("[") || methodName.contains(".")) throw new IllegalArgumentException(
"Your method name contained dots or braces. Perhaps you switched return type and method name around?");
this.hasDescription = hasDescription;
this.classSpec = classSpec;
this.methodName = methodName;
this.returnSpec = returnSpec;
this.parameterSpec = parameterSpecs == null ? null : Collections.unmodifiableList(Arrays.asList(parameterSpecs));
}
private static final String JVM_TYPE_SPEC = "\\[*(?:[BCDFIJSZ]|L[^;]+;)";
private static final Pattern PARAM_SPEC = Pattern.compile(JVM_TYPE_SPEC);
private static final Pattern COMPLETE_SPEC = Pattern.compile("^\\(((?:" + JVM_TYPE_SPEC + ")*)\\)(V|" + JVM_TYPE_SPEC + ")$");
private static final Pattern BRACE_PAIRS = Pattern.compile("^(?:\\[\\])*$");
/**
* Decomposes the types; the list starts with the return type, and is followed by the parameter types.
*/
public static List decomposeFullDesc(String desc) {
Matcher descMatcher = COMPLETE_SPEC.matcher(desc);
if (!descMatcher.matches()) throw new IllegalArgumentException("This isn't a valid spec: " + desc);
List out = new ArrayList();
out.add(descMatcher.group(2));
Matcher paramMatcher = PARAM_SPEC.matcher(descMatcher.group(1));
while (paramMatcher.find()) {
out.add(paramMatcher.group(0));
}
return out;
}
/**
* @param classSpec a JVM-style class name to check against this MethodTarget's target class (e.g. {@code java/lang/String}).
* @return {@code true} if the class spec seems to match this MethodTarget's class. So, if this target is set to class
* {@code java.lang.String} and you supply as classSpec: {@code java/lang/String}, this method returns {@code true}.
*/
public boolean classMatches(String classSpec) {
return typeMatches(classSpec, this.classSpec);
}
public Collection getAffectedClasses() {
return Collections.singleton(classSpec);
}
/**
* Returns true if the provided classSpec/methodName/methodDescriptor (as per the JVM Specification, and the way ASM
* provides them) fits this MethodTarget.
*
* @param classSpec a Class Specification, JVM-style (e.g. {@code java/lang/String}).
* @param methodName The name of the method.
* @param descriptor A Method descriptor, ASM-style (e.g. {@code (II)V}.
*/
public boolean matches(String classSpec, String methodName, String descriptor) {
if (!methodName.equals(this.methodName)) return false;
if (!classMatches(classSpec)) return false;
return descriptorMatch(descriptor);
}
private boolean descriptorMatch(String descriptor) {
if (returnSpec == null) return true;
Iterator targetSpecs = decomposeFullDesc(descriptor).iterator();
if (!typeSpecMatch(targetSpecs.next(), this.returnSpec)) return false;
Iterator patternSpecs = parameterSpec.iterator();
while (targetSpecs.hasNext() && patternSpecs.hasNext()) {
if (!typeSpecMatch(targetSpecs.next(), patternSpecs.next())) return false;
}
return !targetSpecs.hasNext() && !patternSpecs.hasNext();
}
public static boolean typeSpecMatch(String type, String pattern) {
if (type.equals("V")) return pattern.equals("void");
int dimsInType;
/* Count array dimension of 'type' */ {
for (dimsInType = 0; dimsInType < type.length(); dimsInType++) {
if (type.charAt(dimsInType) != '[') break;
}
type = type.substring(dimsInType);
}
/* Count down as many brace pairs in 'pattern' as we found in 'type'. */ {
dimsInType *= 2;
int start = pattern.length() - dimsInType;
if (start<0) return false;
String braces = pattern.substring(start);
if (!BRACE_PAIRS.matcher(braces).matches()) return false;
pattern = pattern.substring(0, start);
}
switch (type.charAt(0)) {
case 'B':
return pattern.equals("byte");
case 'C':
return pattern.equals("char");
case 'D':
return pattern.equals("double");
case 'F':
return pattern.equals("float");
case 'I':
return pattern.equals("int");
case 'J':
return pattern.equals("long");
case 'S':
return pattern.equals("short");
case 'Z':
return pattern.equals("boolean");
case 'L':
return typeMatches(type.substring(1, type.length() -1), pattern);
default:
return false;
}
}
public static boolean typeMatches(String type, String pattern) {
return type.replace("/", ".").equals(pattern);
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/PatchScript.java 0000664 0000000 0000000 00000030425 13010176641 0024565 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2012 The Project Lombok Authors.
*
* 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 lombok.patcher;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import lombok.Cleanup;
import lombok.Getter;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* Represents a patch script. Contains a convenience method to run ASM on the class you want to transform.
* Usually you can find a subclass of this class that does what you want. If you need to do some novel transformation,
* you should extend this class.
*/
public abstract class PatchScript {
/**
* Defaults to the class name, but you can give a fancier name to your script if you want.
*/
public String getPatchScriptName() {
return getClass().getSimpleName();
}
/**
* Each class name (as standard java class name, with dots) listed here will be reloaded if the JVM supports this
* and you start the ScriptManager while the JVM is already running instead of via the -javaagent parameter. Generally
* you want to list each class you patch.
*/
public abstract Collection getClassesToReload();
public static boolean classMatches(String className, Collection classSpecs) {
for (String classSpec : classSpecs) {
if (MethodTarget.typeMatches(className, classSpec)) return true;
}
return false;
}
/**
* Transforms the class. You may return {@code null} if you have no interest in transforming this particular class.
*/
public abstract byte[] patch(String className, byte[] byteCode, TransplantMapper mapper);
private static class FixedClassWriter extends ClassWriter {
FixedClassWriter(ClassReader classReader, int flags) {
super(classReader, flags);
}
@Override protected String getCommonSuperClass(String type1, String type2) {
//By default, ASM will attempt to live-load the class types, which will fail if meddling with classes in an
//environment with custom classloaders, such as Equinox. It's just an optimization; returning Object is always legal.
// This code is only called for class files <50 (java 1.5 and below), where we turn on COMPUTE_FRAMES, which causes this code to be run.
// We don't quite understand how ASM works here; you don't need frames at 49 or less, so why is COMPUTE_FRAMES shorthand at that point for
// "don't crash right out of the gates?" Dunno. At any rate, returning Object here doesn't seem to break anything even though it's obviously wrong.
try {
return super.getCommonSuperClass(type1, type2);
} catch (Throwable t) {
return "java/lang/Object";
}
}
}
/**
* Runs ASM on the provider byteCode, chaining a reader to a writer and using the {@code ClassVisitor} you yourself provide
* via the {@see #createClassVisitor(ClassWriter)} method as the filter.
*/
protected byte[] runASM(byte[] byteCode, boolean computeStacks, TransplantMapper transplantMapper) {
ClassReader reader = new ClassReader(byteCode);
int classFileFormatVersion = 48;
if (byteCode.length > 7) classFileFormatVersion = byteCode[7] & 0xFF;
int flags = classFileFormatVersion < 50 ? ClassWriter.COMPUTE_FRAMES : 0;
if (computeStacks) flags |= ClassWriter.COMPUTE_MAXS;
ClassWriter writer = new FixedClassWriter(reader, flags);
ClassVisitor visitor = createClassVisitor(writer, reader.getClassName(), transplantMapper);
reader.accept(visitor, 0);
return writer.toByteArray();
}
/**
* You need to override this method if you want to call {@see #runASM(byte[])}.
*
* @param writer The parent writer.
* @param classSpec The name of the class you need to make a visitor for.
*/
protected ClassVisitor createClassVisitor(ClassWriter writer, String classSpec, TransplantMapper transplantMapper) {
throw new IllegalStateException("If you're going to call runASM, then you need to implement createClassVisitor");
}
/**
* If you want to use the {@see MethodPatcher} class, you need to supply an implementation of this factory.
*/
public interface MethodPatcherFactory {
/**
* Supply a {@code MethodVisitor} that knows how to process the provided method.
*
* @param methodName the name of the method.
* @param methodDescription the description of the method, such as (II)V (method takes 2 ints as parameters and returns void).
* @param parent the visitor that will write to the actual class file.
* @param logistics contains useful methods for interacting with the method, such as generating the opcode to
* put a certain parameter on the stack.
*/
public MethodVisitor createMethodVisitor(String methodName, String methodDescription, MethodVisitor parent, MethodLogistics logistics);
}
private static byte[] readStream(String resourceName) {
try {
@Cleanup InputStream wrapStream = PatchScript.class.getResourceAsStream(resourceName);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[65536];
while (true) {
int r = wrapStream.read(b);
if (r == -1) break;
baos.write(b, 0, r);
}
return baos.toByteArray();
} catch (Exception e) {
throw new IllegalArgumentException("resource " + resourceName + " does not exist.", e);
}
}
private static abstract class NoopClassVisitor extends ClassVisitor {
public NoopClassVisitor() {
super(Opcodes.ASM4);
}
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {}
public void visitAttribute(Attribute attr) {}
public void visitEnd() {}
public void visitOuterClass(String owner, String name, String desc) {}
public void visitSource(String source, String debug) {}
public void visitInnerClass(String name, String outerName, String innerName, int access) {}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return null; }
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { return null; }
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { return null; }
}
protected static void insertMethod(final Hook methodToInsert, final MethodVisitor target) {
byte[] classData = readStream("/" + methodToInsert.getClassSpec() + ".class");
ClassReader reader = new ClassReader(classData);
ClassVisitor methodFinder = new NoopClassVisitor() {
@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (name.equals(methodToInsert.getMethodName()) && desc.equals(methodToInsert.getMethodDescriptor())) {
return new InsertBodyOfMethodIntoAnotherVisitor(target);
}
return null;
}
};
reader.accept(methodFinder, 0);
}
protected static void transplantMethod(final String resourceName, final Hook methodToTransplant, final ClassVisitor target) {
byte[] classData = readStream(resourceName);
ClassReader reader = new ClassReader(classData);
ClassVisitor methodFinder = new NoopClassVisitor() {
@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (name.equals(methodToTransplant.getMethodName()) && desc.equals(methodToTransplant.getMethodDescriptor())) {
return target.visitMethod(access, name, desc, signature, exceptions);
}
return null;
}
};
reader.accept(methodFinder, 0);
}
private static final class InsertBodyOfMethodIntoAnotherVisitor extends MethodVisitor {
private InsertBodyOfMethodIntoAnotherVisitor(MethodVisitor mv) {
super(Opcodes.ASM4, mv);
}
@Override public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { return null; }
@Override public void visitMaxs(int maxStack, int maxLocals) {}
@Override public void visitLineNumber(int line, Label start) {}
@Override public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {}
@Override public void visitEnd() {}
@Override public void visitCode() {}
@Override public void visitInsn(int opcode) {
if (opcode == Opcodes.RETURN || opcode == Opcodes.ARETURN || opcode == Opcodes.IRETURN
|| opcode == Opcodes.DRETURN || opcode == Opcodes.FRETURN || opcode == Opcodes.LRETURN)
/* do nothing */ return;
super.visitInsn(opcode);
}
@Override public void visitAttribute(Attribute attr) {}
@Override public AnnotationVisitor visitAnnotationDefault() { return null; }
@Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return null;}
}
/**
* Convenience implementation of the {@code ClassVisitor} that you can return for {@see #createClassVisitor(ClassWriter)};
* it will call into a custom {@code MethodVisitor} for specified methods, and pass through everything else. Perfect if you
* want to rewrite one or more methods.
*/
protected static class MethodPatcher extends ClassVisitor {
private List targets = new ArrayList();
private @Getter String ownClassSpec;
private final MethodPatcherFactory factory;
private List transplants = new ArrayList();
private final TransplantMapper transplantMapper;
private int classFileFormatVersion;
public MethodPatcher(ClassVisitor cv, TransplantMapper transplantMapper, MethodPatcherFactory factory) {
super(Opcodes.ASM4, cv);
this.factory = factory;
this.transplantMapper = transplantMapper;
}
/**
* The {@code factory} will be called for any methods that match any added target.
*/
public void addTargetMatcher(TargetMatcher t) {
targets.add(t);
}
@Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.ownClassSpec = name;
this.classFileFormatVersion = version;
super.visit(version, access, name, signature, superName, interfaces);
}
public void addTransplant(Hook transplant) {
if (transplant == null) throw new NullPointerException("transplant");
transplants.add(transplant);
}
@Override public void visitEnd() {
for (Hook transplant : transplants) {
String resourceName = "/" + transplantMapper.mapResourceName(classFileFormatVersion, transplant.getClassSpec() + ".class");
transplantMethod(resourceName, transplant, cv);
}
}
@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor visitor = super.visitMethod(access, name, desc, signature, exceptions);
/* Remove transplant jobs where the method already exists - probably because of an earlier patch script. */ {
Iterator it = transplants.iterator();
while (it.hasNext()) {
Hook h = it.next();
if (h.getMethodName().equals(name) && h.getMethodDescriptor().equals(desc)) it.remove();
}
}
for (TargetMatcher t : targets) {
if (t.matches(ownClassSpec, name, desc)) {
return factory.createMethodVisitor(name, desc, visitor, new MethodLogistics(access, desc));
}
}
return visitor;
}
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/ScriptManager.java 0000664 0000000 0000000 00000021267 13010176641 0025104 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2016 The Project Lombok Authors.
*
* 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 lombok.patcher;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarFile;
public class ScriptManager {
private final List scripts = new ArrayList();
private TransplantMapper transplantMapper = TransplantMapper.IDENTITY_MAPPER;
private Filter filter = Filter.ALWAYS;
public void addScript(PatchScript script) {
scripts.add(script);
}
public void setFilter(Filter filter) {
this.filter = filter == null ? Filter.ALWAYS : filter;
}
public void registerTransformer(Instrumentation instrumentation) {
try {
Method m = Instrumentation.class.getMethod("addTransformer", ClassFileTransformer.class, boolean.class);
m.invoke(instrumentation, transformer, true);
} catch (Throwable t) {
//We're on java 1.5, or something even crazier happened. This one works in 1.5 as well:
instrumentation.addTransformer(transformer);
}
}
public void reloadClasses(Instrumentation instrumentation) {
Set toReload = new HashSet();
for (PatchScript s : scripts) toReload.addAll(s.getClassesToReload());
for (Class> c : instrumentation.getAllLoadedClasses()) {
if (toReload.contains(c.getName())) {
try {
//instrumentation.retransformClasses(c); - //not in java 1.5.
Instrumentation.class.getMethod("retransformClasses", Class[].class).invoke(instrumentation,
new Object[] { new Class[] {c }});
} catch ( InvocationTargetException e ) {
throw new UnsupportedOperationException(
"The " + c.getName() + " class is already loaded and cannot be modified. " +
"You'll have to restart the application to patch it. Reason: " + e.getCause());
} catch ( Throwable t ) {
throw new UnsupportedOperationException(
"This appears to be a JVM v1.5, which cannot reload already loaded classes. " +
"You'll have to restart the application to patch it.");
}
}
}
}
private static final String DEBUG_PATCHING;
static {
DEBUG_PATCHING = System.getProperty("lombok.patcher.patchDebugDir", null);
}
private final OurClassFileTransformer transformer = new OurClassFileTransformer();
private class OurClassFileTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if (className == null) return null;
if (!filter.shouldTransform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer)) return null;
byte[] byteCode = classfileBuffer;
boolean patched = false;
for (PatchScript script : scripts) {
byte[] transformed = null;
try {
transformed = script.patch(className, byteCode, transplantMapper);
} catch (Throwable t) {
//Exceptions get silently swallowed by instrumentation, so this is a slight improvement.
System.err.printf("Transformer %s failed on %s. Trace:\n", script.getPatchScriptName(), className);
t.printStackTrace();
transformed = null;
}
if (transformed != null) {
patched = true;
byteCode = transformed;
}
}
if (patched && DEBUG_PATCHING != null) {
try {
writeArray(DEBUG_PATCHING, className + ".class", byteCode);
writeArray(DEBUG_PATCHING, className + "_OLD.class", classfileBuffer);
} catch (IOException e) {
System.err.println("Can't log patch result.");
e.printStackTrace();
}
}
return patched ? byteCode : null;
}
private void writeArray(String dir, String fileName, byte[] bytes) throws IOException {
File f = new File(dir, fileName);
f.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(f);
fos.write(bytes);
fos.close();
}
};
private static boolean classpathContains(String property, String path) {
String pathCanonical = new File(path).getAbsolutePath();
try {
pathCanonical = new File(path).getCanonicalPath();
} catch (Exception ignore) {}
for (String existingPath : System.getProperty(property, "").split(File.pathSeparator)) {
String p = new File(existingPath).getAbsolutePath();
try {
p = new File(existingPath).getCanonicalPath();
} catch (Throwable ignore) {}
if (p.equals(pathCanonical)) return true;
}
return false;
}
/**
* Adds the provided path (must be to a jar file!) to the system classpath.
*
* Will do nothing if the jar is already on either the system or the boot classpath.
*
* @throws IllegalArgumentException If {@code pathToJar} doesn't exist or isn't a jar file.
* @throws IllegalStateException If you try this on a 1.5 VM - it requires 1.6 or up VM.
*/
public void addToSystemClasspath(Instrumentation instrumentation, String pathToJar) {
if (pathToJar == null) throw new NullPointerException("pathToJar");
if (classpathContains("sun.boot.class.path", pathToJar)) return;
if (classpathContains("java.class.path", pathToJar)) return;
try {
Method m = instrumentation.getClass().getMethod("appendToSystemClassLoaderSearch", JarFile.class);
m.invoke(instrumentation, new JarFile(pathToJar));
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Adding to the classloader path is not possible on a v1.5 JVM");
} catch (IOException e) {
throw new IllegalArgumentException("not found or not a jar file: " + pathToJar, e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("appendToSystemClassLoaderSearch isn't public? This isn't a JVM...");
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) throw (RuntimeException)cause;
throw new IllegalArgumentException("Unknown issue: " + cause, cause);
}
}
/**
* Adds the provided path (must be to a jar file!) to the system classpath.
*
* Will do nothing if the jar is already on the boot classpath.
*
* @throws IllegalArgumentException If {@code pathToJar} doesn't exist or isn't a jar file.
* @throws IllegalStateException If you try this on a 1.5 VM - it requires 1.6 or up VM.
*/
public void addToBootClasspath(Instrumentation instrumentation, String pathToJar) {
if (pathToJar == null) throw new NullPointerException("pathToJar");
if (classpathContains("sun.boot.class.path", pathToJar)) return;
try {
Method m = instrumentation.getClass().getMethod("appendToBootstrapClassLoaderSearch", JarFile.class);
m.invoke(instrumentation, new JarFile(pathToJar));
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Adding to the classloader path is not possible on a v1.5 JVM");
} catch (IOException e) {
throw new IllegalArgumentException("not found or not a jar file: " + pathToJar, e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("appendToSystemClassLoaderSearch isn't public? This isn't a JVM...");
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) throw (RuntimeException)cause;
throw new IllegalArgumentException("Unknown issue: " + cause, cause);
}
}
public void setTransplantMapper(TransplantMapper transplantMapper) {
this.transplantMapper = transplantMapper == null ? TransplantMapper.IDENTITY_MAPPER : transplantMapper;
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/StackRequest.java 0000664 0000000 0000000 00000003761 13010176641 0024762 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009 The Project Lombok Authors.
*
* 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 lombok.patcher;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import lombok.Getter;
/**
* Various patch scripts support various method parmater signatures in your hook methods; use StackRequest values to specify
* which parameters you want. It doesn't matter how you specify your StackRequest values; you MUST order your parameters
* in the order of the values in the enum (so, first the return value, then the this pointer, then any parameters you want).
*/
public enum StackRequest {
RETURN_VALUE(-1), THIS(-1), PARAM1(0), PARAM2(1), PARAM3(2), PARAM4(3), PARAM5(4), PARAM6(5);
@Getter
private final int paramPos;
StackRequest(int paramPos) {
this.paramPos = paramPos;
}
public static final List PARAMS_IN_ORDER = Collections.unmodifiableList(Arrays.asList(
PARAM1, PARAM2, PARAM3, PARAM4, PARAM5, PARAM6));
}
lombok.patcher-0.22/src/patcher/lombok/patcher/Symbols.java 0000664 0000000 0000000 00000005677 13010176641 0024004 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009 The Project Lombok Authors.
*
* 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 lombok.patcher;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* The various SetSymbol patch scripts cause events in patched source to emit push and pop calls onto this symbol stack.
*
* Use this class to see if a certain method is in the JVM thread stack (faster than looping through the stack trace).
*/
public class Symbols {
private static final ThreadLocal> stack = new ThreadLocal>() {
@Override protected LinkedList initialValue() {
return new LinkedList();
}
};
private Symbols() {}
/**
* Calls to push are automatically generated by the SetSymbol patch scripts. Do not call it yourself!
*/
public static void push(String symbol) {
stack.get().addFirst(symbol);
}
/**
* Calls to pop are automatically generated by the SetSymbol patch scripts. Do not call it yourself!
*/
public static void pop() {
stack.get().poll();
}
public static boolean isEmpty() {
return stack.get().isEmpty();
}
public static int size() {
return stack.get().size();
}
/**
* Checks if the given symbol appears anywhere on the stack.
*/
public static boolean hasSymbol(String symbol) {
if (symbol == null) throw new NullPointerException("symbol");
return stack.get().contains(symbol);
}
/**
* Checks if the last symbol put on the stack equals a given symbol.
*/
public static boolean hasTail(String symbol) {
if (symbol == null) throw new NullPointerException("symbol");
return symbol.equals(stack.get().peek());
}
/**
* Returns a {@code List} that starts at the oldest thing thrown on the stack, and ends with the latest.
* It's a copy, so mess with it, save it, modify it in another thread, whatever you want.
*/
public static List getCopy() {
return new ArrayList(stack.get());
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/TargetMatcher.java 0000664 0000000 0000000 00000003545 13010176641 0025076 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009 The Project Lombok Authors.
*
* 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 lombok.patcher;
import java.util.Collection;
public interface TargetMatcher {
/**
* Return the classes you wish to reload, in binary naming (dots to separate packages and classname, $ for inner classes).
*/
public abstract Collection getAffectedClasses();
/**
* Returns true if the provided classSpec/methodName/methodDescriptor (as per the JVM Specification, and the way ASM
* provides them) fits this MethodTarget.
*
* @param classSpec a Class Specification, JVM-style (e.g. {@code java/lang/String}).
* @param methodName The name of the method.
* @param descriptor A Method descriptor, ASM-style (e.g. {@code (II)V}.
*/
public abstract boolean matches(String classSpec, String methodName, String descriptor);
} lombok.patcher-0.22/src/patcher/lombok/patcher/TransplantMapper.java 0000664 0000000 0000000 00000004222 13010176641 0025630 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2015 The Project Lombok Authors.
*
* 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 lombok.patcher;
/**
* If you are transplanting code into classes which could have varying class file numbers, you probably need to supply at least 2 variants of the exact same class file
* containing the same code; for example, pre 1.5 you can't use generics at all, and starting with 1.6 you need frame info.
*/
public interface TransplantMapper {
/**
* By default, a transplant call will take the class of the 'hook' verbatim, but you can specify an alternate path.
*
* For example, if the hook is from class com.foo.bar.Baz, then by default, the file "com/foo/bar/Baz.class" is read. However,
* if you return "Class50/com/foo/bar/Baz.class" here, the file "Class50/com/foo/bar/Baz.class" is used. Just return {@code resourceName} if no mapping is needed.
*/
String mapResourceName(int classFileFormatVersion, String resourceName);
public static final TransplantMapper IDENTITY_MAPPER = new TransplantMapper() {
public String mapResourceName(int classFileFormatVersion, String resourceName) {
return resourceName;
}
};
}
lombok.patcher-0.22/src/patcher/lombok/patcher/Version.java 0000664 0000000 0000000 00000003315 13010176641 0023764 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2016 The Project Lombok Authors.
*
* 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 lombok.patcher;
/**
* This class just holds lombok.patcher's current version.
*/
public class Version {
// ** CAREFUL ** - this class must always compile with 0 dependencies (it must not refer to any other sources or libraries).
private static final String VERSION = "0.22";
private Version() {
//Prevent instantiation
}
/**
* Prints the version followed by a newline, and exits.
*/
public static void main(String[] args) {
System.out.println(VERSION);
}
/**
* Get the current lombok.patcher version.
*/
public static String getVersion() {
return VERSION;
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/scripts/ 0000775 0000000 0000000 00000000000 13010176641 0023161 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/src/patcher/lombok/patcher/scripts/AddFieldScript.java 0000664 0000000 0000000 00000006705 13010176641 0026655 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2014 The Project Lombok Authors.
*
* 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 lombok.patcher.scripts;
import java.util.Collection;
import java.util.List;
import lombok.patcher.MethodTarget;
import lombok.patcher.PatchScript;
import lombok.patcher.TransplantMapper;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Opcodes;
/**
* Adds a field to any class.
*/
public class AddFieldScript extends PatchScript {
private final int accessFlags;
private final List targetClasses;
private final String fieldName;
private final String fieldType;
private final Object value;
/**
* @param targetClasses The class(es) to add the field to, separated with dots (e.g. java.lang.String).
* @param fieldName the name of the field to create.
* @param typeSpec the type of the field, in JVM spec (e.g. [I for an int array).
*/
AddFieldScript(List targetClasses, int accessFlags, String fieldName, String fieldType, Object value) {
if (targetClasses == null) throw new NullPointerException("targetClass");
if (fieldName == null) throw new NullPointerException("fieldName");
if (fieldType == null) throw new NullPointerException("typeSpec");
this.accessFlags = accessFlags;
this.targetClasses = targetClasses;
this.fieldName = fieldName;
this.fieldType = fieldType;
this.value = value;
}
@Override public byte[] patch(String className, byte[] byteCode, TransplantMapper transplantMapper) {
for (String tc : targetClasses) if (MethodTarget.typeMatches(className, tc)) return runASM(byteCode, false, transplantMapper);
return null;
}
@Override protected ClassVisitor createClassVisitor(ClassWriter writer, String classSpec, TransplantMapper transplantMapper) {
return new ClassVisitor(Opcodes.ASM4, writer) {
private boolean alreadyAdded = false;
@Override public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
if (name != null && name.equals(fieldName)) alreadyAdded = true;
return super.visitField(access, name, desc, signature, value);
}
@Override public void visitEnd() {
if (!alreadyAdded) {
FieldVisitor fv = cv.visitField(accessFlags, fieldName, fieldType, null, value);
fv.visitEnd();
}
super.visitEnd();
}
};
}
@Override public Collection getClassesToReload() {
return targetClasses;
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/scripts/ExitFromMethodEarlyScript.java 0000664 0000000 0000000 00000014303 13010176641 0031105 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2014 The Project Lombok Authors.
*
* 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 lombok.patcher.scripts;
import java.util.List;
import java.util.Set;
import lombok.patcher.Hook;
import lombok.patcher.MethodLogistics;
import lombok.patcher.StackRequest;
import lombok.patcher.TargetMatcher;
import lombok.patcher.TransplantMapper;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* Receive (optional) 'this' reference as well as any parameters, then choose to return early with provided value, or let
* the method continue.
*/
public class ExitFromMethodEarlyScript extends MethodLevelPatchScript {
private final Hook decisionWrapper, valueWrapper;
private final Set requests;
private final boolean transplant, insert;
private final boolean insertCallOnly;
ExitFromMethodEarlyScript(List matchers, Hook decisionWrapper, Hook valueWrapper, boolean transplant, boolean insert, Set requests) {
super(matchers);
this.decisionWrapper = decisionWrapper;
this.valueWrapper = valueWrapper;
this.requests = requests;
this.transplant = transplant;
this.insert = insert;
this.insertCallOnly = decisionWrapper != null && decisionWrapper.getMethodDescriptor().endsWith(")V");
if (!this.insertCallOnly && decisionWrapper != null && !decisionWrapper.getMethodDescriptor().endsWith(")Z")) {
throw new IllegalArgumentException("The decisionWrapper method must either return 'boolean' or return 'void'.");
}
assert !(insert && transplant);
}
@Override protected MethodPatcher createPatcher(ClassWriter writer, final String classSpec, TransplantMapper transplantMapper) {
MethodPatcher patcher = new MethodPatcher(writer, transplantMapper, new MethodPatcherFactory() {
public MethodVisitor createMethodVisitor(String name, String desc, MethodVisitor parent, MethodLogistics logistics) {
if (valueWrapper == null && !insertCallOnly && logistics.getReturnOpcode() != Opcodes.RETURN) {
throw new IllegalStateException("method " + name + desc + " must return something, but " +
"you did not provide a value hook method.");
}
return new ExitEarly(parent, logistics, classSpec);
}
});
if (transplant) {
patcher.addTransplant(decisionWrapper);
if (valueWrapper != null) patcher.addTransplant(valueWrapper);
}
return patcher;
}
private class ExitEarly extends MethodVisitor {
private final MethodLogistics logistics;
private final String ownClassSpec;
public ExitEarly(MethodVisitor mv, MethodLogistics logistics, String ownClassSpec) {
super(Opcodes.ASM4, mv);
this.logistics = logistics;
this.ownClassSpec = ownClassSpec;
}
@Override public void visitCode() {
if (decisionWrapper == null) {
//Always return early.
if (logistics.getReturnOpcode() == Opcodes.RETURN) {
mv.visitInsn(Opcodes.RETURN);
return;
}
insertValueWrapperCall();
return;
}
if (requests.contains(StackRequest.THIS)) logistics.generateLoadOpcodeForThis(mv);
for (StackRequest param : StackRequest.PARAMS_IN_ORDER) {
if (!requests.contains(param)) continue;
logistics.generateLoadOpcodeForParam(param.getParamPos(), mv);
}
if (insert) insertMethod(decisionWrapper, mv);
else super.visitMethodInsn(Opcodes.INVOKESTATIC, transplant ? ownClassSpec : decisionWrapper.getClassSpec(),
decisionWrapper.getMethodName(), decisionWrapper.getMethodDescriptor(), false);
if (insertCallOnly) {
super.visitCode();
return;
}
/* Inject:
* if ([result of decision hook]) {
* //if method body is not VOID:
* reload-on-stack-what-needs-reloading
* invokeReturnValueHook
* //end if
* xRETURN;
* }
*/
Label label0 = new Label();
mv.visitJumpInsn(Opcodes.IFEQ, label0);
if (logistics.getReturnOpcode() == Opcodes.RETURN) {
mv.visitInsn(Opcodes.RETURN);
} else {
if (requests.contains(StackRequest.THIS)) logistics.generateLoadOpcodeForThis(mv);
for (StackRequest param : StackRequest.PARAMS_IN_ORDER) {
if (!requests.contains(param)) continue;
logistics.generateLoadOpcodeForParam(param.getParamPos(), mv);
}
if (insert) insertMethod(valueWrapper, mv);
else super.visitMethodInsn(Opcodes.INVOKESTATIC, transplant ? ownClassSpec : valueWrapper.getClassSpec(),
valueWrapper.getMethodName(), valueWrapper.getMethodDescriptor(), false);
logistics.generateReturnOpcode(mv);
}
mv.visitLabel(label0);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
super.visitCode();
}
private void insertValueWrapperCall() {
if (requests.contains(StackRequest.THIS)) logistics.generateLoadOpcodeForThis(mv);
for (StackRequest param : StackRequest.PARAMS_IN_ORDER) {
if (!requests.contains(param)) continue;
logistics.generateLoadOpcodeForParam(param.getParamPos(), mv);
}
if (insert) insertMethod(valueWrapper, mv);
else super.visitMethodInsn(Opcodes.INVOKESTATIC, transplant ? ownClassSpec : valueWrapper.getClassSpec(),
valueWrapper.getMethodName(), valueWrapper.getMethodDescriptor(), false);
logistics.generateReturnOpcode(mv);
}
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/scripts/MethodLevelPatchScript.java 0000664 0000000 0000000 00000005162 13010176641 0030405 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009 The Project Lombok Authors.
*
* 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 lombok.patcher.scripts;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import lombok.patcher.PatchScript;
import lombok.patcher.TargetMatcher;
import lombok.patcher.TransplantMapper;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
public abstract class MethodLevelPatchScript extends PatchScript {
private final Set affectedClasses;
private final Collection matchers;
public MethodLevelPatchScript(Collection matchers) {
this.matchers = matchers;
Set affected = new HashSet();
for (TargetMatcher t : matchers) affected.addAll(t.getAffectedClasses());
this.affectedClasses = Collections.unmodifiableSet(affected);
}
@Override public Collection getClassesToReload() {
return affectedClasses;
}
@Override public byte[] patch(String className, byte[] byteCode, TransplantMapper transplantMapper) {
if (!classMatches(className, affectedClasses)) return null;
return runASM(byteCode, true, transplantMapper);
}
@Override protected final ClassVisitor createClassVisitor(ClassWriter writer, final String classSpec, TransplantMapper transplantMapper) {
MethodPatcher patcher = createPatcher(writer, classSpec, transplantMapper);
for (TargetMatcher matcher : matchers) patcher.addTargetMatcher(matcher);
return patcher;
}
protected abstract MethodPatcher createPatcher(ClassWriter writer, String classSpec, TransplantMapper transplantMapper);
}
lombok.patcher-0.22/src/patcher/lombok/patcher/scripts/ReplaceMethodCallScript.java 0000664 0000000 0000000 00000010277 13010176641 0030530 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2014 The Project Lombok Authors.
*
* 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 lombok.patcher.scripts;
import java.util.List;
import java.util.Set;
import lombok.patcher.Hook;
import lombok.patcher.MethodLogistics;
import lombok.patcher.StackRequest;
import lombok.patcher.TargetMatcher;
import lombok.patcher.TransplantMapper;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* Replaces all calls to a given method with another (static) method. It is your job to ensure both all parameters and the
* return type are perfectly compatible. If you're replacing an instance method, make sure your static method's first
* parameter is type-compatible with the LHS of the instance method.
*/
public class ReplaceMethodCallScript extends MethodLevelPatchScript {
private final Hook wrapper;
private final Hook methodToReplace;
private final boolean transplant, insert;
private final Set extraRequests;
ReplaceMethodCallScript(List matchers, Hook callToReplace, Hook wrapper, boolean transplant, boolean insert, Set extraRequests) {
super(matchers);
if (callToReplace == null) throw new NullPointerException("callToReplace");
if (wrapper == null) throw new NullPointerException("wrapper");
this.methodToReplace = callToReplace;
this.wrapper = wrapper;
this.transplant = transplant;
this.insert = insert;
assert !(insert && transplant);
this.extraRequests = extraRequests;
}
@Override protected MethodPatcher createPatcher(ClassWriter writer, final String classSpec, TransplantMapper transplantMapper) {
final MethodPatcher patcher = new MethodPatcher(writer, transplantMapper, new MethodPatcherFactory() {
public MethodVisitor createMethodVisitor(String name, String desc, MethodVisitor parent, MethodLogistics logistics) {
return new ReplaceMethodCall(parent, classSpec, logistics);
}
});
if (transplant) patcher.addTransplant(wrapper);
return patcher;
}
private class ReplaceMethodCall extends MethodVisitor {
private final String ownClassSpec;
private final MethodLogistics logistics;
public ReplaceMethodCall(MethodVisitor mv, String ownClassSpec, MethodLogistics logistics) {
super(Opcodes.ASM4, mv);
this.ownClassSpec = ownClassSpec;
this.logistics = logistics;
}
@Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (methodToReplace.getClassSpec().equals(owner) &&
methodToReplace.getMethodName().equals(name) &&
methodToReplace.getMethodDescriptor().equals(desc)) {
if (extraRequests.contains(StackRequest.THIS)) logistics.generateLoadOpcodeForThis(mv);
for (StackRequest param : StackRequest.PARAMS_IN_ORDER) {
if (!extraRequests.contains(param)) continue;
logistics.generateLoadOpcodeForParam(param.getParamPos(), mv);
}
if (insert) insertMethod(wrapper, mv);
else super.visitMethodInsn(Opcodes.INVOKESTATIC, transplant ? ownClassSpec : wrapper.getClassSpec(),
wrapper.getMethodName(), wrapper.getMethodDescriptor(), itf);
} else {
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
}
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/scripts/ScriptBuilder.java 0000664 0000000 0000000 00000031462 13010176641 0026605 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009 The Project Lombok Authors.
*
* 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 lombok.patcher.scripts;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import lombok.patcher.Hook;
import lombok.patcher.StackRequest;
import lombok.patcher.TargetMatcher;
import org.objectweb.asm.Opcodes;
public class ScriptBuilder {
private ScriptBuilder() throws NoSuchMethodException {
throw new NoSuchMethodException("ScriptBuilder cannot be instantiated - just use the static methods.");
}
private static void checkTypeSyntaxSlash(String spec) {
if (spec.indexOf('.') > -1) throw new IllegalArgumentException(
"Your type specification includes a dot, but this method wants a slash-separated type specification");
}
private static void checkTypeSyntaxDot(String spec) {
if (spec.indexOf('/') > -1) throw new IllegalArgumentException(
"Your type specification includes a slash, but this method wants a dot-separated type specification");
}
public static class AddFieldBuilder {
private int accessFlags;
private List targetClasses = new ArrayList();
private String fieldName;
private String fieldType;
private Object value;
private static final int NO_ACCESS_LEVELS = ~(Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_PRIVATE);
public AddFieldScript build() {
if (targetClasses.isEmpty()) throw new IllegalStateException("You have to set at least one targetClass.");
if (fieldName == null) throw new IllegalStateException("You have to set a fieldName");
if (fieldType == null) throw new IllegalStateException("You have to set the new field's type by calling fieldType");
if (value != null) {
setStatic();
setFinal();
}
return new AddFieldScript(targetClasses, accessFlags, fieldName, fieldType, value);
}
/**
* @param targetClass The class to add the field to, separated with dots (e.g. java.lang.String).
*/
public AddFieldBuilder targetClass(String targetClass) {
checkTypeSyntaxDot(targetClass);
this.targetClasses.add(targetClass);
return this;
}
public AddFieldBuilder value(Object value) {
this.value = value;
return this;
}
/**
* @param fieldName the name of the field to create.
*/
public AddFieldBuilder fieldName(String fieldName) {
this.fieldName = fieldName;
return this;
}
/**
* @param fieldType the type of the field, in JVM spec (e.g. [I for an int array).
*/
public AddFieldBuilder fieldType(String fieldType) {
checkTypeSyntaxSlash(fieldType);
this.fieldType = fieldType;
return this;
}
public AddFieldBuilder setPublic() {
accessFlags = (accessFlags & NO_ACCESS_LEVELS) | Opcodes.ACC_PUBLIC;
return this;
}
public AddFieldBuilder setPrivate() {
accessFlags = (accessFlags & NO_ACCESS_LEVELS) | Opcodes.ACC_PRIVATE;
return this;
}
public AddFieldBuilder setProtected() {
accessFlags = (accessFlags & NO_ACCESS_LEVELS) | Opcodes.ACC_PROTECTED;
return this;
}
public AddFieldBuilder setPackageAccess() {
accessFlags = (accessFlags & NO_ACCESS_LEVELS);
return this;
}
public AddFieldBuilder setStatic() {
accessFlags |= Opcodes.ACC_STATIC;
return this;
}
public AddFieldBuilder setFinal() {
accessFlags |= Opcodes.ACC_FINAL;
return this;
}
public AddFieldBuilder setVolatile() {
accessFlags |= Opcodes.ACC_VOLATILE;
return this;
}
public AddFieldBuilder setTransient() {
accessFlags |= Opcodes.ACC_TRANSIENT;
return this;
}
}
public static class ExitEarlyBuilder {
private List matchers = new ArrayList();
private Hook decisionMethod, valueMethod;
private Set requests = new HashSet();
private boolean transplant, insert;
public ExitFromMethodEarlyScript build() {
if (matchers.isEmpty()) throw new IllegalStateException("You have to set a target method matcher");
return new ExitFromMethodEarlyScript(matchers, decisionMethod, valueMethod, transplant, insert, requests);
}
public ExitEarlyBuilder target(TargetMatcher matcher) {
this.matchers.add(matcher);
return this;
}
public ExitEarlyBuilder decisionMethod(Hook hook) {
this.decisionMethod = hook;
return this;
}
public ExitEarlyBuilder valueMethod(Hook hook) {
this.valueMethod = hook;
return this;
}
public ExitEarlyBuilder transplant() {
this.transplant = true;
this.insert = false;
return this;
}
public ExitEarlyBuilder insert() {
this.transplant = false;
this.insert = true;
return this;
}
public ExitEarlyBuilder request(StackRequest... requests) {
for (StackRequest r : requests) {
if (r == StackRequest.RETURN_VALUE) throw new IllegalArgumentException(
"You cannot ask for the tentative return value in ExitFromMethodEarlyScript.");
this.requests.add(r);
}
return this;
}
}
public static class ReplaceMethodCallBuilder {
private List matchers = new ArrayList();
private Hook replacementMethod;
private Hook methodToReplace;
private Set extraRequests = new HashSet();
private boolean transplant, insert;
public ReplaceMethodCallScript build() {
if (matchers.isEmpty()) throw new IllegalStateException("You have to set a target method matcher");
if (replacementMethod == null) throw new IllegalStateException("You have to set a replacement method");
if (methodToReplace == null) throw new IllegalStateException("You have to set a method call to replace");
return new ReplaceMethodCallScript(matchers, methodToReplace, replacementMethod, transplant, insert, extraRequests);
}
public ReplaceMethodCallBuilder target(TargetMatcher matcher) {
this.matchers.add(matcher);
return this;
}
public ReplaceMethodCallBuilder replacementMethod(Hook hook) {
this.replacementMethod = hook;
return this;
}
public ReplaceMethodCallBuilder methodToReplace(Hook hook) {
this.methodToReplace = hook;
return this;
}
public ReplaceMethodCallBuilder transplant() {
this.transplant = true;
this.insert = false;
return this;
}
public ReplaceMethodCallBuilder insert() {
this.transplant = false;
this.insert = true;
return this;
}
public ReplaceMethodCallBuilder requestExtra(StackRequest... requests) {
for (StackRequest r : requests) {
if (r == StackRequest.RETURN_VALUE) throw new IllegalArgumentException(
"You cannot ask for the tentative return value in ReplaceMethodCallScript.");
this.extraRequests.add(r);
}
return this;
}
}
public static class WrapMethodCallBuilder {
private List matchers = new ArrayList();
private Hook wrapMethod;
private Hook methodToWrap;
private Set extraRequests = new HashSet();
private boolean transplant, insert;
public WrapMethodCallScript build() {
if (matchers.isEmpty()) throw new IllegalStateException("You have to set a target method matcher");
if (wrapMethod == null) throw new IllegalStateException("You have to set method to wrap with");
if (methodToWrap == null) throw new IllegalStateException("You have to set a method call to wrap");
return new WrapMethodCallScript(matchers, methodToWrap, wrapMethod, transplant, insert, extraRequests);
}
public WrapMethodCallBuilder target(TargetMatcher matcher) {
this.matchers.add(matcher);
return this;
}
public WrapMethodCallBuilder wrapMethod(Hook hook) {
this.wrapMethod = hook;
return this;
}
public WrapMethodCallBuilder methodToWrap(Hook hook) {
this.methodToWrap = hook;
return this;
}
public WrapMethodCallBuilder transplant() {
this.transplant = true;
this.insert = false;
return this;
}
public WrapMethodCallBuilder insert() {
this.transplant = false;
this.insert = true;
return this;
}
public WrapMethodCallBuilder requestExtra(StackRequest... requests) {
for (StackRequest r : requests) {
if (r == StackRequest.RETURN_VALUE) throw new IllegalArgumentException(
"You cannot ask for the tentative return value in WrapMethodCallBuilder.");
this.extraRequests.add(r);
}
return this;
}
}
public static class WrapReturnValueBuilder {
private List matchers = new ArrayList();
private Hook wrapMethod;
private Set requests = new HashSet();
private boolean transplant, insert;
public WrapReturnValuesScript build() {
if (matchers.isEmpty()) throw new IllegalStateException("You have to set a target method matcher");
if (wrapMethod == null) throw new IllegalStateException("You have to set a method you'd like to wrap the return values with");
return new WrapReturnValuesScript(matchers, wrapMethod, transplant, insert, requests);
}
public WrapReturnValueBuilder target(TargetMatcher matcher) {
this.matchers.add(matcher);
return this;
}
public WrapReturnValueBuilder wrapMethod(Hook hook) {
this.wrapMethod = hook;
return this;
}
public WrapReturnValueBuilder transplant() {
this.transplant = true;
this.insert = false;
return this;
}
public WrapReturnValueBuilder insert() {
this.transplant = false;
this.insert = true;
return this;
}
public WrapReturnValueBuilder request(StackRequest... requests) {
for (StackRequest r : requests) this.requests.add(r);
return this;
}
}
public static class SetSymbolDuringMethodCallBuilder {
private List matchers = new ArrayList();
private Hook callToWrap;
private String symbol;
private boolean report;
public SetSymbolDuringMethodCallScript build() {
if (matchers.isEmpty()) throw new IllegalStateException("You have to set a target method matcher");
if (callToWrap == null) throw new IllegalStateException("You have to set a method that needs to set the symbol during its invocation");
if (symbol == null) throw new IllegalStateException("You have to specify the symbol that is on the stack during callToWrap's invocation");
return new SetSymbolDuringMethodCallScript(matchers, callToWrap, symbol, report);
}
public SetSymbolDuringMethodCallBuilder target(TargetMatcher matcher) {
this.matchers.add(matcher);
return this;
}
public SetSymbolDuringMethodCallBuilder callToWrap(Hook callToWrap) {
this.callToWrap = callToWrap;
return this;
}
public SetSymbolDuringMethodCallBuilder symbol(String symbol) {
this.symbol = symbol;
return this;
}
public SetSymbolDuringMethodCallBuilder report() {
this.report = true;
return this;
}
}
/**
* Adds a field to any class.
*/
public static AddFieldBuilder addField() {
return new AddFieldBuilder();
}
/**
* Allows you patch any method so that you get called first, and you can choose to take over entirely if you want.
*/
public static ExitEarlyBuilder exitEarly() {
return new ExitEarlyBuilder();
}
/**
* Allows you to replace all calls to a given method in a given method with calls to a method of your choosing.
*/
public static ReplaceMethodCallBuilder replaceMethodCall() {
return new ReplaceMethodCallBuilder();
}
/**
* Allows you to inspect and optionally replace the result of calls to a given method in a given method.
*/
public static WrapMethodCallBuilder wrapMethodCall() {
return new WrapMethodCallBuilder();
}
/**
* Allows you to inspect every value right before it is returned, and, optionally, replace it with something else.
*/
public static WrapReturnValueBuilder wrapReturnValue() {
return new WrapReturnValueBuilder();
}
/**
* Allows you to push a symbol for the duration of all calls to method A in method B.
*/
public static SetSymbolDuringMethodCallBuilder setSymbolDuringMethodCall() {
return new SetSymbolDuringMethodCallBuilder();
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/scripts/SetSymbolDuringMethodCallScript.java 0000664 0000000 0000000 00000013727 13010176641 0032252 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2015 The Project Lombok Authors.
*
* 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 lombok.patcher.scripts;
import java.util.ArrayList;
import java.util.List;
import lombok.patcher.Hook;
import lombok.patcher.MethodLogistics;
import lombok.patcher.TargetMatcher;
import lombok.patcher.TransplantMapper;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* Will wrap any invocation to a given method in another given method by setting a symbol for the duration
* of the method, properly guarded with a try/finally block. See {@link lombok.patcher.Symbols} for how to
* query symbols states.
*/
public class SetSymbolDuringMethodCallScript extends MethodLevelPatchScript {
private final Hook callToWrap;
private final String symbol;
private final boolean report;
SetSymbolDuringMethodCallScript(List matchers, Hook callToWrap, String symbol, boolean report) {
super(matchers);
if (callToWrap == null) throw new NullPointerException("callToWrap");
if (symbol == null) throw new NullPointerException("symbol");
this.callToWrap = callToWrap;
this.symbol = symbol;
this.report = report;
}
@Override protected MethodPatcher createPatcher(ClassWriter writer, final String classSpec, TransplantMapper transplantMapper) {
final List descriptors = new ArrayList();
final MethodPatcher patcher = new MethodPatcher(writer, transplantMapper, new MethodPatcherFactory() {
public MethodVisitor createMethodVisitor(String name, String desc, MethodVisitor parent, MethodLogistics logistics) {
return new WrapWithSymbol(name, parent, classSpec, descriptors);
}
}) {
@Override public void visitEnd() {
for (WrapperMethodDescriptor wmd : descriptors) {
makeWrapperMethod(this, wmd);
}
super.visitEnd();
}
};
return patcher;
}
private void makeWrapperMethod(ClassVisitor cv, WrapperMethodDescriptor wmd) {
MethodVisitor mv = cv.visitMethod(Opcodes.ACC_SYNTHETIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, wmd.getWrapperName(), wmd.getWrapperDescriptor(), null, null);
MethodLogistics logistics = new MethodLogistics(Opcodes.ACC_STATIC, wmd.getWrapperDescriptor());
mv.visitCode();
Label start = new Label();
Label end = new Label();
Label handler = new Label();
mv.visitTryCatchBlock(start, end, handler, null);
mv.visitLabel(start);
mv.visitLdcInsn(symbol);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "lombok/patcher/Symbols", "push", "(Ljava/lang/String;)V", false);
for (int i = 0; i < logistics.getParamCount(); i++) {
logistics.generateLoadOpcodeForParam(i, mv);
}
mv.visitMethodInsn(wmd.getOpcode(), wmd.getOwner(), wmd.getName(), wmd.getTargetDescriptor(), wmd.isItf());
mv.visitLabel(end);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "lombok/patcher/Symbols", "pop", "()V", false);
logistics.generateReturnOpcode(mv);
mv.visitLabel(handler);
mv.visitFrame(Opcodes.F_FULL, 0, null, 1, new Object[] {"java/lang/Throwable"});
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "lombok/patcher/Symbols", "pop", "()V", false);
mv.visitInsn(Opcodes.ATHROW);
mv.visitMaxs(Math.max(1, logistics.getParamCount()), logistics.getParamCount());
mv.visitEnd();
}
private class WrapWithSymbol extends MethodVisitor {
private final String selfMethodName;
private final String selfTypeName;
private final List descriptors;
public WrapWithSymbol(String selfMethodName, MethodVisitor mv, String selfTypeName, List descriptors) {
super(Opcodes.ASM4, mv);
this.selfMethodName = selfMethodName;
this.selfTypeName = selfTypeName;
this.descriptors = descriptors;
}
@Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
boolean addOwner;
if (opcode == Opcodes.INVOKEINTERFACE || opcode == Opcodes.INVOKEVIRTUAL) addOwner = true;
else if (opcode == Opcodes.INVOKESTATIC) addOwner = false;
else {
super.visitMethodInsn(opcode, owner, name, desc, itf);
return;
}
if (!callToWrap.getClassSpec().equals(owner) ||
!callToWrap.getMethodName().equals(name) ||
!callToWrap.getMethodDescriptor().equals(desc)) {
super.visitMethodInsn(opcode, owner, name, desc, itf);
return;
}
String fixedDesc;
if (addOwner) {
fixedDesc = "(L" + callToWrap.getClassSpec() + ";" + desc.substring(1);
} else {
fixedDesc = desc;
}
WrapperMethodDescriptor wmd = new WrapperMethodDescriptor(descriptors.size(), opcode, owner, name, fixedDesc, desc, itf);
if (report) System.out.println("Changing method " + selfTypeName + "::" + selfMethodName + " wrapping call to " + owner + "::" + name + " to set symbol " + symbol);
super.visitMethodInsn(Opcodes.INVOKESTATIC, selfTypeName, wmd.getWrapperName(), fixedDesc, false);
descriptors.add(wmd);
}
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/scripts/WrapMethodCallScript.java 0000664 0000000 0000000 00000011560 13010176641 0030062 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2014 The Project Lombok Authors.
*
* 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 lombok.patcher.scripts;
import java.util.List;
import java.util.Set;
import lombok.patcher.Hook;
import lombok.patcher.MethodLogistics;
import lombok.patcher.MethodTarget;
import lombok.patcher.StackRequest;
import lombok.patcher.TargetMatcher;
import lombok.patcher.TransplantMapper;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* Inserts a method call to your static method immediately after any method call to a given method. You can inspect the returned
* value (if any) and choose to replace it with a new one if you want.
*
* The first parameter must always be type compatible with the return value of the method you're wrapping. Omit it if you're
* wrapping a method that returns void. You can also get a reference to the 'this' context, if you're modifying a non-static method,
* as well as any parameters to the original method you're modifying (*NOT* to the method call that you're trying to wrap!)
*/
public class WrapMethodCallScript extends MethodLevelPatchScript {
private final Hook wrapper;
private final Hook callToWrap;
private final boolean transplant, insert;
private final boolean leaveReturnValueIntact;
private final Set extraRequests;
WrapMethodCallScript(List matchers, Hook callToWrap, Hook wrapper, boolean transplant, boolean insert, Set extraRequests) {
super(matchers);
if (callToWrap == null) throw new NullPointerException("callToWrap");
if (wrapper == null) throw new NullPointerException("wrapper");
this.leaveReturnValueIntact = wrapper.getMethodDescriptor().endsWith(")V") && (!callToWrap.getMethodDescriptor().endsWith(")V") || callToWrap.isConstructor());
this.callToWrap = callToWrap;
this.wrapper = wrapper;
this.transplant = transplant;
this.insert = insert;
assert !(insert && transplant);
this.extraRequests = extraRequests;
}
@Override protected MethodPatcher createPatcher(ClassWriter writer, final String classSpec, TransplantMapper transplantMapper) {
final MethodPatcher patcher = new MethodPatcher(writer, transplantMapper, new MethodPatcherFactory() {
public MethodVisitor createMethodVisitor(String name, String desc, MethodVisitor parent, MethodLogistics logistics) {
return new WrapMethodCall(parent, classSpec, logistics);
}
});
if (transplant) patcher.addTransplant(wrapper);
return patcher;
}
private class WrapMethodCall extends MethodVisitor {
private final String ownClassSpec;
private final MethodLogistics logistics;
public WrapMethodCall(MethodVisitor mv, String ownClassSpec, MethodLogistics logistics) {
super(Opcodes.ASM4, mv);
this.ownClassSpec = ownClassSpec;
this.logistics = logistics;
}
@Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
super.visitMethodInsn(opcode, owner, name, desc, itf);
if (callToWrap.getClassSpec().equals(owner) &&
callToWrap.getMethodName().equals(name) &&
callToWrap.getMethodDescriptor().equals(desc)) {
if (leaveReturnValueIntact) {
if (callToWrap.isConstructor()) mv.visitInsn(Opcodes.DUP);
else MethodLogistics.generateDupForType(MethodTarget.decomposeFullDesc(callToWrap.getMethodDescriptor()).get(0), mv);
}
if (extraRequests.contains(StackRequest.THIS)) logistics.generateLoadOpcodeForThis(mv);
for (StackRequest param : StackRequest.PARAMS_IN_ORDER) {
if (!extraRequests.contains(param)) continue;
logistics.generateLoadOpcodeForParam(param.getParamPos(), mv);
}
if (insert) insertMethod(wrapper, mv);
else super.visitMethodInsn(Opcodes.INVOKESTATIC, transplant ? ownClassSpec : wrapper.getClassSpec(),
wrapper.getMethodName(), wrapper.getMethodDescriptor(), false);
}
}
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/scripts/WrapReturnValuesScript.java 0000664 0000000 0000000 00000011254 13010176641 0030505 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2014 The Project Lombok Authors.
*
* 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 lombok.patcher.scripts;
import java.util.List;
import java.util.Set;
import lombok.NonNull;
import lombok.ToString;
import lombok.patcher.Hook;
import lombok.patcher.MethodLogistics;
import lombok.patcher.StackRequest;
import lombok.patcher.TargetMatcher;
import lombok.patcher.TransplantMapper;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* Will find every 'return' instruction in the target method and will insert right before it a call to the wrapper.
*/
@ToString
public final class WrapReturnValuesScript extends MethodLevelPatchScript {
private final @NonNull Hook wrapper;
private final Set requests;
private final boolean hijackReturnValue;
private final boolean transplant, insert;
/**
* @param targetMethod The target method to patch.
* @param wrapper A call to this method will be inserted in front of each return in the target method (must be static).
* @param transplant If true, the method content is loaded directly into the target class. Make sure you don't call
* helper methods if you use this!
* @param requests The kinds of parameters you want your hook method to receive.
*/
WrapReturnValuesScript(List matchers, Hook wrapper, boolean transplant, boolean insert, Set requests) {
super(matchers);
if (wrapper == null) throw new NullPointerException("wrapper");
this.wrapper = wrapper;
this.hijackReturnValue = !wrapper.getMethodDescriptor().endsWith(")V");
this.requests = requests;
this.transplant = transplant;
this.insert = insert;
assert !(insert && transplant);
}
@Override protected MethodPatcher createPatcher(ClassWriter writer, final String classSpec, TransplantMapper transplantMapper) {
final MethodPatcher patcher = new MethodPatcher(writer, transplantMapper, new MethodPatcherFactory() {
public MethodVisitor createMethodVisitor(String name, String desc, MethodVisitor parent, MethodLogistics logistics) {
return new WrapReturnValues(parent, logistics, classSpec);
}
});
if (transplant) patcher.addTransplant(wrapper);
return patcher;
}
private class WrapReturnValues extends MethodVisitor {
private final MethodLogistics logistics;
private final String ownClassSpec;
public WrapReturnValues(MethodVisitor mv, MethodLogistics logistics, String ownClassSpec) {
super(Opcodes.ASM4, mv);
this.logistics = logistics;
this.ownClassSpec = ownClassSpec;
}
@Override public void visitInsn(int opcode) {
if (opcode != logistics.getReturnOpcode()) {
super.visitInsn(opcode);
return;
}
if (requests.contains(StackRequest.RETURN_VALUE)) {
if (!hijackReturnValue) {
//The supposed return value is on stack, but the wrapper wants it and will not supply a new one, so duplicate it.
logistics.generateDupForReturn(mv);
}
} else {
if (hijackReturnValue) {
//The supposed return value is on stack, but the wrapper doesn't want it and will supply a new one, so, kill it.
logistics.generatePopForReturn(mv);
}
}
if (requests.contains(StackRequest.THIS)) logistics.generateLoadOpcodeForThis(mv);
for (StackRequest param : StackRequest.PARAMS_IN_ORDER) {
if (!requests.contains(param)) continue;
logistics.generateLoadOpcodeForParam(param.getParamPos(), mv);
}
if (insert) insertMethod(wrapper, mv);
else super.visitMethodInsn(Opcodes.INVOKESTATIC, transplant ? ownClassSpec : wrapper.getClassSpec(), wrapper.getMethodName(),
wrapper.getMethodDescriptor(), false);
super.visitInsn(opcode);
}
}
}
lombok.patcher-0.22/src/patcher/lombok/patcher/scripts/WrapperMethodDescriptor.java 0000664 0000000 0000000 00000000472 13010176641 0030647 0 ustar 00root root 0000000 0000000 package lombok.patcher.scripts;
import lombok.Value;
@Value
public class WrapperMethodDescriptor {
int count;
int opcode;
String owner;
String name;
String wrapperDescriptor;
String targetDescriptor;
boolean itf;
public String getWrapperName() {
return "$lombok$$wrapper$" + count + "$" + name;
}
}
lombok.patcher-0.22/test/ 0000775 0000000 0000000 00000000000 13010176641 0015323 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/test/lombok/ 0000775 0000000 0000000 00000000000 13010176641 0016606 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/test/lombok/patcher/ 0000775 0000000 0000000 00000000000 13010176641 0020234 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/test/lombok/patcher/TestMethodTarget.java 0000664 0000000 0000000 00000012441 13010176641 0024330 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009 The Project Lombok Authors.
*
* 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 lombok.patcher;
import static org.junit.Assert.*;
import org.junit.Test;
public class TestMethodTarget {
@Test
public void arrayTypeMatching() {
assertTrue("Matching String[][] failed.", MethodTarget.typeSpecMatch("[[Ljava/lang/String;", "java.lang.String[][]"));
assertFalse("Matching String[][] failed.", MethodTarget.typeSpecMatch("[[Ljava/lang/String;", "java.lang.String[][][]"));
assertFalse("Matching String[][] failed.", MethodTarget.typeSpecMatch("[[Ljava/lang/String;", "java.lang.String[]"));
}
@Test
public void primitiveTypeMatching() {
assertTrue("Primitive fail: int", MethodTarget.typeSpecMatch("I", "int"));
assertTrue("Primitive fail: byte[]", MethodTarget.typeSpecMatch("[B", "byte[]"));
assertTrue("Primitive fail: short", MethodTarget.typeSpecMatch("S", "short"));
assertTrue("Primitive fail: long", MethodTarget.typeSpecMatch("J", "long"));
assertTrue("Primitive fail: float", MethodTarget.typeSpecMatch("F", "float"));
assertTrue("Primitive fail: double", MethodTarget.typeSpecMatch("D", "double"));
assertTrue("Primitive fail: char", MethodTarget.typeSpecMatch("C", "char"));
assertTrue("Primitive fail: boolean", MethodTarget.typeSpecMatch("Z", "boolean"));
assertTrue("Primitive fail: void", MethodTarget.typeSpecMatch("V", "void"));
assertFalse(MethodTarget.typeSpecMatch("[I", "int"));
assertFalse(MethodTarget.typeSpecMatch("I", "Int"));
assertFalse(MethodTarget.typeSpecMatch("J", "int"));
}
@Test
public void innerClassTypeMatching() {
assertTrue(MethodTarget.typeSpecMatch("[Ljava/util/Map$Entry;", "java.util.Map$Entry[]"));
}
@Test
public void fullSpecMatch() {
TargetMatcher toLowerCase = new MethodTarget("java.lang.String", "toLowerCase", "java.lang.String");
TargetMatcher mapPut = new MethodTarget("java.util.Map", "put", "java.lang.Object", "java.lang.Object", "java.lang.Object");
TargetMatcher listAdd = new MethodTarget("java.util.List", "add", "boolean", "java.lang.Object");
TargetMatcher listToArray1 = new MethodTarget("java.util.ArrayList", "toArray", "java.lang.Object[]");
TargetMatcher listToArray2 = new MethodTarget("java.util.ArrayList", "toArray", "java.lang.Object[]", "java.lang.Object[]");
TargetMatcher threadSleep2 = new MethodTarget("java.lang.Thread", "sleep", "void", "long", "int");
assertTrue(toLowerCase.matches("java/lang/String", "toLowerCase", "()Ljava/lang/String;"));
assertTrue(mapPut.matches("java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"));
assertTrue(listAdd.matches("java/util/List", "add", "(Ljava/lang/Object;)Z"));
assertTrue(listToArray1.matches("java/util/ArrayList", "toArray", "()[Ljava/lang/Object;"));
assertTrue(listToArray2.matches("java/util/ArrayList", "toArray", "([Ljava/lang/Object;)[Ljava/lang/Object;"));
assertTrue(threadSleep2.matches("java/lang/Thread", "sleep", "(JI)V"));
assertFalse(threadSleep2.matches("java/lang/Thread", "sleep", "(J)V"));
assertFalse(threadSleep2.matches("java/lang/Thread", "sleep", "(JIJ)V"));
}
@Test
public void partialSpecMatch() {
TargetMatcher toLowerCase = new MethodTarget("java.lang.String", "toLowerCase");
TargetMatcher mapPut = new MethodTarget("java.util.Map", "put");
TargetMatcher listAdd = new MethodTarget("java.util.List", "add");
TargetMatcher listToArray = new MethodTarget("java.util.ArrayList", "toArray");
TargetMatcher threadSleep = new MethodTarget("java.lang.Thread", "sleep");
assertTrue(toLowerCase.matches("java/lang/String", "toLowerCase", "()Ljava/lang/String;"));
assertTrue(mapPut.matches("java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"));
assertTrue(listAdd.matches("java/util/List", "add", "(Ljava/lang/Object;)Z"));
assertTrue(listToArray.matches("java/util/ArrayList", "toArray", "()[Ljava/lang/Object;"));
assertTrue(listToArray.matches("java/util/ArrayList", "toArray", "([Ljava/lang/Object;)[Ljava/lang/Object;"));
assertTrue(threadSleep.matches("java/lang/Thread", "sleep", "(JI)V"));
assertTrue(threadSleep.matches("java/lang/Thread", "sleep", "(J)V"));
assertFalse(threadSleep.matches("java/lang/Thread", "sleep2", "(JI)V"));
assertFalse(threadSleep.matches("java/lang/Thread", "slee", "(JI)V"));
}
}
lombok.patcher-0.22/test/lombok/patcher/scripts/ 0000775 0000000 0000000 00000000000 13010176641 0021723 5 ustar 00root root 0000000 0000000 lombok.patcher-0.22/test/lombok/patcher/scripts/ScriptTestUtils.java 0000664 0000000 0000000 00000004220 13010176641 0025711 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009 The Project Lombok Authors.
*
* 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 lombok.patcher.scripts;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
class ScriptTestUtils {
static byte[] readFromStream(InputStream raw) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
byte[] b = new byte[4096];
while (true) {
int r = raw.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
return out.toByteArray();
} finally {
raw.close();
}
}
static Class> loadRaw(String className, byte[] classByteCode) throws ClassNotFoundException {
return Class.forName(className, true, getRawLoader(className, classByteCode));
}
static ClassLoader getRawLoader(final String className, final byte[] classByteCode) {
return new ClassLoader() {
@Override protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.equals(className)) {
return defineClass(name, classByteCode, 0, classByteCode.length);
}
return super.loadClass(name, resolve);
}
};
}
}
lombok.patcher-0.22/test/lombok/patcher/scripts/TestAddFieldScript.java 0000664 0000000 0000000 00000005542 13010176641 0026255 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009 The Project Lombok Authors.
*
* 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 lombok.patcher.scripts;
import static org.junit.Assert.*;
import static lombok.patcher.scripts.ScriptTestUtils.*;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import lombok.patcher.TransplantMapper;
import org.junit.Test;
public class TestAddFieldScript {
@Test
public void testAddFieldScript() throws Exception {
InputStream raw = TestAddFieldScript.class.getResourceAsStream("/lombok/patcher/scripts/TestAddFieldScriptEx1.class");
byte[] pretransform = readFromStream(raw);
byte[] posttransform = ScriptBuilder.addField()
.targetClass("lombok.patcher.scripts.TestAddFieldScriptEx1")
.setProtected().setStatic().fieldName("$test").fieldType("I").build()
.patch("lombok/patcher/scripts/TestAddFieldScriptEx1", pretransform, TransplantMapper.IDENTITY_MAPPER);
Class> ex1 = loadRaw("lombok.patcher.scripts.TestAddFieldScriptEx1", posttransform);
Method checkMethod = ex1.getMethod("check", String.class, int.class);
checkMethod.setAccessible(true);
assertTrue((Boolean)checkMethod.invoke(null, "$test", Modifier.STATIC | Modifier.PROTECTED));
boolean pass = false;
try {
checkMethod.invoke(null, "$test2", Modifier.STATIC | Modifier.PROTECTED);
} catch (InvocationTargetException expected) {
pass = expected.getCause() instanceof NoSuchFieldException;
}
if (!pass) fail("$test2 was never added and should thus have thrown a NoSuchFieldException.");
}
}
class TestAddFieldScriptEx1 {
int x;
public static boolean check(String fieldName, int modifiers) throws Exception {
Field f = TestAddFieldScriptEx1.class.getDeclaredField(fieldName);
return f.getModifiers() == modifiers;
}
}
lombok.patcher-0.22/test/lombok/patcher/scripts/TestExitFromMethodEarlyScript.java 0000664 0000000 0000000 00000011137 13010176641 0030511 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009 The Project Lombok Authors.
*
* 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 lombok.patcher.scripts;
import static lombok.patcher.scripts.ScriptTestUtils.*;
import static org.junit.Assert.*;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import lombok.patcher.Hook;
import lombok.patcher.MethodTarget;
import lombok.patcher.StackRequest;
import lombok.patcher.TransplantMapper;
import org.junit.Test;
public class TestExitFromMethodEarlyScript {
@Test
public void testExitEarlyScript() throws Exception {
InputStream raw = TestExitFromMethodEarlyScript.class.getResourceAsStream("/lombok/patcher/scripts/TestExitFromMethodEarlyScriptEx1.class");
byte[] pretransform = readFromStream(raw);
byte[] posttransform1 = ScriptBuilder.exitEarly()
.target(new MethodTarget("lombok.patcher.scripts.TestExitFromMethodEarlyScriptEx1", "voidReturnMethod"))
.decisionMethod(new Hook("lombok.patcher.scripts.TestExitFromMethodEarlyScript$TestExitFromMethodEarlyScriptEx2",
"hook1", "boolean", "java.lang.Object", "int", "java.lang.String"))
.request(StackRequest.THIS, StackRequest.PARAM1, StackRequest.PARAM2).build()
.patch("lombok/patcher/scripts/TestExitFromMethodEarlyScriptEx1", pretransform, TransplantMapper.IDENTITY_MAPPER);
byte[] posttransform2 = ScriptBuilder.exitEarly()
.target(new MethodTarget("lombok.patcher.scripts.TestExitFromMethodEarlyScriptEx1", "returnsSomething", "double"))
.decisionMethod(new Hook("lombok.patcher.scripts.TestExitFromMethodEarlyScript$TestExitFromMethodEarlyScriptEx2",
"hook2", "boolean"))
.valueMethod(new Hook("lombok.patcher.scripts.TestExitFromMethodEarlyScript$TestExitFromMethodEarlyScriptEx2",
"hook3", "double"))
.transplant().build()
.patch("lombok/patcher/scripts/TestExitFromMethodEarlyScriptEx1", posttransform1, TransplantMapper.IDENTITY_MAPPER);
Class> ex1 = loadRaw("lombok.patcher.scripts.TestExitFromMethodEarlyScriptEx1", posttransform2);
Method voidMethod = ex1.getMethod("voidReturnMethod", int.class, String.class);
Method retMethod = ex1.getMethod("returnsSomething");
Field markerField = ex1.getField("marker");
Constructor> ex1Constructor = ex1.getDeclaredConstructor();
voidMethod.setAccessible(true);
retMethod.setAccessible(true);
ex1Constructor.setAccessible(true);
markerField.setAccessible(true);
Object instance = ex1Constructor.newInstance();
assertFalse("marker-preinvoke", (Boolean)markerField.get(instance));
voidMethod.invoke(instance, 5, "foo");
assertFalse("marker-interinvoke", (Boolean)markerField.get(instance));
voidMethod.invoke(instance, 50, null);
assertTrue("marker-postinvoke", (Boolean)markerField.get(instance));
assertEquals("returnsSomething", Double.valueOf(Double.NaN), retMethod.invoke(instance));
}
@SuppressWarnings("all")
public static class TestExitFromMethodEarlyScriptEx2 {
public static boolean hook1(Object thisRef, int param1, String param2) {
assertEquals("typeOf thisRef", "lombok.patcher.scripts.TestExitFromMethodEarlyScriptEx1", thisRef.getClass().getName());
assertTrue("param1", param1 == 50 || param1 == 5);
return param1 == 5;
}
public static boolean hook2() {
return true;
}
public static double hook3() {
return Double.NaN;
}
}
}
@SuppressWarnings("all")
class TestExitFromMethodEarlyScriptEx1 {
public boolean marker = false;
public void voidReturnMethod(int a, String b) {
if (a < 10) fail("I shouldn't run");
marker = true;
}
public double returnsSomething() {
return 5.0;
}
}
lombok.patcher-0.22/test/lombok/patcher/scripts/TestSymbols.java 0000664 0000000 0000000 00000006057 13010176641 0025066 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009 The Project Lombok Authors.
*
* 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 lombok.patcher.scripts;
import static lombok.patcher.scripts.ScriptTestUtils.*;
import static junit.framework.Assert.*;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import lombok.patcher.Hook;
import lombok.patcher.MethodTarget;
import lombok.patcher.Symbols;
import lombok.patcher.TransplantMapper;
import org.junit.Test;
public class TestSymbols {
@Test
public void testSymbols() throws Throwable {
InputStream raw = TestSymbols.class.getResourceAsStream("/lombok/patcher/scripts/TestSymbolsEx1.class");
byte[] pretransform = readFromStream(raw);
byte[] posttransform = ScriptBuilder.setSymbolDuringMethodCall()
.target(new MethodTarget("lombok.patcher.scripts.TestSymbolsEx1", "aMethod"))
.callToWrap(new Hook("lombok.patcher.scripts.TestSymbolsEx1", "bMethod", "void"))
.symbol("Foobar").build().patch("lombok/patcher/scripts/TestSymbolsEx1", pretransform, TransplantMapper.IDENTITY_MAPPER);
Class> ex1 = loadRaw("lombok.patcher.scripts.TestSymbolsEx1", posttransform);
Method aMethod = ex1.getMethod("aMethod");
Constructor> ex1Constructor = ex1.getDeclaredConstructor();
aMethod.setAccessible(true);
ex1Constructor.setAccessible(true);
Object instance = ex1Constructor.newInstance();
assertTrue("marker-preinvoke", Symbols.isEmpty());
try {
aMethod.invoke(instance);
} catch (InvocationTargetException e) {
throw e.getCause();
}
assertTrue("marker-postinvoke", Symbols.isEmpty());
}
}
class TestSymbolsEx1 {
public boolean marker = false;
public void aMethod() {
cMethod();
bMethod();
}
public void cMethod() {
assertFalse(Symbols.hasSymbol("Foobar"));
assertFalse(Symbols.hasTail("Foobar"));
assertEquals(0, Symbols.size());
}
public void bMethod() {
assertTrue(Symbols.hasSymbol("Foobar"));
assertTrue(Symbols.hasTail("Foobar"));
assertEquals(1, Symbols.size());
}
}
lombok.patcher-0.22/test/lombok/patcher/scripts/TestWrapReturnValuesScript.java 0000664 0000000 0000000 00000007227 13010176641 0030114 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009 The Project Lombok Authors.
*
* 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 lombok.patcher.scripts;
import static lombok.patcher.scripts.ScriptTestUtils.*;
import static org.junit.Assert.*;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import lombok.patcher.Hook;
import lombok.patcher.MethodTarget;
import lombok.patcher.StackRequest;
import lombok.patcher.TransplantMapper;
import org.junit.Test;
public class TestWrapReturnValuesScript {
@Test
public void testWrapReturnValuesScript() throws Exception {
InputStream raw = TestWrapReturnValuesScript.class.getResourceAsStream("/lombok/patcher/scripts/TestWrapReturnValuesScriptEx1.class");
byte[] pretransform = readFromStream(raw);
byte[] posttransform = ScriptBuilder.wrapReturnValue()
.target(new MethodTarget("lombok.patcher.scripts.TestWrapReturnValuesScriptEx1", "foo",
"int", "int", "java.lang.String[]"))
.wrapMethod(new Hook("lombok.patcher.scripts.TestWrapReturnValuesScript$TestWrapReturnValuesScriptEx2",
"hook1", "int", "int", "java.lang.Object", "int", "java.lang.String[]"))
.transplant().request(StackRequest.THIS, StackRequest.RETURN_VALUE, StackRequest.PARAM1, StackRequest.PARAM2)
.build().patch("lombok/patcher/scripts/TestWrapReturnValuesScriptEx1", pretransform, TransplantMapper.IDENTITY_MAPPER);
Class> ex1 = loadRaw("lombok.patcher.scripts.TestWrapReturnValuesScriptEx1", posttransform);
Method fooMethod = ex1.getMethod("foo", int.class, String[].class);
Constructor> ex1Constructor = ex1.getDeclaredConstructor();
fooMethod.setAccessible(true);
ex1Constructor.setAccessible(true);
assertEquals("patched return value", 160, (int)(Integer)fooMethod.invoke(ex1Constructor.newInstance(), 50, new String[] { "foo", "bar"}));
assertEquals("patched return value", 20, (int)(Integer)fooMethod.invoke(ex1Constructor.newInstance(), 5, null));
}
public static class TestWrapReturnValuesScriptEx2 {
public static int hook1(int supposedReturnValue, Object thisRef, int param1, String[] param2) {
assertEquals("supposedReturnValue", param1 < 10 ? 10 : 80, supposedReturnValue);
assertEquals("typeOf thisRef", "lombok.patcher.scripts.TestWrapReturnValuesScriptEx1", thisRef.getClass().getName());
assertTrue("param1", param1 == 50 || param1 == 5);
if (param1 == 50)
assertArrayEquals("param2", new String[] { "foo", "bar" }, param2);
else
assertNull("param2", param2);
return supposedReturnValue *2;
}
}
}
@SuppressWarnings("all")
class TestWrapReturnValuesScriptEx1 {
public int foo(int x, String[] y) {
if (x < 10) return 10;
return 80;
}
}