├── .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 | ![](https://img.shields.io/github/v/release/taboolib/reflex.svg) 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> { 61 | return property(name, arrayListOf()) 62 | } 63 | 64 | // 给 Java 用 65 | fun getEnum(name: String): T { 66 | return enum(name) 67 | } 68 | 69 | fun getEnum(name: String, def: T): T { 70 | return enum(name, def) 71 | } 72 | 73 | // 当时起这名字没想到 Java 无法访问,我笑了。 74 | fun enum(name: String): T { 75 | val el = property(name) ?: throw EnumNotFoundException(name) 76 | return if (el is LazyEnum) el.instance as T else el as T 77 | } 78 | 79 | fun enum(name: String, def: T): T { 80 | val el = property(name) ?: return def 81 | return if (el is LazyEnum) el.instance as T else el as T 82 | } 83 | 84 | fun enumName(name: String): String { 85 | val el = property(name) ?: throw EnumNotFoundException(name) 86 | return if (el is LazyEnum) el.name else el.toString() 87 | } 88 | 89 | fun enumName(name: String, def: String): String { 90 | val el = property(name) ?: return def 91 | return if (el is LazyEnum) el.name else el.toString() 92 | } 93 | 94 | fun enumList(name: String): List { 95 | return list(name).map { it.instance as T } 96 | } 97 | 98 | fun enumNameList(name: String): List { 99 | return list(name).map { it.name } 100 | } 101 | 102 | fun type(name: String): LazyClass { 103 | return type(name, { Class.forName(it.replace('/', '.')) }) 104 | } 105 | 106 | fun type(name: String, classFinder: ClassAnalyser.ClassFinder): LazyClass { 107 | val el = property(name) ?: throw TypeNotFoundException(name) 108 | return when (el) { 109 | is Class<*> -> LazyClass.of(el) 110 | is Type -> AsmSignature.signatureToClass(el.descriptor, classFinder)[0] 111 | else -> throw TypeNotFoundException(name) 112 | } 113 | } 114 | 115 | override fun toString(): String { 116 | return "ClassAnnotation(source=$source, properties=${propertyKeys()})" 117 | } 118 | 119 | override fun equals(other: Any?): Boolean { 120 | if (this === other) return true 121 | if (other !is ClassAnnotation) return false 122 | if (source != other.source) return false 123 | return true 124 | } 125 | 126 | override fun hashCode(): Int { 127 | return source.hashCode() 128 | } 129 | 130 | companion object { 131 | 132 | fun of(reader: BinaryReader, classFinder: ClassAnalyser.ClassFinder?): ClassAnnotation { 133 | val type = reader.readInt() 134 | if (type == 1) { 135 | val source = LazyClass.of(reader, classFinder) 136 | val propertyMap = reader.readAnnotationProperties(classFinder) 137 | return AsmAnnotation(source, propertyMap) 138 | } else { 139 | error("Unknown type: $type") 140 | } 141 | } 142 | } 143 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/ClassConstructor.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import org.tabooproject.reflex.asm.AsmClassConstructor 4 | import org.tabooproject.reflex.asm.AsmClassMethod 5 | import org.tabooproject.reflex.serializer.BinaryReader 6 | import org.tabooproject.reflex.serializer.BinarySerializable 7 | 8 | /** 9 | * @author 坏黑 10 | * @since 2022/1/21 6:41 PM 11 | */ 12 | abstract class ClassConstructor(name: String, owner: LazyClass) : ClassMember(name, owner), BinarySerializable { 13 | 14 | abstract val parameter: List 15 | 16 | abstract fun instance(vararg values: Any?): Any? 17 | 18 | val parameterTypes by lazy(LazyThreadSafetyMode.NONE) { 19 | parameter.map { p -> p.instance ?: Unknown::class.java }.toTypedArray() 20 | } 21 | 22 | override fun toString(): String { 23 | return "ClassConstructor(parameter=$parameter)" 24 | } 25 | 26 | companion object { 27 | 28 | fun of(reader: BinaryReader, classFinder: ClassAnalyser.ClassFinder?): ClassConstructor { 29 | val type = reader.readInt() 30 | if (type == 1) { 31 | val method = AsmClassMethod.readFrom(reader, classFinder) 32 | return AsmClassConstructor( 33 | method.name, 34 | method.owner, 35 | method.descriptor, 36 | method.access, 37 | method.parameterAnnotations, 38 | classFinder ?: ClassAnalyser.ClassFinder.default, 39 | method.annotations, 40 | method.parameter 41 | ) 42 | } else { 43 | error("Unknown type: $type") 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/ClassField.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import org.tabooproject.reflex.asm.AsmClassField 4 | import org.tabooproject.reflex.serializer.BinaryReader 5 | import org.tabooproject.reflex.serializer.BinarySerializable 6 | 7 | /** 8 | * @author 坏黑 9 | * @since 2022/1/21 6:41 PM 10 | */ 11 | abstract class ClassField(name: String, owner: LazyClass) : ClassMember(name, owner), BinarySerializable { 12 | 13 | abstract val type: LazyClass 14 | 15 | abstract val isTransient: Boolean 16 | 17 | abstract fun get(src: Any? = null): Any? 18 | 19 | abstract fun set(src: Any? = null, value: Any?) 20 | 21 | fun setStatic(value: Any?) { 22 | set(StaticSrc, value) 23 | } 24 | 25 | val fieldType: Class<*> 26 | get() = type.instance ?: Unknown::class.java 27 | 28 | override fun toString(): String { 29 | return "ClassField(type=$type) ${super.toString()}" 30 | } 31 | 32 | companion object { 33 | 34 | fun of(reader: BinaryReader, classFinder: ClassAnalyser.ClassFinder?): ClassField { 35 | val type = reader.readInt() 36 | if (type == 1) { 37 | val name = reader.readNullableString()!! 38 | val owner = LazyClass.of(reader, classFinder) 39 | val descriptor = reader.readNullableString()!! 40 | val access = reader.readInt() 41 | val annotations = reader.readAnnotationList(classFinder) 42 | val type = LazyClass.of(reader, classFinder) 43 | return AsmClassField( 44 | name, 45 | owner, 46 | descriptor, 47 | access, 48 | classFinder ?: ClassAnalyser.ClassFinder.default, 49 | annotations, 50 | type 51 | ) 52 | } else { 53 | error("Unknown type: $type") 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/ClassMember.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | /** 4 | * @author 坏黑 5 | * @since 2022/1/21 6:31 PM 6 | */ 7 | abstract class ClassMember(val name: String, val owner: LazyClass) { 8 | 9 | abstract val annotations: List 10 | 11 | abstract val isStatic: Boolean 12 | 13 | abstract val isFinal: Boolean 14 | 15 | abstract val isPublic: Boolean 16 | 17 | abstract val isProtected: Boolean 18 | 19 | abstract val isPrivate: Boolean 20 | 21 | fun isAnnotationPresent(annotation: Class): Boolean { 22 | return annotations.any { it.source.name == annotation.name } 23 | } 24 | 25 | fun getAnnotation(annotation: Class): ClassAnnotation { 26 | return annotations.first { it.source.name == annotation.name } 27 | } 28 | 29 | fun getAnnotationIfPresent(annotation: Class): ClassAnnotation? { 30 | return annotations.find { it.source.name == annotation.name } 31 | } 32 | 33 | override fun toString(): String { 34 | return "ClassMember(name='$name', owner=$owner, annotations=$annotations, isStatic=$isStatic)" 35 | } 36 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/ClassMethod.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import org.tabooproject.reflex.asm.AsmClassMethod 4 | import org.tabooproject.reflex.serializer.BinaryReader 5 | import org.tabooproject.reflex.serializer.BinarySerializable 6 | 7 | /** 8 | * @author 坏黑 9 | * @since 2022/1/21 6:41 PM 10 | */ 11 | abstract class ClassMethod(name: String, owner: LazyClass) : ClassMember(name, owner), BinarySerializable { 12 | 13 | abstract val result: LazyClass 14 | 15 | abstract val parameter: List 16 | 17 | abstract val isNative: Boolean 18 | 19 | abstract val isAbstract: Boolean 20 | 21 | abstract val isVolatile: Boolean 22 | 23 | abstract val isSynchronized: Boolean 24 | 25 | abstract fun invoke(src: Any, vararg values: Any?): Any? 26 | 27 | abstract fun invokeStatic(vararg values: Any?): Any? 28 | 29 | val returnType by lazy(LazyThreadSafetyMode.NONE) { 30 | try { 31 | result.instance ?: Unknown::class.java 32 | } catch (ex: Throwable) { 33 | println("Field to get return type of $this") 34 | throw ex 35 | } 36 | } 37 | 38 | val parameterTypes by lazy(LazyThreadSafetyMode.NONE) { 39 | parameter.map { p -> p.instance ?: Unknown::class.java }.toTypedArray() 40 | } 41 | 42 | override fun toString(): String { 43 | return "ClassMethod(result=$result)" 44 | } 45 | 46 | companion object { 47 | 48 | fun of(reader: BinaryReader, classFinder: ClassAnalyser.ClassFinder?): ClassMethod { 49 | val type = reader.readInt() 50 | if (type == 1) { 51 | val method = AsmClassMethod.readFrom(reader, classFinder) 52 | // 返回值 53 | val result = LazyClass.of(reader, classFinder) 54 | return AsmClassMethod( 55 | method.name, 56 | method.owner, 57 | method.descriptor, 58 | method.access, 59 | method.parameterAnnotations, 60 | classFinder ?: ClassAnalyser.ClassFinder.default, 61 | method.annotations, 62 | result, 63 | method.parameter 64 | ) 65 | } else { 66 | error("Unknown type: $type") 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/ClassStructure.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import org.tabooproject.reflex.serializer.BinarySerializable 4 | import java.lang.reflect.Modifier 5 | import java.util.* 6 | 7 | /** 8 | * @author 坏黑 9 | * @since 2022/1/21 6:41 PM 10 | */ 11 | abstract class ClassStructure( 12 | val type: Type, 13 | val owner: LazyClass, 14 | val access: Int, 15 | val superclass: LazyClass?, 16 | interfaces: List, 17 | annotations: List, 18 | fields: List, 19 | methods: List, 20 | constructors: List, 21 | ) : BinarySerializable { 22 | 23 | val name by lazy(LazyThreadSafetyMode.NONE) { runCatching { owner.name }.getOrNull() } 24 | val simpleName by lazy(LazyThreadSafetyMode.NONE) { runCatching { owner.simpleName }.getOrNull() } 25 | 26 | val interfaces = LinkedList(interfaces) 27 | val annotations = LinkedList(annotations) 28 | val fields = LinkedList(fields) 29 | val methods = LinkedList(methods) 30 | val constructors = LinkedList(constructors) 31 | 32 | val isStatic: Boolean 33 | get() = Modifier.isStatic(access) 34 | 35 | val isFinal: Boolean 36 | get() = Modifier.isFinal(access) 37 | 38 | val isPublic: Boolean 39 | get() = Modifier.isPublic(access) 40 | 41 | val isProtected: Boolean 42 | get() = Modifier.isProtected(access) 43 | 44 | val isPrivate: Boolean 45 | get() = Modifier.isPrivate(access) 46 | 47 | val isAbstract: Boolean 48 | get() = Modifier.isAbstract(access) 49 | 50 | val isInterface: Boolean 51 | get() = Modifier.isInterface(access) 52 | 53 | val isStrict: Boolean 54 | get() = Modifier.isStrict(access) 55 | 56 | abstract fun getField(name: String): ClassField 57 | 58 | abstract fun getFieldSilently(name: String): ClassField? 59 | 60 | abstract fun getMethod(name: String, vararg parameter: Any?): ClassMethod 61 | 62 | abstract fun getMethodSilently(name: String, vararg parameter: Any?): ClassMethod? 63 | 64 | abstract fun getMethodByType(name: String, vararg parameter: Class<*>): ClassMethod 65 | 66 | abstract fun getMethodByTypeSilently(name: String, vararg parameter: Class<*>): ClassMethod? 67 | 68 | abstract fun getConstructor(vararg parameter: Any?): ClassConstructor 69 | 70 | abstract fun getConstructorSilently(vararg parameter: Any?): ClassConstructor? 71 | 72 | abstract fun getConstructorByType(vararg parameter: Class<*>): ClassConstructor 73 | 74 | abstract fun getConstructorByTypeSilently(vararg parameter: Class<*>): ClassConstructor? 75 | 76 | abstract fun getAnnotation(annotation: Class): ClassAnnotation 77 | 78 | abstract fun isAnnotationPresent(annotation: Class): Boolean 79 | 80 | override fun toString(): String { 81 | return "ClassStructure(owner=$owner, superclass=$superclass, interfaces=$interfaces, annotations=$annotations, fields=$fields, methods=$methods, constructors=$constructors)" 82 | } 83 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/EnumNotFoundException.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | class EnumNotFoundException(val enumName: String) : Exception("Enum not found: $enumName") 4 | -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/Internal.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | @Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) 4 | annotation class Internal 5 | -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/JavaClassConstructor.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import java.lang.invoke.MethodType 4 | 5 | /** 6 | * @author 坏黑 7 | * @since 2022/1/21 10:15 PM 8 | */ 9 | @Internal 10 | abstract class JavaClassConstructor(name: String, owner: LazyClass) : ClassConstructor(name, owner) { 11 | 12 | private val handle by lazy(LazyThreadSafetyMode.NONE) { 13 | UnsafeAccess.lookup.findConstructor(owner.instance, MethodType.methodType(Void.TYPE, parameterTypes)) 14 | } 15 | 16 | private val optimizedInvoker by lazy(LazyThreadSafetyMode.NONE) { 17 | if (parameterTypes.isEmpty()) { 18 | handle.asType(handle.type().changeReturnType(Any::class.java)) 19 | } else { 20 | val spreader = handle.asSpreader(Array::class.java, parameterTypes.size) 21 | spreader.asType(spreader.type().wrap().changeReturnType(Any::class.java)) 22 | } 23 | } 24 | 25 | override fun instance(vararg values: Any?): Any? { 26 | // 检查无效参数 27 | if (parameterTypes.any { it == Unknown::class.java }) { 28 | throw NoClassDefFoundError(parameterTypes.joinToString(";") { it.name }) 29 | } 30 | return try { 31 | if (parameterTypes.isEmpty()) { 32 | optimizedInvoker.invoke() 33 | } else { 34 | optimizedInvoker.invoke(values as Array) 35 | } 36 | } catch (ex: Throwable) { 37 | if (ex is RuntimeException || ex is Error) { 38 | throw ex 39 | } 40 | throw RuntimeException(ex) 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/JavaClassField.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | /** 4 | * @author 坏黑 5 | * @since 2022/1/21 10:12 PM 6 | */ 7 | @Internal 8 | abstract class JavaClassField(name: String, owner: LazyClass) : ClassField(name, owner) { 9 | 10 | private val handleGetter by lazy(LazyThreadSafetyMode.NONE) { 11 | val getter = if (isStatic) { 12 | UnsafeAccess.lookup.findStaticGetter(owner.instance, name, fieldType) 13 | } else { 14 | UnsafeAccess.lookup.findGetter(owner.instance, name, fieldType) 15 | } 16 | getter.asType(getter.type().generic()) 17 | } 18 | 19 | private val handleSetter by lazy(LazyThreadSafetyMode.NONE) { 20 | val setter = if (isStatic) { 21 | UnsafeAccess.lookup.findStaticSetter(owner.instance, name, fieldType) 22 | } else { 23 | UnsafeAccess.lookup.findSetter(owner.instance, name, fieldType) 24 | } 25 | setter.asType(setter.type().generic()) 26 | } 27 | 28 | override fun get(src: Any?): Any? { 29 | if (fieldType == Unknown::class.java) { 30 | throw NoClassDefFoundError("${type.name}.$name (${owner})") 31 | } 32 | return try { 33 | if (isStatic) { 34 | handleGetter.invoke() 35 | } else { 36 | handleGetter.invoke(src) 37 | } 38 | } catch (ex: ClassCastException) { 39 | if (!isStatic && src == StaticSrc) { 40 | throw IllegalStateException("$name is not a static field", ex) 41 | } 42 | throw ex 43 | } catch (ex: Throwable) { 44 | if (ex is RuntimeException || ex is Error) { 45 | throw ex 46 | } 47 | throw RuntimeException(ex) 48 | } 49 | } 50 | 51 | override fun set(src: Any?, value: Any?) { 52 | if (fieldType == Unknown::class.java) { 53 | throw NoClassDefFoundError("${type.name}.$name (${owner})") 54 | } 55 | try { 56 | if (isStatic) { 57 | handleSetter.invoke(value) 58 | } else { 59 | handleSetter.invoke(src, value) 60 | } 61 | } catch (ex: ClassCastException) { 62 | if (!isStatic && src == StaticSrc) { 63 | throw IllegalStateException("$name is not a static field", ex) 64 | } 65 | throw ex 66 | } catch (ex: Throwable) { 67 | if (ex is RuntimeException || ex is Error) { 68 | throw ex 69 | } 70 | throw RuntimeException(ex) 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/JavaClassMethod.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import java.lang.invoke.MethodType 4 | 5 | /** 6 | * @author 坏黑 7 | * @since 2022/1/21 10:17 PM 8 | */ 9 | @Internal 10 | abstract class JavaClassMethod(name: String, owner: LazyClass) : ClassMethod(name, owner) { 11 | 12 | private val handle by lazy(LazyThreadSafetyMode.NONE) { 13 | if (isStatic) { 14 | UnsafeAccess.lookup.findStatic(owner.instance, name, MethodType.methodType(returnType, parameterTypes)) 15 | } else { 16 | UnsafeAccess.lookup.findVirtual(owner.instance, name, MethodType.methodType(returnType, parameterTypes)) 17 | } 18 | } 19 | 20 | private val optimizedInvoker by lazy(LazyThreadSafetyMode.NONE) { 21 | when { 22 | isStatic -> { 23 | if (parameterTypes.isEmpty()) { 24 | handle.asType(handle.type().generic()) 25 | } else { 26 | val spreader = handle.asSpreader(Array::class.java, parameterTypes.size) 27 | spreader.asType(spreader.type().generic()) 28 | } 29 | } 30 | parameterTypes.isEmpty() -> { 31 | handle.asType(handle.type().generic()) 32 | } 33 | else -> { 34 | val spreader = handle.asSpreader(Array::class.java, parameterTypes.size) 35 | spreader.asType(spreader.type().generic()) 36 | } 37 | } 38 | } 39 | 40 | override fun invoke(src: Any, vararg values: Any?): Any? { 41 | if (returnType == Unknown::class.java) { 42 | throw NoClassDefFoundError(result.name) 43 | } 44 | if (parameterTypes.any { it == Unknown::class.java }) { 45 | throw NoClassDefFoundError(parameterTypes.joinToString(";") { it.name }) 46 | } 47 | return try { 48 | when { 49 | isStatic -> { 50 | if (parameterTypes.isEmpty()) { 51 | optimizedInvoker.invoke() 52 | } else { 53 | optimizedInvoker.invoke(values as Array) 54 | } 55 | } 56 | parameterTypes.isEmpty() -> { 57 | optimizedInvoker.invoke(src) 58 | } 59 | else -> { 60 | val boundHandle = optimizedInvoker.bindTo(src) 61 | boundHandle.invoke(values as Array) 62 | } 63 | } 64 | } catch (ex: ClassCastException) { 65 | if (!isStatic && src == StaticSrc) { 66 | throw IllegalStateException("$name is not a static method", ex) 67 | } else if (!isStatic) { 68 | throw IllegalStateException("${src.javaClass.name} is not an instance of ${owner.name}", ex) 69 | } else { 70 | throw ex 71 | } 72 | } catch (ex: Throwable) { 73 | if (ex is RuntimeException || ex is Error) { 74 | throw ex 75 | } 76 | throw RuntimeException(ex) 77 | } 78 | } 79 | 80 | override fun invokeStatic(vararg values: Any?): Any? { 81 | return invoke(StaticSrc, *values) 82 | } 83 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/JavaClassStructure.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import org.tabooproject.reflex.serializer.BinaryReader 4 | import org.tabooproject.reflex.serializer.BinaryWriter 5 | 6 | /** 7 | * Reflex 8 | * org.tabooproject.reflex.JavaClassStructure 9 | * 10 | * @author 坏黑 11 | * @since 2022/1/22 2:53 AM 12 | */ 13 | @Internal 14 | class JavaClassStructure( 15 | type: Type, 16 | owner: LazyClass, 17 | access: Int, 18 | superclass: LazyClass?, 19 | interfaces: List, 20 | annotations: List, 21 | fields: List, 22 | methods: List, 23 | constructors: List, 24 | ) : ClassStructure(type, owner, access, superclass, interfaces, annotations, fields, methods, constructors) { 25 | 26 | override fun getField(name: String): ClassField { 27 | return fields.find { it.name == name } ?: throw NoSuchFieldException("${this.name}#$name") 28 | } 29 | 30 | override fun getMethod(name: String, vararg parameter: Any?): ClassMethod { 31 | return methods.find { it.name == name && Reflection.isAssignableFrom(it.parameterTypes, parameter.map { p -> p?.javaClass }.toTypedArray()) } 32 | ?: throw NoSuchMethodException("${this.name}#$name(${parameter.joinToString(";") { it?.javaClass?.name ?: "null" }})") 33 | } 34 | 35 | override fun getMethodByType(name: String, vararg parameter: Class<*>): ClassMethod { 36 | return methods.find { it.name == name && Reflection.isAssignableFrom(it.parameterTypes, parameter.toList().toTypedArray()) } 37 | ?: throw NoSuchMethodException("${this.name}#$name(${parameter.joinToString(";") { it.name }})") 38 | } 39 | 40 | override fun getConstructor(vararg parameter: Any?): ClassConstructor { 41 | return constructors.find { Reflection.isAssignableFrom(it.parameterTypes, parameter.map { p -> p?.javaClass }.toTypedArray()) } 42 | ?: throw NoSuchMethodException("${this.name}#(${parameter.joinToString(";") { it?.javaClass?.name ?: "null" }})") 43 | } 44 | 45 | override fun getConstructorByType(vararg parameter: Class<*>): ClassConstructor { 46 | return constructors.find { Reflection.isAssignableFrom(it.parameterTypes, parameter.toList().toTypedArray()) } 47 | ?: throw NoSuchMethodException("${this.name}#(${parameter.joinToString(";") { it.name }})") 48 | } 49 | 50 | override fun getFieldSilently(name: String): ClassField? { 51 | return runCatching { getField(name) }.getOrNull() 52 | } 53 | 54 | override fun getMethodSilently(name: String, vararg parameter: Any?): ClassMethod? { 55 | return runCatching { getMethod(name, *parameter) }.getOrNull() 56 | } 57 | 58 | override fun getMethodByTypeSilently(name: String, vararg parameter: Class<*>): ClassMethod? { 59 | return runCatching { getMethodByType(name, *parameter) }.getOrNull() 60 | } 61 | 62 | override fun getConstructorSilently(vararg parameter: Any?): ClassConstructor? { 63 | return runCatching { getConstructor(*parameter) }.getOrNull() 64 | } 65 | 66 | override fun getConstructorByTypeSilently(vararg parameter: Class<*>): ClassConstructor? { 67 | return runCatching { getConstructorByType(*parameter) }.getOrNull() 68 | } 69 | 70 | override fun getAnnotation(annotation: Class): ClassAnnotation { 71 | return annotations.first { it.source.name == annotation.name } 72 | } 73 | 74 | override fun isAnnotationPresent(annotation: Class): Boolean { 75 | return annotations.any { it.source.name == annotation.name } 76 | } 77 | 78 | override fun writeTo(writer: BinaryWriter) { 79 | // 反射模式不支持序列化 80 | if (type == Type.REFLECTION) error("Cannot serialize reflection mode") 81 | writer.writeObj(owner) 82 | writer.writeInt(access) 83 | writer.writeNullableObj(superclass) 84 | // println("write interfaces") 85 | writer.writeList(interfaces) 86 | // println("write annotations") 87 | writer.writeList(annotations) 88 | // println("write fields") 89 | writer.writeList(fields) 90 | // println("write methods") 91 | writer.writeList(methods) 92 | // println("write constructors") 93 | writer.writeList(constructors) 94 | } 95 | 96 | companion object { 97 | 98 | fun of(reader: BinaryReader, classFinder: ClassAnalyser.ClassFinder?): JavaClassStructure { 99 | return JavaClassStructure( 100 | Type.ASM, 101 | LazyClass.of(reader, classFinder), 102 | reader.readInt(), 103 | reader.readNullableClass(classFinder), 104 | reader.readClassList(classFinder), 105 | reader.readAnnotationList(classFinder), 106 | reader.readFieldList(classFinder), 107 | reader.readMethodList(classFinder), 108 | reader.readConstructorList(classFinder) 109 | ) 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/Labels.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | class Unknown 4 | 5 | object StaticSrc -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/LazyAnnotatedClass.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import org.tabooproject.reflex.serializer.BinaryWriter 4 | import java.util.function.Supplier 5 | 6 | /** 7 | * @author 坏黑 8 | * @since 2022/1/21 6:47 PM 9 | */ 10 | @Internal 11 | open class LazyAnnotatedClass internal constructor( 12 | source: String, 13 | dimensions: Int, 14 | isInstant: Boolean, 15 | isPrimitive: Boolean, 16 | classGetter: Supplier?>, 17 | val annotations: List, 18 | name: String = source.replace('/', '.'), 19 | simpleName: String = name.substringAfterLast('.'), 20 | ) : LazyClass(source, dimensions, isInstant, isPrimitive, classGetter, name, simpleName) { 21 | 22 | fun getAnnotation(annotation: Class): ClassAnnotation { 23 | return annotations.first { it.source.name == annotation.name } 24 | } 25 | 26 | fun isAnnotationPresent(annotation: Class): Boolean { 27 | return annotations.any { it.source.name == annotation.name } 28 | } 29 | 30 | override fun toString(): String { 31 | return "LazyAnnotatedClass($name,dim=$dimensions,@${annotations})" 32 | } 33 | 34 | override fun writeTo(writer: BinaryWriter) { 35 | writer.writeInt(2) // 2:表示 LazyAnnotatedClass 36 | writer.writeNullableString(name) 37 | writer.writeNullableString(simpleName) 38 | writer.writeInt(dimensions) 39 | writer.writeBoolean(isInstant) 40 | writer.writeBoolean(isPrimitive) 41 | writer.writeList(annotations) 42 | } 43 | 44 | override fun equals(other: Any?): Boolean { 45 | if (this === other) return true 46 | if (other !is LazyAnnotatedClass) return false 47 | if (!super.equals(other)) return false 48 | if (annotations != other.annotations) return false 49 | return true 50 | } 51 | 52 | override fun hashCode(): Int { 53 | var result = super.hashCode() 54 | result = 31 * result + annotations.hashCode() 55 | return result 56 | } 57 | 58 | companion object { 59 | 60 | fun of(clazz: Class<*>, dimensions: Int = clazz.getArrayDimensions(), annotations: List): LazyAnnotatedClass { 61 | return LazyAnnotatedClass(clazz.name, dimensions, isInstant = true, clazz.isPrimitive, classGetter = { clazz }, annotations = annotations) 62 | } 63 | 64 | fun of(source: String, dimensions: Int = 0, isPrimitive: Boolean = false, annotations: List): LazyAnnotatedClass { 65 | return LazyAnnotatedClass( 66 | source, 67 | dimensions, 68 | isInstant = false, 69 | isPrimitive, 70 | classGetter = { runCatching { Class.forName(source) }.getOrNull() }, 71 | annotations 72 | ) 73 | } 74 | 75 | fun of(source: String, dimensions: Int = 0, isPrimitive: Boolean = false, annotations: List, classFinder: ClassAnalyser.ClassFinder?): LazyAnnotatedClass { 76 | val finder = classFinder ?: ClassAnalyser.ClassFinder.default 77 | return LazyAnnotatedClass( 78 | source, 79 | dimensions, 80 | isInstant = false, 81 | isPrimitive, 82 | classGetter = { finder.findClass(source.replace('/', '.')) }, 83 | annotations 84 | ) 85 | } 86 | 87 | fun of(source: String, getter: Supplier?>, annotations: List): LazyAnnotatedClass { 88 | return LazyAnnotatedClass(source, dimensions = 0, isInstant = false, isPrimitive = false, getter, annotations) 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/LazyClass.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.DataOutputStream 7 | import java.lang.ref.WeakReference 8 | import java.util.* 9 | import java.util.concurrent.ConcurrentHashMap 10 | import java.util.function.Supplier 11 | 12 | /** 13 | * 一个懒加载的类 14 | * 15 | * @property source 类地址,可能是 "." 也可能是 "/",不确定 16 | * @property dimensions 数组维度 17 | * @property isPrimitive 是否为基本类型 18 | * @property isInstant 是否已经实例化(是否已经加载,此时 classFinder 必定为空,getter 直接返回类本身) 19 | * @property classGetter 获取 Class 对象,通常是 forName 的包装 20 | */ 21 | open class LazyClass internal constructor( 22 | source: String, 23 | val dimensions: Int, 24 | val isInstant: Boolean, 25 | val isPrimitive: Boolean, 26 | val classGetter: Supplier?>, 27 | val name: String = source.replace('/', '.'), 28 | val simpleName: String = name.substringAfterLast('.'), 29 | ) : BinarySerializable { 30 | 31 | /** 32 | * 是否为数组类型 33 | * dimensions > 0 时为 true 34 | */ 35 | val isArray: Boolean 36 | get() = dimensions > 0 37 | 38 | /** 39 | * 类的实例 40 | * 如果是数组类型,则创建一个多维数组实例 41 | * 例如: 42 | * dimensions = 1 时创建 new Type[0] 43 | * dimensions = 2 时创建 new Type[0][0] 44 | * dimensions = 3 时创建 new Type[0][0][0] 45 | */ 46 | val instance by lazy(LazyThreadSafetyMode.NONE) { 47 | if (isArray) { 48 | // 递归创建多维数组 49 | fun createArray(componentType: Class<*>, dim: Int): Class<*> { 50 | return if (dim <= 0) { 51 | componentType 52 | } else { 53 | createArray(java.lang.reflect.Array.newInstance(componentType, 0).javaClass, dim - 1) 54 | } 55 | } 56 | createArray(classGetter.get()!!, dimensions) 57 | } else { 58 | classGetter.get() 59 | } 60 | } 61 | 62 | /** 63 | * 此类是否存在 64 | * 此方法会尝试加载类,如果加载失败,则返回 false 65 | */ 66 | val isExist by lazy(LazyThreadSafetyMode.NONE) { 67 | try { 68 | instance 69 | true 70 | } catch (e: ClassNotFoundException) { 71 | false 72 | } 73 | } 74 | 75 | /** 76 | * 抛出一个找不到类的异常 77 | */ 78 | fun notfound(): Nothing = throw ClassNotFoundException("Class not found: $name") 79 | 80 | override fun toString(): String { 81 | return "LazyClass($name,dim=$dimensions)" 82 | } 83 | 84 | override fun writeTo(writer: BinaryWriter) { 85 | writer.writeInt(1) // 1:表示 LazyClass 86 | writer.writeNullableString(name) 87 | writer.writeNullableString(simpleName) 88 | writer.writeInt(dimensions) 89 | writer.writeBoolean(isInstant) 90 | writer.writeBoolean(isPrimitive) 91 | } 92 | 93 | override fun equals(other: Any?): Boolean { 94 | if (this === other) return true 95 | if (other !is LazyClass) return false 96 | if (dimensions != other.dimensions) return false 97 | if (name != other.name) return false 98 | return true 99 | } 100 | 101 | override fun hashCode(): Int { 102 | var result = dimensions 103 | result = 31 * result + name.hashCode() 104 | return result 105 | } 106 | 107 | companion object { 108 | 109 | private data class CacheKey(val clazz: Class<*>, val dimensions: Int) 110 | private data class StringCacheKey(val source: String, val dimensions: Int, val isPrimitive: Boolean, val finder: ClassAnalyser.ClassFinder) 111 | private data class SupplierCacheKey(val source: String, val dimensions: Int, val isPrimitive: Boolean) 112 | 113 | private val classCache = ConcurrentHashMap>() 114 | private val stringCache = ConcurrentHashMap>() 115 | private val supplierCache = ConcurrentHashMap>() 116 | 117 | /** 118 | * 创建一个 LazyClass 实例 119 | * 120 | * @param clazz 类对象 121 | * @return LazyClass 实例 122 | */ 123 | fun of(clazz: Class<*>, dimensions: Int = clazz.getArrayDimensions()): LazyClass { 124 | val key = CacheKey(clazz, dimensions) 125 | val ref = classCache[key]?.get() 126 | if (ref != null) { 127 | return ref 128 | } 129 | val lazyClass = LazyClass(clazz.name, dimensions, isInstant = true, clazz.isPrimitive, { clazz }) 130 | classCache[key] = WeakReference(lazyClass) 131 | return lazyClass 132 | } 133 | 134 | /** 135 | * 创建一个 LazyClass 实例 136 | * 137 | * @param source 类名 138 | * @param classFinder 类查找器 139 | * @return LazyClass 实例 140 | */ 141 | fun of(source: String, dimensions: Int = 0, isPrimitive: Boolean = false, classFinder: ClassAnalyser.ClassFinder?): LazyClass { 142 | val finder = classFinder ?: ClassAnalyser.ClassFinder.default 143 | val key = StringCacheKey(source, dimensions, isPrimitive, finder) 144 | val ref = stringCache[key]?.get() 145 | if (ref != null) { 146 | return ref 147 | } 148 | val lazyClass = LazyClass(source, dimensions, isInstant = false, isPrimitive, { finder.findClass(source.replace('/', '.')) }) 149 | stringCache[key] = WeakReference(lazyClass) 150 | return lazyClass 151 | } 152 | 153 | /** 154 | * 创建一个 LazyClass 实例 155 | * 156 | * @param source 类名 157 | * @param getter 类获取器 158 | * @return LazyClass 实例 159 | */ 160 | fun of(source: String, dimensions: Int = 0, isPrimitive: Boolean = false, getter: Supplier?>): LazyClass { 161 | val key = SupplierCacheKey(source, dimensions, isPrimitive) 162 | val ref = supplierCache[key]?.get() 163 | if (ref != null) { 164 | return ref 165 | } 166 | val lazyClass = LazyClass(source, dimensions, isInstant = false, isPrimitive, classGetter = getter) 167 | supplierCache[key] = WeakReference(lazyClass) 168 | return lazyClass 169 | } 170 | 171 | /** 172 | * 从 BinaryReader 中读取一个 LazyClass 实例 173 | */ 174 | fun of(reader: BinaryReader, classFinder: ClassAnalyser.ClassFinder?): LazyClass { 175 | val type = reader.readInt() 176 | val name = reader.readNullableString()!! 177 | val simpleName = reader.readNullableString()!! 178 | val dimensions = reader.readInt() 179 | val isInstant = reader.readBoolean() 180 | val isPrimitive = reader.readBoolean() 181 | val finder = classFinder ?: ClassAnalyser.ClassFinder.default 182 | val classGetter = Supplier { if (isPrimitive) Reflection.getPrimitiveType(name[0]) else finder.findClass(name) } 183 | when (type) { 184 | 1 -> { 185 | return LazyClass(name, dimensions, isInstant, isPrimitive, classGetter, name, simpleName) 186 | } 187 | 2 -> { 188 | val annotations = reader.readAnnotationList(classFinder) 189 | return LazyAnnotatedClass(name, dimensions, isInstant, isPrimitive, classGetter, annotations, name, simpleName) 190 | } 191 | else -> { 192 | error("Unknown type: $type") 193 | } 194 | } 195 | } 196 | 197 | fun writeTo(clazz: Class<*>, output: DataOutputStream) { 198 | // 名字 199 | val name = clazz.name 200 | output.writeInt(name.length) 201 | output.write(name.toByteArray(), 0, name.length) 202 | // 简单名 203 | val simpleName = clazz.simpleName 204 | output.writeInt(simpleName.length) 205 | output.write(simpleName.toByteArray(), 0, simpleName.length) 206 | // 类信息 207 | output.writeInt(clazz.getArrayDimensions()) 208 | output.writeBoolean(true) 209 | output.writeBoolean(clazz.isPrimitive) 210 | } 211 | } 212 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/LazyEnum.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import java.util.concurrent.ConcurrentHashMap 4 | 5 | /** 6 | * @author 坏黑 7 | * @since 2022/1/24 9:27 PM 8 | */ 9 | @Suppress("UNCHECKED_CAST") 10 | class LazyEnum(val source: LazyClass, val name: String) { 11 | 12 | val instance by lazy(LazyThreadSafetyMode.NONE) { 13 | allOf(source.instance as Class>)[name]!! 14 | } 15 | 16 | override fun toString(): String { 17 | return "LazyEnum(source=$source, name='$name')" 18 | } 19 | 20 | companion object { 21 | 22 | val map = ConcurrentHashMap>>() 23 | 24 | fun allOf(enumClass: Class>): Map> { 25 | return map.getOrPut(enumClass.name) { enumClass.enumConstants.associateBy { (it as Enum<*>).name } } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/Reflection.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import org.apache.commons.lang3.JavaVersion 4 | import org.apache.commons.lang3.SystemUtils 5 | 6 | /** 7 | * @author 坏黑 8 | * @since 2022/1/21 11:19 PM 9 | */ 10 | object Reflection { 11 | 12 | val autoboxing = runCatching { SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_5) }.getOrElse { true } 13 | 14 | fun isAssignableFrom(left: Array>, right: Array?>): Boolean { 15 | if (left.size != right.size) { 16 | return false 17 | } 18 | return left.indices.all { right[it] == null || getReferenceType(left[it]).isAssignableFrom(getReferenceType(right[it]!!)) } 19 | } 20 | 21 | fun getPrimitiveType(descriptor: Char): Class<*> { 22 | return when (descriptor) { 23 | 'B' -> java.lang.Byte.TYPE 24 | 'C' -> Character.TYPE 25 | 'D' -> java.lang.Double.TYPE 26 | 'F' -> java.lang.Float.TYPE 27 | 'I' -> Integer.TYPE 28 | 'J' -> java.lang.Long.TYPE 29 | 'S' -> java.lang.Short.TYPE 30 | 'V' -> Void.TYPE 31 | 'Z' -> java.lang.Boolean.TYPE 32 | else -> throw IllegalArgumentException() 33 | } 34 | } 35 | 36 | fun getReferenceType(primitive: Class<*>): Class<*> { 37 | return when (primitive) { 38 | Integer.TYPE -> Integer::class.java 39 | Character.TYPE -> Character::class.java 40 | java.lang.Byte.TYPE -> java.lang.Byte::class.java 41 | java.lang.Long.TYPE -> java.lang.Long::class.java 42 | java.lang.Double.TYPE -> java.lang.Double::class.java 43 | java.lang.Float.TYPE -> java.lang.Float::class.java 44 | java.lang.Short.TYPE -> java.lang.Short::class.java 45 | java.lang.Boolean.TYPE -> java.lang.Boolean::class.java 46 | else -> primitive 47 | } 48 | } 49 | } 50 | 51 | fun Class<*>.getArrayDimensions(): Int { 52 | var dimensions = 0 53 | var currentClass: Class<*>? = this 54 | // 通过检查类名来计算维度 55 | while (currentClass?.isArray == true) { 56 | dimensions++ 57 | currentClass = currentClass.componentType 58 | } 59 | return dimensions 60 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/Type.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | enum class Type { 4 | 5 | REFLECTION, ASM 6 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/TypeNotFoundException.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | class TypeNotFoundException(val typeName: String) : Exception("Type not found: $typeName") 4 | -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/UnsafeAccess.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import sun.misc.Unsafe 4 | import java.lang.invoke.MethodHandles 5 | import java.lang.reflect.Field 6 | import java.lang.reflect.Modifier 7 | 8 | @Suppress("UNCHECKED_CAST") 9 | object UnsafeAccess { 10 | 11 | var unsafe: Unsafe 12 | private set 13 | 14 | var lookup: MethodHandles.Lookup 15 | private set 16 | 17 | init { 18 | try { 19 | val theUnsafe = Unsafe::class.java.getDeclaredField("theUnsafe") 20 | theUnsafe.isAccessible = true 21 | unsafe = theUnsafe.get(null) as Unsafe 22 | try { 23 | Unsafe::class.java.getDeclaredMethod("ensureClassInitialized", Class::class.java).invoke(unsafe, MethodHandles.Lookup::class.java) 24 | } catch (ignored: Throwable) { 25 | // Fix JDK22 compatibility 26 | // MethodHandles.lookup().ensureInitialized(MethodHandles.Lookup::class.java) 27 | MethodHandles.Lookup::class.java.getDeclaredMethod("ensureInitialized", Class::class.java).invoke(MethodHandles.lookup(), MethodHandles.Lookup::class.java) 28 | } 29 | val lookupField = MethodHandles.Lookup::class.java.getDeclaredField("IMPL_LOOKUP") 30 | val lookupBase = unsafe.staticFieldBase(lookupField) 31 | val lookupOffset = unsafe.staticFieldOffset(lookupField) 32 | lookup = unsafe.getObject(lookupBase, lookupOffset) as MethodHandles.Lookup 33 | } catch (t: Throwable) { 34 | throw IllegalStateException("Unsafe not found", t) 35 | } 36 | } 37 | 38 | fun put(src: Any?, field: Field, value: Any?) { 39 | val methodHandle = lookup.unreflectSetter(field) 40 | if (Modifier.isStatic(field.modifiers)) { 41 | methodHandle.invokeWithArguments(value) 42 | } else { 43 | methodHandle.bindTo(src).invokeWithArguments(value) 44 | } 45 | } 46 | 47 | fun get(src: Any?, field: Field): T? { 48 | val methodHandle = lookup.unreflectGetter(field) 49 | return if (Modifier.isStatic(field.modifiers)) { 50 | methodHandle.invokeWithArguments() 51 | } else { 52 | methodHandle.bindTo(src).invokeWithArguments() 53 | } as? T 54 | } 55 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/asm/AsmAnnotation.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.asm 2 | 3 | import org.tabooproject.reflex.ClassAnnotation 4 | import org.tabooproject.reflex.Internal 5 | import org.tabooproject.reflex.LazyClass 6 | import org.tabooproject.reflex.serializer.BinaryWriter 7 | 8 | /** 9 | * @author 坏黑 10 | * @since 2022/1/24 8:48 PM 11 | */ 12 | @Internal 13 | class AsmAnnotation(source: LazyClass, val propertyMap: Map) : ClassAnnotation(source) { 14 | 15 | constructor(annotationVisitor: AsmClassAnnotationVisitor) : this(annotationVisitor.source, annotationVisitor.propertyMap) 16 | 17 | @Suppress("UNCHECKED_CAST") 18 | override fun property(name: String): T? { 19 | return propertyMap[name] as? T? 20 | } 21 | 22 | override fun property(name: String, def: T): T { 23 | return property(name) ?: def 24 | } 25 | 26 | override fun properties(): Map { 27 | return propertyMap 28 | } 29 | 30 | override fun propertyKeys(): Set { 31 | return propertyMap.keys 32 | } 33 | 34 | override fun toString(): String { 35 | return "AsmAnnotation() ${super.toString()}" 36 | } 37 | 38 | override fun writeTo(writer: BinaryWriter) { 39 | writer.writeInt(1) // 1: ASM MODE 40 | writer.writeObj(source) 41 | try { 42 | writer.writeAnnotationProperties(propertyMap) 43 | } catch (ex: Throwable) { 44 | println("Failed to write annotation properties $propertyMap") 45 | println("Source: $source") 46 | throw ex 47 | } 48 | } 49 | 50 | companion object { 51 | 52 | private val internalMethods = arrayOf("equals", "hashCode", "toString") 53 | } 54 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/asm/AsmClassAnnotationVisitor.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.asm 2 | 3 | import org.objectweb.asm.AnnotationVisitor 4 | import org.objectweb.asm.Opcodes 5 | import org.tabooproject.reflex.ClassAnalyser 6 | import org.tabooproject.reflex.Internal 7 | import org.tabooproject.reflex.LazyEnum 8 | 9 | /** 10 | * @author 坏黑 11 | * @since 2022/1/24 9:11 PM 12 | */ 13 | @Internal 14 | class AsmClassAnnotationVisitor( 15 | annotationVisitor: AnnotationVisitor, 16 | val descriptor: String, 17 | val classFinder: ClassAnalyser.ClassFinder, 18 | val context: Context? = null, 19 | ) : AnnotationVisitor(Opcodes.ASM9, annotationVisitor), Opcodes { 20 | 21 | val source = AsmSignature.signatureToClass(descriptor, classFinder).first() 22 | val propertyMap = HashMap() 23 | 24 | // 临时列表 25 | // 用来读取匿名数据 26 | val tempArray = ArrayList() 27 | 28 | override fun visit(name: String?, value: Any) { 29 | super.visit(name, value) 30 | if (name == null) { 31 | tempArray += value 32 | } else { 33 | propertyMap[name] = value 34 | } 35 | } 36 | 37 | override fun visitEnum(name: String?, descriptor: String, value: String) { 38 | super.visitEnum(name, descriptor, value) 39 | val enum = LazyEnum(AsmSignature.signatureToClass(descriptor, classFinder).first(), value) 40 | if (name == null) { 41 | tempArray += enum 42 | } else { 43 | propertyMap[name] = enum 44 | } 45 | } 46 | 47 | override fun visitAnnotation(name: String?, descriptor: String): AnnotationVisitor { 48 | return AsmClassAnnotationVisitor(super.visitAnnotation(name, descriptor), descriptor, classFinder, Context(name, this)) 49 | } 50 | 51 | override fun visitArray(name: String?): AnnotationVisitor { 52 | return AsmClassAnnotationVisitor(super.visitArray(name), descriptor, classFinder, Context(name, this)) 53 | } 54 | 55 | override fun visitEnd() { 56 | super.visitEnd() 57 | if (context != null) { 58 | // 取二者之一不为空的值 59 | val value = tempArray.ifEmpty { propertyMap.ifEmpty { null } } ?: return 60 | // 有名字则为对象,反之为数组 61 | if (context.name != null) { 62 | context.visitor.propertyMap[context.name] = value 63 | } else { 64 | context.visitor.tempArray += value 65 | } 66 | } 67 | } 68 | 69 | class Context(val name: String?, val visitor: AsmClassAnnotationVisitor) 70 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/asm/AsmClassConstructor.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.asm 2 | 3 | import org.tabooproject.reflex.* 4 | import org.tabooproject.reflex.serializer.BinaryWriter 5 | import java.lang.reflect.Modifier 6 | 7 | /** 8 | * @author 坏黑 9 | * @since 2022/1/21 6:34 PM 10 | */ 11 | @Internal 12 | class AsmClassConstructor( 13 | name: String, 14 | owner: LazyClass, 15 | val descriptor: String, 16 | val access: Int, 17 | val parameterAnnotations: Map>, 18 | val classFinder: ClassAnalyser.ClassFinder, 19 | override val annotations: List, 20 | override val parameter: List = AsmSignature.signatureToClass(descriptor, classFinder) 21 | .mapIndexed { idx, it -> 22 | LazyAnnotatedClass( 23 | it.name, 24 | it.dimensions, 25 | it.isInstant, 26 | it.isPrimitive, 27 | it.classGetter, 28 | annotations = parameterAnnotations[idx] ?: emptyList(), 29 | it.name, 30 | it.simpleName 31 | ) 32 | }, 33 | ) : JavaClassConstructor(name, owner) { 34 | 35 | override val isStatic: Boolean 36 | get() = true 37 | 38 | override val isFinal: Boolean 39 | get() = true 40 | 41 | override val isPublic: Boolean 42 | get() = Modifier.isPublic(access) 43 | 44 | override val isProtected: Boolean 45 | get() = Modifier.isProtected(access) 46 | 47 | override val isPrivate: Boolean 48 | get() = Modifier.isPrivate(access) 49 | 50 | override fun toString(): String { 51 | return "AsmClassConstructor(descriptor='$descriptor', access=$access) ${super.toString()}" 52 | } 53 | 54 | override fun writeTo(writer: BinaryWriter) { 55 | writer.writeInt(1) // 1: ASM MODE 56 | // 函数信息 57 | AsmClassMethod.writeTo(writer, name, owner, descriptor, access, parameterAnnotations, annotations, parameter) 58 | } 59 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/asm/AsmClassField.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.asm 2 | 3 | import org.tabooproject.reflex.* 4 | import org.tabooproject.reflex.serializer.BinaryWriter 5 | import java.lang.reflect.Modifier 6 | 7 | /** 8 | * @author 坏黑 9 | * @since 2022/1/21 6:33 PM 10 | */ 11 | @Internal 12 | class AsmClassField( 13 | name: String, 14 | owner: LazyClass, 15 | val descriptor: String, 16 | val access: Int, 17 | val classFinder: ClassAnalyser.ClassFinder, 18 | override val annotations: List, 19 | override val type: LazyClass = AsmSignature.signatureToClass(descriptor, classFinder).first(), 20 | ) : JavaClassField(name, owner) { 21 | 22 | override val isStatic: Boolean 23 | get() = Modifier.isStatic(access) 24 | 25 | override val isTransient: Boolean 26 | get() = Modifier.isTransient(access) 27 | 28 | override val isFinal: Boolean 29 | get() = Modifier.isFinal(access) 30 | 31 | override val isPublic: Boolean 32 | get() = Modifier.isPublic(access) 33 | 34 | override val isProtected: Boolean 35 | get() = Modifier.isProtected(access) 36 | 37 | override val isPrivate: Boolean 38 | get() = Modifier.isPrivate(access) 39 | 40 | override fun toString(): String { 41 | return "AsmClassField(descriptor='$descriptor', access=$access) ${super.toString()}" 42 | } 43 | 44 | override fun writeTo(writer: BinaryWriter) { 45 | writer.writeInt(1) // 1: ASM MODE 46 | writer.writeNullableString(name) 47 | writer.writeObj(owner) 48 | writer.writeNullableString(descriptor) 49 | writer.writeInt(access) 50 | writer.writeList(annotations) 51 | writer.writeObj(type) 52 | } 53 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/asm/AsmClassFieldVisitor.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.asm 2 | 3 | import org.objectweb.asm.AnnotationVisitor 4 | import org.objectweb.asm.FieldVisitor 5 | import org.objectweb.asm.Opcodes 6 | import org.tabooproject.reflex.ClassAnalyser 7 | import org.tabooproject.reflex.Internal 8 | 9 | /** 10 | * @author 坏黑 11 | * @since 2022/1/24 9:11 PM 12 | */ 13 | @Internal 14 | class AsmClassFieldVisitor(fieldVisitor: FieldVisitor, val classFinder: ClassAnalyser.ClassFinder) : FieldVisitor(Opcodes.ASM9, fieldVisitor), Opcodes { 15 | 16 | val annotations = ArrayList() 17 | 18 | override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor { 19 | return AsmClassAnnotationVisitor(super.visitAnnotation(descriptor, visible), descriptor, classFinder).also { 20 | annotations += AsmAnnotation(it) 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/asm/AsmClassMethod.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.asm 2 | 3 | import org.objectweb.asm.signature.SignatureReader 4 | import org.objectweb.asm.signature.SignatureVisitor 5 | import org.objectweb.asm.signature.SignatureWriter 6 | import org.tabooproject.reflex.* 7 | import org.tabooproject.reflex.serializer.BinaryReader 8 | import org.tabooproject.reflex.serializer.BinaryWriter 9 | import java.lang.reflect.Modifier 10 | 11 | /** 12 | * @author 坏黑 13 | * @since 2022/1/21 6:34 PM 14 | */ 15 | @Internal 16 | class AsmClassMethod( 17 | name: String, 18 | owner: LazyClass, 19 | val descriptor: String, 20 | val access: Int, 21 | val parameterAnnotations: Map>, 22 | val classFinder: ClassAnalyser.ClassFinder, 23 | override val annotations: List, 24 | // 延迟加载的数据 25 | private var localResult: LazyClass? = null, 26 | private val localParameter: MutableList = ArrayList(), 27 | ) : JavaClassMethod(name, owner) { 28 | 29 | override val result: LazyClass 30 | get() = localResult!! 31 | 32 | override val parameter: List 33 | get() = localParameter 34 | 35 | override val isStatic: Boolean 36 | get() = Modifier.isStatic(access) 37 | 38 | override val isFinal: Boolean 39 | get() = Modifier.isFinal(access) 40 | 41 | override val isPublic: Boolean 42 | get() = Modifier.isPublic(access) 43 | 44 | override val isProtected: Boolean 45 | get() = Modifier.isProtected(access) 46 | 47 | override val isPrivate: Boolean 48 | get() = Modifier.isPrivate(access) 49 | 50 | override val isNative: Boolean 51 | get() = Modifier.isNative(access) 52 | 53 | override val isAbstract: Boolean 54 | get() = Modifier.isAbstract(access) 55 | 56 | override val isVolatile: Boolean 57 | get() = Modifier.isVolatile(access) 58 | 59 | override val isSynchronized: Boolean 60 | get() = Modifier.isSynchronized(access) 61 | 62 | fun read() { 63 | // println("AsmClassMethod.read $name$descriptor") 64 | var visitParameterType = false 65 | var visitReturnType = false 66 | var dimensions = 0 67 | SignatureReader(descriptor).accept(object : SignatureWriter() { 68 | 69 | override fun visitParameterType(): SignatureVisitor { 70 | // println(" visitParameterType") 71 | visitParameterType = true 72 | visitReturnType = false 73 | return super.visitParameterType() 74 | } 75 | 76 | override fun visitReturnType(): SignatureVisitor { 77 | // println(" visitReturnType") 78 | visitParameterType = false 79 | visitReturnType = true 80 | return super.visitReturnType() 81 | } 82 | 83 | override fun visitClassType(name: String) { 84 | // println(" visitClassType $name, dim=$dimensions") 85 | if (visitParameterType) { 86 | val annotations = parameterAnnotations[localParameter.size] ?: emptyList() 87 | localParameter += LazyAnnotatedClass.of(name, dimensions, annotations = annotations, classFinder = classFinder) 88 | } 89 | if (visitReturnType) { 90 | localResult = LazyClass.of(name, dimensions, classFinder = classFinder) 91 | } 92 | dimensions = 0 93 | super.visitClassType(name) 94 | } 95 | 96 | override fun visitBaseType(descriptor: Char) { 97 | // println(" visitBaseType $descriptor, dim=$dimensions") 98 | if (visitParameterType) { 99 | val annotations = parameterAnnotations[localParameter.size] ?: emptyList() 100 | localParameter += LazyAnnotatedClass.of(descriptor.toString(), dimensions, isPrimitive = true, annotations) { Reflection.getPrimitiveType(descriptor) } 101 | } 102 | if (visitReturnType) { 103 | localResult = LazyClass.of(descriptor.toString(), dimensions, isPrimitive = true) { Reflection.getPrimitiveType(descriptor) } 104 | } 105 | dimensions = 0 106 | super.visitBaseType(descriptor) 107 | } 108 | 109 | override fun visitArrayType(): SignatureVisitor { 110 | // println(" visitArrayType") 111 | dimensions++ 112 | return super.visitArrayType() 113 | } 114 | }) 115 | } 116 | 117 | override fun toString(): String { 118 | return "AsmClassMethod(descriptor='$descriptor', access=$access) ${super.toString()}" 119 | } 120 | 121 | override fun writeTo(writer: BinaryWriter) { 122 | writer.writeInt(1) // 1: ASM MODE 123 | // 函数信息 124 | writeTo(writer, name, owner, descriptor, access, parameterAnnotations, annotations, parameter) 125 | // 返回值 126 | writer.writeObj(result) 127 | } 128 | 129 | companion object { 130 | 131 | fun writeTo( 132 | writer: BinaryWriter, 133 | name: String, 134 | owner: LazyClass, 135 | descriptor: String, 136 | access: Int, 137 | parameterAnnotations: Map>, 138 | annotations: List, 139 | parameter: List, 140 | ) { 141 | writer.writeNullableString(name) 142 | writer.writeObj(owner) 143 | writer.writeNullableString(descriptor) 144 | writer.writeInt(access) 145 | // 参数注解 146 | writer.writeInt(parameterAnnotations.size) 147 | parameterAnnotations.forEach { (k, v) -> 148 | writer.writeInt(k) 149 | writer.writeList(v) 150 | } 151 | // 注解 152 | writer.writeList(annotations) 153 | // 参数 154 | writer.writeList(parameter) 155 | } 156 | 157 | fun readFrom(reader: BinaryReader, classFinder: ClassAnalyser.ClassFinder?): Method { 158 | val name = reader.readNullableString()!! 159 | val owner = LazyClass.of(reader, classFinder) 160 | val descriptor = reader.readNullableString()!! 161 | val access = reader.readInt() 162 | // 参数注解 163 | val parameterAnnotations = HashMap>() 164 | val size = reader.readInt() 165 | for (i in 0 until size) { 166 | val index = reader.readInt() 167 | val annotations = reader.readAnnotationList(classFinder) 168 | parameterAnnotations[index] = annotations 169 | } 170 | // 注解 171 | val annotations = reader.readAnnotationList(classFinder) 172 | // 参数 173 | val parameter = reader.readAnnotationClassList(classFinder) 174 | return Method(name, owner, descriptor, access, parameterAnnotations, annotations, parameter) 175 | } 176 | 177 | class Method( 178 | val name: String, 179 | val owner: LazyClass, 180 | val descriptor: String, 181 | val access: Int, 182 | val parameterAnnotations: Map>, 183 | val annotations: List, 184 | val parameter: MutableList, 185 | ) 186 | } 187 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/asm/AsmClassMethodVisitor.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.asm 2 | 3 | import org.objectweb.asm.AnnotationVisitor 4 | import org.objectweb.asm.MethodVisitor 5 | import org.objectweb.asm.Opcodes 6 | import org.tabooproject.reflex.ClassAnalyser 7 | import org.tabooproject.reflex.Internal 8 | 9 | /** 10 | * @author 坏黑 11 | * @since 2022/1/24 9:11 PM 12 | */ 13 | @Internal 14 | class AsmClassMethodVisitor(methodVisitor: MethodVisitor, val classFinder: ClassAnalyser.ClassFinder) : MethodVisitor(Opcodes.ASM9, methodVisitor), Opcodes { 15 | 16 | val annotations = ArrayList() 17 | val parameterAnnotations = LinkedHashMap>() 18 | 19 | override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor { 20 | return AsmClassAnnotationVisitor(super.visitAnnotation(descriptor, visible), descriptor, classFinder).also { 21 | annotations += AsmAnnotation(it) 22 | } 23 | } 24 | 25 | override fun visitParameterAnnotation(parameter: Int, descriptor: String, visible: Boolean): AnnotationVisitor { 26 | return AsmClassAnnotationVisitor(super.visitParameterAnnotation(parameter, descriptor, visible), descriptor, classFinder).also { 27 | parameterAnnotations.getOrPut(parameter) { ArrayList() } += AsmAnnotation(it) 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/asm/AsmClassVisitor.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.asm 2 | 3 | import org.objectweb.asm.* 4 | import org.tabooproject.reflex.ClassAnalyser 5 | import org.tabooproject.reflex.ClassAnnotation 6 | import org.tabooproject.reflex.Internal 7 | import org.tabooproject.reflex.LazyClass 8 | 9 | /** 10 | * @author izzel 11 | */ 12 | @Internal 13 | class AsmClassVisitor(val owner: LazyClass, val classFinder: ClassAnalyser.ClassFinder, classVisitor: ClassVisitor) : ClassVisitor(Opcodes.ASM9, classVisitor), Opcodes { 14 | 15 | var access = -1 16 | var superclass: LazyClass? = null 17 | val interfaces = ArrayList() 18 | val annotations = ArrayList() 19 | val fields = ArrayList() 20 | val methods = ArrayList() 21 | val constructors = ArrayList() 22 | 23 | override fun visit(version: Int, access: Int, name: String?, signature: String?, superName: String?, interfaces: Array?) { 24 | super.visit(version, access, name, signature, superName, interfaces) 25 | this.access = access 26 | // 获取父类信息 27 | if (superName != null) { 28 | this.superclass = LazyClass.of(superName, classFinder = classFinder) 29 | } 30 | // 获取接口信息 31 | if (interfaces != null) { 32 | this.interfaces += interfaces.map { LazyClass.of(it, classFinder = classFinder) } 33 | } 34 | } 35 | 36 | override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor { 37 | return AsmClassAnnotationVisitor(super.visitAnnotation(descriptor, visible), descriptor, classFinder).apply { 38 | annotations += AsmAnnotation(this) 39 | } 40 | } 41 | 42 | override fun visitField(access: Int, name: String, descriptor: String, signature: String?, value: Any?): FieldVisitor { 43 | return AsmClassFieldVisitor(super.visitField(access, name, descriptor, signature, value), classFinder).apply { 44 | fields += AsmClassField(name, owner, descriptor, access, classFinder, annotations) 45 | } 46 | } 47 | 48 | override fun visitMethod(access: Int, name: String, descriptor: String, signature: String?, exceptions: Array?): MethodVisitor { 49 | if (name == "") { 50 | return super.visitMethod(access, name, descriptor, signature, exceptions) 51 | } 52 | return AsmClassMethodVisitor(super.visitMethod(access, name, descriptor, signature, exceptions), classFinder).apply { 53 | if (name == "") { 54 | constructors += AsmClassConstructor(name, owner, descriptor, access, parameterAnnotations, classFinder, annotations) 55 | } else { 56 | methods += AsmClassMethod(name, owner, descriptor, access, parameterAnnotations, classFinder, annotations) 57 | } 58 | } 59 | } 60 | 61 | override fun visitEnd() { 62 | methods.forEach { it.read() } 63 | } 64 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/asm/AsmSignature.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.asm 2 | 3 | import org.objectweb.asm.signature.SignatureReader 4 | import org.objectweb.asm.signature.SignatureVisitor 5 | import org.objectweb.asm.signature.SignatureWriter 6 | import org.tabooproject.reflex.ClassAnalyser 7 | import org.tabooproject.reflex.Internal 8 | import org.tabooproject.reflex.LazyClass 9 | import org.tabooproject.reflex.Reflection 10 | import java.util.concurrent.ConcurrentHashMap 11 | 12 | @Internal 13 | object AsmSignature { 14 | 15 | val cacheMap = ConcurrentHashMap>() 16 | 17 | fun signatureToClass(signature: String, classFinder: ClassAnalyser.ClassFinder? = null): List { 18 | return cacheMap.getOrPut(signature) { 19 | val list = ArrayList() 20 | var dimensions = 0 21 | SignatureReader(signature).accept(object : SignatureWriter() { 22 | 23 | override fun visitClassType(name: String) { 24 | super.visitClassType(name) 25 | list.add(LazyClass.of(name, dimensions, isPrimitive = false, classFinder)) 26 | } 27 | 28 | override fun visitBaseType(descriptor: Char) { 29 | super.visitBaseType(descriptor) 30 | list.add(LazyClass.of(descriptor.toString(), dimensions, isPrimitive = true) { Reflection.getPrimitiveType(descriptor) }) 31 | } 32 | 33 | override fun visitArrayType(): SignatureVisitor { 34 | super.visitArrayType() 35 | dimensions++ 36 | return this 37 | } 38 | }) 39 | if (list.lastOrNull()?.name == "V") { 40 | // Caused by: java.lang.NoSuchMethodError: 'java.lang.Object java.util.ArrayList.removeLast()' 41 | // 你在逗我玩吗兄弟? 42 | list.removeAt(list.size - 1) 43 | } 44 | return list 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/reflection/InstantAnnotation.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.reflection 2 | 3 | import org.tabooproject.reflex.ClassAnnotation 4 | import org.tabooproject.reflex.Internal 5 | import org.tabooproject.reflex.LazyClass 6 | import org.tabooproject.reflex.serializer.BinaryWriter 7 | 8 | /** 9 | * @author 坏黑 10 | * @since 2022/1/24 8:48 PM 11 | */ 12 | @Internal 13 | class InstantAnnotation(val annotation: Annotation) : ClassAnnotation(LazyClass.of(annotation.annotationClass.java)) { 14 | 15 | val methods = source.instance!!.methods.filter { it.name !in internalMethods }.associateBy { it.name } 16 | 17 | @Suppress("UNCHECKED_CAST") 18 | override fun property(name: String): T? { 19 | return methods[name]?.invoke(annotation) as? T 20 | } 21 | 22 | override fun property(name: String, def: T): T { 23 | return property(name) ?: def 24 | } 25 | 26 | override fun properties(): Map { 27 | return methods.mapValues { it.value.invoke(annotation) } 28 | } 29 | 30 | override fun propertyKeys(): Set { 31 | return methods.keys 32 | } 33 | 34 | override fun toString(): String { 35 | return "InstantAnnotation() ${super.toString()}" 36 | } 37 | 38 | override fun writeTo(writer: BinaryWriter) { 39 | error("InstantAnnotation cannot be serialized") 40 | } 41 | 42 | companion object { 43 | 44 | private val internalMethods = arrayOf("equals", "hashCode", "toString", "annotationType") 45 | } 46 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/reflection/InstantClassConstructor.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.reflection 2 | 3 | import org.tabooproject.reflex.* 4 | import org.tabooproject.reflex.serializer.BinaryWriter 5 | import java.lang.reflect.Constructor 6 | import java.lang.reflect.Modifier 7 | 8 | /** 9 | * @author 坏黑 10 | * @since 2022/1/21 7:11 PM 11 | */ 12 | @Internal 13 | class InstantClassConstructor(owner: LazyClass, private val constructor: Constructor<*>) : JavaClassConstructor("", owner) { 14 | 15 | val annotationsLocal by lazy(LazyThreadSafetyMode.NONE) { 16 | constructor.declaredAnnotations.map { InstantAnnotation(it) } 17 | } 18 | 19 | val parameterLocal by lazy(LazyThreadSafetyMode.NONE) { 20 | val parameterAnnotations = constructor.parameterAnnotations 21 | constructor.parameterTypes.mapIndexed { idx, it -> LazyAnnotatedClass.of(it, annotations = parameterAnnotations[idx].map { i -> InstantAnnotation(i) }) } 22 | } 23 | 24 | override val isStatic: Boolean 25 | get() = true 26 | 27 | override val isFinal: Boolean 28 | get() = true 29 | 30 | override val isPublic: Boolean 31 | get() = Modifier.isPublic(constructor.modifiers) 32 | 33 | override val isProtected: Boolean 34 | get() = Modifier.isProtected(constructor.modifiers) 35 | 36 | override val isPrivate: Boolean 37 | get() = Modifier.isPrivate(constructor.modifiers) 38 | 39 | override val parameter: List 40 | get() = parameterLocal 41 | 42 | override val annotations: List 43 | get() = annotationsLocal 44 | 45 | override fun toString(): String { 46 | return "InstantClassConstructor(constructor=$constructor)" 47 | } 48 | 49 | override fun writeTo(writer: BinaryWriter) { 50 | error("InstantClassConstructor cannot be serialized") 51 | } 52 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/reflection/InstantClassField.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.reflection 2 | 3 | import org.tabooproject.reflex.ClassAnnotation 4 | import org.tabooproject.reflex.Internal 5 | import org.tabooproject.reflex.JavaClassField 6 | import org.tabooproject.reflex.LazyClass 7 | import org.tabooproject.reflex.serializer.BinaryWriter 8 | import java.lang.reflect.Field 9 | import java.lang.reflect.Modifier 10 | 11 | /** 12 | * @author 坏黑 13 | * @since 2022/1/21 7:11 PM 14 | */ 15 | @Internal 16 | class InstantClassField(owner: LazyClass, private val field: Field) : JavaClassField(field.name, owner) { 17 | 18 | val annotationsLocal = field.declaredAnnotations.map { InstantAnnotation(it) } 19 | 20 | override val type: LazyClass 21 | get() = LazyClass.of(this.field.type) 22 | 23 | override val isTransient: Boolean 24 | get() = Modifier.isTransient(this.field.modifiers) 25 | 26 | override val isStatic: Boolean 27 | get() = Modifier.isStatic(this.field.modifiers) 28 | 29 | override val isFinal: Boolean 30 | get() = Modifier.isFinal(this.field.modifiers) 31 | 32 | override val isPublic: Boolean 33 | get() = Modifier.isPublic(this.field.modifiers) 34 | 35 | override val isProtected: Boolean 36 | get() = Modifier.isProtected(this.field.modifiers) 37 | 38 | override val isPrivate: Boolean 39 | get() = Modifier.isPrivate(this.field.modifiers) 40 | 41 | override val annotations: List 42 | get() = annotationsLocal 43 | 44 | override fun toString(): String { 45 | return "InstantClassField(field=$field)" 46 | } 47 | 48 | override fun writeTo(writer: BinaryWriter) { 49 | error("InstantClassField cannot be serialized") 50 | } 51 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/reflection/InstantClassMethod.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.reflection 2 | 3 | import org.tabooproject.reflex.* 4 | import org.tabooproject.reflex.serializer.BinaryWriter 5 | import java.lang.reflect.Method 6 | import java.lang.reflect.Modifier 7 | 8 | /** 9 | * @author 坏黑 10 | * @since 2022/1/21 7:11 PM 11 | */ 12 | @Internal 13 | class InstantClassMethod(owner: LazyClass, private val method: Method) : JavaClassMethod(method.name, owner) { 14 | 15 | val annotationsLocal by lazy(LazyThreadSafetyMode.NONE) { 16 | method.declaredAnnotations.map { InstantAnnotation(it) } 17 | } 18 | 19 | val parameterLocal by lazy(LazyThreadSafetyMode.NONE) { 20 | val parameterAnnotations = method.parameterAnnotations 21 | method.parameterTypes.mapIndexed { idx, it -> LazyAnnotatedClass.of(it, annotations = parameterAnnotations[idx].map { i -> InstantAnnotation(i) }) } 22 | } 23 | 24 | override val result: LazyClass 25 | get() = LazyClass.of(method.returnType) 26 | 27 | override val parameter: List 28 | get() = parameterLocal 29 | 30 | override val annotations: List 31 | get() = annotationsLocal 32 | 33 | override val isStatic: Boolean 34 | get() = Modifier.isStatic(method.modifiers) 35 | 36 | override val isFinal: Boolean 37 | get() = Modifier.isFinal(method.modifiers) 38 | 39 | override val isPublic: Boolean 40 | get() = Modifier.isPublic(method.modifiers) 41 | 42 | override val isProtected: Boolean 43 | get() = Modifier.isProtected(method.modifiers) 44 | 45 | override val isPrivate: Boolean 46 | get() = Modifier.isPrivate(method.modifiers) 47 | 48 | override val isNative: Boolean 49 | get() = Modifier.isNative(method.modifiers) 50 | 51 | override val isAbstract: Boolean 52 | get() = Modifier.isAbstract(method.modifiers) 53 | 54 | override val isVolatile: Boolean 55 | get() = Modifier.isVolatile(method.modifiers) 56 | 57 | override val isSynchronized: Boolean 58 | get() = Modifier.isSynchronized(method.modifiers) 59 | 60 | override fun toString(): String { 61 | return "InstantClassMethod(method=$method)" 62 | } 63 | 64 | override fun writeTo(writer: BinaryWriter) { 65 | error("InstantClassMethod cannot be serialized") 66 | } 67 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/serializer/BinaryReader.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.serializer 2 | 3 | import org.tabooproject.reflex.* 4 | import java.nio.ByteBuffer 5 | import java.nio.ByteOrder 6 | import java.nio.charset.StandardCharsets 7 | import java.util.function.Supplier 8 | 9 | /** 10 | * 二进制读取器 11 | * 基于 ByteBuffer 实现高效读取 12 | */ 13 | class BinaryReader(bytes: ByteArray) { 14 | 15 | val buffer: ByteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN) 16 | 17 | fun readNullableString(): String? { 18 | val size = buffer.int 19 | return if (size == -1) null else { 20 | val bytes = ByteArray(size) 21 | buffer.get(bytes, 0, size) 22 | String(bytes, StandardCharsets.UTF_8) 23 | } 24 | } 25 | 26 | fun readString(): String { 27 | val size = buffer.int 28 | val bytes = ByteArray(size) 29 | buffer.get(bytes, 0, size) 30 | return String(bytes, StandardCharsets.UTF_8) 31 | } 32 | 33 | fun readByte(): Byte = buffer.get() 34 | 35 | fun readShort(): Short = buffer.short 36 | 37 | fun readInt(): Int = buffer.int 38 | 39 | fun readLong(): Long = buffer.long 40 | 41 | fun readFloat(): Float = buffer.float 42 | 43 | fun readDouble(): Double = buffer.double 44 | 45 | fun readChar(): Char = buffer.char 46 | 47 | fun readBoolean(): Boolean = buffer.get() != 0.toByte() 48 | 49 | inline fun readList(factory: () -> T): MutableList { 50 | val size = buffer.int 51 | return (0 until size).mapTo(ArrayList()) { factory() } 52 | } 53 | 54 | inline fun readArray(factory: () -> T): Array { 55 | val size = buffer.int 56 | return Array(size) { factory() } 57 | } 58 | 59 | fun readClassList(classFinder: ClassAnalyser.ClassFinder?): MutableList { 60 | val size = buffer.int 61 | return (0 until size).mapTo(ArrayList()) { LazyClass.of(this, classFinder) } 62 | } 63 | 64 | fun readAnnotationClassList(classFinder: ClassAnalyser.ClassFinder?): MutableList { 65 | val size = buffer.int 66 | return (0 until size).mapTo(ArrayList()) { LazyClass.of(this, classFinder) as LazyAnnotatedClass } 67 | } 68 | 69 | fun readAnnotationList(classFinder: ClassAnalyser.ClassFinder?): MutableList { 70 | val size = buffer.int 71 | return (0 until size).mapTo(ArrayList()) { ClassAnnotation.of(this, classFinder) } 72 | } 73 | 74 | fun readFieldList(classFinder: ClassAnalyser.ClassFinder?): MutableList { 75 | val size = buffer.int 76 | return (0 until size).mapTo(ArrayList()) { ClassField.of(this, classFinder) } 77 | } 78 | 79 | fun readMethodList(classFinder: ClassAnalyser.ClassFinder?): MutableList { 80 | val size = buffer.int 81 | return (0 until size).mapTo(ArrayList()) { ClassMethod.of(this, classFinder) } 82 | } 83 | 84 | fun readConstructorList(classFinder: ClassAnalyser.ClassFinder?): MutableList { 85 | val size = buffer.int 86 | return (0 until size).mapTo(ArrayList()) { ClassConstructor.of(this, classFinder) } 87 | } 88 | 89 | fun readNullableObj(factor: Supplier): T? { 90 | val size = buffer.int 91 | return if (size == -1) null else factor.get() 92 | } 93 | 94 | fun readNullableClass(classFinder: ClassAnalyser.ClassFinder?): LazyClass? { 95 | val size = buffer.int 96 | return if (size == -1) null else LazyClass.of(this, classFinder) 97 | } 98 | 99 | fun readAnnotationProperties(classFinder: ClassAnalyser.ClassFinder?): Map { 100 | val size = buffer.int 101 | return (0 until size).associate { 102 | val key = readNullableString()!! 103 | val value = BinarySerializer.readFrom(this, classFinder) 104 | key to value 105 | } 106 | } 107 | 108 | companion object { 109 | 110 | fun from(bytes: ByteArray): BinaryReader = BinaryReader(bytes) 111 | } 112 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/serializer/BinarySerializable.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.serializer 2 | 3 | interface BinarySerializable { 4 | 5 | /** 6 | * 写入到 ByteBuffer 7 | */ 8 | fun writeTo(writer: BinaryWriter) 9 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/serializer/BinarySerializer.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.serializer 2 | 3 | import org.objectweb.asm.Type 4 | import org.tabooproject.reflex.ClassAnalyser 5 | import org.tabooproject.reflex.LazyClass 6 | import org.tabooproject.reflex.LazyEnum 7 | import org.tabooproject.reflex.Reflection 8 | import java.io.DataOutputStream 9 | 10 | object BinarySerializer { 11 | 12 | // 类型映射表 13 | private val typeMapping = hashMapOf, SerializationType>().apply { 14 | // Kotlin 类型 15 | put(Byte::class.java, SerializationType.BYTE) 16 | put(Short::class.java, SerializationType.SHORT) 17 | put(Int::class.java, SerializationType.INT) 18 | put(Long::class.java, SerializationType.LONG) 19 | put(Float::class.java, SerializationType.FLOAT) 20 | put(Double::class.java, SerializationType.DOUBLE) 21 | put(Boolean::class.java, SerializationType.BOOLEAN) 22 | put(Char::class.java, SerializationType.CHAR) 23 | 24 | // Java 类型 25 | put(java.lang.Byte::class.java, SerializationType.BYTE) 26 | put(java.lang.Short::class.java, SerializationType.SHORT) 27 | put(java.lang.Integer::class.java, SerializationType.INT) 28 | put(java.lang.Long::class.java, SerializationType.LONG) 29 | put(java.lang.Float::class.java, SerializationType.FLOAT) 30 | put(java.lang.Double::class.java, SerializationType.DOUBLE) 31 | put(java.lang.Boolean::class.java, SerializationType.BOOLEAN) 32 | put(java.lang.Character::class.java, SerializationType.CHAR) 33 | 34 | // 其他类型保持不变 35 | put(String::class.java, SerializationType.STRING) 36 | put(Class::class.java, SerializationType.CLASS) 37 | put(LazyClass::class.java, SerializationType.LAZY_CLASS) 38 | put(Enum::class.java, SerializationType.ENUM) 39 | put(LazyEnum::class.java, SerializationType.LAZY_ENUM) 40 | put(Type::class.java, SerializationType.TYPE) 41 | 42 | // 基础类型数组 43 | put(ByteArray::class.java, SerializationType.BYTE_ARRAY) 44 | put(ShortArray::class.java, SerializationType.SHORT_ARRAY) 45 | put(IntArray::class.java, SerializationType.INT_ARRAY) 46 | put(LongArray::class.java, SerializationType.LONG_ARRAY) 47 | put(FloatArray::class.java, SerializationType.FLOAT_ARRAY) 48 | put(DoubleArray::class.java, SerializationType.DOUBLE_ARRAY) 49 | put(BooleanArray::class.java, SerializationType.BOOLEAN_ARRAY) 50 | put(CharArray::class.java, SerializationType.CHAR_ARRAY) 51 | } 52 | 53 | // 获取类型的辅助方法 54 | fun getSerializationType(value: Any): SerializationType { 55 | return when (value) { 56 | is Array<*> -> SerializationType.ARRAY 57 | is List<*> -> SerializationType.LIST 58 | is Map<*, *> -> SerializationType.MAP 59 | is Enum<*> -> SerializationType.ENUM 60 | else -> { 61 | val clazz = Reflection.getReferenceType(value.javaClass) 62 | typeMapping[clazz] ?: error("Unsupported type: $clazz") 63 | } 64 | } 65 | } 66 | 67 | fun writeTo(output: DataOutputStream, value: Any) { 68 | val type = getSerializationType(value) 69 | output.writeInt(type.id) 70 | when (type) { 71 | SerializationType.BYTE -> output.writeByte((value as Byte).toInt()) 72 | SerializationType.SHORT -> output.writeShort((value as Short).toInt()) 73 | SerializationType.INT -> output.writeInt(value as Int) 74 | SerializationType.LONG -> output.writeLong(value as Long) 75 | SerializationType.FLOAT -> output.writeFloat(value as Float) 76 | SerializationType.DOUBLE -> output.writeDouble(value as Double) 77 | SerializationType.BOOLEAN -> output.writeBoolean(value as Boolean) 78 | SerializationType.CHAR -> output.writeChar((value as Char).code) 79 | SerializationType.STRING -> { 80 | val str = value as String 81 | output.writeInt(str.length) 82 | output.write(str.toByteArray(), 0, str.length) 83 | } 84 | 85 | SerializationType.ARRAY -> { 86 | val array = value as Array<*> 87 | output.writeInt(array.size) 88 | array.forEach { writeTo(output, it!!) } 89 | } 90 | 91 | SerializationType.LIST -> { 92 | val list = value as List<*> 93 | output.writeInt(list.size) 94 | list.forEach { writeTo(output, it!!) } 95 | } 96 | 97 | SerializationType.MAP -> { 98 | val map = value as Map<*, *> 99 | output.writeInt(map.size) 100 | map.forEach { (k, v) -> 101 | writeTo(output, k!!) 102 | writeTo(output, v!!) 103 | } 104 | } 105 | 106 | SerializationType.BYTE_ARRAY -> { 107 | val array = value as ByteArray 108 | output.writeInt(array.size) 109 | output.write(array) 110 | } 111 | 112 | SerializationType.SHORT_ARRAY -> { 113 | val array = value as ShortArray 114 | output.writeInt(array.size) 115 | array.forEach { output.writeShort(it.toInt()) } 116 | } 117 | 118 | SerializationType.INT_ARRAY -> { 119 | val array = value as IntArray 120 | output.writeInt(array.size) 121 | array.forEach { output.writeInt(it) } 122 | } 123 | 124 | SerializationType.LONG_ARRAY -> { 125 | val array = value as LongArray 126 | output.writeInt(array.size) 127 | array.forEach { output.writeLong(it) } 128 | } 129 | 130 | SerializationType.FLOAT_ARRAY -> { 131 | val array = value as FloatArray 132 | output.writeInt(array.size) 133 | array.forEach { output.writeFloat(it) } 134 | } 135 | 136 | SerializationType.DOUBLE_ARRAY -> { 137 | val array = value as DoubleArray 138 | output.writeInt(array.size) 139 | array.forEach { output.writeDouble(it) } 140 | } 141 | 142 | SerializationType.BOOLEAN_ARRAY -> { 143 | val array = value as BooleanArray 144 | output.writeInt(array.size) 145 | array.forEach { output.writeBoolean(it) } 146 | } 147 | 148 | SerializationType.CHAR_ARRAY -> { 149 | val array = value as CharArray 150 | output.writeInt(array.size) 151 | array.forEach { output.writeChar(it.code) } 152 | } 153 | 154 | SerializationType.CLASS -> { 155 | output.writeInt(1) // 1:表示 LazyClass 156 | LazyClass.writeTo(value as Class<*>, output) 157 | } 158 | 159 | SerializationType.LAZY_CLASS -> { 160 | val lazyClass = value as LazyClass 161 | output.write(BinaryWriter().also { lazyClass.writeTo(it) }.toByteArray()) 162 | } 163 | 164 | SerializationType.ENUM -> { 165 | output.writeInt(1) // 1:表示 LazyClass 166 | val enum = value as Enum<*> 167 | // 名字 168 | LazyClass.writeTo(enum.javaClass, output) 169 | // 枚举名 170 | output.writeInt(enum.name.length) 171 | output.write(enum.name.toByteArray(), 0, enum.name.length) 172 | } 173 | 174 | SerializationType.LAZY_ENUM -> { 175 | val lazyEnum = value as LazyEnum 176 | output.write(BinaryWriter().also { lazyEnum.source.writeTo(it) }.toByteArray()) 177 | output.writeInt(lazyEnum.name.length) 178 | output.write(lazyEnum.name.toByteArray(), 0, lazyEnum.name.length) 179 | } 180 | 181 | SerializationType.TYPE -> { 182 | val type = value as Type 183 | val name = type.descriptor 184 | output.writeInt(name.length) 185 | output.write(name.toByteArray(), 0, name.length) 186 | } 187 | } 188 | } 189 | 190 | fun readFrom(reader: BinaryReader, classFinder: ClassAnalyser.ClassFinder?): Any { 191 | val typeId = reader.readInt() 192 | val type = SerializationType.fromId(typeId) 193 | return when (type) { 194 | SerializationType.BYTE -> reader.readByte() 195 | SerializationType.SHORT -> reader.readShort() 196 | SerializationType.INT -> reader.readInt() 197 | SerializationType.LONG -> reader.readLong() 198 | SerializationType.FLOAT -> reader.readFloat() 199 | SerializationType.DOUBLE -> reader.readDouble() 200 | SerializationType.BOOLEAN -> reader.readBoolean() 201 | SerializationType.CHAR -> reader.readChar() 202 | SerializationType.STRING -> reader.readString() 203 | SerializationType.ARRAY -> reader.readArray { readFrom(reader, classFinder) } 204 | SerializationType.LIST -> reader.readList { readFrom(reader, classFinder) } 205 | SerializationType.MAP -> { 206 | val size = reader.readInt() 207 | HashMap(size).apply { 208 | repeat(size) { 209 | val key = readFrom(reader, classFinder) 210 | val value = readFrom(reader, classFinder) 211 | put(key, value) 212 | } 213 | } 214 | } 215 | 216 | SerializationType.BYTE_ARRAY -> { 217 | val size = reader.readInt() 218 | ByteArray(size) { reader.readByte() } 219 | } 220 | 221 | SerializationType.SHORT_ARRAY -> { 222 | val size = reader.readInt() 223 | ShortArray(size) { reader.readShort() } 224 | } 225 | 226 | SerializationType.INT_ARRAY -> { 227 | val size = reader.readInt() 228 | IntArray(size) { reader.readInt() } 229 | } 230 | 231 | SerializationType.LONG_ARRAY -> { 232 | val size = reader.readInt() 233 | LongArray(size) { reader.readLong() } 234 | } 235 | 236 | SerializationType.FLOAT_ARRAY -> { 237 | val size = reader.readInt() 238 | FloatArray(size) { reader.readFloat() } 239 | } 240 | 241 | SerializationType.DOUBLE_ARRAY -> { 242 | val size = reader.readInt() 243 | DoubleArray(size) { reader.readDouble() } 244 | } 245 | 246 | SerializationType.BOOLEAN_ARRAY -> { 247 | val size = reader.readInt() 248 | BooleanArray(size) { reader.readBoolean() } 249 | } 250 | 251 | SerializationType.CHAR_ARRAY -> { 252 | val size = reader.readInt() 253 | CharArray(size) { reader.readChar() } 254 | } 255 | 256 | SerializationType.CLASS, SerializationType.LAZY_CLASS -> { 257 | LazyClass.of(reader, classFinder) 258 | } 259 | 260 | SerializationType.ENUM, SerializationType.LAZY_ENUM -> { 261 | LazyEnum(LazyClass.of(reader, classFinder), reader.readString()) 262 | } 263 | 264 | SerializationType.TYPE -> { 265 | Type.getType(reader.readString()) 266 | } 267 | } 268 | } 269 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/serializer/BinaryWriter.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.serializer 2 | 3 | import java.io.ByteArrayOutputStream 4 | import java.io.DataOutputStream 5 | import java.util.function.Consumer 6 | 7 | /** 8 | * 二进制写入器 9 | * 基于 ByteArrayOutputStream 实现自动扩容 10 | */ 11 | class BinaryWriter { 12 | 13 | private val byteArrayOutputStream = ByteArrayOutputStream() 14 | val output = DataOutputStream(byteArrayOutputStream) 15 | 16 | /** 17 | * 写入可为空的字符串 18 | */ 19 | fun writeNullableString(str: String?) { 20 | if (str == null) { 21 | output.writeInt(-1) 22 | } else { 23 | val bytes = str.toByteArray() 24 | output.writeInt(bytes.size) 25 | output.write(bytes) 26 | } 27 | } 28 | 29 | fun writeInt(value: Int) { 30 | output.writeInt(value) 31 | } 32 | 33 | fun writeBoolean(value: Boolean) { 34 | output.writeBoolean(value) 35 | } 36 | 37 | fun writeList(list: List) { 38 | output.writeInt(list.size) 39 | list.forEach { it.writeTo(this) } 40 | } 41 | 42 | fun writeList(list: List, writer: Consumer) { 43 | output.writeInt(list.size) 44 | list.forEach { writer.accept(it) } 45 | } 46 | 47 | fun writeObj(obj: BinarySerializable) { 48 | obj.writeTo(this) 49 | } 50 | 51 | fun writeNullableObj(obj: BinarySerializable?) { 52 | if (obj == null) { 53 | output.writeInt(-1) 54 | } else { 55 | output.writeInt(0) 56 | obj.writeTo(this) 57 | } 58 | } 59 | 60 | /** 61 | * 写入注解属性 62 | */ 63 | fun writeAnnotationProperties(properties: Map) { 64 | output.writeInt(properties.size) 65 | properties.forEach { (key, value) -> 66 | writeNullableString(key) 67 | BinarySerializer.writeTo(output, value) 68 | } 69 | } 70 | 71 | /** 72 | * 获取写入的字节数组 73 | */ 74 | fun toByteArray(): ByteArray { 75 | return byteArrayOutputStream.toByteArray() 76 | } 77 | } -------------------------------------------------------------------------------- /analyser/src/main/kotlin/org/tabooproject/reflex/serializer/SerializationType.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.serializer 2 | 3 | import org.tabooproject.reflex.serializer.SerializationType.values 4 | 5 | enum class SerializationType(val id: Int) { 6 | 7 | // 基础类型 8 | BYTE(0), 9 | SHORT(1), 10 | INT(2), 11 | LONG(3), 12 | FLOAT(4), 13 | DOUBLE(5), 14 | BOOLEAN(6), 15 | CHAR(7), 16 | STRING(8), 17 | 18 | // 数组和集合 19 | ARRAY(10), 20 | LIST(11), 21 | 22 | // 嵌套对象会被转换为 Map 23 | MAP(12), 24 | 25 | // 类相关 26 | CLASS(21), 27 | LAZY_CLASS(22), 28 | ENUM(23), 29 | LAZY_ENUM(24), 30 | TYPE(25), // ASM 类型 31 | 32 | // 基础类型数组 33 | BYTE_ARRAY(31), 34 | SHORT_ARRAY(32), 35 | INT_ARRAY(33), 36 | LONG_ARRAY(34), 37 | FLOAT_ARRAY(35), 38 | DOUBLE_ARRAY(36), 39 | BOOLEAN_ARRAY(37), 40 | CHAR_ARRAY(38); 41 | 42 | companion object { 43 | 44 | val typeMap = values().associateBy { it.id } 45 | 46 | fun fromId(id: Int): SerializationType { 47 | return typeMap[id] ?: error("Unknown type id: $id") 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /analyser/src/test/java/org/tabooproject/reflex/res/DependencyScope.java: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.res; 2 | 3 | /** 4 | * The scope of a dependency 5 | * 6 | * @author Zach Deibert, sky 7 | * @since 1.0.0 8 | */ 9 | public enum DependencyScope { 10 | 11 | /** 12 | * 依赖项在编译代码时需要,因此它将在运行时解析依赖项时下载。 13 | */ 14 | COMPILE, 15 | 16 | /** 17 | * 依赖项由运行时环境提供,因此在运行时解析依赖项时无需下载。 18 | */ 19 | PROVIDED, 20 | 21 | /** 22 | * 依赖项在应用程序运行时需要,因此它将在运行时解析依赖项时下载。 23 | */ 24 | RUNTIME, 25 | 26 | /** 27 | * 依赖项在编译和运行单元测试时需要,因此在运行时解析依赖项时无需下载。 28 | */ 29 | TEST, 30 | 31 | /** 32 | * 依赖项应该已经在系统上,因此在运行时解析依赖项时无需下载。 33 | */ 34 | SYSTEM, 35 | 36 | /** 37 | * 依赖项实际上只是一个 pom 而不是一个 jar,因此我们不需要下载它。 38 | */ 39 | IMPORT 40 | } 41 | -------------------------------------------------------------------------------- /analyser/src/test/java/org/tabooproject/reflex/res/RuntimeResource.java: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.res; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Target(ElementType.TYPE) 6 | @Retention(RetentionPolicy.RUNTIME) 7 | public @interface RuntimeResource { 8 | 9 | String value(); 10 | 11 | String hash(); 12 | 13 | DependencyScope[] scopes(); 14 | } -------------------------------------------------------------------------------- /analyser/src/test/java/org/tabooproject/reflex/res/RuntimeResources.java: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.res; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.TYPE) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface RuntimeResources { 11 | 12 | RuntimeResource[] value1(); 13 | 14 | int[] value2(); 15 | 16 | String[] value3(); 17 | 18 | boolean[] value4(); 19 | 20 | boolean value5(); 21 | } 22 | -------------------------------------------------------------------------------- /analyser/src/test/kotlin/org/tabooproject/reflex/AnalyserAnnotation.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import kotlin.reflect.KClass 4 | 5 | @Retention(AnnotationRetention.RUNTIME) 6 | @Target(AnnotationTarget.CLASS, AnnotationTarget.FIELD, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.CONSTRUCTOR) 7 | annotation class AnalyserAnnotation( 8 | val value: String, 9 | val type: Test = Test.A, 10 | val cls: KClass<*> = Test::class 11 | ) { 12 | 13 | enum class Test { 14 | 15 | A, B, C 16 | } 17 | } -------------------------------------------------------------------------------- /analyser/src/test/kotlin/org/tabooproject/reflex/AnalyserTestAsm.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import org.apache.commons.lang3.BitField 4 | import org.apache.commons.lang3.Range 5 | import org.junit.jupiter.api.Test 6 | import org.junit.jupiter.api.TestTemplate 7 | import org.tabooproject.reflex.asm.AsmAnnotation 8 | import org.tabooproject.reflex.asm.AsmClassConstructor 9 | import kotlin.reflect.full.memberProperties 10 | 11 | /** 12 | * @author 坏黑 13 | * @since 2022/1/21 11:49 PM 14 | */ 15 | class AnalyserTestAsm { 16 | 17 | private val analyse = ClassAnalyser.analyseByASM(TestTargetAsm::class.java) 18 | 19 | @AnalyserAnnotation("test1", type = AnalyserAnnotation.Test.C) 20 | private class TestTargetAsm(private val intVal: Int) { 21 | 22 | @AnalyserAnnotation("test2") 23 | var range: Range? = null // NoClassDefFoundError 24 | 25 | var stringVar = "test" 26 | 27 | var stringArrayVar = arrayOf("test1", "test2") 28 | 29 | @AnalyserAnnotation("test3") 30 | constructor() : this(0) 31 | 32 | fun method() { 33 | } 34 | 35 | @AnalyserAnnotation("test4") 36 | private fun method(@AnalyserAnnotation("test5") value: Int): Int { 37 | return value 38 | } 39 | 40 | private fun methodArray(arr1: Array, value: String, arr2: IntArray): DoubleArray { 41 | return doubleArrayOf(1.0, 2.0) 42 | } 43 | 44 | companion object { 45 | 46 | @JvmField 47 | var bitField: BitField? = null // NoClassDefFoundError 48 | 49 | @JvmField 50 | val intRangeVal = IntRange.EMPTY 51 | 52 | @JvmStatic 53 | fun methodStatic(value: Int): Int { 54 | return value 55 | } 56 | } 57 | } 58 | 59 | // @Test 60 | fun testKotlinReflect() { 61 | val target = TestTargetAsm(10) 62 | val find = TestTargetAsm::class.memberProperties.first { it.name == "intVal" } 63 | try { 64 | find.get(target) 65 | throw IllegalStateException() 66 | } catch (_: NoClassDefFoundError) { 67 | } 68 | } 69 | 70 | // @Test 71 | fun testEnv() { 72 | try { 73 | Range.between(0, 10) 74 | throw IllegalStateException() 75 | } catch (_: NoClassDefFoundError) { 76 | } 77 | } 78 | 79 | @Test 80 | fun testAsm() { 81 | assert(analyse.constructors[0] is AsmClassConstructor) 82 | } 83 | 84 | @Test 85 | fun testAnalyse() { 86 | // intVal, range, bitField, stringVar, stringArrayVar, intRangeVal, Companion == 7 87 | assert(analyse.fields.size == 7) 88 | // getRange, setRange, getStringVar, setStringVar, getStringArrayVar, setStringArrayVar, method, method, methodArray, methodStatic == 10 89 | assert(analyse.methods.size == 10) 90 | assert(analyse.constructors.size == 2) 91 | } 92 | 93 | @Test 94 | fun testInstance() { 95 | analyse.getConstructor().instance()!! 96 | analyse.getConstructor(10).instance(10)!! 97 | analyse.getConstructorByType(Integer::class.java).instance(10)!! 98 | } 99 | 100 | @Test 101 | fun testGetVal() { 102 | val target = TestTargetAsm(10) 103 | assert(analyse.getField("intVal").get(target) == 10) 104 | } 105 | 106 | @Test 107 | fun testSetVal() { 108 | val target = TestTargetAsm(10) 109 | analyse.getField("intVal").set(target, 20) 110 | assert(analyse.getField("intVal").get(target) == 20) 111 | } 112 | 113 | @Test 114 | fun testGetVar() { 115 | val target = TestTargetAsm() 116 | assert(analyse.getField("stringVar").get(target) == "test") 117 | } 118 | 119 | @Test 120 | fun testSetVar() { 121 | val target = TestTargetAsm() 122 | analyse.getField("stringVar").set(target, "update") 123 | assert(target.stringVar == "update") 124 | } 125 | 126 | @Test 127 | fun testGetArrayVar() { 128 | assert(analyse.getField("stringArrayVar").fieldType == Array::class.java) 129 | } 130 | 131 | @Test 132 | fun testSetArrayVer() { 133 | val target = TestTargetAsm() 134 | analyse.getField("stringArrayVar").set(target, arrayOf("update1", "update2")) 135 | assert(target.stringArrayVar.contentEquals(arrayOf("update1", "update2"))) 136 | } 137 | 138 | @Test 139 | fun testGetStatic() { 140 | analyse.getField("intRangeVal").get()!! 141 | } 142 | 143 | @Test 144 | fun testSetStatic() { 145 | analyse.getField("intRangeVal").setStatic(IntRange(1, 10)) 146 | assert(TestTargetAsm.intRangeVal == IntRange(1, 10)) 147 | } 148 | 149 | @Test 150 | fun testInvokeMethod() { 151 | val target = TestTargetAsm() 152 | analyse.getMethod("method").invoke(target) 153 | assert(analyse.getMethod("method", 10).invoke(target, 10) == 10) 154 | assert(analyse.getMethodByType("method", Int::class.java).invoke(target, 10) == 10) 155 | } 156 | 157 | @Test 158 | fun testInvokeArrayMethod() { 159 | val target = TestTargetAsm() 160 | analyse.getMethod("methodArray", arrayOf("test1", "test2"), "test", intArrayOf(1, 2)).invoke(target, arrayOf("test1", "test2"), "test", intArrayOf(1, 2)) 161 | } 162 | 163 | @Test 164 | fun testInvokeStaticMethod() { 165 | assert(analyse.getMethod("methodStatic", 10).invokeStatic(10) == 10) 166 | assert(analyse.getMethodByType("methodStatic", Int::class.java).invokeStatic(10) == 10) 167 | } 168 | 169 | @Test 170 | fun testClassAnnotation() { 171 | val annotations = analyse.annotations 172 | assert(annotations.size == 2) 173 | assert(annotations[0] is AsmAnnotation) 174 | assert(annotations[0].source.name == "org.tabooproject.reflex.AnalyserAnnotation") 175 | } 176 | 177 | @Test 178 | fun testClassAnnotationGet1() { 179 | val annotation = analyse.getAnnotation(AnalyserAnnotation::class.java) 180 | assert(annotation is AsmAnnotation) 181 | assert(annotation.property("value") == "test1") 182 | } 183 | 184 | @Test 185 | fun testClassAnnotationGet2() { 186 | val annotation = analyse.getField("range").getAnnotation(AnalyserAnnotation::class.java) 187 | assert(annotation is AsmAnnotation) 188 | assert(annotation.property("value") == "test2") 189 | } 190 | 191 | @Test 192 | fun testClassAnnotationGet3() { 193 | val annotation = analyse.getConstructor().getAnnotation(AnalyserAnnotation::class.java) 194 | assert(annotation is AsmAnnotation) 195 | assert(annotation.property("value") == "test3") 196 | } 197 | 198 | @Test 199 | fun testClassAnnotationGet4() { 200 | val annotation = analyse.getMethod("method", 0).getAnnotation(AnalyserAnnotation::class.java) 201 | assert(annotation is AsmAnnotation) 202 | assert(annotation.property("value") == "test4") 203 | } 204 | 205 | @Test 206 | fun testClassAnnotationGet5() { 207 | val annotation = analyse.getMethod("method", 0).parameter[0].getAnnotation(AnalyserAnnotation::class.java) 208 | assert(annotation is AsmAnnotation) 209 | assert(annotation.property("value") == "test5") 210 | } 211 | 212 | @Test 213 | fun testClassAnnotationGetEnum() { 214 | val annotation = analyse.getAnnotation(AnalyserAnnotation::class.java) 215 | assert(annotation is AsmAnnotation) 216 | assert(annotation.enum("type") == AnalyserAnnotation.Test.C) 217 | } 218 | } -------------------------------------------------------------------------------- /analyser/src/test/kotlin/org/tabooproject/reflex/AnalyserTestAsmAnnotation1.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.tabooproject.reflex.res.* 5 | 6 | /** 7 | * Reflex 8 | * org.tabooproject.reflex.AnalyserTestAnnotation 9 | * 10 | * @author 坏黑 11 | * @since 2022/8/6 14:47 12 | */ 13 | @RuntimeResources( 14 | value1 = [ 15 | RuntimeResource("resource1", hash = "1", scopes = [DependencyScope.RUNTIME]), 16 | RuntimeResource("resource2", hash = "1", scopes = [DependencyScope.RUNTIME, DependencyScope.COMPILE]) 17 | ], 18 | value2 = [1, 2], 19 | value3 = ["aaa", "bbb"], 20 | value4 = [true], 21 | value5 = true, 22 | ) 23 | class AnalyserTestAsmAnnotation1 { 24 | 25 | @SubscribeEvent(priority = EventPriority.HIGH) 26 | fun event() { 27 | } 28 | 29 | @Test 30 | fun testAnnotation() { 31 | val analyser = ClassAnalyser.analyseByASM(AnalyserTestAsmAnnotation1::class.java) 32 | val method = analyser.getMethod("event") 33 | assert(method.getAnnotation(SubscribeEvent::class.java).properties().size == 1) 34 | } 35 | 36 | @Test 37 | fun testAnnotationObjectArray() { 38 | val analyser = ClassAnalyser.analyseByASM(AnalyserTestAsmAnnotation1::class.java) 39 | val value1 = analyser.getAnnotation(RuntimeResources::class.java).list("value1") 40 | assert(value1.isNotEmpty()) 41 | } 42 | 43 | @Test 44 | fun testAnnotationIntArray() { 45 | val analyser = ClassAnalyser.analyseByASM(AnalyserTestAsmAnnotation1::class.java) 46 | val value2 = analyser.getAnnotation(RuntimeResources::class.java).intArray("value2") 47 | assert(value2 != null) 48 | } 49 | 50 | @Test 51 | fun testAnnotationStringArray() { 52 | val analyser = ClassAnalyser.analyseByASM(AnalyserTestAsmAnnotation1::class.java) 53 | val value3 = analyser.getAnnotation(RuntimeResources::class.java).list("value3") 54 | assert(value3.isNotEmpty()) 55 | } 56 | 57 | @Test 58 | fun testAnnotationBooleanArray() { 59 | val analyser = ClassAnalyser.analyseByASM(AnalyserTestAsmAnnotation1::class.java) 60 | val value4 = analyser.getAnnotation(RuntimeResources::class.java).booleanArray("value4") 61 | assert(value4 != null) 62 | } 63 | 64 | @Test 65 | fun testAnnotationBoolean() { 66 | val analyser = ClassAnalyser.analyseByASM(AnalyserTestAsmAnnotation1::class.java) 67 | val value5 = analyser.getAnnotation(RuntimeResources::class.java).property("value5", false) 68 | assert(value5) 69 | } 70 | 71 | @Test 72 | fun testAnnotationEnumArray() { 73 | val analyser = ClassAnalyser.analyseByASM(AnalyserTestAsmAnnotation1::class.java) 74 | println(analyser.getAnnotation(RuntimeResources::class.java).properties()) 75 | } 76 | } -------------------------------------------------------------------------------- /analyser/src/test/kotlin/org/tabooproject/reflex/AnalyserTestAsmAnnotation2.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.tabooproject.reflex.asm.AsmAnnotation 5 | 6 | /** 7 | * @author 坏黑 8 | * @since 2022/1/21 11:49 PM 9 | */ 10 | class AnalyserTestAsmAnnotation2 { 11 | 12 | private val analyse = ClassAnalyser.analyseByASM(TestTargetAsm::class.java) 13 | 14 | @AnalyserAnnotation("test1", cls = AnalyserAnnotation::class) 15 | private class TestTargetAsm 16 | 17 | @Test 18 | fun testClassAnnotationGetEnum() { 19 | val annotation = analyse.getAnnotation(AnalyserAnnotation::class.java) 20 | assert(annotation is AsmAnnotation) 21 | assert(annotation.type("cls").name == AnalyserAnnotation::class.java.name) 22 | } 23 | } -------------------------------------------------------------------------------- /analyser/src/test/kotlin/org/tabooproject/reflex/AnalyserTestReflection.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.tabooproject.reflex.asm.AsmAnnotation 5 | import org.tabooproject.reflex.reflection.InstantAnnotation 6 | import org.tabooproject.reflex.reflection.InstantClassConstructor 7 | 8 | /** 9 | * @author 坏黑 10 | * @since 2022/1/21 11:49 PM 11 | */ 12 | class AnalyserTestReflection { 13 | 14 | private val analyse = ClassAnalyser.analyse(TestTargetReflection::class.java, AnalyseMode.REFLECTION_ONLY) 15 | 16 | @AnalyserAnnotation("test1", type = AnalyserAnnotation.Test.C) 17 | private class TestTargetReflection(val intVal: Int) { 18 | 19 | @AnalyserAnnotation("test2") 20 | private var stringVar = "test" 21 | 22 | @AnalyserAnnotation("test3") 23 | constructor() : this(0) 24 | 25 | fun method() { 26 | } 27 | 28 | @AnalyserAnnotation("test4") 29 | private fun method(@AnalyserAnnotation("test5") value1: Int, value2: Int): Int { 30 | return value1 + value2 31 | } 32 | 33 | companion object { 34 | 35 | @JvmField 36 | val intRangeVal = IntRange.EMPTY 37 | 38 | @JvmStatic 39 | fun methodStatic(value: Int): Int { 40 | return value 41 | } 42 | } 43 | } 44 | 45 | @Test 46 | fun testInstant() { 47 | assert(analyse.constructors[0] is InstantClassConstructor) 48 | } 49 | 50 | @Test 51 | fun testAnalyse() { 52 | // intVal, stringVar, intRangeVal, Companion == 4 53 | assert(analyse.fields.size == 4) 54 | // method, method, getIntVal, methodStatic == 4 55 | assert(analyse.methods.size == 4) 56 | assert(analyse.constructors.size == 2) 57 | } 58 | 59 | @Test 60 | fun testInstance() { 61 | analyse.getConstructor().instance()!! 62 | analyse.getConstructor(10).instance(10)!! 63 | analyse.getConstructorByType(Integer::class.java).instance(10)!! 64 | } 65 | 66 | @Test 67 | fun testGetVal() { 68 | val target = TestTargetReflection(10) 69 | assert(analyse.getField("intVal").get(target) == 10) 70 | } 71 | 72 | @Test 73 | fun testSetVal() { 74 | val target = TestTargetReflection(10) 75 | analyse.getField("intVal").set(target, 20) 76 | assert(target.intVal == 20) 77 | } 78 | 79 | @Test 80 | fun testGetVar() { 81 | val target = TestTargetReflection() 82 | assert(analyse.getField("stringVar").get(target) == "test") 83 | } 84 | 85 | @Test 86 | fun testSetVar() { 87 | val target = TestTargetReflection() 88 | analyse.getField("stringVar").set(target, "update") 89 | assert(analyse.getField("stringVar").get(target) == "update") 90 | } 91 | 92 | @Test 93 | fun testGetStatic() { 94 | analyse.getField("intRangeVal").get()!! 95 | } 96 | 97 | @Test 98 | fun testSetStatic() { 99 | analyse.getField("intRangeVal").setStatic(IntRange(1, 10)) 100 | assert(TestTargetReflection.intRangeVal == IntRange(1, 10)) 101 | } 102 | 103 | @Test 104 | fun testInvokeMethod() { 105 | val target = TestTargetReflection() 106 | analyse.getMethod("method").invoke(target) 107 | assert(analyse.getMethod("method", 10, 10).invoke(target, 10, 10) == 20) 108 | assert(analyse.getMethodByType("method", Int::class.java, Int::class.java).invoke(target, 10, 10) == 20) 109 | } 110 | 111 | @Test 112 | fun testInvokeStaticMethod() { 113 | assert(analyse.getMethod("methodStatic", 10).invokeStatic(10) == 10) 114 | assert(analyse.getMethodByType("methodStatic", Int::class.java).invokeStatic(10) == 10) 115 | } 116 | 117 | @Test 118 | fun testClassAnnotation() { 119 | val annotations = analyse.annotations 120 | assert(annotations.size == 2) 121 | assert(annotations[0] is InstantAnnotation) 122 | assert(annotations[0].source.name == "org.tabooproject.reflex.AnalyserAnnotation") 123 | } 124 | 125 | @Test 126 | fun testClassAnnotationGet1() { 127 | val annotation = analyse.getAnnotation(AnalyserAnnotation::class.java) 128 | assert(annotation is InstantAnnotation) 129 | assert(annotation.property("value") == "test1") 130 | } 131 | 132 | @Test 133 | fun testClassAnnotationGet2() { 134 | val annotation = analyse.getField("stringVar").getAnnotation(AnalyserAnnotation::class.java) 135 | assert(annotation is InstantAnnotation) 136 | assert(annotation.property("value") == "test2") 137 | } 138 | 139 | @Test 140 | fun testClassAnnotationGet3() { 141 | val annotation = analyse.getConstructor().getAnnotation(AnalyserAnnotation::class.java) 142 | assert(annotation is InstantAnnotation) 143 | assert(annotation.property("value") == "test3") 144 | } 145 | 146 | @Test 147 | fun testClassAnnotationGet4() { 148 | val annotation = analyse.getMethod("method", 0, 0).getAnnotation(AnalyserAnnotation::class.java) 149 | assert(annotation is InstantAnnotation) 150 | assert(annotation.property("value") == "test4") 151 | } 152 | 153 | @Test 154 | fun testClassAnnotationGet5() { 155 | val annotation = analyse.getMethod("method", 0, 0).parameter[0].getAnnotation(AnalyserAnnotation::class.java) 156 | assert(annotation is InstantAnnotation) 157 | assert(annotation.property("value") == "test5") 158 | } 159 | 160 | @Test 161 | fun testClassAnnotationGetEnum() { 162 | val annotation = analyse.getAnnotation(AnalyserAnnotation::class.java) 163 | assert(annotation is InstantAnnotation) 164 | assert(annotation.enum("type") == AnalyserAnnotation.Test.C) 165 | } 166 | } -------------------------------------------------------------------------------- /analyser/src/test/kotlin/org/tabooproject/reflex/AnalyserTestReflectionAnnotation.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.tabooproject.reflex.res.EventPriority 5 | import org.tabooproject.reflex.res.RuntimeResource 6 | import org.tabooproject.reflex.res.RuntimeResources 7 | import org.tabooproject.reflex.res.SubscribeEvent 8 | 9 | /** 10 | * Reflex 11 | * org.tabooproject.reflex.AnalyserTestAnnotation 12 | * 13 | * @author 坏黑 14 | * @since 2022/8/6 14:47 15 | */ 16 | class AnalyserTestReflectionAnnotation { 17 | 18 | @SubscribeEvent(priority = EventPriority.HIGH) 19 | fun event() { 20 | } 21 | 22 | @Test 23 | fun testAnnotation() { 24 | val analyser = ClassAnalyser.analyseByReflection(AnalyserTestReflectionAnnotation::class.java) 25 | val method = analyser.getMethod("event") 26 | assert(method.getAnnotation(SubscribeEvent::class.java).properties().isNotEmpty()) 27 | } 28 | } -------------------------------------------------------------------------------- /analyser/src/test/kotlin/org/tabooproject/reflex/BinaryTest.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex 2 | 3 | import org.tabooproject.reflex.serializer.BinaryReader 4 | import java.io.File 5 | 6 | class BinaryTest { 7 | 8 | // @Test 9 | fun test() { 10 | val reader = BinaryReader(File("platform.cache").readBytes()) 11 | println("env: ${reader.readList { reader.readString() }}") 12 | } 13 | } -------------------------------------------------------------------------------- /analyser/src/test/kotlin/org/tabooproject/reflex/LazyClassSerialize.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.BinaryWriter 6 | 7 | class LazyClassSerialize { 8 | 9 | @Test 10 | fun testClass() { 11 | val lazyClass = LazyClass.of(LazyClassSerialize::class.java) 12 | val buffer = BinaryWriter() 13 | lazyClass.writeTo(buffer) 14 | val byteArray = buffer.toByteArray() 15 | 16 | val wrap = BinaryReader.from(byteArray) 17 | assert(wrap.readInt() == 1) 18 | assert(wrap.readNullableString() == "org.tabooproject.reflex.LazyClassSerialize") 19 | assert(wrap.readNullableString() == "LazyClassSerialize") 20 | assert(wrap.readInt() == 0) // dimensions 21 | assert(wrap.readBoolean() == true) // isInstant 22 | assert(wrap.readBoolean() == false) // isPrimitive 23 | } 24 | } -------------------------------------------------------------------------------- /analyser/src/test/kotlin/org/tabooproject/reflex/res/EventOrder.kt: -------------------------------------------------------------------------------- 1 | package org.tabooproject.reflex.res 2 | 3 | /** 4 | * Sponge Only 5 | * 6 | * TabooLib 7 | * org.tabooproject.reflex.res.EventOrder 8 | * 9 | * @author sky 10 | * @since 2021/6/21 6:24 下午 11 | */ 12 | enum class EventOrder { 13 | 14 | /** 15 | * The order point of PRE handles setting up things that need to be done 16 | * before other things are handled PRE is read only and cannot cancel the 17 | * events. 18 | */ 19 | PRE, 20 | 21 | /** 22 | * The order point of AFTER_PRE handles things that need to be done after 23 | * PRE AFTER_PRE is read only and cannot cancel the events. 24 | */ 25 | AFTER_PRE, 26 | 27 | /** 28 | * The order point of FIRST handles cancellation by protection plugins for 29 | * informational responses FIRST is read only but can cancel events. 30 | */ 31 | FIRST, 32 | 33 | /** 34 | * The order point of EARLY handles standard actions that need to be done 35 | * before other plugins EARLY is not read only and can cancel events. 36 | */ 37 | EARLY, 38 | 39 | /** 40 | * The order point of DEFAULT handles just standard event handlings, you 41 | * should use this unless you know you need otherwise DEFAULT is not read 42 | * only and can cancel events. 43 | */ 44 | DEFAULT, 45 | 46 | /** 47 | * The order point of LATE handles standard actions that need to be done 48 | * after other plugins LATE is not read only and can cancel the event. 49 | */ 50 | LATE, 51 | 52 | /** 53 | * The order point of LAST handles last minute cancellations by protection 54 | * plugins LAST is read only but can cancel events. 55 | */ 56 | LAST, 57 | 58 | /** 59 | * The order point of BEFORE_POST handles preparation for things needing 60 | * to be done in post BEFORE_POST is read only and cannot cancel events. 61 | */ 62 | BEFORE_POST, 63 | 64 | /** 65 | * The order point of POST handles last minute things and monitoring 66 | * of events for rollback or logging POST is read only and 67 | * cannot cancel events.

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") --------------------------------------------------------------------------------