├── .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.