├── README.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle ├── .gitignore ├── src └── main │ └── java │ └── codechicken │ └── asm │ ├── transformers │ ├── ClassNodeTransformer.java │ ├── MethodTransformer.java │ ├── FieldWriter.java │ ├── MethodReplacer.java │ ├── MethodInjector.java │ └── MethodWriter.java │ ├── CC_ClassWriter.java │ ├── LocalVariablesSorterVisitor.java │ ├── ImportantInsnVisitor.java │ ├── ModularASMTransformer.java │ ├── ClassHierarchyManager.java │ ├── ObfMapping.java │ ├── ASMBlock.java │ ├── ASMHelper.java │ ├── InsnComparator.java │ ├── InsnListSection.java │ ├── ASMReader.java │ └── StackAnalyser.java ├── gradlew.bat ├── gradlew └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | ChickenASM 2 | ============ 3 | A small lib for ASM related things. 4 | 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCBProject/ChickenASM/master/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenLocal() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | plugins { 9 | id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0' 10 | } 11 | 12 | rootProject.name = 'ChickenASM' 13 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # exclude all 2 | /* 3 | 4 | # Include important folders 5 | !src/ 6 | 7 | # Gradle stuff 8 | !gradle/ 9 | !gradlew 10 | !gradlew.bat 11 | !build.gradle 12 | !build.properties 13 | !settings.gradle 14 | 15 | # Other Files. 16 | !LICENSE 17 | !README.md 18 | !curse_upload.json 19 | !.travis.yml 20 | 21 | # Include git important files 22 | !.gitmodules 23 | !.gitignore 24 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/transformers/ClassNodeTransformer.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm.transformers; 2 | 3 | import codechicken.asm.ObfMapping; 4 | import org.objectweb.asm.ClassWriter; 5 | import org.objectweb.asm.tree.ClassNode; 6 | 7 | import java.util.Set; 8 | 9 | /** 10 | * Parent to all transformer's. 11 | */ 12 | public abstract class ClassNodeTransformer { 13 | 14 | public int writeFlags; 15 | 16 | public ClassNodeTransformer(int writeFlags) { 17 | this.writeFlags = writeFlags; 18 | } 19 | 20 | public ClassNodeTransformer() { 21 | this(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 22 | } 23 | 24 | public abstract String className(); 25 | 26 | public abstract void transform(ClassNode cnode); 27 | 28 | public void addMethodsToSort(Set set) { 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/CC_ClassWriter.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm; 2 | 3 | import org.objectweb.asm.ClassWriter; 4 | 5 | public class CC_ClassWriter extends ClassWriter { 6 | 7 | public CC_ClassWriter(int flags) { 8 | super(flags); 9 | } 10 | 11 | @Override 12 | protected String getCommonSuperClass(String type1, String type2) { 13 | String c = type1.replace('/', '.'); 14 | String d = type2.replace('/', '.'); 15 | if (ClassHierarchyManager.classExtends(d, c)) { 16 | return type1; 17 | } 18 | if (ClassHierarchyManager.classExtends(c, d)) { 19 | return type2; 20 | } 21 | do { 22 | c = ClassHierarchyManager.getSuperClass(c); 23 | } 24 | while (!ClassHierarchyManager.classExtends(d, c)); 25 | 26 | return c.replace('.', '/'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/transformers/MethodTransformer.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm.transformers; 2 | 3 | import codechicken.asm.ObfMapping; 4 | import org.objectweb.asm.tree.ClassNode; 5 | import org.objectweb.asm.tree.MethodNode; 6 | 7 | import static codechicken.asm.ASMHelper.findMethod; 8 | 9 | /** 10 | * Base Method transformer. 11 | */ 12 | public abstract class MethodTransformer extends ClassNodeTransformer { 13 | 14 | public final ObfMapping method; 15 | 16 | public MethodTransformer(ObfMapping method) { 17 | this.method = method; 18 | } 19 | 20 | @Override 21 | public String className() { 22 | return method.javaClass(); 23 | } 24 | 25 | @Override 26 | public void transform(ClassNode cnode) { 27 | MethodNode mv = findMethod(method, cnode); 28 | if (mv == null) { 29 | throw new RuntimeException("Method not found: " + method); 30 | } 31 | 32 | try { 33 | transform(mv); 34 | } catch (Exception e) { 35 | throw new RuntimeException("Error transforming method: " + method, e); 36 | } 37 | } 38 | 39 | public abstract void transform(MethodNode mv); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/LocalVariablesSorterVisitor.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | import org.objectweb.asm.commons.LocalVariablesSorter; 7 | 8 | import java.util.Set; 9 | 10 | public class LocalVariablesSorterVisitor extends ClassVisitor { 11 | 12 | public Set methods; 13 | public String owner; 14 | 15 | public LocalVariablesSorterVisitor(Set methods, ClassVisitor cv) { 16 | super(Opcodes.ASM9, cv); 17 | this.methods = methods; 18 | } 19 | 20 | public LocalVariablesSorterVisitor(ClassVisitor cv) { 21 | this(null, cv); 22 | } 23 | 24 | @Override 25 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 26 | super.visit(version, access, name, signature, superName, interfaces); 27 | owner = name; 28 | } 29 | 30 | @Override 31 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 32 | MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions); 33 | return methods == null || methods.contains(new ObfMapping(owner, name, desc)) ? new LocalVariablesSorter(access, desc, mv) : mv; 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/ImportantInsnVisitor.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | import org.objectweb.asm.tree.MethodNode; 7 | 8 | public class ImportantInsnVisitor extends ClassVisitor { 9 | 10 | public ImportantInsnVisitor(ClassVisitor cv) { 11 | super(Opcodes.ASM9, cv); 12 | } 13 | 14 | @Override 15 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 16 | return new ImportantInsnMethodVisitor(access, name, desc, signature, exceptions); 17 | } 18 | 19 | public class ImportantInsnMethodVisitor extends MethodVisitor { 20 | 21 | MethodVisitor delegate; 22 | 23 | public ImportantInsnMethodVisitor(int access, String name, String desc, String signature, String[] exceptions) { 24 | super(Opcodes.ASM9, new MethodNode(access, name, desc, signature, exceptions)); 25 | delegate = cv.visitMethod(access, name, desc, signature, exceptions); 26 | } 27 | 28 | @Override 29 | public void visitEnd() { 30 | super.visitEnd(); 31 | MethodNode mnode = (MethodNode) mv; 32 | mnode.instructions = InsnComparator.getImportantList(mnode.instructions); 33 | mnode.accept(delegate); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/transformers/FieldWriter.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm.transformers; 2 | 3 | import codechicken.asm.ModularASMTransformer; 4 | import codechicken.asm.ObfMapping; 5 | import org.objectweb.asm.tree.ClassNode; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import javax.annotation.Nonnull; 10 | import javax.annotation.Nullable; 11 | 12 | /** 13 | * Writes a field to a class. 14 | * ObfMapping contains the class to put the field. 15 | */ 16 | public class FieldWriter extends ClassNodeTransformer { 17 | 18 | private static final Logger LOGGER = LoggerFactory.getLogger(FieldWriter.class); 19 | 20 | public final ObfMapping field; 21 | public final int access; 22 | public final Object value; 23 | 24 | /** 25 | * Writes a field to the provided class with a default value. 26 | * 27 | * @param access The access modifiers the field will have. 28 | * @param field The name and class for the field. 29 | * @param value The default value for the field. 30 | */ 31 | public FieldWriter(@Nonnull int access, @Nonnull ObfMapping field, @Nullable Object value) { 32 | this.field = field; 33 | this.access = access; 34 | this.value = value; 35 | } 36 | 37 | /** 38 | * Writes a field to the provided class. 39 | * 40 | * @param access The access modifiers the field will have. 41 | * @param field The name and class for the field. 42 | */ 43 | public FieldWriter(@Nonnull int access, @Nonnull ObfMapping field) { 44 | this(access, field, null); 45 | } 46 | 47 | @Override 48 | public String className() { 49 | return field.javaClass(); 50 | } 51 | 52 | @Override 53 | public void transform(ClassNode cNode) { 54 | if (ModularASMTransformer.DEBUG) { 55 | LOGGER.info("Writing field '{}' to class '{}'.", field, cNode.name); 56 | } else { 57 | LOGGER.debug("Writing field '{}' to class '{}'.", field, cNode.name); 58 | } 59 | field.visitField(cNode, access, value); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/transformers/MethodReplacer.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm.transformers; 2 | 3 | import codechicken.asm.*; 4 | import org.objectweb.asm.tree.InsnList; 5 | import org.objectweb.asm.tree.MethodNode; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import javax.annotation.Nonnull; 10 | import java.util.Set; 11 | 12 | /** 13 | * Replaces a specific needle with a specific replacement. 14 | * Can replace more than one needle. 15 | */ 16 | public class MethodReplacer extends MethodTransformer { 17 | 18 | private static final Logger LOGGER = LoggerFactory.getLogger(MethodReplacer.class); 19 | 20 | public ASMBlock needle; 21 | public ASMBlock replacement; 22 | 23 | /** 24 | * Replaces the provided needle with the provided replacement. 25 | * 26 | * @param method The method to replace in. 27 | * @param needle The needle to replace. 28 | * @param replacement The replacement to apply. 29 | */ 30 | public MethodReplacer(@Nonnull ObfMapping method, @Nonnull ASMBlock needle, @Nonnull ASMBlock replacement) { 31 | super(method); 32 | this.needle = needle; 33 | this.replacement = replacement; 34 | } 35 | 36 | /** 37 | * Replaces the provided needle with the provided replacement. 38 | * 39 | * @param method The method to replace in. 40 | * @param needle The needle to replace. 41 | * @param replacement The replacement to apply. 42 | */ 43 | public MethodReplacer(@Nonnull ObfMapping method, @Nonnull InsnList needle, @Nonnull InsnList replacement) { 44 | this(method, new ASMBlock(needle), new ASMBlock(replacement)); 45 | } 46 | 47 | @Override 48 | public void addMethodsToSort(Set set) { 49 | set.add(method); 50 | } 51 | 52 | @Override 53 | public void transform(MethodNode mv) { 54 | for (InsnListSection key : InsnComparator.findN(mv.instructions, needle.list)) { 55 | if (ModularASMTransformer.DEBUG) { 56 | LOGGER.info("Replacing method '{}' @ {} - {}.", method, key.start, key.end); 57 | } else { 58 | LOGGER.debug("Replacing method '{}' @ {} - {}.", method, key.start, key.end); 59 | } 60 | ASMBlock replaceBlock = replacement.copy().pullLabels(needle.applyLabels(key)); 61 | key.insert(replaceBlock.list.list); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/transformers/MethodInjector.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm.transformers; 2 | 3 | import codechicken.asm.*; 4 | import org.objectweb.asm.tree.InsnList; 5 | import org.objectweb.asm.tree.MethodNode; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import javax.annotation.Nonnull; 10 | import javax.annotation.Nullable; 11 | import java.util.Set; 12 | 13 | /** 14 | * Injects a call before or after the needle. 15 | * If needle is null it will inject at the start or end of the method. 16 | */ 17 | public class MethodInjector extends MethodTransformer { 18 | 19 | private static final Logger LOGGER = LoggerFactory.getLogger(MethodInjector.class); 20 | 21 | @Nullable 22 | public ASMBlock needle; 23 | @Nonnull 24 | public ASMBlock injection; 25 | public boolean before; 26 | 27 | /** 28 | * Injects the provided injection instructions at the location of the needle. 29 | * 30 | * @param method The method to inject to. 31 | * @param needle Where to inject. 32 | * @param injection The injection to apply. 33 | * @param before Weather to apply before or after the provided needle. 34 | */ 35 | public MethodInjector(@Nonnull ObfMapping method, @Nullable ASMBlock needle, @Nonnull ASMBlock injection, boolean before) { 36 | super(method); 37 | this.needle = needle; 38 | this.injection = injection; 39 | this.before = before; 40 | } 41 | 42 | /** 43 | * Injects the provided injection to the beginning or end of the method. 44 | * 45 | * @param method The method to inject to. 46 | * @param injection The injection to apply. 47 | * @param before Weather to apply at the start of the method or the end. 48 | */ 49 | public MethodInjector(@Nonnull ObfMapping method, @Nonnull ASMBlock injection, boolean before) { 50 | this(method, null, injection, before); 51 | } 52 | 53 | /** 54 | * Injects the provided injection instructions at the location of the needle. 55 | * 56 | * @param method The method to inject to. 57 | * @param needle Where to inject. 58 | * @param injection The injection to apply. 59 | * @param before Weather to apply before or after the provided needle. 60 | */ 61 | public MethodInjector(@Nonnull ObfMapping method, @Nonnull InsnList needle, @Nonnull InsnList injection, boolean before) { 62 | this(method, new ASMBlock(needle), new ASMBlock(injection), before); 63 | } 64 | 65 | /** 66 | * Injects the provided injection to the beginning or end of the method. 67 | * 68 | * @param method The method to inject to. 69 | * @param injection The injection to apply. 70 | * @param before Weather to apply at the start of the method or the end. 71 | */ 72 | public MethodInjector(@Nonnull ObfMapping method, @Nonnull InsnList injection, boolean before) { 73 | this(method, null, new ASMBlock(injection), before); 74 | } 75 | 76 | @Override 77 | public void addMethodsToSort(Set set) { 78 | set.add(method); 79 | } 80 | 81 | @Override 82 | public void transform(MethodNode mv) { 83 | if (needle == null) { 84 | if (ModularASMTransformer.DEBUG) { 85 | LOGGER.info("Injecting {} method '{}'", before ? "before" : "after", method); 86 | } else { 87 | LOGGER.debug("Injecting {} method '{}'", before ? "before" : "after", method); 88 | } 89 | if (before) { 90 | mv.instructions.insert(injection.rawListCopy()); 91 | } else { 92 | mv.instructions.add(injection.rawListCopy()); 93 | } 94 | } else { 95 | for (InsnListSection key : InsnComparator.findN(mv.instructions, needle.list)) { 96 | if (ModularASMTransformer.DEBUG) { 97 | LOGGER.info("Injecting {} method '{}' @ {} - {}", before ? "before" : "after", method, key.start, key.end); 98 | } else { 99 | LOGGER.debug("Injecting {} method '{}' @ {} - {}", before ? "before" : "after", method, key.start, key.end); 100 | } 101 | ASMBlock injectBlock = injection.copy().mergeLabels(needle.applyLabels(key)); 102 | 103 | if (before) { 104 | key.insertBefore(injectBlock.list.list); 105 | } else { 106 | key.insert(injectBlock.list.list); 107 | } 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/transformers/MethodWriter.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm.transformers; 2 | 3 | import codechicken.asm.ASMBlock; 4 | import codechicken.asm.ModularASMTransformer; 5 | import codechicken.asm.ObfMapping; 6 | import org.objectweb.asm.tree.ClassNode; 7 | import org.objectweb.asm.tree.InsnList; 8 | import org.objectweb.asm.tree.MethodNode; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import javax.annotation.Nonnull; 13 | import javax.annotation.Nullable; 14 | 15 | import static codechicken.asm.ASMHelper.findMethod; 16 | 17 | /** 18 | * Writes a method containing the provided InsnList with the ObfMapping as the method name and desc. 19 | */ 20 | public class MethodWriter extends ClassNodeTransformer { 21 | 22 | private static final Logger LOGGER = LoggerFactory.getLogger(MethodWriter.class); 23 | 24 | public final int access; 25 | public final ObfMapping method; 26 | public final String[] exceptions; 27 | public InsnList list; 28 | 29 | /** 30 | * Write a new method with no instructions or exceptions. 31 | * 32 | * @param access Access flags. 33 | * @param method The method name and desc. 34 | */ 35 | public MethodWriter(int access, @Nonnull ObfMapping method) { 36 | this(access, method, null, (InsnList) null); 37 | } 38 | 39 | /** 40 | * Write a new method with the provided instructions. 41 | * 42 | * @param access Access flags. 43 | * @param method The method name and desc. 44 | * @param list The instructions to use. 45 | */ 46 | public MethodWriter(int access, @Nonnull ObfMapping method, @Nullable InsnList list) { 47 | this(access, method, null, list); 48 | } 49 | 50 | /** 51 | * Write a new method with the provided instructions. 52 | * 53 | * @param access Access flags. 54 | * @param method The method name and desc. 55 | * @param block The instructions to use. 56 | */ 57 | public MethodWriter(int access, @Nonnull ObfMapping method, @Nonnull ASMBlock block) { 58 | this(access, method, null, block); 59 | } 60 | 61 | /** 62 | * Write a new method with no instructions and the provided exceptions. 63 | * 64 | * @param access Access flags. 65 | * @param method The method name and desc. 66 | * @param exceptions The exceptions this method can throw. 67 | */ 68 | public MethodWriter(int access, @Nonnull ObfMapping method, @Nullable String[] exceptions) { 69 | this(access, method, exceptions, (InsnList) null); 70 | } 71 | 72 | /** 73 | * Write a new method with the provided instructions and the provided exceptions. 74 | * 75 | * @param access Access flags. 76 | * @param method The method name and desc. 77 | * @param exceptions The exceptions this method can throw. 78 | * @param list The instructions to use. 79 | */ 80 | public MethodWriter(int access, @Nonnull ObfMapping method, @Nullable String[] exceptions, @Nullable InsnList list) { 81 | this.access = access; 82 | this.method = method; 83 | this.exceptions = exceptions; 84 | this.list = list; 85 | } 86 | 87 | /** 88 | * Write a new method with the provided instructions and the provided exceptions. 89 | * 90 | * @param access Access flags. 91 | * @param method The method name and desc. 92 | * @param exceptions The exceptions this method can throw. 93 | * @param block The instructions to use. 94 | */ 95 | public MethodWriter(int access, @Nonnull ObfMapping method, @Nullable String[] exceptions, @Nonnull ASMBlock block) { 96 | this(access, method, exceptions, block.rawListCopy()); 97 | } 98 | 99 | @Override 100 | public String className() { 101 | return method.javaClass(); 102 | } 103 | 104 | @Override 105 | public void transform(ClassNode cnode) { 106 | MethodNode mv = findMethod(method, cnode); 107 | if (mv == null) { 108 | mv = (MethodNode) method.visitMethod(cnode, access, exceptions); 109 | } else { 110 | mv.access = access; 111 | mv.instructions.clear(); 112 | if (mv.localVariables != null) { 113 | mv.localVariables.clear(); 114 | } 115 | if (mv.tryCatchBlocks != null) { 116 | mv.tryCatchBlocks.clear(); 117 | } 118 | } 119 | 120 | write(mv); 121 | } 122 | 123 | public void write(MethodNode mv) { 124 | if (ModularASMTransformer.DEBUG) { 125 | LOGGER.info("Writing method '{}'.", method); 126 | } else { 127 | LOGGER.debug("Writing method '{}'.", method); 128 | } 129 | list.accept(mv); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/ModularASMTransformer.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm; 2 | 3 | import codechicken.asm.transformers.ClassNodeTransformer; 4 | import org.objectweb.asm.ClassReader; 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.tree.ClassNode; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import javax.annotation.Nonnull; 11 | import javax.annotation.Nullable; 12 | import java.io.File; 13 | import java.io.FileOutputStream; 14 | import java.util.HashMap; 15 | import java.util.HashSet; 16 | import java.util.LinkedList; 17 | import java.util.List; 18 | 19 | import static codechicken.asm.ASMHelper.createBytes; 20 | import static codechicken.asm.ASMHelper.dump; 21 | 22 | public class ModularASMTransformer { 23 | 24 | //debug will print all transformations to console by default. 25 | public static final boolean DEBUG = Boolean.parseBoolean(System.getProperty("ccl.asm.debug", "false")); 26 | //The format in which to dump the transformed classes. 27 | public static final boolean DUMP_RAW = Boolean.parseBoolean(System.getProperty("ccl.asm.debug.dump_raw", "false")) && DEBUG; 28 | public static final boolean DUMP_TEXT = Boolean.parseBoolean(System.getProperty("ccl.asm.debug.dump_text", "false")) && DEBUG; 29 | 30 | public File dumpFolder; 31 | 32 | public HashMap transformers = new HashMap<>(); 33 | public String name; 34 | 35 | public ModularASMTransformer(File dumpFolder, String name) { 36 | this.name = name; 37 | this.dumpFolder = dumpFolder; 38 | } 39 | 40 | /** 41 | * Adds a ClassNodeTransformer to this transformer. 42 | * 43 | * @param t Transformer to add. 44 | */ 45 | public void add(@Nonnull ClassNodeTransformer t) { 46 | ClassNodeTransformerList list = transformers.computeIfAbsent(t.className(), k -> new ClassNodeTransformerList()); 47 | list.add(t); 48 | } 49 | 50 | /** 51 | * Runs the transform. 52 | * 53 | * @param name name of the class being loaded. 54 | * @param bytes Class bytes. 55 | * @return Returns null if the class passed is null, returns original class if there are no transformers for a given class. 56 | * Otherwise returns transformed class. 57 | */ 58 | @Nullable 59 | public byte[] transform(@Nonnull String name, @Nullable byte[] bytes) { 60 | if (bytes == null) { 61 | return null; 62 | } 63 | 64 | ClassNodeTransformerList list = transformers.get(name); 65 | return list == null ? bytes : list.transform(bytes); 66 | } 67 | 68 | /** 69 | * Contains a list of transformers for a given class. 70 | * Also contains some basic logic for doing the actual transform. 71 | */ 72 | public class ClassNodeTransformerList { 73 | 74 | List transformers = new LinkedList<>(); 75 | HashSet methodsToSort = new HashSet<>(); 76 | 77 | public void add(ClassNodeTransformer t) { 78 | transformers.add(t); 79 | t.addMethodsToSort(methodsToSort); 80 | } 81 | 82 | public byte[] transform(byte[] bytes) { 83 | ClassNode cnode = new ClassNode(); 84 | ClassReader reader = new ClassReader(bytes); 85 | ClassVisitor cv = cnode; 86 | if (!methodsToSort.isEmpty()) { 87 | cv = new LocalVariablesSorterVisitor(methodsToSort, cv); 88 | } 89 | reader.accept(cv, ClassReader.EXPAND_FRAMES); 90 | 91 | try { 92 | int writeFlags = 0; 93 | for (ClassNodeTransformer t : transformers) { 94 | t.transform(cnode); 95 | writeFlags |= t.writeFlags; 96 | } 97 | 98 | bytes = createBytes(cnode, writeFlags); 99 | if (DUMP_RAW) { 100 | File file = new File(dumpFolder, cnode.name.replace('/', '.') + ".class"); 101 | if (!file.exists()) { 102 | if (!file.getParentFile().exists()) { 103 | file.getParentFile().mkdirs(); 104 | } 105 | file.createNewFile(); 106 | } 107 | FileOutputStream fos = new FileOutputStream(file); 108 | fos.write(bytes); 109 | fos.flush(); 110 | fos.close(); 111 | } else if (DUMP_TEXT) { 112 | dump(bytes, new File(dumpFolder, cnode.name.replace('/', '.') + ".txt"), false, false, true); 113 | } 114 | return bytes; 115 | } catch (Exception e) { 116 | dump(bytes, new File(dumpFolder, cnode.name.replace('/', '.') + ".txt"), false, false, true); 117 | throw new RuntimeException(e); 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/ClassHierarchyManager.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | import org.objectweb.asm.ClassReader; 5 | 6 | import javax.annotation.Nonnull; 7 | import java.util.*; 8 | import java.util.function.Function; 9 | 10 | public class ClassHierarchyManager { 11 | 12 | public static class SuperCache { 13 | 14 | String superclass; 15 | public Set parents = new HashSet<>(); 16 | private boolean flattened; 17 | 18 | public void add(String parent) { 19 | parents.add(parent); 20 | } 21 | 22 | public void flatten() { 23 | if (flattened) { 24 | return; 25 | } 26 | 27 | for (String s : new ArrayList<>(parents)) { 28 | SuperCache c = declareClass(s); 29 | if (c != null) { 30 | c.flatten(); 31 | parents.addAll(c.parents); 32 | } 33 | } 34 | flattened = true; 35 | } 36 | } 37 | 38 | public static final HashMap superclasses = new HashMap<>(); 39 | private static final List> CLASS_BYTE_LOOKUPS = new ArrayList<>(0); 40 | 41 | /** 42 | * Add a factory to find class bytes with. 43 | * 44 | * @param func The function. 45 | */ 46 | public static void addByteLookupFunc(Function func) { 47 | CLASS_BYTE_LOOKUPS.add(func); 48 | } 49 | 50 | /** 51 | * @param name The class in question 52 | * @param superclass The class being extended 53 | * @return true if clazz extends, either directly or indirectly, superclass. 54 | */ 55 | public static boolean classExtends(String name, String superclass) { 56 | 57 | if (name.equals(superclass)) { 58 | return true; 59 | } 60 | 61 | SuperCache cache = declareClass(name); 62 | if (cache == null)//just can't handle this 63 | { 64 | return false; 65 | } 66 | 67 | cache.flatten(); 68 | return cache.parents.contains(superclass); 69 | } 70 | 71 | private static SuperCache declareClass(String name) { 72 | SuperCache cache = superclasses.get(name); 73 | 74 | if (cache != null) { 75 | return cache; 76 | } 77 | 78 | for (Function func : CLASS_BYTE_LOOKUPS) { 79 | try { 80 | byte[] bytes = func.apply(name); 81 | if (bytes != null) { 82 | cache = declareASM(bytes); 83 | break; 84 | } 85 | } catch(Throwable ignored) { 86 | } 87 | } 88 | 89 | if (cache != null) { 90 | return cache; 91 | } 92 | 93 | try { 94 | cache = declareReflection(name); 95 | } catch (ClassNotFoundException ignored) { 96 | } 97 | 98 | return cache; 99 | } 100 | 101 | private static SuperCache declareReflection(String name) throws ClassNotFoundException { 102 | Class aclass = Class.forName(name); 103 | 104 | SuperCache cache = getOrCreateCache(name); 105 | if (aclass.isInterface()) { 106 | cache.superclass = "java.lang.Object"; 107 | } else if (name.equals("java.lang.Object")) { 108 | return cache; 109 | } else { 110 | cache.superclass = aclass.getSuperclass().getName(); 111 | } 112 | 113 | cache.add(cache.superclass); 114 | for (Class iclass : aclass.getInterfaces()) { 115 | cache.add(iclass.getName()); 116 | } 117 | 118 | return cache; 119 | } 120 | 121 | private static SuperCache declareASM(@Nonnull byte[] bytes) { 122 | ClassReader reader = new ClassReader(bytes); 123 | String name = reader.getClassName(); 124 | 125 | SuperCache cache = getOrCreateCache(name); 126 | cache.superclass = reader.getSuperName().replace('/', '.'); 127 | cache.add(cache.superclass); 128 | for (String iclass : reader.getInterfaces()) { 129 | cache.add(iclass.replace('/', '.')); 130 | } 131 | 132 | return cache; 133 | } 134 | 135 | public static void declare(@Nonnull String name, @Nonnull byte[] bytes) { 136 | if (!superclasses.containsKey(name)) { 137 | declareASM(bytes); 138 | } 139 | } 140 | 141 | @Nonnull 142 | public static SuperCache getOrCreateCache(@Nonnull String name) { 143 | return superclasses.computeIfAbsent(name, k -> new SuperCache()); 144 | } 145 | 146 | public static String getSuperClass(@Nonnull String name) { 147 | SuperCache cache = declareClass(name); 148 | if (cache == null) { 149 | return "java.lang.Object"; 150 | } 151 | 152 | cache.flatten(); 153 | return cache.superclass; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/ObfMapping.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.FieldVisitor; 5 | import org.objectweb.asm.MethodVisitor; 6 | import org.objectweb.asm.Opcodes; 7 | import org.objectweb.asm.commons.Remapper; 8 | import org.objectweb.asm.tree.*; 9 | 10 | public class ObfMapping { 11 | 12 | public String s_owner; 13 | public String s_name; 14 | public String s_desc; 15 | 16 | public ObfMapping(String owner) { 17 | this(owner, "", ""); 18 | } 19 | 20 | public ObfMapping(String owner, String name) { 21 | this(owner, name, ""); 22 | } 23 | 24 | public ObfMapping(String owner, String name, String desc) { 25 | this.s_owner = owner; 26 | this.s_name = name; 27 | this.s_desc = desc; 28 | 29 | if (s_owner.contains(".")) { 30 | throw new IllegalArgumentException(s_owner); 31 | } 32 | } 33 | 34 | public ObfMapping(ObfMapping descmap, String subclass) { 35 | this(subclass, descmap.s_name, descmap.s_desc); 36 | } 37 | 38 | public static ObfMapping fromDesc(String s) { 39 | int lastDot = s.lastIndexOf('.'); 40 | if (lastDot < 0) { 41 | return new ObfMapping(s, "", ""); 42 | } 43 | int sep = s.indexOf('(');//methods 44 | int sep_end = sep; 45 | if (sep < 0) { 46 | sep = s.indexOf(' ');//some stuffs 47 | sep_end = sep + 1; 48 | } 49 | if (sep < 0) { 50 | sep = s.indexOf(':');//fields 51 | sep_end = sep + 1; 52 | } 53 | if (sep < 0) { 54 | return new ObfMapping(s.substring(0, lastDot), s.substring(lastDot + 1), ""); 55 | } 56 | 57 | return new ObfMapping(s.substring(0, lastDot), s.substring(lastDot + 1, sep), s.substring(sep_end)); 58 | } 59 | 60 | public ObfMapping subclass(String subclass) { 61 | return new ObfMapping(this, subclass); 62 | } 63 | 64 | public boolean matches(MethodNode node) { 65 | return s_name.equals(node.name) && s_desc.equals(node.desc); 66 | } 67 | 68 | public boolean matches(MethodInsnNode node) { 69 | return s_owner.equals(node.owner) && s_name.equals(node.name) && s_desc.equals(node.desc); 70 | } 71 | 72 | public AbstractInsnNode toInsn(int opcode) { 73 | if (isClass()) { 74 | return new TypeInsnNode(opcode, s_owner); 75 | } else if (isMethod()) { 76 | return new MethodInsnNode(opcode, s_owner, s_name, s_desc, opcode == Opcodes.INVOKEINTERFACE); 77 | } else { 78 | return new FieldInsnNode(opcode, s_owner, s_name, s_desc); 79 | } 80 | } 81 | 82 | public void visitTypeInsn(MethodVisitor mv, int opcode) { 83 | mv.visitTypeInsn(opcode, s_owner); 84 | } 85 | 86 | public void visitMethodInsn(MethodVisitor mv, int opcode) { 87 | mv.visitMethodInsn(opcode, s_owner, s_name, s_desc, opcode == Opcodes.INVOKEINTERFACE); 88 | } 89 | 90 | public void visitFieldInsn(MethodVisitor mv, int opcode) { 91 | mv.visitFieldInsn(opcode, s_owner, s_name, s_desc); 92 | } 93 | 94 | public MethodVisitor visitMethod(ClassVisitor visitor, int access, String[] exceptions) { 95 | return visitor.visitMethod(access, s_name, s_desc, null, exceptions); 96 | } 97 | 98 | public FieldVisitor visitField(ClassVisitor visitor, int access, Object value) { 99 | return visitor.visitField(access, s_name, s_desc, null, value); 100 | } 101 | 102 | public boolean isClass(String name) { 103 | return name.replace('.', '/').equals(s_owner); 104 | } 105 | 106 | public boolean matches(String name, String desc) { 107 | return s_name.equals(name) && s_desc.equals(desc); 108 | } 109 | 110 | public boolean matches(FieldNode node) { 111 | return s_name.equals(node.name) && s_desc.equals(node.desc); 112 | } 113 | 114 | public boolean matches(FieldInsnNode node) { 115 | return s_owner.equals(node.owner) && s_name.equals(node.name) && s_desc.equals(node.desc); 116 | } 117 | 118 | public String javaClass() { 119 | return s_owner.replace('/', '.'); 120 | } 121 | 122 | public String methodDesc() { 123 | return s_owner + "." + s_name + s_desc; 124 | } 125 | 126 | public String fieldDesc() { 127 | return s_owner + "." + s_name + ":" + s_desc; 128 | } 129 | 130 | public boolean isClass() { 131 | return s_name.length() == 0; 132 | } 133 | 134 | public boolean isMethod() { 135 | return s_desc.contains("("); 136 | } 137 | 138 | public boolean isField() { 139 | return !isClass() && !isMethod(); 140 | } 141 | 142 | public ObfMapping map(Remapper mapper) { 143 | if (mapper == null) { 144 | return this; 145 | } 146 | 147 | if (isMethod()) { 148 | s_name = mapper.mapMethodName(s_owner, s_name, s_desc); 149 | } else if (isField()) { 150 | s_name = mapper.mapFieldName(s_owner, s_name, s_desc); 151 | } 152 | 153 | s_owner = mapper.mapType(s_owner); 154 | 155 | if (isMethod()) { 156 | s_desc = mapper.mapMethodDesc(s_desc); 157 | } else if (s_desc.length() > 0) { 158 | s_desc = mapper.mapDesc(s_desc); 159 | } 160 | 161 | return this; 162 | } 163 | 164 | public ObfMapping copy() { 165 | return new ObfMapping(s_owner, s_name, s_desc); 166 | } 167 | 168 | @Override 169 | public boolean equals(Object obj) { 170 | if (!(obj instanceof ObfMapping)) { 171 | return false; 172 | } 173 | 174 | ObfMapping desc = (ObfMapping) obj; 175 | return s_owner.equals(desc.s_owner) && s_name.equals(desc.s_name) && s_desc.equals(desc.s_desc); 176 | } 177 | 178 | @Override 179 | public int hashCode() { 180 | int result = s_owner != null ? s_owner.hashCode() : 0; 181 | result = 31 * result + (s_name != null ? s_name.hashCode() : 0); 182 | result = 31 * result + (s_desc != null ? s_desc.hashCode() : 0); 183 | return result; 184 | } 185 | 186 | @Override 187 | public String toString() { 188 | if (s_name.length() == 0) { 189 | return "[" + s_owner + "]"; 190 | } 191 | if (s_desc.length() == 0) { 192 | return "[" + s_owner + "." + s_name + "]"; 193 | } 194 | return "[" + (isMethod() ? methodDesc() : fieldDesc()) + "]"; 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/ASMBlock.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import org.objectweb.asm.tree.InsnList; 5 | import org.objectweb.asm.tree.JumpInsnNode; 6 | import org.objectweb.asm.tree.LabelNode; 7 | 8 | import java.util.*; 9 | import java.util.Map.Entry; 10 | 11 | import static org.objectweb.asm.tree.AbstractInsnNode.*; 12 | 13 | public class ASMBlock { 14 | 15 | public InsnListSection list; 16 | public Map labels; 17 | 18 | public ASMBlock(InsnListSection list, Map labels) { 19 | this.list = list; 20 | this.labels = labels; 21 | } 22 | 23 | public ASMBlock(InsnListSection list) { 24 | this(list, new HashMap<>()); 25 | } 26 | 27 | public ASMBlock(InsnList list) { 28 | this(new InsnListSection(list)); 29 | } 30 | 31 | public ASMBlock() { 32 | this(new InsnListSection()); 33 | } 34 | 35 | public LabelNode getOrAdd(String s) { 36 | LabelNode l = get(s); 37 | if (l == null) { 38 | labels.put(s, l = new LabelNode()); 39 | } 40 | return l; 41 | } 42 | 43 | public LabelNode get(String s) { 44 | return labels.get(s); 45 | } 46 | 47 | public void replaceLabels(Map labelMap, Set usedLabels) { 48 | for (AbstractInsnNode insn : list) { 49 | switch (insn.getType()) { 50 | case LABEL: 51 | LabelNode insn2 = (LabelNode) insn.clone(labelMap); 52 | if (insn2 == insn) {//identity mapping 53 | continue; 54 | } 55 | if (usedLabels.contains(insn2)) { 56 | throw new IllegalStateException("LabelNode cannot be a part of two InsnLists"); 57 | } 58 | list.replace(insn, insn2); 59 | break; 60 | case JUMP_INSN: 61 | case FRAME: 62 | case LOOKUPSWITCH_INSN: 63 | case TABLESWITCH_INSN: 64 | list.replace(insn, insn.clone(labelMap)); 65 | } 66 | } 67 | 68 | for (Entry entry : labelMap.entrySet()) { 69 | // I hate this stream call. It exists because BiMap was dropped.. 70 | labels.entrySet().stream() 71 | .filter(e -> e.getValue().equals(entry.getKey())) 72 | .findFirst() 73 | .map(Entry::getKey) 74 | .ifPresent(key -> labels.put(key, entry.getValue())); 75 | 76 | } 77 | } 78 | 79 | public void replaceLabels(Map labelMap) { 80 | replaceLabels(labelMap, Collections.emptySet()); 81 | } 82 | 83 | public void replaceLabel(String s, LabelNode l) { 84 | LabelNode old = get(s); 85 | if (old != null) { 86 | replaceLabels(Collections.singletonMap(old, l)); 87 | } 88 | } 89 | 90 | /** 91 | * Pulls all common labels from other into this 92 | * 93 | * @return this 94 | */ 95 | public ASMBlock mergeLabels(ASMBlock other) { 96 | if (labels.isEmpty() || other.labels.isEmpty()) { 97 | return this; 98 | } 99 | 100 | //common labels, give them our nodes 101 | HashMap labelMap = list.identityLabelMap(); 102 | for (Entry entry : other.labels.entrySet()) { 103 | LabelNode old = labels.get(entry.getKey()); 104 | if (old != null) { 105 | labelMap.put(old, entry.getValue()); 106 | } 107 | } 108 | HashSet usedLabels = new HashSet<>(); 109 | for (AbstractInsnNode insn = other.list.list.getFirst(); insn != null; insn = insn.getNext()) { 110 | if (insn.getType() == LABEL) { 111 | usedLabels.add((LabelNode) insn); 112 | } 113 | } 114 | 115 | replaceLabels(labelMap, usedLabels); 116 | return this; 117 | } 118 | 119 | /** 120 | * Like mergeLabels but pulls insns from other list into this so LabelNodes can be transferred 121 | * 122 | * @return this 123 | */ 124 | public ASMBlock pullLabels(ASMBlock other) { 125 | other.list.remove(); 126 | return mergeLabels(other); 127 | } 128 | 129 | public ASMBlock copy() { 130 | Map labels = new HashMap<>(); 131 | Map labelMap = list.cloneLabels(); 132 | 133 | for (Entry entry : this.labels.entrySet()) { 134 | labels.put(entry.getKey(), labelMap.get(entry.getValue())); 135 | } 136 | 137 | return new ASMBlock(list.copy(labelMap), labels); 138 | } 139 | 140 | public ASMBlock applyLabels(InsnListSection list2) { 141 | if (labels.isEmpty()) { 142 | return new ASMBlock(list2); 143 | } 144 | 145 | Set cFlowLabels1 = new HashSet<>(labels.values()); 146 | Set cFlowLabels2 = InsnComparator.getControlFlowLabels(list2); 147 | ASMBlock block = new ASMBlock(list2); 148 | 149 | HashMap labelMap = new HashMap<>(); 150 | 151 | for (int i = 0, k = 0; i < list.size() && k < list2.size(); ) { 152 | AbstractInsnNode insn1 = list.get(i); 153 | if (!InsnComparator.insnImportant(insn1, cFlowLabels1)) { 154 | i++; 155 | continue; 156 | } 157 | 158 | AbstractInsnNode insn2 = list2.get(k); 159 | if (!InsnComparator.insnImportant(insn2, cFlowLabels2)) { 160 | k++; 161 | continue; 162 | } 163 | 164 | if (insn1.getOpcode() != insn2.getOpcode()) { 165 | throw new IllegalArgumentException("Lists do not match:\n" + list + "\n\n" + list2); 166 | } 167 | 168 | switch (insn1.getType()) { 169 | case LABEL: 170 | labelMap.put((LabelNode) insn1, (LabelNode) insn2); 171 | break; 172 | case JUMP_INSN: 173 | labelMap.put(((JumpInsnNode) insn1).label, ((JumpInsnNode) insn2).label); 174 | break; 175 | } 176 | i++; 177 | k++; 178 | } 179 | 180 | for (Entry entry : labels.entrySet()) { 181 | block.labels.put(entry.getKey(), labelMap.get(entry.getValue())); 182 | } 183 | 184 | return block; 185 | } 186 | 187 | public InsnList rawListCopy() { 188 | return list.copy().list; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/ASMHelper.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm; 2 | 3 | import org.objectweb.asm.ClassReader; 4 | import org.objectweb.asm.ClassVisitor; 5 | import org.objectweb.asm.ClassWriter; 6 | import org.objectweb.asm.tree.*; 7 | import org.objectweb.asm.util.ASMifier; 8 | import org.objectweb.asm.util.Textifier; 9 | import org.objectweb.asm.util.TraceClassVisitor; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.io.PrintWriter; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | public class ASMHelper { 22 | 23 | private static final Logger LOGGER = LoggerFactory.getLogger(ASMHelper.class); 24 | 25 | public static MethodNode findMethod(ObfMapping methodmap, ClassNode cnode) { 26 | for (MethodNode mnode : cnode.methods) { 27 | if (methodmap.matches(mnode)) { 28 | return mnode; 29 | } 30 | } 31 | return null; 32 | } 33 | 34 | public static FieldNode findField(ObfMapping fieldmap, ClassNode cnode) { 35 | for (FieldNode fnode : cnode.fields) { 36 | if (fieldmap.matches(fnode)) { 37 | return fnode; 38 | } 39 | } 40 | return null; 41 | } 42 | 43 | public static ClassNode createClassNode(byte[] bytes) { 44 | return createClassNode(bytes, 0); 45 | } 46 | 47 | public static ClassNode createClassNode(byte[] bytes, int flags) { 48 | ClassNode cnode = new ClassNode(); 49 | ClassReader reader = new ClassReader(bytes); 50 | reader.accept(cnode, flags); 51 | return cnode; 52 | } 53 | 54 | public static byte[] createBytes(ClassNode cnode, int flags) { 55 | ClassWriter cw = new CC_ClassWriter(flags); 56 | cnode.accept(cw); 57 | return cw.toByteArray(); 58 | } 59 | 60 | public static Map cloneLabels(InsnList list) { 61 | return new InsnListSection(list).cloneLabels(); 62 | } 63 | 64 | public static InsnList cloneInsnList(InsnList list) { 65 | return new InsnListSection(list).copy().list; 66 | } 67 | 68 | public static InsnList cloneInsnList(Map labelMap, InsnList list) { 69 | return new InsnListSection(list).copy(labelMap).list; 70 | } 71 | 72 | public static List cloneTryCatchBlocks(Map labelMap, List tcblocks) { 73 | ArrayList clone = new ArrayList<>(); 74 | for (TryCatchBlockNode node : tcblocks) { 75 | clone.add(new TryCatchBlockNode(labelMap.get(node.start), labelMap.get(node.end), labelMap.get(node.handler), node.type)); 76 | } 77 | 78 | return clone; 79 | } 80 | 81 | public static List cloneLocals(Map labelMap, List locals) { 82 | ArrayList clone = new ArrayList<>(locals.size()); 83 | for (LocalVariableNode node : locals) { 84 | clone.add(new LocalVariableNode(node.name, node.desc, node.signature, labelMap.get(node.start), labelMap.get(node.end), node.index)); 85 | } 86 | 87 | return clone; 88 | } 89 | 90 | public static void copy(MethodNode src, MethodNode dst) { 91 | Map labelMap = cloneLabels(src.instructions); 92 | dst.instructions = cloneInsnList(labelMap, src.instructions); 93 | dst.tryCatchBlocks = cloneTryCatchBlocks(labelMap, src.tryCatchBlocks); 94 | if (src.localVariables != null) { 95 | dst.localVariables = cloneLocals(labelMap, src.localVariables); 96 | } 97 | dst.visibleAnnotations = src.visibleAnnotations; 98 | dst.invisibleAnnotations = src.invisibleAnnotations; 99 | dst.visitMaxs(src.maxStack, src.maxLocals); 100 | } 101 | 102 | public static String toString(InsnList list) { 103 | return new InsnListSection(list).toString(); 104 | } 105 | 106 | public static int getLocal(List list, String name) { 107 | int found = -1; 108 | for (LocalVariableNode node : list) { 109 | if (node.name.equals(name)) { 110 | if (found >= 0) { 111 | throw new RuntimeException("Duplicate local variable: " + name + " not coded to handle this scenario."); 112 | } 113 | 114 | found = node.index; 115 | } 116 | } 117 | return found; 118 | } 119 | 120 | public static void replaceMethod(MethodNode original, MethodNode replacement) { 121 | original.instructions.clear(); 122 | if (original.localVariables != null) { 123 | original.localVariables.clear(); 124 | } 125 | if (original.tryCatchBlocks != null) { 126 | original.tryCatchBlocks.clear(); 127 | } 128 | replacement.accept(original); 129 | } 130 | 131 | public static void dump(Acceptor acceptor, File file, boolean filterImportant, boolean sortLocals, boolean textify) { 132 | try { 133 | if (!file.exists()) { 134 | if (!file.getParentFile().exists()) { 135 | file.getParentFile().mkdirs(); 136 | } 137 | file.createNewFile(); 138 | } 139 | 140 | PrintWriter pout = new PrintWriter(file); 141 | ClassVisitor cv = new TraceClassVisitor(null, textify ? new Textifier() : new ASMifier(), pout); 142 | if (filterImportant) { 143 | cv = new ImportantInsnVisitor(cv); 144 | } 145 | if (sortLocals) { 146 | cv = new LocalVariablesSorterVisitor(cv); 147 | } 148 | acceptor.accept(cv); 149 | pout.close(); 150 | } catch (IOException e) { 151 | LOGGER.warn("Failed to dump class to file!", e); 152 | } 153 | } 154 | 155 | public static void dump(final byte[] bytes, File file, boolean filterImportant, boolean sortLocals, boolean textify) { 156 | dump(cv -> new ClassReader(bytes).accept(cv, ClassReader.EXPAND_FRAMES), file, filterImportant, sortLocals, textify); 157 | } 158 | 159 | public static void dump(final InputStream is, File file, boolean filterImportant, boolean sortLocals, boolean textify) { 160 | dump(cv -> new ClassReader(is).accept(cv, ClassReader.EXPAND_FRAMES), file, filterImportant, sortLocals, textify); 161 | } 162 | 163 | public static void dump(final ClassNode cnode, File file, boolean filterImportant, boolean sortLocals, boolean textify) { 164 | dump(cnode::accept, file, filterImportant, sortLocals, textify); 165 | } 166 | 167 | public interface Acceptor { 168 | 169 | void accept(ClassVisitor cv) throws IOException; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/InsnComparator.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm; 2 | 3 | import org.objectweb.asm.tree.*; 4 | 5 | import java.util.*; 6 | 7 | import static org.objectweb.asm.tree.AbstractInsnNode.*; 8 | 9 | public class InsnComparator { 10 | 11 | public static boolean varInsnEqual(VarInsnNode insn1, VarInsnNode insn2) { 12 | return insn1.var == -1 || insn2.var == -1 || insn1.var == insn2.var; 13 | } 14 | 15 | public static boolean methodInsnEqual(MethodInsnNode insn1, MethodInsnNode insn2) { 16 | return insn1.owner.equals(insn2.owner) && insn1.name.equals(insn2.name) && insn1.desc.equals(insn2.desc); 17 | } 18 | 19 | public static boolean fieldInsnEqual(FieldInsnNode insn1, FieldInsnNode insn2) { 20 | return insn1.owner.equals(insn2.owner) && insn1.name.equals(insn2.name) && insn1.desc.equals(insn2.desc); 21 | } 22 | 23 | public static boolean ldcInsnEqual(LdcInsnNode insn1, LdcInsnNode insn2) { 24 | return insn1.cst == null || insn2.cst == null || insn1.cst.equals(insn2.cst); 25 | } 26 | 27 | public static boolean typeInsnEqual(TypeInsnNode insn1, TypeInsnNode insn2) { 28 | return insn1.desc.equals("*") || insn2.desc.equals("*") || insn1.desc.equals(insn2.desc); 29 | } 30 | 31 | public static boolean iincInsnEqual(IincInsnNode node1, IincInsnNode node2) { 32 | return node1.var == node2.var && node1.incr == node2.incr; 33 | } 34 | 35 | public static boolean intInsnEqual(IntInsnNode node1, IntInsnNode node2) { 36 | return node1.operand == -1 || node2.operand == -1 || node1.operand == node2.operand; 37 | } 38 | 39 | public static boolean insnEqual(AbstractInsnNode node1, AbstractInsnNode node2) { 40 | if (node1.getOpcode() != node2.getOpcode()) { 41 | return false; 42 | } 43 | 44 | switch (node2.getType()) { 45 | case VAR_INSN: 46 | return varInsnEqual((VarInsnNode) node1, (VarInsnNode) node2); 47 | case TYPE_INSN: 48 | return typeInsnEqual((TypeInsnNode) node1, (TypeInsnNode) node2); 49 | case FIELD_INSN: 50 | return fieldInsnEqual((FieldInsnNode) node1, (FieldInsnNode) node2); 51 | case METHOD_INSN: 52 | return methodInsnEqual((MethodInsnNode) node1, (MethodInsnNode) node2); 53 | case LDC_INSN: 54 | return ldcInsnEqual((LdcInsnNode) node1, (LdcInsnNode) node2); 55 | case IINC_INSN: 56 | return iincInsnEqual((IincInsnNode) node1, (IincInsnNode) node2); 57 | case INT_INSN: 58 | return intInsnEqual((IntInsnNode) node1, (IntInsnNode) node2); 59 | default: 60 | return true; 61 | } 62 | } 63 | 64 | public static boolean insnImportant(AbstractInsnNode insn, Set controlFlowLabels) { 65 | switch (insn.getType()) { 66 | case LINE: 67 | case FRAME: 68 | return false; 69 | case LABEL: 70 | return controlFlowLabels.contains(insn); 71 | default: 72 | return true; 73 | } 74 | } 75 | 76 | public static Set getControlFlowLabels(InsnListSection list) { 77 | return getControlFlowLabels(list.list); 78 | } 79 | 80 | public static Set getControlFlowLabels(InsnList list) { 81 | HashSet controlFlowLabels = new HashSet<>(); 82 | for (AbstractInsnNode insn = list.getFirst(); insn != null; insn = insn.getNext()) { 83 | switch (insn.getType()) { 84 | case JUMP_INSN: 85 | JumpInsnNode jinsn = (JumpInsnNode) insn; 86 | controlFlowLabels.add(jinsn.label); 87 | break; 88 | case TABLESWITCH_INSN: 89 | TableSwitchInsnNode tsinsn = (TableSwitchInsnNode) insn; 90 | controlFlowLabels.add(tsinsn.dflt); 91 | controlFlowLabels.addAll(tsinsn.labels); 92 | break; 93 | case LOOKUPSWITCH_INSN: 94 | LookupSwitchInsnNode lsinsn = (LookupSwitchInsnNode) insn; 95 | controlFlowLabels.add(lsinsn.dflt); 96 | controlFlowLabels.addAll(lsinsn.labels); 97 | break; 98 | } 99 | } 100 | return controlFlowLabels; 101 | } 102 | 103 | public static InsnList getImportantList(InsnList list) { 104 | return getImportantList(new InsnListSection(list)).list; 105 | } 106 | 107 | public static InsnListSection getImportantList(InsnListSection list) { 108 | if (list.size() == 0) { 109 | return list; 110 | } 111 | 112 | Set controlFlowLabels = getControlFlowLabels(list); 113 | Map labelMap = new HashMap<>(); 114 | controlFlowLabels.forEach(e -> labelMap.put(e, e)); 115 | 116 | InsnListSection importantNodeList = new InsnListSection(); 117 | for (AbstractInsnNode insn : list) { 118 | if (insnImportant(insn, controlFlowLabels)) { 119 | importantNodeList.add(insn.clone(labelMap)); 120 | } 121 | } 122 | 123 | return importantNodeList; 124 | } 125 | 126 | public static List find(InsnListSection haystack, InsnListSection needle) { 127 | Set controlFlowLabels = getControlFlowLabels(haystack); 128 | LinkedList list = new LinkedList<>(); 129 | for (int start = 0; start <= haystack.size() - needle.size(); start++) { 130 | InsnListSection section = matches(haystack.drop(start), needle, controlFlowLabels); 131 | if (section != null) { 132 | list.add(section); 133 | start = section.end - 1; 134 | } 135 | } 136 | 137 | return list; 138 | } 139 | 140 | public static List find(InsnList haystack, InsnListSection needle) { 141 | return find(new InsnListSection(haystack), needle); 142 | } 143 | 144 | public static InsnListSection matches(InsnListSection haystack, InsnListSection needle, Set controlFlowLabels) { 145 | int h = 0, n = 0; 146 | for (; h < haystack.size() && n < needle.size(); h++) { 147 | AbstractInsnNode insn = haystack.get(h); 148 | if (!insnImportant(insn, controlFlowLabels)) { 149 | continue; 150 | } 151 | 152 | if (!insnEqual(haystack.get(h), needle.get(n))) { 153 | return null; 154 | } 155 | n++; 156 | } 157 | if (n != needle.size()) { 158 | return null; 159 | } 160 | 161 | return haystack.take(h); 162 | } 163 | 164 | public static InsnListSection findOnce(InsnListSection haystack, InsnListSection needle) { 165 | List list = find(haystack, needle); 166 | if (list.size() != 1) { 167 | throw new RuntimeException("Needle found " + list.size() + " times in Haystack:\n" + haystack + "\n\n" + needle); 168 | } 169 | 170 | return list.get(0); 171 | } 172 | 173 | public static InsnListSection findOnce(InsnList haystack, InsnListSection needle) { 174 | return findOnce(new InsnListSection(haystack), needle); 175 | } 176 | 177 | public static List findN(InsnListSection haystack, InsnListSection needle) { 178 | List list = find(haystack, needle); 179 | if (list.isEmpty()) { 180 | throw new RuntimeException("Needle not found in Haystack:\n" + haystack + "\n\n" + needle); 181 | } 182 | 183 | return list; 184 | } 185 | 186 | public static List findN(InsnList haystack, InsnListSection needle) { 187 | return findN(new InsnListSection(haystack), needle); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/InsnListSection.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm; 2 | 3 | import org.objectweb.asm.MethodVisitor; 4 | import org.objectweb.asm.tree.*; 5 | import org.objectweb.asm.util.Textifier; 6 | import org.objectweb.asm.util.TraceMethodVisitor; 7 | 8 | import java.io.PrintWriter; 9 | import java.io.StringWriter; 10 | import java.util.HashMap; 11 | import java.util.Iterator; 12 | import java.util.Map; 13 | import java.util.Map.Entry; 14 | import java.util.Set; 15 | 16 | import static org.objectweb.asm.tree.AbstractInsnNode.*; 17 | 18 | /** 19 | * A section of an InsnList, may become invalid if the insn list is modified 20 | */ 21 | public class InsnListSection implements Iterable { 22 | 23 | public InsnList list; 24 | public int start; 25 | public int end; 26 | 27 | public InsnListSection(InsnList list, int start, int end) { 28 | this.list = list; 29 | this.start = start; 30 | this.end = end; 31 | } 32 | 33 | public InsnListSection(InsnList list, AbstractInsnNode first, AbstractInsnNode last) { 34 | this(list, list.indexOf(first), list.indexOf(last) + 1); 35 | } 36 | 37 | public InsnListSection(InsnList list) { 38 | this(list, 0, list.size()); 39 | } 40 | 41 | public InsnListSection() { 42 | this(new InsnList()); 43 | } 44 | 45 | public void accept(MethodVisitor mv) { 46 | for (AbstractInsnNode insn : this) { 47 | insn.accept(mv); 48 | } 49 | } 50 | 51 | public AbstractInsnNode getFirst() { 52 | return size() == 0 ? null : list.get(start); 53 | } 54 | 55 | public AbstractInsnNode getLast() { 56 | return size() == 0 ? null : list.get(end - 1); 57 | } 58 | 59 | public int size() { 60 | return end - start; 61 | } 62 | 63 | public AbstractInsnNode get(int i) { 64 | return list.get(start + i); 65 | } 66 | 67 | public void set(int i, AbstractInsnNode insn) { 68 | list.set(get(i), insn); 69 | } 70 | 71 | public void remove(int i) { 72 | list.remove(get(i)); 73 | end--; 74 | } 75 | 76 | public void replace(AbstractInsnNode location, AbstractInsnNode insn) { 77 | list.set(location, insn); 78 | } 79 | 80 | public void add(AbstractInsnNode insn) { 81 | list.add(insn); 82 | end++; 83 | } 84 | 85 | public void insertBefore(InsnList insns) { 86 | int s = insns.size(); 87 | if (this.list.size() == 0) { 88 | list.insert(insns); 89 | } else { 90 | list.insertBefore(list.get(start), insns); 91 | } 92 | start += s; 93 | end += s; 94 | } 95 | 96 | public void insert(InsnList insns) { 97 | if (end == 0) { 98 | list.insert(insns); 99 | } else { 100 | list.insert(list.get(end - 1), insns); 101 | } 102 | } 103 | 104 | public void replace(InsnList insns) { 105 | int s = insns.size(); 106 | remove(); 107 | insert(insns); 108 | end = start + s; 109 | } 110 | 111 | public void remove() { 112 | while (end != start) { 113 | remove(0); 114 | } 115 | } 116 | 117 | public void setLast(AbstractInsnNode last) { 118 | end = list.indexOf(last) + 1; 119 | } 120 | 121 | public void setFirst(AbstractInsnNode first) { 122 | start = list.indexOf(first); 123 | } 124 | 125 | public InsnListSection drop(int n) { 126 | return slice(n, size()); 127 | } 128 | 129 | public InsnListSection take(int n) { 130 | return slice(0, n); 131 | } 132 | 133 | public InsnListSection slice(int start, int end) { 134 | return new InsnListSection(list, this.start + start, this.start + end); 135 | } 136 | 137 | /** 138 | * Removes leading and trailing labels and line number nodes that don't affect control flow 139 | * 140 | * @return this 141 | */ 142 | public InsnListSection trim(Set controlFlowLabels) { 143 | while (start < end && !InsnComparator.insnImportant(getFirst(), controlFlowLabels)) { 144 | start++; 145 | } 146 | 147 | while (start < end && !InsnComparator.insnImportant(getLast(), controlFlowLabels)) { 148 | end--; 149 | } 150 | 151 | return this; 152 | } 153 | 154 | public String toString() { 155 | Textifier t = new Textifier(); 156 | accept(new TraceMethodVisitor(t)); 157 | StringWriter sw = new StringWriter(); 158 | t.print(new PrintWriter(sw)); 159 | return sw.toString(); 160 | } 161 | 162 | public void println() { 163 | System.out.println(toString()); 164 | } 165 | 166 | public HashMap identityLabelMap() { 167 | HashMap labelMap = new HashMap<>(); 168 | for (AbstractInsnNode insn : this) { 169 | switch (insn.getType()) { 170 | case LABEL: 171 | labelMap.put((LabelNode) insn, (LabelNode) insn); 172 | break; 173 | case JUMP_INSN: 174 | labelMap.put(((JumpInsnNode) insn).label, ((JumpInsnNode) insn).label); 175 | break; 176 | case LOOKUPSWITCH_INSN: 177 | LookupSwitchInsnNode linsn = (LookupSwitchInsnNode) insn; 178 | labelMap.put(linsn.dflt, linsn.dflt); 179 | for (LabelNode label : linsn.labels) { 180 | labelMap.put(label, label); 181 | } 182 | break; 183 | case TABLESWITCH_INSN: 184 | TableSwitchInsnNode tinsn = (TableSwitchInsnNode) insn; 185 | labelMap.put(tinsn.dflt, tinsn.dflt); 186 | for (LabelNode label : tinsn.labels) { 187 | labelMap.put(label, label); 188 | } 189 | break; 190 | case FRAME: 191 | FrameNode fnode = (FrameNode) insn; 192 | if (fnode.local != null) { 193 | for (Object o : fnode.local) { 194 | if (o instanceof LabelNode) { 195 | labelMap.put((LabelNode) o, (LabelNode) o); 196 | } 197 | } 198 | } 199 | if (fnode.stack != null) { 200 | for (Object o : fnode.stack) { 201 | if (o instanceof LabelNode) { 202 | labelMap.put((LabelNode) o, (LabelNode) o); 203 | } 204 | } 205 | } 206 | break; 207 | } 208 | } 209 | 210 | return labelMap; 211 | } 212 | 213 | public Map cloneLabels() { 214 | Map labelMap = identityLabelMap(); 215 | for (Entry entry : labelMap.entrySet()) { 216 | entry.setValue(new LabelNode()); 217 | } 218 | 219 | return labelMap; 220 | } 221 | 222 | public InsnListSection copy() { 223 | return copy(cloneLabels()); 224 | } 225 | 226 | public InsnListSection copy(Map labelMap) { 227 | InsnListSection copy = new InsnListSection(); 228 | for (AbstractInsnNode insn : this) { 229 | copy.add(insn.clone(labelMap)); 230 | } 231 | 232 | return copy; 233 | } 234 | 235 | @Override 236 | public Iterator iterator() { 237 | return new InsnListSectionIterator(); 238 | } 239 | 240 | private class InsnListSectionIterator implements Iterator { 241 | 242 | int i = 0; 243 | 244 | @Override 245 | public boolean hasNext() { 246 | return i < size(); 247 | } 248 | 249 | @Override 250 | public AbstractInsnNode next() { 251 | return get(i++); 252 | } 253 | 254 | @Override 255 | public void remove() { 256 | InsnListSection.this.remove(--i); 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/ASMReader.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm; 2 | 3 | import org.objectweb.asm.tree.*; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.InputStreamReader; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | import static org.objectweb.asm.Opcodes.*; 15 | import static org.objectweb.asm.tree.AbstractInsnNode.*; 16 | 17 | public class ASMReader { 18 | 19 | private static final Logger LOGGER = LoggerFactory.getLogger(ASMReader.class); 20 | 21 | public static Map opCodes = new HashMap<>(); 22 | public static byte[] TYPE; 23 | 24 | static { 25 | opCodes.put("NOP", NOP); 26 | opCodes.put("ACONST_NULL", ACONST_NULL); 27 | opCodes.put("ICONST_M1", ICONST_M1); 28 | opCodes.put("ICONST_0", ICONST_0); 29 | opCodes.put("ICONST_1", ICONST_1); 30 | opCodes.put("ICONST_2", ICONST_2); 31 | opCodes.put("ICONST_3", ICONST_3); 32 | opCodes.put("ICONST_4", ICONST_4); 33 | opCodes.put("ICONST_5", ICONST_5); 34 | opCodes.put("LCONST_0", LCONST_0); 35 | opCodes.put("LCONST_1", LCONST_1); 36 | opCodes.put("FCONST_0", FCONST_0); 37 | opCodes.put("FCONST_1", FCONST_1); 38 | opCodes.put("FCONST_2", FCONST_2); 39 | opCodes.put("DCONST_0", DCONST_0); 40 | opCodes.put("DCONST_1", DCONST_1); 41 | opCodes.put("BIPUSH", BIPUSH); 42 | opCodes.put("SIPUSH", SIPUSH); 43 | opCodes.put("LDC", LDC); 44 | opCodes.put("ILOAD", ILOAD); 45 | opCodes.put("LLOAD", LLOAD); 46 | opCodes.put("FLOAD", FLOAD); 47 | opCodes.put("DLOAD", DLOAD); 48 | opCodes.put("ALOAD", ALOAD); 49 | opCodes.put("IALOAD", IALOAD); 50 | opCodes.put("LALOAD", LALOAD); 51 | opCodes.put("FALOAD", FALOAD); 52 | opCodes.put("DALOAD", DALOAD); 53 | opCodes.put("AALOAD", AALOAD); 54 | opCodes.put("BALOAD", BALOAD); 55 | opCodes.put("CALOAD", CALOAD); 56 | opCodes.put("SALOAD", SALOAD); 57 | opCodes.put("ISTORE", ISTORE); 58 | opCodes.put("LSTORE", LSTORE); 59 | opCodes.put("FSTORE", FSTORE); 60 | opCodes.put("DSTORE", DSTORE); 61 | opCodes.put("ASTORE", ASTORE); 62 | opCodes.put("IASTORE", IASTORE); 63 | opCodes.put("LASTORE", LASTORE); 64 | opCodes.put("FASTORE", FASTORE); 65 | opCodes.put("DASTORE", DASTORE); 66 | opCodes.put("AASTORE", AASTORE); 67 | opCodes.put("BASTORE", BASTORE); 68 | opCodes.put("CASTORE", CASTORE); 69 | opCodes.put("SASTORE", SASTORE); 70 | opCodes.put("POP", POP); 71 | opCodes.put("POP2", POP2); 72 | opCodes.put("DUP", DUP); 73 | opCodes.put("DUP_X1", DUP_X1); 74 | opCodes.put("DUP_X2", DUP_X2); 75 | opCodes.put("DUP2", DUP2); 76 | opCodes.put("DUP2_X1", DUP2_X1); 77 | opCodes.put("DUP2_X2", DUP2_X2); 78 | opCodes.put("SWAP", SWAP); 79 | opCodes.put("IADD", IADD); 80 | opCodes.put("LADD", LADD); 81 | opCodes.put("FADD", FADD); 82 | opCodes.put("DADD", DADD); 83 | opCodes.put("ISUB", ISUB); 84 | opCodes.put("LSUB", LSUB); 85 | opCodes.put("FSUB", FSUB); 86 | opCodes.put("DSUB", DSUB); 87 | opCodes.put("IMUL", IMUL); 88 | opCodes.put("LMUL", LMUL); 89 | opCodes.put("FMUL", FMUL); 90 | opCodes.put("DMUL", DMUL); 91 | opCodes.put("IDIV", IDIV); 92 | opCodes.put("LDIV", LDIV); 93 | opCodes.put("FDIV", FDIV); 94 | opCodes.put("DDIV", DDIV); 95 | opCodes.put("IREM", IREM); 96 | opCodes.put("LREM", LREM); 97 | opCodes.put("FREM", FREM); 98 | opCodes.put("DREM", DREM); 99 | opCodes.put("INEG", INEG); 100 | opCodes.put("LNEG", LNEG); 101 | opCodes.put("FNEG", FNEG); 102 | opCodes.put("DNEG", DNEG); 103 | opCodes.put("ISHL", ISHL); 104 | opCodes.put("LSHL", LSHL); 105 | opCodes.put("ISHR", ISHR); 106 | opCodes.put("LSHR", LSHR); 107 | opCodes.put("IUSHR", IUSHR); 108 | opCodes.put("LUSHR", LUSHR); 109 | opCodes.put("IAND", IAND); 110 | opCodes.put("LAND", LAND); 111 | opCodes.put("IOR", IOR); 112 | opCodes.put("LOR", LOR); 113 | opCodes.put("IXOR", IXOR); 114 | opCodes.put("LXOR", LXOR); 115 | opCodes.put("IINC", IINC); 116 | opCodes.put("I2L", I2L); 117 | opCodes.put("I2F", I2F); 118 | opCodes.put("I2D", I2D); 119 | opCodes.put("L2I", L2I); 120 | opCodes.put("L2F", L2F); 121 | opCodes.put("L2D", L2D); 122 | opCodes.put("F2I", F2I); 123 | opCodes.put("F2L", F2L); 124 | opCodes.put("F2D", F2D); 125 | opCodes.put("D2I", D2I); 126 | opCodes.put("D2L", D2L); 127 | opCodes.put("D2F", D2F); 128 | opCodes.put("I2B", I2B); 129 | opCodes.put("I2C", I2C); 130 | opCodes.put("I2S", I2S); 131 | opCodes.put("LCMP", LCMP); 132 | opCodes.put("FCMPL", FCMPL); 133 | opCodes.put("FCMPG", FCMPG); 134 | opCodes.put("DCMPL", DCMPL); 135 | opCodes.put("DCMPG", DCMPG); 136 | opCodes.put("IFEQ", IFEQ); 137 | opCodes.put("IFNE", IFNE); 138 | opCodes.put("IFLT", IFLT); 139 | opCodes.put("IFGE", IFGE); 140 | opCodes.put("IFGT", IFGT); 141 | opCodes.put("IFLE", IFLE); 142 | opCodes.put("IF_ICMPEQ", IF_ICMPEQ); 143 | opCodes.put("IF_ICMPNE", IF_ICMPNE); 144 | opCodes.put("IF_ICMPLT", IF_ICMPLT); 145 | opCodes.put("IF_ICMPGE", IF_ICMPGE); 146 | opCodes.put("IF_ICMPGT", IF_ICMPGT); 147 | opCodes.put("IF_ICMPLE", IF_ICMPLE); 148 | opCodes.put("IF_ACMPEQ", IF_ACMPEQ); 149 | opCodes.put("IF_ACMPNE", IF_ACMPNE); 150 | opCodes.put("GOTO", GOTO); 151 | opCodes.put("JSR", JSR); 152 | opCodes.put("RET", RET); 153 | opCodes.put("TABLESWITCH", TABLESWITCH); 154 | opCodes.put("LOOKUPSWITCH", LOOKUPSWITCH); 155 | opCodes.put("IRETURN", IRETURN); 156 | opCodes.put("LRETURN", LRETURN); 157 | opCodes.put("FRETURN", FRETURN); 158 | opCodes.put("DRETURN", DRETURN); 159 | opCodes.put("ARETURN", ARETURN); 160 | opCodes.put("RETURN", RETURN); 161 | opCodes.put("GETSTATIC", GETSTATIC); 162 | opCodes.put("PUTSTATIC", PUTSTATIC); 163 | opCodes.put("GETFIELD", GETFIELD); 164 | opCodes.put("PUTFIELD", PUTFIELD); 165 | opCodes.put("INVOKEVIRTUAL", INVOKEVIRTUAL); 166 | opCodes.put("INVOKESPECIAL", INVOKESPECIAL); 167 | opCodes.put("INVOKESTATIC", INVOKESTATIC); 168 | opCodes.put("INVOKEINTERFACE", INVOKEINTERFACE); 169 | opCodes.put("INVOKEDYNAMIC", INVOKEDYNAMIC); 170 | opCodes.put("NEW", NEW); 171 | opCodes.put("NEWARRAY", NEWARRAY); 172 | opCodes.put("ANEWARRAY", ANEWARRAY); 173 | opCodes.put("ARRAYLENGTH", ARRAYLENGTH); 174 | opCodes.put("ATHROW", ATHROW); 175 | opCodes.put("CHECKCAST", CHECKCAST); 176 | opCodes.put("INSTANCEOF", INSTANCEOF); 177 | opCodes.put("MONITORENTER", MONITORENTER); 178 | opCodes.put("MONITOREXIT", MONITOREXIT); 179 | opCodes.put("MULTIANEWARRAY", MULTIANEWARRAY); 180 | opCodes.put("IFNULL", IFNULL); 181 | opCodes.put("IFNONNULL", IFNONNULL); 182 | 183 | //derived from classWriter, mapped to AbstractInsnNode 184 | TYPE = new byte[200]; 185 | //@formatter:off 186 | String s = 187 | "AAAAAAAAAAAAAAAABBJ__CCCCC____________________AAAAAAAACC" + 188 | "CCC____________________AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + 189 | "AAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHHHHCLMAA" + 190 | "AAAAEEEEFFFFGDBDAADDAA_NHH"; 191 | //@formatter:on 192 | for (int i = 0; i < s.length(); i++) { 193 | TYPE[i] = (byte) (s.charAt(i) - 'A'); 194 | } 195 | } 196 | 197 | public static Map loadResource(String res) { 198 | return loadResource(ASMHelper.class.getResourceAsStream(res), res); 199 | } 200 | 201 | public static Map loadResource(InputStream in, String res) { 202 | HashMap blocks = new HashMap<>(); 203 | String current = "unnamed"; 204 | ASMBlock block = new ASMBlock(); 205 | try { 206 | BufferedReader r = new BufferedReader(new InputStreamReader(in)); 207 | String line; 208 | while ((line = r.readLine()) != null) { 209 | { 210 | int hpos = line.indexOf('#'); 211 | if (hpos >= 0) { 212 | line = line.substring(0, hpos); 213 | } 214 | } 215 | line = line.trim(); 216 | if (line.isEmpty()) { 217 | continue; 218 | } 219 | if (line.startsWith("list ")) { 220 | if (block.list.size() > 0) { 221 | blocks.put(current, block); 222 | } 223 | current = line.substring(5); 224 | block = new ASMBlock(); 225 | continue; 226 | } 227 | 228 | try { 229 | AbstractInsnNode insn = null; 230 | String[] split = line.replace(" : ", ":").split(" "); 231 | Integer i_opcode = opCodes.get(split[0]); 232 | if (i_opcode == null) { 233 | if (split[0].equals("LINENUMBER")) { 234 | insn = new LineNumberNode(Integer.parseInt(split[1]), block.getOrAdd(split[2])); 235 | } else if (split[0].startsWith("L")) { 236 | insn = block.getOrAdd(split[0]); 237 | } else { 238 | throw new Exception("Unknown opcode " + split[0]); 239 | } 240 | } else { 241 | int opcode = i_opcode; 242 | switch (TYPE[opcode]) { 243 | case INSN: 244 | insn = new InsnNode(opcode); 245 | break; 246 | case INT_INSN: 247 | insn = new IntInsnNode(opcode, Integer.parseInt(split[1])); 248 | break; 249 | case VAR_INSN: 250 | insn = new VarInsnNode(opcode, Integer.parseInt(split[1])); 251 | break; 252 | case TYPE_INSN: 253 | insn = new ObfMapping(split[1]).toInsn(opcode); 254 | break; 255 | case FIELD_INSN: 256 | case METHOD_INSN: 257 | StringBuilder sb = new StringBuilder(); 258 | for (int i = 1; i < split.length; i++) { 259 | sb.append(split[i]); 260 | } 261 | insn = ObfMapping.fromDesc(sb.toString()).toInsn(opcode); 262 | break; 263 | case INVOKE_DYNAMIC_INSN: 264 | throw new Exception("Found INVOKEDYNAMIC while reading"); 265 | case JUMP_INSN: 266 | insn = new JumpInsnNode(opcode, block.getOrAdd(split[1])); 267 | break; 268 | case LDC_INSN: 269 | StringBuilder csb = new StringBuilder(); 270 | for (int i = 1; i < split.length; i++) { 271 | csb.append(split[i]); 272 | } 273 | String cst = csb.toString(); 274 | if (cst.equals("*")) { 275 | insn = new LdcInsnNode(null); 276 | } else if (cst.endsWith("\"")) { 277 | insn = new LdcInsnNode(cst.substring(1, cst.length() - 1)); 278 | } else if (cst.endsWith("L")) { 279 | insn = new LdcInsnNode(Long.valueOf(cst.substring(0, cst.length() - 1))); 280 | } else if (cst.endsWith("F")) { 281 | insn = new LdcInsnNode(Float.valueOf(cst.substring(0, cst.length() - 1))); 282 | } else if (cst.endsWith("D")) { 283 | insn = new LdcInsnNode(Double.valueOf(cst.substring(0, cst.length() - 1))); 284 | } else if (cst.contains(".")) { 285 | insn = new LdcInsnNode(Double.valueOf(cst)); 286 | } else { 287 | insn = new LdcInsnNode(Integer.valueOf(cst)); 288 | } 289 | break; 290 | case IINC_INSN: 291 | insn = new IincInsnNode(opcode, Integer.parseInt(split[1])); 292 | break; 293 | case LABEL: 294 | throw new Exception("Use L# for labels"); 295 | case TABLESWITCH_INSN: 296 | case LOOKUPSWITCH_INSN: 297 | throw new Exception("I don't know how to deal with this insn type"); 298 | case MULTIANEWARRAY_INSN: 299 | insn = new MultiANewArrayInsnNode(split[1], Integer.parseInt(split[2])); 300 | break; 301 | case FRAME: 302 | throw new Exception("Use ClassWriter.COMPUTE_FRAMES"); 303 | } 304 | } 305 | 306 | if (insn != null) { 307 | block.list.add(insn); 308 | } 309 | } catch (Exception e) { 310 | LOGGER.error("Error while reading ASM Block {} from {}, line: {}", current, res, line, e); 311 | } 312 | } 313 | 314 | r.close(); 315 | if (block.list.size() > 0) { 316 | blocks.put(current, block); 317 | } 318 | } catch (IOException e) { 319 | throw new RuntimeException("Failed to read ASM resource: " + res, e); 320 | } 321 | return blocks; 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 2013-2020 Chicken-Bones, covers1624 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | {signature of Ty Coon}, 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | -------------------------------------------------------------------------------- /src/main/java/codechicken/asm/StackAnalyser.java: -------------------------------------------------------------------------------- 1 | package codechicken.asm; 2 | 3 | import org.objectweb.asm.ConstantDynamic; 4 | import org.objectweb.asm.Handle; 5 | import org.objectweb.asm.Type; 6 | import org.objectweb.asm.tree.*; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.*; 11 | import java.util.stream.Stream; 12 | import java.util.stream.StreamSupport; 13 | 14 | import static org.objectweb.asm.Opcodes.*; 15 | import static org.objectweb.asm.Type.*; 16 | import static org.objectweb.asm.tree.AbstractInsnNode.*; 17 | 18 | /** 19 | * Created by covers1624 on 21/5/20. 20 | */ 21 | public class StackAnalyser { 22 | 23 | private static final boolean DEBUG = Boolean.getBoolean("codechicken.mixin.StackAnalyser.debug"); 24 | private static final boolean DEBUG_FRAMES = Boolean.getBoolean("codechicken.mixin.StackAnalyser.debug_frames"); 25 | private static final Logger LOGGER = LoggerFactory.getLogger(StackAnalyser.class); 26 | 27 | public static int width(Type type) { 28 | return type.getSize(); 29 | } 30 | 31 | public static int width(String type) { 32 | return width(getType(type)); 33 | } 34 | 35 | public static int width(Iterable it) { 36 | return width(StreamSupport.stream(it.spliterator(), false)); 37 | } 38 | 39 | public static int width(Type[] it) { 40 | return width(Arrays.stream(it)); 41 | } 42 | 43 | public static int width(Stream stream) { 44 | return stream 45 | .mapToInt(StackAnalyser::width) 46 | .reduce(Integer::sum) 47 | .orElse(0); 48 | } 49 | 50 | public final Type owner; 51 | public final MethodNode mNode; 52 | 53 | private final List stack = new LinkedList<>(); 54 | private final List locals = new LinkedList<>(); 55 | private final Map catchHandlers = new HashMap<>(); 56 | 57 | public StackAnalyser(Type owner, MethodNode mNode) { 58 | this.owner = owner; 59 | this.mNode = mNode; 60 | 61 | if ((mNode.access & ACC_STATIC) == 0) { 62 | pushL(new This(owner)); 63 | } 64 | 65 | Type[] pTypes = getArgumentTypes(mNode.desc); 66 | for (int i = 0; i < pTypes.length; i++) { 67 | pushL(new Param(pTypes[i], i)); 68 | } 69 | 70 | for (TryCatchBlockNode node : mNode.tryCatchBlocks) { 71 | catchHandlers.put(node.handler, node); 72 | } 73 | } 74 | 75 | public void pushL(LocalEntry entry) { 76 | locals.add(entry);//To the end 77 | } 78 | 79 | public void setL(int i, LocalEntry entry) { 80 | while (i + entry.type.getSize() > locals.size()) { 81 | locals.add(null);//Ensure room 82 | } 83 | locals.set(i, entry); 84 | if (entry.type.getSize() == 2) { 85 | locals.set(i + 1, entry); 86 | } 87 | } 88 | 89 | public void push(StackEntry entry) { 90 | insert(0, entry); 91 | } 92 | 93 | public StackEntry _pop() { 94 | return _pop(0); 95 | } 96 | 97 | public StackEntry _pop(int i) { 98 | return stack.remove(stack.size() - i - 1); 99 | } 100 | 101 | public StackEntry pop() { 102 | return pop(0); 103 | } 104 | 105 | public StackEntry pop(int i) { 106 | StackEntry e = _pop(i); 107 | if (e.type.getSize() == 2) { 108 | if (peek(i) != e) { 109 | throw new IllegalStateException("Wide stack entry elems don't match (" + e + ", " + peek(i) + ")"); 110 | } 111 | _pop(i); 112 | } 113 | return e; 114 | } 115 | 116 | public StackEntry peek() { 117 | return peek(0); 118 | } 119 | 120 | public StackEntry peek(int i) { 121 | return stack.get(stack.size() - i - 1); 122 | } 123 | 124 | public void insert(int i, StackEntry entry) { 125 | if (entry.type.getSize() == 0) { 126 | return; 127 | } 128 | stack.add(stack.size() - i, entry); 129 | if (entry.type.getSize() == 2) { 130 | stack.add(stack.size() - i, entry); 131 | } 132 | } 133 | 134 | public List popArgs(String desc) { 135 | int len = getArgumentTypes(desc).length; 136 | StackEntry[] args = new StackEntry[len]; 137 | for (int i = 0; i < len; i++) { 138 | args[len - i - 1] = pop(); 139 | } 140 | return Arrays.asList(args); 141 | } 142 | 143 | public void visitInsn(AbstractInsnNode aInsn) { 144 | switch (aInsn.getType()) { 145 | case INSN: 146 | handleInsnNode((InsnNode) aInsn); 147 | break; 148 | case INT_INSN: { 149 | IntInsnNode insn = (IntInsnNode) aInsn; 150 | switch (insn.getOpcode()) { 151 | //@formatter:off 152 | case BIPUSH: push(new Const(insn, ((byte) insn.operand))); break; 153 | case SIPUSH: push(new Const(insn, ((short) insn.operand))); break; 154 | //@formatter:on 155 | default: { 156 | if (DEBUG) { 157 | LOGGER.warn("Unhandled Opcode for IntInsnNode: {}", insn.getOpcode()); 158 | } 159 | } 160 | } 161 | break; 162 | } 163 | case LDC_INSN: { 164 | LdcInsnNode insn = (LdcInsnNode) aInsn; 165 | if (insn.getOpcode() == LDC) { 166 | push(new Const(insn, insn.cst)); 167 | } else if (DEBUG) { 168 | LOGGER.warn("Unhandled Opcode for LdcInsnNode: {}", insn.getOpcode()); 169 | } 170 | break; 171 | } 172 | case VAR_INSN: { 173 | VarInsnNode insn = (VarInsnNode) aInsn; 174 | switch (insn.getOpcode()) { 175 | case ILOAD: 176 | case LLOAD: 177 | case FLOAD: 178 | case DLOAD: 179 | case ALOAD: 180 | push(new Load(insn, locals.get(insn.var))); 181 | break; 182 | case ISTORE: 183 | case LSTORE: 184 | case FSTORE: 185 | case DSTORE: 186 | case ASTORE: 187 | setL(insn.var, new Store(pop())); 188 | break; 189 | default: { 190 | if (DEBUG) { 191 | LOGGER.warn("Unhandled Opcode for VarInsnNode: {}", insn.getOpcode()); 192 | } 193 | } 194 | } 195 | break; 196 | } 197 | case IINC_INSN: { 198 | IincInsnNode insn = (IincInsnNode) aInsn; 199 | if (insn.getOpcode() == IINC) { 200 | setL(insn.var, new Store(new BinaryOp(insn, new Const(insn, insn.incr), new Load(insn, locals.get(insn.var))))); 201 | } else if (DEBUG) { 202 | LOGGER.warn("Unhandled Opcode for IincInsnNode: {}", insn.getOpcode()); 203 | } 204 | break; 205 | } 206 | case JUMP_INSN: { 207 | JumpInsnNode insn = (JumpInsnNode) aInsn; 208 | switch (insn.getOpcode()) { 209 | case IFEQ: 210 | case IFNE: 211 | case IFLT: 212 | case IFGE: 213 | case IFGT: 214 | case IFLE: 215 | pop(); 216 | break; 217 | case IF_ICMPEQ: 218 | case IF_ICMPNE: 219 | case IF_ICMPLT: 220 | case IF_ICMPGE: 221 | case IF_ICMPGT: 222 | case IF_ICMPLE: 223 | case IF_ACMPEQ: 224 | case IF_ACMPNE: 225 | pop(); 226 | pop(); 227 | break; 228 | case JSR: 229 | push(new ReturnAddress(insn)); 230 | break; 231 | case IFNULL: 232 | case IFNONNULL: 233 | pop(); 234 | break; 235 | case GOTO: 236 | break; 237 | default: { 238 | if (DEBUG) { 239 | LOGGER.warn("Unhandled Opcode for JumpInsnNode: {}", insn.getOpcode()); 240 | } 241 | } 242 | } 243 | break; 244 | } 245 | case TABLESWITCH_INSN: 246 | case LOOKUPSWITCH_INSN: 247 | pop(); 248 | break; 249 | case FIELD_INSN: { 250 | FieldInsnNode insn = (FieldInsnNode) aInsn; 251 | switch (aInsn.getOpcode()) { 252 | case GETSTATIC: 253 | push(new GetField(insn, null)); 254 | break; 255 | case PUTSTATIC: 256 | pop(); 257 | break; 258 | case GETFIELD: 259 | push(new GetField(insn, pop())); 260 | break; 261 | case PUTFIELD: 262 | pop(); 263 | pop(); 264 | break; 265 | default: 266 | if (DEBUG) { 267 | LOGGER.warn("Unhandled Opcode for FieldInsnNode: {}", insn.getOpcode()); 268 | } 269 | } 270 | break; 271 | } 272 | case METHOD_INSN: { 273 | MethodInsnNode insn = (MethodInsnNode) aInsn; 274 | switch (insn.getOpcode()) { 275 | case INVOKEVIRTUAL: 276 | case INVOKESPECIAL: 277 | case INVOKEINTERFACE: 278 | push(new Invoke(insn, popArgs(insn.desc), pop())); 279 | break; 280 | case INVOKESTATIC: { 281 | push(new Invoke(insn, popArgs(insn.desc), null)); 282 | break; 283 | } 284 | default: 285 | if (DEBUG) { 286 | LOGGER.warn("Unhandled Opcode for MethodInsnNode: {}", insn.getOpcode()); 287 | } 288 | } 289 | break; 290 | } 291 | case INVOKE_DYNAMIC_INSN: { 292 | InvokeDynamicInsnNode insn = (InvokeDynamicInsnNode) aInsn; 293 | push(new InvokeDynamic(insn, popArgs(insn.desc))); 294 | break; 295 | } 296 | case TYPE_INSN: { 297 | TypeInsnNode insn = (TypeInsnNode) aInsn; 298 | switch (insn.getOpcode()) { 299 | case NEW: 300 | push(new New(insn, getObjectType(insn.desc))); 301 | break; 302 | case NEWARRAY: 303 | push(new NewArray(insn, getObjectType(insn.desc), pop())); 304 | break; 305 | case ANEWARRAY: 306 | push(new NewArray(insn, getType("[" + insn.desc), pop())); 307 | break; 308 | case CHECKCAST: 309 | push(new Cast(insn, getObjectType(insn.desc), pop())); 310 | break; 311 | case INSTANCEOF: 312 | push(new UnaryOp(insn, pop())); 313 | break; 314 | default: 315 | if (DEBUG) { 316 | LOGGER.warn("Unhandled Opcode for TypeInsnNode: {}", insn.getOpcode()); 317 | } 318 | } 319 | break; 320 | } 321 | case MULTIANEWARRAY_INSN: { 322 | MultiANewArrayInsnNode insn = (MultiANewArrayInsnNode) aInsn; 323 | List sizes = new ArrayList<>(insn.dims); 324 | for (int i = 0; i < insn.dims; i++) { 325 | sizes.add(pop()); 326 | } 327 | push(new NewMultiArray(insn, getType(insn.desc), sizes)); 328 | break; 329 | } 330 | case FRAME: 331 | if (DEBUG_FRAMES) { 332 | FrameNode insn = (FrameNode) aInsn; 333 | switch (insn.type) { 334 | case F_NEW: 335 | case F_FULL: 336 | LOGGER.info("Reset stacks/locals."); 337 | break; 338 | case F_APPEND: 339 | LOGGER.info("Add locals."); 340 | break; 341 | case F_CHOP: 342 | LOGGER.info("Remove locals."); 343 | break; 344 | case F_SAME: 345 | LOGGER.info("Reset."); 346 | break; 347 | case F_SAME1: 348 | LOGGER.info("Reset locals and all but bottom stack."); 349 | break; 350 | default: 351 | LOGGER.info("Unhandled frame type: {}", insn.type); 352 | } 353 | } 354 | break; 355 | case LABEL: { 356 | LabelNode insn = (LabelNode) aInsn; 357 | TryCatchBlockNode handlerNode = catchHandlers.get(insn); 358 | if (handlerNode != null && handlerNode.type != null) { 359 | push(new CaughtException(insn, getObjectType(handlerNode.type))); 360 | } 361 | break; 362 | } 363 | default: 364 | if (DEBUG) { 365 | LOGGER.warn("Unhandled AbstractInsnNode type: {}", aInsn.getType()); 366 | } 367 | } 368 | 369 | } 370 | 371 | private void handleInsnNode(InsnNode insn) { 372 | switch (insn.getOpcode()) { 373 | //@formatter:off 374 | case ACONST_NULL: push(new Const(insn, null)); break; 375 | case ICONST_M1: push(new Const(insn, -1)); break; 376 | case ICONST_0: push(new Const(insn, 0)); break; 377 | case ICONST_1: push(new Const(insn, 1)); break; 378 | case ICONST_2: push(new Const(insn, 2)); break; 379 | case ICONST_3: push(new Const(insn, 3)); break; 380 | case ICONST_4: push(new Const(insn, 4)); break; 381 | case ICONST_5: push(new Const(insn, 5)); break; 382 | case LCONST_0: push(new Const(insn, 0L)); break; 383 | case LCONST_1: push(new Const(insn, 1L)); break; 384 | case FCONST_0: push(new Const(insn, 0F)); break; 385 | case FCONST_1: push(new Const(insn, 1F)); break; 386 | case FCONST_2: push(new Const(insn, 2F)); break; 387 | case DCONST_0: push(new Const(insn, 0D)); break; 388 | case DCONST_1: push(new Const(insn, 1D)); break; 389 | case IALOAD: 390 | case LALOAD: 391 | case FALOAD: 392 | case DALOAD: 393 | case AALOAD: 394 | case BALOAD: 395 | case CALOAD: 396 | case SALOAD: push(new ArrayLoad(insn, pop(), pop())); break; 397 | case IASTORE: 398 | case LASTORE: 399 | case FASTORE: 400 | case DASTORE: 401 | case AASTORE: 402 | case BASTORE: 403 | case CASTORE: 404 | case SASTORE: pop(); pop(); pop(); break; 405 | case POP: pop(); break; 406 | case POP2: _pop(); _pop(); break; 407 | case DUP: push(peek()); break; 408 | case DUP_X1: insert(2, peek()); break; 409 | case DUP_X2: insert(3, peek()); break; 410 | case DUP2: push(peek(1)); push(peek(1)); break; 411 | case DUP2_X1: insert(3, peek(1)); insert(3, peek()); break; 412 | case DUP2_X2: insert(4, peek(1)); insert(4, peek()); break; 413 | case SWAP: push(pop(1)); break; 414 | case IADD: 415 | case LADD: 416 | case FADD: 417 | case DADD: 418 | case ISUB: 419 | case LSUB: 420 | case FSUB: 421 | case DSUB: 422 | case IMUL: 423 | case LMUL: 424 | case FMUL: 425 | case DMUL: 426 | case IDIV: 427 | case LDIV: 428 | case FDIV: 429 | case DDIV: 430 | case IREM: 431 | case LREM: 432 | case FREM: 433 | case DREM: push(new BinaryOp(insn, pop(), pop())); break; 434 | case INEG: 435 | case LNEG: 436 | case FNEG: 437 | case DNEG: push(new UnaryOp(insn, pop())); break; 438 | case ISHL: 439 | case LSHL: 440 | case ISHR: 441 | case LSHR: 442 | case IUSHR: 443 | case LUSHR: 444 | case IAND: 445 | case LAND: 446 | case IOR: 447 | case LOR: 448 | case IXOR: 449 | case LXOR: push(new BinaryOp(insn, pop(), pop())); break; 450 | case L2I: 451 | case F2I: 452 | case D2I: push(new PrimitiveCast(insn, INT_TYPE, pop())); break; 453 | case I2L: 454 | case F2L: 455 | case D2L: push(new PrimitiveCast(insn, LONG_TYPE, pop())); break; 456 | case I2F: 457 | case L2F: 458 | case D2F: push(new PrimitiveCast(insn, FLOAT_TYPE, pop())); break; 459 | case I2D: 460 | case L2D: 461 | case F2D: push(new PrimitiveCast(insn, DOUBLE_TYPE, pop())); break; 462 | case I2B: push(new PrimitiveCast(insn, BYTE_TYPE, pop())); break; 463 | case I2C: push(new PrimitiveCast(insn, CHAR_TYPE, pop())); break; 464 | case I2S: push(new PrimitiveCast(insn, SHORT_TYPE, pop())); break; 465 | case LCMP: 466 | case FCMPL: 467 | case FCMPG: 468 | case DCMPL: 469 | case DCMPG: push(new BinaryOp(insn, pop(), pop())); break; 470 | case IRETURN: 471 | case LRETURN: 472 | case FRETURN: 473 | case DRETURN: 474 | case ARETURN: pop(); break; 475 | case ARRAYLENGTH: push(new ArrayLength(insn, pop())); break; 476 | case ATHROW: pop(); break; 477 | case MONITORENTER: 478 | case MONITOREXIT: pop(); break; 479 | //@formatter:on 480 | default: 481 | if (DEBUG) { 482 | LOGGER.warn("Unhandled Opcode for InsnNode: {}", insn.getOpcode()); 483 | } 484 | } 485 | } 486 | 487 | private static Type computeConstType(Object obj) { 488 | if (obj instanceof Byte) { 489 | return BYTE_TYPE; 490 | } else if (obj instanceof Short) { 491 | return SHORT_TYPE; 492 | } else if (obj instanceof Integer) { 493 | return INT_TYPE; 494 | } else if (obj instanceof Long) { 495 | return LONG_TYPE; 496 | } else if (obj instanceof Float) { 497 | return FLOAT_TYPE; 498 | } else if (obj instanceof Double) { 499 | return DOUBLE_TYPE; 500 | } else if (obj instanceof Character) { 501 | return CHAR_TYPE; 502 | } else if (obj instanceof Boolean) { 503 | return BOOLEAN_TYPE; 504 | } else if (obj instanceof String) { 505 | return getObjectType("java/lang/String"); 506 | } else if (obj == null) { 507 | return getObjectType("java/lang/Object"); 508 | } else if (obj instanceof Type) { 509 | int sort = ((Type) obj).getSort(); 510 | if (sort == OBJECT || sort == ARRAY) { 511 | return getObjectType("java/lang/Class"); 512 | } else if (sort == METHOD) { 513 | return getObjectType("java/lang/invoke/MethodType"); 514 | } else { 515 | throw new IllegalArgumentException("Invalid Type const: " + obj); 516 | } 517 | } else if (obj instanceof Handle) { 518 | return getObjectType("java/lang/invoke/MethodHandle"); 519 | } else if (obj instanceof ConstantDynamic) { 520 | throw new IllegalArgumentException("ConstantDynamic currently not supported."); 521 | } 522 | 523 | throw new IllegalArgumentException("Unknown const: " + obj); 524 | } 525 | 526 | public static abstract class Entry { 527 | 528 | public final Type type; 529 | 530 | public Entry(Type type) { 531 | this.type = type; 532 | } 533 | } 534 | 535 | public static abstract class StackEntry extends Entry { 536 | 537 | public final AbstractInsnNode insn; 538 | 539 | public StackEntry(AbstractInsnNode insn, Type type) { 540 | super(type); 541 | this.insn = insn; 542 | } 543 | } 544 | 545 | public static abstract class LocalEntry extends Entry { 546 | 547 | public LocalEntry(Type type) { 548 | super(type); 549 | } 550 | } 551 | 552 | public static class This extends LocalEntry { 553 | 554 | public This(Type type) { 555 | super(type); 556 | } 557 | } 558 | 559 | public static class Param extends LocalEntry { 560 | 561 | public final int i; 562 | 563 | public Param(Type type, int i) { 564 | super(type); 565 | this.i = i; 566 | } 567 | } 568 | 569 | public static class Store extends LocalEntry { 570 | 571 | public final StackEntry e; 572 | 573 | public Store(StackEntry e) { 574 | super(e.type); 575 | this.e = e; 576 | } 577 | } 578 | 579 | public static class Const extends StackEntry { 580 | 581 | public final Object constObj; 582 | 583 | public Const(AbstractInsnNode insn, Object constObj) { 584 | super(insn, computeConstType(constObj)); 585 | this.constObj = constObj; 586 | } 587 | } 588 | 589 | public static class Load extends StackEntry { 590 | 591 | public final LocalEntry e; 592 | 593 | public Load(AbstractInsnNode insn, LocalEntry e) { 594 | super(insn, e.type); 595 | this.e = e; 596 | } 597 | } 598 | 599 | public static class UnaryOp extends StackEntry { 600 | 601 | public final StackEntry e; 602 | public final int op; 603 | 604 | public UnaryOp(AbstractInsnNode insn, StackEntry e) { 605 | this(insn, e, insn.getOpcode()); 606 | } 607 | 608 | public UnaryOp(AbstractInsnNode insn, StackEntry e, int op) { 609 | super(insn, e.type); 610 | this.e = e; 611 | this.op = op; 612 | } 613 | } 614 | 615 | public static class BinaryOp extends StackEntry { 616 | 617 | public final StackEntry e2; 618 | public final StackEntry e1; 619 | public final int op; 620 | 621 | public BinaryOp(AbstractInsnNode insn, StackEntry e2, StackEntry e1) { 622 | this(insn, e2, e1, insn.getOpcode()); 623 | } 624 | 625 | public BinaryOp(AbstractInsnNode insn, StackEntry e2, StackEntry e1, int op) { 626 | super(insn, e1.type); 627 | this.e2 = e2; 628 | this.e1 = e1; 629 | this.op = op; 630 | } 631 | } 632 | 633 | public static class PrimitiveCast extends StackEntry { 634 | 635 | public final StackEntry e; 636 | 637 | public PrimitiveCast(AbstractInsnNode insn, Type type, StackEntry e) { 638 | super(insn, type); 639 | this.e = e; 640 | } 641 | } 642 | 643 | public static class ReturnAddress extends StackEntry { 644 | 645 | public ReturnAddress(AbstractInsnNode insn) { 646 | super(insn, INT_TYPE); 647 | } 648 | } 649 | 650 | public static class GetField extends StackEntry { 651 | 652 | public final FieldInsnNode field; 653 | public final StackEntry obj; 654 | 655 | public GetField(FieldInsnNode field, StackEntry obj) { 656 | super(field, getType(field.desc)); 657 | this.obj = obj; 658 | this.field = field; 659 | } 660 | } 661 | 662 | public static class Invoke extends StackEntry { 663 | 664 | public final int op; 665 | public final List params; 666 | public final StackEntry obj; 667 | 668 | public Invoke(MethodInsnNode method, List params, StackEntry obj) { 669 | this(method, method.getOpcode(), params, obj); 670 | } 671 | 672 | public Invoke(MethodInsnNode method, int op, List params, StackEntry obj) { 673 | super(method, getReturnType(method.desc)); 674 | this.op = op; 675 | this.params = params; 676 | this.obj = obj; 677 | } 678 | } 679 | 680 | public static class InvokeDynamic extends StackEntry { 681 | 682 | public final int op; 683 | public final List params; 684 | 685 | public InvokeDynamic(InvokeDynamicInsnNode method, List params) { 686 | this(method, method.getOpcode(), params); 687 | } 688 | 689 | public InvokeDynamic(InvokeDynamicInsnNode method, int op, List params) { 690 | super(method, getReturnType(method.desc)); 691 | this.op = op; 692 | this.params = params; 693 | } 694 | } 695 | 696 | public static class New extends StackEntry { 697 | 698 | public New(AbstractInsnNode insn, Type type) { 699 | super(insn, type); 700 | } 701 | } 702 | 703 | public static class NewArray extends StackEntry { 704 | 705 | public final StackEntry len; 706 | 707 | public NewArray(AbstractInsnNode insn, Type type, StackEntry len) { 708 | super(insn, type); 709 | this.len = len; 710 | } 711 | } 712 | 713 | public static class ArrayLength extends StackEntry { 714 | 715 | public final StackEntry array; 716 | 717 | public ArrayLength(AbstractInsnNode insn, StackEntry array) { 718 | super(insn, INT_TYPE); 719 | this.array = array; 720 | } 721 | } 722 | 723 | public static class ArrayLoad extends StackEntry { 724 | 725 | public final StackEntry index; 726 | public final StackEntry array; 727 | 728 | public ArrayLoad(AbstractInsnNode insn, StackEntry index, StackEntry array) { 729 | super(insn, array.type.getElementType()); 730 | this.index = index; 731 | this.array = array; 732 | } 733 | } 734 | 735 | public static class Cast extends StackEntry { 736 | 737 | public final StackEntry obj; 738 | 739 | public Cast(AbstractInsnNode insn, Type type, StackEntry obj) { 740 | super(insn, type); 741 | this.obj = obj; 742 | } 743 | } 744 | 745 | public static class NewMultiArray extends StackEntry { 746 | 747 | public final List sizes; 748 | 749 | public NewMultiArray(AbstractInsnNode insn, Type type, List sizes) { 750 | super(insn, type); 751 | this.sizes = sizes; 752 | } 753 | } 754 | 755 | public static class CaughtException extends StackEntry { 756 | 757 | public CaughtException(AbstractInsnNode insn, Type type) { 758 | super(insn, type); 759 | } 760 | } 761 | 762 | } 763 | --------------------------------------------------------------------------------