├── .gitattributes ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── test.yml │ └── update-gradle-wrapper.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── config └── checkstyle │ ├── checkstyle.xml │ └── suppressions.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── j9 ├── build.gradle └── src │ ├── main │ └── java │ │ └── net │ │ └── lenni0451 │ │ └── reflect │ │ ├── Classes$MR.java │ │ ├── Modules.java │ │ └── localcapture │ │ ├── LocalCapturer.java │ │ └── LocalStackFrameImpl.java │ └── test │ └── java │ └── net │ └── lenni0451 │ └── reflect │ ├── Classes$MRTest.java │ ├── ModulesTest.java │ └── localcapture │ └── LocalCapturerTest.java ├── settings.gradle └── src ├── main ├── java │ └── net │ │ └── lenni0451 │ │ └── reflect │ │ ├── Agents.java │ │ ├── Arrays.java │ │ ├── ClassLoaders.java │ │ ├── Classes$MR.java │ │ ├── Classes.java │ │ ├── Constructors.java │ │ ├── Enums.java │ │ ├── Fields.java │ │ ├── JVMConstants.java │ │ ├── JavaBypass.java │ │ ├── Methods.java │ │ ├── Modules.java │ │ ├── Objects.java │ │ ├── accessor │ │ ├── AccessorUtils.java │ │ ├── FieldAccessor.java │ │ └── MethodAccessor.java │ │ ├── bytecode │ │ ├── BytecodeUtils.java │ │ ├── builder │ │ │ ├── BytecodeBuilder.java │ │ │ ├── ClassBuilder.java │ │ │ ├── FieldBuilder.java │ │ │ └── MethodBuilder.java │ │ ├── impl │ │ │ └── asm │ │ │ │ ├── ASMBuilder.java │ │ │ │ ├── ASMBuiltClass.java │ │ │ │ ├── ASMClassBuilder.java │ │ │ │ ├── ASMFieldBuilder.java │ │ │ │ └── ASMMethodBuilder.java │ │ └── wrapper │ │ │ ├── BuiltClass.java │ │ │ ├── BytecodeLabel.java │ │ │ └── BytecodeType.java │ │ ├── exceptions │ │ ├── ConstructorInvocationException.java │ │ ├── ConstructorNotFoundException.java │ │ ├── FieldNotFoundException.java │ │ ├── InvalidOOPSizeException.java │ │ ├── MethodInvocationException.java │ │ └── MethodNotFoundException.java │ │ ├── localcapture │ │ ├── LocalCapturer.java │ │ ├── LocalStackFrame.java │ │ └── PrimitiveValue.java │ │ ├── proxy │ │ ├── InvocationHandler.java │ │ ├── ProxyBuilder.java │ │ ├── ProxyClass.java │ │ ├── ProxyClassDefiner.java │ │ ├── impl │ │ │ ├── Proxy.java │ │ │ ├── ProxyMethod.java │ │ │ └── ProxyRuntime.java │ │ └── internal │ │ │ ├── ProxyMethodBuilder.java │ │ │ └── ProxyUtils.java │ │ ├── stream │ │ ├── RStream.java │ │ ├── constructor │ │ ├── field │ │ │ ├── FieldStream.java │ │ │ └── FieldWrapper.java │ │ ├── general │ │ │ └── ModifierWrapper.java │ │ └── method │ │ │ ├── MethodStream.java │ │ │ └── MethodWrapper.java │ │ ├── utils │ │ ├── FieldInitializer.java │ │ └── ObjectPrinter.java │ │ └── wrapper │ │ └── ASMWrapper.java └── templates │ ├── UnsafeAccess.json │ ├── UnsafeAccess.mustache │ └── UnsafeAccessGenerator └── test └── java └── net └── lenni0451 └── reflect ├── AgentsTest.java ├── ArraysTest.java ├── ClassLoadersTest.java ├── ClassesTest.java ├── ConstructorsTest.java ├── EnumsTest.java ├── FieldsTest.java ├── JavaBypassTest.java ├── MethodsTest.java ├── ModulesTest.java ├── ObjectsTest.java ├── Tests.java ├── accessor ├── FieldAccessorTest.java └── MethodAccessorTest.java ├── bytecode └── BytecodeBuilderTest.java └── proxy ├── ProxyTest.java └── test ├── Class1.java ├── Class2.java ├── Class3.java ├── Class4.java └── Interface1.java /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | 10 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | buy_me_a_coffee: "lv0nrncj3m" 2 | custom: ["https://lenni0451.net/donate"] 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: "gradle" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: "test" 2 | on: ["push", "pull_request", "workflow_dispatch"] 3 | 4 | jobs: 5 | test: 6 | runs-on: "ubuntu-latest" 7 | strategy: 8 | matrix: 9 | java: ["8", "11", "17", "21", "23"] 10 | distribution: ["temurin", "semeru"] 11 | exclude: 12 | - java: "23" 13 | distribution: "semeru" 14 | name: "Java ${{ matrix.java }} (${{ matrix.distribution }})" 15 | steps: 16 | - name: "Checkout" 17 | uses: "actions/checkout@v4" 18 | - name: "Validate Gradle Wrapper" 19 | uses: "gradle/actions/wrapper-validation@v4" 20 | - name: "Set up JDKs" 21 | uses: "actions/setup-java@v4" 22 | with: 23 | java-version: "${{ matrix.java }}" 24 | distribution: "${{ matrix.distribution }}" 25 | - name: "Set Gradle Permissions" 26 | run: "chmod +x ./gradlew" 27 | - name: "Run Tests" 28 | run: "./gradlew test" 29 | -------------------------------------------------------------------------------- /.github/workflows/update-gradle-wrapper.yml: -------------------------------------------------------------------------------- 1 | name: Update Gradle Wrapper 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 1 * *" 7 | 8 | jobs: 9 | update-gradle-wrapper: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | 16 | - name: Make Gradle Wrapper Executable 17 | run: chmod +x gradlew 18 | 19 | - name: Update Gradle Wrapper 20 | uses: gradle-update/update-gradle-wrapper-action@v2 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /out/ 3 | */out/ 4 | /bin/ 5 | /.gradle/ 6 | build/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Lenni0451 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reflect 2 | A reflection library with some useful methods to bypass reflection restrictions in Java. 3 | 4 | ## Usage 5 | Check out [maven central](https://mvnrepository.com/artifact/net.lenni0451/Reflect) or my [maven server](https://maven.lenni0451.net/#/releases/net/lenni0451/Reflect) for the latest version. 6 | 7 | ### Gradle 8 | ```groovy 9 | dependencies { 10 | implementation "net.lenni0451:Reflect:x.x.x" 11 | } 12 | ``` 13 | 14 | ### Maven 15 | ```xml 16 | 17 | net.lenni0451 18 | Reflect 19 | x.x.x 20 | 21 | ``` 22 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "maven-publish" 3 | id "signing" 4 | id "net.lenni0451.template-processor" version "1.0.1" 5 | } 6 | 7 | allprojects { 8 | apply plugin: "java-library" 9 | apply plugin: "checkstyle" 10 | 11 | base { 12 | java.toolchain.languageVersion = JavaLanguageVersion.of(8) 13 | compileJava.options.encoding = compileTestJava.options.encoding = javadoc.options.encoding = "UTF-8" 14 | 15 | archivesName = project.maven_name ?: rootProject.maven_name 16 | group = project.maven_group ?: rootProject.maven_group 17 | version = project.maven_version ?: rootProject.maven_version 18 | } 19 | 20 | repositories { 21 | mavenCentral() 22 | } 23 | 24 | dependencies { 25 | compileOnly "com.google.code.findbugs:jsr305:3.0.2" 26 | compileOnly "org.jetbrains:annotations:26.0.2" 27 | compileOnly(annotationProcessor("org.projectlombok:lombok:1.18.38")) 28 | 29 | testImplementation "org.ow2.asm:asm:9.8" 30 | testImplementation(platform("org.junit:junit-bom:5.13.0")) 31 | testImplementation "org.junit.jupiter:junit-jupiter" 32 | testRuntimeOnly "org.junit.platform:junit-platform-launcher" 33 | testCompileOnly(testAnnotationProcessor("org.projectlombok:lombok:1.18.38")) 34 | } 35 | 36 | test { 37 | useJUnitPlatform() 38 | testLogging { 39 | events "passed", "skipped", "failed" 40 | } 41 | maxParallelForks Runtime.runtime.availableProcessors() 42 | } 43 | 44 | checkstyle { 45 | toolVersion = "9.3" //Latest version for Java 8: 9.3 46 | configFile = rootProject.file("config/checkstyle/checkstyle.xml") 47 | } 48 | 49 | build.dependsOn(test) 50 | build.dependsOn(check) 51 | } 52 | 53 | templateProcessor { 54 | templateDir = project.file("src/main/templates") 55 | outputDir = layout.buildDirectory.dir("generated/templates/main/java") 56 | markAsSource(project.sourceSets.main) 57 | } 58 | 59 | java { 60 | withSourcesJar() 61 | withJavadocJar() 62 | } 63 | 64 | artifacts { 65 | archives javadocJar, sourcesJar 66 | } 67 | 68 | javadoc { 69 | failOnError = false 70 | } 71 | 72 | jar { 73 | manifest { 74 | attributes "Multi-Release": "true" 75 | } 76 | 77 | dependsOn ":j9:classes" 78 | from(project(":j9").sourceSets.main.output) { 79 | into "META-INF/versions/9" 80 | } 81 | } 82 | 83 | publishing { 84 | repositories { 85 | maven { 86 | name = "reposilite" 87 | def releasesUrl = "https://maven.lenni0451.net/releases" 88 | def snapshotsUrl = "https://maven.lenni0451.net/snapshots" 89 | url = project.maven_version.endsWith("SNAPSHOT") ? snapshotsUrl : releasesUrl 90 | 91 | credentials(PasswordCredentials) 92 | authentication { 93 | basic(BasicAuthentication) 94 | } 95 | } 96 | maven { 97 | name = "ossrh" 98 | def releasesUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" 99 | def snapshotsUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" 100 | url = project.maven_version.endsWith("SNAPSHOT") ? snapshotsUrl : releasesUrl 101 | 102 | credentials(PasswordCredentials) 103 | authentication { 104 | basic(BasicAuthentication) 105 | } 106 | } 107 | } 108 | publications { 109 | maven(MavenPublication) { 110 | artifactId = project.maven_name ?: rootProject.maven_name 111 | groupId = rootProject.maven_group 112 | version = rootProject.maven_version 113 | 114 | from components.java 115 | 116 | pom { 117 | name = rootProject.name 118 | description = rootProject.maven_description 119 | url = "https://github.com/" + rootProject.github_repo 120 | licenses { 121 | license { 122 | name = "MIT License" 123 | url = "https://github.com/" + rootProject.github_repo + "/blob/main/LICENSE" 124 | } 125 | } 126 | developers { 127 | developer { 128 | id = "Lenni0451" 129 | } 130 | } 131 | scm { 132 | connection = "scm:git:git://github.com/" + rootProject.github_repo + ".git" 133 | developerConnection = "scm:git:ssh://github.com/" + rootProject.github_repo + ".git" 134 | url = "github.com/" + rootProject.github_repo 135 | } 136 | } 137 | } 138 | } 139 | } 140 | 141 | signing { 142 | setRequired false 143 | sign configurations.archives 144 | sign publishing.publications.maven 145 | } 146 | 147 | project.tasks.withType(PublishToMavenRepository).forEach { 148 | it.dependsOn(project.tasks.withType(Sign)) 149 | } 150 | -------------------------------------------------------------------------------- /config/checkstyle/suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | #Gradle settings 2 | org.gradle.daemon=true 3 | org.gradle.parallel=true 4 | org.gradle.configureondemand=true 5 | 6 | #Maven settings 7 | maven_name=Reflect 8 | maven_group=net.lenni0451 9 | maven_version=1.5.0 10 | 11 | maven_description=A reflection library with some useful methods to bypass reflection restrictions in Java 12 | github_repo=Lenni0451/Reflect 13 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lenni0451/Reflect/8c24db9192c4a41a91984bb19e0f5bc6b70e5552/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip 5 | networkTimeout=10000 6 | validateDistributionUrl=true 7 | zipStoreBase=GRADLE_USER_HOME 8 | zipStorePath=wrapper/dists 9 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH= 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /j9/build.gradle: -------------------------------------------------------------------------------- 1 | base { 2 | java.toolchain.languageVersion = JavaLanguageVersion.of(9) 3 | } 4 | 5 | configurations { 6 | testImplementation.extendsFrom compileOnly 7 | } 8 | 9 | dependencies { 10 | compileOnly project(":") 11 | } 12 | -------------------------------------------------------------------------------- /j9/src/main/java/net/lenni0451/reflect/Classes$MR.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import lombok.SneakyThrows; 4 | 5 | /** 6 | * This class contains methods which need to be replaced by other implementations for newer JDKs. 7 | */ 8 | class Classes$MR { 9 | 10 | private static final StackWalker STACK_WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); 11 | 12 | @SneakyThrows 13 | public static Class getCallerClass(final int depth) { 14 | return STACK_WALKER.walk(s -> s 15 | .skip(depth + 3) 16 | .findFirst() 17 | .map(StackWalker.StackFrame::getDeclaringClass) 18 | .orElse(null)); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /j9/src/main/java/net/lenni0451/reflect/Modules.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import lombok.SneakyThrows; 4 | import net.lenni0451.reflect.exceptions.FieldNotFoundException; 5 | import net.lenni0451.reflect.exceptions.MethodNotFoundException; 6 | 7 | import java.lang.invoke.MethodHandle; 8 | import java.lang.reflect.Field; 9 | 10 | import static net.lenni0451.reflect.JVMConstants.*; 11 | import static net.lenni0451.reflect.JavaBypass.TRUSTED_LOOKUP; 12 | import static net.lenni0451.reflect.utils.FieldInitializer.reqInit; 13 | 14 | /** 15 | * This class contains some useful methods for working with modules. 16 | */ 17 | public class Modules { 18 | 19 | private static final Field moduleField = reqInit( 20 | () -> Fields.getDeclaredField(Class.class, FIELD_Class_module), 21 | () -> new FieldNotFoundException(FIELD_Class_module, Class.class.getName()) 22 | ); 23 | private static final Field everyoneModuleField = reqInit( 24 | () -> Fields.getDeclaredField(Module.class, FIELD_Module_EVERYONE_MODULE), 25 | () -> new FieldNotFoundException(FIELD_Module_EVERYONE_MODULE, Module.class.getName()) 26 | ); 27 | private static final MethodHandle implAddExportsOrOpens = reqInit( 28 | () -> Methods.getDeclaredMethod(Module.class, METHOD_Module_implAddExportsOrOpens, String.class, Module.class, boolean.class, boolean.class), 29 | TRUSTED_LOOKUP::unreflect, 30 | () -> new MethodNotFoundException(Module.class.getName(), METHOD_Module_implAddExportsOrOpens, String.class, Module.class, boolean.class, boolean.class) 31 | ); 32 | 33 | /** 34 | * Copy the module from one class to another.
35 | * This allows the usage of jdk internal classes which are normally protected by restricted module access.
36 | * In Java 8 this method does nothing. 37 | * 38 | * @param from The class to copy the module from 39 | * @param to The class to copy the module to 40 | */ 41 | public static void copyModule(final Class from, final Class to) { 42 | Fields.copyObject(from, to, moduleField); 43 | } 44 | 45 | /** 46 | * Open a module of a class to everyone.
47 | * This allows the usage of jdk internal classes which are normally protected by restricted module access.
48 | * In Java 8 this method does nothing. 49 | * 50 | * @param clazz The class to open the module of 51 | */ 52 | public static void openModule(final Class clazz) { 53 | openModule(clazz, clazz.getPackage().getName()); 54 | } 55 | 56 | /** 57 | * Open a package of a module to everyone.
58 | * This allows the usage of jdk internal classes which are normally protected by restricted module access.
59 | * In Java 8 this method does nothing. 60 | * 61 | * @param clazz The class to open the module of 62 | * @param pkg The package to open 63 | */ 64 | @SneakyThrows 65 | public static void openModule(final Class clazz, final String pkg) { 66 | Module everyone = Fields.get(null, everyoneModuleField); 67 | implAddExportsOrOpens.invoke(clazz.getModule(), pkg, everyone, true, true); 68 | } 69 | 70 | /** 71 | * Open all packages of a module to everyone.
72 | * This allows the usage of jdk internal classes which are normally protected by restricted module access.
73 | * In Java 8 this method does nothing. 74 | * 75 | * @param clazz The class to open the module of 76 | */ 77 | @SneakyThrows 78 | public static void openEntireModule(final Class clazz) { 79 | Module everyone = Fields.get(null, everyoneModuleField); 80 | for (String pkg : clazz.getModule().getPackages()) { 81 | implAddExportsOrOpens.invoke(clazz.getModule(), pkg, everyone, true, true); 82 | } 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /j9/src/main/java/net/lenni0451/reflect/localcapture/LocalCapturer.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.localcapture; 2 | 3 | import net.lenni0451.reflect.Classes; 4 | import net.lenni0451.reflect.exceptions.MethodNotFoundException; 5 | 6 | import java.lang.invoke.MethodHandle; 7 | import java.lang.invoke.MethodType; 8 | import java.util.Set; 9 | import java.util.function.Consumer; 10 | import java.util.function.Function; 11 | import java.util.stream.Stream; 12 | 13 | import static net.lenni0451.reflect.JavaBypass.TRUSTED_LOOKUP; 14 | import static net.lenni0451.reflect.utils.FieldInitializer.reqInit; 15 | 16 | public class LocalCapturer { 17 | 18 | private static final Class liveStackFrame = Classes.forName("java.lang.LiveStackFrame"); 19 | private static final StackWalker stackWalker = reqInit( 20 | () -> { 21 | MethodHandle getStackWalker = TRUSTED_LOOKUP.findStatic(liveStackFrame, "getStackWalker", MethodType.methodType(StackWalker.class, Set.class)); 22 | return (StackWalker) getStackWalker.invokeExact(Set.of(StackWalker.Option.RETAIN_CLASS_REFERENCE)); 23 | }, 24 | () -> new MethodNotFoundException(liveStackFrame.getName(), "getStackWalker", StackWalker.class) 25 | ); 26 | 27 | public static void forEach(final Consumer consumer) { 28 | walk(s -> { 29 | s.skip(1).forEach(consumer); 30 | return null; 31 | }); 32 | } 33 | 34 | public static T walk(final Function, T> function) { 35 | return stackWalker.walk(s -> function.apply(s.skip(1).map(LocalStackFrameImpl::new))); 36 | } 37 | 38 | public static LocalStackFrame[] getStackFrames() { 39 | return walk(s -> s.toArray(LocalStackFrame[]::new)); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /j9/src/main/java/net/lenni0451/reflect/localcapture/LocalStackFrameImpl.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.localcapture; 2 | 3 | import lombok.Getter; 4 | import lombok.SneakyThrows; 5 | import net.lenni0451.reflect.Classes; 6 | import net.lenni0451.reflect.exceptions.FieldNotFoundException; 7 | import net.lenni0451.reflect.exceptions.MethodNotFoundException; 8 | import org.intellij.lang.annotations.MagicConstant; 9 | 10 | import java.lang.invoke.MethodHandle; 11 | import java.lang.invoke.MethodType; 12 | 13 | import static net.lenni0451.reflect.JavaBypass.TRUSTED_LOOKUP; 14 | import static net.lenni0451.reflect.utils.FieldInitializer.reqInit; 15 | 16 | class LocalStackFrameImpl implements LocalStackFrame { 17 | 18 | private static final Class liveStackFrameInfo = Classes.forName("java.lang.LiveStackFrameInfo"); 19 | private static final Class primitiveSlot32 = Classes.forName("java.lang.LiveStackFrameInfo$PrimitiveSlot32"); 20 | private static final Class primitiveSlot64 = Classes.forName("java.lang.LiveStackFrameInfo$PrimitiveSlot64"); 21 | 22 | private static final MethodHandle liveStackFrameInfo_getMonitors = reqInit( 23 | () -> TRUSTED_LOOKUP.findVirtual(liveStackFrameInfo, "getMonitors", MethodType.methodType(Object[].class)), 24 | handle -> handle.asType(MethodType.methodType(Object[].class, StackWalker.StackFrame.class)), 25 | () -> new MethodNotFoundException(liveStackFrameInfo.getName(), "getMonitors", Object[].class) 26 | ); 27 | private static final MethodHandle liveStackFrameInfo_getLocals = reqInit( 28 | () -> TRUSTED_LOOKUP.findVirtual(liveStackFrameInfo, "getLocals", MethodType.methodType(Object[].class)), 29 | handle -> handle.asType(MethodType.methodType(Object[].class, StackWalker.StackFrame.class)), 30 | () -> new MethodNotFoundException(liveStackFrameInfo.getName(), "getLocals", Object[].class) 31 | ); 32 | private static final MethodHandle liveStackFrameInfo_getStack = reqInit( 33 | () -> TRUSTED_LOOKUP.findVirtual(liveStackFrameInfo, "getStack", MethodType.methodType(Object[].class)), 34 | handle -> handle.asType(MethodType.methodType(Object[].class, StackWalker.StackFrame.class)), 35 | () -> new MethodNotFoundException(liveStackFrameInfo.getName(), "getStack", Object[].class) 36 | ); 37 | private static final MethodHandle liveStackFrameInfo_mode = reqInit( 38 | () -> TRUSTED_LOOKUP.findGetter(liveStackFrameInfo, "mode", int.class), 39 | handle -> handle.asType(MethodType.methodType(int.class, StackWalker.StackFrame.class)), 40 | () -> new FieldNotFoundException(liveStackFrameInfo.getName(), "mode") 41 | ); 42 | private static final MethodHandle primitiveSlot32_value = reqInit( 43 | () -> TRUSTED_LOOKUP.findGetter(primitiveSlot32, "value", int.class), 44 | handle -> handle.asType(MethodType.methodType(int.class, Object.class)), 45 | () -> new FieldNotFoundException(primitiveSlot32.getName(), "value") 46 | ); 47 | private static final MethodHandle primitiveSlot64_value = reqInit( 48 | () -> TRUSTED_LOOKUP.findGetter(primitiveSlot64, "value", long.class), 49 | handle -> handle.asType(MethodType.methodType(long.class, Object.class)), 50 | () -> new FieldNotFoundException(primitiveSlot64.getName(), "value") 51 | ); 52 | 53 | private static final int MODE_INTERPRETED = 0x01; 54 | private static final int MODE_COMPILED = 0x02; 55 | 56 | private final StackWalker.StackFrame parent; 57 | @Getter(lazy = true) 58 | private final Object[] monitors = this.convertObjectArray(liveStackFrameInfo_getMonitors); 59 | @Getter(lazy = true) 60 | private final Object[] locals = this.convertObjectArray(liveStackFrameInfo_getLocals); 61 | @Getter(lazy = true) 62 | private final Object[] stack = this.convertObjectArray(liveStackFrameInfo_getStack); 63 | 64 | public LocalStackFrameImpl(final StackWalker.StackFrame parent) { 65 | if (!liveStackFrameInfo.isInstance(parent)) throw new IllegalArgumentException("The parent stack frame is not an instance of LiveStackFrameInfo"); 66 | this.parent = parent; 67 | } 68 | 69 | @Override 70 | public String getClassName() { 71 | return this.parent.getClassName(); 72 | } 73 | 74 | @Override 75 | public String getMethodName() { 76 | return this.parent.getMethodName(); 77 | } 78 | 79 | @Override 80 | public Class getDeclaringClass() { 81 | return this.parent.getDeclaringClass(); 82 | } 83 | 84 | @Override 85 | public int getByteCodeIndex() { 86 | return this.parent.getByteCodeIndex(); 87 | } 88 | 89 | @Override 90 | public String getFileName() { 91 | return this.parent.getFileName(); 92 | } 93 | 94 | @Override 95 | public int getLineNumber() { 96 | return this.parent.getLineNumber(); 97 | } 98 | 99 | @Override 100 | public boolean isNativeMethod() { 101 | return this.parent.isNativeMethod(); 102 | } 103 | 104 | @Override 105 | public StackTraceElement toStackTraceElement() { 106 | return this.parent.toStackTraceElement(); 107 | } 108 | 109 | @Override 110 | @SneakyThrows 111 | @MagicConstant(intValues = {MODE_INTERPRETED, MODE_COMPILED}) 112 | public int getMode() { 113 | return (int) liveStackFrameInfo_mode.invokeExact(this.parent); 114 | } 115 | 116 | @SneakyThrows 117 | private Object[] convertObjectArray(final MethodHandle getter) { 118 | Object[] array = (Object[]) getter.invokeExact(this.parent); 119 | Object[] converted = new Object[array.length]; 120 | for (int i = 0; i < array.length; i++) { 121 | Object item = array[i]; 122 | if (primitiveSlot32.isInstance(item)) { 123 | converted[i] = new PrimitiveValue(4, (int) primitiveSlot32_value.invokeExact(item)); 124 | } else if (primitiveSlot64.isInstance(item)) { 125 | converted[i] = new PrimitiveValue(8, (long) primitiveSlot64_value.invokeExact(item)); 126 | } else { 127 | converted[i] = item; 128 | } 129 | } 130 | return converted; 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /j9/src/test/java/net/lenni0451/reflect/Classes$MRTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertEquals; 6 | 7 | class Classes$MRTest { 8 | 9 | @Test 10 | void getCallerClass() { 11 | assertEquals(Classes$MRTest.class, TestClasses.getCallerClass(0)); 12 | } 13 | 14 | 15 | private static class TestClasses { 16 | /** 17 | * Base implementation of {@link Classes#getCallerClass(int)}.
18 | * Normally the Classes method should be called, but this doesn't work in the test environment because of multi release jars. 19 | */ 20 | private static Class getCallerClass(final int depth) { 21 | return Classes$MR.getCallerClass(depth); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /j9/src/test/java/net/lenni0451/reflect/ModulesTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 6 | 7 | class ModulesTest { 8 | 9 | @Test 10 | void copyModule() { 11 | assertDoesNotThrow(() -> Modules.copyModule(Object.class, ModuleHolder.class)); 12 | } 13 | 14 | @Test 15 | void openModule() { 16 | assertDoesNotThrow(() -> Modules.openModule(ModulesTest.class)); 17 | } 18 | 19 | 20 | private static class ModuleHolder { 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /j9/src/test/java/net/lenni0451/reflect/localcapture/LocalCapturerTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.localcapture; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | class LocalCapturerTest { 7 | 8 | @Test 9 | void test() { 10 | String secret = "This is a secret text"; 11 | Assertions.assertEquals(secret, getSecret()); 12 | } 13 | 14 | private static String getSecret() { 15 | return LocalCapturer.walk(s -> s 16 | .filter(f -> f.getMethodName().equals("test")) 17 | .findFirst() 18 | .map(LocalStackFrame::getLocals) 19 | .orElseThrow(() -> new IllegalStateException("No locals found"))[1] //0 is the 'this' reference 20 | .toString()); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | maven { 6 | url = "https://maven.lenni0451.net/releases" 7 | } 8 | } 9 | } 10 | 11 | plugins { 12 | id "org.gradle.toolchains.foojay-resolver-convention" version "0.10.0" 13 | } 14 | 15 | rootProject.name = "Reflect" 16 | 17 | include("j9") 18 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/Arrays.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import net.lenni0451.reflect.accessor.UnsafeAccess; 4 | 5 | import javax.annotation.ParametersAreNonnullByDefault; 6 | 7 | /** 8 | * This class contains some useful methods for working with arrays. 9 | */ 10 | @ParametersAreNonnullByDefault 11 | public class Arrays { 12 | 13 | static { 14 | if (JVMConstants.OPENJ9_RUNTIME) throw new UnsupportedOperationException("OpenJ9 is not supported"); 15 | } 16 | 17 | /** 18 | * Set the length of an array.
19 | * It is recommended to only decrease the length of the array.
20 | * Increasing the length of the array would overwrite the memory of the next object. 21 | * 22 | * @param array The array 23 | * @param newLength The new length 24 | */ 25 | public static void setLength(final Object array, final int newLength) { 26 | if (!array.getClass().isArray()) throw new IllegalArgumentException("Object is not an array"); 27 | UnsafeAccess.putInt(array, Objects.OBJECT_HEADER_SIZE, newLength); 28 | } 29 | 30 | /** 31 | * Fill a byte array with a value.
32 | * This directly writes to the memory of the array and is therefore very fast. 33 | * 34 | * @param array The array 35 | * @param value The value to fill the array with 36 | */ 37 | public static void fill(final byte[] array, final byte value) { 38 | UnsafeAccess.setMemory(array, Objects.ARRAY_HEADER_SIZE, array.length, value); 39 | } 40 | 41 | /** 42 | * Fill a short array with a value.
43 | * This directly writes to the memory of the array and is therefore very fast. 44 | * 45 | * @param array The array 46 | * @param value The value to fill the array with 47 | */ 48 | public static void fill(final short[] array, final short value) { 49 | if (value == 0) { 50 | UnsafeAccess.setMemory(array, Objects.ARRAY_HEADER_SIZE, array.length * 2L, (byte) 0); 51 | } else { 52 | for (int i = 0; i < array.length * 2; i += 2) { 53 | UnsafeAccess.putShort(array, (long) Objects.ARRAY_HEADER_SIZE + i, value); 54 | } 55 | } 56 | } 57 | 58 | /** 59 | * Fill a char array with a value.
60 | * This directly writes to the memory of the array and is therefore very fast. 61 | * 62 | * @param array The array 63 | * @param value The value to fill the array with 64 | */ 65 | public static void fill(final char[] array, final char value) { 66 | if (value == 0) { 67 | UnsafeAccess.setMemory(array, Objects.ARRAY_HEADER_SIZE, array.length * 2L, (byte) 0); 68 | } else { 69 | for (int i = 0; i < array.length * 2; i += 2) { 70 | UnsafeAccess.putChar(array, (long) Objects.ARRAY_HEADER_SIZE + i, value); 71 | } 72 | } 73 | } 74 | 75 | /** 76 | * Fill an int array with a value.
77 | * This directly writes to the memory of the array and is therefore very fast. 78 | * 79 | * @param array The array 80 | * @param value The value to fill the array with 81 | */ 82 | public static void fill(final int[] array, final int value) { 83 | if (value == 0) { 84 | UnsafeAccess.setMemory(array, Objects.ARRAY_HEADER_SIZE, array.length * 4L, (byte) 0); 85 | } else { 86 | for (int i = 0; i < array.length * 4; i += 4) { 87 | UnsafeAccess.putInt(array, (long) Objects.ARRAY_HEADER_SIZE + i, value); 88 | } 89 | } 90 | } 91 | 92 | /** 93 | * Fill a long array with a value.
94 | * This directly writes to the memory of the array and is therefore very fast. 95 | * 96 | * @param array The array 97 | * @param value The value to fill the array with 98 | */ 99 | public static void fill(final long[] array, final long value) { 100 | if (value == 0) { 101 | UnsafeAccess.setMemory(array, Objects.ARRAY_HEADER_SIZE, array.length * 8L, (byte) 0); 102 | } else { 103 | for (int i = 0; i < array.length * 8; i += 8) { 104 | UnsafeAccess.putLong(array, (long) Objects.ARRAY_HEADER_SIZE + i, value); 105 | } 106 | } 107 | } 108 | 109 | /** 110 | * Fill a float array with a value.
111 | * This directly writes to the memory of the array and is therefore very fast. 112 | * 113 | * @param array The array 114 | * @param value The value to fill the array with 115 | */ 116 | public static void fill(final float[] array, final float value) { 117 | if (value == 0) { 118 | UnsafeAccess.setMemory(array, Objects.ARRAY_HEADER_SIZE, array.length * 4L, (byte) 0); 119 | } else { 120 | for (int i = 0; i < array.length * 4; i += 4) { 121 | UnsafeAccess.putFloat(array, (long) Objects.ARRAY_HEADER_SIZE + i, value); 122 | } 123 | } 124 | } 125 | 126 | /** 127 | * Fill a double array with a value.
128 | * This directly writes to the memory of the array and is therefore very fast. 129 | * 130 | * @param array The array 131 | * @param value The value to fill the array with 132 | */ 133 | public static void fill(final double[] array, final double value) { 134 | if (value == 0) { 135 | UnsafeAccess.setMemory(array, Objects.ARRAY_HEADER_SIZE, array.length * 8L, (byte) 0); 136 | } else { 137 | for (int i = 0; i < array.length * 8; i += 8) { 138 | UnsafeAccess.putDouble(array, (long) Objects.ARRAY_HEADER_SIZE + i, value); 139 | } 140 | } 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/Classes$MR.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import net.lenni0451.reflect.exceptions.MethodNotFoundException; 4 | import net.lenni0451.reflect.utils.FieldInitializer; 5 | import sun.reflect.Reflection; 6 | 7 | import java.lang.invoke.MethodHandle; 8 | import java.lang.invoke.MethodType; 9 | 10 | /** 11 | * This class contains methods which need to be replaced by other implementations for newer JDKs. 12 | */ 13 | class Classes$MR { 14 | 15 | private static final SecurityManager SECURITY_MANAGER = new SecurityManager(); 16 | private static final MethodHandle GET_CLASS_CONTEXT = FieldInitializer.reqInit( 17 | () -> JavaBypass.TRUSTED_LOOKUP.findVirtual(SecurityManager.class, "getClassContext", MethodType.methodType(Class[].class)), 18 | () -> new MethodNotFoundException(SecurityManager.class.getName(), "getClassContext") 19 | ); 20 | 21 | public static Class getCallerClass(final int depth) throws Throwable { 22 | try { 23 | return Reflection.getCallerClass(depth + 3); 24 | } catch (Throwable ignored) { 25 | } 26 | try { 27 | Class[] classes = (Class[]) GET_CLASS_CONTEXT.invokeExact(SECURITY_MANAGER); 28 | return classes[depth + 2]; 29 | } catch (ArrayIndexOutOfBoundsException e) { 30 | //Don't crash if the depth is too high 31 | return null; 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/Constructors.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import lombok.SneakyThrows; 4 | import net.lenni0451.reflect.exceptions.ConstructorInvocationException; 5 | import net.lenni0451.reflect.exceptions.MethodNotFoundException; 6 | 7 | import javax.annotation.Nullable; 8 | import java.lang.invoke.MethodHandle; 9 | import java.lang.reflect.Constructor; 10 | import java.util.Arrays; 11 | 12 | import static net.lenni0451.reflect.JVMConstants.METHOD_Class_getDeclaredConstructors0; 13 | import static net.lenni0451.reflect.JavaBypass.TRUSTED_LOOKUP; 14 | import static net.lenni0451.reflect.utils.FieldInitializer.reqInit; 15 | 16 | /** 17 | * This class contains some useful methods for working with constructors. 18 | */ 19 | public class Constructors { 20 | 21 | private static final MethodHandle getDeclaredConstructors0 = reqInit( 22 | () -> { 23 | if (JVMConstants.OPENJ9_RUNTIME) return Methods.getDeclaredMethod(Class.class, METHOD_Class_getDeclaredConstructors0); 24 | else return Methods.getDeclaredMethod(Class.class, METHOD_Class_getDeclaredConstructors0, boolean.class); 25 | }, 26 | TRUSTED_LOOKUP::unreflect, () -> new MethodNotFoundException(Class.class.getName(), METHOD_Class_getDeclaredConstructors0, JVMConstants.OPENJ9_RUNTIME ? "" : "boolean") 27 | ); 28 | 29 | /** 30 | * Get all declared constructors of a class.
31 | * The reflection filter of the class will be ignored.
32 | * An empty array will be returned if the method could not be invoked. 33 | * 34 | * @param clazz The class to get the constructors from 35 | * @param The type of the class 36 | * @return An array of all declared constructors of the class 37 | * @throws MethodNotFoundException If the {@link Class} internal {@code getDeclaredConstructors0} method could not be found 38 | */ 39 | @SneakyThrows 40 | public static Constructor[] getDeclaredConstructors(final Class clazz) { 41 | if (JVMConstants.OPENJ9_RUNTIME) return (Constructor[]) getDeclaredConstructors0.invokeExact(clazz); 42 | else return (Constructor[]) getDeclaredConstructors0.invokeExact(clazz, false); 43 | } 44 | 45 | /** 46 | * Get a declared constructor of a class by its parameter types.
47 | * The reflection filter of the class will be ignored. 48 | * 49 | * @param clazz The class to get the constructor from 50 | * @param parameterTypes The parameter types of the constructor 51 | * @param The type of the class 52 | * @return The constructor or null if it doesn't exist 53 | */ 54 | @Nullable 55 | public static Constructor getDeclaredConstructor(final Class clazz, final Class... parameterTypes) { 56 | for (Constructor constructor : getDeclaredConstructors(clazz)) { 57 | if (Arrays.equals(constructor.getParameterTypes(), parameterTypes)) return constructor; 58 | } 59 | return null; 60 | } 61 | 62 | 63 | /** 64 | * Invoke a constructor without any checks.
65 | * The constructor does not have to be accessible. 66 | * 67 | * @param constructor The constructor to invoke 68 | * @param args The arguments to pass to the constructor 69 | * @param The type of the class 70 | * @return The instance of the class 71 | * @throws RuntimeException If the constructor could not be invoked 72 | */ 73 | public static T invoke(final Constructor constructor, final Object... args) { 74 | try { 75 | return (T) TRUSTED_LOOKUP.unreflectConstructor(constructor).asSpreader(Object[].class, args.length).invoke(args); 76 | } catch (Throwable t) { 77 | throw new ConstructorInvocationException(constructor).cause(t); 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/Enums.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import net.lenni0451.reflect.exceptions.ConstructorNotFoundException; 4 | import net.lenni0451.reflect.exceptions.FieldNotFoundException; 5 | 6 | import javax.annotation.Nullable; 7 | import java.lang.reflect.Constructor; 8 | import java.lang.reflect.Field; 9 | import java.util.Arrays; 10 | 11 | import static net.lenni0451.reflect.JVMConstants.*; 12 | import static net.lenni0451.reflect.utils.FieldInitializer.reqOptInit; 13 | 14 | /** 15 | * This class contains some useful methods for working with enums. 16 | */ 17 | public class Enums { 18 | 19 | private static final Field enumVarsField = reqOptInit( 20 | OPENJ9_RUNTIME, 21 | () -> Fields.getDeclaredField(Class.class, FIELD_Class_EnumVars), 22 | () -> new FieldNotFoundException(Class.class.getName(), FIELD_Class_EnumVars) 23 | ); 24 | private static final Field enumConstantsField = reqOptInit( 25 | !OPENJ9_RUNTIME, 26 | () -> Fields.getDeclaredField(Class.class, FIELD_Class_enumConstants), 27 | () -> new FieldNotFoundException(Class.class.getName(), FIELD_Class_enumConstants) 28 | ); 29 | private static final Field enumConstantDirectoryField = reqOptInit( 30 | !OPENJ9_RUNTIME, 31 | () -> Fields.getDeclaredField(Class.class, FIELD_Class_enumConstantDirectory), 32 | () -> new FieldNotFoundException(Class.class.getName(), FIELD_Class_enumConstantDirectory) 33 | ); 34 | 35 | /** 36 | * Create a new instance of an enum.
37 | * The value will not be added to the enum class. Use {@link #addEnumInstance(Class, Enum)} for that. 38 | * 39 | * @param enumClass The enum class 40 | * @param name The name of the enum value 41 | * @param ordinal The ordinal of the enum value 42 | * @param argumentTypes The argument types of the constructor 43 | * @param arguments The arguments of the constructor 44 | * @param The enum type 45 | * @return The new enum value 46 | */ 47 | public static > T newInstance(final Class enumClass, final String name, final int ordinal, final Class[] argumentTypes, final Object[] arguments) { 48 | Class[] types = new Class[arguments.length + 2]; 49 | types[0] = String.class; 50 | types[1] = int.class; 51 | System.arraycopy(argumentTypes, 0, types, 2, argumentTypes.length); 52 | 53 | Object[] args = new Object[arguments.length + 2]; 54 | args[0] = name; 55 | args[1] = ordinal; 56 | System.arraycopy(arguments, 0, args, 2, arguments.length); 57 | 58 | Constructor constructor = Constructors.getDeclaredConstructor(enumClass, types); 59 | if (constructor == null) throw new ConstructorNotFoundException(enumClass.getSimpleName(), argumentTypes); 60 | return Constructors.invoke(constructor, args); 61 | } 62 | 63 | /** 64 | * Add a new enum value to an enum class.
65 | * The enum value will be added to the enum class and the enum value cache will be cleared.
66 | * The ordinal of the enum value will be ignored. The value will be added at the end of the enum. 67 | * 68 | * @param enumClass The enum class 69 | * @param enumValue The enum value to add 70 | * @param The enum type 71 | */ 72 | public static > void addEnumInstance(final Class enumClass, final T enumValue) { 73 | //Add the enum value to the enum class 74 | Field values = Fields.getDeclaredField(enumClass, FIELD_Enum_$VALUES); 75 | Object[] valuesArray = Fields.getObject(null, values); 76 | valuesArray = Arrays.copyOf(valuesArray, valuesArray.length + 1); 77 | valuesArray[valuesArray.length - 1] = enumValue; 78 | Fields.setObject(null, values, valuesArray); 79 | 80 | clearEnumCache(enumClass); 81 | } 82 | 83 | /** 84 | * Clear the enum value cache of an enum class.
85 | * This will force the JVM to re-calculate the enum values when calling {@link Class#getEnumConstants()}. 86 | * 87 | * @param enumClass The enum class 88 | */ 89 | public static void clearEnumCache(final Class enumClass) { 90 | if (OPENJ9_RUNTIME) { 91 | Fields.setObject(enumClass, enumVarsField, null); 92 | } else { 93 | Fields.setObject(enumClass, enumConstantsField, null); 94 | Fields.setObject(enumClass, enumConstantDirectoryField, null); 95 | } 96 | } 97 | 98 | /** 99 | * Get an enum value by its name ignoring the case.
100 | * This method will return null if the enum value doesn't exist. 101 | * 102 | * @param enumClass The enum class 103 | * @param name The name of the enum value 104 | * @return The enum value or null if it doesn't exist 105 | */ 106 | @Nullable 107 | public static Object valueOfIgnoreCase(final Class enumClass, final String name) { 108 | for (Object constant : enumClass.getEnumConstants()) { 109 | Enum enumConstant = (Enum) constant; 110 | if (enumConstant.name().equalsIgnoreCase(name)) return constant; 111 | } 112 | return null; 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/JVMConstants.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | public class JVMConstants { 4 | 5 | public static final boolean OPENJ9_RUNTIME = System.getProperty("java.vm.name").toLowerCase().contains("openj9"); 6 | 7 | public static final String CLASS_InstrumentationImpl = calc("sun.instrument.InstrumentationImpl"); 8 | public static final String CLASS_MethodHandles_Lookup_ClassOption = calc("java.lang.invoke.MethodHandles$Lookup$ClassOption"); 9 | public static final String CLASS_INTERNAL_Unsafe = calc("jdk.internal.misc.Unsafe"); 10 | public static final String CLASS_INTERNAL_Reflection = calc("jdk.internal.reflect.Reflection"); 11 | public static final String CLASS_SUN_Reflection = calc("sun.reflect.Reflection"); 12 | 13 | public static final String FIELD_MethodHandles_Lookup_IMPL_LOOKUP = calc("IMPL_LOOKUP"); 14 | public static final String FIELD_URLClassLoader_ucp = calc("ucp"); 15 | public static final String FIELD_Enum_$VALUES = calc("$VALUES"); 16 | public static final String FIELD_Class_enumConstants = calc("enumConstants"); 17 | public static final String FIELD_Class_enumConstantDirectory = calc("enumConstantDirectory"); 18 | public static final String FIELD_Class_EnumVars = calc("enumVars"); 19 | public static final String FIELD_Reflection_fieldFilterMap = calc("fieldFilterMap"); 20 | public static final String FIELD_Reflection_methodFilterMap = calc("methodFilterMap"); 21 | public static final String FIELD_Class_module = calc("module"); 22 | public static final String FIELD_Module_EVERYONE_MODULE = calc("EVERYONE_MODULE"); 23 | 24 | public static final String METHOD_Class_getDeclaredClasses0 = calc("getDeclaredClasses0", OPENJ9_RUNTIME, "getDeclaredClassesImpl"); 25 | public static final String METHOD_Class_getDeclaredFields0 = calc("getDeclaredFields0", OPENJ9_RUNTIME, "getDeclaredFieldsImpl"); 26 | public static final String METHOD_Class_getDeclaredConstructors0 = calc("getDeclaredConstructors0", OPENJ9_RUNTIME, "getDeclaredConstructorsImpl"); 27 | public static final String METHOD_Class_getDeclaredMethods0 = calc("getDeclaredMethods0", OPENJ9_RUNTIME, "getDeclaredMethodsImpl"); 28 | public static final String METHOD_InstrumentationImpl_loadAgent = calc("loadAgent"); 29 | public static final String METHOD_Unsafe_defineAnonymousClass = calc("defineAnonymousClass"); 30 | public static final String METHOD_MethodHandles_Lookup_defineHiddenClass = calc("defineHiddenClass"); 31 | public static final String METHOD_URLClassPath_addURL = calc("addURL"); 32 | public static final String METHOD_URLClassPath_getURLs = calc("getURLs"); 33 | public static final String METHOD_ClassLoader_defineClass = calc("defineClass"); 34 | public static final String METHOD_Module_implAddExportsOrOpens = calc("implAddExportsOrOpens"); 35 | public static final String METHOD_Module_getPackages = calc("getPackages"); 36 | public static final String METHOD_InternalUnsafe_staticFieldOffset = calc("staticFieldOffset"); 37 | public static final String METHOD_InternalUnsafe_objectFieldOffset = calc("objectFieldOffset"); 38 | 39 | 40 | /** 41 | * Prevent the java compiler from inlining static final strings.
42 | * Also allows to use a different string depending on the JVM runtime. 43 | * 44 | * @param s The string to prevent from inlining 45 | * @param args The arguments to check 46 | * @return The same string 47 | */ 48 | private static String calc(final String s, final Object... args) { 49 | if (args.length % 2 != 0) throw new IllegalArgumentException("Arguments must be in pairs"); 50 | for (int i = 0; i < args.length; i += 2) { 51 | if (!(args[i] instanceof Boolean)) throw new IllegalArgumentException("Argument " + i + " must be a boolean"); 52 | if (Boolean.TRUE.equals(args[i])) return args[i + 1].toString(); 53 | } 54 | return s; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/JavaBypass.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import sun.misc.Unsafe; 4 | import sun.reflect.ReflectionFactory; 5 | 6 | import javax.annotation.Nullable; 7 | import java.lang.invoke.MethodHandles; 8 | import java.lang.reflect.Field; 9 | 10 | import static net.lenni0451.reflect.JVMConstants.*; 11 | import static net.lenni0451.reflect.utils.FieldInitializer.ThrowingSupplier.getFirst; 12 | import static net.lenni0451.reflect.utils.FieldInitializer.*; 13 | 14 | /** 15 | * The main class for bypassing java restrictions.
16 | * This class contains the unsafe instance and the trusted lookup instance used for everything else. 17 | */ 18 | public class JavaBypass { 19 | 20 | /** 21 | * The instance of the unsafe class. 22 | */ 23 | public static final Unsafe UNSAFE = getUnsafe(); 24 | /** 25 | * The instance of the trusted lookup. 26 | */ 27 | public static final MethodHandles.Lookup TRUSTED_LOOKUP = getTrustedLookup(); 28 | /** 29 | * The instance of the internal unsafe class. 30 | */ 31 | @Nullable 32 | public static final Object INTERNAL_UNSAFE = getInternalUnsafe(); 33 | 34 | /** 35 | * Get the unsafe instance.
36 | * You should use the static instance {@link #UNSAFE} instead. 37 | * 38 | * @return The unsafe instance 39 | * @throws IllegalStateException If the unsafe instance could not be gotten 40 | */ 41 | public static Unsafe getUnsafe() { 42 | return process( 43 | () -> reqInit( 44 | () -> { 45 | for (Field field : Unsafe.class.getDeclaredFields()) { 46 | if (field.getType().equals(Unsafe.class)) { 47 | field.setAccessible(true); 48 | return (Unsafe) field.get(null); 49 | } 50 | } 51 | return null; 52 | }, 53 | () -> new IllegalStateException("Unsafe field not found or was null") 54 | ), 55 | cause -> new IllegalStateException("Unable to get unsafe instance", cause) 56 | ); 57 | } 58 | 59 | /** 60 | * Get the trusted lookup instance.
61 | * You should use the static instance {@link #TRUSTED_LOOKUP} instead. 62 | * 63 | * @return The trusted lookup instance 64 | * @throws IllegalStateException If the trusted lookup instance could not be gotten 65 | */ 66 | public static MethodHandles.Lookup getTrustedLookup() { 67 | return process( 68 | () -> reqInit( 69 | getFirst(() -> { 70 | MethodHandles.lookup(); //Load class before getting the trusted lookup 71 | Field lookupField = MethodHandles.Lookup.class.getDeclaredField(FIELD_MethodHandles_Lookup_IMPL_LOOKUP); 72 | long lookupFieldOffset = UNSAFE.staticFieldOffset(lookupField); 73 | return (MethodHandles.Lookup) UNSAFE.getObject(UNSAFE.staticFieldBase(lookupField), lookupFieldOffset); 74 | }, () -> { 75 | MethodHandles.Lookup lookup = (MethodHandles.Lookup) ReflectionFactory.getReflectionFactory() 76 | .newConstructorForSerialization(MethodHandles.Lookup.class, MethodHandles.Lookup.class.getDeclaredConstructor(Class.class)) 77 | .newInstance(MethodHandles.Lookup.class); 78 | return (MethodHandles.Lookup) lookup.findStaticGetter(MethodHandles.Lookup.class, FIELD_MethodHandles_Lookup_IMPL_LOOKUP, MethodHandles.Lookup.class).invokeExact(); 79 | }), 80 | () -> new IllegalStateException("Lookup field was null") 81 | ), 82 | cause -> new IllegalStateException("Unable to get trusted lookup instance", cause) 83 | ); 84 | } 85 | 86 | /** 87 | * Get the internal unsafe instance.
88 | * You should use the static instance {@link #INTERNAL_UNSAFE} instead.
89 | * The internal unsafe was added in Java 11 and has more low level access. 90 | * 91 | * @return The internal unsafe instance 92 | */ 93 | @Nullable 94 | public static Object getInternalUnsafe() { 95 | return process( 96 | () -> condReqInit( 97 | () -> Class.forName(CLASS_INTERNAL_Unsafe), 98 | unsafeClass -> { 99 | for (Field field : unsafeClass.getDeclaredFields()) { 100 | if (field.getType().equals(unsafeClass)) return TRUSTED_LOOKUP.unreflectGetter(field).invoke(); 101 | } 102 | return null; 103 | }, 104 | () -> new IllegalStateException("Internal unsafe field not found or was null") 105 | ), 106 | cause -> new IllegalStateException("Unable to get internal unsafe instance", cause) 107 | ); 108 | } 109 | 110 | /** 111 | * Clear the reflection filter maps.
112 | * This will allow you to access all fields and methods of a class. 113 | * 114 | * @throws ClassNotFoundException If the Reflection class is not found 115 | */ 116 | public static void clearReflectionFilter() throws ClassNotFoundException { 117 | Class reflectionClass; 118 | try { 119 | reflectionClass = Class.forName(CLASS_INTERNAL_Reflection); 120 | } catch (Throwable t) { 121 | reflectionClass = Class.forName(CLASS_SUN_Reflection); 122 | } 123 | 124 | Fields.setObject(null, Fields.getDeclaredField(reflectionClass, FIELD_Reflection_fieldFilterMap), null); 125 | Fields.setObject(null, Fields.getDeclaredField(reflectionClass, FIELD_Reflection_methodFilterMap), null); 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/Methods.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import lombok.SneakyThrows; 4 | import net.lenni0451.reflect.exceptions.MethodInvocationException; 5 | import net.lenni0451.reflect.exceptions.MethodNotFoundException; 6 | 7 | import javax.annotation.Nonnull; 8 | import javax.annotation.Nullable; 9 | import java.lang.invoke.MethodHandle; 10 | import java.lang.reflect.Method; 11 | import java.lang.reflect.Modifier; 12 | import java.util.Arrays; 13 | 14 | import static net.lenni0451.reflect.JVMConstants.METHOD_Class_getDeclaredMethods0; 15 | import static net.lenni0451.reflect.JavaBypass.TRUSTED_LOOKUP; 16 | import static net.lenni0451.reflect.utils.FieldInitializer.reqInit; 17 | 18 | /** 19 | * This class contains some useful methods for working with methods. 20 | */ 21 | public class Methods { 22 | 23 | private static final MethodHandle getDeclaredMethods0 = reqInit( 24 | () -> { 25 | if (JVMConstants.OPENJ9_RUNTIME) return Class.class.getDeclaredMethod(METHOD_Class_getDeclaredMethods0); 26 | else return Class.class.getDeclaredMethod(METHOD_Class_getDeclaredMethods0, boolean.class); 27 | }, 28 | TRUSTED_LOOKUP::unreflect, () -> new MethodNotFoundException(Class.class.getName(), METHOD_Class_getDeclaredMethods0, JVMConstants.OPENJ9_RUNTIME ? "" : "boolean") 29 | ); 30 | 31 | /** 32 | * Get all declared methods of a class.
33 | * The reflection filter of the class will be ignored. 34 | * 35 | * @param clazz The class to get the methods from 36 | * @return An array of all declared methods of the class 37 | * @throws MethodNotFoundException If the {@link Class} internal {@code getDeclaredMethods0} method could not be found 38 | */ 39 | @SneakyThrows 40 | public static Method[] getDeclaredMethods(final Class clazz) { 41 | if (JVMConstants.OPENJ9_RUNTIME) return (Method[]) getDeclaredMethods0.invokeExact(clazz); 42 | else return (Method[]) getDeclaredMethods0.invokeExact(clazz, false); 43 | } 44 | 45 | /** 46 | * Get a declared method of a class by its name and parameter types.
47 | * The reflection filter of the class will be ignored. 48 | * 49 | * @param clazz The class to get the method from 50 | * @param name The name of the method 51 | * @param parameterTypes The parameter types of the method 52 | * @return The method or null if it doesn't exist 53 | */ 54 | @Nullable 55 | public static Method getDeclaredMethod(final Class clazz, final String name, final Class... parameterTypes) { 56 | for (Method method : getDeclaredMethods(clazz)) { 57 | if (method.getName().equals(name) && Arrays.equals(method.getParameterTypes(), parameterTypes)) return method; 58 | } 59 | return null; 60 | } 61 | 62 | 63 | /** 64 | * Invoke a method without any checks.
65 | * The method does not have to be accessible. 66 | * 67 | * @param instance The instance to invoke the method on 68 | * @param method The method to invoke 69 | * @param args The arguments to pass to the method 70 | * @param The return type of the method 71 | * @return The return value of the method (null if void) 72 | * @throws MethodInvocationException If the method could not be invoked 73 | */ 74 | public static T invoke(@Nullable final Object instance, final Method method, final Object... args) { 75 | try { 76 | if (Modifier.isStatic(method.getModifiers())) return (T) TRUSTED_LOOKUP.unreflect(method).invokeWithArguments(args); 77 | else return (T) TRUSTED_LOOKUP.unreflect(method).bindTo(instance).invokeWithArguments(args); 78 | } catch (Throwable t) { 79 | throw new MethodInvocationException(method).cause(t); 80 | } 81 | } 82 | 83 | /** 84 | * Invoke a super method without any checks.
85 | * The method does not have to be accessible. 86 | * 87 | * @param instance The instance to invoke the method on 88 | * @param superClass The super class to call the method of 89 | * @param method The method to invoke 90 | * @param args The arguments to pass to the method 91 | * @param The type of the instance 92 | * @param The type of the super class 93 | * @param The return type of the method 94 | * @return The return value of the method (null if void) 95 | * @throws IllegalStateException If the method is static 96 | * @throws MethodInvocationException If the method could not be invoked 97 | */ 98 | public static T invokeSuper(@Nonnull final I instance, @Nonnull final Class superClass, final Method method, final Object... args) { 99 | if (Modifier.isStatic(method.getModifiers())) throw new IllegalArgumentException("Cannot invoke static super method"); 100 | try { 101 | return (T) TRUSTED_LOOKUP.unreflectSpecial(method, superClass).bindTo(instance).invokeWithArguments(args); 102 | } catch (Throwable t) { 103 | throw new MethodInvocationException(method).cause(t); 104 | } 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/Modules.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import lombok.SneakyThrows; 4 | 5 | /** 6 | * This class contains some useful methods for working with modules. 7 | */ 8 | public class Modules { 9 | 10 | /** 11 | * Copy the module from one class to another.
12 | * This allows the usage of jdk internal classes which are normally protected by restricted module access.
13 | * In Java 8 this method does nothing. 14 | * 15 | * @param from The class to copy the module from 16 | * @param to The class to copy the module to 17 | */ 18 | public static void copyModule(final Class from, final Class to) { 19 | //Nothing to do in Java 8 20 | //Check out the Java 9+ version 21 | } 22 | 23 | /** 24 | * Open a module of a class to everyone.
25 | * This allows the usage of jdk internal classes which are normally protected by restricted module access.
26 | * In Java 8 this method does nothing. 27 | * 28 | * @param clazz The class to open the module of 29 | */ 30 | public static void openModule(final Class clazz) { 31 | //Nothing to do in Java 8 32 | //Check out the Java 9+ version 33 | } 34 | 35 | /** 36 | * Open a package of a module to everyone.
37 | * This allows the usage of jdk internal classes which are normally protected by restricted module access.
38 | * In Java 8 this method does nothing. 39 | * 40 | * @param clazz The class to open the module of 41 | * @param pkg The package to open 42 | */ 43 | @SneakyThrows 44 | public static void openModule(final Class clazz, final String pkg) { 45 | //Nothing to do in Java 8 46 | //Check out the Java 9+ version 47 | } 48 | 49 | /** 50 | * Open all packages of a module to everyone.
51 | * This allows the usage of jdk internal classes which are normally protected by restricted module access.
52 | * In Java 8 this method does nothing. 53 | * 54 | * @param clazz The class to open the module of 55 | */ 56 | @SneakyThrows 57 | public static void openEntireModule(final Class clazz) { 58 | //Nothing to do in Java 8 59 | //Check out the Java 9+ version 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/accessor/AccessorUtils.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.accessor; 2 | 3 | import net.lenni0451.reflect.bytecode.builder.BytecodeBuilder; 4 | import net.lenni0451.reflect.bytecode.builder.ClassBuilder; 5 | import org.jetbrains.annotations.ApiStatus; 6 | 7 | import javax.annotation.Nullable; 8 | import java.util.function.Supplier; 9 | 10 | import static net.lenni0451.reflect.bytecode.BytecodeUtils.*; 11 | 12 | /** 13 | * Utils for creating accessors for fields and methods. 14 | */ 15 | @ApiStatus.Internal 16 | class AccessorUtils { 17 | 18 | public static void addConstructor(final BytecodeBuilder builder, final ClassBuilder cb, @Nullable final Supplier> instanceType, final boolean isStatic) { 19 | if (isStatic || instanceType == null) { 20 | cb.method(builder.opcode("ACC_PUBLIC"), "", mdesc(void.class), null, null, mb -> mb 21 | .var(builder.opcode("ALOAD"), 0) 22 | .method(builder.opcode("INVOKESPECIAL"), slash(Object.class), "", mdesc(void.class), false) 23 | .insn(builder.opcode("RETURN")) 24 | .maxs(1, 1) 25 | ); 26 | } else { 27 | cb.field(builder.opcode("ACC_PRIVATE", "ACC_FINAL"), "instance", desc(instanceType.get()), null, null, fb -> {}); 28 | 29 | cb.method(builder.opcode("ACC_PUBLIC"), "", mdesc(void.class, instanceType.get()), null, null, mb -> mb 30 | .var(builder.opcode("ALOAD"), 0) 31 | .method(builder.opcode("INVOKESPECIAL"), slash(Object.class), "", mdesc(void.class), false) 32 | .var(builder.opcode("ALOAD"), 0) 33 | .var(builder.opcode("ALOAD"), 1) 34 | .field(builder.opcode("PUTFIELD"), cb.getName(), "instance", desc(instanceType.get())) 35 | .insn(builder.opcode("RETURN")) 36 | .maxs(2, 2) 37 | ); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/bytecode/BytecodeUtils.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.bytecode; 2 | 3 | import org.jetbrains.annotations.ApiStatus; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | @ApiStatus.Experimental 8 | public class BytecodeUtils { 9 | 10 | /** 11 | * Replace all dots with slashes. 12 | * 13 | * @param className The class name 14 | * @return The class name with replaced dots 15 | */ 16 | public static String slash(final String className) { 17 | return className.replace('.', '/'); 18 | } 19 | 20 | /** 21 | * Get the name of the class with slashes instead of dots. 22 | * 23 | * @param clazz The class 24 | * @return The class name with slashes 25 | */ 26 | public static String slash(final Class clazz) { 27 | return slash(clazz.getName()); 28 | } 29 | 30 | /** 31 | * Replace all slashes with dots. 32 | * 33 | * @param className The class name 34 | * @return The class name with replaced slashes 35 | */ 36 | public static String dot(final String className) { 37 | return className.replace('/', '.'); 38 | } 39 | 40 | /** 41 | * Get a descriptor for the given class name.
42 | * Dots in the class name will automatically be replaced with slashes. 43 | * 44 | * @param className The class name 45 | * @return The descriptor 46 | */ 47 | public static String desc(final String className) { 48 | return "L" + slash(className) + ";"; 49 | } 50 | 51 | /** 52 | * Get the descriptor for the given class.
53 | * This also supports primitive types and arrays. 54 | * 55 | * @param clazz The class 56 | * @return The descriptor 57 | */ 58 | public static String desc(final Class clazz) { 59 | if (void.class.equals(clazz)) return "V"; 60 | else if (boolean.class.equals(clazz)) return "Z"; 61 | else if (byte.class.equals(clazz)) return "B"; 62 | else if (short.class.equals(clazz)) return "S"; 63 | else if (char.class.equals(clazz)) return "C"; 64 | else if (int.class.equals(clazz)) return "I"; 65 | else if (long.class.equals(clazz)) return "J"; 66 | else if (float.class.equals(clazz)) return "F"; 67 | else if (double.class.equals(clazz)) return "D"; 68 | else if (clazz.isArray()) return "[" + desc(clazz.getComponentType()); 69 | else return desc(clazz.getName()); 70 | } 71 | 72 | /** 73 | * Get the descriptor for the given method. 74 | * 75 | * @param method The method 76 | * @return The descriptor 77 | */ 78 | public static String desc(final Method method) { 79 | return mdesc(method.getReturnType(), method.getParameterTypes()); 80 | } 81 | 82 | /** 83 | * Get the descriptor for the given parameter types and return type. 84 | * 85 | * @param parameterTypes The parameter types 86 | * @param returnType The return type 87 | * @return The descriptor 88 | */ 89 | public static String mdesc(final Class returnType, final Class... parameterTypes) { 90 | StringBuilder builder = new StringBuilder("("); 91 | for (Class parameterType : parameterTypes) builder.append(desc(parameterType)); 92 | builder.append(")").append(desc(returnType)); 93 | return builder.toString(); 94 | } 95 | 96 | /** 97 | * Get the fitting return opcode for the given type. 98 | * 99 | * @param clazz The type 100 | * @return The opcode name 101 | */ 102 | public static String getLoadOpcode(final Class clazz) { 103 | if (boolean.class.equals(clazz) || byte.class.equals(clazz) || char.class.equals(clazz) || short.class.equals(clazz) || int.class.equals(clazz)) return "ILOAD"; 104 | if (long.class.equals(clazz)) return "LLOAD"; 105 | if (float.class.equals(clazz)) return "FLOAD"; 106 | if (double.class.equals(clazz)) return "DLOAD"; 107 | return "ALOAD"; 108 | } 109 | 110 | /** 111 | * Get the fitting return opcode for the given type.
112 | * {@link Void} will return {@code RETURN}. 113 | * 114 | * @param clazz The type 115 | * @return The opcode name 116 | */ 117 | public static String getReturnOpcode(final Class clazz) { 118 | if (void.class.equals(clazz)) return "RETURN"; 119 | if (boolean.class.equals(clazz) || byte.class.equals(clazz) || char.class.equals(clazz) || short.class.equals(clazz) || int.class.equals(clazz)) return "IRETURN"; 120 | if (long.class.equals(clazz)) return "LRETURN"; 121 | if (float.class.equals(clazz)) return "FRETURN"; 122 | if (double.class.equals(clazz)) return "DRETURN"; 123 | return "ARETURN"; 124 | } 125 | 126 | /** 127 | * Get the stack size for the given type. 128 | * 129 | * @param clazz The type 130 | * @return The stack size 131 | */ 132 | public static int getStackSize(final Class clazz) { 133 | if (long.class.equals(clazz) || double.class.equals(clazz)) return 2; 134 | return 1; 135 | } 136 | 137 | public static Class boxed(final Class clazz) { 138 | if (clazz == void.class) return Void.class; 139 | if (clazz == boolean.class) return Boolean.class; 140 | if (clazz == byte.class) return Byte.class; 141 | if (clazz == short.class) return Short.class; 142 | if (clazz == char.class) return Character.class; 143 | if (clazz == int.class) return Integer.class; 144 | if (clazz == long.class) return Long.class; 145 | if (clazz == float.class) return Float.class; 146 | if (clazz == double.class) return Double.class; 147 | return clazz; 148 | } 149 | 150 | public static Class unboxed(final Class clazz) { 151 | if (clazz == Void.class) return void.class; 152 | if (clazz == Boolean.class) return boolean.class; 153 | if (clazz == Byte.class) return byte.class; 154 | if (clazz == Short.class) return short.class; 155 | if (clazz == Character.class) return char.class; 156 | if (clazz == Integer.class) return int.class; 157 | if (clazz == Long.class) return long.class; 158 | if (clazz == Float.class) return float.class; 159 | if (clazz == Double.class) return double.class; 160 | return clazz; 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/bytecode/builder/BytecodeBuilder.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.bytecode.builder; 2 | 3 | import lombok.SneakyThrows; 4 | import net.lenni0451.reflect.Classes; 5 | import net.lenni0451.reflect.bytecode.wrapper.BuiltClass; 6 | import net.lenni0451.reflect.bytecode.wrapper.BytecodeLabel; 7 | import net.lenni0451.reflect.bytecode.wrapper.BytecodeType; 8 | import org.jetbrains.annotations.ApiStatus; 9 | 10 | import java.lang.invoke.MethodType; 11 | import java.util.function.Consumer; 12 | 13 | import static net.lenni0451.reflect.JavaBypass.TRUSTED_LOOKUP; 14 | 15 | @ApiStatus.Experimental 16 | public interface BytecodeBuilder { 17 | 18 | @SneakyThrows 19 | static BytecodeBuilder get() { 20 | if (Classes.byName("org.objectweb.asm.Opcodes", BytecodeBuilder.class.getClassLoader()) != null 21 | || Classes.byName("jdk.internal.org.objectweb.asm.Opcodes", BytecodeBuilder.class.getClassLoader()) != null) { 22 | Class impl = Classes.forName("net.lenni0451.reflect.bytecode.impl.asm.ASMBuilder", BytecodeBuilder.class.getClassLoader()); 23 | return (BytecodeBuilder) TRUSTED_LOOKUP.findConstructor(impl, MethodType.methodType(void.class)).invoke(); 24 | } 25 | throw new UnsupportedOperationException("No supported bytecode library found"); 26 | } 27 | 28 | 29 | BuiltClass class_(final int access, final String name, final String signature, final String superName, final String[] interfaces, final Consumer consumer); 30 | 31 | BytecodeLabel label(); 32 | 33 | BytecodeType type(final String descriptor); 34 | 35 | int opcode(final String name); 36 | 37 | default int opcode(final String name, final String... or) { 38 | int opcode = this.opcode(name); 39 | for (String s : or) opcode |= this.opcode(s); 40 | return opcode; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/bytecode/builder/ClassBuilder.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.bytecode.builder; 2 | 3 | import org.jetbrains.annotations.ApiStatus; 4 | 5 | import java.util.function.Consumer; 6 | 7 | @ApiStatus.Experimental 8 | public interface ClassBuilder { 9 | 10 | String getName(); 11 | 12 | void field(final int access, final String name, final String descriptor, final String signature, final Object defaultValue, final Consumer consumer); 13 | 14 | default void field(final int access, final String name, final String descriptor, final String signature, final Object defaultValue) { 15 | this.field(access, name, descriptor, signature, defaultValue, fb -> {}); 16 | } 17 | 18 | void method(final int access, final String name, final String descriptor, final String signature, final String[] exceptions, final Consumer consumer); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/bytecode/builder/FieldBuilder.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.bytecode.builder; 2 | 3 | import org.jetbrains.annotations.ApiStatus; 4 | 5 | @ApiStatus.Experimental 6 | public interface FieldBuilder { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/bytecode/builder/MethodBuilder.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.bytecode.builder; 2 | 3 | import net.lenni0451.reflect.bytecode.wrapper.BytecodeLabel; 4 | import org.jetbrains.annotations.ApiStatus; 5 | 6 | import static net.lenni0451.reflect.bytecode.BytecodeUtils.*; 7 | 8 | @ApiStatus.Experimental 9 | public interface MethodBuilder { 10 | 11 | MethodBuilder insn(final int opcode); 12 | 13 | MethodBuilder int_(final int opcode, final int value); 14 | 15 | default MethodBuilder intPush(final BytecodeBuilder builder, final int i) { 16 | if (i >= -1 && i <= 5) return this.insn(builder.opcode("ICONST_" + i)); 17 | if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) return this.int_(builder.opcode("BIPUSH"), i); 18 | if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) return this.int_(builder.opcode("SIPUSH"), i); 19 | return this.ldc(i); 20 | } 21 | 22 | MethodBuilder var(final int opcode, final int varIndex); 23 | 24 | MethodBuilder type(final int opcode, final String type); 25 | 26 | default MethodBuilder box(final BytecodeBuilder builder, final Class primitive) { 27 | Class boxed = boxed(primitive); 28 | if (boxed != primitive) { 29 | this.method(builder.opcode("INVOKESTATIC"), slash(boxed), "valueOf", mdesc(boxed, primitive), false); 30 | } 31 | return this; 32 | } 33 | 34 | default MethodBuilder unbox(final BytecodeBuilder builder, final Class primitive) { 35 | Class boxed = boxed(primitive); 36 | if (boxed != primitive) { 37 | this.method(builder.opcode("INVOKEVIRTUAL"), slash(boxed), primitive.getSimpleName() + "Value", mdesc(primitive), false); 38 | } 39 | return this; 40 | } 41 | 42 | MethodBuilder field(final int opcode, final String owner, final String name, final String descriptor); 43 | 44 | MethodBuilder method(final int opcode, final String owner, final String name, final String descriptor, final boolean isInterface); 45 | 46 | MethodBuilder jump(final int opcode, final BytecodeLabel label); 47 | 48 | MethodBuilder label(final BytecodeLabel label); 49 | 50 | MethodBuilder ldc(final Object value); 51 | 52 | default MethodBuilder typeLdc(final BytecodeBuilder builder, final Class clazz) { 53 | Class boxed = boxed(clazz); 54 | if (boxed == clazz) { 55 | this.ldc(builder.type(desc(clazz))); 56 | } else { 57 | this.field(builder.opcode("GETSTATIC"), slash(boxed), "TYPE", desc(Class.class)); 58 | } 59 | return this; 60 | } 61 | 62 | MethodBuilder iinc(final int varIndex, final int increment); 63 | 64 | MethodBuilder multiANewArray(final String descriptor, final int dimensions); 65 | 66 | MethodBuilder tryCatch(final BytecodeLabel start, final BytecodeLabel end, final BytecodeLabel handler, final String type); 67 | 68 | MethodBuilder maxs(final int maxStack, final int maxLocals); 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/bytecode/impl/asm/ASMBuilder.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.bytecode.impl.asm; 2 | 3 | import lombok.SneakyThrows; 4 | import net.lenni0451.reflect.bytecode.builder.BytecodeBuilder; 5 | import net.lenni0451.reflect.bytecode.builder.ClassBuilder; 6 | import net.lenni0451.reflect.bytecode.wrapper.BuiltClass; 7 | import net.lenni0451.reflect.bytecode.wrapper.BytecodeLabel; 8 | import net.lenni0451.reflect.bytecode.wrapper.BytecodeType; 9 | import net.lenni0451.reflect.stream.RStream; 10 | import org.jetbrains.annotations.ApiStatus; 11 | 12 | import java.lang.invoke.MethodHandle; 13 | import java.lang.invoke.MethodType; 14 | import java.util.HashMap; 15 | import java.util.Locale; 16 | import java.util.Map; 17 | import java.util.function.Consumer; 18 | 19 | import static net.lenni0451.reflect.JavaBypass.TRUSTED_LOOKUP; 20 | 21 | @ApiStatus.Internal 22 | class ASMBuilder implements BytecodeBuilder { 23 | 24 | public static final Class CLASS_Opcodes; 25 | public static final Class CLASS_ClassWriter; 26 | public static final Class CLASS_FieldVisitor; 27 | public static final Class CLASS_MethodVisitor; 28 | public static final Class CLASS_Label; 29 | public static final Class CLASS_type; 30 | 31 | private static final Map opcodes = new HashMap<>(); 32 | 33 | static { 34 | CLASS_Opcodes = forName("org.objectweb.asm.Opcodes", "jdk.internal.org.objectweb.asm.Opcodes"); 35 | CLASS_ClassWriter = forName("org.objectweb.asm.ClassWriter", "jdk.internal.org.objectweb.asm.ClassWriter"); 36 | CLASS_FieldVisitor = forName("org.objectweb.asm.FieldVisitor", "jdk.internal.org.objectweb.asm.FieldVisitor"); 37 | CLASS_MethodVisitor = forName("org.objectweb.asm.MethodVisitor", "jdk.internal.org.objectweb.asm.MethodVisitor"); 38 | CLASS_Label = forName("org.objectweb.asm.Label", "jdk.internal.org.objectweb.asm.Label"); 39 | CLASS_type = forName("org.objectweb.asm.Type", "jdk.internal.org.objectweb.asm.Type"); 40 | 41 | RStream.of(CLASS_Opcodes).fields().filter(true).filter(int.class).forEach(f -> opcodes.put(f.name(), f.get())); 42 | } 43 | 44 | private static Class forName(final String... names) { 45 | for (String name : names) { 46 | try { 47 | return Class.forName(name); 48 | } catch (Throwable ignored) { 49 | } 50 | } 51 | throw new IllegalStateException("Could not find any of the classes: " + String.join(", ", names)); 52 | } 53 | 54 | 55 | @Override 56 | @SneakyThrows 57 | public BuiltClass class_(int access, String name, String signature, String superName, String[] interfaces, Consumer consumer) { 58 | MethodHandle constructor = TRUSTED_LOOKUP.findConstructor(CLASS_ClassWriter, MethodType.methodType(void.class, int.class)); 59 | MethodHandle visit = TRUSTED_LOOKUP.findVirtual(CLASS_ClassWriter, "visit", MethodType.methodType(void.class, int.class, int.class, String.class, String.class, String.class, String[].class)); 60 | MethodHandle visitEnd = TRUSTED_LOOKUP.findVirtual(CLASS_ClassWriter, "visitEnd", MethodType.methodType(void.class)); 61 | 62 | Object classWriter = constructor.invoke(2 /*COMPUTE_FRAMES*/); 63 | visit.invoke(classWriter, this.opcode("V1_8"), access, name, signature, superName, interfaces); 64 | ASMClassBuilder builder = new ASMClassBuilder(classWriter, name); 65 | consumer.accept(builder); 66 | visitEnd.invoke(classWriter); 67 | 68 | return new ASMBuiltClass(classWriter, name); 69 | } 70 | 71 | @Override 72 | @SneakyThrows 73 | public BytecodeLabel label() { 74 | MethodHandle constructor = TRUSTED_LOOKUP.findConstructor(CLASS_Label, MethodType.methodType(void.class)); 75 | return new BytecodeLabel(constructor.invoke()); 76 | } 77 | 78 | @Override 79 | @SneakyThrows 80 | public BytecodeType type(String descriptor) { 81 | MethodHandle method = TRUSTED_LOOKUP.findStatic(CLASS_type, "getType", MethodType.methodType(CLASS_type, String.class)); 82 | return new BytecodeType(method.invoke(descriptor)); 83 | } 84 | 85 | @Override 86 | public int opcode(String name) { 87 | return opcodes.get(name.toUpperCase(Locale.ROOT)); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/bytecode/impl/asm/ASMBuiltClass.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.bytecode.impl.asm; 2 | 3 | import lombok.SneakyThrows; 4 | import net.lenni0451.reflect.bytecode.wrapper.BuiltClass; 5 | 6 | import java.lang.invoke.MethodHandle; 7 | import java.lang.invoke.MethodType; 8 | 9 | import static net.lenni0451.reflect.JavaBypass.TRUSTED_LOOKUP; 10 | import static net.lenni0451.reflect.bytecode.impl.asm.ASMBuilder.CLASS_ClassWriter; 11 | 12 | class ASMBuiltClass implements BuiltClass { 13 | 14 | private final Object classWriter; 15 | private final String name; 16 | 17 | public ASMBuiltClass(final Object classWriter, final String name) { 18 | this.classWriter = classWriter; 19 | this.name = name; 20 | } 21 | 22 | @Override 23 | public String getName() { 24 | return this.name; 25 | } 26 | 27 | @Override 28 | @SneakyThrows 29 | public byte[] toBytes() { 30 | MethodHandle toByteArray = TRUSTED_LOOKUP.findVirtual(CLASS_ClassWriter, "toByteArray", MethodType.methodType(byte[].class)); 31 | return (byte[]) toByteArray.invoke(this.classWriter); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/bytecode/impl/asm/ASMClassBuilder.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.bytecode.impl.asm; 2 | 3 | import lombok.SneakyThrows; 4 | import net.lenni0451.reflect.bytecode.builder.ClassBuilder; 5 | import net.lenni0451.reflect.bytecode.builder.FieldBuilder; 6 | import net.lenni0451.reflect.bytecode.builder.MethodBuilder; 7 | import org.jetbrains.annotations.ApiStatus; 8 | 9 | import java.lang.invoke.MethodHandle; 10 | import java.lang.invoke.MethodType; 11 | import java.util.function.Consumer; 12 | 13 | import static net.lenni0451.reflect.JavaBypass.TRUSTED_LOOKUP; 14 | import static net.lenni0451.reflect.bytecode.impl.asm.ASMBuilder.*; 15 | 16 | @ApiStatus.Internal 17 | class ASMClassBuilder implements ClassBuilder { 18 | 19 | private final Object classWriter; 20 | private final String name; 21 | 22 | public ASMClassBuilder(final Object classWriter, final String name) { 23 | this.classWriter = classWriter; 24 | this.name = name; 25 | } 26 | 27 | public Object getClassWriter() { 28 | return this.classWriter; 29 | } 30 | 31 | @Override 32 | public String getName() { 33 | return this.name; 34 | } 35 | 36 | @Override 37 | @SneakyThrows 38 | public void field(int access, String name, String descriptor, String signature, Object defaultValue, Consumer consumer) { 39 | MethodHandle visitField = TRUSTED_LOOKUP.findVirtual(CLASS_ClassWriter, "visitField", MethodType.methodType(CLASS_FieldVisitor, int.class, String.class, String.class, String.class, Object.class)); 40 | MethodHandle visitEnd = TRUSTED_LOOKUP.findVirtual(CLASS_FieldVisitor, "visitEnd", MethodType.methodType(void.class)); 41 | 42 | ASMFieldBuilder builder = new ASMFieldBuilder(visitField.invoke(this.classWriter, access, name, descriptor, signature, defaultValue)); 43 | consumer.accept(builder); 44 | visitEnd.invoke(builder.getFieldVisitor()); 45 | } 46 | 47 | @Override 48 | @SneakyThrows 49 | public void method(int access, String name, String descriptor, String signature, String[] exceptions, Consumer consumer) { 50 | MethodHandle visitMethod = TRUSTED_LOOKUP.findVirtual(CLASS_ClassWriter, "visitMethod", MethodType.methodType(CLASS_MethodVisitor, int.class, String.class, String.class, String.class, String[].class)); 51 | MethodHandle visitCode = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitCode", MethodType.methodType(void.class)); 52 | MethodHandle visitEnd = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitEnd", MethodType.methodType(void.class)); 53 | 54 | ASMMethodBuilder builder = new ASMMethodBuilder(visitMethod.invoke(this.classWriter, access, name, descriptor, signature, exceptions)); 55 | visitCode.invoke(builder.getMethodVisitor()); 56 | consumer.accept(builder); 57 | visitEnd.invoke(builder.getMethodVisitor()); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/bytecode/impl/asm/ASMFieldBuilder.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.bytecode.impl.asm; 2 | 3 | import net.lenni0451.reflect.bytecode.builder.FieldBuilder; 4 | import org.jetbrains.annotations.ApiStatus; 5 | 6 | @ApiStatus.Internal 7 | class ASMFieldBuilder implements FieldBuilder { 8 | 9 | private final Object fieldVisitor; 10 | 11 | public ASMFieldBuilder(final Object fieldVisitor) { 12 | this.fieldVisitor = fieldVisitor; 13 | } 14 | 15 | public Object getFieldVisitor() { 16 | return this.fieldVisitor; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/bytecode/impl/asm/ASMMethodBuilder.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.bytecode.impl.asm; 2 | 3 | import lombok.SneakyThrows; 4 | import net.lenni0451.reflect.bytecode.builder.MethodBuilder; 5 | import net.lenni0451.reflect.bytecode.wrapper.BytecodeLabel; 6 | import net.lenni0451.reflect.bytecode.wrapper.BytecodeType; 7 | import org.jetbrains.annotations.ApiStatus; 8 | 9 | import java.lang.invoke.MethodHandle; 10 | import java.lang.invoke.MethodType; 11 | 12 | import static net.lenni0451.reflect.JavaBypass.TRUSTED_LOOKUP; 13 | import static net.lenni0451.reflect.bytecode.impl.asm.ASMBuilder.CLASS_Label; 14 | import static net.lenni0451.reflect.bytecode.impl.asm.ASMBuilder.CLASS_MethodVisitor; 15 | 16 | @ApiStatus.Internal 17 | class ASMMethodBuilder implements MethodBuilder { 18 | 19 | private final Object methodVisitor; 20 | 21 | public ASMMethodBuilder(final Object methodVisitor) { 22 | this.methodVisitor = methodVisitor; 23 | } 24 | 25 | public Object getMethodVisitor() { 26 | return this.methodVisitor; 27 | } 28 | 29 | @Override 30 | @SneakyThrows 31 | public MethodBuilder insn(int opcode) { 32 | MethodHandle visitInsn = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitInsn", MethodType.methodType(void.class, int.class)); 33 | visitInsn.invoke(this.methodVisitor, opcode); 34 | return this; 35 | } 36 | 37 | @Override 38 | @SneakyThrows 39 | public MethodBuilder int_(int opcode, int value) { 40 | MethodHandle visitIntInsn = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitIntInsn", MethodType.methodType(void.class, int.class, int.class)); 41 | visitIntInsn.invoke(this.methodVisitor, opcode, value); 42 | return this; 43 | } 44 | 45 | @Override 46 | @SneakyThrows 47 | public MethodBuilder var(int opcode, int varIndex) { 48 | MethodHandle visitVarInsn = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitVarInsn", MethodType.methodType(void.class, int.class, int.class)); 49 | visitVarInsn.invoke(this.methodVisitor, opcode, varIndex); 50 | return this; 51 | } 52 | 53 | @Override 54 | @SneakyThrows 55 | public MethodBuilder type(int opcode, String type) { 56 | MethodHandle visitTypeInsn = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitTypeInsn", MethodType.methodType(void.class, int.class, String.class)); 57 | visitTypeInsn.invoke(this.methodVisitor, opcode, type); 58 | return this; 59 | } 60 | 61 | @Override 62 | @SneakyThrows 63 | public MethodBuilder field(int opcode, String owner, String name, String descriptor) { 64 | MethodHandle visitFieldInsn = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitFieldInsn", MethodType.methodType(void.class, int.class, String.class, String.class, String.class)); 65 | visitFieldInsn.invoke(this.methodVisitor, opcode, owner, name, descriptor); 66 | return this; 67 | } 68 | 69 | @Override 70 | @SneakyThrows 71 | public MethodBuilder method(int opcode, String owner, String name, String descriptor, boolean isInterface) { 72 | MethodHandle visitMethodInsn = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitMethodInsn", MethodType.methodType(void.class, int.class, String.class, String.class, String.class, boolean.class)); 73 | visitMethodInsn.invoke(this.methodVisitor, opcode, owner, name, descriptor, isInterface); 74 | return this; 75 | } 76 | 77 | @Override 78 | @SneakyThrows 79 | public MethodBuilder jump(int opcode, BytecodeLabel label) { 80 | MethodHandle visitJumpInsn = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitJumpInsn", MethodType.methodType(void.class, int.class, CLASS_Label)); 81 | visitJumpInsn.invoke(this.methodVisitor, opcode, label.getHandle()); 82 | return this; 83 | } 84 | 85 | @Override 86 | @SneakyThrows 87 | public MethodBuilder label(BytecodeLabel label) { 88 | MethodHandle visitLabel = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitLabel", MethodType.methodType(void.class, CLASS_Label)); 89 | visitLabel.invoke(this.methodVisitor, label.getHandle()); 90 | return this; 91 | } 92 | 93 | @Override 94 | @SneakyThrows 95 | public MethodBuilder ldc(Object value) { 96 | if (value instanceof BytecodeType) value = ((BytecodeType) value).getHandle(); 97 | MethodHandle visitLdcInsn = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitLdcInsn", MethodType.methodType(void.class, Object.class)); 98 | visitLdcInsn.invoke(this.methodVisitor, value); 99 | return this; 100 | } 101 | 102 | @Override 103 | @SneakyThrows 104 | public MethodBuilder iinc(int varIndex, int increment) { 105 | MethodHandle visitIincInsn = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitIincInsn", MethodType.methodType(void.class, int.class, int.class)); 106 | visitIincInsn.invoke(this.methodVisitor, varIndex, increment); 107 | return this; 108 | } 109 | 110 | @Override 111 | @SneakyThrows 112 | public MethodBuilder multiANewArray(String descriptor, int dimensions) { 113 | MethodHandle visitMultiANewArrayInsn = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitMultiANewArrayInsn", MethodType.methodType(void.class, String.class, int.class)); 114 | visitMultiANewArrayInsn.invoke(this.methodVisitor, descriptor, dimensions); 115 | return this; 116 | } 117 | 118 | @Override 119 | @SneakyThrows 120 | public MethodBuilder tryCatch(BytecodeLabel start, BytecodeLabel end, BytecodeLabel handler, String type) { 121 | MethodHandle visitTryCatchBlock = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitTryCatchBlock", MethodType.methodType(void.class, CLASS_Label, CLASS_Label, CLASS_Label, String.class)); 122 | visitTryCatchBlock.invoke(this.methodVisitor, start.getHandle(), end.getHandle(), handler.getHandle(), type); 123 | return this; 124 | } 125 | 126 | @Override 127 | @SneakyThrows 128 | public MethodBuilder maxs(int maxStack, int maxLocals) { 129 | MethodHandle visitMaxs = TRUSTED_LOOKUP.findVirtual(CLASS_MethodVisitor, "visitMaxs", MethodType.methodType(void.class, int.class, int.class)); 130 | visitMaxs.invoke(this.methodVisitor, maxStack, maxLocals); 131 | return this; 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/bytecode/wrapper/BuiltClass.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.bytecode.wrapper; 2 | 3 | import net.lenni0451.reflect.ClassLoaders; 4 | import org.jetbrains.annotations.ApiStatus; 5 | 6 | @ApiStatus.Experimental 7 | public interface BuiltClass { 8 | 9 | String getName(); 10 | 11 | byte[] toBytes(); 12 | 13 | default Class defineAnonymous(final Class parent) { 14 | return ClassLoaders.defineAnonymousClass(parent, this.toBytes()); 15 | } 16 | 17 | default Class defineMetafactory(final Class parent) { 18 | return ClassLoaders.defineAnonymousClass(parent, this.toBytes(), "NESTMATE", "STRONG"); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/bytecode/wrapper/BytecodeLabel.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.bytecode.wrapper; 2 | 3 | import org.jetbrains.annotations.ApiStatus; 4 | 5 | @ApiStatus.Experimental 6 | public class BytecodeLabel { 7 | 8 | private final Object handle; 9 | 10 | public BytecodeLabel(final Object handle) { 11 | this.handle = handle; 12 | } 13 | 14 | public Object getHandle() { 15 | return this.handle; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/bytecode/wrapper/BytecodeType.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.bytecode.wrapper; 2 | 3 | public class BytecodeType { 4 | 5 | private final Object handle; 6 | 7 | public BytecodeType(final Object handle) { 8 | this.handle = handle; 9 | } 10 | 11 | public Object getHandle() { 12 | return this.handle; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/exceptions/ConstructorInvocationException.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.exceptions; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.util.Arrays; 5 | 6 | public class ConstructorInvocationException extends RuntimeException { 7 | 8 | public ConstructorInvocationException(final Constructor constructor) { 9 | this(constructor.getDeclaringClass().getName(), Arrays.stream(constructor.getParameterTypes()).map(Class::getName).toArray(String[]::new)); 10 | } 11 | 12 | public ConstructorInvocationException(final String owner, final String... args) { 13 | super("Could not invoke constructor '" + String.join(", ", args) + "' in class '" + owner + "'"); 14 | } 15 | 16 | public ConstructorInvocationException cause(final Throwable cause) { 17 | this.initCause(cause); 18 | return this; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/exceptions/ConstructorNotFoundException.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.exceptions; 2 | 3 | import javax.annotation.Nullable; 4 | import java.util.Arrays; 5 | import java.util.Optional; 6 | 7 | public class ConstructorNotFoundException extends RuntimeException { 8 | 9 | public ConstructorNotFoundException(final String owner, @Nullable final Class... args) { 10 | this( 11 | owner, 12 | Optional 13 | .ofNullable(args) 14 | .map(Arrays::stream) 15 | .map(s -> s.map(Class::getSimpleName)) 16 | .map(s -> s.toArray(String[]::new)) 17 | .orElse(new String[0]) 18 | ); 19 | } 20 | 21 | public ConstructorNotFoundException(final String owner, final String... args) { 22 | super("Could not find constructor '(" + String.join(", ", args) + ")' in class '" + owner + "'"); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/exceptions/FieldNotFoundException.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.exceptions; 2 | 3 | public class FieldNotFoundException extends RuntimeException { 4 | 5 | public FieldNotFoundException(final String owner, final String... args) { 6 | super("Could not find field '" + String.join(", ", args) + "' in class '" + owner + "'"); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/exceptions/InvalidOOPSizeException.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.exceptions; 2 | 3 | public class InvalidOOPSizeException extends RuntimeException { 4 | 5 | public InvalidOOPSizeException() { 6 | super("OOP size is not 4 or 8"); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/exceptions/MethodInvocationException.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.exceptions; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.Arrays; 5 | 6 | public class MethodInvocationException extends RuntimeException { 7 | 8 | public MethodInvocationException(final Method method) { 9 | this(method.getDeclaringClass().getName(), method.getName(), Arrays.stream(method.getParameterTypes()).map(Class::getName).toArray(String[]::new)); 10 | } 11 | 12 | public MethodInvocationException(final String owner, final String name) { 13 | super("Could not invoke method '" + name + "' in class '" + owner + "'"); 14 | } 15 | 16 | public MethodInvocationException(final String owner, final String name, final String... args) { 17 | super("Could not invoke method '" + name + "(" + String.join(", ", args) + ")' in class '" + owner + "'"); 18 | } 19 | 20 | public MethodInvocationException cause(final Throwable cause) { 21 | this.initCause(cause); 22 | return this; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/exceptions/MethodNotFoundException.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.exceptions; 2 | 3 | import javax.annotation.Nullable; 4 | import java.util.Arrays; 5 | import java.util.Optional; 6 | 7 | public class MethodNotFoundException extends RuntimeException { 8 | 9 | public MethodNotFoundException(final String owner, final String name) { 10 | super("Could not find method '" + name + "' in class '" + owner + "'"); 11 | } 12 | 13 | public MethodNotFoundException(final String owner, @Nullable final String name, @Nullable final Class... args) { 14 | this( 15 | owner, 16 | name, 17 | Optional 18 | .ofNullable(args) 19 | .map(Arrays::stream) 20 | .map(s -> s.map(Class::getSimpleName)) 21 | .map(s -> s.toArray(String[]::new)) 22 | .orElse(new String[0]) 23 | ); 24 | } 25 | 26 | public MethodNotFoundException(final String owner, @Nullable final String name, final String... args) { 27 | super("Could not find method '" + (name == null ? "" : name) + "(" + String.join(", ", args) + ")' in class '" + owner + "'"); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/localcapture/LocalCapturer.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.localcapture; 2 | 3 | import java.util.function.Consumer; 4 | import java.util.function.Function; 5 | import java.util.stream.Stream; 6 | 7 | /** 8 | * This class is used to capture the local stack frames of the current thread.
9 | * The stack frames contain monitors, locals and stack slots of the current thread. 10 | */ 11 | public class LocalCapturer { 12 | 13 | /** 14 | * Iterate over all stack frames of the current thread. 15 | * 16 | * @param consumer The consumer to call for each stack frame 17 | */ 18 | public static void forEach(final Consumer consumer) { 19 | throw new UnsupportedOperationException("Not supported in Java 8"); 20 | } 21 | 22 | /** 23 | * Walk over the stack frames of the current thread and return the result of the function. 24 | * 25 | * @param function The function to call for each stack frame 26 | * @param The type of the result 27 | * @return The result of the function 28 | * @see StackWalker#walk(Function) 29 | */ 30 | public static T walk(final Function, T> function) { 31 | throw new UnsupportedOperationException("Not supported in Java 8"); 32 | } 33 | 34 | /** 35 | * Get all stack frames of the current thread. 36 | * 37 | * @return An array of all stack frames of the current thread 38 | */ 39 | public static LocalStackFrame[] getStackFrames() { 40 | throw new UnsupportedOperationException("Not supported in Java 8"); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/localcapture/LocalStackFrame.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.localcapture; 2 | 3 | import org.intellij.lang.annotations.MagicConstant; 4 | 5 | public interface LocalStackFrame { 6 | 7 | int MODE_INTERPRETED = 0x01; 8 | int MODE_COMPILED = 0x02; 9 | 10 | /** 11 | * Get the binary name of the declaring class of the method represented by this stack frame. 12 | * 13 | * @return The binary name of the declaring class 14 | */ 15 | String getClassName(); 16 | 17 | /** 18 | * Get the name of the method represented by this stack frame. 19 | * 20 | * @return The name of the method 21 | */ 22 | String getMethodName(); 23 | 24 | /** 25 | * Get the declaring class of the method represented by this stack frame. 26 | * 27 | * @return The declaring class 28 | */ 29 | Class getDeclaringClass(); 30 | 31 | /** 32 | * Get the index to the code array of the {@code Code} attribute containing the execution point represented by this stack frame.
33 | * The code array gives the actual bytes of Java Virtual Machine code that implement the method. 34 | * 35 | * @return The index to the code array 36 | */ 37 | int getByteCodeIndex(); 38 | 39 | /** 40 | * Get the name of the source file containing the execution point represented by this stack frame.
41 | * Generally, this corresponds to the {@code SourceFile} attribute of the relevant {@code class} file as defined by The Java Virtual Machine Specification.
42 | * In some systems, the name may refer to some source code unit other than a file, such as an entry in a source repository. 43 | * 44 | * @return The name of the source file 45 | */ 46 | String getFileName(); 47 | 48 | /** 49 | * Get the line number of the source line containing the execution point represented by this stack frame.
50 | * Generally, this is derived from the {@code LineNumberTable} attribute of the relevant {@code class} file as defined by The Java Virtual Machine Specification. 51 | * 52 | * @return The line number of the source line 53 | */ 54 | int getLineNumber(); 55 | 56 | /** 57 | * Get if the method containing the execution point represented by this stack frame is a native method. 58 | * 59 | * @return {@code true} if the method is native, {@code false} otherwise 60 | */ 61 | boolean isNativeMethod(); 62 | 63 | /** 64 | * Convert the stack frame to a {@link StackTraceElement} object. 65 | * 66 | * @return The stack trace element 67 | */ 68 | StackTraceElement toStackTraceElement(); 69 | 70 | /** 71 | * Get the mode of the stack frame.
72 | * This is either {@link #MODE_INTERPRETED} or {@link #MODE_COMPILED} (at the time of writing this). 73 | * 74 | * @return The mode of the stack frame 75 | */ 76 | @MagicConstant(intValues = {MODE_INTERPRETED, MODE_COMPILED}) 77 | int getMode(); 78 | 79 | /** 80 | * Get the monitors of the stack frame.
81 | * This is a list of all objects that are currently locked by the thread represented by this stack frame. 82 | * 83 | * @return The monitors of the stack frame 84 | */ 85 | Object[] getMonitors(); 86 | 87 | /** 88 | * Get the locals of the stack frame.
89 | * This is a list of all local variables in the method represented by this stack frame.
90 | * The locals are represented as an array of objects, where each object is either a reference to an object or a primitive value wrapped in a {@link PrimitiveValue} object. 91 | * 92 | * @return The locals of the stack frame 93 | */ 94 | Object[] getLocals(); 95 | 96 | /** 97 | * Get the stack of the stack frame.
98 | * This is a list of all values on the operand stack of the method represented by this stack frame.
99 | * The stack is represented as an array of objects, where each object is either a reference to an object or a primitive value wrapped in a {@link PrimitiveValue} object. 100 | * 101 | * @return The stack of the stack frame 102 | */ 103 | Object[] getStack(); 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/localcapture/PrimitiveValue.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.localcapture; 2 | 3 | /** 4 | * Represents a primitive value in the JVM.
5 | * When getting stack frame information, the JVM returns all primitives wrapped without type information.
6 | * The primitive wrapped in this class may be a {@code boolean}, {@code byte}, {@code short}, {@code char}, {@code int}, {@code long}, {@code float} or {@code double}. 7 | */ 8 | public class PrimitiveValue { 9 | 10 | private final int size; 11 | private final long value; 12 | 13 | public PrimitiveValue(final int size, final long value) { 14 | this.size = size; 15 | this.value = value; 16 | } 17 | 18 | /** 19 | * @return The size of the value in bytes 20 | */ 21 | public int getSize() { 22 | return this.size; 23 | } 24 | 25 | /** 26 | * @return The value of the primitive 27 | */ 28 | public long getValue() { 29 | return this.value; 30 | } 31 | 32 | /** 33 | * @return The value as a boolean 34 | */ 35 | public boolean asBoolean() { 36 | return this.value != 0; 37 | } 38 | 39 | /** 40 | * @return The value as a byte 41 | */ 42 | public byte asByte() { 43 | return (byte) this.value; 44 | } 45 | 46 | /** 47 | * @return The value as a short 48 | */ 49 | public short asShort() { 50 | return (short) this.value; 51 | } 52 | 53 | /** 54 | * @return The value as a char 55 | */ 56 | public char asChar() { 57 | return (char) this.value; 58 | } 59 | 60 | /** 61 | * @return The value as an int 62 | */ 63 | public int asInt() { 64 | return (int) this.value; 65 | } 66 | 67 | /** 68 | * @return The value as a long 69 | */ 70 | public long asLong() { 71 | return this.value; 72 | } 73 | 74 | /** 75 | * @return The value as a float 76 | */ 77 | public float asFloat() { 78 | return Float.intBitsToFloat((int) this.value); 79 | } 80 | 81 | /** 82 | * @return The value as a double 83 | */ 84 | public double asDouble() { 85 | return Double.longBitsToDouble(this.value); 86 | } 87 | 88 | @Override 89 | public String toString() { 90 | return String.valueOf(this.value); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/proxy/InvocationHandler.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.proxy; 2 | 3 | import net.lenni0451.reflect.proxy.impl.ProxyMethod; 4 | 5 | import java.lang.reflect.Modifier; 6 | 7 | /** 8 | * The handler which is called when a method of a proxy class is invoked. 9 | */ 10 | public interface InvocationHandler { 11 | 12 | /** 13 | * Get a new invocation handler which forwards all method calls to the super class.
14 | * If the method is abstract it will be cancelled. 15 | * 16 | * @return The new invocation handler 17 | */ 18 | static InvocationHandler forwarding() { 19 | return (thiz, proxyMethod, args) -> { 20 | if (Modifier.isAbstract(proxyMethod.getInvokedMethod().getModifiers())) { 21 | return proxyMethod.cancel(); 22 | } else { 23 | return proxyMethod.invokeSuper(args); 24 | } 25 | }; 26 | } 27 | 28 | /** 29 | * Get a new invocation handler which cancels all method calls. 30 | * 31 | * @return The new invocation handler 32 | */ 33 | static InvocationHandler cancelling() { 34 | return (thiz, proxyMethod, args) -> proxyMethod.cancel(); 35 | } 36 | 37 | 38 | /** 39 | * Handle a proxy method invocation.
40 | * The method can be forwarded to the super class, another instance or cancelled. 41 | * 42 | * @param thiz The instance of the proxy 43 | * @param proxyMethod The proxy method which was invoked 44 | * @param args The arguments of the method call 45 | * @return The result of the method call 46 | */ 47 | Object invoke(final Object thiz, final ProxyMethod proxyMethod, final Object... args); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/proxy/ProxyClass.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.proxy; 2 | 3 | import net.lenni0451.reflect.Constructors; 4 | import net.lenni0451.reflect.Objects; 5 | import net.lenni0451.reflect.exceptions.ConstructorNotFoundException; 6 | import net.lenni0451.reflect.proxy.impl.Proxy; 7 | 8 | import java.lang.reflect.Constructor; 9 | 10 | /** 11 | * Represents a built proxy class.
12 | * This class is used to create an instance of the proxy class. 13 | */ 14 | public class ProxyClass { 15 | 16 | private final Class proxyClass; 17 | private InvocationHandler invocationHandler; 18 | 19 | public ProxyClass(final Class proxyClass, final InvocationHandler invocationHandler) { 20 | this.proxyClass = proxyClass; 21 | this.invocationHandler = invocationHandler; 22 | } 23 | 24 | /** 25 | * @return The built proxy class 26 | */ 27 | public Class getProxyClass() { 28 | return this.proxyClass; 29 | } 30 | 31 | /** 32 | * @return The invocation handler 33 | */ 34 | public InvocationHandler getInvocationHandler() { 35 | return this.invocationHandler; 36 | } 37 | 38 | /** 39 | * Sets the invocation handler for the proxy class. 40 | * 41 | * @param invocationHandler The invocation handler 42 | */ 43 | public void setInvocationHandler(final InvocationHandler invocationHandler) { 44 | this.invocationHandler = invocationHandler; 45 | } 46 | 47 | /** 48 | * Allocate a new instance of the proxy class without calling a constructor.
49 | * The invocation handler will be set automatically. 50 | * 51 | * @param The type of the proxy class 52 | * @return The new instance of the proxy class 53 | */ 54 | public T allocateInstance() { 55 | Object instance = Objects.allocate(this.proxyClass); 56 | ((Proxy) instance).setInvocationHandler(this.invocationHandler); 57 | return (T) instance; 58 | } 59 | 60 | /** 61 | * Instantiate a new instance of the proxy class with the given constructor parameters and arguments.
62 | * The invocation handler will be set automatically. 63 | * 64 | * @param constructorParameters The parameters of the constructor 65 | * @param constructorArguments The arguments for the constructor 66 | * @param The type of the proxy class 67 | * @return The new instance of the proxy class 68 | */ 69 | public T instantiate(final Class[] constructorParameters, final Object[] constructorArguments) { 70 | Constructor constructor = Constructors.getDeclaredConstructor(this.proxyClass, constructorParameters); 71 | if (constructor == null) throw new ConstructorNotFoundException("Proxy", constructorParameters); 72 | Object instance = Constructors.invoke(constructor, constructorArguments); 73 | ((Proxy) instance).setInvocationHandler(this.invocationHandler); 74 | return (T) instance; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/proxy/ProxyClassDefiner.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.proxy; 2 | 3 | import lombok.SneakyThrows; 4 | import net.lenni0451.reflect.ClassLoaders; 5 | import net.lenni0451.reflect.bytecode.wrapper.BuiltClass; 6 | 7 | import javax.annotation.Nullable; 8 | import java.io.File; 9 | import java.nio.file.Files; 10 | 11 | import static net.lenni0451.reflect.bytecode.BytecodeUtils.dot; 12 | import static net.lenni0451.reflect.bytecode.BytecodeUtils.slash; 13 | 14 | /** 15 | * A class definer for proxy classes. 16 | */ 17 | public interface ProxyClassDefiner { 18 | 19 | /** 20 | * Create a new class loader based class definer. 21 | * 22 | * @param classLoader The class loader 23 | * @return The class definer 24 | */ 25 | static ProxyClassDefiner loader(final ClassLoader classLoader) { 26 | return (builtClass, superClass, interfaces) -> ClassLoaders.defineClass(classLoader, dot(builtClass.getName()), builtClass.toBytes()); 27 | } 28 | 29 | /** 30 | * Create a new class definer that dumps the class files to a directory.
31 | * The defining of the class will be delegated to the given class definer. 32 | * 33 | * @param classDefiner The class definer 34 | * @param outputDir The output directory 35 | * @return The class definer 36 | */ 37 | static ProxyClassDefiner dumping(final ProxyClassDefiner classDefiner, final File outputDir) { 38 | return new ProxyClassDefiner() { 39 | @Override 40 | @SneakyThrows 41 | public Class defineProxyClass(BuiltClass builtClass, Class superClass, Class[] interfaces) { 42 | File file = new File(outputDir, slash(builtClass.getName()) + ".class"); 43 | file.getParentFile().mkdirs(); 44 | Files.write(file.toPath(), builtClass.toBytes()); 45 | return classDefiner.defineProxyClass(builtClass, superClass, interfaces); 46 | } 47 | }; 48 | } 49 | 50 | 51 | /** 52 | * Define a built proxy class. 53 | * 54 | * @param builtClass The built class 55 | * @param superClass The super class 56 | * @param interfaces The interfaces 57 | * @return The defined class 58 | */ 59 | Class defineProxyClass(final BuiltClass builtClass, @Nullable final Class superClass, @Nullable final Class[] interfaces); 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/proxy/impl/Proxy.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.proxy.impl; 2 | 3 | import net.lenni0451.reflect.proxy.InvocationHandler; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | /** 8 | * Represents a proxy class. 9 | */ 10 | public interface Proxy { 11 | 12 | /** 13 | * Set the invocation handler for handling the method calls. 14 | * 15 | * @param invocationHandler The invocation handler 16 | */ 17 | void setInvocationHandler(@Nonnull final InvocationHandler invocationHandler); 18 | 19 | /** 20 | * @return The current invocation handler 21 | */ 22 | InvocationHandler getInvocationHandler(); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/proxy/impl/ProxyMethod.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.proxy.impl; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | /** 6 | * Represents a proxied method call.
7 | * The original call can be forwarded to the super class, forwarded to another instance or cancelled. 8 | */ 9 | public interface ProxyMethod { 10 | 11 | /** 12 | * @return The invoked method 13 | */ 14 | Method getInvokedMethod(); 15 | 16 | /** 17 | * Invoke the proxied method with the given instance and arguments. 18 | * 19 | * @param instance The instance to invoke the method on 20 | * @param args The arguments to pass to the method 21 | * @return The result of the method call 22 | */ 23 | Object invokeWith(final Object instance, final Object... args); 24 | 25 | /** 26 | * Invoke the proxied method on the proxy super class with the given arguments.
27 | * This will not work if the super method is abstract. 28 | * 29 | * @param args The arguments to pass to the method 30 | * @return The result of the method call 31 | */ 32 | Object invokeSuper(final Object... args); 33 | 34 | /** 35 | * Get the default return value of the method.
36 | * This can be used to cancel the method call by returning the default. 37 | * 38 | * @return The default return value 39 | */ 40 | Object cancel(); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/proxy/impl/ProxyRuntime.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.proxy.impl; 2 | 3 | import org.jetbrains.annotations.ApiStatus; 4 | 5 | import java.lang.invoke.MethodHandle; 6 | import java.lang.invoke.MethodType; 7 | import java.lang.reflect.Constructor; 8 | import java.lang.reflect.InvocationTargetException; 9 | import java.lang.reflect.Method; 10 | 11 | import static net.lenni0451.reflect.JavaBypass.TRUSTED_LOOKUP; 12 | 13 | /** 14 | * Util methods invoked by the proxy classes at runtime. 15 | */ 16 | @ApiStatus.Internal 17 | public class ProxyRuntime { 18 | 19 | /** 20 | * Invoked in the static initializer of the proxy method class.
21 | * This will get the required method handles for the method. 22 | * 23 | * @param owner The owner of the method 24 | * @param name The name of the method 25 | * @param parameters The parameter types of the method 26 | * @param returnType The return type of the method 27 | * @return The method handles for the method 28 | * @throws NoSuchMethodException If the method does not exist 29 | * @throws IllegalAccessException If the method is not accessible 30 | */ 31 | public static MethodHandle[] getMethodHandles(final Class owner, final Class superOwner, final String name, final Class[] parameters, final Class returnType) throws NoSuchMethodException, IllegalAccessException { 32 | MethodHandle[] methodHandles = new MethodHandle[2]; 33 | methodHandles[0] = TRUSTED_LOOKUP.findVirtual(owner, name, MethodType.methodType(returnType, parameters)); 34 | try { 35 | methodHandles[1] = TRUSTED_LOOKUP.findSpecial(superOwner, name, MethodType.methodType(returnType, parameters), superOwner); 36 | } catch (Throwable ignored) { 37 | } 38 | return methodHandles; 39 | } 40 | 41 | /** 42 | * Invoked in the proxy methods. 43 | * 44 | * @param clazz The class of the proxy method to instantiate 45 | * @param instance The instance of the proxy 46 | * @param method The method to proxy 47 | * @return The instantiated proxy method 48 | * @throws NoSuchMethodException If the constructor does not exist 49 | * @throws InvocationTargetException If the constructor throws an exception 50 | * @throws InstantiationException If the class is abstract 51 | * @throws IllegalAccessException If the constructor is not accessible 52 | */ 53 | public static ProxyMethod instantiateProxyMethod(final Class clazz, final Object instance, final Method method) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { 54 | Constructor constructor = clazz.getDeclaredConstructor(instance.getClass(), Method.class); 55 | constructor.setAccessible(true); 56 | return (ProxyMethod) constructor.newInstance(instance, method); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/proxy/internal/ProxyUtils.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.proxy.internal; 2 | 3 | import net.lenni0451.reflect.Methods; 4 | import net.lenni0451.reflect.proxy.impl.Proxy; 5 | import org.jetbrains.annotations.ApiStatus; 6 | 7 | import java.lang.reflect.Constructor; 8 | import java.lang.reflect.Method; 9 | import java.lang.reflect.Modifier; 10 | import java.util.*; 11 | import java.util.function.Function; 12 | import java.util.function.Predicate; 13 | import java.util.stream.Collectors; 14 | 15 | import static net.lenni0451.reflect.bytecode.BytecodeUtils.desc; 16 | 17 | /** 18 | * Utils for creating proxy classes. 19 | */ 20 | @ApiStatus.Internal 21 | public class ProxyUtils { 22 | 23 | public static void verifySuperClass(final Class clazz) { 24 | if (!Modifier.isPublic(clazz.getModifiers())) throw new IllegalArgumentException("The super class must be public"); 25 | if (clazz.isInterface()) throw new IllegalArgumentException("The super class must be a class"); 26 | if (Modifier.isFinal(clazz.getModifiers())) throw new IllegalArgumentException("The super class must not be final"); 27 | // if (getPublicConstructors(clazz).length == 0) throw new IllegalArgumentException("The super class must have at least one public constructor"); 28 | } 29 | 30 | public static void verifyInterface(final Class clazz) { 31 | if (clazz == Proxy.class) throw new IllegalArgumentException("The 'Proxy' interface is not allowed as interface"); 32 | if (!Modifier.isPublic(clazz.getModifiers())) throw new IllegalArgumentException("The interface must be public"); 33 | if (!clazz.isInterface()) throw new IllegalArgumentException("The interface must be an interface"); 34 | } 35 | 36 | public static Constructor[] getPublicConstructors(final Class clazz) { 37 | List> constructors = new ArrayList<>(); 38 | for (Constructor constructor : clazz.getConstructors()) { 39 | if (Modifier.isPrivate(constructor.getModifiers())) continue; 40 | constructors.add(constructor); 41 | } 42 | return constructors.toArray(new Constructor[0]); 43 | } 44 | 45 | public static Method[] getOverridableMethod(final Class superClass, final Class[] interfaces, final Predicate filter) { 46 | Map methods = new HashMap<>(); 47 | if (superClass != null) getOverridableMethod(superClass, methods); 48 | if (interfaces != null) { 49 | for (Class inter : interfaces) getOverridableMethod(inter, methods); 50 | } 51 | return methods.values().stream().filter(filter).toArray(Method[]::new); 52 | } 53 | 54 | public static void getOverridableMethod(final Class clazz, final Map methods) { 55 | if (clazz == Proxy.class) return; //Ignore the Proxy interface 56 | for (Method method : Methods.getDeclaredMethods(clazz)) { 57 | if (Modifier.isPrivate(method.getModifiers())) continue; 58 | if (Modifier.isStatic(method.getModifiers())) continue; 59 | if (Modifier.isFinal(method.getModifiers())) continue; 60 | if (Modifier.isNative(method.getModifiers())) continue; 61 | methods.putIfAbsent(method.getName() + desc(method), method); 62 | } 63 | if (clazz.getSuperclass() != null) getOverridableMethod(clazz.getSuperclass(), methods); 64 | for (Class inter : clazz.getInterfaces()) getOverridableMethod(inter, methods); 65 | } 66 | 67 | public static Method[] mapMethods(final Method[] methods, final Function methodMapper) { 68 | Method[] originalMethods = new Method[methods.length]; 69 | for (int i = 0; i < methods.length; i++) { 70 | Method method = methods[i]; 71 | Method mappedMethod = methodMapper.apply(method); 72 | if (mappedMethod == null || method.equals(mappedMethod)) continue; 73 | 74 | if (!method.getName().equals(mappedMethod.getName())) { 75 | throw new IllegalArgumentException(String.format("Method '%s' has mismatching name (%1$s != %s)", method.getName(), mappedMethod.getName())); 76 | } 77 | if (!Arrays.equals(method.getParameterTypes(), mappedMethod.getParameterTypes())) { 78 | String originalParameters = Arrays.stream(method.getParameterTypes()).map(Class::getSimpleName).collect(Collectors.joining(", ")); 79 | String mappedParameters = Arrays.stream(mappedMethod.getParameterTypes()).map(Class::getSimpleName).collect(Collectors.joining(", ")); 80 | throw new IllegalArgumentException(String.format("Method '%s' has mismatching parameter types (%s != %s)", mappedMethod.getName(), originalParameters, mappedParameters)); 81 | } 82 | if (method.getReturnType() != mappedMethod.getReturnType()) { 83 | throw new IllegalArgumentException(String.format("Method '%s' has mismatching return type (%s != %s)", mappedMethod.getName(), method.getReturnType().getSimpleName(), mappedMethod.getReturnType().getSimpleName())); 84 | } 85 | if (!mappedMethod.getDeclaringClass().isAssignableFrom(method.getDeclaringClass())) { 86 | throw new IllegalArgumentException(String.format("Declaring class of method '%s' is not assignable from original declaring class (%s != %s)", mappedMethod.getName(), mappedMethod.getDeclaringClass().getName(), method.getDeclaringClass().getName())); 87 | } 88 | methods[i] = mappedMethod; 89 | originalMethods[i] = method; 90 | } 91 | return originalMethods; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/stream/RStream.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.stream; 2 | 3 | import net.lenni0451.reflect.Classes; 4 | import net.lenni0451.reflect.stream.constructor.ConstructorStream; 5 | import net.lenni0451.reflect.stream.field.FieldStream; 6 | import net.lenni0451.reflect.stream.method.MethodStream; 7 | 8 | import javax.annotation.Nonnull; 9 | import javax.annotation.Nullable; 10 | 11 | /** 12 | * Get a stream of all fields, methods or constructors of a class. 13 | */ 14 | public class RStream { 15 | 16 | /** 17 | * Get a stream of the given class.
18 | * When getting a virtual field or invoking a virtual method you have to provide an instance of the class. 19 | * 20 | * @param clazz The class to get the stream of 21 | * @return The stream instance of the given class 22 | */ 23 | public static RStream of(@Nonnull final Class clazz) { 24 | return new RStream(clazz, null); 25 | } 26 | 27 | /** 28 | * Get a stream of the given class with the given instance.
29 | * When getting a virtual field or invoking a virtual method you don't have to provide an instance of the class. 30 | * 31 | * @param clazz The class to get the stream of 32 | * @param instance The instance of the given class 33 | * @return The stream instance of the given class and instance 34 | */ 35 | public static RStream of(@Nonnull final Class clazz, @Nullable final Object instance) { 36 | return new RStream(clazz, instance); 37 | } 38 | 39 | /** 40 | * Get a stream of the given class by name.
41 | * When getting a virtual field or invoking a virtual method you have to provide an instance of the class. 42 | * 43 | * @param className The name of the class to get the stream of 44 | * @return The stream instance of the given class by name 45 | * @throws ClassNotFoundException If the class with the given name doesn't exist 46 | */ 47 | public static RStream of(@Nonnull final String className) { 48 | return of(className, null); 49 | } 50 | 51 | /** 52 | * Get the stream of the given class by name with the given instance.
53 | * When getting a virtual field or invoking a virtual method you don't have to provide an instance of the class. 54 | * 55 | * @param className The name of the class to get the stream of 56 | * @param instance The instance of the given class 57 | * @return The stream instance of the given class by name and instance 58 | * @throws ClassNotFoundException If the class with the given name doesn't exist 59 | */ 60 | public static RStream of(@Nonnull final String className, @Nullable final Object instance) { 61 | return of(Classes.forName(className), instance); 62 | } 63 | 64 | /** 65 | * Get the stream of the given instance.
66 | * The class of the instance will be used to get the stream of.
67 | * When getting a virtual field or invoking a virtual method you don't have to provide an instance of the class. 68 | * 69 | * @param instance The instance to get the stream of 70 | * @return The stream instance of the given instance 71 | */ 72 | public static RStream of(@Nonnull final Object instance) { 73 | return new RStream(instance.getClass(), instance); 74 | } 75 | 76 | 77 | private final Class clazz; 78 | private final Object instance; 79 | private boolean withSuper; 80 | 81 | private RStream(@Nonnull final Class clazz, @Nullable final Object instance) { 82 | this.clazz = clazz; 83 | this.instance = instance; 84 | 85 | Classes.ensureInitialized(this.clazz); 86 | } 87 | 88 | /** 89 | * @return The class of this stream. 90 | */ 91 | public Class clazz() { 92 | return this.clazz; 93 | } 94 | 95 | /** 96 | * @return The instance of this stream. 97 | */ 98 | @Nullable 99 | public Object instance() { 100 | return this.instance; 101 | } 102 | 103 | /** 104 | * Include fields and methods from super classes.
105 | * This will reset the field and method stream. 106 | * 107 | * @return This stream instance 108 | */ 109 | public RStream withSuper() { 110 | if (this.withSuper) return this; 111 | this.withSuper = true; 112 | return this; 113 | } 114 | 115 | 116 | /** 117 | * @return A stream with all fields of this class. 118 | */ 119 | public FieldStream fields() { 120 | return new FieldStream(this, this.withSuper); 121 | } 122 | 123 | /** 124 | * @return A stream with all constructors of this class. 125 | */ 126 | public ConstructorStream constructors() { 127 | return new ConstructorStream(this); 128 | } 129 | 130 | /** 131 | * @return A stream with all methods of this class. 132 | */ 133 | public MethodStream methods() { 134 | return new MethodStream(this, this.withSuper); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/stream/constructor/ConstructorStream.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.stream.constructor; 2 | 3 | import net.lenni0451.reflect.Constructors; 4 | import net.lenni0451.reflect.exceptions.ConstructorNotFoundException; 5 | import net.lenni0451.reflect.stream.RStream; 6 | 7 | import javax.annotation.Nullable; 8 | import java.lang.reflect.Constructor; 9 | import java.util.*; 10 | import java.util.function.Consumer; 11 | import java.util.function.Function; 12 | import java.util.function.Predicate; 13 | import java.util.stream.Stream; 14 | 15 | /** 16 | * A stream of all constructors of a class. 17 | */ 18 | public class ConstructorStream { 19 | 20 | private final RStream parent; 21 | private final List constructors; 22 | 23 | public ConstructorStream(final RStream parent) { 24 | this.parent = parent; 25 | this.constructors = new ArrayList<>(); 26 | 27 | for (Constructor constructor : Constructors.getDeclaredConstructors(parent.clazz())) this.constructors.add(new ConstructorWrapper(this, constructor)); 28 | } 29 | 30 | private ConstructorStream(final RStream parent, final List constructors) { 31 | this.parent = parent; 32 | this.constructors = constructors; 33 | } 34 | 35 | /** 36 | * @return The parent stream 37 | */ 38 | public RStream parent() { 39 | return this.parent; 40 | } 41 | 42 | /** 43 | * @return The amount of methods in this stream 44 | */ 45 | public int size() { 46 | return this.constructors.size(); 47 | } 48 | 49 | 50 | /** 51 | * Get a constructor by the given parameter types. 52 | * 53 | * @param parameterTypes The parameter types of the constructor 54 | * @return The constructor wrapper 55 | */ 56 | public Optional opt(@Nullable final Class... parameterTypes) { 57 | if (parameterTypes == null || parameterTypes.length == 0) { 58 | for (ConstructorWrapper constructor : this.constructors) { 59 | if (constructor.parameterTypes().length == 0) return Optional.of(constructor); 60 | } 61 | } else { 62 | for (ConstructorWrapper constructor : this.constructors) { 63 | if (Arrays.equals(constructor.parameterTypes(), parameterTypes)) return Optional.of(constructor); 64 | } 65 | } 66 | return Optional.empty(); 67 | } 68 | 69 | /** 70 | * Get a constructor by the given index.
71 | * The index is the position of the constructor in the stream. 72 | * 73 | * @param index The index of the constructor 74 | * @return The constructor wrapper 75 | */ 76 | public Optional opt(final int index) { 77 | if (index < 0 || index > this.constructors.size()) return Optional.empty(); 78 | return Optional.of(this.constructors.get(index)); 79 | } 80 | 81 | /** 82 | * Get a constructor by the given parameter types. 83 | * 84 | * @param parameterTypes The parameter types of the constructor 85 | * @return The constructor wrapper 86 | * @throws ConstructorNotFoundException If the constructor doesn't exist 87 | */ 88 | public ConstructorWrapper by(@Nullable final Class... parameterTypes) { 89 | return this.opt(parameterTypes).orElseThrow(() -> new ConstructorNotFoundException(this.parent.clazz().getName(), parameterTypes)); 90 | } 91 | 92 | /** 93 | * Get a constructor by the given index.
94 | * The index is the position of the constructor in the stream. 95 | * 96 | * @param index The index of the constructor 97 | * @return The constructor wrapper 98 | * @throws ConstructorNotFoundException If the constructor doesn't exist 99 | */ 100 | public ConstructorWrapper by(final int index) { 101 | return this.opt(index).orElseThrow(() -> new ConstructorNotFoundException(this.parent.clazz().getName(), String.valueOf(index))); 102 | } 103 | 104 | 105 | /** 106 | * Filter the methods with the given filter.
107 | * The current stream will be modified. 108 | * 109 | * @param filter The filter 110 | * @return This stream 111 | */ 112 | public ConstructorStream filter(final Predicate filter) { 113 | this.constructors.removeIf(filter.negate()); 114 | return this; 115 | } 116 | 117 | /** 118 | * Filter the methods by the given parameter types.
119 | * The current stream will be modified. 120 | * 121 | * @param parameterTypes The parameter types of the constructor 122 | * @return This stream 123 | */ 124 | public ConstructorStream filterParameters(@Nullable final Class... parameterTypes) { 125 | if (parameterTypes == null || parameterTypes.length == 0) { 126 | return this.filter(constructor -> constructor.parameterCount() == 0); 127 | } else { 128 | return this.filter(constructor -> Arrays.equals(constructor.parameterTypes(), parameterTypes)); 129 | } 130 | } 131 | 132 | /** 133 | * Filter the constructors by whether they are static.
134 | * The current stream will be modified. 135 | * 136 | * @param isStatic Whether the constructor should be static 137 | * @return This stream 138 | */ 139 | public ConstructorStream filterStatic(final boolean isStatic) { 140 | return this.filter(constructor -> constructor.modifier().isStatic() == isStatic); 141 | } 142 | 143 | /** 144 | * Filter the constructors by whether they have the given annotation.
145 | * The current stream will be modified. 146 | * 147 | * @param annotation The annotation 148 | * @return This stream 149 | */ 150 | public ConstructorStream filterAnnotation(final Class annotation) { 151 | return this.filter(constructor -> constructor.annotations().anyMatch(a -> a.annotationType().equals(annotation))); 152 | } 153 | 154 | 155 | /** 156 | * @return An iterator of method wrappers 157 | */ 158 | public Iterator iterator() { 159 | return this.constructors.iterator(); 160 | } 161 | 162 | /** 163 | * Map the constructors to a new stream.
164 | * This is the same as calling {@link #jstream()} and then {@link Stream#map(Function)}. 165 | * 166 | * @param mapFunction The map function 167 | * @param The type of the new stream 168 | * @return The new stream 169 | */ 170 | public Stream map(final Function mapFunction) { 171 | return this.jstream().map(mapFunction); 172 | } 173 | 174 | /** 175 | * @return A stream of method wrappers 176 | */ 177 | public Stream jstream() { 178 | return this.constructors.stream(); 179 | } 180 | 181 | /** 182 | * Loop through all constructors in this stream. 183 | * 184 | * @param consumer The consumer 185 | * @return This stream 186 | */ 187 | public ConstructorStream forEach(final Consumer consumer) { 188 | this.constructors.forEach(consumer); 189 | return this; 190 | } 191 | 192 | /** 193 | * @return A copy of this stream 194 | */ 195 | public ConstructorStream copy() { 196 | return new ConstructorStream(this.parent, new ArrayList<>(this.constructors)); 197 | } 198 | 199 | 200 | @Deprecated 201 | public ConstructorStream filter(@Nullable final Class... parameterTypes) { 202 | return this.filterParameters(parameterTypes); 203 | } 204 | 205 | @Deprecated 206 | public ConstructorStream filter(final boolean isStatic) { 207 | return this.filterStatic(isStatic); 208 | } 209 | 210 | } 211 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/stream/constructor/ConstructorWrapper.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.stream.constructor; 2 | 3 | import net.lenni0451.reflect.Constructors; 4 | import net.lenni0451.reflect.stream.RStream; 5 | import net.lenni0451.reflect.stream.general.ModifierWrapper; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.lang.reflect.Constructor; 9 | import java.util.Arrays; 10 | import java.util.stream.Stream; 11 | 12 | /** 13 | * A wrapper of the {@link Constructor} class for easy access to all required methods. 14 | */ 15 | public class ConstructorWrapper { 16 | 17 | private final ConstructorStream parent; 18 | private final Constructor constructor; 19 | private final ModifierWrapper modifier; 20 | 21 | public ConstructorWrapper(final ConstructorStream parent, final Constructor constructor) { 22 | this.parent = parent; 23 | this.constructor = constructor; 24 | this.modifier = new ModifierWrapper(constructor.getModifiers()); 25 | } 26 | 27 | /** 28 | * @return The parent constructor stream 29 | */ 30 | public ConstructorStream parent() { 31 | return this.parent; 32 | } 33 | 34 | /** 35 | * @return The underlying constructor 36 | */ 37 | public Constructor raw() { 38 | return this.constructor; 39 | } 40 | 41 | /** 42 | * @return The parameter types of the constructor 43 | */ 44 | public Class[] parameterTypes() { 45 | return this.constructor.getParameterTypes(); 46 | } 47 | 48 | /** 49 | * @return The amount of parameters of the constructor 50 | */ 51 | public int parameterCount() { 52 | return this.constructor.getParameterCount(); 53 | } 54 | 55 | /** 56 | * @return The owner (declaring) class of the constructor 57 | */ 58 | public Class owner() { 59 | return this.constructor.getDeclaringClass(); 60 | } 61 | 62 | /** 63 | * @return The {@link ModifierWrapper} of the constructor 64 | */ 65 | public ModifierWrapper modifier() { 66 | return this.modifier; 67 | } 68 | 69 | /** 70 | * @return A stream of all annotations of the constructor 71 | */ 72 | public Stream annotations() { 73 | return Arrays.stream(this.constructor.getDeclaredAnnotations()); 74 | } 75 | 76 | 77 | /** 78 | * Create a new instance of the owner class with the given arguments. 79 | * 80 | * @param args The arguments to pass to the constructor 81 | * @param The type of the owner class 82 | * @return The new instance 83 | */ 84 | public T newInstance(final Object... args) { 85 | return (T) Constructors.invoke(this.constructor, args); 86 | } 87 | 88 | /** 89 | * Create a new instance of the owner class with the given arguments and wrap it in a new {@link RStream}. 90 | * 91 | * @param args The arguments to pass to the constructor 92 | * @return The new instance 93 | */ 94 | public RStream streamInstance(final Object... args) { 95 | return RStream.of(this.newInstance(args)); 96 | } 97 | 98 | /** 99 | * Create a new instance of the owner class with the given arguments and wrap it in a new {@link RStream}. 100 | * 101 | * @param clazz The class used for the stream 102 | * @param args The arguments to pass to the constructor 103 | * @return The new instance 104 | */ 105 | public RStream streamInstance(final Class clazz, final Object... args) { 106 | return RStream.of(clazz, this.newInstance(args)); 107 | } 108 | 109 | 110 | @Override 111 | public String toString() { 112 | return this.constructor.toString(); 113 | } 114 | 115 | @Override 116 | public boolean equals(Object obj) { 117 | if (obj instanceof ConstructorWrapper) return this.constructor.equals(((ConstructorWrapper) obj).constructor); 118 | else if (obj instanceof Constructor) return this.constructor.equals(obj); 119 | else return false; 120 | } 121 | 122 | @Override 123 | public int hashCode() { 124 | return this.constructor.hashCode(); 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/stream/field/FieldStream.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.stream.field; 2 | 3 | import net.lenni0451.reflect.Fields; 4 | import net.lenni0451.reflect.exceptions.FieldNotFoundException; 5 | import net.lenni0451.reflect.stream.RStream; 6 | 7 | import java.lang.reflect.Field; 8 | import java.util.*; 9 | import java.util.function.Consumer; 10 | import java.util.function.Function; 11 | import java.util.function.Predicate; 12 | import java.util.stream.Stream; 13 | 14 | /** 15 | * A stream of all fields of a class and super classes (if wanted).
16 | * The fields of super classes are after the fields of the class itself (descending order). 17 | */ 18 | public class FieldStream { 19 | 20 | private final RStream parent; 21 | private final List fields; 22 | 23 | public FieldStream(final RStream parent, final boolean withSuper) { 24 | this.parent = parent; 25 | this.fields = new ArrayList<>(); 26 | 27 | Class clazz = parent.clazz(); 28 | do { 29 | for (Field field : Fields.getDeclaredFields(clazz)) this.fields.add(new FieldWrapper(this, field)); 30 | 31 | if (!withSuper) break; 32 | clazz = clazz.getSuperclass(); 33 | } while (clazz != null); 34 | } 35 | 36 | private FieldStream(final RStream parent, final List fields) { 37 | this.parent = parent; 38 | this.fields = fields; 39 | } 40 | 41 | /** 42 | * @return The parent stream 43 | */ 44 | public RStream parent() { 45 | return this.parent; 46 | } 47 | 48 | /** 49 | * @return The amount of fields in this stream 50 | */ 51 | public int size() { 52 | return this.fields.size(); 53 | } 54 | 55 | 56 | /** 57 | * Get a field by the given name.
58 | * If there are multiple fields with the same name the first one will be returned. 59 | * 60 | * @param name The name of the field 61 | * @return The field wrapper 62 | */ 63 | public Optional opt(final String name) { 64 | for (FieldWrapper field : this.fields) { 65 | if (field.name().equals(name)) return Optional.of(field); 66 | } 67 | return Optional.empty(); 68 | } 69 | 70 | /** 71 | * Get a field by the given index.
72 | * The index is the position of the field in the stream. 73 | * 74 | * @param index The index of the field 75 | * @return The field wrapper 76 | */ 77 | public Optional opt(final int index) { 78 | if (index < 0 || index > this.fields.size()) return Optional.empty(); 79 | return Optional.of(this.fields.get(index)); 80 | } 81 | 82 | /** 83 | * Get a field by the given name.
84 | * If there are multiple fields with the same name the first one will be returned. 85 | * 86 | * @param name The name of the field 87 | * @return The field wrapper 88 | * @throws FieldNotFoundException If the field doesn't exist 89 | */ 90 | public FieldWrapper by(final String name) { 91 | return this.opt(name).orElseThrow(() -> new FieldNotFoundException(this.parent.clazz().getName(), name)); 92 | } 93 | 94 | /** 95 | * Get a field by the given index.
96 | * The index is the position of the field in the stream. 97 | * 98 | * @param index The index of the field 99 | * @return The field wrapper 100 | * @throws FieldNotFoundException If the field doesn't exist 101 | */ 102 | public FieldWrapper by(final int index) { 103 | return this.opt(index).orElseThrow(() -> new FieldNotFoundException(this.parent.clazz().getName(), String.valueOf(index))); 104 | } 105 | 106 | 107 | /** 108 | * Filter the fields with the given filter.
109 | * The current stream will be modified. 110 | * 111 | * @param filter The filter 112 | * @return This stream 113 | */ 114 | public FieldStream filter(final Predicate filter) { 115 | this.fields.removeIf(filter.negate()); 116 | return this; 117 | } 118 | 119 | /** 120 | * Filter the fields by the given names.
121 | * The current stream will be modified. 122 | * 123 | * @param names The names of the fields 124 | * @return This stream 125 | */ 126 | public FieldStream filterNames(final Collection names) { 127 | return this.filter(f -> names.contains(f.name())); 128 | } 129 | 130 | /** 131 | * Filter the fields by the given names.
132 | * The current stream will be modified. 133 | * 134 | * @param names The names of the fields 135 | * @return This stream 136 | */ 137 | public FieldStream filterNames(final String... names) { 138 | return this.filter(Arrays.asList(names)); 139 | } 140 | 141 | /** 142 | * Filter the fields by the given type.
143 | * The current stream will be modified. 144 | * 145 | * @param clazz The type 146 | * @return This stream 147 | */ 148 | public FieldStream filterType(final Class clazz) { 149 | return this.filter(field -> field.type().equals(clazz)); 150 | } 151 | 152 | /** 153 | * Filter the fields by whether they are static.
154 | * The current stream will be modified. 155 | * 156 | * @param isStatic Whether the fields should be static 157 | * @return This stream 158 | */ 159 | public FieldStream filterStatic(final boolean isStatic) { 160 | return this.filter(field -> field.modifier().isStatic() == isStatic); 161 | } 162 | 163 | /** 164 | * Filter the fields by whether they have the given annotation.
165 | * The current stream will be modified. 166 | * 167 | * @param annotation The annotation 168 | * @return This stream 169 | */ 170 | public FieldStream filterAnnotation(final Class annotation) { 171 | return this.filter(field -> field.annotations().anyMatch(a -> a.annotationType().equals(annotation))); 172 | } 173 | 174 | 175 | /** 176 | * @return An iterator of field wrappers 177 | */ 178 | public Iterator iterator() { 179 | return this.fields.iterator(); 180 | } 181 | 182 | /** 183 | * Map the fields to a new stream.
184 | * This is the same as calling {@link #jstream()} and then {@link Stream#map(Function)}. 185 | * 186 | * @param mapFunction The map function 187 | * @param The type of the new stream 188 | * @return The new stream 189 | */ 190 | public Stream map(final Function mapFunction) { 191 | return this.jstream().map(mapFunction); 192 | } 193 | 194 | /** 195 | * @return A stream of field wrappers 196 | */ 197 | public Stream jstream() { 198 | return this.fields.stream(); 199 | } 200 | 201 | /** 202 | * Loop through all fields in this stream. 203 | * 204 | * @param consumer The consumer 205 | * @return This stream 206 | */ 207 | public FieldStream forEach(final Consumer consumer) { 208 | this.fields.forEach(consumer); 209 | return this; 210 | } 211 | 212 | /** 213 | * @return A copy of this stream 214 | */ 215 | public FieldStream copy() { 216 | return new FieldStream(this.parent, new ArrayList<>(this.fields)); 217 | } 218 | 219 | 220 | @Deprecated 221 | public FieldStream filter(final Collection names) { 222 | return this.filterNames(names); 223 | } 224 | 225 | @Deprecated 226 | public FieldStream filter(final String... names) { 227 | return this.filterNames(names); 228 | } 229 | 230 | @Deprecated 231 | public FieldStream filter(final Class type) { 232 | return this.filterType(type); 233 | } 234 | 235 | @Deprecated 236 | public FieldStream filter(final boolean isStatic) { 237 | return this.filterStatic(isStatic); 238 | } 239 | 240 | } 241 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/stream/field/FieldWrapper.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.stream.field; 2 | 3 | import net.lenni0451.reflect.Fields; 4 | import net.lenni0451.reflect.stream.RStream; 5 | import net.lenni0451.reflect.stream.general.ModifierWrapper; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.lang.reflect.Field; 9 | import java.lang.reflect.ParameterizedType; 10 | import java.lang.reflect.Type; 11 | import java.util.Arrays; 12 | import java.util.stream.Stream; 13 | 14 | /** 15 | * A wrapper of the {@link Field} class for easy access to all required methods. 16 | */ 17 | public class FieldWrapper { 18 | 19 | private final FieldStream parent; 20 | private final Field field; 21 | private final ModifierWrapper modifier; 22 | 23 | public FieldWrapper(final FieldStream parent, final Field field) { 24 | this.parent = parent; 25 | this.field = field; 26 | this.modifier = new ModifierWrapper(this.field.getModifiers()); 27 | } 28 | 29 | /** 30 | * @return The parent field stream 31 | */ 32 | public FieldStream parent() { 33 | return this.parent; 34 | } 35 | 36 | /** 37 | * @return The underlying field 38 | */ 39 | public Field raw() { 40 | return this.field; 41 | } 42 | 43 | /** 44 | * @return The name of the field 45 | */ 46 | public String name() { 47 | return this.field.getName(); 48 | } 49 | 50 | /** 51 | * @return The type of the field 52 | */ 53 | public Class type() { 54 | return this.field.getType(); 55 | } 56 | 57 | /** 58 | * @return The owner (declaring) class of the field 59 | */ 60 | public Class owner() { 61 | return this.field.getDeclaringClass(); 62 | } 63 | 64 | /** 65 | * @return The {@link ModifierWrapper} of the field 66 | */ 67 | public ModifierWrapper modifier() { 68 | return this.modifier; 69 | } 70 | 71 | /** 72 | * @return The generic types of the field 73 | */ 74 | public Type[] genericTypes() { 75 | if (this.field.getGenericType() instanceof ParameterizedType) { 76 | ParameterizedType parameterizedType = (ParameterizedType) this.field.getGenericType(); 77 | return parameterizedType.getActualTypeArguments(); 78 | } 79 | return new Type[0]; 80 | } 81 | 82 | /** 83 | * @return A stream of all annotations of the field 84 | */ 85 | public Stream annotations() { 86 | return Arrays.stream(this.field.getDeclaredAnnotations()); 87 | } 88 | 89 | 90 | /** 91 | * Get the value of the field.
92 | * The cached instance of the owner will be used if required. 93 | * 94 | * @param The type of the field 95 | * @return The value of the field 96 | * @throws IllegalStateException If the field is not static and no instance is cached 97 | */ 98 | public T get() { 99 | if (!this.modifier.isStatic() && this.parent.parent().instance() == null) throw new IllegalStateException("Can not get non static field if no instance is provided"); 100 | return Fields.get(this.parent.parent().instance(), this.field); 101 | } 102 | 103 | /** 104 | * Get the value of the field and wrap it in a new {@link RStream}.
105 | * The cached instance of the owner will be used if required. 106 | * 107 | * @return The value of the field 108 | * @throws IllegalStateException If the field is not static and no instance is cached 109 | */ 110 | public RStream stream() { 111 | return RStream.of(this.get()); 112 | } 113 | 114 | /** 115 | * Get the value of the field and wrap it in a new {@link RStream}.
116 | * The cached instance of the owner will be used if required. 117 | * 118 | * @param clazz The class used for the stream 119 | * @return The value of the field 120 | * @throws IllegalStateException If the field is not static and no instance is cached 121 | */ 122 | public RStream stream(final Class clazz) { 123 | return RStream.of(clazz, this.get()); 124 | } 125 | 126 | /** 127 | * Get the value of the field with the given instance. 128 | * 129 | * @param instance The instance of the owner 130 | * @param The type of the field 131 | * @return The value of the field 132 | */ 133 | public T get(final Object instance) { 134 | return Fields.get(instance, this.field); 135 | } 136 | 137 | /** 138 | * Get the value of the field with the given instance and wrap it in a new {@link RStream}. 139 | * 140 | * @param instance The instance of the owner 141 | * @return The value of the field 142 | */ 143 | public RStream stream(final Object instance) { 144 | return RStream.of(this.get(instance)); 145 | } 146 | 147 | /** 148 | * Get the value of the field with the given instance and wrap it in a new {@link RStream}. 149 | * 150 | * @param clazz The class used for the stream 151 | * @param instance The instance of the owner 152 | * @return The value of the field 153 | */ 154 | public RStream stream(final Class clazz, final Object instance) { 155 | return RStream.of(clazz, this.get(instance)); 156 | } 157 | 158 | /** 159 | * Set the value of the {@link Field}. 160 | * 161 | * @param value The value to set the {@link Field} to 162 | * @throws IllegalStateException If trying to set a non-static {@link Field} if no instance is provided 163 | */ 164 | public void set(final Object value) { 165 | if (!this.modifier.isStatic() && this.parent.parent().instance() == null) throw new IllegalStateException("Can not set non-static field if no instance is provided"); 166 | Fields.set(this.parent.parent().instance(), this.field, value); 167 | } 168 | 169 | /** 170 | * Set the value of the {@link Field} with the given owner. 171 | * 172 | * @param instance The instance of the owner 173 | * @param value The value to set the {@link Field} to 174 | */ 175 | public void set(final Object instance, final Object value) { 176 | Fields.set(instance, this.field, value); 177 | } 178 | 179 | /** 180 | * Copy the value of the {@link Field} to the given target. 181 | * 182 | * @param target The target to copy the value to 183 | */ 184 | public void copy(final Object target) { 185 | if (this.modifier.isStatic()) throw new IllegalStateException("Can not copy static field"); 186 | if (this.parent.parent().instance() == null) throw new IllegalStateException("Can not copy field if no instance is provided"); 187 | Fields.copy(this.parent.parent().instance(), target, this.field); 188 | } 189 | 190 | /** 191 | * Copy the value of the {@link Field} to the given target with the given owner. 192 | * 193 | * @param instance The instance of the owner 194 | * @param target The target to copy the value to 195 | */ 196 | public void copy(final Object instance, final Object target) { 197 | if (this.modifier.isStatic()) throw new IllegalStateException("Can not copy static field"); 198 | Fields.copy(instance, target, this.field); 199 | } 200 | 201 | 202 | @Override 203 | public String toString() { 204 | return this.field.toString(); 205 | } 206 | 207 | @Override 208 | public boolean equals(Object obj) { 209 | if (obj instanceof FieldWrapper) return this.field.equals(((FieldWrapper) obj).field); 210 | else if (obj instanceof Field) return this.field.equals(obj); 211 | else return false; 212 | } 213 | 214 | @Override 215 | public int hashCode() { 216 | return this.field.hashCode(); 217 | } 218 | 219 | } 220 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/stream/general/ModifierWrapper.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.stream.general; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.Method; 6 | import java.lang.reflect.Modifier; 7 | 8 | /** 9 | * A wrapper for field/constructor/method modifier for easier usage. 10 | */ 11 | public class ModifierWrapper { 12 | 13 | private final int modifier; 14 | 15 | public ModifierWrapper(final int modifier) { 16 | this.modifier = modifier; 17 | } 18 | 19 | /** 20 | * @return The raw modifier 21 | */ 22 | public int raw() { 23 | return this.modifier; 24 | } 25 | 26 | /** 27 | * Targets: {@link Field}, {@link Constructor}, {@link Method}. 28 | * 29 | * @return If the modifier is public 30 | */ 31 | public boolean isPublic() { 32 | return Modifier.isPublic(this.modifier); 33 | } 34 | 35 | /** 36 | * Targets: {@link Field}, {@link Constructor}, {@link Method}. 37 | * 38 | * @return If the modifier is private 39 | */ 40 | public boolean isPrivate() { 41 | return Modifier.isPrivate(this.modifier); 42 | } 43 | 44 | /** 45 | * Targets: {@link Field}, {@link Constructor}, {@link Method}. 46 | * 47 | * @return If the modifier is protected 48 | */ 49 | public boolean isProtected() { 50 | return Modifier.isProtected(this.modifier); 51 | } 52 | 53 | /** 54 | * Targets: {@link Field}, {@link Method}. 55 | * 56 | * @return If the modifier is static 57 | */ 58 | public boolean isStatic() { 59 | return Modifier.isStatic(this.modifier); 60 | } 61 | 62 | /** 63 | * Targets: {@link Field}, {@link Method}. 64 | * 65 | * @return If the modifier is final 66 | */ 67 | public boolean isFinal() { 68 | return Modifier.isFinal(this.modifier); 69 | } 70 | 71 | /** 72 | * Targets: {@link Field}, {@link Method}. 73 | * 74 | * @return If the modifier is synchronized 75 | */ 76 | public boolean isSynchronized() { 77 | return Modifier.isSynchronized(this.modifier); 78 | } 79 | 80 | /** 81 | * Targets: {@link Field}. 82 | * 83 | * @return If the modifier is volatile 84 | */ 85 | public boolean isVolatile() { 86 | return Modifier.isVolatile(this.modifier); 87 | } 88 | 89 | /** 90 | * Targets: {@link Field}. 91 | * 92 | * @return If the modifier is transient 93 | */ 94 | public boolean isTransient() { 95 | return Modifier.isTransient(this.modifier); 96 | } 97 | 98 | /** 99 | * Targets: {@link Method}. 100 | * 101 | * @return If the modifier is native 102 | */ 103 | public boolean isNative() { 104 | return Modifier.isNative(this.modifier); 105 | } 106 | 107 | /** 108 | * Targets: {@link Method}. 109 | * 110 | * @return If the modifier is abstract 111 | */ 112 | public boolean isAbstract() { 113 | return Modifier.isAbstract(this.modifier); 114 | } 115 | 116 | /** 117 | * Targets: {@link Method}. 118 | * 119 | * @return If the modifier is strict 120 | */ 121 | public boolean isStrict() { 122 | return Modifier.isStrict(this.modifier); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/utils/FieldInitializer.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.utils; 2 | 3 | import lombok.SneakyThrows; 4 | 5 | import java.util.function.Consumer; 6 | import java.util.function.Function; 7 | import java.util.function.Supplier; 8 | 9 | /** 10 | * Helper class to initialize fields with a value or throw an exception if the value is null. 11 | */ 12 | public class FieldInitializer { 13 | 14 | public static T init(final T value, final Consumer initializer) { 15 | initializer.accept(value); 16 | return value; 17 | } 18 | 19 | @SneakyThrows 20 | public static T init(final ThrowingSupplier supplier) { 21 | return supplier.get(); 22 | } 23 | 24 | @SneakyThrows 25 | public static R init(final ThrowingSupplier supplier, final ThrowingFunction processor) { 26 | return processor.apply(supplier.get()); 27 | } 28 | 29 | @SneakyThrows 30 | public static T reqInit(final ThrowingSupplier supplier, final Supplier exceptionSupplier) { 31 | T value = supplier.get(); 32 | if (value == null) throw exceptionSupplier.get(); 33 | return value; 34 | } 35 | 36 | @SneakyThrows 37 | public static R reqInit(final ThrowingSupplier supplier, final ThrowingFunction processor, final Supplier exceptionSupplier) { 38 | T value = supplier.get(); 39 | if (value == null) throw exceptionSupplier.get(); 40 | return processor.apply(value); 41 | } 42 | 43 | @SneakyThrows 44 | public static T condInit(final boolean init, final ThrowingSupplier supplier) { 45 | if (!init) return null; 46 | return supplier.get(); 47 | } 48 | 49 | @SneakyThrows 50 | public static R condInit(final boolean init, final ThrowingSupplier supplier, final ThrowingFunction processor) { 51 | if (!init) return null; 52 | return processor.apply(supplier.get()); 53 | } 54 | 55 | public static T optInit(final ThrowingSupplier supplier) { 56 | try { 57 | return supplier.get(); 58 | } catch (Throwable t) { 59 | return null; 60 | } 61 | } 62 | 63 | public static R optInit(final ThrowingSupplier supplier, final ThrowingFunction processor) { 64 | try { 65 | return processor.apply(supplier.get()); 66 | } catch (Throwable t) { 67 | return null; 68 | } 69 | } 70 | 71 | @SneakyThrows 72 | public static T reqOptInit(final boolean init, final ThrowingSupplier supplier, final Supplier exceptionSupplier) { 73 | if (!init) return null; 74 | T value = supplier.get(); 75 | if (value == null) throw exceptionSupplier.get(); 76 | return value; 77 | } 78 | 79 | @SneakyThrows 80 | public static R reqOptInit(final boolean init, final ThrowingSupplier supplier, final ThrowingFunction processor, final Supplier exceptionSupplier) { 81 | if (!init) return null; 82 | T value = supplier.get(); 83 | if (value == null) throw exceptionSupplier.get(); 84 | return processor.apply(value); 85 | } 86 | 87 | @SneakyThrows 88 | public static T condReqInit(final ThrowingSupplier condition, final ThrowingFunction supplier, final Supplier exceptionSupplier) { 89 | C cond; 90 | try { 91 | cond = condition.get(); 92 | if (cond == null) return null; 93 | } catch (Throwable t) { 94 | return null; 95 | } 96 | T value = supplier.apply(cond); 97 | if (value == null) throw exceptionSupplier.get(); 98 | return value; 99 | } 100 | 101 | @SneakyThrows 102 | public static T process(final ThrowingSupplier supplier, final Function exceptionProcessor) { 103 | try { 104 | return supplier.get(); 105 | } catch (Throwable t) { 106 | throw exceptionProcessor.apply(t); 107 | } 108 | } 109 | 110 | 111 | @FunctionalInterface 112 | public interface ThrowingSupplier { 113 | @SafeVarargs 114 | static ThrowingSupplier getFirst(final ThrowingSupplier... suppliers) { 115 | return () -> { 116 | Throwable cause = null; 117 | for (ThrowingSupplier supplier : suppliers) { 118 | try { 119 | T value = supplier.get(); 120 | if (value != null) return value; 121 | throw new NullPointerException("Supplier returned null"); 122 | } catch (Throwable t) { 123 | if (cause == null) cause = new IllegalStateException("All suppliers failed"); 124 | cause.addSuppressed(t); 125 | } 126 | } 127 | if (cause == null) throw new IllegalStateException("All suppliers failed"); 128 | throw cause; 129 | }; 130 | } 131 | 132 | T get() throws Throwable; 133 | } 134 | 135 | @FunctionalInterface 136 | public interface ThrowingFunction { 137 | @SafeVarargs 138 | static ThrowingFunction getFirst(final ThrowingFunction... functions) { 139 | return a -> { 140 | Throwable cause = null; 141 | for (ThrowingFunction function : functions) { 142 | try { 143 | R value = function.apply(a); 144 | if (value != null) return value; 145 | throw new NullPointerException("Function returned null"); 146 | } catch (Throwable t) { 147 | if (cause == null) cause = new IllegalStateException("All functions failed"); 148 | cause.addSuppressed(t); 149 | } 150 | } 151 | if (cause == null) throw new IllegalStateException("All functions failed"); 152 | throw cause; 153 | }; 154 | } 155 | 156 | R apply(final A a) throws Throwable; 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/net/lenni0451/reflect/utils/ObjectPrinter.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.utils; 2 | 3 | import net.lenni0451.reflect.Fields; 4 | 5 | import java.lang.reflect.Array; 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.Modifier; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.Optional; 12 | import java.util.function.Function; 13 | import java.util.function.Predicate; 14 | 15 | /** 16 | * Convert an Object to a String using reflection to access all fields. 17 | */ 18 | public class ObjectPrinter { 19 | 20 | private static final List> PRIMITIVE_CLASSES = FieldInitializer.init(new ArrayList<>(), list -> { 21 | list.add(Boolean.class); 22 | list.add(Byte.class); 23 | list.add(Short.class); 24 | list.add(Character.class); 25 | list.add(Integer.class); 26 | list.add(Long.class); 27 | list.add(Float.class); 28 | list.add(Double.class); 29 | list.add(String.class); 30 | }); 31 | private static final List CONVERTERS = FieldInitializer.init(new ArrayList<>(), list -> { 32 | list.add(new Converter(Class::isArray, (array, out, valueToString, includeSuper) -> { 33 | out.append(array.getClass().getComponentType().getSimpleName()).append("[]{"); 34 | int size = Array.getLength(array); 35 | for (int i = 0; i < size; i++) { 36 | Object value = Array.get(array, i); 37 | out.append(valueToString.apply(value)).append(", "); 38 | } 39 | if (size > 0) out.setLength(out.length() - 2); 40 | out.append("}"); 41 | })); 42 | list.add(new Converter(Iterable.class::isAssignableFrom, (it, out, valueToString, includeSuper) -> { 43 | Iterable iterable = (Iterable) it; 44 | out.append(it.getClass().getSimpleName()).append("{"); 45 | boolean hasElements = false; 46 | for (Object value : iterable) { 47 | hasElements = true; 48 | out.append(valueToString.apply(value)).append(", "); 49 | } 50 | if (hasElements) out.setLength(out.length() - 2); 51 | out.append("}"); 52 | })); 53 | list.add(new Converter(Map.class::isAssignableFrom, (m, out, valueToString, includeSuper) -> { 54 | Map map = (Map) m; 55 | out.append(m.getClass().getSimpleName()).append("{"); 56 | boolean hasEntries = false; 57 | for (Map.Entry entry : map.entrySet()) { 58 | hasEntries = true; 59 | out.append(valueToString.apply(entry.getKey())).append("=").append(valueToString.apply(entry.getValue())).append(", "); 60 | } 61 | if (hasEntries) out.setLength(out.length() - 2); 62 | out.append("}"); 63 | })); 64 | 65 | //Has to be last 66 | list.add(new Converter(c -> true, (o, out, valueToString, includeSuper) -> { 67 | out.append(o.getClass().getSimpleName()).append("{"); 68 | Field[] fields = getFields(o.getClass(), includeSuper); 69 | for (Field field : fields) { 70 | Object value = Fields.get(o, field); 71 | out.append(field.getName()).append("=").append(valueToString.apply(value)).append(", "); 72 | } 73 | if (fields.length > 0) out.setLength(out.length() - 2); 74 | out.append("}"); 75 | })); 76 | }); 77 | 78 | /** 79 | * Convert an object to a string using reflection to access all fields.
80 | * The depth is set to {@code 0} so only the object itself will be printed.
81 | * Super fields will not be included. 82 | * 83 | * @param o The object to convert to a string 84 | * @return The string representation of the object 85 | * @see #toString(Object, int, boolean) 86 | */ 87 | public static String toString(final Object o) { 88 | return toString(o, 0, false); 89 | } 90 | 91 | /** 92 | * Convert an object to a string using reflection to access all fields.
93 | * Special handling exists for: {@code null}, primitive types, arrays, {@link Iterable}s and {@link Map}s.
94 | * A depth of {@code 0} is the default and only the object itself will be printed.
95 | * For fields outside the depth, only the {@code toString()} method of the object will be called. 96 | * 97 | * @param o The object to convert to a string 98 | * @param depth The maximum depth of fields to access 99 | * @param includeSuper If the fields of super classes should be included 100 | * @return The string representation of the object 101 | */ 102 | public static String toString(final Object o, final int depth, final boolean includeSuper) { 103 | Optional plainString = plainToString(o); 104 | if (plainString.isPresent()) return plainString.get(); 105 | 106 | StringBuilder out = new StringBuilder(); 107 | Converter converter = CONVERTERS.stream() 108 | .filter(conv -> conv.filter.test(o.getClass())) 109 | .findFirst() 110 | .orElse(null); 111 | if (converter == null) throw new IllegalStateException("No converter found! This should never happen!"); 112 | converter.converter.convert(o, out, value -> { 113 | if (depth > 0) return toString(value, depth - 1, includeSuper); 114 | else return plainToString(value).orElseGet(() -> String.valueOf(value)); 115 | }, includeSuper); 116 | return out.toString(); 117 | } 118 | 119 | private static Field[] getFields(final Class clazz, final boolean includeSuper) { 120 | List fields = new ArrayList<>(); 121 | Class current = clazz; 122 | while (true) { 123 | for (Field field : Fields.getDeclaredFields(current)) { 124 | if (Modifier.isStatic(field.getModifiers())) continue; 125 | fields.add(field); 126 | } 127 | if (!includeSuper || current == null || Object.class.equals(current)) break; 128 | current = current.getSuperclass(); 129 | } 130 | return fields.toArray(new Field[0]); 131 | } 132 | 133 | private static Optional plainToString(final Object o) { 134 | if (o == null) { 135 | return Optional.of("null"); 136 | } else if (PRIMITIVE_CLASSES.contains(o.getClass())) { 137 | String quote; 138 | if (o instanceof String) quote = "\""; 139 | else if (o instanceof Character) quote = "'"; 140 | else quote = ""; 141 | return Optional.of(quote + o + quote); 142 | } 143 | return Optional.empty(); 144 | } 145 | 146 | 147 | private static class Converter { 148 | private final Predicate> filter; 149 | private final ConverterFunction converter; 150 | 151 | private Converter(final Predicate> filter, final ConverterFunction converter) { 152 | this.filter = filter; 153 | this.converter = converter; 154 | } 155 | } 156 | 157 | @FunctionalInterface 158 | private interface ConverterFunction { 159 | void convert(final Object o, final StringBuilder out, final Function valueToString, final boolean includeSuper); 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /src/main/templates/UnsafeAccess.mustache: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.accessor; 2 | 3 | import lombok.SneakyThrows; 4 | import net.lenni0451.reflect.JavaBypass; 5 | import net.lenni0451.reflect.Objects; 6 | 7 | import javax.annotation.Nullable; 8 | import java.lang.invoke.MethodHandle; 9 | import java.lang.invoke.MethodType; 10 | 11 | /** 12 | * A class that provides access to all methods found in the unsafe classes (sun.misc.Unsafe and jdk.internal.misc.Unsafe).
13 | * The methods in this class are based on Java {{sourceVersion}} and might be different in other Java versions.
14 | * If a method is present in both classes, the method from jdk.internal.misc.Unsafe is used.
15 | *
16 | * Every method has comments describing where the method was found and if it is deprecated.
17 | * Chances are, that methods deprecated in both classes will no longer work in future Java versions.
18 | *
19 | * WARNING! Methods in this class may be removed at any time without prior notice! If you want a stable API, use the methods in the {@link Objects} class! 20 | */ 21 | public class UnsafeAccess { 22 | 23 | private static final String UNAVAILABLE_MESSAGE = "This method is not supported on this platform or Java version!"; 24 | {{#each this}} 25 | private static final MethodHandle {{name.0}}_{{iterIndex}} = tryGet(new String[]{{alt "{"}}{{#name}}"{{.}}"{{#iterHasNext}}, {{/iterHasNext}}{{/name}}{{alt "}"}}, MethodType.methodType({{returnType}}.class{{#args}}, {{..type}}.class{{/args}})); 26 | {{/each}} 27 | 28 | {{#each this}} 29 | /** 30 | {{#notices}} 31 | * {{.}}{{#iterHasNext}}
{{/iterHasNext}} 32 | {{/notices}} 33 | * 34 | {{#args}} 35 | * @param {{..name}} {@link {{..type}}} 36 | {{/args}} 37 | {{#isNotEq returnType "void"}} 38 | * @return {@link {{returnType}}} 39 | {{/isNotEq}} 40 | **/ 41 | @SneakyThrows 42 | public static {{returnType}} {{name.0}}({{#args}}final {{..type}} {{..name}}{{#iterHasNext}}, {{/iterHasNext}}{{/args}}) { 43 | if ({{name.0}}_{{iterIndex}} == null) throw new UnsupportedOperationException(UNAVAILABLE_MESSAGE); 44 | {{#isNotEq returnType "void"}}return ({{returnType}}) {{/isNotEq}}{{name.0}}_{{iterIndex}}.invokeExact({{#args}}{{..name}}{{#iterHasNext}}, {{/iterHasNext}}{{/args}}); 45 | } 46 | 47 | {{/each}} 48 | 49 | @Nullable 50 | @SneakyThrows 51 | private static MethodHandle tryGet(final String[] names, final MethodType methodType) { 52 | for (String name : names) { 53 | MethodHandle handle = tryGet(JavaBypass.INTERNAL_UNSAFE, name, methodType); 54 | if (handle != null) return handle; 55 | } 56 | for (String name : names) { 57 | MethodHandle handle = tryGet(JavaBypass.UNSAFE, name, methodType); 58 | if (handle != null) return handle; 59 | } 60 | return null; 61 | } 62 | 63 | @Nullable 64 | private static MethodHandle tryGet(final Object instance, final String name, final MethodType methodType) { 65 | try { 66 | return JavaBypass.TRUSTED_LOOKUP.findVirtual(instance.getClass(), name, methodType).bindTo(instance); 67 | } catch (Throwable ignored) { 68 | return null; 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/templates/UnsafeAccessGenerator: -------------------------------------------------------------------------------- 1 | public static void main(final String[] args) throws Throwable { 2 | Map, ClassNode> classNodes = new HashMap<>(); 3 | List allMethods = new ArrayList<>(); 4 | Collections.addAll(allMethods, JavaBypass.UNSAFE.getClass().getDeclaredMethods()); 5 | Collections.addAll(allMethods, JavaBypass.INTERNAL_UNSAFE.getClass().getDeclaredMethods()); 6 | 7 | Map methods = new LinkedHashMap<>(); 8 | Map> notices = new HashMap<>(); 9 | for (Method method : allMethods) { 10 | if (Modifier.isStatic(method.getModifiers())) continue; 11 | if (Modifier.isPrivate(method.getModifiers())) continue; 12 | String notice = "Found in {@code " + method.getDeclaringClass().getName() + "}"; 13 | if (method.getDeclaredAnnotation(Deprecated.class) != null) notice += " (deprecated)"; 14 | notice += "."; 15 | if (method.getDeclaredAnnotation(Deprecated.class) != null) notice = "" + notice + ""; 16 | 17 | String uniqueName = method.getName() + Type.getMethodDescriptor(method); 18 | methods.put(uniqueName, method); 19 | notices.computeIfAbsent(uniqueName, m -> new ArrayList<>()).add(notice); 20 | } 21 | 22 | Set set = new HashSet<>(); 23 | JsonArray arr = new JsonArray(); 24 | for (Method method : methods.values()) { 25 | List noticesList = notices.get(method.getName() + Type.getMethodDescriptor(method)); 26 | JsonObject json = new JsonObject(); 27 | 28 | JsonArray names = new JsonArray(); 29 | names.add(method.getName()); 30 | if (method.getDeclaredAnnotation(Deprecated.class) != null) { 31 | Method redirect = findRedirect(method, classNodes.computeIfAbsent(method.getDeclaringClass(), clazz -> loadClassNode(clazz))); 32 | if (redirect != null) { 33 | names.add(redirect.getName()); 34 | String argsString = Arrays.stream(redirect.getParameterTypes()).map(clazz -> trimClassName(clazz)).collect(Collectors.joining(", ")); 35 | noticesList.add("Automatically redirected to {@link #" + redirect.getName() + "(" + argsString + ")} in {@code " + redirect.getDeclaringClass().getName() + "} in case of removal."); 36 | } 37 | } 38 | json.add("name", names); 39 | 40 | String[] argNames = getArgNames(method, classNodes.computeIfAbsent(method.getDeclaringClass(), clazz -> loadClassNode(clazz))); 41 | JsonArray methodArgs = new JsonArray(); 42 | for (Class parameterType : method.getParameterTypes()) { 43 | JsonObject argObject = new JsonObject(); 44 | argObject.addProperty("type", trimClassName(parameterType)); 45 | argObject.addProperty("name", argNames[methodArgs.size()]); 46 | methodArgs.add(argObject); 47 | } 48 | json.add("args", methodArgs); 49 | 50 | json.addProperty("returnType", trimClassName(method.getReturnType())); 51 | 52 | json.add("notices", noticesList.stream().map(JsonPrimitive::new).collect(JsonArray::new, JsonArray::add, JsonArray::addAll)); 53 | 54 | if (set.add(json.toString())) arr.add(json); 55 | } 56 | System.out.println(arr); 57 | } 58 | 59 | private static ClassNode loadClassNode(final Class clazz) throws E { 60 | try { 61 | ClassReader classReader = new ClassReader(Main.class.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".class").readAllBytes()); 62 | ClassNode classNode = new ClassNode(); 63 | classReader.accept(classNode, ClassReader.SKIP_FRAMES); 64 | return classNode; 65 | } catch (Throwable t) { 66 | throw (E) t; 67 | } 68 | } 69 | 70 | private static String trimClassName(final Class clazz) { 71 | String name = clazz.getName(); 72 | if (clazz.isArray()) name = clazz.getComponentType().getName(); 73 | if (name.startsWith("java.lang.") && name.split("\\.").length == 3) return name.split("\\.")[2]; 74 | return name; 75 | } 76 | 77 | private static Method findRedirect(final Method method, final ClassNode classNode) throws NoSuchMethodException { 78 | MethodNode methodNode = classNode.methods.stream().filter(m -> m.name.equals(method.getName())).filter(m -> m.desc.equals(Type.getMethodDescriptor(method))).findFirst().orElse(null); 79 | if (methodNode == null) return null; 80 | String redirectName = null; 81 | for (AbstractInsnNode instruction : methodNode.instructions) { 82 | if (instruction.getOpcode() == Opcodes.INVOKEVIRTUAL) { 83 | if (redirectName == null) { 84 | MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; 85 | if (!methodInsnNode.owner.equals(classNode.name)) return null; 86 | if (!methodInsnNode.desc.equals(Type.getMethodDescriptor(method))) return null; 87 | redirectName = methodInsnNode.name; 88 | } else { 89 | return null; 90 | } 91 | } 92 | } 93 | if (redirectName == null) return null; 94 | return method.getDeclaringClass().getDeclaredMethod(redirectName, method.getParameterTypes()); 95 | } 96 | 97 | private static String[] getArgNames(final Method method, final ClassNode classNode) { 98 | String[] names = new String[method.getParameterCount()]; 99 | for (int i = 0; i < names.length; i++) names[i] = "arg" + i; 100 | 101 | MethodNode methodNode = classNode.methods.stream().filter(m -> m.name.equals(method.getName())).filter(m -> m.desc.equals(Type.getMethodDescriptor(method))).findFirst().orElse(null); 102 | if (methodNode == null) return names; 103 | if (methodNode.localVariables == null) return names; 104 | 105 | Type[] argTypes = Type.getArgumentTypes(method); 106 | int index = 1; 107 | for (int i = 0; i < names.length; i++) { 108 | Type argType = argTypes[i]; 109 | for (int j = 0; j < methodNode.localVariables.size(); j++) { 110 | if (methodNode.localVariables.get(j).index == index) { 111 | names[i] = methodNode.localVariables.get(j).name; 112 | break; 113 | } 114 | } 115 | index += argType.getSize(); 116 | } 117 | return names; 118 | } 119 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/AgentsTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.lang.instrument.Instrumentation; 6 | 7 | import static org.junit.jupiter.api.Assertions.*; 8 | 9 | class AgentsTest { 10 | 11 | @Test 12 | void getInstrumentation() { 13 | if (Tests.JAVA_MAJOR_VERSION < 9) { 14 | assertThrows(IllegalStateException.class, Agents::getInstrumentation); 15 | } else { 16 | Instrumentation instrumentation = assertDoesNotThrow(Agents::getInstrumentation); 17 | assertNotNull(instrumentation); 18 | assertNotNull(instrumentation.getAllLoadedClasses()); 19 | assertTrue(instrumentation.isRetransformClassesSupported()); 20 | assertTrue(instrumentation.isRedefineClassesSupported()); 21 | assertTrue(instrumentation.isNativeMethodPrefixSupported()); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/ArraysTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | class ArraysTest { 7 | 8 | @Test 9 | void setLength() { 10 | if (JVMConstants.OPENJ9_RUNTIME) return; //OpenJ9 does not support this 11 | 12 | byte[] array = new byte[10]; 13 | Assertions.assertEquals(10, array.length); 14 | Arrays.setLength(array, 5); 15 | Assertions.assertEquals(5, array.length); 16 | } 17 | 18 | @Test 19 | void fillByteArray() { 20 | if (JVMConstants.OPENJ9_RUNTIME) return; //OpenJ9 does not support this 21 | 22 | byte[] expected = new byte[10]; 23 | java.util.Arrays.fill(expected, (byte) (Byte.MAX_VALUE - 1)); 24 | 25 | byte[] array = new byte[10]; 26 | Arrays.fill(array, (byte) (Byte.MAX_VALUE - 1)); 27 | Assertions.assertArrayEquals(expected, array); 28 | } 29 | 30 | @Test 31 | void fillShortArray() { 32 | if (JVMConstants.OPENJ9_RUNTIME) return; //OpenJ9 does not support this 33 | 34 | short[] expected = new short[10]; 35 | java.util.Arrays.fill(expected, (short) (Short.MAX_VALUE - 1)); 36 | 37 | short[] array = new short[10]; 38 | Arrays.fill(array, (short) (Short.MAX_VALUE - 1)); 39 | Assertions.assertArrayEquals(expected, array); 40 | 41 | Arrays.fill(array, (short) 0); 42 | Assertions.assertArrayEquals(new short[10], array); 43 | } 44 | 45 | @Test 46 | void fillCharArray() { 47 | if (JVMConstants.OPENJ9_RUNTIME) return; //OpenJ9 does not support this 48 | 49 | char[] expected = new char[10]; 50 | java.util.Arrays.fill(expected, (char) (Character.MAX_VALUE - 1)); 51 | 52 | char[] array = new char[10]; 53 | Arrays.fill(array, (char) (Character.MAX_VALUE - 1)); 54 | Assertions.assertArrayEquals(expected, array); 55 | 56 | Arrays.fill(array, (char) 0); 57 | Assertions.assertArrayEquals(new char[10], array); 58 | } 59 | 60 | @Test 61 | void fillIntArray() { 62 | if (JVMConstants.OPENJ9_RUNTIME) return; //OpenJ9 does not support this 63 | 64 | int[] expected = new int[10]; 65 | java.util.Arrays.fill(expected, Integer.MAX_VALUE - 1); 66 | 67 | int[] array = new int[10]; 68 | Arrays.fill(array, Integer.MAX_VALUE - 1); 69 | Assertions.assertArrayEquals(expected, array); 70 | 71 | Arrays.fill(array, 0); 72 | Assertions.assertArrayEquals(new int[10], array); 73 | } 74 | 75 | @Test 76 | void fillLongArray() { 77 | if (JVMConstants.OPENJ9_RUNTIME) return; //OpenJ9 does not support this 78 | 79 | long[] expected = new long[10]; 80 | java.util.Arrays.fill(expected, Long.MAX_VALUE - 1); 81 | 82 | long[] array = new long[10]; 83 | Arrays.fill(array, Long.MAX_VALUE - 1); 84 | Assertions.assertArrayEquals(expected, array); 85 | 86 | Arrays.fill(array, 0); 87 | Assertions.assertArrayEquals(new long[10], array); 88 | } 89 | 90 | @Test 91 | void fillFloatArray() { 92 | if (JVMConstants.OPENJ9_RUNTIME) return; //OpenJ9 does not support this 93 | 94 | float[] expected = new float[10]; 95 | java.util.Arrays.fill(expected, Float.MAX_VALUE - 1); 96 | 97 | float[] array = new float[10]; 98 | Arrays.fill(array, Float.MAX_VALUE - 1); 99 | Assertions.assertArrayEquals(expected, array); 100 | 101 | Arrays.fill(array, 0); 102 | Assertions.assertArrayEquals(new float[10], array); 103 | } 104 | 105 | @Test 106 | void fillDoubleArray() { 107 | if (JVMConstants.OPENJ9_RUNTIME) return; //OpenJ9 does not support this 108 | 109 | double[] expected = new double[10]; 110 | java.util.Arrays.fill(expected, Double.MAX_VALUE - 1); 111 | 112 | double[] array = new double[10]; 113 | Arrays.fill(array, Double.MAX_VALUE - 1); 114 | Assertions.assertArrayEquals(expected, array); 115 | 116 | Arrays.fill(array, 0); 117 | Assertions.assertArrayEquals(new double[10], array); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/ClassLoadersTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import org.junit.jupiter.api.BeforeAll; 4 | import org.junit.jupiter.api.Test; 5 | import org.objectweb.asm.ClassWriter; 6 | import org.objectweb.asm.MethodVisitor; 7 | import org.objectweb.asm.Opcodes; 8 | 9 | import java.net.URL; 10 | import java.util.function.Supplier; 11 | 12 | import static org.junit.jupiter.api.Assertions.*; 13 | 14 | class ClassLoadersTest { 15 | 16 | private static byte[] testClassBytes; 17 | 18 | @BeforeAll 19 | static void setUp() { 20 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 21 | cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "net/lenni0451/reflect/ASMTestClass", null, "java/lang/Object", new String[]{"java/util/function/Supplier"}); 22 | 23 | MethodVisitor c = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null); 24 | c.visitCode(); 25 | c.visitVarInsn(Opcodes.ALOAD, 0); 26 | c.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false); 27 | c.visitInsn(Opcodes.RETURN); 28 | c.visitMaxs(1, 1); 29 | c.visitEnd(); 30 | 31 | MethodVisitor s = cw.visitMethod(Opcodes.ACC_PUBLIC, "get", "()Ljava/lang/Object;", null, null); 32 | s.visitCode(); 33 | s.visitLdcInsn("Hello World"); 34 | s.visitInsn(Opcodes.ARETURN); 35 | s.visitMaxs(1, 1); 36 | s.visitEnd(); 37 | 38 | cw.visitEnd(); 39 | testClassBytes = cw.toByteArray(); 40 | } 41 | 42 | @Test 43 | void getSystemClassPath() { 44 | URL[] systemClassPath = assertDoesNotThrow(ClassLoaders::getSystemClassPath); 45 | assertNotNull(systemClassPath); 46 | assertTrue(systemClassPath.length > 0); 47 | } 48 | 49 | @Test 50 | void defineClass() { 51 | Class supplier = assertDoesNotThrow(() -> ClassLoaders.defineClass(ClassLoadersTest.class.getClassLoader(), null, testClassBytes)); 52 | Supplier instance = assertDoesNotThrow(() -> (Supplier) supplier.getDeclaredConstructor().newInstance()); 53 | String response = assertDoesNotThrow(instance::get); 54 | assertEquals("Hello World", response); 55 | } 56 | 57 | @Test 58 | void defineAnonymousClass() { 59 | Class supplier = assertDoesNotThrow(() -> ClassLoaders.defineAnonymousClass(ClassLoadersTest.class, testClassBytes)); 60 | Supplier instance = assertDoesNotThrow(() -> (Supplier) supplier.getDeclaredConstructor().newInstance()); 61 | String response = assertDoesNotThrow(instance::get); 62 | assertEquals("Hello World", response); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/ClassesTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.util.Collections; 6 | 7 | import static org.junit.jupiter.api.Assertions.*; 8 | 9 | class ClassesTest { 10 | 11 | @Test 12 | void getDeclaredClasses() { 13 | assertTrue(Classes.getDeclaredClasses(Class.class).length > 0); 14 | } 15 | 16 | @Test 17 | void byName() { 18 | assertNotNull(assertDoesNotThrow(() -> Classes.byName("java.lang.String"))); 19 | assertNotNull(assertDoesNotThrow(() -> Classes.byName("java.lang.String", true))); 20 | assertNotNull(assertDoesNotThrow(() -> Classes.byName("java.lang.String", null))); 21 | assertNotNull(assertDoesNotThrow(() -> Classes.byName("java.lang.String", true, null))); 22 | assertNull(assertDoesNotThrow(() -> Classes.byName("UnknownClass"))); 23 | assertNull(assertDoesNotThrow(() -> Classes.byName("UnknownClass", null))); 24 | assertNull(assertDoesNotThrow(() -> Classes.byName("UnknownClass", true, null))); 25 | } 26 | 27 | @Test 28 | void forName() { 29 | assertNotNull(assertDoesNotThrow(() -> Classes.forName("java.lang.String"))); 30 | assertNotNull(assertDoesNotThrow(() -> Classes.forName("java.lang.String", true))); 31 | assertNotNull(assertDoesNotThrow(() -> Classes.forName("java.lang.String", null))); 32 | assertNotNull(assertDoesNotThrow(() -> Classes.forName("java.lang.String", true, null))); 33 | assertThrows(ClassNotFoundException.class, () -> Classes.forName("UnknownClass")); 34 | assertThrows(ClassNotFoundException.class, () -> Classes.forName("UnknownClass", null)); 35 | assertThrows(ClassNotFoundException.class, () -> Classes.forName("UnknownClass", true, null)); 36 | } 37 | 38 | @Test 39 | void find() { 40 | assertThrows(ClassNotFoundException.class, () -> Classes.find("UnknownClass", true, ClassLoader.getSystemClassLoader())); 41 | assertEquals(String.class, assertDoesNotThrow(() -> Classes.find("java.lang.String", true, ClassLoader.getSystemClassLoader()))); 42 | assertThrows(ClassNotFoundException.class, () -> Classes.find("UnknownClass", true, Collections.singletonList(ClassLoader.getSystemClassLoader()))); 43 | assertEquals(String.class, assertDoesNotThrow(() -> Classes.find("java.lang.String", true, Collections.singletonList(ClassLoader.getSystemClassLoader())))); 44 | } 45 | 46 | @Test 47 | void getCallerClass() { 48 | assertEquals(ClassesTest.class, Classes.getCallerClass(0)); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/ConstructorsTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.lang.reflect.Constructor; 6 | 7 | import static org.junit.jupiter.api.Assertions.*; 8 | 9 | class ConstructorsTest { 10 | 11 | @Test 12 | void getDeclaredConstructors() { 13 | Constructor[] constructors = assertDoesNotThrow(() -> Constructors.getDeclaredConstructors(System.class)); 14 | assertTrue(constructors.length > 0); 15 | } 16 | 17 | @Test 18 | void invoke() { 19 | Constructor constructor = assertDoesNotThrow(() -> Constructors.getDeclaredConstructor(System.class)); 20 | assertNotNull(constructor); 21 | System system = assertDoesNotThrow(() -> Constructors.invoke(constructor)); 22 | assertNotNull(system); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/EnumsTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.time.DayOfWeek; 6 | 7 | import static org.junit.jupiter.api.Assertions.*; 8 | 9 | class EnumsTest { 10 | 11 | @Test 12 | void addEnumInstance() { 13 | assertEquals(7, DayOfWeek.class.getEnumConstants().length); 14 | DayOfWeek dayOfWeek = assertDoesNotThrow(() -> Enums.newInstance(DayOfWeek.class, "KYJSDAY", 8, new Class[0], new Object[0])); 15 | assertNotNull(dayOfWeek); 16 | assertDoesNotThrow(() -> Enums.addEnumInstance(DayOfWeek.class, dayOfWeek)); 17 | assertEquals(8, DayOfWeek.values().length); 18 | assertEquals(8, DayOfWeek.class.getEnumConstants().length); 19 | assertEquals(DayOfWeek.values()[7], dayOfWeek); 20 | } 21 | 22 | @Test 23 | void valueOfIgnoreCase() { 24 | assertEquals(DayOfWeek.MONDAY, Enums.valueOfIgnoreCase(DayOfWeek.class, "monday")); 25 | assertEquals(DayOfWeek.MONDAY, Enums.valueOfIgnoreCase(DayOfWeek.class, "MONDAY")); 26 | assertEquals(DayOfWeek.MONDAY, Enums.valueOfIgnoreCase(DayOfWeek.class, "Monday")); 27 | assertEquals(DayOfWeek.MONDAY, Enums.valueOfIgnoreCase(DayOfWeek.class, "mONdAY")); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/FieldsTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import org.junit.jupiter.api.BeforeAll; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.lang.reflect.Field; 7 | import java.util.Objects; 8 | 9 | import static org.junit.jupiter.api.Assertions.*; 10 | 11 | class FieldsTest { 12 | 13 | private static FieldsClass fc; 14 | 15 | @BeforeAll 16 | static void setUp() { 17 | fc = new FieldsClass(); 18 | fc.bool = true; 19 | fc.b = 1; 20 | fc.s = 2; 21 | fc.c = 3; 22 | fc.i = 4; 23 | fc.l = 5; 24 | fc.f = 6; 25 | fc.d = 7; 26 | fc.str = "8"; 27 | } 28 | 29 | @Test 30 | void offset() { 31 | Field f = assertDoesNotThrow(() -> FieldsClass.class.getDeclaredField("bool")); 32 | assertDoesNotThrow(() -> Fields.offset(f)); 33 | } 34 | 35 | @Test 36 | void getDeclaredFields() { 37 | Field[] fields = assertDoesNotThrow(() -> Fields.getDeclaredFields(FieldsClass.class)); 38 | assertNotNull(fields); 39 | assertEquals(9, fields.length); 40 | } 41 | 42 | @Test 43 | void get() { 44 | for (Field field : FieldsClass.class.getDeclaredFields()) { 45 | Object value = assertDoesNotThrow(() -> Fields.get(fc, field)); 46 | assertNotNull(value); 47 | if (field.getName().equalsIgnoreCase("bool")) assertEquals(true, value); 48 | else if (field.getName().equalsIgnoreCase("b")) assertEquals((byte) 1, value); 49 | else if (field.getName().equalsIgnoreCase("s")) assertEquals((short) 2, value); 50 | else if (field.getName().equalsIgnoreCase("c")) assertEquals((char) 3, value); 51 | else if (field.getName().equalsIgnoreCase("i")) assertEquals(4, value); 52 | else if (field.getName().equalsIgnoreCase("l")) assertEquals(5L, value); 53 | else if (field.getName().equalsIgnoreCase("f")) assertEquals(6F, value); 54 | else if (field.getName().equalsIgnoreCase("d")) assertEquals(7D, value); 55 | else if (field.getName().equalsIgnoreCase("str")) assertEquals("8", value); 56 | else fail("Unknown field: " + field.getName()); 57 | } 58 | } 59 | 60 | @Test 61 | void set() { 62 | FieldsClass fc = new FieldsClass(); 63 | for (Field field : FieldsClass.class.getDeclaredFields()) Fields.set(fc, field, assertDoesNotThrow(() -> field.get(FieldsTest.fc))); 64 | assertEquals(FieldsTest.fc, fc); 65 | } 66 | 67 | @Test 68 | void copy() { 69 | FieldsClass fc = new FieldsClass(); 70 | for (Field field : FieldsClass.class.getDeclaredFields()) Fields.copy(FieldsTest.fc, fc, field); 71 | assertEquals(FieldsTest.fc, fc); 72 | } 73 | 74 | 75 | private static class FieldsClass { 76 | public boolean bool; 77 | public byte b; 78 | public short s; 79 | public char c; 80 | public int i; 81 | public long l; 82 | public float f; 83 | public double d; 84 | public String str; 85 | 86 | @Override 87 | public boolean equals(Object o) { 88 | if (this == o) return true; 89 | if (o == null || getClass() != o.getClass()) return false; 90 | FieldsClass that = (FieldsClass) o; 91 | return this.bool == that.bool && this.b == that.b && this.s == that.s && this.c == that.c && this.i == that.i && this.l == that.l && Float.compare(that.f, this.f) == 0 && Double.compare(that.d, this.d) == 0 && Objects.equals(this.str, that.str); 92 | } 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/JavaBypassTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import sun.misc.Unsafe; 5 | 6 | import java.lang.invoke.MethodHandles; 7 | 8 | import static net.lenni0451.reflect.Tests.JAVA_MAJOR_VERSION; 9 | import static org.junit.jupiter.api.Assertions.*; 10 | 11 | class JavaBypassTest { 12 | 13 | @Test 14 | void getUnsafe() { 15 | Unsafe unsafe = assertDoesNotThrow(JavaBypass::getUnsafe); 16 | assertNotNull(unsafe); 17 | assertDoesNotThrow(() -> assertInstanceOf(String.class, unsafe.allocateInstance(String.class))); 18 | } 19 | 20 | @Test 21 | void getTrustedLookup() { 22 | MethodHandles.Lookup trustedLookup = assertDoesNotThrow(JavaBypass::getTrustedLookup); 23 | assertNotNull(trustedLookup); 24 | } 25 | 26 | @Test 27 | void getInternalUnsafe() { 28 | Object internalUnsafe = assertDoesNotThrow(JavaBypass::getInternalUnsafe); 29 | if (JAVA_MAJOR_VERSION < 11) assertNull(internalUnsafe); //The internal unsafe was added with java 11 30 | else assertNotNull(internalUnsafe); 31 | } 32 | 33 | @Test 34 | void clearReflectionFilter() { 35 | assertDoesNotThrow(JavaBypass::clearReflectionFilter); 36 | assertNotNull(assertDoesNotThrow(() -> Class.class.getDeclaredField("classLoader"))); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/MethodsTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | import static org.junit.jupiter.api.Assertions.*; 8 | 9 | class MethodsTest { 10 | 11 | @Test 12 | void getDeclaredMethods() { 13 | Method[] methods = assertDoesNotThrow(() -> Methods.getDeclaredMethods(Methods.class)); 14 | assertTrue(methods.length > 0); 15 | } 16 | 17 | @Test 18 | void invoke() { 19 | String s = "Hello World"; 20 | Method method = assertDoesNotThrow(() -> String.class.getDeclaredMethod("toString")); 21 | String result = assertDoesNotThrow(() -> Methods.invoke(s, method)); 22 | assertEquals(s, result); 23 | } 24 | 25 | @Test 26 | void invokeSuper() { 27 | String s = "Hello World"; 28 | Method method = assertDoesNotThrow(() -> Object.class.getDeclaredMethod("toString")); 29 | String result = assertDoesNotThrow(() -> Methods.invokeSuper(s, Object.class, method)); 30 | assertNotEquals(s, result); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/ModulesTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 6 | 7 | class ModulesTest { 8 | 9 | @Test 10 | void copyModule() { 11 | assertDoesNotThrow(() -> Modules.copyModule(Object.class, ModuleHolder.class)); 12 | } 13 | 14 | @Test 15 | void openModule() { 16 | assertDoesNotThrow(() -> Modules.openModule(ModulesTest.class)); 17 | } 18 | 19 | 20 | private static class ModuleHolder { 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/ObjectsTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.io.ByteArrayOutputStream; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | 11 | class ObjectsTest { 12 | 13 | @Test 14 | void toFromAddress() { 15 | String in = "Hello World"; 16 | long jvmAddress = assertDoesNotThrow(() -> Objects.toJVMAddress(in)); 17 | long nativeAddress = assertDoesNotThrow(() -> Objects.toNativeAddress(in)); 18 | assertEquals(jvmAddress, Objects.toJVMAddress(nativeAddress)); 19 | assertEquals(nativeAddress, Objects.toNativeAddress(jvmAddress)); 20 | 21 | String fromJVMAddress = assertDoesNotThrow(() -> Objects.fromJVMAddress(jvmAddress)); 22 | String fromNativeAddress = assertDoesNotThrow(() -> Objects.fromJVMAddress(Objects.toJVMAddress(nativeAddress))); 23 | assertEquals(in, fromJVMAddress); 24 | assertEquals(in, fromNativeAddress); 25 | } 26 | 27 | @Test 28 | void cast() { 29 | if (JVMConstants.OPENJ9_RUNTIME) { 30 | Assertions.assertThrows(UnsupportedOperationException.class, () -> Objects.cast(new byte[0], CustomByteArrayOutputStream.class)); 31 | } else { 32 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 33 | Objects.cast(baos, CustomByteArrayOutputStream.class); 34 | assertEquals("Hello World", baos.toString()); 35 | } 36 | } 37 | 38 | 39 | private static class CustomByteArrayOutputStream extends ByteArrayOutputStream { 40 | @Override 41 | public synchronized String toString() { 42 | return "Hello World"; 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/Tests.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect; 2 | 3 | public class Tests { 4 | 5 | public static final int JAVA_MAJOR_VERSION; 6 | 7 | static { 8 | String javaVersion = System.getProperty("java.version"); 9 | if (javaVersion.startsWith("1.")) javaVersion = javaVersion.substring(2); 10 | JAVA_MAJOR_VERSION = Integer.parseInt(javaVersion.split("\\.")[0]); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/accessor/FieldAccessorTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.accessor; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.lang.reflect.Field; 7 | import java.util.function.BiConsumer; 8 | import java.util.function.Consumer; 9 | import java.util.function.Function; 10 | import java.util.function.Supplier; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 13 | import static org.junit.jupiter.api.Assertions.assertEquals; 14 | 15 | class FieldAccessorTest { 16 | 17 | private FieldClass fc; 18 | private Field field; 19 | 20 | @BeforeEach 21 | void setUp() { 22 | this.fc = new FieldClass(); 23 | this.field = assertDoesNotThrow(() -> FieldClass.class.getDeclaredField("field")); 24 | } 25 | 26 | @Test 27 | void makeSetter() { 28 | Consumer setter = assertDoesNotThrow(() -> FieldAccessor.makeSetter(Consumer.class, this.fc, this.field)); 29 | setter.accept("World"); 30 | assertEquals("World", this.fc.getField()); 31 | } 32 | 33 | @Test 34 | void makeDynamicSetter() { 35 | BiConsumer dynamicSetter = assertDoesNotThrow(() -> FieldAccessor.makeDynamicSetter(BiConsumer.class, this.field)); 36 | dynamicSetter.accept(this.fc, "World"); 37 | assertEquals("World", this.fc.getField()); 38 | } 39 | 40 | @Test 41 | void makeGetter() { 42 | Supplier getter = assertDoesNotThrow(() -> FieldAccessor.makeGetter(Supplier.class, this.fc, this.field)); 43 | assertEquals("Hello", getter.get()); 44 | } 45 | 46 | @Test 47 | void makeDynamicGetter() { 48 | Function dynamicGetter = assertDoesNotThrow(() -> FieldAccessor.makeDynamicGetter(Function.class, this.field)); 49 | assertEquals("Hello", dynamicGetter.apply(this.fc)); 50 | } 51 | 52 | 53 | @SuppressWarnings("FieldMayBeFinal") 54 | private static class FieldClass { 55 | private String field = "Hello"; 56 | 57 | public String getField() { 58 | return this.field; 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/accessor/MethodAccessorTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.accessor; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.lang.reflect.Method; 7 | import java.util.function.BiFunction; 8 | import java.util.function.Function; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | 13 | class MethodAccessorTest { 14 | 15 | private MethodClass mc; 16 | private Method method1; 17 | private Method method2; 18 | 19 | @BeforeEach 20 | void setUp() { 21 | this.mc = new MethodClass(); 22 | this.method1 = assertDoesNotThrow(() -> MethodClass.class.getDeclaredMethod("reverse", String.class)); 23 | this.method2 = assertDoesNotThrow(() -> MethodClass.class.getDeclaredMethod("add", String.class, int.class, double.class)); 24 | } 25 | 26 | @Test 27 | void makeInvoker() { 28 | Function invoker = assertDoesNotThrow(() -> MethodAccessor.makeInvoker(Function.class, this.mc, this.method1)); 29 | assertEquals("cba", invoker.apply("abc")); 30 | } 31 | 32 | @Test 33 | void makeArrayInvoker() { 34 | Function arrayInvoker = assertDoesNotThrow(() -> MethodAccessor.makeArrayInvoker(this.mc, this.method2)); 35 | assertEquals(6, arrayInvoker.apply(new Object[]{"abc", 1, 2.78D})); 36 | } 37 | 38 | @Test 39 | void makeDynamicInvoker() { 40 | BiFunction dynamicInvoker = assertDoesNotThrow(() -> MethodAccessor.makeDynamicInvoker(BiFunction.class, this.method1)); 41 | assertEquals("cba", dynamicInvoker.apply(this.mc, "abc")); 42 | } 43 | 44 | @Test 45 | void makeDynamicArrayInvoker() { 46 | BiFunction dynamicArrayInvoker = assertDoesNotThrow(() -> MethodAccessor.makeDynamicArrayInvoker(this.method2)); 47 | assertEquals(6, dynamicArrayInvoker.apply(this.mc, new Object[]{"abc", 1, 2.78D})); 48 | } 49 | 50 | 51 | private static class MethodClass { 52 | private String reverse(final String s) { 53 | char[] chars = s.toCharArray(); 54 | char[] reversed = new char[chars.length]; 55 | for (int i = 0; i < chars.length; i++) reversed[i] = chars[chars.length - i - 1]; 56 | return new String(reversed); 57 | } 58 | 59 | private int add(final String s, final int a, final double b) { 60 | return s.length() + a + (int) b; 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/bytecode/BytecodeBuilderTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.bytecode; 2 | 3 | import net.lenni0451.reflect.bytecode.builder.BytecodeBuilder; 4 | import net.lenni0451.reflect.bytecode.wrapper.BuiltClass; 5 | import net.lenni0451.reflect.bytecode.wrapper.BytecodeLabel; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.util.function.Supplier; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | 13 | class BytecodeBuilderTest { 14 | 15 | @Test 16 | void test() { 17 | BytecodeBuilder builder = BytecodeBuilder.get(); 18 | BuiltClass builtClass = builder.class_(builder.opcode("ACC_PUBLIC"), "net/lenni0451/reflect/bytecode/BytecodeBuilderTestSupplier", null, "java/lang/Object", new String[]{"java/util/function/Supplier"}, clazz -> { 19 | clazz.method(builder.opcode("ACC_PUBLIC"), "", "()V", null, null, method -> method 20 | .var(builder.opcode("ALOAD"), 0) 21 | .method(builder.opcode("INVOKESPECIAL"), "java/lang/Object", "", "()V", false) 22 | .insn(builder.opcode("RETURN")) 23 | .maxs(1, 1)); 24 | clazz.method(builder.opcode("ACC_PUBLIC"), "get", "()Ljava/lang/Object;", null, null, method -> { 25 | BytecodeLabel start = builder.label(); 26 | BytecodeLabel end = builder.label(); 27 | BytecodeLabel handler = builder.label(); 28 | 29 | method 30 | .jump(builder.opcode("GOTO"), start) 31 | .label(start) 32 | .ldc("Hello World") 33 | .insn(builder.opcode("ARETURN")) 34 | .label(end) 35 | .label(handler) 36 | .insn(builder.opcode("ACONST_NULL")) 37 | .insn(builder.opcode("ARETURN")) 38 | .tryCatch(start, end, handler, "java/lang/Exception") 39 | .maxs(1, 1); 40 | }); 41 | }); 42 | 43 | Class clazz = builtClass.defineAnonymous(BytecodeBuilderTest.class); 44 | Supplier supplier = (Supplier) assertDoesNotThrow(() -> clazz.getDeclaredConstructor().newInstance()); 45 | assertEquals("Hello World", supplier.get()); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/proxy/ProxyTest.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.proxy; 2 | 3 | import net.lenni0451.reflect.Methods; 4 | import net.lenni0451.reflect.proxy.impl.Proxy; 5 | import net.lenni0451.reflect.proxy.test.*; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import java.lang.reflect.Method; 10 | 11 | import static org.junit.jupiter.api.Assertions.*; 12 | 13 | class ProxyTest { 14 | 15 | private Class1 proxy; 16 | 17 | @BeforeEach 18 | void setUp() { 19 | ProxyBuilder proxyBuilder = new ProxyBuilder(); 20 | proxyBuilder.setSuperClass(Class1.class); 21 | proxyBuilder.addInterface(Interface1.class); 22 | this.proxy = proxyBuilder.build().allocateInstance(); 23 | } 24 | 25 | @Test 26 | void testChangeReturnValue() { 27 | ((Proxy) this.proxy).setInvocationHandler((thiz, proxyMethod, args) -> 10); 28 | assertEquals(10, this.proxy.test()); 29 | } 30 | 31 | @Test 32 | void testForward() { 33 | ((Proxy) this.proxy).setInvocationHandler(InvocationHandler.forwarding()); 34 | assertEquals(1, this.proxy.test()); 35 | assertEquals(10L, this.proxy.conv((byte) 10)); 36 | } 37 | 38 | @Test 39 | void testInvokeOther() { 40 | Class2 other = new Class2(); 41 | ((Proxy) this.proxy).setInvocationHandler((thiz, proxyMethod, args) -> proxyMethod.invokeWith(other, args)); 42 | assertEquals(2, this.proxy.test()); 43 | } 44 | 45 | @Test 46 | void testArgTypes() { 47 | this.proxy.takeAll(true, (byte) 1, (short) 2, 'c', 3, 4L, 5F, 6D, "test"); 48 | } 49 | 50 | @Test 51 | void testCancel() { 52 | ((Proxy) this.proxy).setInvocationHandler((thiz, proxyMethod, args) -> proxyMethod.cancel()); 53 | assertFalse(this.proxy.getBoolean()); 54 | assertEquals(0, this.proxy.getByte()); 55 | assertEquals(0, this.proxy.getShort()); 56 | assertEquals(0, this.proxy.getChar()); 57 | assertEquals(0, this.proxy.getInt()); 58 | assertEquals(0, this.proxy.getLong()); 59 | assertEquals(0, this.proxy.getFloat()); 60 | assertEquals(0, this.proxy.getDouble()); 61 | assertNull(this.proxy.getString()); 62 | } 63 | 64 | @Test 65 | void testInterface() { 66 | Class2 other = new Class2(); 67 | ((Proxy) this.proxy).setInvocationHandler((thiz, proxyMethod, args) -> proxyMethod.invokeWith(other, args)); 68 | assertEquals(12, ((Interface1) this.proxy).interfaceTest()); 69 | } 70 | 71 | @Test 72 | void testInvokeAbstractSuper() { 73 | ((Proxy) this.proxy).setInvocationHandler((thiz, proxyMethod, args) -> proxyMethod.invokeSuper(args)); 74 | assertThrows(AbstractMethodError.class, () -> ((Interface1) this.proxy).interfaceTest()); 75 | } 76 | 77 | @Test 78 | void testDefaultMethods() { 79 | InvocationHandler handler = InvocationHandler.forwarding(); 80 | ((Proxy) this.proxy).setInvocationHandler(handler); 81 | assertEquals(handler, ((Proxy) this.proxy).getInvocationHandler()); 82 | } 83 | 84 | @Test 85 | void testNoConstructorProxy() { 86 | ProxyClass proxyClass = new ProxyBuilder() 87 | .setSuperClass(Class3.class) 88 | .setInvocationHandler(InvocationHandler.cancelling()) 89 | .build(); 90 | Class3 instance = proxyClass.allocateInstance(); 91 | assertNotNull(instance); 92 | assertNull(instance.toString()); 93 | } 94 | 95 | @Test 96 | void testSuperImplementsProxy() { 97 | ProxyClass proxyClass = assertDoesNotThrow(() -> new ProxyBuilder() 98 | .setSuperClass(Class4.class) 99 | .setInvocationHandler(InvocationHandler.forwarding()) 100 | .build()); 101 | Class4 instance = proxyClass.allocateInstance(); 102 | assertDoesNotThrow(instance::get); 103 | } 104 | 105 | @Test 106 | void testInvokeWrongObject() { 107 | ProxyClass proxyClass = new ProxyBuilder() 108 | .setSuperClass(Class2.class) 109 | .setInvocationHandler((thiz, proxyMethod, args) -> proxyMethod.invokeWith(new Class1(), args)) 110 | .build(); 111 | Class2 proxy = proxyClass.allocateInstance(); 112 | //The method found by the proxy builder belongs to Class2 (the proxy super class) 113 | //Invoking it with an instance of Class1 should throw an exception because Class1 does not extend Class2, even tho it is the other way round 114 | //The cast is done by the proxy to allow calling invokeExact of the method handle 115 | assertThrows(ClassCastException.class, proxy::test); 116 | } 117 | 118 | @Test 119 | void testMethodMapper() { 120 | ProxyClass proxyClass = new ProxyBuilder() 121 | .setSuperClass(Class2.class) 122 | .setMethodMapper(method -> { 123 | Method class1Method = Methods.getDeclaredMethod(Class1.class, method.getName(), method.getParameterTypes()); 124 | if (class1Method != null) return class1Method; 125 | return method; 126 | }) 127 | .setInvocationHandler((thiz, proxyMethod, args) -> proxyMethod.invokeWith(new Class1(), args)) 128 | .build(); 129 | Class2 proxy = proxyClass.allocateInstance(); 130 | //The invocation is the same as in testInvokeWrongObject but this time the method mapper maps the method to one owned by Class1 131 | //This should work because Class2 extends Class1 and the correct method should be invoked 132 | assertEquals(1, proxy.test()); 133 | 134 | ((Proxy) proxy).setInvocationHandler(InvocationHandler.forwarding()); 135 | //Here it should return 2 because the super method is still the one from Class2 136 | assertEquals(2, proxy.test()); 137 | 138 | ((Proxy) proxy).setInvocationHandler((thiz, proxyMethod, args) -> proxyMethod.invokeWith(new Class2(), args)); 139 | //This should also work because it's forwarded to Class2 140 | assertEquals(2, proxy.test()); 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/proxy/test/Class1.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.proxy.test; 2 | 3 | public class Class1 { 4 | 5 | public int test() { 6 | return 1; 7 | } 8 | 9 | public long conv(final byte b) { 10 | return b; 11 | } 12 | 13 | public void takeAll(final boolean b, final byte b2, final short s, final char c, final int i, final long l, final float f, final double d, final String s2) { 14 | } 15 | 16 | public boolean getBoolean() { 17 | return true; 18 | } 19 | 20 | public byte getByte() { 21 | return 1; 22 | } 23 | 24 | public short getShort() { 25 | return 1; 26 | } 27 | 28 | public char getChar() { 29 | return 'a'; 30 | } 31 | 32 | public int getInt() { 33 | return 1; 34 | } 35 | 36 | public long getLong() { 37 | return 1; 38 | } 39 | 40 | public float getFloat() { 41 | return 1; 42 | } 43 | 44 | public double getDouble() { 45 | return 1; 46 | } 47 | 48 | public String getString() { 49 | return "test"; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/proxy/test/Class2.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.proxy.test; 2 | 3 | public class Class2 extends Class1 implements Interface1 { 4 | 5 | @Override 6 | public int test() { 7 | return 2; 8 | } 9 | 10 | @Override 11 | public int interfaceTest() { 12 | return 12; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/proxy/test/Class3.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.proxy.test; 2 | 3 | public abstract class Class3 { 4 | 5 | private Class3() { 6 | throw new UnsupportedOperationException(); 7 | } 8 | 9 | public abstract void test(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/proxy/test/Class4.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.proxy.test; 2 | 3 | import net.lenni0451.reflect.proxy.InvocationHandler; 4 | import net.lenni0451.reflect.proxy.impl.Proxy; 5 | 6 | public abstract class Class4 implements Proxy { 7 | 8 | public InvocationHandler get() { 9 | return this.getInvocationHandler(); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/net/lenni0451/reflect/proxy/test/Interface1.java: -------------------------------------------------------------------------------- 1 | package net.lenni0451.reflect.proxy.test; 2 | 3 | public interface Interface1 { 4 | 5 | int interfaceTest(); 6 | 7 | } 8 | --------------------------------------------------------------------------------