From 4fcd221d0121ff93f339e79209e8a2ce9e46af00 Mon Sep 17 00:00:00 2001 From: tldyl <756560020@qq.com> Date: Wed, 23 Nov 2022 00:15:22 +0800 Subject: [PATCH 1/4] Add SpireMethod patch in ClassPatchInfo for more convenient to adding methods to classes. --- .../modthespire/lib/SpireMethod.java | 16 +++++++ .../modthespire/patcher/ClassPatchInfo.java | 46 ++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireMethod.java diff --git a/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireMethod.java b/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireMethod.java new file mode 100644 index 00000000..59db4b59 --- /dev/null +++ b/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireMethod.java @@ -0,0 +1,16 @@ +package com.evacipated.cardcrawl.modthespire.lib; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface SpireMethod { + String onConflict() default "Postfix"; + + String POSTFIX = "Postfix"; + + String PREFIX = "Prefix"; +} diff --git a/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java b/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java index 23a5ca96..5fe81fb9 100644 --- a/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java +++ b/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java @@ -2,6 +2,7 @@ import com.evacipated.cardcrawl.modthespire.Loader; import com.evacipated.cardcrawl.modthespire.lib.SpireField; +import com.evacipated.cardcrawl.modthespire.lib.SpireMethod; import com.evacipated.cardcrawl.modthespire.lib.StaticSpireField; import javassist.*; import javassist.bytecode.*; @@ -12,6 +13,9 @@ import javassist.expr.NewExpr; import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -21,6 +25,8 @@ public class ClassPatchInfo extends PatchInfo private CtClass ctPatchClass; private CtClass ctClassToPatch; + private static final Map methodCache = new HashMap<>(); + public ClassPatchInfo(CtClass ctClassToPatch, CtClass ctPatchClass) { super(null, null); @@ -208,10 +214,48 @@ public void doPatch() throws PatchingException } } } + for (CtMethod m : ctPatchClass.getDeclaredMethods()) { + if (m.hasAnnotation(SpireMethod.class)) { + CtClass[] params = m.getParameterTypes(); + SpireMethod anno = (SpireMethod) m.getAnnotation(SpireMethod.class); + StringBuilder sb = new StringBuilder(); + for (int i=1;i 0) { + ctClassToPatch.setModifiers(ctClassToPatch.getModifiers() - Modifier.ABSTRACT); + } + } catch (DuplicateMemberException duplicateMemberException) { + ctMethod = methodCache.get(Descriptor.ofMethod(m.getReturnType(), Arrays.copyOfRange(params, 1, params.length))); + System.out.println(Descriptor.ofMethod(m.getReturnType(), Arrays.copyOfRange(params, 1, params.length))); + if (SpireMethod.POSTFIX.equals(anno.onConflict())) { + ctMethod.insertAfter(ctPatchClass.getName() + "." + m.getName() + "($0" + sb + ");"); + } else { + ctMethod.insertBefore(ctPatchClass.getName() + "." + m.getName() + "($0" + sb + ");"); + } + } + } + } if (Loader.DEBUG) { System.out.println(); } - } catch (CannotCompileException | NotFoundException e) { + } catch (CannotCompileException | NotFoundException | ClassNotFoundException e) { throw new PatchingException(e); } } From 588964f451020a25b5e1f22bfc7e503d478969bc Mon Sep 17 00:00:00 2001 From: tldyl <756560020@qq.com> Date: Wed, 23 Nov 2022 14:55:45 +0800 Subject: [PATCH 2/4] Now you can add methods with return values. It has not been tested yet. I will test it later. --- .../modthespire/lib/SpireMethod.java | 2 + .../modthespire/patcher/ClassPatchInfo.java | 72 +++++++++++++++---- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireMethod.java b/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireMethod.java index 59db4b59..b736522f 100644 --- a/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireMethod.java +++ b/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireMethod.java @@ -10,6 +10,8 @@ public @interface SpireMethod { String onConflict() default "Postfix"; + Class returnType(); + String POSTFIX = "Postfix"; String PREFIX = "Prefix"; diff --git a/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java b/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java index 5fe81fb9..52d75db6 100644 --- a/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java +++ b/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java @@ -1,6 +1,7 @@ package com.evacipated.cardcrawl.modthespire.patcher; import com.evacipated.cardcrawl.modthespire.Loader; +import com.evacipated.cardcrawl.modthespire.lib.LineFinder; import com.evacipated.cardcrawl.modthespire.lib.SpireField; import com.evacipated.cardcrawl.modthespire.lib.SpireMethod; import com.evacipated.cardcrawl.modthespire.lib.StaticSpireField; @@ -13,10 +14,7 @@ import javassist.expr.NewExpr; import java.lang.reflect.Proxy; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -218,9 +216,13 @@ public void doPatch() throws PatchingException if (m.hasAnnotation(SpireMethod.class)) { CtClass[] params = m.getParameterTypes(); SpireMethod anno = (SpireMethod) m.getAnnotation(SpireMethod.class); + boolean hasReturnValue = !anno.returnType().equals(void.class); StringBuilder sb = new StringBuilder(); for (int i=1;i 0) { ctClassToPatch.setModifiers(ctClassToPatch.getModifiers() - Modifier.ABSTRACT); } } catch (DuplicateMemberException duplicateMemberException) { - ctMethod = methodCache.get(Descriptor.ofMethod(m.getReturnType(), Arrays.copyOfRange(params, 1, params.length))); - System.out.println(Descriptor.ofMethod(m.getReturnType(), Arrays.copyOfRange(params, 1, params.length))); + newMethod = methodCache.get(Descriptor.ofMethod(m.getReturnType(), Arrays.copyOfRange(params, hasReturnValue ? 2 : 1, params.length))); if (SpireMethod.POSTFIX.equals(anno.onConflict())) { - ctMethod.insertAfter(ctPatchClass.getName() + "." + m.getName() + "($0" + sb + ");"); + com.evacipated.cardcrawl.modthespire.lib.Matcher matcher = new com.evacipated.cardcrawl.modthespire.lib.Matcher.TypeCastMatcher(returnType.getName()); + int[] lineNum = LineFinder.findInOrder(newMethod, new ArrayList<>(), matcher); + newMethod.insertAt(lineNum[0], methodBody); } else { - ctMethod.insertBefore(ctPatchClass.getName() + "." + m.getName() + "($0" + sb + ");"); + newMethod.insertAt(1, methodBody); } } } From 59ba155ba6d664a127c984a47fab775503c28184 Mon Sep 17 00:00:00 2001 From: tldyl <756560020@qq.com> Date: Wed, 23 Nov 2022 20:50:14 +0800 Subject: [PATCH 3/4] Fixed runtime errors. It now works. --- .../modthespire/patcher/ClassPatchInfo.java | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java b/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java index 52d75db6..326a45f5 100644 --- a/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java +++ b/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java @@ -1,7 +1,6 @@ package com.evacipated.cardcrawl.modthespire.patcher; import com.evacipated.cardcrawl.modthespire.Loader; -import com.evacipated.cardcrawl.modthespire.lib.LineFinder; import com.evacipated.cardcrawl.modthespire.lib.SpireField; import com.evacipated.cardcrawl.modthespire.lib.SpireMethod; import com.evacipated.cardcrawl.modthespire.lib.StaticSpireField; @@ -9,9 +8,7 @@ import javassist.bytecode.*; import javassist.bytecode.annotation.Annotation; import javassist.bytecode.annotation.AnnotationImpl; -import javassist.expr.ExprEditor; -import javassist.expr.MethodCall; -import javassist.expr.NewExpr; +import javassist.expr.*; import java.lang.reflect.Proxy; import java.util.*; @@ -24,6 +21,7 @@ public class ClassPatchInfo extends PatchInfo private CtClass ctClassToPatch; private static final Map methodCache = new HashMap<>(); + private static final Map> methodBodyCache = new HashMap<>(); public ClassPatchInfo(CtClass ctClassToPatch, CtClass ctPatchClass) { @@ -217,13 +215,6 @@ public void doPatch() throws PatchingException CtClass[] params = m.getParameterTypes(); SpireMethod anno = (SpireMethod) m.getAnnotation(SpireMethod.class); boolean hasReturnValue = !anno.returnType().equals(void.class); - StringBuilder sb = new StringBuilder(); - for (int i=1;i src = new ArrayList<>(); + src.add("java.lang.Object[] retVal = new java.lang.Object[1];"); + src.add(methodBody); + src.add(String.format("return%s", hasReturnValue ? " ($r) retVal[0];" : ";")); + methodBodyCache.put(Descriptor.ofMethod(newMethod.getReturnType(), newMethod.getParameterTypes()), src); methodCache.put(Descriptor.ofMethod(newMethod.getReturnType(), newMethod.getParameterTypes()), newMethod); + StringBuilder tmp = new StringBuilder(); + tmp.append("{"); + for (String s : src) { + tmp.append(s).append("\n"); + } + tmp.append("}"); + newMethod.setBody(tmp.toString()); - ctClassToPatch.setModifiers(Modifier.PUBLIC); + ctClassToPatch.setModifiers(ctClassToPatch.getModifiers() | Modifier.PUBLIC); if ((ctClassToPatch.getModifiers() & Modifier.ABSTRACT) > 0) { - ctClassToPatch.setModifiers(ctClassToPatch.getModifiers() - Modifier.ABSTRACT); + ctClassToPatch.setModifiers(ctClassToPatch.getModifiers() & ~Modifier.ABSTRACT); } } catch (DuplicateMemberException duplicateMemberException) { - newMethod = methodCache.get(Descriptor.ofMethod(m.getReturnType(), Arrays.copyOfRange(params, hasReturnValue ? 2 : 1, params.length))); + newMethod = methodCache.get(Descriptor.ofMethod(newMethod.getReturnType(), Arrays.copyOfRange(params, hasReturnValue ? 2 : 1, params.length))); + List src = methodBodyCache.get(Descriptor.ofMethod(newMethod.getReturnType(), Arrays.copyOfRange(params, hasReturnValue ? 2 : 1, params.length))); if (SpireMethod.POSTFIX.equals(anno.onConflict())) { - com.evacipated.cardcrawl.modthespire.lib.Matcher matcher = new com.evacipated.cardcrawl.modthespire.lib.Matcher.TypeCastMatcher(returnType.getName()); - int[] lineNum = LineFinder.findInOrder(newMethod, new ArrayList<>(), matcher); - newMethod.insertAt(lineNum[0], methodBody); + src.add(src.size() - 1, methodBody); } else { - newMethod.insertAt(1, methodBody); + src.add(1, methodBody); + } + StringBuilder tmp = new StringBuilder(); + tmp.append("{"); + for (String s : src) { + tmp.append(s).append("\n"); } + tmp.append("}"); + newMethod.setBody(tmp.toString()); } } } From 3ae3f2d8ed60ba6c478c7037eff34e74332bbc14 Mon Sep 17 00:00:00 2001 From: tldyl <756560020@qq.com> Date: Wed, 23 Nov 2022 20:50:14 +0800 Subject: [PATCH 4/4] Fixed runtime errors. It now works. --- .../modthespire/patcher/ClassPatchInfo.java | 56 ++++++++++--------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java b/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java index 52d75db6..99382d55 100644 --- a/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java +++ b/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ClassPatchInfo.java @@ -1,7 +1,6 @@ package com.evacipated.cardcrawl.modthespire.patcher; import com.evacipated.cardcrawl.modthespire.Loader; -import com.evacipated.cardcrawl.modthespire.lib.LineFinder; import com.evacipated.cardcrawl.modthespire.lib.SpireField; import com.evacipated.cardcrawl.modthespire.lib.SpireMethod; import com.evacipated.cardcrawl.modthespire.lib.StaticSpireField; @@ -9,9 +8,7 @@ import javassist.bytecode.*; import javassist.bytecode.annotation.Annotation; import javassist.bytecode.annotation.AnnotationImpl; -import javassist.expr.ExprEditor; -import javassist.expr.MethodCall; -import javassist.expr.NewExpr; +import javassist.expr.*; import java.lang.reflect.Proxy; import java.util.*; @@ -24,6 +21,7 @@ public class ClassPatchInfo extends PatchInfo private CtClass ctClassToPatch; private static final Map methodCache = new HashMap<>(); + private static final Map> methodBodyCache = new HashMap<>(); public ClassPatchInfo(CtClass ctClassToPatch, CtClass ctPatchClass) { @@ -217,13 +215,6 @@ public void doPatch() throws PatchingException CtClass[] params = m.getParameterTypes(); SpireMethod anno = (SpireMethod) m.getAnnotation(SpireMethod.class); boolean hasReturnValue = !anno.returnType().equals(void.class); - StringBuilder sb = new StringBuilder(); - for (int i=1;i src = new ArrayList<>(); + src.add("java.lang.Object[] retVal = new java.lang.Object[1];"); + src.add(methodBody); + src.add(String.format("return%s", hasReturnValue ? " ($r) retVal[0];" : ";")); + methodBodyCache.put(Descriptor.ofMethod(newMethod.getReturnType(), newMethod.getParameterTypes()) + ctClassToPatch.getName() + newMethod.getName(), src); + methodCache.put(Descriptor.ofMethod(newMethod.getReturnType(), newMethod.getParameterTypes()) + ctClassToPatch.getName() + newMethod.getName(), newMethod); + StringBuilder tmp = new StringBuilder(); + tmp.append("{"); + for (String s : src) { + tmp.append(s).append("\n"); + } + tmp.append("}"); + newMethod.setBody(tmp.toString()); - ctClassToPatch.setModifiers(Modifier.PUBLIC); + ctClassToPatch.setModifiers(ctClassToPatch.getModifiers() | Modifier.PUBLIC); if ((ctClassToPatch.getModifiers() & Modifier.ABSTRACT) > 0) { - ctClassToPatch.setModifiers(ctClassToPatch.getModifiers() - Modifier.ABSTRACT); + ctClassToPatch.setModifiers(ctClassToPatch.getModifiers() & ~Modifier.ABSTRACT); } } catch (DuplicateMemberException duplicateMemberException) { - newMethod = methodCache.get(Descriptor.ofMethod(m.getReturnType(), Arrays.copyOfRange(params, hasReturnValue ? 2 : 1, params.length))); + newMethod = methodCache.get(Descriptor.ofMethod(newMethod.getReturnType(), Arrays.copyOfRange(params, hasReturnValue ? 2 : 1, params.length)) + ctClassToPatch.getName() + newMethod.getName()); + List src = methodBodyCache.get(Descriptor.ofMethod(newMethod.getReturnType(), Arrays.copyOfRange(params, hasReturnValue ? 2 : 1, params.length)) + ctClassToPatch.getName() + newMethod.getName()); if (SpireMethod.POSTFIX.equals(anno.onConflict())) { - com.evacipated.cardcrawl.modthespire.lib.Matcher matcher = new com.evacipated.cardcrawl.modthespire.lib.Matcher.TypeCastMatcher(returnType.getName()); - int[] lineNum = LineFinder.findInOrder(newMethod, new ArrayList<>(), matcher); - newMethod.insertAt(lineNum[0], methodBody); + src.add(src.size() - 1, methodBody); } else { - newMethod.insertAt(1, methodBody); + src.add(1, methodBody); + } + StringBuilder tmp = new StringBuilder(); + tmp.append("{"); + for (String s : src) { + tmp.append(s).append("\n"); } + tmp.append("}"); + newMethod.setBody(tmp.toString()); } } }