pax_global_header 0000666 0000000 0000000 00000000064 13574020516 0014515 g ustar 00root root 0000000 0000000 52 comment=f214433b2c5ab18e8da516d74aaa8010978194c2
lombok.patcher-0.36/ 0000775 0000000 0000000 00000000000 13574020516 0014355 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/.gitignore 0000664 0000000 0000000 00000000124 13574020516 0016342 0 ustar 00root root 0000000 0000000 .classpath
.project
.settings
bin
build
dist
escudo-upload.key
lib
contrib
ivyCache
lombok.patcher-0.36/README.markdown 0000664 0000000 0000000 00000001256 13574020516 0017062 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](https://github.com/rzwitserloot/lombok/tree/master/src/eclipseAgent/lombok/eclipse/agent/) lombok.patcher-0.36/build.xml 0000664 0000000 0000000 00000020642 13574020516 0016202 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}
automated uploading and deployment temporarily disabled. Upload dist/lombok.patcher-${lombok.patcher.version}.jar to the server and deploy manually.
lombok.patcher-0.36/buildScripts/ 0000775 0000000 0000000 00000000000 13574020516 0017024 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/buildScripts/ivy-repo/ 0000775 0000000 0000000 00000000000 13574020516 0020576 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/buildScripts/ivy-repo/projectlombok.org-jsch-ant-fixed-0.1.45.xml 0000664 0000000 0000000 00000000734 13574020516 0030311 0 ustar 00root root 0000000 0000000
lombok.patcher-0.36/buildScripts/ivy.xml 0000664 0000000 0000000 00000001764 13574020516 0020365 0 ustar 00root root 0000000 0000000
lombok.patcher-0.36/buildScripts/ivysettings.xml 0000664 0000000 0000000 00000000662 13574020516 0022142 0 ustar 00root root 0000000 0000000
lombok.patcher-0.36/propagateDistToLombok 0000775 0000000 0000000 00000001465 13574020516 0020566 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.36/runAsm 0000775 0000000 0000000 00000001147 13574020516 0015553 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.36/src/ 0000775 0000000 0000000 00000000000 13574020516 0015144 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/src/injector/ 0000775 0000000 0000000 00000000000 13574020516 0016761 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/src/injector/lombok/ 0000775 0000000 0000000 00000000000 13574020516 0020244 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/src/injector/lombok/patcher/ 0000775 0000000 0000000 00000000000 13574020516 0021672 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/src/injector/lombok/patcher/inject/ 0000775 0000000 0000000 00000000000 13574020516 0023146 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/src/injector/lombok/patcher/inject/LiveInjector.java 0000664 0000000 0000000 00000014101 13574020516 0026403 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.36/src/patcher/ 0000775 0000000 0000000 00000000000 13574020516 0016572 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/src/patcher/lombok/ 0000775 0000000 0000000 00000000000 13574020516 0020055 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/src/patcher/lombok/patcher/ 0000775 0000000 0000000 00000000000 13574020516 0021503 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/src/patcher/lombok/patcher/ClassRootFinder.java 0000664 0000000 0000000 00000006330 13574020516 0025411 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.36/src/patcher/lombok/patcher/Filter.java 0000664 0000000 0000000 00000003101 13574020516 0023566 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.36/src/patcher/lombok/patcher/Hook.java 0000664 0000000 0000000 00000012757 13574020516 0023262 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2017 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;
/**
* 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
*/
public class Hook {
private final String className;
private final String methodName;
private final String returnType;
private final List parameterTypes;
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 boolean isConstructor() {
return "".equals(methodName);
}
public String getClassName() {
return className;
}
public String getMethodName() {
return methodName;
}
public String getReturnType() {
return returnType;
}
public List getParameterTypes() {
return parameterTypes;
}
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();
}
@Override public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((className == null) ? 0 : className.hashCode());
result = prime * result + ((methodName == null) ? 0 : methodName.hashCode());
result = prime * result + ((parameterTypes == null) ? 0 : parameterTypes.hashCode());
result = prime * result + ((returnType == null) ? 0 : returnType.hashCode());
return result;
}
@Override public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Hook other = (Hook) obj;
if (className == null) {
if (other.className != null) return false;
} else if (!className.equals(other.className)) return false;
if (methodName == null) {
if (other.methodName != null) return false;
} else if (!methodName.equals(other.methodName)) return false;
if (parameterTypes == null) {
if (other.parameterTypes != null) return false;
} else if (!parameterTypes.equals(other.parameterTypes)) return false;
if (returnType == null) {
if (other.returnType != null) return false;
} else if (!returnType.equals(other.returnType)) return false;
return true;
}
@Override public String toString() {
return "Hook [className=" + className + ", methodName=" + methodName + ", returnType=" + returnType + ", parameterTypes=" + parameterTypes + "]";
}
}
lombok.patcher-0.36/src/patcher/lombok/patcher/MethodLogistics.java 0000664 0000000 0000000 00000015554 13574020516 0025461 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2017 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 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;
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();
}
public int getReturnOpcode() {
return returnOpcode;
}
/**
* 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.36/src/patcher/lombok/patcher/MethodTarget.java 0000664 0000000 0000000 00000023634 13574020516 0024745 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2017 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;
/**
* 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.
*/
public final class MethodTarget implements TargetMatcher {
private final String classSpec;
private final String methodName;
private final String returnSpec;
private final List parameterSpec;
private boolean hasDescription;
public String getClassSpec() {
return classSpec;
}
public String getMethodName() {
return methodName;
}
public String getReturnSpec() {
return returnSpec;
}
public List getParameterSpec() {
return parameterSpec;
}
public boolean isHasDescription() {
return 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);
}
@Override public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((classSpec == null) ? 0 : classSpec.hashCode());
result = prime * result + (hasDescription ? 1231 : 1237);
result = prime * result + ((methodName == null) ? 0 : methodName.hashCode());
result = prime * result + ((parameterSpec == null) ? 0 : parameterSpec.hashCode());
result = prime * result + ((returnSpec == null) ? 0 : returnSpec.hashCode());
return result;
}
@Override public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
MethodTarget other = (MethodTarget) obj;
if (classSpec == null) {
if (other.classSpec != null) return false;
} else if (!classSpec.equals(other.classSpec)) return false;
if (hasDescription != other.hasDescription) return false;
if (methodName == null) {
if (other.methodName != null) return false;
} else if (!methodName.equals(other.methodName)) return false;
if (parameterSpec == null) {
if (other.parameterSpec != null) return false;
} else if (!parameterSpec.equals(other.parameterSpec)) return false;
if (returnSpec == null) {
if (other.returnSpec != null) return false;
} else if (!returnSpec.equals(other.returnSpec)) return false;
return true;
}
@Override public String toString() {
return "MethodTarget[classSpec=" + classSpec + ", methodName=" + methodName + ", returnSpec=" + returnSpec + ", parameterSpec=" + parameterSpec + ", hasDescription=" + hasDescription + "]";
}
}
lombok.patcher-0.36/src/patcher/lombok/patcher/PatchScript.java 0000664 0000000 0000000 00000030664 13574020516 0024603 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2017 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.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
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) {
InputStream wrapStream = null;
try {
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);
} finally {
if (wrapStream != null) try {
wrapStream.close();
} catch (IOException ignore) {}
}
}
private static abstract class NoopClassVisitor extends ClassVisitor {
public NoopClassVisitor() {
super(Opcodes.ASM7);
}
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.ASM7, 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 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.ASM7, cv);
this.factory = factory;
this.transplantMapper = transplantMapper;
}
public String getOwnClassSpec() {
return ownClassSpec;
}
/**
* 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.36/src/patcher/lombok/patcher/ScriptManager.java 0000664 0000000 0000000 00000021267 13574020516 0025115 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.36/src/patcher/lombok/patcher/StackRequest.java 0000664 0000000 0000000 00000004011 13574020516 0024760 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2017 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;
/**
* 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);
private final int paramPos;
StackRequest(int paramPos) {
this.paramPos = paramPos;
}
public int getParamPos() {
return paramPos;
}
public static final List PARAMS_IN_ORDER = Collections.unmodifiableList(Arrays.asList(
PARAM1, PARAM2, PARAM3, PARAM4, PARAM5, PARAM6));
}
lombok.patcher-0.36/src/patcher/lombok/patcher/Symbols.java 0000664 0000000 0000000 00000005677 13574020516 0024015 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.36/src/patcher/lombok/patcher/TargetMatcher.java 0000664 0000000 0000000 00000003545 13574020516 0025107 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.36/src/patcher/lombok/patcher/TransplantMapper.java 0000664 0000000 0000000 00000004222 13574020516 0025641 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.36/src/patcher/lombok/patcher/Version.java 0000664 0000000 0000000 00000003315 13574020516 0023775 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2019 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.36";
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.36/src/patcher/lombok/patcher/scripts/ 0000775 0000000 0000000 00000000000 13574020516 0023172 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/src/patcher/lombok/patcher/scripts/AddFieldScript.java 0000664 0000000 0000000 00000006705 13574020516 0026666 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.ASM7, 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.36/src/patcher/lombok/patcher/scripts/ExitFromMethodEarlyScript.java 0000664 0000000 0000000 00000014303 13574020516 0031116 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.ASM7, 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.36/src/patcher/lombok/patcher/scripts/MethodLevelPatchScript.java 0000664 0000000 0000000 00000005162 13574020516 0030416 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.36/src/patcher/lombok/patcher/scripts/ReplaceMethodCallScript.java 0000664 0000000 0000000 00000010277 13574020516 0030541 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.ASM7, 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.36/src/patcher/lombok/patcher/scripts/ScriptBuilder.java 0000664 0000000 0000000 00000047370 13574020516 0026623 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2019 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;
/**
* Defines scripts to modify bytecode directly.
*
* Some definitions:
*
* transplant
*
* Transplant means that the code you are trying to inject into another class (generally called the {@code hook}) is copied
* into the target class. Specifically, you identify a method (the hook) that contains the code that must run as part of the
* method targeted for manipulation (the target). Transplantation means that the entire hook method, signature and all, is copied
* straight into the target class. You should definitely make the hook static unless you really know what you are doing.
*
* - Advantage: Your hook is loaded by the same classloader as the target, which helps a lot with classloader issues
* - Advantage: Whilst you'd have to write it reflectively otherwise your hook can't be compiled, you still get free, unfettered access even to private members of the target
* - Disadvantage: Because your hook method is moved, you cannot reference anything else from the surroundings of where your hook method lives. Don't make helper methods!
*
*
* insert
*
* Insert is like transplant but even more aggressive: The actual bytecode is dropped straight into the relevant place inside the target
* method. insertion only works for extremely simple methods, because no effort is made to ensure that the target location's local space
* and/or framesizes are sufficient, and in any case any usage of local slots by your injected code would simply overwrite. Generally,
* for one-liners, especially if the one-liner is to replace something with a constant or wrap it with a single method call, you can use it.
*
* cast
*
* This lets you have the method that contains the code you wish to inject simply return {@code java.lang.Object}; this value will be
* casted to the required type in the targeted method.
*/
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;
}
/**
* @param value The value the field should be initialized to. Has to be a constant value of the appropriate type. Optional;
* if skipped, you get the default 0/false/null.
*/
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);
}
/**
* The method that you want to modify.
*/
public ExitEarlyBuilder target(TargetMatcher matcher) {
this.matchers.add(matcher);
return this;
}
/**
* The code to be used to decide whether or not a given call should return immediately. If omitted, it's as if you hook
* in the code: {@code return true;}. {@code hook} must be a static method with a {@code boolean} return type.
*/
public ExitEarlyBuilder decisionMethod(Hook hook) {
this.decisionMethod = hook;
return this;
}
/**
* The code to be used to decide the return value. Cannot be specified if {@code target} is a method that returns {@code void},
* otherwise, mandatory. {@code hook} must be a static method with the same return value as {@code target}.
*/
public ExitEarlyBuilder valueMethod(Hook hook) {
this.valueMethod = hook;
return this;
}
/**
* See {@link ScriptBuilder} javadoc for details.
* Applies to both the {@code valueMethod} and the {@code decisionMethod}.
*/
public ExitEarlyBuilder transplant() {
this.transplant = true;
this.insert = false;
return this;
}
/**
* See {@link ScriptBuilder} javadoc for details.
* Applies to both the {@code valueMethod} and the {@code decisionMethod}.
*/
public ExitEarlyBuilder insert() {
this.transplant = false;
this.insert = true;
return this;
}
/**
* Defines the parameter(s) of your decision and value methods.
*/
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);
}
/**
* The method that you want to modify.
*/
public ReplaceMethodCallBuilder target(TargetMatcher matcher) {
this.matchers.add(matcher);
return this;
}
/**
* The method you want to modify is scanned for any calls to {@code methodToReplace}; such calls are then replaced to be
* calls to this method instead. The {@code hook} should be static; its first argument should be the type of receiver
* and then all parameters of the {@code methodToReplace}, and then any further arguments you ask for via {@code requestExtra}.
*
* Example:
* You target instance method {@code bar} in class {@code Foo}.
* Your methodToReplace is: java.lang.String charAt(int).
* You added {@code StackRequest.THIS} via requestExtra.
* Your replacement method signature should be: {@code public static char replacementCharAt(String receiver, int param1, Foo caller)}.
*/
public ReplaceMethodCallBuilder replacementMethod(Hook hook) {
this.replacementMethod = hook;
return this;
}
/**
* The method you want to modify is scanned for any calls to this specific method; such calls are then replaced to be
* calls to {@code replacementMethod}.
*/
public ReplaceMethodCallBuilder methodToReplace(Hook hook) {
this.methodToReplace = hook;
return this;
}
/**
* See {@link ScriptBuilder} javadoc for details.
* Applies to {@code replacementMethod}.
*/
public ReplaceMethodCallBuilder transplant() {
this.transplant = true;
this.insert = false;
return this;
}
/**
* See {@link ScriptBuilder} javadoc for details.
* Applies to {@code replacementMethod}.
*/
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;
}
}
/**
* This script scans a target method (The target) for calls to a certain method (The methodToWrap
) and
* adds a call to a third method (The wrapMethod
), which can inspect and even modify the value.
*/
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);
}
/**
* The method that you want to modify.
*/
public WrapMethodCallBuilder target(TargetMatcher matcher) {
this.matchers.add(matcher);
return this;
}
/**
* The method you want to modify is scanned for any calls to {@code methodToWrap}; a call to {@code wrapMethod}
* is injected immediately after every call to {@code methodToWrap}.
* The {@code hook} should be static; the returntype of the method you are wrapping should be replicated both as
* the hook's return type and as the type of its first argument. The hook should then have any further arguments
* you ask for via {@code requestExtra}.
*
* Example:
* You target instance method {@code bar} in class {@code Foo}.
* Your methodToWrap is: java.lang.String charAt(int).
* You added {@code StackRequest.THIS} via requestExtra.
* Your wrap method signature should be: {@code public static char wrapCharAt(char result, Foo caller)}.
*/
public WrapMethodCallBuilder wrapMethod(Hook hook) {
this.wrapMethod = hook;
return this;
}
/**
* The method you want to modify is scanned for any calls to this specific method; calls to {@code wrapMethod}
* are injected immediately following any calls to this method.
*/
public WrapMethodCallBuilder methodToWrap(Hook hook) {
this.methodToWrap = hook;
return this;
}
/**
* See {@link ScriptBuilder} javadoc for details.
* Applies to {@code wrapMethod}.
*/
public WrapMethodCallBuilder transplant() {
this.transplant = true;
this.insert = false;
return this;
}
/**
* See {@link ScriptBuilder} javadoc for details.
* Applies to {@code wrapMethod}.
*/
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;
}
}
/**
* This script lets you inspect (and replace) the returned value for any target method.
*/
public static class WrapReturnValueBuilder {
private List matchers = new ArrayList();
private Hook wrapMethod;
private Set requests = new HashSet();
private boolean transplant, insert, cast;
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, cast, requests);
}
/**
* The method that you want to modify.
*/
public WrapReturnValueBuilder target(TargetMatcher matcher) {
this.matchers.add(matcher);
return this;
}
/**
* All attempts to return in the {@code target} will be preceded by a call to this {@code hook} immediately prior to the return.
*/
public WrapReturnValueBuilder wrapMethod(Hook hook) {
this.wrapMethod = hook;
return this;
}
/**
* See {@link ScriptBuilder} javadoc for details.
* Applies to {@code wrapMethod}.
*/
public WrapReturnValueBuilder transplant() {
this.transplant = true;
this.insert = false;
return this;
}
/**
* See {@link ScriptBuilder} javadoc for details.
* Applies to {@code wrapMethod}.
*/
public WrapReturnValueBuilder insert() {
if (this.cast) throw new IllegalArgumentException("cast and insert are mutually exlusive");
this.transplant = false;
this.insert = true;
return this;
}
/**
* See {@link ScriptBuilder} javadoc for details.
* Applies to {@code wrapMethod}.
*/
public WrapReturnValueBuilder cast() {
if (this.insert) throw new IllegalArgumentException("insert and cast are mutually exlusive");
this.cast = 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();
}
/**
* This script lets you modify a target method to return immediately upon being invoked; this can also be used to replace
* entirely what it does.
* Your can control:
* (optional) Have a 'decider' method which decides whether or not a call to the target should return immediately.
* (mandatory) The value to be returned. For void methods this is irrelevant, of course.
*/
public static ExitEarlyBuilder exitEarly() {
return new ExitEarlyBuilder();
}
/**
* This script scans a target method (The target) for calls to a certain method (The methodToReplace
) and
* replaces these calls with a different call; usually the replacement is your own creation.
*/
public static ReplaceMethodCallBuilder replaceMethodCall() {
return new ReplaceMethodCallBuilder();
}
/**
* This script scans a target method (The target) for calls to a certain method (The methodToWrap
) and
* adds a call to a third method (The wrapMethod
), which can inspect and even modify the value.
*/
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.36/src/patcher/lombok/patcher/scripts/SetSymbolDuringMethodCallScript.java 0000664 0000000 0000000 00000013727 13574020516 0032263 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.ASM7, 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.36/src/patcher/lombok/patcher/scripts/WrapMethodCallScript.java 0000664 0000000 0000000 00000011560 13574020516 0030073 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.ASM7, 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.36/src/patcher/lombok/patcher/scripts/WrapReturnValuesScript.java 0000664 0000000 0000000 00000012653 13574020516 0030522 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2019 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;
/**
* Will find every 'return' instruction in the target method and will insert right before it a call to the wrapper.
*/
public final class WrapReturnValuesScript extends MethodLevelPatchScript {
private final Hook wrapper;
private final Set requests;
private final boolean hijackReturnValue;
private final boolean transplant, insert, cast;
/**
* @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, boolean cast, 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;
this.cast = cast && hijackReturnValue;
assert !(insert && transplant);
assert !(cast && insert);
}
@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, desc);
}
});
if (transplant) patcher.addTransplant(wrapper);
return patcher;
}
private static String extractReturnValueFromDesc(String desc) {
int lastIdx = desc == null ? -1 : desc.lastIndexOf(')');
if (lastIdx == -1) return null;
String rd = desc.substring(lastIdx + 1);
if (rd.startsWith("L") && rd.endsWith(";")) return rd.substring(1, rd.length() - 1);
return rd;
}
private class WrapReturnValues extends MethodVisitor {
private final MethodLogistics logistics;
private final String ownClassSpec;
private final String returnValueDesc;
public WrapReturnValues(MethodVisitor mv, MethodLogistics logistics, String ownClassSpec, String desc) {
super(Opcodes.ASM7, mv);
this.logistics = logistics;
this.ownClassSpec = ownClassSpec;
this.returnValueDesc = extractReturnValueFromDesc(desc);
}
@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);
}
if (cast) super.visitTypeInsn(Opcodes.CHECKCAST, returnValueDesc);
super.visitInsn(opcode);
}
}
@Override public String toString() {
return "WrapReturnValues(wrapper: " + wrapper + ", hijackReturn: " + hijackReturnValue + ", transplant: " + transplant + ", insert: " + insert + ", requests: " + requests + ")";
}
}
lombok.patcher-0.36/src/patcher/lombok/patcher/scripts/WrapperMethodDescriptor.java 0000664 0000000 0000000 00000007651 13574020516 0030666 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2017 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;
public class WrapperMethodDescriptor {
private final int count;
private final int opcode;
private final String owner;
private final String name;
private final String wrapperDescriptor;
private final String targetDescriptor;
private final boolean itf; // interface
public WrapperMethodDescriptor(int count, int opcode, String owner, String name, String wrapperDescriptor, String targetDescriptor, boolean itf) {
this.count = count;
this.opcode = opcode;
this.owner = owner;
this.name = name;
this.wrapperDescriptor = wrapperDescriptor;
this.targetDescriptor = targetDescriptor;
this.itf = itf;
}
public int getCount() {
return count;
}
public int getOpcode() {
return opcode;
}
public String getOwner() {
return owner;
}
public String getName() {
return name;
}
public String getWrapperDescriptor() {
return wrapperDescriptor;
}
public String getTargetDescriptor() {
return targetDescriptor;
}
public boolean isItf() {
return itf;
}
public String getWrapperName() {
return "$lombok$$wrapper$" + count + "$" + name;
}
@Override public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + count;
result = prime * result + (itf ? 1231 : 1237);
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + opcode;
result = prime * result + ((owner == null) ? 0 : owner.hashCode());
result = prime * result + ((targetDescriptor == null) ? 0 : targetDescriptor.hashCode());
result = prime * result + ((wrapperDescriptor == null) ? 0 : wrapperDescriptor.hashCode());
return result;
}
@Override public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
WrapperMethodDescriptor other = (WrapperMethodDescriptor) obj;
if (count != other.count) return false;
if (itf != other.itf) return false;
if (name == null) {
if (other.name != null) return false;
} else if (!name.equals(other.name)) return false;
if (opcode != other.opcode) return false;
if (owner == null) {
if (other.owner != null) return false;
} else if (!owner.equals(other.owner)) return false;
if (targetDescriptor == null) {
if (other.targetDescriptor != null) return false;
} else if (!targetDescriptor.equals(other.targetDescriptor)) return false;
if (wrapperDescriptor == null) {
if (other.wrapperDescriptor != null) return false;
} else if (!wrapperDescriptor.equals(other.wrapperDescriptor)) return false;
return true;
}
@Override public String toString() {
return "WrapperMethodDescriptor[count=" + count + ", opcode=" + opcode + ", owner=" + owner + ", name=" + name + ", wrapperDescriptor=" + wrapperDescriptor + ", targetDescriptor=" + targetDescriptor + ", itf=" + itf + "]";
}
}
lombok.patcher-0.36/test/ 0000775 0000000 0000000 00000000000 13574020516 0015334 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/test/lombok/ 0000775 0000000 0000000 00000000000 13574020516 0016617 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/test/lombok/patcher/ 0000775 0000000 0000000 00000000000 13574020516 0020245 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/test/lombok/patcher/TestMethodTarget.java 0000664 0000000 0000000 00000012441 13574020516 0024341 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.36/test/lombok/patcher/scripts/ 0000775 0000000 0000000 00000000000 13574020516 0021734 5 ustar 00root root 0000000 0000000 lombok.patcher-0.36/test/lombok/patcher/scripts/ScriptTestUtils.java 0000664 0000000 0000000 00000004220 13574020516 0025722 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.36/test/lombok/patcher/scripts/TestAddFieldScript.java 0000664 0000000 0000000 00000005542 13574020516 0026266 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.36/test/lombok/patcher/scripts/TestExitFromMethodEarlyScript.java 0000664 0000000 0000000 00000011137 13574020516 0030522 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.36/test/lombok/patcher/scripts/TestSymbols.java 0000664 0000000 0000000 00000006051 13574020516 0025071 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.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.36/test/lombok/patcher/scripts/TestWrapReturnValuesScript.java 0000664 0000000 0000000 00000012222 13574020516 0030114 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2009-2019 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));
}
@Test
public void testWrapReturnWithCast() 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", "bar",
"java.lang.String[]"))
.wrapMethod(new Hook("lombok.patcher.scripts.TestWrapReturnValuesScript$TestWrapReturnValuesScriptEx2",
"hook2", "java.lang.Object", "java.lang.Object"))
.cast().request(StackRequest.RETURN_VALUE)
.build().patch("lombok/patcher/scripts/TestWrapReturnValuesScriptEx1", pretransform, TransplantMapper.IDENTITY_MAPPER);
Class> ex1 = loadRaw("lombok.patcher.scripts.TestWrapReturnValuesScriptEx1", posttransform);
Method barMethod = ex1.getMethod("bar");
Constructor> ex1Constructor = ex1.getDeclaredConstructor();
barMethod.setAccessible(true);
ex1Constructor.setAccessible(true);
assertArrayEquals("patched return value", new String[] {"B"}, (String[]) barMethod.invoke(ex1Constructor.newInstance()));
}
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;
}
public static Object hook2(Object supposedReturnValue) {
assertEquals("supposedReturnValue-type", String[].class, supposedReturnValue.getClass());
String[] srv = (String[]) supposedReturnValue;
assertArrayEquals("supposedReturnValue", new String[] {"A"}, srv);
return new String[] {"B"};
}
}
}
@SuppressWarnings("all")
class TestWrapReturnValuesScriptEx1 {
public int foo(int x, String[] y) {
if (x < 10) return 10;
return 80;
}
public String[] bar() {
return new String[] {"A"};
}
}