├── .gitignore
├── LICENSE
├── README.md
├── analyser
├── build.gradle.kts
└── src
│ ├── main
│ ├── java
│ │ └── org
│ │ │ └── tabooproject
│ │ │ └── reflex
│ │ │ └── util
│ │ │ └── ClassHelper.java
│ └── kotlin
│ │ └── org
│ │ └── tabooproject
│ │ └── reflex
│ │ ├── AnalyseMode.kt
│ │ ├── ClassAnalyser.kt
│ │ ├── ClassAnnotation.kt
│ │ ├── ClassConstructor.kt
│ │ ├── ClassField.kt
│ │ ├── ClassMember.kt
│ │ ├── ClassMethod.kt
│ │ ├── ClassStructure.kt
│ │ ├── EnumNotFoundException.kt
│ │ ├── Internal.kt
│ │ ├── JavaClassConstructor.kt
│ │ ├── JavaClassField.kt
│ │ ├── JavaClassMethod.kt
│ │ ├── JavaClassStructure.kt
│ │ ├── Labels.kt
│ │ ├── LazyAnnotatedClass.kt
│ │ ├── LazyClass.kt
│ │ ├── LazyEnum.kt
│ │ ├── Reflection.kt
│ │ ├── Type.kt
│ │ ├── TypeNotFoundException.kt
│ │ ├── UnsafeAccess.kt
│ │ ├── asm
│ │ ├── AsmAnnotation.kt
│ │ ├── AsmClassAnnotationVisitor.kt
│ │ ├── AsmClassConstructor.kt
│ │ ├── AsmClassField.kt
│ │ ├── AsmClassFieldVisitor.kt
│ │ ├── AsmClassMethod.kt
│ │ ├── AsmClassMethodVisitor.kt
│ │ ├── AsmClassVisitor.kt
│ │ └── AsmSignature.kt
│ │ ├── reflection
│ │ ├── InstantAnnotation.kt
│ │ ├── InstantClassConstructor.kt
│ │ ├── InstantClassField.kt
│ │ └── InstantClassMethod.kt
│ │ └── serializer
│ │ ├── BinaryReader.kt
│ │ ├── BinarySerializable.kt
│ │ ├── BinarySerializer.kt
│ │ ├── BinaryWriter.kt
│ │ └── SerializationType.kt
│ └── test
│ ├── java
│ └── org
│ │ └── tabooproject
│ │ └── reflex
│ │ └── res
│ │ ├── DependencyScope.java
│ │ ├── RuntimeResource.java
│ │ └── RuntimeResources.java
│ └── kotlin
│ └── org
│ └── tabooproject
│ └── reflex
│ ├── AnalyserAnnotation.kt
│ ├── AnalyserTestAsm.kt
│ ├── AnalyserTestAsmAnnotation1.kt
│ ├── AnalyserTestAsmAnnotation2.kt
│ ├── AnalyserTestReflection.kt
│ ├── AnalyserTestReflectionAnnotation.kt
│ ├── BinaryTest.kt
│ ├── LazyClassSerialize.kt
│ └── res
│ ├── EventOrder.kt
│ ├── EventPriority.kt
│ ├── PostOrder.kt
│ └── SubscribeEvent.kt
├── build.gradle.kts
├── fast-instance-getter
├── build.gradle.kts
└── src
│ ├── main
│ └── java
│ │ └── org
│ │ └── tabooproject
│ │ └── reflex
│ │ └── FastInstGetter.java
│ └── test
│ └── kotlin
│ └── org
│ └── tabooproject
│ └── reflex
│ └── FastInstFetterTest.kt
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── reflex
├── build.gradle.kts
└── src
│ ├── main
│ └── kotlin
│ │ └── org
│ │ └── tabooproject
│ │ └── reflex
│ │ ├── Reflex.kt
│ │ ├── ReflexClass.kt
│ │ ├── ReflexClassMap.kt
│ │ └── ReflexRemapper.kt
│ └── test
│ ├── java
│ └── org
│ │ └── tabooproject
│ │ └── reflex
│ │ ├── OpenAPI.java
│ │ └── OpenResult.java
│ └── kotlin
│ └── org
│ └── tabooproject
│ └── reflex
│ ├── Asm.kt
│ ├── InvokeMethodTest.kt
│ ├── ReflexClassSerialization.kt
│ ├── ReflexClassTest.kt
│ ├── ReflexJarScanner.kt
│ ├── ReflexJava.kt
│ ├── ReflexPropertyGetter.kt
│ └── ReflexTest.kt
└── settings.gradle.kts
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | .idea
3 | build
4 | bin
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | Statement of Purpose
15 |
16 | The laws of most jurisdictions throughout the world automatically confer
17 | exclusive Copyright and Related Rights (defined below) upon the creator
18 | and subsequent owner(s) (each and all, an "owner") of an original work of
19 | authorship and/or a database (each, a "Work").
20 |
21 | Certain owners wish to permanently relinquish those rights to a Work for
22 | the purpose of contributing to a commons of creative, cultural and
23 | scientific works ("Commons") that the public can reliably and without fear
24 | of later claims of infringement build upon, modify, incorporate in other
25 | works, reuse and redistribute as freely as possible in any form whatsoever
26 | and for any purposes, including without limitation commercial purposes.
27 | These owners may contribute to the Commons to promote the ideal of a free
28 | culture and the further production of creative, cultural and scientific
29 | works, or to gain reputation or greater distribution for their Work in
30 | part through the use and efforts of others.
31 |
32 | For these and/or other purposes and motivations, and without any
33 | expectation of additional consideration or compensation, the person
34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35 | is an owner of Copyright and Related Rights in the Work, voluntarily
36 | elects to apply CC0 to the Work and publicly distribute the Work under its
37 | terms, with knowledge of his or her Copyright and Related Rights in the
38 | Work and the meaning and intended legal effect of CC0 on those rights.
39 |
40 | 1. Copyright and Related Rights. A Work made available under CC0 may be
41 | protected by copyright and related or neighboring rights ("Copyright and
42 | Related Rights"). Copyright and Related Rights include, but are not
43 | limited to, the following:
44 |
45 | i. the right to reproduce, adapt, distribute, perform, display,
46 | communicate, and translate a Work;
47 | ii. moral rights retained by the original author(s) and/or performer(s);
48 | iii. publicity and privacy rights pertaining to a person's image or
49 | likeness depicted in a Work;
50 | iv. rights protecting against unfair competition in regards to a Work,
51 | subject to the limitations in paragraph 4(a), below;
52 | v. rights protecting the extraction, dissemination, use and reuse of data
53 | in a Work;
54 | vi. database rights (such as those arising under Directive 96/9/EC of the
55 | European Parliament and of the Council of 11 March 1996 on the legal
56 | protection of databases, and under any national implementation
57 | thereof, including any amended or successor version of such
58 | directive); and
59 | vii. other similar, equivalent or corresponding rights throughout the
60 | world based on applicable law or treaty, and any national
61 | implementations thereof.
62 |
63 | 2. Waiver. To the greatest extent permitted by, but not in contravention
64 | of, applicable law, Affirmer hereby overtly, fully, permanently,
65 | irrevocably and unconditionally waives, abandons, and surrenders all of
66 | Affirmer's Copyright and Related Rights and associated claims and causes
67 | of action, whether now known or unknown (including existing as well as
68 | future claims and causes of action), in the Work (i) in all territories
69 | worldwide, (ii) for the maximum duration provided by applicable law or
70 | treaty (including future time extensions), (iii) in any current or future
71 | medium and for any number of copies, and (iv) for any purpose whatsoever,
72 | including without limitation commercial, advertising or promotional
73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74 | member of the public at large and to the detriment of Affirmer's heirs and
75 | successors, fully intending that such Waiver shall not be subject to
76 | revocation, rescission, cancellation, termination, or any other legal or
77 | equitable action to disrupt the quiet enjoyment of the Work by the public
78 | as contemplated by Affirmer's express Statement of Purpose.
79 |
80 | 3. Public License Fallback. Should any part of the Waiver for any reason
81 | be judged legally invalid or ineffective under applicable law, then the
82 | Waiver shall be preserved to the maximum extent permitted taking into
83 | account Affirmer's express Statement of Purpose. In addition, to the
84 | extent the Waiver is so judged Affirmer hereby grants to each affected
85 | person a royalty-free, non transferable, non sublicensable, non exclusive,
86 | irrevocable and unconditional license to exercise Affirmer's Copyright and
87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
88 | maximum duration provided by applicable law or treaty (including future
89 | time extensions), (iii) in any current or future medium and for any number
90 | of copies, and (iv) for any purpose whatsoever, including without
91 | limitation commercial, advertising or promotional purposes (the
92 | "License"). The License shall be deemed effective as of the date CC0 was
93 | applied by Affirmer to the Work. Should any part of the License for any
94 | reason be judged legally invalid or ineffective under applicable law, such
95 | partial invalidity or ineffectiveness shall not invalidate the remainder
96 | of the License, and in such case Affirmer hereby affirms that he or she
97 | will not (i) exercise any of his or her remaining Copyright and Related
98 | Rights in the Work or (ii) assert any associated claims and causes of
99 | action with respect to the Work, in either case contrary to Affirmer's
100 | express Statement of Purpose.
101 |
102 | 4. Limitations and Disclaimers.
103 |
104 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
105 | surrendered, licensed or otherwise affected by this document.
106 | b. Affirmer offers the Work as-is and makes no representations or
107 | warranties of any kind concerning the Work, express, implied,
108 | statutory or otherwise, including without limitation warranties of
109 | title, merchantability, fitness for a particular purpose, non
110 | infringement, or the absence of latent or other defects, accuracy, or
111 | the present or absence of errors, whether or not discoverable, all to
112 | the greatest extent permissible under applicable law.
113 | c. Affirmer disclaims responsibility for clearing rights of other persons
114 | that may apply to the Work or any use thereof, including without
115 | limitation any person's Copyright and Related Rights in the Work.
116 | Further, Affirmer disclaims responsibility for obtaining any necessary
117 | consents, permissions or other rights required for any use of the
118 | Work.
119 | d. Affirmer understands and acknowledges that Creative Commons is not a
120 | party to this document and has no duty or obligation with respect to
121 | this CC0 or use of the Work.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Reflex
2 | 
3 |
4 | ```kotlin
5 | repositories {
6 | maven { url = uri("https://repo.tabooproject.org/repository/releases/") }
7 | mavenCentral()
8 | }
9 |
10 | dependencies {
11 | // 本体
12 | implementation("org.tabooproject.reflex:analyser:{version}")
13 | implementation("org.tabooproject.reflex:fast-instance-getter:{version}")
14 | implementation("org.tabooproject.reflex:reflex:{version}") // 需要 analyser 模块
15 | // 本体依赖
16 | implementation("org.ow2.asm:asm:9.2")
17 | implementation("org.ow2.asm:asm-util:9.2")
18 | implementation("org.ow2.asm:asm-commons:9.2")
19 | implementation(kotlin("stdlib"))
20 | }
21 | ```
22 |
23 | ## analyser & reflex
24 | Reflex 为基于 Kotlin 语言开发的反射工具,其与 Java 原生反射 API 及 `kotlin-reflect` 间最大区别在于其可**无视软兼容**反射目标类中的字段或方法。
25 |
26 | 如下方代码所示,此类现象在 Bukkit 插件开发中较为常见:
27 |
28 | ```java
29 | public class AnyPlugin extends JavaPlugin {
30 |
31 | private PlayerPointsAPI api; // PlayerPoints
32 | private AnyField target;
33 | }
34 | ```
35 |
36 | 习惯于将其他插件内的接口缓存于主类的 Bukkit 开发者不在少数,因即便此时 `PlayerPoints` 并未安装,类的运行和插件的启动也不会受到影响。但此做法造成的结果是开发者无法通过反射获取该类中的任何字段:
37 |
38 | ```java
39 | Field field = Main.class.getDeclaredField("target"); // NoClassDefFoundError: PlayerPointsAPI
40 | ```
41 |
42 | 同类问题也存在于 Bukkit 监听器的注册之上。若插件运行于 1.12 版本以下的环境时,下述 `PlayerJoinEvent` 监听器将无法正常注册。
43 |
44 | ```java
45 | public class AnyListener extends Listener {
46 |
47 | @EventHandler
48 | public void onJoin(PlayerJoinEvent event) {
49 | // 全版本
50 | }
51 |
52 | @EventHandler
53 | public void onSwap(PlayerSwapItemEvent event) {
54 | // 1.12 新增
55 | }
56 | }
57 | ```
58 |
59 | 因此, Reflex 脱身于 [TabooLib](https://github.com/taboolib/taboolib) 并发展为独立类库。通过此类库,下述操作将成为可能:
60 |
61 | + 在 [AnalyserTestAsm.kt](https://github.com/TabooLib/Reflex/blob/master/analyser/src/test/kotlin/org/tabooproject/reflex/AnalyserTestAsm.kt) 中获取 `analyser` 的用法。
62 | + 在 [ReflexTest.kt](https://github.com/TabooLib/Reflex/blob/master/reflex/src/test/kotlin/org/tabooproject/reflex/ReflexTest.kt) 中获取 `reflex` 的用法。
63 |
64 | ## fast-instance-getter
65 | 基于 Java 且不依赖反射的高性能 Kotlin 单例/伴生类实例获取工具,此工具同样解决了上述问题。
66 |
67 | ```kotlin
68 | val getter = FastInstGetter(ObjectTarget::class.java.name) // 初始化损耗较高,复用时需手动缓存该实例
69 | getter.instance // 获取单例实例
70 | getter.companion // 获取伴生类实例
71 | ```
72 |
--------------------------------------------------------------------------------
/analyser/build.gradle.kts:
--------------------------------------------------------------------------------
1 | dependencies {
2 | implementation("org.ow2.asm:asm:9.2")
3 | implementation("org.ow2.asm:asm-util:9.2")
4 | implementation("org.ow2.asm:asm-commons:9.2")
5 | implementation("org.apache.commons:commons-lang3:3.5")
6 | testImplementation("org.apache.commons:commons-lang3:3.5")
7 | testImplementation(kotlin("stdlib"))
8 | testImplementation(kotlin("reflect"))
9 | }
10 |
11 | shrinking {
12 | annotation = "org.tabooproject.reflex.Internal"
13 | }
--------------------------------------------------------------------------------
/analyser/src/main/kotlin/org/tabooproject/reflex/AnalyseMode.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | enum class AnalyseMode {
4 |
5 | /**
6 | * 反射优先
7 | * 失败会尝试使用 ASM 模式
8 | * 稳定性高,但是初始化速度慢
9 | */
10 | REFLECTION_FIRST,
11 |
12 | /**
13 | * 仅反射
14 | * 失败会抛出异常
15 | */
16 | REFLECTION_ONLY,
17 |
18 | /**
19 | * ASM 优先
20 | * 失败会尝试使用反射模式
21 | * 初始化速度快,稳定性暂未知(缺少测试数据)
22 | */
23 | ASM_FIRST,
24 |
25 | /**
26 | * 仅 ASM
27 | * 失败会抛出异常
28 | */
29 | ASM_ONLY;
30 |
31 | companion object {
32 |
33 | /**
34 | * 默认使用的分析模式
35 | * 自 1.1.1 版本起,采用 ASM_FIRST 模式,以提高初始化速度
36 | */
37 | var default = ASM_FIRST
38 | }
39 | }
--------------------------------------------------------------------------------
/analyser/src/main/kotlin/org/tabooproject/reflex/ClassAnalyser.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | import org.objectweb.asm.ClassReader
4 | import org.objectweb.asm.ClassWriter
5 | import org.tabooproject.reflex.asm.AsmClassVisitor
6 | import org.tabooproject.reflex.reflection.InstantAnnotation
7 | import org.tabooproject.reflex.reflection.InstantClassConstructor
8 | import org.tabooproject.reflex.reflection.InstantClassField
9 | import org.tabooproject.reflex.reflection.InstantClassMethod
10 | import java.io.InputStream
11 |
12 | /**
13 | * @author 坏黑
14 | */
15 | object ClassAnalyser {
16 |
17 | fun analyse(clazz: Class<*>): ClassStructure {
18 | return analyse(clazz, AnalyseMode.default)
19 | }
20 |
21 | fun analyse(clazz: Class<*>, mode: AnalyseMode): ClassStructure {
22 | return when (mode) {
23 | // 反射优先
24 | AnalyseMode.REFLECTION_FIRST -> {
25 | return try {
26 | analyseByReflection(clazz)
27 | } catch (ex: Throwable) {
28 | when (ex) {
29 | is NoClassDefFoundError, is ArrayStoreException -> analyseByASM(clazz)
30 | else -> throw ex
31 | }
32 | }
33 | }
34 | // 仅反射
35 | AnalyseMode.REFLECTION_ONLY -> analyseByReflection(clazz)
36 | // ASM 优先
37 | AnalyseMode.ASM_FIRST -> {
38 | return try {
39 | analyseByASM(clazz)
40 | } catch (ex: Throwable) {
41 | when (ex) {
42 | is ClassNotFoundException -> analyseByReflection(clazz)
43 | else -> throw ex
44 | }
45 | }
46 | }
47 | // 仅 ASM
48 | AnalyseMode.ASM_ONLY -> analyseByASM(clazz)
49 | }
50 | }
51 |
52 | fun analyseByReflection(clazz: Class<*>): JavaClassStructure {
53 | val lc = LazyClass.of(clazz)
54 | val superclass = clazz.superclass?.let { LazyClass.of(it) }
55 | val interfaces = clazz.interfaces.map { LazyClass.of(it) }
56 | val annotations = clazz.declaredAnnotations.map { InstantAnnotation(it) }
57 | val fields = clazz.declaredFields.map { InstantClassField(lc, it) }
58 | val methods = clazz.declaredMethods.map { InstantClassMethod(lc, it) }
59 | val constructors = clazz.declaredConstructors.map { InstantClassConstructor(lc, it) }
60 | return JavaClassStructure(Type.REFLECTION, lc, clazz.modifiers, superclass, interfaces, annotations, fields, methods, constructors)
61 | }
62 |
63 | fun analyseByASM(clazz: Class<*>): JavaClassStructure {
64 | return analyseByASM(clazz) { Class.forName(it) }
65 | }
66 |
67 | @Suppress("FoldInitializerAndIfToElvis")
68 | fun analyseByASM(clazz: Class<*>, classFinder: ClassFinder): JavaClassStructure {
69 | val resourceAsStream = clazz.getResourceAsStream("/${clazz.name.replace('.', '/')}.class")
70 | if (resourceAsStream == null) {
71 | // 无法从资源文件中找到对应的类文件,可能来自远程加载
72 | throw ClassNotFoundException("Class ${clazz.name} not found (file not in the jar)")
73 | }
74 | return analyseByASM(LazyClass.of(clazz), resourceAsStream, classFinder)
75 | }
76 |
77 | fun analyseByASM(clazz: LazyClass, inputStream: InputStream): JavaClassStructure {
78 | return analyseByASM(clazz, inputStream) {
79 | try {
80 | Class.forName(it)
81 | } catch (ex: ClassNotFoundException) {
82 | throw ClassNotFoundException("Class \"$it\" not found, classloader ${javaClass.classLoader}")
83 | }
84 | }
85 | }
86 |
87 | fun analyseByASM(clazz: LazyClass, inputStream: InputStream, classFinder: ClassFinder): JavaClassStructure {
88 | val classReader = ClassReader(inputStream)
89 | val analyser = AsmClassVisitor(clazz, classFinder, ClassWriter(ClassWriter.COMPUTE_MAXS))
90 | classReader.accept(analyser, ClassReader.SKIP_DEBUG)
91 | return JavaClassStructure(
92 | Type.ASM, clazz, analyser.access, analyser.superclass, analyser.interfaces, analyser.annotations, analyser.fields, analyser.methods, analyser.constructors
93 | )
94 | }
95 |
96 | fun interface ClassFinder {
97 |
98 | fun findClass(name: String): Class<*>?
99 |
100 | companion object {
101 |
102 | val default = ClassFinder { Class.forName(it.replace('/', '.')) }
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/analyser/src/main/kotlin/org/tabooproject/reflex/ClassAnnotation.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | import org.objectweb.asm.Type
4 | import org.tabooproject.reflex.asm.AsmAnnotation
5 | import org.tabooproject.reflex.asm.AsmSignature
6 | import org.tabooproject.reflex.serializer.BinaryReader
7 | import org.tabooproject.reflex.serializer.BinarySerializable
8 |
9 | /**
10 | * @author 坏黑
11 | * @since 2022/1/24 7:42 PM
12 | */
13 | @Suppress("UNCHECKED_CAST")
14 | abstract class ClassAnnotation(val source: LazyClass) : BinarySerializable {
15 |
16 | abstract fun property(name: String): T?
17 |
18 | abstract fun property(name: String, def: T): T
19 |
20 | abstract fun properties(): Map
21 |
22 | abstract fun propertyKeys(): Set
23 |
24 | fun charArray(name: String): CharArray? {
25 | return property(name, null)
26 | }
27 |
28 | fun byteArray(name: String): ByteArray? {
29 | return property(name, null)
30 | }
31 |
32 | fun shortArray(name: String): ShortArray? {
33 | return property(name, null)
34 | }
35 |
36 | fun intArray(name: String): IntArray? {
37 | return property(name, null)
38 | }
39 |
40 | fun longArray(name: String): LongArray? {
41 | return property(name, null)
42 | }
43 |
44 | fun doubleArray(name: String): DoubleArray? {
45 | return property(name, null)
46 | }
47 |
48 | fun floatArray(name: String): FloatArray? {
49 | return property(name, null)
50 | }
51 |
52 | fun booleanArray(name: String): BooleanArray? {
53 | return property(name, null)
54 | }
55 |
56 | fun list(name: String): List {
57 | return property(name, arrayListOf())
58 | }
59 |
60 | fun mapList(name: String): List
68 | */
69 | POST
70 |
71 | }
--------------------------------------------------------------------------------
/analyser/src/test/kotlin/org/tabooproject/reflex/res/EventPriority.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex.res
2 |
3 | /**
4 | * TabooLib
5 | * org.tabooproject.reflex.res.EventPriority
6 | *
7 | * @author sky
8 | * @since 2021/6/16 1:07 上午
9 | */
10 | enum class EventPriority(val level: Int) {
11 |
12 | LOWEST(-64), LOW(-32), NORMAL(0), HIGH(32), HIGHEST(64), MONITOR(128)
13 | }
--------------------------------------------------------------------------------
/analyser/src/test/kotlin/org/tabooproject/reflex/res/PostOrder.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex.res
2 |
3 | /**
4 | * TabooLib
5 | * org.tabooproject.reflex.res.PostOrder
6 | *
7 | * @author sky
8 | * @since 2021/7/2 11:26 下午
9 | */
10 | enum class PostOrder {
11 |
12 | FIRST, EARLY, NORMAL, LATE, LAST
13 | }
--------------------------------------------------------------------------------
/analyser/src/test/kotlin/org/tabooproject/reflex/res/SubscribeEvent.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex.res
2 |
3 | import org.tabooproject.reflex.res.EventOrder
4 | import org.tabooproject.reflex.res.EventPriority
5 | import org.tabooproject.reflex.res.PostOrder
6 |
7 | @Target(AnnotationTarget.FUNCTION)
8 | @kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
9 | annotation class SubscribeEvent(
10 | val priority: EventPriority = EventPriority.NORMAL,
11 | val ignoreCancelled: Boolean = false,
12 | // only bungeecord platform
13 | val level: Int = -1,
14 | // only velocity
15 | val postOrder: PostOrder = PostOrder.NORMAL,
16 | // only sponge platform
17 | val order: EventOrder = EventOrder.DEFAULT,
18 | val beforeModifications: Boolean = false,
19 | // optional event
20 | val bind: String = ""
21 | )
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2 |
3 | plugins {
4 | java
5 | `maven-publish`
6 | id("org.jetbrains.kotlin.jvm") version "1.8.22" apply false
7 | id("org.tabooproject.shrinkingkt") version "1.0.6" apply false
8 | }
9 |
10 | subprojects {
11 | apply(plugin = "java-library")
12 | apply(plugin = "maven-publish")
13 | apply(plugin = "org.jetbrains.kotlin.jvm")
14 | apply(plugin = "org.tabooproject.shrinkingkt")
15 |
16 | repositories {
17 | mavenLocal()
18 | mavenCentral()
19 | }
20 |
21 | dependencies {
22 | "implementation"(kotlin("stdlib"))
23 | "testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.8.1")
24 | "testImplementation"("org.junit.jupiter:junit-jupiter-api:5.8.1")
25 | }
26 |
27 | java {
28 | withSourcesJar()
29 | }
30 |
31 | configure {
32 | sourceCompatibility = JavaVersion.VERSION_1_8
33 | targetCompatibility = JavaVersion.VERSION_1_8
34 | }
35 |
36 | tasks.withType {
37 | useJUnitPlatform()
38 | }
39 |
40 | tasks.withType {
41 | options.encoding = "UTF-8"
42 | options.compilerArgs.addAll(listOf("-XDenableSunApiLintControl"))
43 | }
44 |
45 | tasks.withType {
46 | kotlinOptions.freeCompilerArgs += listOf("-module-name", "${project.group}.${project.name}")
47 | }
48 |
49 | publishing {
50 | repositories {
51 | maven("https://repo.tabooproject.org/repository/releases") {
52 | credentials {
53 | username = project.findProperty("taboolibUsername").toString()
54 | password = project.findProperty("taboolibPassword").toString()
55 | }
56 | authentication {
57 | create("basic")
58 | }
59 | }
60 | mavenLocal()
61 | }
62 | publications {
63 | create("maven") {
64 | from(components.findByName("java"))
65 | }
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/fast-instance-getter/build.gradle.kts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TabooLib/reflex/96a4d8a5c3f3bb67d242f41b6af4d26ce0ca0235/fast-instance-getter/build.gradle.kts
--------------------------------------------------------------------------------
/fast-instance-getter/src/main/java/org/tabooproject/reflex/FastInstGetter.java:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex;
2 |
3 | /**
4 | * TabooLib用instance获取器
5 | * 采用纯粹的byte数组操作动态生成类
6 | *
7 | * @author YiMiner
8 | */
9 | public class FastInstGetter extends ClassLoader {
10 |
11 | private static final byte[] START = {-54, -2, -70, -66, 0, 0, 0, 52, 0, 33, 10, 0, 5, 0, 19, 9, 0, 20, 0, 21, 9, 0, 22, 0, 23, 7, 0, 24, 7, 0, 25, 7, 0, 26, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 18, 76, 111, 99, 97, 108, 86, 97, 114, 105, 97, 98, 108, 101, 84, 97, 98, 108, 101, 1, 0, 4, 116, 104, 105, 115, 1, 0, 33, 76, 112, 107, 117, 47, 121, 105, 109, 47, 116, 111, 111, 108, 115, 47, 107, 111, 116, 108, 105, 110, 47, 73, 110, 115, 116, 71, 101, 116, 116, 101, 114, 59, 1, 0, 11, 103, 101, 116, 73, 110, 115, 116, 97, 110, 99, 101, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 1, 0, 12, 103, 101, 116, 67, 111, 109, 112, 97, 110, 105, 111, 110, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 15, 73, 110, 115, 116, 71, 101, 116, 116, 101, 114, 46, 106, 97, 118, 97, 12, 0, 7, 0, 8, 7, 0, 27, 12, 0, 28, 0, 29, 7, 0, 30, 12, 0, 31, 0, 32, 1, 0, 31, 112, 107, 117, 47, 121, 105, 109, 47, 116, 111, 111, 108, 115, 47, 107, 111, 116, 108, 105, 110, 47, 73, 110, 115, 116, 71, 101, 116, 116, 101, 114, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 1};
12 | private static final byte[] END = {0, 33, 0, 4, 0, 5, 0, 1, 0, 6, 0, 0, 0, 3, 0, 1, 0, 7, 0, 8, 0, 1, 0, 9, 0, 0, 0, 47, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 2, 0, 10, 0, 0, 0, 6, 0, 1, 0, 0, 0, 7, 0, 11, 0, 0, 0, 12, 0, 1, 0, 0, 0, 5, 0, 12, 0, 13, 0, 0, 0, 1, 0, 14, 0, 15, 0, 1, 0, 9, 0, 0, 0, 46, 0, 1, 0, 1, 0, 0, 0, 4, -78, 0, 2, -80, 0, 0, 0, 2, 0, 10, 0, 0, 0, 6, 0, 1, 0, 0, 0, 11, 0, 11, 0, 0, 0, 12, 0, 1, 0, 0, 0, 4, 0, 12, 0, 13, 0, 0, 0, 1, 0, 16, 0, 15, 0, 1, 0, 9, 0, 0, 0, 46, 0, 1, 0, 1, 0, 0, 0, 4, -78, 0, 3, -80, 0, 0, 0, 2, 0, 10, 0, 0, 0, 6, 0, 1, 0, 0, 0, 16, 0, 11, 0, 0, 0, 12, 0, 1, 0, 0, 0, 4, 0, 12, 0, 13, 0, 0, 0, 1, 0, 17, 0, 0, 0, 2, 0, 18,};
13 | private IGetter getter = null;
14 |
15 | public FastInstGetter(String className) {
16 | className = className.replace('.', '/');
17 | String interfaceName = IGetter.class.getName().replace('.', '/');
18 | int totalMaxLength = START.length
19 | + 2 + interfaceName.length() * 3 + 1
20 | + 2 + className.length() * 3 + 1
21 | + 2 + 8 + 1
22 | + 2 + 1 + className.length() * 3 + 1 + 1
23 | + 2 + className.length() * 3 + 1
24 | + 2 + 9 + 1
25 | + 2 + 1 + className.length() * 3 + 11
26 | + END.length;
27 | byte [] synthetic = new byte[totalMaxLength];
28 | System.arraycopy(START,0,synthetic,0,START.length);
29 | int pt = encode(synthetic, START.length,interfaceName);
30 | synthetic[pt++] = 1;
31 | pt = encode(synthetic, pt, className);
32 | synthetic[pt++] = 1;
33 | pt = encode(synthetic, pt, "INSTANCE");
34 | synthetic[pt++] = 1;
35 | pt = encode(synthetic, pt, "L" + className + ";");
36 | synthetic[pt++] = 1;
37 | pt = encode(synthetic, pt, className);
38 | synthetic[pt++] = 1;
39 | pt = encode(synthetic, pt, "Companion");
40 | synthetic[pt++] = 1;
41 | pt = encode(synthetic, pt, "L" + className + "$Companion;");
42 | System.arraycopy(END,0,synthetic,pt,END.length);
43 | pt += END.length;
44 | Class> clazz = this.defineClass("pku.yim.tools.kotlin.InstGetter", synthetic, 0, pt);
45 | try {
46 | getter = (IGetter) clazz.newInstance();
47 | } catch (Exception e) {
48 | e.printStackTrace();
49 | }
50 | }
51 |
52 | private static int encode(byte [] data, int index, String stringValue) {
53 | int charLength = stringValue.length();
54 | if (charLength > 65535) {
55 | throw new IllegalArgumentException("UTF8 string too large");
56 | } else {
57 | int tmpIndex = index;
58 | data[index++] = (byte) (charLength >>> 8);
59 | data[index++] = (byte) (charLength);
60 | for (int i = 0; i < charLength; ++i) {
61 | char charValue = stringValue.charAt(i);
62 | if (charValue < 1 || charValue > 127) {
63 | return encodeUtf8(data, stringValue, tmpIndex, i);
64 | }
65 | data[index++] = ((byte) charValue);
66 | }
67 | }
68 | return index;
69 | }
70 |
71 | public static int encodeUtf8(byte[] data, String stringValue, int index, int offset) {
72 | int charLength = stringValue.length();
73 | int byteLength = offset;
74 |
75 | int byteLengthOffset;
76 | for (byteLengthOffset = offset; byteLengthOffset < charLength; ++byteLengthOffset) {
77 | char charValue = stringValue.charAt(byteLengthOffset);
78 | if (charValue >= 1 && charValue <= 127) {
79 | ++byteLength;
80 | } else if (charValue <= 2047) {
81 | byteLength += 2;
82 | } else {
83 | byteLength += 3;
84 | }
85 | }
86 |
87 | if (byteLength > 65535) {
88 | throw new IllegalArgumentException("UTF8 string too large");
89 | } else {
90 | data[index++] = (byte) (byteLength >>> 8);
91 | data[index++] = (byte) byteLength;
92 | index += offset;
93 | for (int i = offset; i < charLength; ++i) {
94 | char charValue = stringValue.charAt(i);
95 | if (charValue >= 1 && charValue <= 127) {
96 | data[index++] = (byte) charValue;
97 | } else if (charValue <= 2047) {
98 | data[index++] = (byte) (192 | charValue >> 6 & 31);
99 | data[index++] = (byte) (128 | charValue & 63);
100 | } else {
101 | data[index++] = (byte) (224 | charValue >> 12 & 15);
102 | data[index++] = (byte) (128 | charValue >> 6 & 63);
103 | data[index++] = (byte) (128 | charValue & 63);
104 | }
105 | }
106 | return index;
107 | }
108 | }
109 |
110 | public Object getInstance() {
111 | return getter == null ? null : getter.getInstance();
112 | }
113 |
114 | public Object getCompanion() {
115 | return getter == null ? null : getter.getCompanion();
116 | }
117 |
118 | public interface IGetter {
119 | Object getInstance();
120 |
121 | Object getCompanion();
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/fast-instance-getter/src/test/kotlin/org/tabooproject/reflex/FastInstFetterTest.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | import org.junit.jupiter.api.Test
4 |
5 | /**
6 | * @author 坏黑
7 | * @since 2022/1/22 4:03 AM
8 | */
9 | class FastInstFetterTest {
10 |
11 | object ObjectTarget
12 |
13 | class CompanionTarget {
14 |
15 | companion object
16 | }
17 |
18 | @Test
19 | fun testObjectInstance() {
20 | assert(ObjectTarget == FastInstGetter(ObjectTarget::class.java.name).instance)
21 | }
22 |
23 | @Test
24 | fun testCompanionInstance() {
25 | assert(CompanionTarget.Companion == FastInstGetter(CompanionTarget::class.java.name).companion)
26 | }
27 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | group=org.tabooproject.reflex
2 | version=1.2.0
3 | kotlin.incremental=true
4 | kotlin.incremental.java=true
5 | kotlin.caching.enabled=true
6 | kotlin.parallel.tasks.in.project=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TabooLib/reflex/96a4d8a5c3f3bb67d242f41b6af4d26ce0ca0235/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/reflex/build.gradle.kts:
--------------------------------------------------------------------------------
1 | dependencies {
2 | implementation(project(":analyser"))
3 | testImplementation("org.ow2.asm:asm:9.2")
4 | testImplementation("org.ow2.asm:asm-util:9.2")
5 | testImplementation("org.ow2.asm:asm-commons:9.2")
6 | testImplementation(kotlin("test"))
7 | }
--------------------------------------------------------------------------------
/reflex/src/main/kotlin/org/tabooproject/reflex/Reflex.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | @Suppress("UNCHECKED_CAST")
4 | class Reflex {
5 |
6 | /**
7 | * 为了兼容性,保持原有的形势
8 | */
9 | @Suppress("FunctionName")
10 | companion object {
11 |
12 | /**
13 | * 已注册的 ReflexRemapper
14 | * 直接添加到改容器即可完成注册,起初用于转换 1.17 版本的混淆字段名称
15 | */
16 | val remapper = ArrayList()
17 |
18 | /**
19 | * 不通过构造函数实例化对象
20 | */
21 | fun Class.unsafeInstance(): Any {
22 | return UnsafeAccess.unsafe.allocateInstance(this)!!
23 | }
24 |
25 | /**
26 | * 通过构造方法实例化对象
27 | */
28 | fun Class.invokeConstructor(vararg parameter: Any?): T {
29 | return ReflexClass.of(this).newInstance(*parameter) as T
30 | }
31 |
32 | /**
33 | * 通过构造方法实例化对象
34 | */
35 | fun Class.invokeConstructor(mode: AnalyseMode, vararg parameter: Any?): T {
36 | return ReflexClass.of(this, mode).newInstance(*parameter) as T
37 | }
38 |
39 | /**
40 | * 执行方法
41 | * 为 Java 调用提供便利,不查父类,不重映射
42 | */
43 | fun Any.invokeLocalMethod(name: String, vararg parameter: Any?): T? {
44 | return invokeMethod(name, *parameter, false, false, false, AnalyseMode.default)
45 | }
46 |
47 | /**
48 | * 执行方法
49 | * @param name 方法名称
50 | * @param parameter 方法参数
51 | * @param isStatic 是否为静态方法
52 | * @param findToParent 是否查找父类方法
53 | * @param remap 是否应用重映射
54 | * @param mode 分析模式
55 | */
56 | fun Any.invokeMethod(
57 | name: String,
58 | vararg parameter: Any?,
59 | isStatic: Boolean = false,
60 | findToParent: Boolean = true,
61 | remap: Boolean = true,
62 | mode: AnalyseMode = AnalyseMode.default
63 | ): T? {
64 | return if (isStatic && this is Class<*>) {
65 | ReflexClass.of(this, mode).getMethod(name, findToParent, remap, *parameter).invokeStatic(*parameter) as T?
66 | } else {
67 | ReflexClass.of(javaClass, mode).getMethod(name, findToParent, remap, *parameter).invoke(this, *parameter) as T?
68 | }
69 | }
70 |
71 | /**
72 | * 获取字段
73 | * 为 Java 调用提供便利,不查父类,不重映射
74 | */
75 | fun Any.getLocalProperty(name: String): T? {
76 | return getProperty(name, false, findToParent = false, remap = false, mode = AnalyseMode.default)
77 | }
78 |
79 | /**
80 | * 获取字段
81 | * @param path 字段名称,使用 "/" 符号进行递归获取
82 | * @param isStatic 是否为静态字段
83 | * @param findToParent 是否查找父类字段
84 | * @param remap 是否应用重映射
85 | * @param mode 分析模式
86 | */
87 | fun Any.getProperty(
88 | path: String,
89 | isStatic: Boolean = false,
90 | findToParent: Boolean = true,
91 | remap: Boolean = true,
92 | mode: AnalyseMode = AnalyseMode.default
93 | ): T? {
94 | return if (path.contains('/')) {
95 | val left = path.substringBefore('/')
96 | val right = path.substringAfter('/')
97 | _get(left, isStatic, findToParent, remap, mode)?.getProperty(right, isStatic, findToParent, remap, mode)
98 | } else {
99 | _get(path, isStatic, findToParent, remap, mode)
100 | }
101 | }
102 |
103 | /**
104 | * 修改字段
105 | * 为 Java 调用提供便利,不查父类,不重映射
106 | */
107 | fun Any.setLocalProperty(name: String, value: Any?) {
108 | setProperty(name, value, false, findToParent = false, remap = false, mode = AnalyseMode.default)
109 | }
110 |
111 | /**
112 | * 修改字段
113 | * @param path 字段名称,使用 "/" 符号进行递归获取
114 | * @param value 值
115 | * @param isStatic 是否为静态字段
116 | * @param findToParent 是否查找到父类字段
117 | * @param remap 是否应用重映射
118 | * @param mode 分析模式
119 | */
120 | fun Any.setProperty(
121 | path: String,
122 | value: Any?,
123 | isStatic: Boolean = false,
124 | findToParent: Boolean = true,
125 | remap: Boolean = true,
126 | mode: AnalyseMode = AnalyseMode.default
127 | ) {
128 | if (path.contains('/')) {
129 | val left = path.substringBefore('/')
130 | val right = path.substringAfter('/')
131 | _get(left, isStatic, findToParent, remap, mode)?.setProperty(right, value, isStatic, findToParent, remap, mode)
132 | } else {
133 | _set(path, value, isStatic, findToParent, remap, mode)
134 | }
135 | }
136 |
137 | private fun Any._get(
138 | name: String,
139 | isStatic: Boolean = false,
140 | findToParent: Boolean = true,
141 | remap: Boolean = true,
142 | mode: AnalyseMode = AnalyseMode.default
143 | ): T? {
144 | return if (isStatic && this is Class<*>) {
145 | ReflexClass.of(this, mode).getField(name, findToParent, remap).get() as T?
146 | } else {
147 | ReflexClass.of(javaClass, mode).getField(name, findToParent, remap).get(this) as T?
148 | }
149 | }
150 |
151 | private fun Any._set(
152 | name: String,
153 | value: Any?,
154 | isStatic: Boolean = false,
155 | findToParent: Boolean = true,
156 | remap: Boolean = true,
157 | mode: AnalyseMode = AnalyseMode.default
158 | ) {
159 | if (isStatic && this is Class<*>) {
160 | ReflexClass.of(this, mode).getField(name, findToParent, remap).setStatic(value)
161 | } else {
162 | ReflexClass.of(javaClass, mode).getField(name, findToParent, remap).set(this, value)
163 | }
164 | }
165 | }
166 | }
--------------------------------------------------------------------------------
/reflex/src/main/kotlin/org/tabooproject/reflex/ReflexClass.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | import org.tabooproject.reflex.serializer.BinaryReader
4 | import org.tabooproject.reflex.serializer.BinarySerializable
5 | import org.tabooproject.reflex.serializer.BinaryWriter
6 | import java.io.InputStream
7 | import java.util.concurrent.ConcurrentHashMap
8 |
9 | /**
10 | * @author 坏黑
11 | * @since 2022/1/22 2:25 AM
12 | */
13 | class ReflexClass(val structure: ClassStructure, val mode: AnalyseMode) : BinarySerializable {
14 |
15 | /** 类名 */
16 | val name = structure.name
17 |
18 | /** 简单类名 */
19 | val simpleName = structure.simpleName
20 |
21 | /** 父类 */
22 | val superclass by lazy(LazyThreadSafetyMode.NONE) {
23 | val superclass = structure.superclass
24 | if (superclass != null && superclass != Any::class.java) of(superclass.instance ?: superclass.notfound(), mode) else null
25 | }
26 |
27 | /** 接口 */
28 | val interfaces by lazy(LazyThreadSafetyMode.NONE) {
29 | structure.interfaces.map { of(it.instance ?: it.notfound(), mode) }
30 | }
31 |
32 | /** 单例字段 */
33 | private val singletonField by lazy(LazyThreadSafetyMode.NONE) { getLocalField("INSTANCE") }
34 |
35 | /** 单例实例 */
36 | private var singletonInstance: Any? = null
37 |
38 | /** 单例实例是否已被初始化 */
39 | private var isSingletonInstanceInitialized = false
40 |
41 | /**
42 | * 是否为伴生类
43 | * 并非基于 Kotlin API,因此此方法不支持识别改过名字的伴生类
44 | * ```
45 | * companion object CustomCompanionName // 不受支持
46 | * ```
47 | */
48 | fun isCompanion(): Boolean {
49 | return simpleName?.endsWith("\$Companion") == true
50 | }
51 |
52 | /**
53 | * 是否为单例类
54 | * 通过检查是否存在 `INSTANCE` 字段来判断
55 | */
56 | fun isSingleton(): Boolean {
57 | return try {
58 | singletonField.isStatic
59 | } catch (ex: NoSuchFieldException) {
60 | false
61 | }
62 | }
63 |
64 | /**
65 | * 获取实例
66 | */
67 | fun getInstance(): Any? {
68 | return getInstance { Class.forName(it) }
69 | }
70 |
71 | /**
72 | * 获取实例
73 | * 伴生类或单例类则返回对应的实例,如果不是则返回 null
74 | */
75 | @Synchronized
76 | fun getInstance(classFinder: ClassAnalyser.ClassFinder): Any? {
77 | if (isSingletonInstanceInitialized) return singletonInstance
78 | isSingletonInstanceInitialized = true
79 | singletonInstance = when {
80 | // 伴生类
81 | isCompanion() -> {
82 | name ?: error("Unknown class name.")
83 | val parentClass = of(classFinder.findClass(name.substringBeforeLast('$'))!!, mode)
84 | parentClass.getLocalField("Companion").get()
85 | }
86 | // 单例类
87 | isSingleton() -> singletonField.get()
88 | // 其他
89 | else -> null
90 | }
91 | return singletonInstance
92 | }
93 |
94 | /**
95 | * 创建实例
96 | */
97 | fun newInstance(vararg parameter: Any?): Any? {
98 | // 经过测试,对于无参构造器,Java Reflect 速度更快
99 | return if (parameter.isEmpty()) {
100 | structure.owner.instance?.getDeclaredConstructor()?.newInstance()
101 | } else {
102 | getConstructor(*parameter).instance(*parameter)
103 | }
104 | }
105 |
106 | /**
107 | * 是否实现了接口(在本类中)
108 | */
109 | fun hasInterface(ic: Class<*>): Boolean {
110 | val name = ic.name
111 | return structure.interfaces.any { it.name == name }
112 | }
113 |
114 | /**
115 | * 注解是否存在
116 | */
117 | fun hasAnnotation(annotation: Class): Boolean {
118 | return structure.isAnnotationPresent(annotation)
119 | }
120 |
121 | /**
122 | * 获取注解信息
123 | * 如果不存在则抛出异常
124 | */
125 | fun getAnnotation(annotation: Class): ClassAnnotation {
126 | return structure.getAnnotation(annotation)
127 | }
128 |
129 | /**
130 | * 获取注解信息
131 | * 如果不存在则返回 null
132 | */
133 | fun getAnnotationIfPresent(annotation: Class): ClassAnnotation? {
134 | return if (hasAnnotation(annotation)) getAnnotation(annotation) else null
135 | }
136 |
137 | /**
138 | * 获取构造器
139 | * @param parameter 构造器参数
140 | */
141 | fun getConstructor(vararg parameter: Any?): ClassConstructor {
142 | return structure.getConstructor(*parameter)
143 | }
144 |
145 | /**
146 | * 获取字段
147 | * 不查父类,不重映射
148 | */
149 | fun getLocalField(name: String): ClassField {
150 | return getField(name, findToParent = false, remap = false)
151 | }
152 |
153 | /**
154 | * 获取字段
155 | * @param name 字段名
156 | * @param findToParent 是否向上查找父类
157 | * @param remap 是否应用重映射
158 | */
159 | fun getField(name: String, findToParent: Boolean = true, remap: Boolean = true): ClassField {
160 | var fixed = name
161 | if (remap) {
162 | Reflex.remapper.forEach { fixed = it.field(structure.name ?: return@forEach, fixed) }
163 | }
164 | return try {
165 | structure.getField(fixed)
166 | } catch (ex: NoSuchFieldException) {
167 | if (findToParent) {
168 | superclass?.getField(name, true, remap) ?: throw ex
169 | } else {
170 | throw ex
171 | }
172 | }
173 | }
174 |
175 | /**
176 | * 获取方法
177 | * 不查父类,不重映射
178 | */
179 | fun getLocalMethod(name: String, vararg parameter: Any?): ClassMethod {
180 | return getMethod(name, findToParent = false, remap = false, *parameter)
181 | }
182 |
183 | /**
184 | * 获取方法
185 | * @param name 方法名
186 | * @param findToParent 是否向上查找父类
187 | * @param remap 是否应用重映射
188 | * @param parameter 方法参数
189 | */
190 | fun getMethod(name: String, findToParent: Boolean = true, remap: Boolean = true, vararg parameter: Any?): ClassMethod {
191 | var fixed = name
192 | if (remap) {
193 | Reflex.remapper.forEach { fixed = it.method(structure.name ?: return@forEach, fixed, *parameter) }
194 | }
195 | return try {
196 | structure.getMethod(fixed, *parameter)
197 | } catch (ex: NoSuchMethodException) {
198 | if (findToParent) {
199 | try {
200 | superclass?.getMethod(name, true, remap, *parameter) ?: throw ex
201 | } catch (ex: NoSuchMethodException) {
202 | interfaces.forEach { runCatching { return it.getMethod(name, true, remap, *parameter) } }
203 | throw ex
204 | }
205 | } else {
206 | throw ex
207 | }
208 | }
209 | }
210 |
211 | /**
212 | * 获取当前类的 [Class] 对象
213 | */
214 | fun toClass(): Class {
215 | return structure.owner.instance ?: error("Class not found: $name")
216 | }
217 |
218 | /**
219 | * 获取当前类的 [Class] 对象,可为空
220 | */
221 | fun toClassOrNull(): Class? {
222 | return structure.owner.instance
223 | }
224 |
225 | override fun toString(): String {
226 | return "ReflexClass $mode($name)"
227 | }
228 |
229 | override fun writeTo(writer: BinaryWriter) {
230 | writer.writeObj(structure)
231 | }
232 |
233 | companion object {
234 |
235 | val reflexClassCacheMap = ConcurrentHashMap()
236 |
237 | fun of(clazz: Class<*>): ReflexClass {
238 | return of(clazz, true)
239 | }
240 |
241 | fun of(clazz: Class<*>, mode: AnalyseMode): ReflexClass {
242 | return of(clazz, mode, true)
243 | }
244 |
245 | fun of(clazz: Class<*>, saving: Boolean): ReflexClass {
246 | return of(clazz, AnalyseMode.default, saving)
247 | }
248 |
249 | fun of(clazz: Class<*>, mode: AnalyseMode, saving: Boolean): ReflexClass {
250 | if (saving && reflexClassCacheMap.containsKey(clazz.name)) {
251 | return reflexClassCacheMap[clazz.name]!!
252 | }
253 | return ReflexClass(ClassAnalyser.analyse(clazz, mode), mode).also {
254 | if (saving) {
255 | reflexClassCacheMap[clazz.name] = it
256 | }
257 | }
258 | }
259 |
260 | fun of(clazz: LazyClass, inputStream: InputStream): ReflexClass {
261 | return of(clazz, inputStream, true)
262 | }
263 |
264 | fun of(clazz: LazyClass, inputStream: InputStream, saving: Boolean): ReflexClass {
265 | if (saving && reflexClassCacheMap.containsKey(clazz.name)) {
266 | return reflexClassCacheMap[clazz.name]!!
267 | }
268 | return ReflexClass(ClassAnalyser.analyseByASM(clazz, inputStream), AnalyseMode.ASM_ONLY).also {
269 | if (saving) {
270 | reflexClassCacheMap[clazz.name] = it
271 | }
272 | }
273 | }
274 |
275 | fun of(reader: BinaryReader, classFinder: ClassAnalyser.ClassFinder? = null): ReflexClass {
276 | return ReflexClass(JavaClassStructure.of(reader, classFinder), AnalyseMode.ASM_ONLY)
277 | }
278 | }
279 | }
--------------------------------------------------------------------------------
/reflex/src/main/kotlin/org/tabooproject/reflex/ReflexClassMap.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | import org.tabooproject.reflex.serializer.BinaryReader
4 | import org.tabooproject.reflex.serializer.BinaryWriter
5 |
6 | /**
7 | * 基于 ByteBuffer 的二进制序列化工具
8 | * 以避免每次启动时重新加载所有类导致的性能问题
9 | *
10 | * 目前仅支持对 ASM 模式进行序列化
11 | */
12 | object ReflexClassMap {
13 |
14 | const val VERSION = 0
15 |
16 | fun serializeToBytes(map: Map): ByteArray {
17 | val writer = BinaryWriter()
18 | writer.writeInt(VERSION)
19 | writer.writeInt(map.size)
20 | map.forEach { (k, v) ->
21 | try {
22 | writer.writeNullableString(k)
23 | writer.writeObj(v)
24 | } catch (ex: Throwable) {
25 | println("Failed to serialize class $k")
26 | throw ex
27 | }
28 | }
29 | return writer.toByteArray()
30 | }
31 |
32 | fun deserializeFromBytes(bytes: ByteArray, classFinder: ClassAnalyser.ClassFinder?): Map {
33 | val map = mutableMapOf()
34 | val reader = BinaryReader(bytes)
35 | // 读取版本号
36 | val version = reader.readInt()
37 | if (version != VERSION) {
38 | throw IllegalArgumentException("Unsupported version: $version")
39 | }
40 | repeat(reader.readInt()) {
41 | val name = reader.readNullableString()!!
42 | try {
43 | map[name] = ReflexClass.of(reader, classFinder)
44 | } catch (ex: Throwable) {
45 | println("Failed to deserialize class $name")
46 | throw ex
47 | }
48 | }
49 | return map
50 | }
51 | }
--------------------------------------------------------------------------------
/reflex/src/main/kotlin/org/tabooproject/reflex/ReflexRemapper.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | /**
4 | * @author sky
5 | * @since 2021/6/18 1:56 下午
6 | */
7 | interface ReflexRemapper {
8 |
9 | /**
10 | * @param name net.minecraft.server.v1_16_R1.EntityPlayer
11 | * @param field a
12 | */
13 | fun field(name: String, field: String): String
14 |
15 | /**
16 | * @param name net.minecraft.server.v1_16_R1.EntityPlayer
17 | * @param method a
18 | */
19 | fun method(name: String, method: String, vararg parameter: Any?): String
20 | }
--------------------------------------------------------------------------------
/reflex/src/test/java/org/tabooproject/reflex/OpenAPI.java:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import java.util.Arrays;
6 |
7 | /**
8 | * TabooLib
9 | * taboolib.common.org.tabooproject.reflex.OpenAPI
10 | *
11 | * @author sky
12 | * @since 2021/7/2 10:37 下午
13 | */
14 | public class OpenAPI {
15 |
16 | Object[] data;
17 |
18 | public OpenAPI(Object[] data) {
19 | this.data = data;
20 | }
21 |
22 | @NotNull
23 | public static OpenResult call(String name, Object[] data) {
24 | System.out.println("OpenAPI call " + name + " " + Arrays.toString(data));
25 | return null;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/reflex/src/test/java/org/tabooproject/reflex/OpenResult.java:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 | import org.jetbrains.annotations.Nullable;
5 |
6 | public class OpenResult {
7 |
8 | private final boolean successful;
9 | private final Object value;
10 |
11 | public OpenResult(boolean successful, @Nullable Object value) {
12 | this.successful = successful;
13 | this.value = value;
14 | }
15 |
16 | /**
17 | * 是否成功
18 | *
19 | * @return boolean 是否成功
20 | */
21 | public boolean isSuccessful() {
22 | return successful;
23 | }
24 |
25 | /**
26 | * 是否失败
27 | *
28 | * @return boolean 是否失败
29 | */
30 | public boolean isFailed() {
31 | return !successful;
32 | }
33 |
34 | /**
35 | * 获取返回值
36 | *
37 | * @return Object 返回值
38 | */
39 | @Nullable
40 | public Object getValue() {
41 | return value;
42 | }
43 |
44 | /**
45 | * 创建一个成功的 org.tabooproject.reflex.OpenResult
46 | *
47 | * @return org.tabooproject.reflex.OpenResult
48 | */
49 | @NotNull
50 | public static OpenResult successful() {
51 | return new OpenResult(true, null);
52 | }
53 |
54 | /**
55 | * 创建一个带有返回值的成功的 org.tabooproject.reflex.OpenResult
56 | *
57 | * @param value 返回值
58 | * @return org.tabooproject.reflex.OpenResult
59 | */
60 | @NotNull
61 | public static OpenResult successful(@Nullable Object value) {
62 | return new OpenResult(true, value);
63 | }
64 |
65 | /**
66 | * 创建一个失败的 org.tabooproject.reflex.OpenResult
67 | *
68 | * @return org.tabooproject.reflex.OpenResult
69 | */
70 | @NotNull
71 | public static OpenResult failed() {
72 | return new OpenResult(false, null);
73 | }
74 |
75 | /**
76 | * 从其他插件的 org.tabooproject.reflex.OpenResult 转换为当前插件的 org.tabooproject.reflex.OpenResult
77 | */
78 | public static OpenResult cast(Object source) {
79 | Object successful = Reflex.Companion.getLocalProperty(source, "successful");
80 | Object value = Reflex.Companion.getLocalProperty(source, "value");
81 | return new OpenResult(Boolean.TRUE.equals(successful), value);
82 | }
83 | }
--------------------------------------------------------------------------------
/reflex/src/test/kotlin/org/tabooproject/reflex/Asm.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | import org.junit.jupiter.api.Test
4 | import org.objectweb.asm.Type
5 | import org.tabooproject.reflex.Asm.TypeTest
6 | import org.tabooproject.reflex.asm.AsmSignature
7 | import org.tabooproject.reflex.serializer.BinaryReader
8 | import org.tabooproject.reflex.serializer.BinaryWriter
9 | import kotlin.reflect.KClass
10 |
11 | @TypeTest(String::class)
12 | class Asm {
13 |
14 | annotation class TypeTest(val value: KClass<*>)
15 |
16 | private val test = arrayOf(intArrayOf(1, 2))
17 | private val bool = false
18 |
19 | fun test(): Boolean {
20 | return true
21 | }
22 |
23 | fun empty() {
24 | }
25 |
26 | class VoidConstructor(empty: Void)
27 |
28 | class Obj(val type: BaseType)
29 |
30 | open class BaseType
31 |
32 | class SubType : BaseType()
33 |
34 | @Test
35 | fun testAsm() {
36 | val rClass = ReflexClass.of(Asm::class.java, saving = false)
37 | val type = rClass.getAnnotation(TypeTest::class.java).property("value")!!
38 | println(type.descriptor)
39 | println(type.className)
40 | println(type.dimensions)
41 | println(type.sort)
42 | }
43 |
44 | @Test
45 | fun testAsm2() {
46 | val rClass = ReflexClass.of(Asm::class.java, saving = false)
47 | val field = rClass.getField("test")
48 | println(field.type.name) // I
49 | println(field.fieldType) // [[I
50 | println(test::class.java) // [[I
51 | println(test::class.java.getArrayDimensions()) // 2
52 | }
53 |
54 | @Test
55 | fun testAsm3() {
56 | val rClass = ReflexClass.of(Asm::class.java, saving = false)
57 | val field = rClass.getMethod("test")
58 | println(field.result.name) // Z
59 | println(field.returnType) // boolean
60 | println(field.result.isPrimitive)
61 |
62 | val writer = BinaryWriter()
63 | field.result.writeTo(writer)
64 | val toByteArray = writer.toByteArray()
65 | val of = LazyClass.of(BinaryReader(toByteArray), null)
66 | println(of)
67 | println(of.isPrimitive)
68 | }
69 |
70 | @Test
71 | fun testAsm4() {
72 | val rClass = ReflexClass.of(Asm::class.java, saving = false)
73 | val writer = BinaryWriter()
74 | rClass.writeTo(writer)
75 | val toByteArray = writer.toByteArray()
76 | val nClass = ReflexClass.of(BinaryReader(toByteArray), null)
77 | println(nClass)
78 | val field = nClass.getField("bool")
79 | println(field.type.name)
80 | println(field.fieldType)
81 | }
82 |
83 | @Test
84 | fun testAsm5() {
85 | val rClass = ReflexClass.of(Asm::class.java, saving = false)
86 | val field = rClass.getMethod("empty")
87 | println(field.result.name) // V
88 | println(field.result.isPrimitive) // true
89 | println(field.returnType) // void
90 | }
91 |
92 | @Test
93 | fun testAsm6() {
94 | val rClass = ReflexClass.of(Asm::class.java, saving = false)
95 | val constructor = rClass.structure.constructors[0]
96 | constructor.parameter.forEach {
97 | println(it)
98 | println(it.name)
99 | println(it.instance)
100 | }
101 | }
102 |
103 | @Test
104 | fun testAsm7() {
105 | val rClass = ReflexClass.of(Obj::class.java, saving = false)
106 | val constructor = rClass.structure.constructors[0]
107 | println(constructor)
108 | constructor.parameter.forEach { t ->
109 | println("----")
110 | println(t)
111 | println(t.name)
112 | println(t.instance)
113 | }
114 | println("====")
115 |
116 | println(rClass.structure.getConstructorByType(BaseType::class.java))
117 | println(rClass.getConstructor(BaseType()))
118 | println(rClass.getConstructor(SubType()))
119 | }
120 |
121 | @Test
122 | fun testArray() {
123 | println(AsmSignature.signatureToClass("[[I")) // [LazyClass([[int)]
124 | }
125 | }
--------------------------------------------------------------------------------
/reflex/src/test/kotlin/org/tabooproject/reflex/InvokeMethodTest.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | import org.junit.jupiter.api.Test
4 | import kotlin.time.ExperimentalTime
5 | import kotlin.time.measureTime
6 |
7 | class InvokeMethodTest {
8 |
9 | object Obj {
10 |
11 | fun func(): String {
12 | return "ok"
13 | }
14 | }
15 |
16 | @OptIn(ExperimentalTime::class)
17 | @Test
18 | fun testInvokeMethod() {
19 | val reflexClass = ReflexClass.of(Obj::class.java, AnalyseMode.REFLECTION_ONLY)
20 | val func = reflexClass.getMethod("func")
21 | println(func)
22 | println(func.invoke(Obj))
23 |
24 | val time = measureTime {
25 | repeat(1000000) {
26 | func.invoke(Obj)
27 | }
28 | }
29 | println(time)
30 | }
31 | }
--------------------------------------------------------------------------------
/reflex/src/test/kotlin/org/tabooproject/reflex/ReflexClassSerialization.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | import org.junit.jupiter.api.Test
4 | import org.tabooproject.reflex.serializer.BinaryReader
5 | import org.tabooproject.reflex.serializer.BinarySerializer
6 | import org.tabooproject.reflex.serializer.BinaryWriter
7 | import kotlin.time.ExperimentalTime
8 | import kotlin.time.measureTime
9 |
10 | class ReflexClassSerialization {
11 |
12 | @OptIn(ExperimentalTime::class)
13 | @Test
14 | fun testAnalyze() {
15 | // 解析
16 | println("解析 ${measureTime { ReflexClass.of(ReflexClassSerialization::class.java, saving = false) }}")
17 | // println("100000 次解析 ${measureTime { repeat(100000) { ReflexClass.of(ReflexClassSerialization::class.java, saving = false) } }}")
18 |
19 | // JFR 监控
20 | // val time: Duration
21 | // Recording(Configuration.getConfiguration("profile")).use { recording ->
22 | // recording.start()
23 | //
24 | // time = measureTime { repeat(100000) { ReflexClass.of(LazyClassSerialize::class.java, saving = false) } }
25 | //
26 | // // 停止记录并保存
27 | // recording.stop()
28 | // recording.dump(Path.of("analyze-profile.jfr"))
29 | // }
30 | // println("十万次解析完成 $time")
31 | }
32 |
33 | @OptIn(ExperimentalTime::class)
34 | @Test
35 | fun testClass() {
36 | // 写入
37 | val lazyClass = ReflexClass.of(ReflexClassSerialization::class.java, saving = false)
38 | val writer = BinaryWriter()
39 | lazyClass.writeTo(writer)
40 | println("写入 ${measureTime { lazyClass.writeTo(BinaryWriter()) }}")
41 | // println("10000 次写入 ${measureTime { repeat(10000) { lazyClass.writeTo(BinaryWriter()) } }}")
42 |
43 | // 读取
44 | val byteArray = writer.toByteArray()
45 | println("读取 ${measureTime { ReflexClass.of(BinaryReader.from(byteArray)) }}")
46 | // println("10000 次读取 ${measureTime { repeat(10000) { ReflexClass.of(BinaryReader.from(byteArray)) } }}")
47 | }
48 |
49 | @OptIn(ExperimentalTime::class)
50 | @Test
51 | fun testJfr() {
52 | // 写入
53 | val lazyClass = ReflexClass.of(ReflexClassSerialization::class.java, saving = false)
54 | val writer = BinaryWriter()
55 | lazyClass.writeTo(writer)
56 | val byteArray = writer.toByteArray()
57 |
58 | // JFR 监控
59 | // val time: Duration
60 | // Recording(Configuration.getConfiguration("profile")).use { recording ->
61 | // recording.start()
62 | //
63 | // time = measureTime { repeat(1000000) { ReflexClass.of(BinaryReader.from(byteArray)) } }
64 | //
65 | // // 停止记录并保存
66 | // recording.stop()
67 | // recording.dump(Path.of("serialization-profile.jfr"))
68 | // }
69 | // println("一百万次读取完成 $time")
70 | }
71 |
72 | @Test
73 | fun testClass1() {
74 | val lazyClass = ReflexClass.of(ReflexClassSerialization::class.java, saving = false)
75 | val first = lazyClass.structure.annotations.first()
76 |
77 | val writer = BinaryWriter()
78 | val properties = first.properties()
79 | writer.writeInt(properties.size)
80 | println("propertyMap size: ${properties.size}")
81 | properties.forEach { (k, v) ->
82 | writer.writeNullableString(k)
83 | println("propertyMap write key: $k")
84 |
85 | BinarySerializer.writeTo(writer.output, v)
86 | println("propertyMap write value: $v (${BinarySerializer.getSerializationType(v)})")
87 | if (v is List<*>) {
88 | println(" list size: ${v.size}")
89 | if (v[0] is String) {
90 | println(" list first element len: ${(v[0] as String).length}")
91 | }
92 | }
93 | }
94 |
95 | println("-".repeat(30))
96 |
97 | val reader = BinaryReader.from(writer.toByteArray())
98 | val size = reader.readInt()
99 | println("propertyMap size: $size")
100 | repeat(size) {
101 | println("propertyMap key: ${reader.readNullableString()}")
102 | val value = BinarySerializer.readFrom(reader, null)
103 | println("propertyMap value: ${value}")
104 | if (value is List<*>) {
105 | println(" list size: ${value.size}")
106 | if (value[0] is String) {
107 | println(" list first element len: ${(value[0] as String).length}")
108 | }
109 | }
110 | }
111 | }
112 |
113 | @Test
114 | fun testMap() {
115 | val map = mapOf("a" to listOf("a".repeat(60)), "b" to listOf("b".repeat(60)))
116 | val writer = BinaryWriter()
117 | writer.writeAnnotationProperties(map)
118 | val reader = BinaryReader.from(writer.toByteArray())
119 | val readMap = reader.readAnnotationProperties(null)
120 | println(readMap)
121 | }
122 | }
--------------------------------------------------------------------------------
/reflex/src/test/kotlin/org/tabooproject/reflex/ReflexClassTest.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | import org.junit.jupiter.api.Test
4 | import java.lang.IllegalStateException
5 |
6 | /**
7 | * Reflex
8 | * org.tabooproject.reflex.ReflexTest
9 | *
10 | * @author 坏黑
11 | * @since 2022/1/22 3:01 AM
12 | */
13 | class ReflexClassTest {
14 |
15 | private open class TargetParentParent {
16 |
17 | val level = 0
18 | }
19 |
20 | private open class TargetParent(val user: String) : TargetParentParent() {
21 |
22 | fun walk() {}
23 | }
24 |
25 | private class Target(val id: Int, user: String) : TargetParent(user) {
26 |
27 | fun run() {}
28 | }
29 |
30 | private val target = Target(10, "test")
31 | private val targetReflexClass = ReflexClass.of(target::class.java)
32 |
33 | @Test
34 | fun testGetField() {
35 | targetReflexClass.getField("id")
36 | }
37 |
38 | @Test
39 | fun testGetParentField() {
40 | targetReflexClass.getField("user")
41 | }
42 |
43 | @Test
44 | fun testGetParentParentField() {
45 | targetReflexClass.getField("level")
46 | }
47 |
48 | @Test
49 | fun testGetInvalidField() {
50 | try {
51 | targetReflexClass.getField("grade")
52 | throw IllegalStateException()
53 | } catch (_: NoSuchFieldException) {
54 | }
55 | }
56 |
57 | @Test
58 | fun testGetMethod() {
59 | targetReflexClass.getMethod("run")
60 | }
61 |
62 | @Test
63 | fun testGetParentMethod() {
64 | targetReflexClass.getMethod("walk")
65 | }
66 |
67 | @Test
68 | fun testGetInvalidMethod() {
69 | try {
70 | targetReflexClass.getMethod("NotDefined")
71 | throw IllegalStateException()
72 | } catch (_: NoSuchMethodException) {
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/reflex/src/test/kotlin/org/tabooproject/reflex/ReflexJarScanner.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | import java.io.File
4 | import java.net.JarURLConnection
5 | import java.net.URISyntaxException
6 | import java.net.URL
7 | import java.util.concurrent.ConcurrentHashMap
8 | import java.util.jar.JarFile
9 | import kotlin.test.Test
10 | import kotlin.time.ExperimentalTime
11 | import kotlin.time.measureTime
12 |
13 | @OptIn(ExperimentalTime::class)
14 | class ReflexJarScanner {
15 |
16 | // @Test
17 | fun testJar1() {
18 | val classes = File("common-legacy-api-6.2.2-local-test-1-53cd8f1d.jar").toURI().toURL().getClasses()
19 | println("共 ${classes.size} 个类")
20 | val serializeToBytes = ReflexClassMap.serializeToBytes(classes)
21 | println("序列化 ${serializeToBytes.size} 字节")
22 |
23 | // 反序列化
24 | ReflexClassMap.deserializeFromBytes(serializeToBytes, null)
25 |
26 | // 写入文件
27 | val file = File("classes-legacy-api")
28 | if (!file.exists()) {
29 | file.createNewFile()
30 | }
31 | file.writeBytes(serializeToBytes)
32 |
33 | // 从文件里读出来
34 | val fromBytes = ReflexClassMap.deserializeFromBytes(File("classes-legacy-api").readBytes(), null)
35 | }
36 |
37 | // @Test
38 | fun scanner() {
39 | val classes: Map
40 | val time1 = measureTime { classes = File("Adyeshach-2.0.25-api.jar").toURI().toURL().getClasses() }
41 | println("首次读取 $time1")
42 | println("共 ${classes.size} 个类")
43 |
44 | val time2 = measureTime { File("Adyeshach-2.0.25-api.jar").toURI().toURL().getClasses() }
45 | println("预热后读取 $time2")
46 |
47 | val bytes: ByteArray
48 | val time3 = measureTime { bytes = ReflexClassMap.serializeToBytes(classes) }
49 | println("序列化 $time3")
50 | println("序列化 ${bytes.size} 字节")
51 |
52 | val file = File("classes")
53 | if (!file.exists()) {
54 | file.createNewFile()
55 | }
56 | file.writeBytes(bytes)
57 |
58 | val read: Map
59 | val time4 = measureTime { read = ReflexClassMap.deserializeFromBytes(file.readBytes(), null) }
60 | println("反序列化 $time4")
61 | println("共 ${read.size} 个类")
62 |
63 | val time5 = measureTime { ReflexClassMap.deserializeFromBytes(file.readBytes(), null) }
64 | println("预热后反序列化 $time5")
65 | }
66 |
67 | /**
68 | * 获取 URL 下的所有类
69 | */
70 | fun URL.getClasses(): MutableMap {
71 | val classes = ConcurrentHashMap()
72 | val srcFile = try {
73 | File(toURI())
74 | } catch (ex: IllegalArgumentException) {
75 | File((openConnection() as JarURLConnection).jarFileURL.toURI())
76 | } catch (ex: URISyntaxException) {
77 | File(path)
78 | }
79 | val classLoader = ReflexJarScanner::class.java.classLoader
80 | // 是文件
81 | if (srcFile.isFile) {
82 | JarFile(srcFile).use { jar ->
83 | jar.stream()
84 | .parallel()
85 | .filter { it.name.endsWith(".class") }
86 | .forEach {
87 | val className = it.name.replace('/', '.').substringBeforeLast('.')
88 | val lc = LazyClass.of(className) { Class.forName(className, false, classLoader) }
89 | classes[className] = ReflexClass.of(lc, jar.getInputStream(it), saving = false)
90 | }
91 | }
92 | }
93 | // 是目录
94 | else {
95 | srcFile.walk().filter { it.extension == "class" }.forEach {
96 | val className = it.path.substringAfter(srcFile.path).drop(1).replace('/', '.').replace('\\', '.').substringBeforeLast(".class")
97 | val lc = LazyClass.of(className) { Class.forName(className, false, classLoader) }
98 | classes[className] = ReflexClass.of(lc, it.inputStream())
99 | }
100 | }
101 | return classes
102 | }
103 | }
--------------------------------------------------------------------------------
/reflex/src/test/kotlin/org/tabooproject/reflex/ReflexJava.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | import org.junit.jupiter.api.Test
4 | import org.tabooproject.reflex.Reflex.Companion.invokeMethod
5 |
6 | class ReflexJava {
7 |
8 | @Test
9 | fun test1() {
10 | val rc = ReflexClass.of(OpenAPI::class.java)
11 | rc.structure.fields.forEach {
12 | println("Field: $it")
13 | }
14 | rc.structure.constructors.forEach {
15 | println("Constructor: $it")
16 | it.parameter.forEach { klass ->
17 | println(" Parameter: $klass")
18 | }
19 | }
20 | rc.structure.methods.forEach {
21 | println("Method: $it")
22 | it.parameter.forEach { klass ->
23 | println(" Parameter: $klass")
24 | }
25 | }
26 | }
27 |
28 | @Test
29 | fun test2() {
30 | OpenAPI::class.java.invokeMethod("call", "sb", arrayOf(1), isStatic = true)
31 | }
32 | }
--------------------------------------------------------------------------------
/reflex/src/test/kotlin/org/tabooproject/reflex/ReflexPropertyGetter.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | import org.junit.jupiter.api.Test
4 | import kotlin.reflect.KProperty1
5 |
6 | class ReflexPropertyGetter {
7 |
8 | @Retention(AnnotationRetention.RUNTIME)
9 | @Target(AnnotationTarget.FIELD)
10 | annotation class Key(val id: String)
11 |
12 | class Data(
13 | @Key("sb")
14 | val name: String,
15 | )
16 |
17 | inline fun query(process: Processor.() -> Unit) {
18 | Processor(T::class.java).also(process)
19 | }
20 |
21 | class Processor(val owner: Class<*>) {
22 |
23 | infix fun KProperty1<*, *>.eq(any: Any) {
24 | println("正在进行 ${owner.name} 下的判定: $name eq $any")
25 | val id = ReflexClass.of(owner).getField(name).getAnnotation(Key::class.java).property("id")
26 | println("@Key = $id")
27 | }
28 | }
29 |
30 | @Test
31 | fun test() {
32 | query {
33 | Data::name eq "SB"
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/reflex/src/test/kotlin/org/tabooproject/reflex/ReflexTest.kt:
--------------------------------------------------------------------------------
1 | package org.tabooproject.reflex
2 |
3 | import org.junit.jupiter.api.Test
4 | import org.tabooproject.reflex.Reflex.Companion.getProperty
5 | import org.tabooproject.reflex.Reflex.Companion.invokeConstructor
6 | import org.tabooproject.reflex.Reflex.Companion.invokeMethod
7 | import java.lang.IllegalStateException
8 |
9 | /**
10 | * Reflex
11 | * org.tabooproject.reflex.ReflexTest
12 | *
13 | * @author 坏黑
14 | * @since 2022/1/22 3:01 AM
15 | */
16 | class ReflexTest {
17 |
18 | private open class TargetParentParent {
19 |
20 | val level = 5
21 | }
22 |
23 | private open class TargetParent(val user: String) : TargetParentParent() {
24 |
25 | fun walk(value: Int) = value
26 | }
27 |
28 | private class Target(val id: Int, user: String) : TargetParent(user) {
29 |
30 | fun run(value: Int) = value
31 | }
32 |
33 | @Test
34 | fun testGetField() {
35 | val target = Target(10, "test")
36 | assert(target.getProperty("id") == 10)
37 | }
38 |
39 | @Test
40 | fun testGetParentField() {
41 | val target = Target(10, "test")
42 | assert(target.getProperty("user") == "test")
43 | }
44 |
45 | @Test
46 | fun testGetParentParentField() {
47 | val target = Target(10, "test")
48 | assert(target.getProperty("level") == 5)
49 | }
50 |
51 | @Test
52 | fun testGetMethod() {
53 | val target = Target(10, "test")
54 | assert(target.invokeMethod("run", 10) == 10)
55 | }
56 |
57 | @Test
58 | fun testGetParentMethod() {
59 | val target = Target(10, "test")
60 | assert(target.invokeMethod("walk", 10) == 10)
61 | }
62 |
63 | @Test
64 | fun testConstructorASM() {
65 | assert(ReflexClass.of(TargetParentParent::class.java, AnalyseMode.ASM_ONLY).newInstance() != null)
66 | }
67 |
68 | @Test
69 | fun testConstructorReflection() {
70 | assert(ReflexClass.of(TargetParentParent::class.java, AnalyseMode.REFLECTION_ONLY).newInstance() != null)
71 | }
72 | }
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "Reflex"
2 | include("analyser")
3 | include("reflex")
4 | include("fast-instance-getter")
--------------------------------------------------------------------------------