├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main └── java └── com └── voxelgenesis └── injector ├── Inject.java ├── InjectionManager.java ├── Injector.java ├── Local.java ├── config ├── ConfigManager.java └── InjectorConfig.java ├── launch ├── InjectorTransformer.java └── InjectorTweaker.java └── target ├── InjectionTarget.java ├── TypeInjector.java ├── match ├── InjectionMatcher.java ├── InjectionModifier.java ├── MatchedStatements.java └── modifier │ ├── ConditionReplaceMatcher.java │ ├── ConditionValueModifier.java │ ├── InstructionReplaceMatcher.java │ ├── InstructionValueModifier.java │ └── StatementInsertModifier.java └── parse ├── Lexer.java ├── MatchParser.java ├── ParseToken.java └── TokenType.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse stuff 2 | /.classpath 3 | /.project 4 | /.settings/ 5 | 6 | # netbeans 7 | /nbproject 8 | 9 | # gradle 10 | /.gradle/ 11 | 12 | # vim 13 | /.*.sw[a-p] 14 | 15 | # various other potential build files 16 | /*/build/ 17 | /buildassets/map_*.txt 18 | /*/bin/ 19 | /*/dist/ 20 | /*/manifest.mf 21 | /release 22 | 23 | # Mac filesystem dust 24 | .DS_Store 25 | 26 | # intellij 27 | *.iml 28 | *.ipr 29 | *.iws 30 | /.idea/ 31 | /out/ 32 | 33 | /.checkstyle 34 | 35 | /bin/ 36 | /build/ 37 | /run/ 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Despector 4 | Copyright (c) contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Injector 2 | =========== 3 | 4 | A tool for injecting code into an existing java program at runtime. 5 | 6 | # Usage 7 | 8 | # Issues 9 | 10 | Issues and feature requests can be opened in our [Issue Tracker]. 11 | 12 | [Gradle]: https://www.gradle.org/ 13 | [ASM]: http://asm.ow2.org/ 14 | [Development/Support Chat]: https://webchat.esper.net/?channels=decompiler 15 | [Issue Tracker]: https://github.com/Despector/Injector/issues 16 | [HOCON]: https://github.com/typesafehub/config/blob/master/HOCON.md 17 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'maven' 4 | 5 | id 'idea' 6 | id 'eclipse' 7 | 8 | id 'net.minecrell.licenser' version '0.3' 9 | id 'com.github.johnrengelman.shadow' version '1.2.4' 10 | } 11 | 12 | defaultTasks 'clean', 'licenseFormat', 'build' 13 | 14 | sourceCompatibility = 1.8 15 | targetCompatibility = 1.8 16 | 17 | group = 'com.voxelgenesis' 18 | archivesBaseName = project.name.toLowerCase() 19 | version = '0.1.0-SNAPSHOT' 20 | 21 | repositories { 22 | mavenLocal() 23 | mavenCentral() 24 | maven { 25 | url "https://repo.voxelgenesis.com/artifactory/decompiler/" 26 | } 27 | maven { 28 | name = 'minecraft' 29 | url = 'https://libraries.minecraft.net/' 30 | } 31 | } 32 | 33 | // Common dependencies 34 | dependencies { 35 | compile 'org.ow2.asm:asm-all:5.0.3' 36 | compile 'com.google.guava:guava:18.0' 37 | compile 'com.google.code.findbugs:jsr305:1.3.9' 38 | compile 'ninja.leaping.configurate:configurate-hocon:3.2' 39 | compile 'org.spongepowered:despector:0.1.0-SNAPSHOT' 40 | compile('net.minecraft:launchwrapper:1.12') { 41 | exclude module: 'lwjgl' 42 | } 43 | 44 | testCompile 'junit:junit:4.11' 45 | testCompile 'org.mockito:mockito-core:1.9.5' 46 | } 47 | 48 | // License header formatting 49 | license { 50 | header file('LICENSE') 51 | include '**/*.java' 52 | newLine = false 53 | } 54 | 55 | // Source compiler configuration 56 | configure([compileJava, compileTestJava]) { 57 | options.compilerArgs += ['-Xlint:all', '-Xlint:-path'] 58 | options.deprecation = true 59 | options.encoding = 'UTF-8' 60 | } 61 | 62 | processResources { 63 | // Include LICENSE in final JAR 64 | from 'LICENSE' 65 | } 66 | 67 | // Set manifest entries 68 | jar { 69 | classifier 'base' 70 | 71 | manifest { 72 | attributes( 73 | 'Built-By': System.properties['user.name'], 74 | 'Created-By': "${System.properties['java.vm.version']} (${System.properties['java.vm.vendor']})" 75 | ) 76 | } 77 | } 78 | 79 | shadowJar { 80 | classifier '' 81 | 82 | dependencies { 83 | include(dependency('ninja.leaping.configurate:configurate-core')) 84 | include(dependency('ninja.leaping.configurate:configurate-hocon')) 85 | include(dependency('org.spongepowered:despector')) 86 | } 87 | } 88 | 89 | task sourceJar(type: Jar) { 90 | classifier = 'sources' 91 | from sourceSets.main.allSource 92 | } 93 | 94 | javadoc { 95 | options.encoding = 'UTF-8' 96 | options.charSet = 'UTF-8' 97 | options.links( 98 | 'https://docs.oracle.com/javase/8/docs/api/' 99 | ) 100 | // Disable the crazy super-strict doclint tool in Java 8 101 | options.addStringOption('Xdoclint:none', '-quiet') 102 | } 103 | 104 | task javadocJar(type: Jar, dependsOn: javadoc) { 105 | classifier = 'javadoc' 106 | from javadoc.destinationDir 107 | } 108 | 109 | artifacts { 110 | archives shadowJar 111 | archives sourceJar 112 | archives javadocJar 113 | } 114 | 115 | task wrapper(type: Wrapper) { 116 | gradleVersion = '3.1' 117 | } 118 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | name = Injector 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Despector/Injector/91bb76ab19175a7fc7e34d499b6e073b34e3079a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Nov 11 18:53:12 PST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 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 %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="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 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = name 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/Inject.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector; 26 | 27 | import java.lang.annotation.ElementType; 28 | import java.lang.annotation.Retention; 29 | import java.lang.annotation.RetentionPolicy; 30 | import java.lang.annotation.Target; 31 | 32 | @Target(ElementType.METHOD) 33 | @Retention(RetentionPolicy.RUNTIME) 34 | public @interface Inject { 35 | 36 | String target(); 37 | 38 | String matcher(); 39 | 40 | Class[] imports() default {}; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/InjectionManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector; 26 | 27 | import com.voxelgenesis.injector.target.TypeInjector; 28 | import org.spongepowered.despector.ast.Annotation; 29 | import org.spongepowered.despector.ast.AnnotationType; 30 | import org.spongepowered.despector.ast.SourceSet; 31 | import org.spongepowered.despector.ast.generic.ClassTypeSignature; 32 | import org.spongepowered.despector.ast.type.TypeEntry; 33 | import org.spongepowered.despector.decompiler.Decompilers; 34 | 35 | import java.io.File; 36 | import java.io.IOException; 37 | import java.util.HashMap; 38 | import java.util.Map; 39 | 40 | public class InjectionManager { 41 | 42 | private static final InjectionManager instance = new InjectionManager(); 43 | 44 | public static InjectionManager get() { 45 | return instance; 46 | } 47 | 48 | private final SourceSet injector_sourceset = new SourceSet(); 49 | private final Map targets = new HashMap<>(); 50 | 51 | private final AnnotationType injection_annotation; 52 | 53 | private InjectionManager() { 54 | this.injection_annotation = this.injector_sourceset.getAnnotationType("com/voxelgenesis/injector/Injector"); 55 | } 56 | 57 | public SourceSet getSourceSet() { 58 | return this.injector_sourceset; 59 | } 60 | 61 | public void addInjector(Class src, String injector) { 62 | String path = src.getProtectionDomain().getCodeSource().getLocation().getPath(); 63 | File file = new File(path, injector.replace('.', '/') + ".class"); 64 | try { 65 | TypeEntry type = Decompilers.WILD.decompile(file, this.injector_sourceset); 66 | Decompilers.WILD.flushTasks(); 67 | Annotation anno = type.getAnnotation(this.injection_annotation); 68 | if (anno == null) { 69 | System.err.println("Injector " + injector + " is missing the @Injector annotation"); 70 | return; 71 | } 72 | String target = anno.getValue("value").getClassName(); 73 | this.targets.put(target, new TypeInjector(target, type)); 74 | } catch (IOException e) { 75 | e.printStackTrace(); 76 | } 77 | } 78 | 79 | public TypeInjector getInjection(String target) { 80 | return this.targets.get(target); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/Injector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector; 26 | 27 | import java.lang.annotation.ElementType; 28 | import java.lang.annotation.Retention; 29 | import java.lang.annotation.RetentionPolicy; 30 | import java.lang.annotation.Target; 31 | 32 | @Target(ElementType.TYPE) 33 | @Retention(RetentionPolicy.RUNTIME) 34 | public @interface Injector { 35 | 36 | Class value(); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/Local.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector; 26 | 27 | import java.lang.annotation.ElementType; 28 | import java.lang.annotation.Retention; 29 | import java.lang.annotation.RetentionPolicy; 30 | import java.lang.annotation.Target; 31 | 32 | @Target(ElementType.PARAMETER) 33 | @Retention(RetentionPolicy.RUNTIME) 34 | public @interface Local { 35 | 36 | String value(); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/config/ConfigManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.config; 26 | 27 | import ninja.leaping.configurate.ConfigurationOptions; 28 | import ninja.leaping.configurate.commented.CommentedConfigurationNode; 29 | import ninja.leaping.configurate.hocon.HoconConfigurationLoader; 30 | import ninja.leaping.configurate.objectmapping.ObjectMapper; 31 | 32 | import java.nio.file.Files; 33 | import java.nio.file.Path; 34 | 35 | /** 36 | * The manager for all configuration. 37 | */ 38 | public class ConfigManager { 39 | 40 | private static final String HEADER = "Despector decompiler configuration:"; 41 | 42 | private static HoconConfigurationLoader loader; 43 | private static CommentedConfigurationNode node; 44 | private static ObjectMapper.BoundInstance configMapper; 45 | 46 | private static InjectorConfig config = null; 47 | 48 | /** 49 | * Gets the global configuration object. 50 | */ 51 | public static InjectorConfig getConfig() { 52 | if (config == null) { 53 | config = new InjectorConfig(); 54 | } 55 | return config; 56 | } 57 | 58 | /** 59 | * Loads the given configuration file. 60 | */ 61 | public static void load(Path path) { 62 | System.out.println("Loading config from " + path.toString()); 63 | try { 64 | Files.createDirectories(path.getParent()); 65 | if (Files.notExists(path)) { 66 | Files.createFile(path); 67 | } 68 | 69 | loader = HoconConfigurationLoader.builder().setPath(path).build(); 70 | configMapper = ObjectMapper.forClass(InjectorConfig.class).bindToNew(); 71 | node = loader.load(ConfigurationOptions.defaults().setHeader(HEADER)); 72 | config = configMapper.populate(node); 73 | configMapper.serialize(node); 74 | loader.save(node); 75 | } catch (Exception e) { 76 | System.err.println("Error loading configuration:"); 77 | e.printStackTrace(); 78 | } 79 | } 80 | 81 | /** 82 | * Saves the config back to disk to persist and changes made. 83 | */ 84 | public static void update() { 85 | try { 86 | configMapper.serialize(node); 87 | loader.save(node); 88 | } catch (Exception e) { 89 | System.err.println("Error saving configuration:"); 90 | e.printStackTrace(); 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/config/InjectorConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.config; 26 | 27 | public class InjectorConfig { 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/launch/InjectorTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.launch; 26 | 27 | import com.voxelgenesis.injector.InjectionManager; 28 | import com.voxelgenesis.injector.target.TypeInjector; 29 | import net.minecraft.launchwrapper.IClassTransformer; 30 | import org.spongepowered.despector.ast.type.TypeEntry; 31 | import org.spongepowered.despector.decompiler.Decompilers; 32 | import org.spongepowered.despector.emitter.Emitters; 33 | import org.spongepowered.despector.emitter.bytecode.BytecodeEmitterContext; 34 | 35 | import java.io.ByteArrayInputStream; 36 | import java.io.ByteArrayOutputStream; 37 | import java.io.IOException; 38 | 39 | public class InjectorTransformer implements IClassTransformer { 40 | 41 | @Override 42 | public byte[] transform(String name, String transformedName, byte[] basicClass) { 43 | TypeInjector injection = InjectionManager.get().getInjection(transformedName); 44 | if (injection != null) { 45 | try { 46 | TypeEntry type = Decompilers.JAVA.decompile(new ByteArrayInputStream(basicClass), InjectionManager.get().getSourceSet()); 47 | Decompilers.JAVA.flushTasks(); 48 | injection.apply(type); 49 | 50 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 51 | BytecodeEmitterContext ctx = new BytecodeEmitterContext(out); 52 | Emitters.BYTECODE.emit(ctx, type); 53 | return out.toByteArray(); 54 | } catch (IOException e) { 55 | e.printStackTrace(); 56 | return basicClass; 57 | } 58 | } 59 | return basicClass; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/launch/InjectorTweaker.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.launch; 26 | 27 | import net.minecraft.launchwrapper.ITweaker; 28 | import net.minecraft.launchwrapper.LaunchClassLoader; 29 | import org.spongepowered.despector.config.LibraryConfiguration; 30 | 31 | import java.io.File; 32 | import java.util.List; 33 | 34 | public class InjectorTweaker implements ITweaker { 35 | 36 | @Override 37 | public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { 38 | 39 | } 40 | 41 | @Override 42 | public void injectIntoClassLoader(LaunchClassLoader cl) { 43 | cl.addClassLoaderExclusion("com.voxelgenesis.injector"); 44 | cl.addClassLoaderExclusion("org.spongepowered.despector"); 45 | cl.registerTransformer("com.voxelgenesis.injector.launch.InjectorTransformer"); 46 | LibraryConfiguration.parallel = false; 47 | } 48 | 49 | @Override 50 | public String getLaunchTarget() { 51 | return null; 52 | } 53 | 54 | @Override 55 | public String[] getLaunchArguments() { 56 | return new String[] {}; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/target/InjectionTarget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.target; 26 | 27 | import com.voxelgenesis.injector.target.match.InjectionMatcher; 28 | import com.voxelgenesis.injector.target.match.MatchedStatements; 29 | import org.spongepowered.despector.ast.stmt.Statement; 30 | import org.spongepowered.despector.ast.type.MethodEntry; 31 | 32 | import java.util.ArrayList; 33 | import java.util.HashSet; 34 | import java.util.List; 35 | import java.util.Set; 36 | 37 | public class InjectionTarget { 38 | 39 | private final String target; 40 | private final List points = new ArrayList<>(); 41 | 42 | public InjectionTarget(String target) { 43 | this.target = target; 44 | } 45 | 46 | public String getTarget() { 47 | return this.target; 48 | } 49 | 50 | public void addInjection(InjectionMatcher point) { 51 | this.points.add(point); 52 | } 53 | 54 | public void apply(MethodEntry mth) { 55 | List matches = new ArrayList<>(); 56 | Set modified = new HashSet<>(); 57 | for (InjectionMatcher point : this.points) { 58 | MatchedStatements match = point.match(mth); 59 | if (match == null) { 60 | throw new IllegalStateException("No match"); 61 | } 62 | for (Statement stmt : match.getModified()) { 63 | if (!modified.add(stmt)) { 64 | throw new IllegalStateException("Overlapping injectors"); 65 | } 66 | } 67 | matches.add(match); 68 | } 69 | 70 | for (MatchedStatements match : matches) { 71 | match.getMatcher().apply(match, mth); 72 | } 73 | 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/target/TypeInjector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.target; 26 | 27 | import com.voxelgenesis.injector.target.match.InjectionMatcher; 28 | import com.voxelgenesis.injector.target.parse.MatchParser; 29 | import org.spongepowered.despector.ast.Annotation; 30 | import org.spongepowered.despector.ast.AnnotationType; 31 | import org.spongepowered.despector.ast.generic.ClassTypeSignature; 32 | import org.spongepowered.despector.ast.type.MethodEntry; 33 | import org.spongepowered.despector.ast.type.TypeEntry; 34 | 35 | import java.util.HashMap; 36 | import java.util.List; 37 | import java.util.Map; 38 | 39 | public class TypeInjector { 40 | 41 | private final String target; 42 | private final TypeEntry injector; 43 | 44 | private final Map targets = new HashMap<>(); 45 | 46 | public TypeInjector(String target, TypeEntry type) { 47 | this.target = target; 48 | this.injector = type; 49 | } 50 | 51 | public String getTarget() { 52 | return this.target; 53 | } 54 | 55 | public TypeEntry getInjector() { 56 | return this.injector; 57 | } 58 | 59 | private void buildTargets() { 60 | AnnotationType inject_anno = this.injector.getSource().getAnnotationType("com/voxelgenesis/injector/Inject"); 61 | for (MethodEntry mth : this.injector.getMethods()) { 62 | Annotation inject = mth.getAnnotation(inject_anno); 63 | if (inject != null) { 64 | String target = inject.getValue("target"); 65 | String matcher = inject.getValue("matcher"); 66 | InjectionTarget itarget = this.targets.get(target); 67 | if (itarget == null) { 68 | itarget = new InjectionTarget(target); 69 | this.targets.put(target, itarget); 70 | } 71 | MatchParser parser = new MatchParser(matcher, mth); 72 | List imports = inject.getValue("imports"); 73 | if (imports != null) { 74 | for (ClassTypeSignature im : imports) { 75 | String type = im.getDescriptor(); 76 | type = type.substring(1, type.length() - 1); 77 | String simple = type.substring(type.lastIndexOf('/') + 1); 78 | parser.addImport(simple, im.getDescriptor()); 79 | } 80 | } 81 | InjectionMatcher imatcher = parser.parse(); 82 | itarget.addInjection(imatcher); 83 | } 84 | } 85 | 86 | } 87 | 88 | public void apply(TypeEntry type) { 89 | buildTargets(); 90 | 91 | for (MethodEntry mth : type.getMethods()) { 92 | InjectionTarget target = this.targets.get(mth.getName() + mth.getDescription()); 93 | if (target != null) { 94 | target.apply(mth); 95 | } 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/target/match/InjectionMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.target.match; 26 | 27 | import org.spongepowered.despector.ast.stmt.Statement; 28 | import org.spongepowered.despector.ast.stmt.StatementBlock; 29 | import org.spongepowered.despector.ast.stmt.branch.DoWhile; 30 | import org.spongepowered.despector.ast.stmt.branch.For; 31 | import org.spongepowered.despector.ast.stmt.branch.ForEach; 32 | import org.spongepowered.despector.ast.stmt.branch.If; 33 | import org.spongepowered.despector.ast.stmt.branch.If.Elif; 34 | import org.spongepowered.despector.ast.stmt.branch.Switch; 35 | import org.spongepowered.despector.ast.stmt.branch.Switch.Case; 36 | import org.spongepowered.despector.ast.stmt.branch.TryCatch; 37 | import org.spongepowered.despector.ast.stmt.branch.TryCatch.CatchBlock; 38 | import org.spongepowered.despector.ast.stmt.branch.While; 39 | import org.spongepowered.despector.ast.type.MethodEntry; 40 | import org.spongepowered.despector.transform.matcher.MatchContext; 41 | import org.spongepowered.despector.transform.matcher.StatementMatcher; 42 | 43 | import java.util.ArrayList; 44 | import java.util.List; 45 | 46 | public class InjectionMatcher { 47 | 48 | private final List> matcher; 49 | private final InjectionModifier modifier; 50 | private final int start; 51 | private final int end; 52 | 53 | public InjectionMatcher(List> matcher, InjectionModifier modifier, int start, int end) { 54 | this.matcher = matcher; 55 | this.modifier = modifier; 56 | this.start = start; 57 | this.end = end; 58 | } 59 | 60 | public List> getMatcher() { 61 | return this.matcher; 62 | } 63 | 64 | public InjectionModifier getModifier() { 65 | return this.modifier; 66 | } 67 | 68 | public MatchedStatements match(MethodEntry mth) { 69 | return match(mth.getInstructions()); 70 | } 71 | 72 | public MatchedStatements match(StatementBlock block) { 73 | MatchedStatements match = null; 74 | outer: for (int i = 0; i < block.size(); i++) { 75 | MatchContext ctx = MatchContext.create(); 76 | match = check(block.get(i), match); 77 | if (i < block.size() - this.matcher.size() + 1) { 78 | for (int j = 0; j < this.matcher.size(); j++) { 79 | Statement check = block.get(i + j); 80 | StatementMatcher next_matcher = this.matcher.get(j); 81 | if (!next_matcher.matches(ctx, check)) { 82 | continue outer; 83 | } 84 | } 85 | } else { 86 | continue; 87 | } 88 | if (match != null) { 89 | throw new IllegalStateException(); 90 | } 91 | List matched = new ArrayList<>(); 92 | for (int j = 0; j < this.matcher.size(); j++) { 93 | matched.add(block.get(i + j)); 94 | } 95 | match = new MatchedStatements(this, ctx, block, matched); 96 | for (int j = this.start; j <= this.end; j++) { 97 | match.markModified(matched.get(j)); 98 | } 99 | break; 100 | } 101 | return match; 102 | } 103 | 104 | private MatchedStatements check(Statement stmt, MatchedStatements previous) { 105 | MatchedStatements match = previous; 106 | if (stmt instanceof If) { 107 | If iif = (If) stmt; 108 | MatchedStatements pos = match(iif.getBody()); 109 | if (match != null && pos != null) { 110 | throw new IllegalStateException(); 111 | } else if (pos != null) { 112 | match = pos; 113 | } 114 | for (Elif elif : iif.getElifBlocks()) { 115 | pos = match(elif.getBody()); 116 | if (match != null && pos != null) { 117 | throw new IllegalStateException(); 118 | } else if (pos != null) { 119 | match = pos; 120 | } 121 | } 122 | if (iif.getElseBlock() != null) { 123 | pos = match(iif.getElseBlock().getBody()); 124 | if (match != null && pos != null) { 125 | throw new IllegalStateException(); 126 | } else if (pos != null) { 127 | match = pos; 128 | } 129 | } 130 | } else if (stmt instanceof For) { 131 | For ffor = (For) stmt; 132 | MatchedStatements pos = match(ffor.getBody()); 133 | if (match != null && pos != null) { 134 | throw new IllegalStateException(); 135 | } else if (pos != null) { 136 | match = pos; 137 | } 138 | } else if (stmt instanceof ForEach) { 139 | ForEach ffor = (ForEach) stmt; 140 | MatchedStatements pos = match(ffor.getBody()); 141 | if (match != null && pos != null) { 142 | throw new IllegalStateException(); 143 | } else if (pos != null) { 144 | match = pos; 145 | } 146 | } else if (stmt instanceof While) { 147 | While wwhile = (While) stmt; 148 | MatchedStatements pos = match(wwhile.getBody()); 149 | if (match != null && pos != null) { 150 | throw new IllegalStateException(); 151 | } else if (pos != null) { 152 | match = pos; 153 | } 154 | } else if (stmt instanceof DoWhile) { 155 | DoWhile dowhile = (DoWhile) stmt; 156 | MatchedStatements pos = match(dowhile.getBody()); 157 | if (match != null && pos != null) { 158 | throw new IllegalStateException(); 159 | } else if (pos != null) { 160 | match = pos; 161 | } 162 | } else if (stmt instanceof TryCatch) { 163 | TryCatch ttry = (TryCatch) stmt; 164 | MatchedStatements pos = match(ttry.getTryBlock()); 165 | if (match != null && pos != null) { 166 | throw new IllegalStateException(); 167 | } else if (pos != null) { 168 | match = pos; 169 | } 170 | for (CatchBlock ccatch : ttry.getCatchBlocks()) { 171 | pos = match(ccatch.getBlock()); 172 | if (match != null && pos != null) { 173 | throw new IllegalStateException(); 174 | } else if (pos != null) { 175 | match = pos; 176 | } 177 | } 178 | } else if (stmt instanceof Switch) { 179 | Switch sswitch = (Switch) stmt; 180 | MatchedStatements pos = null; 181 | for (Case cs : sswitch.getCases()) { 182 | pos = match(cs.getBody()); 183 | if (match != null && pos != null) { 184 | throw new IllegalStateException(); 185 | } else if (pos != null) { 186 | match = pos; 187 | } 188 | } 189 | } 190 | return match; 191 | } 192 | 193 | public void apply(MatchedStatements mth, MethodEntry target) { 194 | List statements = new ArrayList<>(mth.getStatements()); 195 | this.modifier.apply(statements, this.start, this.end, target, mth.getMatchContext()); 196 | List backing = mth.getBlock().getStatements(); 197 | int start = backing.indexOf(mth.getStatements().get(0)); 198 | for (Statement s : mth.getStatements()) { 199 | backing.remove(s); 200 | } 201 | for (int i = statements.size() - 1; i >= 0; i--) { 202 | backing.add(start, statements.get(i)); 203 | } 204 | } 205 | 206 | } 207 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/target/match/InjectionModifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.target.match; 26 | 27 | import org.spongepowered.despector.ast.stmt.Statement; 28 | import org.spongepowered.despector.ast.type.MethodEntry; 29 | import org.spongepowered.despector.transform.matcher.MatchContext; 30 | 31 | import java.util.List; 32 | 33 | public interface InjectionModifier { 34 | 35 | void apply(List statements, int start, int end, MethodEntry target, MatchContext match); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/target/match/MatchedStatements.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.target.match; 26 | 27 | import org.spongepowered.despector.ast.stmt.Statement; 28 | import org.spongepowered.despector.ast.stmt.StatementBlock; 29 | import org.spongepowered.despector.transform.matcher.MatchContext; 30 | 31 | import java.util.ArrayList; 32 | import java.util.Collection; 33 | import java.util.List; 34 | 35 | public class MatchedStatements { 36 | 37 | private final InjectionMatcher point; 38 | private final MatchContext ctx; 39 | private final StatementBlock block; 40 | private final List matched = new ArrayList<>(); 41 | private final List modified = new ArrayList<>(); 42 | 43 | public MatchedStatements(InjectionMatcher point, MatchContext ctx, StatementBlock block, Collection stmt) { 44 | this.point = point; 45 | this.ctx = ctx; 46 | this.block = block; 47 | this.matched.addAll(stmt); 48 | } 49 | 50 | public InjectionMatcher getMatcher() { 51 | return this.point; 52 | } 53 | 54 | public MatchContext getMatchContext() { 55 | return this.ctx; 56 | } 57 | 58 | public StatementBlock getBlock() { 59 | return this.block; 60 | } 61 | 62 | public List getStatements() { 63 | return this.matched; 64 | } 65 | 66 | public void markModified(Statement stmt) { 67 | this.modified.add(stmt); 68 | } 69 | 70 | public List getModified() { 71 | return this.modified; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/target/match/modifier/ConditionReplaceMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.target.match.modifier; 26 | 27 | import org.spongepowered.despector.ast.insn.condition.Condition; 28 | import org.spongepowered.despector.transform.matcher.ConditionMatcher; 29 | import org.spongepowered.despector.transform.matcher.MatchContext; 30 | 31 | public class ConditionReplaceMatcher implements ConditionMatcher { 32 | 33 | private final ConditionMatcher child; 34 | 35 | public ConditionReplaceMatcher(ConditionMatcher child) { 36 | this.child = child; 37 | } 38 | 39 | @SuppressWarnings("unchecked") 40 | @Override 41 | public T match(MatchContext ctx, Condition insn) { 42 | return this.child == null ? (T) insn : this.child.match(ctx, insn); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/target/match/modifier/ConditionValueModifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.target.match.modifier; 26 | 27 | import com.voxelgenesis.injector.target.match.InjectionModifier; 28 | import org.spongepowered.despector.ast.Locals.LocalInstance; 29 | import org.spongepowered.despector.ast.insn.condition.Condition; 30 | import org.spongepowered.despector.ast.stmt.Statement; 31 | import org.spongepowered.despector.ast.stmt.assign.LocalAssignment; 32 | import org.spongepowered.despector.ast.stmt.branch.If; 33 | import org.spongepowered.despector.ast.type.MethodEntry; 34 | import org.spongepowered.despector.transform.matcher.ConditionMatcher; 35 | import org.spongepowered.despector.transform.matcher.InstructionMatcher; 36 | import org.spongepowered.despector.transform.matcher.MatchContext; 37 | import org.spongepowered.despector.transform.matcher.StatementMatcher; 38 | import org.spongepowered.despector.transform.matcher.statement.IfMatcher; 39 | import org.spongepowered.despector.util.ConditionUtil; 40 | 41 | import java.util.List; 42 | import java.util.Map; 43 | 44 | public class ConditionValueModifier implements InjectionModifier { 45 | 46 | private static final StatementMatcher RETURN_TRUE = StatementMatcher.returnValue() 47 | .value(InstructionMatcher.intConstant() 48 | .value(1) 49 | .build()) 50 | .build(); 51 | 52 | private final MethodEntry replacement; 53 | private final StatementMatcher matcher; 54 | 55 | public ConditionValueModifier(MethodEntry replacement, StatementMatcher matcher) { 56 | this.replacement = replacement; 57 | this.matcher = matcher; 58 | } 59 | 60 | @Override 61 | public void apply(List statements, int start, int end, MethodEntry target, MatchContext match) { 62 | Map local_translation = StatementInsertModifier.buildLocalTranslation(target, this.replacement, match, start); 63 | replaceInStatement(statements.get(start), this.matcher, StatementInsertModifier.translate(getConditionReplace(), local_translation)); 64 | } 65 | 66 | private Condition getConditionReplace() { 67 | Statement s = this.replacement.getInstructions().get(0); 68 | if (s instanceof If) { 69 | If iif = (If) s; 70 | if (RETURN_TRUE.matches(MatchContext.create(), iif.getBody().get(0))) { 71 | return iif.getCondition(); 72 | } 73 | return ConditionUtil.inverse(iif.getCondition()); 74 | } 75 | throw new IllegalStateException(); 76 | } 77 | 78 | public static void replaceInStatement(Statement value, StatementMatcher root_matcher, Condition replacement) { 79 | if (root_matcher instanceof MatchContext.LocalStoreMatcher) { 80 | replaceInStatement(value, ((MatchContext.LocalStoreMatcher) root_matcher).getInternalMatcher(), replacement); 81 | } else if(value instanceof If) { 82 | IfMatcher matcher = (IfMatcher) root_matcher; 83 | If iif = (If) value; 84 | iif.setCondition(replaceInCondition(iif.getCondition(), matcher.getConditionMatcher(), replacement)); 85 | } else if (value instanceof LocalAssignment) { 86 | } else { 87 | throw new IllegalStateException("Unsupported matcher " + root_matcher.getClass().getName()); 88 | } 89 | } 90 | 91 | public static Condition replaceInCondition(Condition value, ConditionMatcher root_matcher, Condition replacement) { 92 | if (root_matcher instanceof ConditionReplaceMatcher) { 93 | return replacement; 94 | } 95 | throw new IllegalStateException("Unsupported matcher " + root_matcher.getClass().getName()); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/target/match/modifier/InstructionReplaceMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.target.match.modifier; 26 | 27 | import org.spongepowered.despector.ast.insn.Instruction; 28 | import org.spongepowered.despector.transform.matcher.InstructionMatcher; 29 | import org.spongepowered.despector.transform.matcher.MatchContext; 30 | 31 | public class InstructionReplaceMatcher implements InstructionMatcher { 32 | 33 | private final InstructionMatcher child; 34 | 35 | public InstructionReplaceMatcher(InstructionMatcher child) { 36 | this.child = child; 37 | } 38 | 39 | @SuppressWarnings("unchecked") 40 | @Override 41 | public T match(MatchContext ctx, Instruction insn) { 42 | return this.child == null ? (T) insn : this.child.match(ctx, insn); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/target/match/modifier/InstructionValueModifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.target.match.modifier; 26 | 27 | import com.voxelgenesis.injector.target.match.InjectionModifier; 28 | import org.spongepowered.despector.ast.Locals.LocalInstance; 29 | import org.spongepowered.despector.ast.insn.Instruction; 30 | import org.spongepowered.despector.ast.insn.condition.Condition; 31 | import org.spongepowered.despector.ast.stmt.Statement; 32 | import org.spongepowered.despector.ast.stmt.assign.LocalAssignment; 33 | import org.spongepowered.despector.ast.stmt.branch.If; 34 | import org.spongepowered.despector.ast.stmt.misc.Return; 35 | import org.spongepowered.despector.ast.type.MethodEntry; 36 | import org.spongepowered.despector.transform.matcher.ConditionMatcher; 37 | import org.spongepowered.despector.transform.matcher.InstructionMatcher; 38 | import org.spongepowered.despector.transform.matcher.MatchContext; 39 | import org.spongepowered.despector.transform.matcher.StatementMatcher; 40 | import org.spongepowered.despector.transform.matcher.instruction.IntConstantMatcher; 41 | import org.spongepowered.despector.transform.matcher.instruction.StringConstantMatcher; 42 | import org.spongepowered.despector.transform.matcher.statement.LocalAssignmentMatcher; 43 | import org.spongepowered.despector.util.ConditionUtil; 44 | 45 | import java.util.List; 46 | import java.util.Map; 47 | 48 | public class InstructionValueModifier implements InjectionModifier { 49 | 50 | private final MethodEntry replacement; 51 | private final StatementMatcher matcher; 52 | 53 | public InstructionValueModifier(MethodEntry replacement, StatementMatcher matcher) { 54 | this.replacement = replacement; 55 | this.matcher = matcher; 56 | } 57 | 58 | @Override 59 | public void apply(List statements, int start, int end, MethodEntry target, MatchContext match) { 60 | Map local_translation = StatementInsertModifier.buildLocalTranslation(target, this.replacement, match, start); 61 | replaceInStatement(statements.get(start), this.matcher, StatementInsertModifier.translate(getValueReplace(), local_translation)); 62 | } 63 | 64 | private Instruction getValueReplace() { 65 | return ((Return) this.replacement.getInstructions().get(0)).getValue().get(); 66 | } 67 | 68 | public static void replaceInStatement(Statement value, StatementMatcher root_matcher, Instruction replacement) { 69 | if (root_matcher instanceof MatchContext.LocalStoreMatcher) { 70 | replaceInStatement(value, ((MatchContext.LocalStoreMatcher) root_matcher).getInternalMatcher(), replacement); 71 | } else if (value instanceof LocalAssignment) { 72 | LocalAssignmentMatcher match = (LocalAssignmentMatcher) root_matcher; 73 | LocalAssignment assign = (LocalAssignment) value; 74 | assign.setValue(replaceInValue(assign.getValue(), match.getValueMatcher(), replacement)); 75 | } else { 76 | throw new IllegalStateException("Unsupported matcher " + root_matcher.getClass().getName()); 77 | } 78 | } 79 | 80 | public static Instruction replaceInValue(Instruction value, InstructionMatcher root_matcher, Instruction replacement) { 81 | if (root_matcher instanceof InstructionReplaceMatcher) { 82 | return replacement; 83 | } else if (root_matcher instanceof StringConstantMatcher || root_matcher instanceof IntConstantMatcher) { 84 | return value; 85 | } 86 | throw new IllegalStateException("Unsupported matcher " + root_matcher.getClass().getName()); 87 | } 88 | 89 | public static Condition replaceInCondition(Condition value, ConditionMatcher root_matcher, Condition replacement) { 90 | if (root_matcher instanceof ConditionReplaceMatcher) { 91 | return replacement; 92 | } 93 | throw new IllegalStateException("Unsupported matcher " + root_matcher.getClass().getName()); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/target/match/modifier/StatementInsertModifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.target.match.modifier; 26 | 27 | import com.voxelgenesis.injector.target.match.InjectionModifier; 28 | import org.spongepowered.despector.ast.Annotation; 29 | import org.spongepowered.despector.ast.Locals.Local; 30 | import org.spongepowered.despector.ast.Locals.LocalInstance; 31 | import org.spongepowered.despector.ast.insn.Instruction; 32 | import org.spongepowered.despector.ast.insn.condition.AndCondition; 33 | import org.spongepowered.despector.ast.insn.condition.BooleanCondition; 34 | import org.spongepowered.despector.ast.insn.condition.CompareCondition; 35 | import org.spongepowered.despector.ast.insn.condition.Condition; 36 | import org.spongepowered.despector.ast.insn.condition.OrCondition; 37 | import org.spongepowered.despector.ast.insn.cst.NullConstant; 38 | import org.spongepowered.despector.ast.insn.cst.StringConstant; 39 | import org.spongepowered.despector.ast.insn.var.LocalAccess; 40 | import org.spongepowered.despector.ast.stmt.Statement; 41 | import org.spongepowered.despector.ast.stmt.assign.LocalAssignment; 42 | import org.spongepowered.despector.ast.stmt.invoke.InstanceMethodInvoke; 43 | import org.spongepowered.despector.ast.stmt.invoke.StaticMethodInvoke; 44 | import org.spongepowered.despector.ast.type.MethodEntry; 45 | import org.spongepowered.despector.transform.matcher.MatchContext; 46 | import org.spongepowered.despector.util.TypeHelper; 47 | 48 | import java.util.ArrayList; 49 | import java.util.HashMap; 50 | import java.util.List; 51 | import java.util.Map; 52 | 53 | public class StatementInsertModifier implements InjectionModifier { 54 | 55 | private final MethodEntry injector; 56 | 57 | public StatementInsertModifier(MethodEntry injector) { 58 | this.injector = injector; 59 | } 60 | 61 | @Override 62 | public void apply(List statements, int start, int end, MethodEntry target, MatchContext match) { 63 | Map local_translation = buildLocalTranslation(target, this.injector, match, start); 64 | 65 | for (int i = this.injector.getInstructions().size() - 2; i >= 0; i--) { 66 | Statement stmt = this.injector.getInstructions().get(i); 67 | statements.add(start, translate(stmt, local_translation)); 68 | } 69 | } 70 | 71 | @SuppressWarnings("unchecked") 72 | public static T translate(T stmt, Map local_translation) { 73 | Translator trans = (Translator) statement_translators.get(stmt.getClass()); 74 | if (trans == null) { 75 | throw new IllegalStateException("No translator for statement type: " + stmt.getClass().getName()); 76 | } 77 | return trans.translate(stmt, local_translation); 78 | } 79 | 80 | @SuppressWarnings("unchecked") 81 | public static T translate(T stmt, Map local_translation) { 82 | Translator trans = (Translator) instruction_translators.get(stmt.getClass()); 83 | if (trans == null) { 84 | throw new IllegalStateException("No translator for instruction type: " + stmt.getClass().getName()); 85 | } 86 | return trans.translate(stmt, local_translation); 87 | } 88 | 89 | @SuppressWarnings("unchecked") 90 | public static T translate(T stmt, Map local_translation) { 91 | Translator trans = (Translator) condition_translators.get(stmt.getClass()); 92 | if (trans == null) { 93 | throw new IllegalStateException("No translator for condition type: " + stmt.getClass().getName()); 94 | } 95 | return trans.translate(stmt, local_translation); 96 | } 97 | 98 | public static Map buildLocalTranslation(MethodEntry target, MethodEntry injector, MatchContext match, int offs) { 99 | Map local_translation = new HashMap<>(); 100 | int local_index = 0; 101 | if (!injector.isStatic()) { 102 | LocalInstance inj_this = injector.getLocals().getLocal(0).getParameterInstance(); 103 | LocalInstance target_this = target.getLocals().getLocal(0).getParameterInstance(); 104 | local_translation.put(inj_this, target_this); 105 | local_index++; 106 | } 107 | for (String p : TypeHelper.splitSig(injector.getDescription())) { 108 | LocalInstance inj_param = injector.getLocals().getLocal(local_index).getParameterInstance(); 109 | boolean handled = false; 110 | for (Annotation anno : inj_param.getAnnotations()) { 111 | if (anno.getType().getName().equals("com/voxelgenesis/injector/Local")) { 112 | LocalInstance target_local = match.getLocal(anno.getValue("value")); 113 | local_translation.put(inj_param, target_local); 114 | handled = true; 115 | break; 116 | } 117 | } 118 | 119 | if (!handled) { 120 | throw new IllegalStateException(); 121 | } 122 | 123 | local_index++; 124 | if (p.equals("D") || p.equals("J")) { 125 | local_index++; 126 | } 127 | } 128 | 129 | int highest_local = 0; 130 | if (!target.isStatic()) { 131 | highest_local++; 132 | } 133 | for (String p : TypeHelper.splitSig(target.getDescription())) { 134 | highest_local++; 135 | if (p.equals("D") || p.equals("J")) { 136 | highest_local++; 137 | } 138 | } 139 | for (LocalInstance instance : target.getLocals().getAllInstances()) { 140 | int local = instance.getLocal().getIndex(); 141 | if (local > highest_local) { 142 | highest_local = local; 143 | } 144 | } 145 | highest_local++; 146 | Map local_index_mapping = new HashMap<>(); 147 | for (LocalInstance instance : injector.getLocals().getAllInstances()) { 148 | if (local_translation.containsKey(instance)) { 149 | continue; 150 | } 151 | if (instance.getLocal().getIndex() < local_index) { 152 | throw new IllegalStateException(); 153 | } 154 | Integer new_index = local_index_mapping.get(instance.getLocal().getIndex()); 155 | if (new_index == null) { 156 | new_index = highest_local++; 157 | if (instance.getType().getDescriptor().equals("D") || instance.getType().getDescriptor().equals("J")) { 158 | highest_local++; 159 | } 160 | local_index_mapping.put(instance.getLocal().getIndex(), new_index); 161 | } 162 | Local new_local = target.getLocals().getLocal(new_index); 163 | int start = instance.getStart(); 164 | int end = instance.getEnd() + offs; 165 | if (start >= 0) { 166 | start += offs; 167 | } 168 | LocalInstance new_instance = new LocalInstance(new_local, instance.getName(), instance.getType(), start, end); 169 | new_local.addInstance(new_instance); 170 | local_translation.put(instance, new_instance); 171 | } 172 | 173 | return local_translation; 174 | } 175 | 176 | private static final Map, Translator> statement_translators = new HashMap<>(); 177 | private static final Map, Translator> instruction_translators = new HashMap<>(); 178 | private static final Map, Translator> condition_translators = new HashMap<>(); 179 | 180 | private static void registerStatementTranslator(Class type, Translator trans) { 181 | statement_translators.put(type, trans); 182 | } 183 | 184 | private static void registerInstructionTranslator(Class type, Translator trans) { 185 | instruction_translators.put(type, trans); 186 | } 187 | 188 | private static void registerConditionTranslator(Class type, Translator trans) { 189 | condition_translators.put(type, trans); 190 | } 191 | 192 | private static interface Translator { 193 | 194 | T translate(T val, Map local_translation); 195 | 196 | } 197 | 198 | static { 199 | registerStatementTranslator(LocalAssignment.class, (stmt, locals) -> { 200 | LocalInstance mapped = locals.get(stmt.getLocal()); 201 | return new LocalAssignment(mapped, translate(stmt.getValue(), locals)); 202 | }); 203 | 204 | registerInstructionTranslator(InstanceMethodInvoke.class, (insn, locals) -> { 205 | Instruction[] args = new Instruction[insn.getParameters().length]; 206 | for (int i = 0; i < insn.getParameters().length; i++) { 207 | args[i] = translate(insn.getParameters()[i], locals); 208 | } 209 | Instruction callee = translate(insn.getCallee(), locals); 210 | return new InstanceMethodInvoke(insn.getType(), insn.getMethodName(), insn.getMethodDescription(), insn.getOwner(), args, callee); 211 | }); 212 | registerInstructionTranslator(LocalAccess.class, (insn, locals) -> { 213 | LocalInstance mapped = locals.get(insn.getLocal()); 214 | return new LocalAccess(mapped); 215 | }); 216 | registerInstructionTranslator(NullConstant.class, (insn, locals) -> NullConstant.NULL); 217 | registerInstructionTranslator(StaticMethodInvoke.class, (insn, locals) -> { 218 | Instruction[] args = new Instruction[insn.getParameters().length]; 219 | for (int i = 0; i < insn.getParameters().length; i++) { 220 | args[i] = translate(insn.getParameters()[i], locals); 221 | } 222 | return new StaticMethodInvoke(insn.getMethodName(), insn.getMethodDescription(), insn.getOwner(), args); 223 | }); 224 | registerInstructionTranslator(StringConstant.class, (insn, locals) -> new StringConstant(insn.getConstant())); 225 | 226 | registerConditionTranslator(AndCondition.class, (cond, locals) -> { 227 | List ops = new ArrayList<>(); 228 | for (Condition op : cond.getOperands()) { 229 | ops.add(translate(op, locals)); 230 | } 231 | return new AndCondition(ops); 232 | }); 233 | registerConditionTranslator(BooleanCondition.class, (cond, locals) -> { 234 | Instruction val = translate(cond.getConditionValue(), locals); 235 | return new BooleanCondition(val, cond.isInverse()); 236 | }); 237 | registerConditionTranslator(CompareCondition.class, (cond, locals) -> { 238 | Instruction left = translate(cond.getLeft(), locals); 239 | Instruction right = translate(cond.getRight(), locals); 240 | return new CompareCondition(left, right, cond.getOperator()); 241 | }); 242 | registerConditionTranslator(OrCondition.class, (cond, locals) -> { 243 | List ops = new ArrayList<>(); 244 | for (Condition op : cond.getOperands()) { 245 | ops.add(translate(op, locals)); 246 | } 247 | return new OrCondition(ops); 248 | }); 249 | } 250 | 251 | } 252 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/target/parse/Lexer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.target.parse; 26 | 27 | import java.util.BitSet; 28 | 29 | public class Lexer { 30 | 31 | public static final BitSet LOWER_ALPHA; 32 | public static final BitSet UPPER_ALPHA; 33 | public static final BitSet ALPHA; 34 | public static final BitSet NUMERIC; 35 | public static final BitSet HEXNUMBER; 36 | public static final BitSet ALPHA_NUMERIC; 37 | public static final BitSet IDENTIFIER; 38 | 39 | private final String str; 40 | private final int length; 41 | private int index = 0; 42 | 43 | private ParseToken next = null; 44 | 45 | public Lexer(String s) { 46 | this.str = s; 47 | this.length = s.length(); 48 | } 49 | 50 | private void check() { 51 | if (this.next != null) { 52 | return; 53 | } 54 | if (this.index >= this.length) { 55 | return; 56 | } 57 | char n = this.str.charAt(this.index++); 58 | while (Character.isWhitespace(n) && this.index < this.length) { 59 | n = this.str.charAt(this.index++); 60 | } 61 | if (ALPHA.get(n)) { 62 | StringBuilder token = new StringBuilder(); 63 | token.append(n); 64 | n = this.str.charAt(this.index++); 65 | while (IDENTIFIER.get(n)) { 66 | token.append(n); 67 | n = this.str.charAt(this.index++); 68 | } 69 | this.index--; 70 | this.next = new ParseToken(TokenType.IDENTIFIER, token.toString()); 71 | } else if (NUMERIC.get(n)) { 72 | StringBuilder token = new StringBuilder(); 73 | token.append(n); 74 | n = this.str.charAt(this.index++); 75 | if (n == 'x') { 76 | token.append(n); 77 | n = this.str.charAt(this.index++); 78 | while (HEXNUMBER.get(n)) { 79 | token.append(n); 80 | n = this.str.charAt(this.index++); 81 | } 82 | this.index--; 83 | this.next = new ParseToken(TokenType.HEXADECIMAL, token.toString()); 84 | } 85 | while (NUMERIC.get(n)) { 86 | token.append(n); 87 | n = this.str.charAt(this.index++); 88 | } 89 | if (n == 'L') { 90 | this.next = new ParseToken(TokenType.LONG, token.toString()); 91 | } else if (n == '.') { 92 | token.append(n); 93 | n = this.str.charAt(this.index++); 94 | while (NUMERIC.get(n)) { 95 | token.append(n); 96 | n = this.str.charAt(this.index++); 97 | } 98 | if (n == 'F' || n == 'f') { 99 | this.next = new ParseToken(TokenType.FLOAT, token.toString()); 100 | } else if (n == 'D' || n == 'd') { 101 | this.next = new ParseToken(TokenType.DOUBLE, token.toString()); 102 | } else { 103 | this.index--; 104 | this.next = new ParseToken(TokenType.DOUBLE, token.toString()); 105 | } 106 | } else { 107 | this.index--; 108 | this.next = new ParseToken(TokenType.INTEGER, token.toString()); 109 | } 110 | } else if (n == '"') { 111 | StringBuilder str = new StringBuilder(); 112 | n = this.str.charAt(this.index++); 113 | while (n != '"') { 114 | if (n == '\\') { 115 | n = this.str.charAt(this.index++); 116 | switch (n) { 117 | case '"': 118 | str.append('"'); 119 | break; 120 | case 'n': 121 | str.append('\n'); 122 | break; 123 | case 't': 124 | str.append('\t'); 125 | break; 126 | case 'r': 127 | str.append('\r'); 128 | break; 129 | case 'b': 130 | str.append('\b'); 131 | break; 132 | case 'f': 133 | str.append('\f'); 134 | break; 135 | case '\\': 136 | str.append('\\'); 137 | break; 138 | case '\'': 139 | str.append('\''); 140 | break; 141 | default: 142 | throw new IllegalStateException("Invalid escape char in stringF '" + n + "'"); 143 | } 144 | continue; 145 | } 146 | str.append(n); 147 | n = this.str.charAt(this.index++); 148 | } 149 | this.next = new ParseToken(TokenType.STRING_CONSTANT, str.toString()); 150 | } else { 151 | switch (n) { 152 | case '$': 153 | this.next = new ParseToken(TokenType.INJECTION_TOKEN); 154 | break; 155 | case ';': 156 | this.next = new ParseToken(TokenType.SEMICOLON); 157 | break; 158 | case '.': 159 | this.next = new ParseToken(TokenType.DOT); 160 | break; 161 | case ',': 162 | this.next = new ParseToken(TokenType.COMMA); 163 | break; 164 | case ':': 165 | this.next = new ParseToken(TokenType.COLON); 166 | break; 167 | case '(': 168 | this.next = new ParseToken(TokenType.LEFT_PAREN); 169 | break; 170 | case ')': 171 | this.next = new ParseToken(TokenType.RIGHT_PAREN); 172 | break; 173 | case '/': 174 | this.next = new ParseToken(TokenType.FORWARD_SLASH); 175 | break; 176 | case '=': { 177 | char nn = this.str.charAt(this.index); 178 | if (nn == '=') { 179 | this.index++; 180 | this.next = new ParseToken(TokenType.COMPARE_EQUALS); 181 | } else { 182 | this.next = new ParseToken(TokenType.EQUALS); 183 | } 184 | break; 185 | } 186 | case '!': { 187 | char nn = this.str.charAt(this.index); 188 | if (nn == '=') { 189 | this.index++; 190 | this.next = new ParseToken(TokenType.NOT_EQUALS); 191 | } else { 192 | this.next = new ParseToken(TokenType.NOT); 193 | } 194 | break; 195 | } 196 | default: 197 | throw new IllegalStateException("Unexpected symbol '" + n + "'"); 198 | } 199 | } 200 | } 201 | 202 | public boolean hasNext() { 203 | check(); 204 | return this.next != null; 205 | } 206 | 207 | public TokenType peekType() { 208 | check(); 209 | return this.next == null ? null : this.next.getType(); 210 | } 211 | 212 | public ParseToken peek() { 213 | check(); 214 | return this.next; 215 | } 216 | 217 | public ParseToken pop() { 218 | check(); 219 | ParseToken n = this.next; 220 | this.next = null; 221 | return n; 222 | } 223 | 224 | static { 225 | LOWER_ALPHA = new BitSet(); 226 | for (int i = 'a'; i <= 'z'; i++) { 227 | LOWER_ALPHA.set(i); 228 | } 229 | UPPER_ALPHA = new BitSet(); 230 | for (int i = 'A'; i <= 'Z'; i++) { 231 | UPPER_ALPHA.set(i); 232 | } 233 | ALPHA = new BitSet(); 234 | ALPHA.or(LOWER_ALPHA); 235 | ALPHA.or(UPPER_ALPHA); 236 | NUMERIC = new BitSet(); 237 | for (int i = '0'; i <= '9'; i++) { 238 | NUMERIC.set(i); 239 | } 240 | HEXNUMBER = new BitSet(); 241 | HEXNUMBER.or(NUMERIC); 242 | for (int i = 'a'; i <= 'f'; i++) { 243 | HEXNUMBER.set(i); 244 | } 245 | for (int i = 'A'; i <= 'F'; i++) { 246 | HEXNUMBER.set(i); 247 | } 248 | ALPHA_NUMERIC = new BitSet(); 249 | ALPHA_NUMERIC.or(ALPHA); 250 | ALPHA_NUMERIC.or(NUMERIC); 251 | IDENTIFIER = new BitSet(); 252 | IDENTIFIER.or(ALPHA_NUMERIC); 253 | IDENTIFIER.set('_'); 254 | IDENTIFIER.set('$'); 255 | } 256 | 257 | } 258 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/target/parse/MatchParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.target.parse; 26 | 27 | import static com.voxelgenesis.injector.target.parse.TokenType.*; 28 | 29 | import com.voxelgenesis.injector.target.match.InjectionMatcher; 30 | import com.voxelgenesis.injector.target.match.InjectionModifier; 31 | import com.voxelgenesis.injector.target.match.modifier.ConditionReplaceMatcher; 32 | import com.voxelgenesis.injector.target.match.modifier.ConditionValueModifier; 33 | import com.voxelgenesis.injector.target.match.modifier.InstructionReplaceMatcher; 34 | import com.voxelgenesis.injector.target.match.modifier.InstructionValueModifier; 35 | import com.voxelgenesis.injector.target.match.modifier.StatementInsertModifier; 36 | import org.spongepowered.despector.ast.generic.ClassTypeSignature; 37 | import org.spongepowered.despector.ast.insn.Instruction; 38 | import org.spongepowered.despector.ast.insn.condition.CompareCondition.CompareOperator; 39 | import org.spongepowered.despector.ast.insn.condition.Condition; 40 | import org.spongepowered.despector.ast.stmt.Statement; 41 | import org.spongepowered.despector.ast.stmt.branch.If; 42 | import org.spongepowered.despector.ast.stmt.misc.Return; 43 | import org.spongepowered.despector.ast.type.MethodEntry; 44 | import org.spongepowered.despector.transform.matcher.ConditionMatcher; 45 | import org.spongepowered.despector.transform.matcher.InstructionMatcher; 46 | import org.spongepowered.despector.transform.matcher.MatchContext; 47 | import org.spongepowered.despector.transform.matcher.StatementMatcher; 48 | import org.spongepowered.despector.transform.matcher.instruction.InstanceMethodInvokeMatcher; 49 | import org.spongepowered.despector.util.ConditionUtil; 50 | 51 | import java.util.ArrayList; 52 | import java.util.HashMap; 53 | import java.util.List; 54 | import java.util.Map; 55 | 56 | public class MatchParser { 57 | 58 | private final Lexer lexer; 59 | private final MethodEntry injector; 60 | 61 | private ModifierType modifier_type; 62 | private int index = 0; 63 | private int start, end; 64 | 65 | private final Map imports = new HashMap<>(); 66 | 67 | public MatchParser(String str, MethodEntry mth) { 68 | this.lexer = new Lexer(str); 69 | this.injector = mth; 70 | 71 | this.imports.put("String", "Ljava/lang/String;"); 72 | } 73 | 74 | public void addImport(String key, String clazz) { 75 | this.imports.put(key, clazz); 76 | } 77 | 78 | private void error(String msg) { 79 | // TODO add line and character information for debugging 80 | throw new IllegalStateException(msg); 81 | } 82 | 83 | private ParseToken expect(TokenType type) { 84 | ParseToken actual = this.lexer.pop(); 85 | if (actual.getType() != type) { 86 | error("Expected " + type.name() + " but whats " + actual.getType().name()); 87 | } 88 | return actual; 89 | } 90 | 91 | public InjectionMatcher parse() { 92 | List> matchers = new ArrayList<>(); 93 | 94 | while (this.lexer.hasNext()) { 95 | this.index = matchers.size(); 96 | StatementMatcher next = parseStatement(); 97 | if (next != null) { 98 | matchers.add(next); 99 | } 100 | } 101 | InjectionModifier modifier = null; 102 | switch (this.modifier_type) { 103 | case INSTRUCTION_REPLACE: 104 | modifier = new InstructionValueModifier(this.injector, matchers.get(this.start)); 105 | break; 106 | case CONDITION_REPLACE: 107 | modifier = new ConditionValueModifier(this.injector, matchers.get(this.start)); 108 | break; 109 | case STATEMENT_INSERT: 110 | modifier = new StatementInsertModifier(this.injector); 111 | break; 112 | default: 113 | throw new IllegalStateException(); 114 | } 115 | 116 | return new InjectionMatcher(matchers, modifier, this.start, this.end); 117 | } 118 | 119 | private StatementMatcher parseStatement() { 120 | if (this.lexer.peekType() == IDENTIFIER) { 121 | ParseToken first = this.lexer.pop(); 122 | if (first.getToken().equals("if")) { 123 | expect(LEFT_PAREN); 124 | ConditionMatcher condition = parseCondition(); 125 | expect(RIGHT_PAREN); 126 | if (!this.lexer.hasNext()) { 127 | return StatementMatcher.ifThen().condition(condition).build(); 128 | } 129 | throw new IllegalStateException(); 130 | } 131 | if (this.lexer.peekType() == LEFT_PAREN) { 132 | throw new IllegalStateException(); // TODO 133 | } 134 | String type = null; 135 | if (this.lexer.peekType() == TokenType.FORWARD_SLASH) { 136 | StringBuilder str = new StringBuilder("L").append(first.getToken()); 137 | while (this.lexer.peekType() == FORWARD_SLASH) { 138 | this.lexer.pop(); 139 | ParseToken next = expect(TokenType.IDENTIFIER); 140 | str.append("/").append(next.getToken()); 141 | } 142 | str.append(";"); 143 | type = str.toString(); 144 | } else { 145 | type = this.imports.get(first.getToken()); 146 | } 147 | if (this.lexer.peekType() == IDENTIFIER) { 148 | ParseToken name = this.lexer.pop(); 149 | if (this.lexer.peekType() == EQUALS) { 150 | this.lexer.pop(); 151 | InstructionMatcher val = parseInstruction(); 152 | expect(SEMICOLON); 153 | return MatchContext.storeLocal(name.getToken(), 154 | StatementMatcher.localAssign().type(ClassTypeSignature.of(type.toString())).value(val).build()); 155 | } 156 | throw new IllegalStateException(); 157 | } 158 | if (this.lexer.peekType() != DOT) { 159 | throw new IllegalStateException(); 160 | } 161 | InstructionMatcher owner = null; 162 | while (this.lexer.peekType() == DOT) { 163 | this.lexer.pop(); 164 | ParseToken next = expect(IDENTIFIER); 165 | if (this.lexer.peekType() == DOT) { 166 | if (owner == null) { 167 | owner = InstructionMatcher.staticFieldAccess().owner(ClassTypeSignature.of(type.toString())).name(next.getToken()).build(); 168 | } else { 169 | owner = InstructionMatcher.instanceFieldAccess().owner(owner).name(next.getToken()).build(); 170 | } 171 | } else if (this.lexer.peekType() == LEFT_PAREN) { 172 | this.lexer.pop(); 173 | InstanceMethodInvokeMatcher.Builder mth = InstructionMatcher.instanceMethodInvoke().callee(owner).name(next.getToken()); 174 | if (this.lexer.peekType() != RIGHT_PAREN) { 175 | int param_index = 0; 176 | while (true) { 177 | InstructionMatcher param = parseInstruction(); 178 | mth.param(param_index, param); 179 | if (this.lexer.peekType() == RIGHT_PAREN) { 180 | break; 181 | } 182 | expect(COMMA); 183 | } 184 | } 185 | expect(RIGHT_PAREN); 186 | owner = mth.build(); 187 | } 188 | } 189 | expect(SEMICOLON); 190 | return StatementMatcher.invoke().value(owner).build(); 191 | } else if (this.lexer.peekType() == INJECTION_TOKEN) { 192 | this.lexer.pop(); 193 | if (this.lexer.peekType() == INJECTION_TOKEN) { 194 | this.lexer.pop(); 195 | this.modifier_type = ModifierType.STATEMENT_INSERT; 196 | this.start = this.end = this.index; 197 | expect(SEMICOLON); 198 | } 199 | return null; 200 | } 201 | error("Expected statement"); 202 | return null; 203 | } 204 | 205 | private ConditionMatcher parseCondition() { 206 | if (this.lexer.peekType() == INJECTION_TOKEN) { 207 | this.lexer.pop(); 208 | this.start = this.end = this.index; 209 | this.modifier_type = ModifierType.CONDITION_REPLACE; 210 | if (this.lexer.peekType() == LEFT_PAREN) { 211 | this.lexer.pop(); 212 | ConditionMatcher child = parseCondition(); 213 | expect(RIGHT_PAREN); 214 | return new ConditionReplaceMatcher<>(child); 215 | } 216 | error("Expected injection child"); 217 | } 218 | InstructionMatcher left = parseInstruction(); 219 | ParseToken operator = this.lexer.pop(); 220 | InstructionMatcher right = parseInstruction(); 221 | switch (operator.getType()) { 222 | case NOT_EQUALS: 223 | return ConditionMatcher.compare().operator(CompareOperator.NOT_EQUAL).left(left).right(right).build(); 224 | default: 225 | error("Invalid condition operator: " + operator.getType().name()); 226 | } 227 | error("Expected condition"); 228 | return null; 229 | } 230 | 231 | private InstructionMatcher parseInstruction() { 232 | if (this.lexer.peekType() == INJECTION_TOKEN) { 233 | this.lexer.pop(); 234 | this.start = this.end = this.index; 235 | this.modifier_type = ModifierType.INSTRUCTION_REPLACE; 236 | if (this.lexer.peekType() == INJECTION_TOKEN) { 237 | return new InstructionReplaceMatcher<>(null); 238 | } else if (this.lexer.peekType() == LEFT_PAREN) { 239 | this.lexer.pop(); 240 | InstructionMatcher child = parseInstruction(); 241 | expect(RIGHT_PAREN); 242 | return new InstructionReplaceMatcher<>(child); 243 | } 244 | error("Expected injection child"); 245 | } 246 | if (this.lexer.peekType() == STRING_CONSTANT) { 247 | ParseToken next = this.lexer.pop(); 248 | return InstructionMatcher.stringConstant().value(next.getToken()).build(); 249 | } 250 | if (this.lexer.peekType() == IDENTIFIER) { 251 | ParseToken next = this.lexer.pop(); 252 | if (next.getToken().equals("null")) { 253 | return InstructionMatcher.nullConstant().build(); 254 | } 255 | return InstructionMatcher.localAccess().allowMissing().fromContext(next.getToken()).build(); 256 | } 257 | error("Expected instruction"); 258 | return null; 259 | } 260 | 261 | private static enum ModifierType { 262 | INSTRUCTION_REPLACE, 263 | CONDITION_REPLACE, 264 | STATEMENT_INSERT 265 | } 266 | 267 | } 268 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/target/parse/ParseToken.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.target.parse; 26 | 27 | public class ParseToken { 28 | 29 | private final TokenType type; 30 | private final String token; 31 | 32 | public ParseToken(TokenType type) { 33 | this.type = type; 34 | this.token = null; 35 | } 36 | 37 | public ParseToken(TokenType type, String s) { 38 | this.type = type; 39 | this.token = s; 40 | } 41 | 42 | public TokenType getType() { 43 | return this.type; 44 | } 45 | 46 | public String getToken() { 47 | return this.token; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/voxelgenesis/injector/target/parse/TokenType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) Despector 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package com.voxelgenesis.injector.target.parse; 26 | 27 | public enum TokenType { 28 | INJECTION_TOKEN, 29 | 30 | IDENTIFIER, 31 | INTEGER, 32 | LONG, 33 | FLOAT, 34 | DOUBLE, 35 | HEXADECIMAL, 36 | STRING_CONSTANT, 37 | 38 | EQUALS, 39 | DOT, 40 | COMMA, 41 | SEMICOLON, 42 | COLON, 43 | RIGHT_PAREN, 44 | LEFT_PAREN, 45 | FORWARD_SLASH, 46 | 47 | COMPARE_EQUALS, 48 | NOT_EQUALS, 49 | NOT, 50 | } 51 | --------------------------------------------------------------------------------