├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── gradle-publish.yml │ └── gradle.yml ├── .gitignore ├── .idea ├── .gitignore ├── codeStyles │ └── codeStyleConfig.xml ├── compiler.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── misc.xml ├── modules.xml ├── modules │ └── apt │ │ └── JavaDynamilizer.apt.main.iml ├── uiDesigner.xml └── vcs.xml ├── LICENSE ├── README.md ├── annotations ├── build.gradle └── src │ └── main │ └── java │ └── dynamilize │ └── annotations │ ├── ContextMaker.java │ ├── DynamicContext.java │ ├── DynamilizeClass.java │ └── NewDynamic.java ├── apt ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── dynamilize │ └── annoprocessor │ ├── BaseProcessor.java │ ├── ClassDynamilizeProcessor.java │ └── DynamicContextProcessor.java ├── baseimpl ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── dynamilize │ ├── Demodulator.java │ ├── DynamicFactory.java │ ├── JavaMethodEntry.java │ ├── JavaVariable.java │ └── UnsafePackageAccHandler.java ├── build.gradle ├── core ├── build.gradle └── src │ └── main │ └── java │ └── dynamilize │ ├── ArgumentList.java │ ├── Calculator.java │ ├── ClassImplements.java │ ├── DataPool.java │ ├── Delegate.java │ ├── DynamicClass.java │ ├── DynamicMaker.java │ ├── DynamicObject.java │ ├── Function.java │ ├── FunctionEntry.java │ ├── FunctionType.java │ ├── IFunctionEntry.java │ ├── IVariable.java │ ├── IllegalHandleException.java │ ├── Initializer.java │ ├── JavaHandleHelper.java │ ├── PackageAccHandler.java │ ├── ProxyMaker.java │ ├── Variable.java │ ├── WrappedObject.java │ ├── classmaker │ ├── ASMGenerator.java │ ├── AbstractClassGenerator.java │ ├── AnnotatedMember.java │ ├── AnnotationDef.java │ ├── BaseClassLoader.java │ ├── ByteClassLoader.java │ ├── ClassInfo.java │ ├── CodeBlock.java │ ├── DefaultReadVisitor.java │ ├── ElementVisitor.java │ ├── FieldInfo.java │ ├── MethodInfo.java │ ├── Parameter.java │ └── code │ │ ├── Element.java │ │ ├── ElementKind.java │ │ ├── IArrayGet.java │ │ ├── IArrayPut.java │ │ ├── ICast.java │ │ ├── IClass.java │ │ ├── ICodeBlock.java │ │ ├── ICompare.java │ │ ├── ICondition.java │ │ ├── IField.java │ │ ├── IGetField.java │ │ ├── IGoto.java │ │ ├── IInstanceOf.java │ │ ├── IInvoke.java │ │ ├── ILoadConstant.java │ │ ├── ILocal.java │ │ ├── ILocalAssign.java │ │ ├── IMarkLabel.java │ │ ├── IMethod.java │ │ ├── INewArray.java │ │ ├── INewInstance.java │ │ ├── IOddOperate.java │ │ ├── IOperate.java │ │ ├── IPutField.java │ │ ├── IReturn.java │ │ ├── ISwitch.java │ │ ├── IThrow.java │ │ ├── Label.java │ │ └── annotation │ │ ├── AnnotatedElement.java │ │ ├── AnnotationType.java │ │ └── IAnnotation.java │ └── runtimeannos │ ├── AspectInterface.java │ ├── Exclude.java │ ├── FuzzyMatch.java │ ├── Super.java │ └── This.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── settings.gradle └── usage_sample ├── build.gradle ├── processorLog └── ClassDynamilizeProcessor.log └── src └── main └── java └── com └── github └── ebwilson ├── sample ├── BaseUse.java ├── README.md ├── aop │ ├── AspectUsage.java │ └── ProxyUsage.java └── dynamic │ ├── HotFixUsage.java │ └── ReflectionFrameUsage.java └── sample_zh ├── BaseUse.java ├── README.md ├── aop ├── AspectUsage.java └── ProxyUsage.java └── dynamic ├── HotFixUsage.java └── ReflectionFrameUsage.java /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/gradle-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created 6 | # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle 7 | 8 | name: Gradle Package 9 | 10 | on: 11 | release: 12 | types: [created] 13 | 14 | jobs: 15 | build: 16 | 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: read 20 | packages: write 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Set up JDK 17 25 | uses: actions/setup-java@v3 26 | with: 27 | java-version: '17' 28 | distribution: 'temurin' 29 | server-id: github # Value of the distributionManagement/repository/id field of the pom.xml 30 | settings-path: ${{ github.workspace }} # location for the settings.xml file 31 | 32 | - name: Build with Gradle 33 | uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0 34 | with: 35 | arguments: build 36 | 37 | # The USERNAME and TOKEN need to correspond to the credentials environment variables used in 38 | # the publishing section of your build.gradle 39 | - name: Publish to GitHub Packages 40 | uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0 41 | with: 42 | arguments: publish 43 | env: 44 | USERNAME: ${{ github.actor }} 45 | TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time 6 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle 7 | 8 | name: Java CI with Gradle 9 | 10 | on: 11 | workflow_dispatch: 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Set up JDK 17 24 | uses: actions/setup-java@v3 25 | with: 26 | java-version: '17' 27 | distribution: 'temurin' 28 | - name: Build with Gradle 29 | uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0 30 | with: 31 | arguments: build 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 项目排除路径 2 | /.gradle/ 3 | /.idea/ 4 | /build/ 5 | /build/classes/java/Test/ 6 | core/build 7 | usage_sample/build 8 | baseimpl/build 9 | annotations/build 10 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 52 | 53 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/modules/apt/JavaDynamilizer.apt.main.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaDynamilizer 2 | 一个java语言的动态化支持框架,允许对java类型进行动态委托并构造动态实例,以类似脚本语言的“函数”和“变量”来替代“方法”和“字段”,它们是可以变化的,动态实例可以委托自任意可用的java类(非final且具有至少对子类可见的构造器),动态实例可以分配给被委托的java类。 3 | **** 4 | 要引用JavaDynamilizer部署项目,需要在项目依赖中添加对此仓库的依赖: 5 | 6 | dependencies { 7 | ...... 8 | implementation 'com.github.EB-wilson.JavaDynamilizer:core:$version' 9 | implementation 'com.github.EB-wilson.JavaDynamilizer:baseimpl:$version'//基础的内部实现,若您需要自定义实现抽象层的话,这个模块就并不是必要的 10 | ...... 11 | } 12 | 13 | > 当前这个项目还尚不成熟和健壮,需要更多的使用与建议来进一步完善和优化,如果您有能力还请不吝参与此框架的开发。 14 | 15 | 您可以参照项目中给出的一些使用[案例](https://github.com/EB-wilson/JavaDynamilizer/tree/master/usage_sample/src/main/java/com/github/ebwilson/sample)和javadoc了解更多关于这个仓库的使用方法。下面是基本使用方法简述: 16 | 17 | ## 基本使用 18 | 创建一个最简单的全委托动态实例,它委托自java类HashMap: 19 | 20 | DynamicMaker maker = DynamicFactory.getDefault(); 21 | DynamicClass Sample = DynamicClass.get("Sample"); 22 | 23 | HashMap map = maker.newInstance(HashMap.class, Sample).objSelf(); 24 | 25 | 其中,map对象具有所有hashMap的行为并且可以分配给HashMap字段或者参数。而本工具的重点在于动态化实例,上面创建的map实例已经被动态化,它具有`dynamilize.DynamicObject`的特征,对于此对象已经可以进行行为变更和代码插桩了。 26 | 27 | 例如,接下来我们对上面的map的`put`方法添加一个监视,使任何时候向该映射中添加映射都会将这个键值对打印出来: 28 | 29 | DynamicObject> dyMap = (DynamicObject>)map; 30 | dyMap.setFunc("put", (self, args) -> { 31 | self.superPointer().invokeFunc("put", args); 32 | System.out.println("map putted, key: " + args.get(0) + ", value: " + args.get(1) + "."); 33 | }, Object.class, Object.class); 34 | 35 | 这时,如果执行`map.put("first", "hello world")`,那么会在系统输出流中收到如下信息: 36 | 37 | map putted, key: first, value: hello world. 38 | 39 | 另外,在构建一个动态实例时,您还可以令实例实现一组接口,同样的,实例可以被正确的分配给这个接口的类型: 40 | 41 | DynamicClass Demo = DynamicClass.get("Demo"); 42 | DynamicObject dyObj = DynamicFactory.getDefault().newInstance(new Class[]{Map.class}, Demo); 43 | Map map = (Map)dyObj; 44 | 45 | 但是在实现接口时,您必须使用`setFunc`方法来对所有的接口抽象方法进行实现,否则对此方法的引用会抛出致命异常。 46 | 47 | ## 动态类型 48 | 49 | > 前面提到了关于实现接口时,需要对方法进行逐一实现的情况,如果对每一个抽象方法都需要逐一引用`setFunc`进行设置实现的话,无疑代码会变得尤其复杂,为此我们引入**动态类型**,以便更加有效的利用动态实例。 50 | 51 | 每一个动态实例,都会有一个关联的动态类型,这类似于java本身的**类**与**对象**的关系,不同的是,动态类型描述了其实例的默认行为,如该类型的实例构建后所具有的默认函数和变量。从之前的例子我们已经可以知道,创建一个动态实例必须要来自一个动态类型: 52 | 53 | DynamicClass Demo = DynamicClass.get("Demo"); 54 | 55 | 动态类型也有它的超类,距离这个动态类型最近的一个超类称为此类的**直接超类**,对于上述例子的`Demo`类,它的直接超类没有被明确确定,则它的直接超类为构造实例进行委托的java类;若有确定的直接超类: 56 | 57 | DynamicClass DemoChild = DynamicClass.declare("DemoChild", Demo); 58 | 59 | 直接超类与java类的类层次结构行为基本一致,即遵循原则:**引用一个函数或者变量时,以最接近此类型的超类优先被访问**。 60 | 61 | 下面,将讨论如何设置动态类型的行为。 62 | 动态类型的行为主要由函数和变量构成,对于函数设置有**访问样版模式**和**lambda模式**,对于变量,则有**访问样版模式**和**函数模式**以及**常量模式**,不同的模式有各自的应用场景和使用方法: 63 | 64 | 先创建一个新的动态类: 65 | 66 | DynamicClass Sample = DynamicLcass.get("Sample"); 67 | **** 68 | - **函数** 69 | 70 | **访问样版模式** 71 | 即使用java类型描述一定的行为,用动态类型去访问这个java类型。 72 | 例如声明一个类型用作样版: 73 | 74 | public class Template{ 75 | public static void run(){ 76 | System.out.println("hello world"); 77 | } 78 | } 79 | 80 | 那么用动态类型去访问: 81 | 82 | DynamicMaker maker = DynamicFactory.getDefault(); 83 | Sample.visitClass(Template.class, maker.getHelper()); 84 | DynamicObject dyObj = maker.newInstance(Sample); 85 | dyObj.invokeFunc("run"); 86 | 87 | 这将会在系统输出流中打印`hello world`。 88 | 访问类型样版会描述类型中声明的所有未排除的public static方法,如果你需要针对某一方法访问,则可以通过反射获取方法对象,使用`Sample.visitMethod(*reflection method*)`方法对方法进行单独访问。 89 | **** 90 | **lambda模式** 91 | 即匿名函数模式,意在使用lambda表达式声明的匿名函数来描述动态类型中函数的行为。 92 | 93 | Sample.setFunction("run", (self, args) -> System.out.println("hello world")); 94 | DynamicObject dyObj = DynamicMaker.getDefault().newInstance(Sample); 95 | dyObj.invokeFunc("run"); 96 | 97 | 这一段代码与前一种方法描述的对象行为是一致的。 98 | **** 99 | - **变量** 100 | 101 | **访问样板模式** 102 | 与函数的访问样板模式类似,只是函数访问的是方法,而变量要访问的是字段 103 | 例如,定义样板类型: 104 | 105 | public class Template{ 106 | public static String var1 = "hello world"; 107 | } 108 | 109 | 如果访问动态对象的变量`var1`: 110 | 111 | DynamicMaker maker = DynamicFactory.getDefault(); 112 | Sample.visitClass(Template.class, maker.getHelper()); 113 | DynamicObject dyObj = maker.newInstance(Sample); 114 | System.out.println(dyObject.getVar("var1")); 115 | 116 | 这会在系统输出流种打印`hello world`。 117 | 与方法相同,访问一个类型样板会访问其中的所有public static字段,若需要就某一字段进行访问,则可以对反射获取的字段对象使用`Sample.visitField(*reflection field*)`方法来访问指定的字段。 118 | 另外,字段的访问样版可以被设置为函数: 119 | 120 | public class Template{ 121 | public static Initializer.Producer var1 = () -> System.nanoTime(); 122 | } 123 | 124 | 这与后文函数模式的效果一致。 125 | **** 126 | **常量模式** 127 | 即直接用一个常量作为变量的初始值: 128 | 129 | Sample.setVariable("var1", "hello world", false); 130 | DynamicObject dyObj = DynamicMaker.getDefault().newInstance(Sample); 131 | System.out.println(dyObject.getVar("var1")); 132 | 133 | 这将获得与前一种方法同样的效果。 134 | **** 135 | **函数模式** 136 | 这种方式使用一个工厂函数生产变量初始值,每一个函数初始化都会调用给出的函数来生产变量的初始值,例如: 137 | 138 | Sample.setVariable("time", () -> System.nanoTime(), false); 139 | 140 | 则会使得对象的`time`变量与对象被创建时的系统纳秒计数一致。 141 | 函数与常量模式参数尾部的布尔值是确定此变量是否为**常量**,若为true,则此变量不可被改变。 142 | **** 143 | - 动态类型声明的变量初始值只在对象被创建时有效,任何时候改变类的变量初始值都不会对已有实例造成影响 144 | -------------------------------------------------------------------------------- /annotations/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | apply plugin: 'maven-publish' 5 | 6 | sourceSets.main.java.srcDirs = ["src/main/java"] 7 | sourceSets.test.java.srcDirs = ["src/test/java"] 8 | 9 | repositories { 10 | mavenCentral() 11 | maven{ url 'https://www.jitpack.io' } 12 | } 13 | 14 | sourceCompatibility = 17 15 | targetCompatibility = 8 16 | 17 | tasks.withType(JavaCompile).configureEach { 18 | options.compilerArgs.addAll([ 19 | "--release", "8" 20 | ]) 21 | } 22 | 23 | publishing { 24 | publications { 25 | maven(MavenPublication) { 26 | groupId = 'com.github.EB-wilson.JavaDynamilizer' 27 | artifactId = 'annotations' 28 | version = "$JDERVersion" 29 | from components.java 30 | } 31 | } 32 | } 33 | 34 | dependencies { 35 | compileOnly project(":core") 36 | annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2' 37 | compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2' 38 | } 39 | -------------------------------------------------------------------------------- /annotations/src/main/java/dynamilize/annotations/ContextMaker.java: -------------------------------------------------------------------------------- 1 | package dynamilize.annotations; 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 | /**动态工厂上下文标记器,用于确定在一个{@linkplain DynamicContext 动态语义上下文}中所使用的动态工厂 9 | *

具体来说,动态上下文的诸多行为会依赖于一个动态工厂,此注解即应用于一个字段或者局部变量,亦或者是一个方法参数之上,其上下文语义与java变量,字段和方法参数无异, 10 | * 请注意,当注解用于java静态字段时,此标记同样会具有静态上下文,反之亦然,即您不能在静态上下文内访问非静态上下文工厂标识 11 | * 12 | *

对于同一个上下文内的多次设置上下文标记,则在访问动态工厂时,会选择在此之前的最后一个标记使用*/ 13 | @Retention(RetentionPolicy.SOURCE) 14 | @Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.PARAMETER}) 15 | public @interface ContextMaker { 16 | } 17 | -------------------------------------------------------------------------------- /annotations/src/main/java/dynamilize/annotations/DynamicContext.java: -------------------------------------------------------------------------------- 1 | package dynamilize.annotations; 2 | 3 | import dynamilize.DynamicClass; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /**动态上下文标记,携带此注解的类型会被视为一个具有动态化语义的上下文 11 | *

具体来说,这个注解是类型内局部动态语义的最外层标记,所有局部动态语义声明的注解都需要其所在的环境具有此注解标记的外层 12 | *

本注解本身没有实际作用,关于这个注解支持的局部语义注解可参见:: 13 | *

    14 | *
  • @{@link ContextMaker} - 上下文动态工厂标记
  • 15 | *
  • @{@link NewDynamic} - new语句动态化委托
  • 16 | *
17 | * 18 | * 关于注解的详细行为请参阅各个受支持的注解*/ 19 | @Retention(RetentionPolicy.SOURCE) 20 | @Target(ElementType.TYPE) 21 | public @interface DynamicContext { 22 | 23 | interface DynamilizedContext{ 24 | DynamilizedContextHandles $ = new DynamilizedContextHandles(); 25 | } 26 | 27 | class DynamilizedContextHandles{ 28 | public void by(DynamicClass dynamicClass){} 29 | public void with(Class... aspects){} 30 | public void impl(Class... interfaces){} 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /annotations/src/main/java/dynamilize/annotations/DynamilizeClass.java: -------------------------------------------------------------------------------- 1 | package dynamilize.annotations; 2 | 3 | import dynamilize.*; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | import java.lang.reflect.Field; 10 | import java.lang.reflect.Method; 11 | 12 | /**此注解用于将一个一般的java类型转化为动态类型记录 13 | *

具体来说,一个具备此注解的类,其中声明的各种方法语句的上下文都会被调整为适配到{@link DynamicClass#visitClass(Class, JavaHandleHelper)}的语义, 14 | * 并保存其对应的动态类型对象及名称。该注解并不会递归的处理被标记类中的内部类,但是被标记的类中不能有任何非静态的内部类 15 | *

此标记转换的动态类类名由DynamilizeClass.{@link DynamilizeClass#className()}给出,默认使用标记的类类名,细节请参阅这个注解属性 16 | * 17 | *
18 | *
19 | *

警告:此注解转换的动态类型标记可能并不能可靠的加载,在您使用这个注解生成的动态类之前,请通过调用类的任意字段/方法来确保此类型已加载

20 | *

特别注意,在注解{@link NewDynamic}中传递该类的NAME常量并不能使jvm加载这个类,因为NAME会被转化为常量提供给注解参数, 21 | * 因此在通过上下文注解引用的该动态类并不能可靠的加载此动态类

22 | *
23 | * 24 | *

以下是注解的工作细节: 25 | *

    26 | *
  • 首先,检查该类型中的声明是否合法,而后在其中搜索{@linkplain JavaHandleHelper 辅助器},向其之后添加两个字段 27 | * {@code public static final DynamicClass INSTANCE}和{@code public static final String NAME}分别保存对动态类实例的引用和动态类类名, 28 | * 这两个字段都会添加{@link dynamilize.runtimeannos.Exclude}以排除,其中INSTANCE的初始化在后文会提到
  • 29 | *
  • 接着,注解处理器会在此类上下文中定位每一个字段引用和方法引用,确定这些引用的目标是否为this或者super,若引用的目标满足且是一个隐式成员引用, 30 | * 那么会将this和super添加为显式声明
  • 31 | *
  • 遍历此类所有方法,将所有成员方法全部变为静态方法,并在参数前两位添加符合{@link DynamicClass#visitMethod(Method, JavaHandleHelper)}规则的self参数和sup参数, 32 | * 而对于声明的静态方法则添加{@link dynamilize.runtimeannos.Exclude}注解从声明中排除
  • 33 | *
  • 然后,将所有符合规则的成员方法中对本类上下文引用的语句转换为通过self参数传入的动态对象的方法调用,例如{@code this.run()}会被替换为{@code self.invokeFunc("run")}, 34 | * 但是默认情况下这个对run的调用会添加方法签名以快速定位函数,关于签名选项,请参阅{@link DynamilizeClass#signatureMethods()}
  • 35 | *
  • 最后,处理所有声明的字段,对成员字段会按照符合{@link DynamicClass#visitField(Field)}的规范变换,若其具有初始化数据, 36 | * 当初始化数据为常量值时会直接将字段设置为static,否则,字段的初始化语句会被包装为{@link dynamilize.Initializer.Producer},且字段类型也会同步调整。 37 | * 对于静态字段则会简单的像是对静态方法一样添加注解进行排除
  • 38 | *
39 | * 40 | *

此外,被标记为动态类型记录的类也可以扩展一个动态类型记录,这会对INSTANCE字段的初始化产生影响,具体来说,初始化语句都是调用{@link DynamicClass#visit(String, Class, DynamicClass, JavaHandleHelper)} 41 | * 来直接获取动态类型实例,而扩展会影响的就是其最后一个参数,直观来看

{@code
42 |  * //一个没有扩展的动态类型记录中,INSTANCE的等价初始化语句是这样的:
43 |  * public static final DynamicClass INSTANCE = DynamicClass.visit(NAME, .class, null, $helper$);
44 |  * //而有扩展一个父类的动态类型记录中,INSTANCE的等价初始化语句是这样的:
45 |  * public static final DynamicClass INSTANCE = DynamicClass.visit(NAME, .class, .INSTANCE, $helper$);
46 |  * }
47 | * 值得关注的是其中的字段{@code $helper$},这个字段是保存用于访问java类使用的辅助器的静态字段, 48 | * 您同样必须在原始的类型定义中创建指定名称的java静态字段,否则将无法通过编译,关于选择这个字段的细节,请参阅DynamilizeClass.{@link DynamilizeClass#helperField()} 49 | * 50 | *

另外,在您使用这个注解标记类型时,将类型标记为abstract并实现{@link DynamilizedClass}接口是一种不错的选择,该接口包含了INSTANCE字段和NAME字段, 51 | * 这是一种魔法操作,例如,在java中,您可以直接对被标记的类调用INSTANCE字段和NAME字段,但这是通过类转移的调用,实际上会指向{@link DynamilizedClass}接口的字段, 52 | * 但是在注解处理后,被标记类中会被添加这两个字段,从外部访问这个类型的INSTANCE和NAME字段的目标就会被确定而非重定向到DynamilizedClass接口, 53 | * 具体细节请参阅{@link DynamilizedClass}*/ 54 | @Retention(RetentionPolicy.SOURCE) 55 | @Target(ElementType.TYPE) 56 | public @interface DynamilizeClass { 57 | /**应用于动态类的名称,这会为此类的NAME字段设置值,默认在不设置这个名称的情况下,注解处理器会使用被标记的类的完整类名作为该动态类的名称*/ 58 | String className() default ""; 59 | /**此类型动态化时所使用的静态java辅助器存储字段名称,在将类型声明转化为动态类的过程中,会在类的一级声明中搜索这个字段, 60 | * 并将INSTANCE字段添加在其后方以确保有效的调用此字段,默认为"$helper$",您可以自行指定字段名称*/ 61 | String helperField() default "$helper$"; 62 | /**是否对所有动态化的方法调用添加方法签名,关于方法签名的性能影响,可参阅{@link DataPool#select(String, FunctionType)},若您将此设置为true, 63 | * 那么所有从方法调用转换来的动态方法引用都会标记其调用的方法签名类型,并在调用时传入以提升性能 64 | *

您可以根据需要设置此选项,假如内存性能十分重要,则推荐设为false,否则通常来说设为true都能提升时间性能,尤其是在动态类型多次扩展嵌套的情况下*/ 65 | boolean signatureMethods() default true; 66 | 67 | /**辅助类型动态声明的魔法接口,通常在您对一个类型标记为{@link DynamilizeClass}时,建议将类型设置为abstract并添加实现此接口 68 | *

接口扩展自{@link DynamicObject},以便在您描述类型行为时可以直接使用动态对象的方法,值得注意的是, 69 | * 您在此上下文中调用属于{@link DynamicObject}的API是不会被再次转化为动态调用动态方法的,即这些API会被注解处理器略过,仅仅将调用对象重定向到self参数 70 | *

此外,该接口中定义了两个常量字段和一个方法,字段用作在java语义中直接引用该类型的INSTANCE字段和NAME字段,在经过注解处理器处理后这两个字段不会被重定向到该接口。 71 | *

方法{@link DynamilizedClass#super$()}则是对动态super指针的虚拟引用,以便您可以以动态描述的方法那样调用super池的行为,对这个方法的调用在注解处理后会被转化为对sup参数的引用*/ 72 | interface DynamilizedClass extends DynamicObject { 73 | DynamicClass INSTANCE = DynamicClass.get(""); 74 | String NAME = ""; 75 | 76 | default DataPool.ReadOnlyPool super$(){ return null; } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /annotations/src/main/java/dynamilize/annotations/NewDynamic.java: -------------------------------------------------------------------------------- 1 | package dynamilize.annotations; 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 | /**此注解依赖{@linkplain DynamicContext 动态上下文标识}!您必须在所在环境外层添加动态上下文标识才能使用此注解 9 | *
实例化变量的动态化语句标志,用于声明一个java语义的动态对象实例化语句 10 | *

具体来讲,此注解的作用即将形如

{@code
11 |  * [modifiers] Type variableName = new Type()
12 |  * }
的语句转换为创建一个动态代理对象,这可以是字段声明,亦可以是方法中的局部变量声明,其中等号右侧必须是一个new语句。 13 | *
注意,右侧的new语句不得携带匿名扩展块,在未来的更新中可能会添加对此的支持 14 | *
假如您实例化的是一个非静态内部类,形如{@code outer.new Inner()}同样也是有效的 15 | * 16 | *

实例化动态代理对象声明的参数从注解的属性内提供,此注解的参数: 17 | *

    18 | *
  • {@link NewDynamic#refDynamicMaker()} - 引用{@linkplain dynamilize.DynamicMaker 动态工厂}的语句
  • 19 | *
  • {@link NewDynamic#dynClassName()} - 此动态对象所用的{@linkplain dynamilize.DynamicClass 动态类型}名称
  • 20 | *
  • {@link NewDynamic#impls()} - 该动态委托应用的额外接口表
  • 21 | *
  • {@link NewDynamic#aspects()} - 该动态委托的目标{@linkplain dynamilize.runtimeannos.AspectInterface 切面接口}
  • 22 | *
23 | * 使用上述的参数,此注解会将一个满足条件的语句变换为创建动态对象,例如: 24 | *
{@code
25 |  * //假设,在上文已经标记了一个动态工厂"maker"
26 |  * @NewDynamic(dynClassName = "SampleDyc", impls = {Collection.class, Runnable.class})
27 |  * Object dyObj = new Object();
28 |  * }
29 | * 那么,经过处理编译后的等效代码是这样的: 30 | *
{@code
31 |  * Object dyObj = maker.newInstance(Object.class, new Class[]{Collection.class, Runnable.class}, (Class[])null, DynamicClass.get("SampleDyc")).objSelf();
32 |  * }
33 | * 如果在new调用构造函数时有传入参数,参数会正确的添加在上述的语句最后一个参数处。一个比较特殊的地方在于,为了内存性能, 34 | * 您传递的接口列表(额外接口和切面接口)都会被处理为一个存在于类中的静态常量以供重复调用,对于列表接口类型相同的一组接口会复用同一个常量接口表 35 | *
36 | *
37 | *

另外,如果您声明的变量类型是{@link dynamilize.DynamicObject},同时将右侧表达式添加类型转换到DynamicObject,此时生成的语句将直接获取原始的动态对象, 38 | * 而不调用其{@code objSelf()}方法,举例来说,假设: 39 | *

{@code
40 |  * @NewDynamic(dynClassName = "SampleDyc", impls = {Collection.class, Runnable.class})
41 |  * DynamicObject dyObj = (DynamicObject)new Object();
42 |  * }
43 |  * 那么,经过处理编译后的等效代码是这样的:
44 |  * 
{@code
45 |  * DynamicObject dyObj = maker.newInstance(Object.class, new Class[]{Collection.class, Runnable.class}, (Class[])null, DynamicClass.get("SampleDyc"));
46 |  * }
47 |  * 简单来说这个注解的作用就是让您更便捷的使用java语句创建一个动态代理对象,而不需要使用maker的{@code newInstance(...)}方法
48 |  * 

关于此注解的作用细节,请参阅本注解的参数说明*/ 49 | @Retention(RetentionPolicy.SOURCE) 50 | @Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE}) 51 | public @interface NewDynamic { 52 | /**创建动态对象使用的{@linkplain dynamilize.DynamicMaker 动态工厂}引用语句,这会直接被整合为一段java语句插入到newInstance调用中, 53 | *

默认使用动态上下文中被标记的动态工厂,并且通常情况下也不推荐设置直接引用语句*/ 54 | String refDynamicMaker() default ""; 55 | /**此动态代理对象的动态类型名称,在生成的语句中直接调用{@code DynamicClass.get(...)}方法来获取指定的动态类型*/ 56 | String dynClassName(); 57 | /**动态委托要实现的额外接口列表*/ 58 | Class[] impls() default {}; 59 | /**动态委托要实现的{@linkplain dynamilize.runtimeannos.AspectInterface 切面接口}列表*/ 60 | Class[] aspects() default {void.class}; 61 | } 62 | -------------------------------------------------------------------------------- /apt/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /apt/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | apply plugin: 'maven-publish' 5 | 6 | sourceSets.main.java.srcDirs = ["src/main/java"] 7 | sourceSets.test.java.srcDirs = ["src/test/java"] 8 | 9 | group = 'com.github.EB-wilson' 10 | 11 | repositories { 12 | mavenCentral() 13 | maven{ url 'https://www.jitpack.io' } 14 | } 15 | 16 | tasks.withType(JavaCompile).configureEach { 17 | options.encoding = "UTF-8" 18 | 19 | sourceCompatibility = 17 20 | targetCompatibility = 17 21 | 22 | options.compilerArgs.addAll([ 23 | "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", 24 | "--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", 25 | "--add-exports", "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", 26 | "--add-exports", "jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", 27 | "--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", 28 | "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", 29 | "--add-exports", "jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", 30 | "--add-exports", "jdk.compiler/com.sun.tools.javac.resources=ALL-UNNAMED", 31 | ]) 32 | } 33 | 34 | publishing { 35 | publications { 36 | maven(MavenPublication) { 37 | groupId = 'com.github.EB-wilson.JavaDynamilizer' 38 | artifactId = 'apt' 39 | version = "$JDERVersion" 40 | from components.java 41 | } 42 | } 43 | } 44 | 45 | dependencies { 46 | implementation project(":annotations") 47 | 48 | compileOnly 'com.google.auto.service:auto-service:1.0.1' 49 | 50 | annotationProcessor 'com.google.auto.service:auto-service:1.0.1' 51 | } 52 | -------------------------------------------------------------------------------- /baseimpl/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /baseimpl/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | apply plugin: 'maven-publish' 5 | 6 | sourceSets.main.java.srcDirs = ["src/main/java"] 7 | sourceSets.test.java.srcDirs = ["src/test/java"] 8 | 9 | group = 'com.github.EB-wilson' 10 | 11 | sourceCompatibility = 16 12 | targetCompatibility = 16 13 | 14 | repositories { 15 | mavenCentral() 16 | maven{ url 'https://www.jitpack.io' } 17 | } 18 | 19 | publishing { 20 | publications { 21 | maven(MavenPublication) { 22 | groupId = 'com.github.EB-wilson.JavaDynamilizer' 23 | artifactId = 'baseimpl' 24 | version = "$JDERVersion" 25 | from components.java 26 | } 27 | } 28 | } 29 | 30 | dependencies { 31 | compileOnly project(":core") 32 | 33 | annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2' 34 | compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2' 35 | } 36 | 37 | tasks.withType(JavaCompile).configureEach { 38 | sourceCompatibility = 9 39 | targetCompatibility = 9 40 | 41 | options.compilerArgs.addAll([ 42 | "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED" 43 | ]) 44 | } 45 | -------------------------------------------------------------------------------- /baseimpl/src/main/java/dynamilize/Demodulator.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | import sun.misc.Unsafe; 4 | 5 | import java.lang.reflect.Constructor; 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.lang.reflect.Method; 9 | import java.util.*; 10 | 11 | /**反模块化工具, 仅提供了一个主要方法{@link Demodulator#makeModuleOpen(Module, Package, Module)}用于强制对需要的模块开放模块的软件包。 12 | *

此类行为可能完全打破模块化的访问保护,本身是不安全的,若不是必要情况,请尽量避免使用该类 13 | *

此类仅在JDK9之后可用,避免在更早的版本引用此类的方法,且此类仅在desktop平台可用,安卓平台不可使用此类的任何行为 14 | * 15 | * @author EBwilson */ 16 | @SuppressWarnings({"unchecked"}) 17 | public class Demodulator{ 18 | private static final long fieldFilterOffset = 112L; 19 | 20 | private static final Unsafe unsafe; 21 | 22 | private static final Field opensField; 23 | private static final Field exportField; 24 | 25 | private static final Method exportNative; 26 | 27 | static{ 28 | try{ 29 | Constructor cstr = Unsafe.class.getDeclaredConstructor(); 30 | cstr.setAccessible(true); 31 | unsafe = cstr.newInstance(); 32 | 33 | ensureFieldOpen(); 34 | 35 | opensField = Module.class.getDeclaredField("openPackages"); 36 | exportField = Module.class.getDeclaredField("exportedPackages"); 37 | 38 | makeModuleOpen(Module.class.getModule(), "java.lang", Demodulator.class.getModule()); 39 | 40 | exportNative = Module.class.getDeclaredMethod("addExports0", Module.class, String.class, Module.class); 41 | exportNative.setAccessible(true); 42 | exportNative.invoke(null, Module.class.getModule(), "java.lang", Demodulator.class.getModule()); 43 | }catch(NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException | 44 | NoSuchFieldException e){ 45 | throw new RuntimeException(e); 46 | } 47 | } 48 | 49 | public static void makeModuleOpen(Module from, Class clazz, Module to){ 50 | if (clazz.isArray()){ 51 | makeModuleOpen(from, clazz.getComponentType(), to); 52 | } 53 | else makeModuleOpen(from, clazz.getPackage(), to); 54 | } 55 | 56 | public static void makeModuleOpen(Module from, Package pac, Module to){ 57 | if(checkModuleOpen(from, pac, to)) return; 58 | 59 | makeModuleOpen(from, pac.getName(), to); 60 | } 61 | 62 | @SuppressWarnings("unchecked") 63 | public static void makeModuleOpen(Module from, String pac, Module to){ 64 | try { 65 | if (exportNative != null) exportNative.invoke(null, from, pac, to); 66 | } catch (IllegalAccessException | InvocationTargetException e) { 67 | throw new RuntimeException(e); 68 | } 69 | 70 | Map> opensMap = (Map>) unsafe.getObjectVolatile(from, unsafe.objectFieldOffset(opensField)); 71 | if(opensMap == null){ 72 | opensMap = new HashMap<>(); 73 | unsafe.putObjectVolatile(from, unsafe.objectFieldOffset(opensField), opensMap); 74 | } 75 | 76 | Map> exportsMap = (Map>) unsafe.getObjectVolatile(from, unsafe.objectFieldOffset(exportField)); 77 | if(exportsMap == null){ 78 | exportsMap = new HashMap<>(); 79 | unsafe.putObjectVolatile(from, unsafe.objectFieldOffset(exportField), exportsMap); 80 | } 81 | 82 | Set opens = opensMap.computeIfAbsent(pac, e -> new HashSet<>()); 83 | Set exports = exportsMap.computeIfAbsent(pac, e -> new HashSet<>()); 84 | 85 | try{ 86 | opens.add(to); 87 | }catch(UnsupportedOperationException e){ 88 | ArrayList lis = new ArrayList<>(opens); 89 | lis.add(to); 90 | opensMap.put(pac, new HashSet<>(lis)); 91 | } 92 | 93 | try{ 94 | exports.add(to); 95 | }catch(UnsupportedOperationException e){ 96 | ArrayList lis = new ArrayList<>(exports); 97 | lis.add(to); 98 | exportsMap.put(pac, new HashSet<>(lis)); 99 | } 100 | } 101 | 102 | public static boolean checkModuleOpen(Module from, Package pac, Module to){ 103 | Objects.requireNonNull(from); 104 | Objects.requireNonNull(to); 105 | 106 | if(pac == null) return true; 107 | 108 | return from.isOpen(pac.getName(), to); 109 | } 110 | 111 | public static void ensureFieldOpen(){ 112 | try{ 113 | Class clazz = Class.forName("jdk.internal.reflect.Reflection"); 114 | Map, Set> map = (Map, Set>) unsafe.getObject(clazz, fieldFilterOffset); 115 | map.clear(); 116 | }catch(ClassNotFoundException e){ 117 | throw new RuntimeException(e); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /baseimpl/src/main/java/dynamilize/DynamicFactory.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | import dynamilize.classmaker.ASMGenerator; 4 | import dynamilize.classmaker.AbstractClassGenerator; 5 | import dynamilize.classmaker.BaseClassLoader; 6 | import dynamilize.classmaker.ByteClassLoader; 7 | 8 | import java.lang.reflect.Constructor; 9 | import java.lang.reflect.Executable; 10 | import java.lang.reflect.Field; 11 | import java.lang.reflect.Method; 12 | import java.util.Objects; 13 | 14 | /**用于创建{@linkplain DynamicMaker 动态生成器}的工厂类,内部提供了默认生成器的声明 15 | * 16 | * @author EBwilson 17 | * @since 1.6*/ 18 | public class DynamicFactory { 19 | protected PackageAccHandler handler; 20 | protected AbstractClassGenerator generator; 21 | protected JavaHandleHelper helper; 22 | 23 | private static final DynamicFactory DEFAULT = new DynamicFactory(){{ 24 | setDefaultGenerator(); 25 | setDefaultHelper(); 26 | setPackageAccHandler(new UnsafePackageAccHandler(generator)); 27 | }}; 28 | 29 | /**直接获取默认的通用实现实例,该方法获取的动态生成器等价于: 30 | *

{@code
 31 |    * new DynamicFactory().setDefaultGenerator().setDefaultHelper().getMaker();
 32 |    * }
33 | * 34 | * @see DynamicFactory#setDefaultGenerator() 35 | * @see DynamicFactory#setDefaultHelper() 36 | * @return 一个由默认声明生产的动态生成器*/ 37 | public static DynamicMaker getDefault(){ 38 | return DEFAULT.getMaker(); 39 | } 40 | 41 | /**获取{@linkplain DynamicMaker 动态生成器}实例 42 | * 43 | * @throws NullPointerException 若有必要的属性尚未设置*/ 44 | public DynamicMaker getMaker(){ 45 | PackageAccHandler handler = this.handler; 46 | 47 | AbstractClassGenerator generator = this.generator; 48 | 49 | Objects.requireNonNull(helper); 50 | Objects.requireNonNull(generator); 51 | 52 | return handler == null? new DynamicMaker(helper) { 53 | @Override 54 | protected Class generateClass(Class baseClass, Class[] interfaces, Class[] aspects) { 55 | return makeClassInfo(baseClass, interfaces, aspects).generate(generator); 56 | } 57 | }: new DynamicMaker(helper) { 58 | @Override 59 | protected Class generateClass(Class baseClass, Class[] interfaces, Class[] aspects) { 60 | return makeClassInfo(baseClass, interfaces, aspects).generate(generator); 61 | } 62 | 63 | @Override 64 | protected Class handleBaseClass(Class baseClass) { 65 | return handler.handle(baseClass); 66 | } 67 | }; 68 | } 69 | 70 | /**设置{@linkplain PackageAccHandler 包私有访问处理器},若不设置则动态生成器不对超类的包私有方法进行委托 71 | * 72 | * @see PackageAccHandler*/ 73 | public DynamicFactory setPackageAccHandler(PackageAccHandler handler){ 74 | this.handler = handler; 75 | return this; 76 | } 77 | 78 | /**设置将{@linkplain dynamilize.classmaker.ClassInfo 类型描述}生成为Java类对象的{@linkplain AbstractClassGenerator 类型生成器},必要! 79 | * 80 | * @see AbstractClassGenerator*/ 81 | public DynamicFactory setGenerator(AbstractClassGenerator generator){ 82 | this.generator = generator; 83 | return this; 84 | } 85 | 86 | /**设置对Java层行为进行处理的{@linkplain JavaHandleHelper Java行为支持器},必要! 87 | * 88 | * @see JavaHandleHelper*/ 89 | public DynamicFactory setJavaHandleHelper(JavaHandleHelper helper){ 90 | this.helper = helper; 91 | return this; 92 | } 93 | 94 | /**使用{@linkplain DynamicFactory#setDefaultGenerator(ByteClassLoader, int) 默认配置}设置类型生成器,字节码加载器使用默认实现{@link BaseClassLoader},目标字节码版本为152(Java1.8) 95 | * 96 | * @see DynamicFactory#setDefaultGenerator(ByteClassLoader, int) 97 | * @see BaseClassLoader*/ 98 | public DynamicFactory setDefaultGenerator(){ 99 | return setDefaultGenerator(new BaseClassLoader(DynamicFactory.class.getClassLoader()), 52); 100 | } 101 | 102 | /**将类型生成器设置为{@link ASMGenerator}的实现,该实现适用于绝大多数情况的JVM 103 | * 104 | * @param loader 加载字节码为类对象的类加载器 105 | * @param byteLevel 目标字节码级别,最低支持50(Java1.8) 106 | * 107 | * @see ASMGenerator*/ 108 | public DynamicFactory setDefaultGenerator(ByteClassLoader loader, int byteLevel){ 109 | if (byteLevel < 52 || byteLevel > 255) 110 | throw new IllegalArgumentException("illegal byte code level: " + byteLevel); 111 | 112 | generator = new ASMGenerator(loader, byteLevel); 113 | 114 | return this; 115 | } 116 | 117 | /**设置{@link JavaHandleHelper}的默认内部实现,该实现直接引用{@link JavaVariable}和{@link JavaMethodEntry}以及默认的反模块化访问 118 | * 119 | * @see JavaVariable 120 | * @see JavaMethodEntry*/ 121 | public DynamicFactory setDefaultHelper(){ 122 | helper = new JavaHandleHelper() { 123 | @Override 124 | public void makeAccess(Object object) { 125 | if (object instanceof Executable ext){ 126 | if (ext instanceof Method method) Demodulator.makeModuleOpen(method.getReturnType().getModule(), method.getReturnType(), DynamicMaker.class.getModule()); 127 | 128 | if (ext instanceof Constructor cstr) Demodulator.makeModuleOpen(cstr.getDeclaringClass().getModule(), cstr.getDeclaringClass(), DynamicMaker.class.getModule()); 129 | 130 | for (Class type : ext.getParameterTypes()) { 131 | Demodulator.makeModuleOpen(type.getModule(), type, DynamicMaker.class.getModule()); 132 | } 133 | 134 | ext.setAccessible(true); 135 | } 136 | else if (object instanceof Field field){ 137 | Demodulator.makeModuleOpen(field.getType().getModule(), field.getType(), DynamicMaker.class.getModule()); 138 | 139 | field.setAccessible(true); 140 | } 141 | else if (object instanceof Class clazz){ 142 | Demodulator.makeModuleOpen(clazz.getModule(), clazz.getPackage(), DynamicMaker.class.getModule()); 143 | } 144 | } 145 | 146 | @Override 147 | public IVariable genJavaVariableRef(Field field) { 148 | makeAccess(field); 149 | return new JavaVariable(field); 150 | } 151 | 152 | @Override 153 | public IFunctionEntry genJavaMethodRef(Method method) { 154 | makeAccess(method); 155 | return new JavaMethodEntry(method); 156 | } 157 | }; 158 | 159 | return this; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /baseimpl/src/main/java/dynamilize/JavaMethodEntry.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.lang.reflect.Method; 5 | 6 | /**对{@linkplain DynamicClass#visitClass(Class, JavaHandleHelper) 行为样版}中方法描述的入口,在动态类中描述子实例的某一函数行为。 7 | *

方法入口的运行实际上是对样版方法的引用,因此需要确保样版方法所在的类始终有效,方法入口会生成这个方法的入口函数提供给动态对象使用 8 | * 9 | * @author EBwilson */ 10 | @SuppressWarnings("unchecked") 11 | public class JavaMethodEntry implements IFunctionEntry{ 12 | private final String name; 13 | private final FunctionType type; 14 | private final Function defFunc; 15 | 16 | /**直接通过目标方法创建方法入口,并生成对方法引用的句柄提供给匿名函数以描述此函数行为 17 | * 18 | * @param invokeMethod 样版方法*/ 19 | public JavaMethodEntry(Method invokeMethod){ 20 | this.name = invokeMethod.getName(); 21 | this.type = FunctionType.inst(invokeMethod); 22 | 23 | defFunc = (self, args) -> { 24 | try { 25 | return invokeMethod.invoke(self.objSelf(), args.args()); 26 | } catch (InvocationTargetException|IllegalAccessException e) { 27 | throw new RuntimeException(e); 28 | } 29 | }; 30 | } 31 | 32 | @Override 33 | public String getName(){ 34 | return name; 35 | } 36 | 37 | @Override 38 | public Function getFunction(){ 39 | return (Function) defFunc; 40 | } 41 | 42 | @Override 43 | public FunctionType getType(){ 44 | return type; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /baseimpl/src/main/java/dynamilize/JavaVariable.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | import java.lang.invoke.MethodHandle; 4 | import java.lang.invoke.MethodHandles; 5 | import java.lang.reflect.Field; 6 | 7 | /**对java字段的引用对象,基于{@linkplain MethodHandle 方法句柄}的默认内部实现 8 | * 9 | * @author EBwilson */ 10 | public class JavaVariable implements IVariable{ 11 | private final Field field; 12 | 13 | private final MethodHandle getter; 14 | private final MethodHandle setter; 15 | 16 | private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 17 | 18 | public JavaVariable(Field field){ 19 | this.field = field; 20 | 21 | try { 22 | getter = LOOKUP.unreflectGetter(field); 23 | setter = LOOKUP.unreflectSetter(field); 24 | } catch (IllegalAccessException e) { 25 | throw new RuntimeException(e); 26 | } 27 | } 28 | 29 | @Override 30 | public String name(){ 31 | return field.getName(); 32 | } 33 | 34 | @Override 35 | public void init(DynamicObject object) { /*no action*/ } 36 | 37 | @SuppressWarnings("unchecked") 38 | @Override 39 | public T get(DynamicObject obj){ 40 | try{ 41 | return (T) getter.invoke(obj); 42 | }catch(ClassCastException e){ 43 | throw e; 44 | }catch(Throwable e){ 45 | throw new IllegalHandleException(e); 46 | } 47 | } 48 | 49 | @Override 50 | public void set(DynamicObject obj, Object value){ 51 | try{ 52 | setter.invoke(obj, value); 53 | }catch(Throwable e){ 54 | throw new IllegalHandleException(e); 55 | } 56 | } 57 | 58 | @Override 59 | public boolean get(DynamicObject obj, boolean def) { 60 | try { 61 | return field.getBoolean(obj); 62 | } catch (IllegalAccessException e) { 63 | throw new IllegalHandleException(e); 64 | } 65 | } 66 | 67 | @Override 68 | public byte get(DynamicObject obj, byte def) { 69 | try { 70 | return field.getByte(obj); 71 | } catch (IllegalAccessException e) { 72 | throw new IllegalHandleException(e); 73 | } 74 | } 75 | 76 | @Override 77 | public short get(DynamicObject obj, short def) { 78 | try { 79 | return field.getShort(obj); 80 | } catch (IllegalAccessException e) { 81 | throw new IllegalHandleException(e); 82 | } 83 | } 84 | 85 | @Override 86 | public int get(DynamicObject obj, int def) { 87 | try { 88 | return field.getInt(obj); 89 | } catch (IllegalAccessException e) { 90 | throw new IllegalHandleException(e); 91 | } 92 | } 93 | 94 | @Override 95 | public long get(DynamicObject obj, long def) { 96 | try { 97 | return field.getLong(obj); 98 | } catch (IllegalAccessException e) { 99 | throw new IllegalHandleException(e); 100 | } 101 | } 102 | 103 | @Override 104 | public float get(DynamicObject obj, float def) { 105 | try { 106 | return field.getFloat(obj); 107 | } catch (IllegalAccessException e) { 108 | throw new IllegalHandleException(e); 109 | } 110 | } 111 | 112 | @Override 113 | public double get(DynamicObject obj, double def) { 114 | try { 115 | return field.getDouble(obj); 116 | } catch (IllegalAccessException e) { 117 | throw new IllegalHandleException(e); 118 | } 119 | } 120 | 121 | @Override 122 | public void set(DynamicObject obj, boolean value) { 123 | try { 124 | field.setBoolean(obj, value); 125 | } catch (IllegalAccessException e) { 126 | throw new RuntimeException(e); 127 | } 128 | } 129 | 130 | @Override 131 | public void set(DynamicObject obj, byte value) { 132 | try { 133 | field.setByte(obj, value); 134 | } catch (IllegalAccessException e) { 135 | throw new RuntimeException(e); 136 | } 137 | } 138 | 139 | @Override 140 | public void set(DynamicObject obj, short value) { 141 | try { 142 | field.setShort(obj, value); 143 | } catch (IllegalAccessException e) { 144 | throw new RuntimeException(e); 145 | } 146 | } 147 | 148 | @Override 149 | public void set(DynamicObject obj, int value) { 150 | try { 151 | field.setInt(obj, value); 152 | } catch (IllegalAccessException e) { 153 | throw new RuntimeException(e); 154 | } 155 | } 156 | 157 | @Override 158 | public void set(DynamicObject obj, long value) { 159 | try { 160 | field.setLong(obj, value); 161 | } catch (IllegalAccessException e) { 162 | throw new RuntimeException(e); 163 | } 164 | } 165 | 166 | @Override 167 | public void set(DynamicObject obj, float value) { 168 | try { 169 | field.setFloat(obj, value); 170 | } catch (IllegalAccessException e) { 171 | throw new RuntimeException(e); 172 | } 173 | } 174 | 175 | @Override 176 | public void set(DynamicObject obj, double value) { 177 | try { 178 | field.setDouble(obj, value); 179 | } catch (IllegalAccessException e) { 180 | throw new RuntimeException(e); 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /baseimpl/src/main/java/dynamilize/UnsafePackageAccHandler.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | import dynamilize.classmaker.AbstractClassGenerator; 4 | import dynamilize.classmaker.ClassInfo; 5 | import jdk.internal.misc.Unsafe; 6 | 7 | import java.lang.reflect.Constructor; 8 | import java.lang.reflect.InvocationTargetException; 9 | 10 | /**基于{@link jdk.internal.misc.Unsafe}实现的强制保护域类加载处理程序 11 | * 12 | * @author EBwilson 13 | * @since 1.6*/ 14 | public class UnsafePackageAccHandler extends PackageAccHandler{ 15 | private static final Object unsafe; 16 | 17 | static{ 18 | try{ 19 | Demodulator.makeModuleOpen(Object.class.getModule(), "jdk.internal.misc", UnsafePackageAccHandler.class.getModule()); 20 | 21 | Constructor cstr = Unsafe.class.getDeclaredConstructor(); 22 | cstr.setAccessible(true); 23 | unsafe = cstr.newInstance(); 24 | }catch(NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e){ 25 | throw new RuntimeException(e); 26 | } 27 | } 28 | 29 | private final AbstractClassGenerator generator; 30 | 31 | public UnsafePackageAccHandler(AbstractClassGenerator generator){ 32 | this.generator = generator; 33 | } 34 | 35 | @Override 36 | protected Class loadClass(ClassInfo clazz, Class baseClass) { 37 | return defineClass(clazz.name(), generator.genByteCode(clazz), baseClass.getClassLoader()); 38 | } 39 | 40 | @SuppressWarnings("unchecked") 41 | static Class defineClass(String name, byte[] bytes, ClassLoader loader){ 42 | return (Class) ((Unsafe)unsafe).defineClass (name, bytes, 0, bytes.length, loader, null); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'com.github.johnrengelman.shadow' version '2.0.4' 4 | } 5 | 6 | apply plugin: 'maven-publish' 7 | 8 | ext{ 9 | JDERVersion = 'V1.9-A1' 10 | } 11 | 12 | group 'com.github.EB-wilson' 13 | version JDERVersion 14 | 15 | publishing { 16 | publications { 17 | maven(MavenPublication) { 18 | from components.java 19 | org.gradle.api.internal.tasks.compile.JdkJavaCompiler 20 | } 21 | } 22 | } 23 | 24 | assemble{ 25 | dependsOn(shadowJar) 26 | } 27 | 28 | generateMetadataFileForMavenPublication{ 29 | dependsOn(shadowJar) 30 | } 31 | 32 | allprojects{ 33 | tasks.withType(JavaCompile).tap { 34 | configureEach { 35 | options.encoding = "UTF-8" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | apply plugin: 'maven-publish' 5 | 6 | sourceSets.main.java.srcDirs = ["src/main/java"] 7 | sourceSets.test.java.srcDirs = ["src/test/java"] 8 | 9 | repositories { 10 | mavenCentral() 11 | maven{ url 'https://www.jitpack.io' } 12 | } 13 | 14 | sourceCompatibility = 17 15 | targetCompatibility = 8 16 | 17 | tasks.withType(JavaCompile).configureEach { 18 | options.compilerArgs.addAll([ 19 | "--release", "8" 20 | ]) 21 | } 22 | 23 | publishing { 24 | publications { 25 | maven(MavenPublication) { 26 | groupId = 'com.github.EB-wilson.JavaDynamilizer' 27 | artifactId = 'core' 28 | version = "$JDERVersion" 29 | from components.java 30 | } 31 | } 32 | } 33 | 34 | dependencies { 35 | implementation 'org.ow2.asm:asm:9.3' 36 | apiElements 'org.ow2.asm:asm:9.3' 37 | 38 | annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2' 39 | compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2' 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/ArgumentList.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | import java.util.Arrays; 4 | import java.util.Stack; 5 | 6 | /**实参列表的封装对象,记录了一份实际参数列表,提供了一个泛型获取参数的方法,用于减少函数引用时所需的冗余类型转换。 7 | *

参数表对象为可复用对象,引用完毕后请使用{@link ArgumentList#recycle()}回收对象以减少堆内存更新频率 8 | * 9 | * @author EBwilson */ 10 | public class ArgumentList{ 11 | public static int MAX_INSTANCE_STACK = 2048; 12 | 13 | public static final Stack[] ARG_LEN_MAP = new Stack[64]; 14 | public static final Object[] EMP_ARG = new Object[0]; 15 | 16 | static { 17 | for(int i = 1; i < ARG_LEN_MAP.length; i++){ 18 | ARG_LEN_MAP[i] = new Stack<>(); 19 | } 20 | } 21 | 22 | private static final Stack INSTANCES = new Stack<>(); 23 | 24 | private Object[] args; 25 | private FunctionType type; 26 | 27 | /**私有构造器,不允许外部直接使用*/ 28 | private ArgumentList(){} 29 | 30 | public static synchronized Object[] getList(int len){ 31 | if(len == 0) return EMP_ARG; 32 | 33 | Stack stack = ARG_LEN_MAP[len]; 34 | return stack.isEmpty()? new Object[len]: stack.pop(); 35 | } 36 | 37 | public static void recycleList(Object[] list){ 38 | if(list.length == 0) return; 39 | 40 | Stack stack = ARG_LEN_MAP[list.length]; 41 | if(stack.size() > MAX_INSTANCE_STACK) return; 42 | stack.push(list); 43 | } 44 | 45 | /**使用一组实参列表获取一个封装参数列表,优先从实例堆栈中弹出,若堆栈中没有实例才会构造一个新的 46 | * 参数如果包含null,建议使用{@link ArgumentList#asWithType(FunctionType, Object...)}来获取参数列表,在明确指定形参的情况下执行可以具有更高的效率 47 | * 48 | * @param args 实参列表 49 | * @return 封装参数对象*/ 50 | public static synchronized ArgumentList as(Object... args){ 51 | ArgumentList res = INSTANCES.isEmpty()? new ArgumentList(): INSTANCES.pop(); 52 | res.args = args; 53 | res.type = FunctionType.inst(args); 54 | 55 | return res; 56 | } 57 | 58 | public static synchronized ArgumentList asWithType(FunctionType type, Object... args){ 59 | ArgumentList res = INSTANCES.isEmpty()? new ArgumentList(): INSTANCES.pop(); 60 | res.args = args; 61 | res.type = type; 62 | 63 | return res; 64 | } 65 | 66 | /**回收实例,使实例重新入栈,若堆栈已到达最大容量,则不会继续插入实例堆栈中*/ 67 | public void recycle(){ 68 | args = null; 69 | type = null; 70 | if(INSTANCES.size() >= MAX_INSTANCE_STACK) return; 71 | 72 | INSTANCES.add(this); 73 | } 74 | 75 | /**获取给定索引处的实参值,类型根据引用推断,若类型不可用会抛出类型转换异常 76 | * 77 | * @param index 参数在列表中的索引位置 78 | * @param 参数类型,通常可以根据引用推断 79 | * @return 实参的值 80 | * @throws ClassCastException 若接受者所需的类型与参数类型不可分配*/ 81 | @SuppressWarnings("unchecked") 82 | public T get(int index){ 83 | return (T) args[index]; 84 | } 85 | 86 | /**获取实参列表的数组 87 | * 88 | * @return 实参构成的数组*/ 89 | public Object[] args(){ 90 | return args; 91 | } 92 | 93 | /**获取形式参数类型封装对象 94 | * 95 | * @return 该实参列表的形式参数类型*/ 96 | public FunctionType type(){ 97 | return type; 98 | } 99 | 100 | @Override 101 | public String toString(){ 102 | String arg = Arrays.toString(args); 103 | return "(" + arg.substring(1, arg.length() - 1) + ")"; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/Calculator.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | /**变量计算器,函数式接口,用于对变量的当前值进行一系列处理后提供回调 4 | * 5 | * @author EBwilson */ 6 | @FunctionalInterface 7 | public interface Calculator{ 8 | Type calculate(Type input); 9 | 10 | @FunctionalInterface interface BoolCalculator{ boolean calculate(boolean input); } 11 | @FunctionalInterface interface ByteCalculator{ byte calculate(byte input); } 12 | @FunctionalInterface interface ShortCalculator{ short calculate(short input); } 13 | @FunctionalInterface interface IntCalculator{ int calculate(int input); } 14 | @FunctionalInterface interface LongCalculator{ long calculate(long input); } 15 | @FunctionalInterface interface FloatCalculator{ float calculate(float input); } 16 | @FunctionalInterface interface DoubleCalculator{ double calculate(double input); } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/ClassImplements.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | import java.util.Arrays; 4 | import java.util.Objects; 5 | 6 | /**保存类的超类及实现接口的容器,用于快速确认委托的构造类型 7 | * 8 | * @author EBwilson */ 9 | class ClassImplements{ 10 | final Class base; 11 | final Class[] interfaces; 12 | final Class[] aspects; 13 | private int hash; 14 | 15 | public ClassImplements(Class base, Class[] interfaces, Class[] aspects){ 16 | this.base = base; 17 | this.interfaces = interfaces; 18 | this.aspects = aspects; 19 | this.hash = Objects.hash(base); 20 | hash = 31*hash + Arrays.hashCode(interfaces)^Arrays.hashCode(aspects); 21 | } 22 | 23 | @Override 24 | public String toString(){ 25 | return base.getCanonicalName() + Arrays.hashCode(Arrays.stream(interfaces).map(Class::getCanonicalName).toArray(String[]::new)); 26 | } 27 | 28 | @Override 29 | public boolean equals(Object o){ 30 | if(this == o) return true; 31 | if(!(o instanceof ClassImplements that)) return false; 32 | return base.equals(that.base) && interfaces.length == that.interfaces.length && (aspects != null && that.aspects != null && aspects.length == that.aspects.length) && hash == that.hash; 33 | } 34 | 35 | @Override 36 | public int hashCode(){ 37 | return hash; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/DataPool.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | import java.util.*; 4 | 5 | /**用于存储和处置动态对象数据的信息容器,不应从外部访问,每一个动态对象都会绑定一个数据池存放对象的变量/函数等信息。 6 | *

对于一个{@linkplain DynamicClass 动态类}的实例,实例的数据池一定会有一个父池,这个池以动态类的直接超类描述的信息进行初始化。 7 | *

访问池信息无论如何都是以最近原则,即若本池内没有找到数据,则以距离实例的池最近的具有此变量/函数的父池的数据为准 8 | * 9 | * @author EBwilson*/ 10 | @SuppressWarnings({"unchecked"}) 11 | public class DataPool{ 12 | private static final String init = ""; 13 | 14 | private static final List TMP_LIS = new ArrayList<>(); 15 | public static final IFunctionEntry[] EMP_METS = new IFunctionEntry[0]; 16 | 17 | private static final List TMP_VAR = new ArrayList<>(); 18 | public static final IVariable[] EMP_VARS = new IVariable[0]; 19 | 20 | private final DataPool superPool; 21 | 22 | private final Map> funcPool = new HashMap<>(); 23 | private final Map varPool = new HashMap<>(); 24 | 25 | /**创建一个池对象并绑定到父池,父池可为null,这种情况下此池应当为被委托类型的方法/字段引用。 26 | *

通常来说你不应该在{@link DynamicMaker}之外的任何地方实例化此类型 27 | * 28 | * @param superPool 此池的父池*/ 29 | public DataPool(DataPool superPool){ 30 | this.superPool = superPool; 31 | } 32 | 33 | public void init(DynamicObject self, Object... args){ 34 | DynamicClass curr = self.getDyClass(); 35 | HashSet varSetted = new HashSet<>(); 36 | 37 | while(curr != null){ 38 | for (IVariable var : curr.getVariables()) { 39 | if (varSetted.add(var.name())){ 40 | var.init(self); 41 | } 42 | } 43 | 44 | curr = curr.superDyClass(); 45 | } 46 | 47 | ArgumentList lis = ArgumentList.as(args); 48 | IFunctionEntry fun = select(init, lis.type()); 49 | if(fun == null) return; 50 | 51 | fun.getFunction().invoke((DynamicObject) self, args); 52 | lis.type().recycle(); 53 | lis.recycle(); 54 | } 55 | 56 | public void setConstructor(Function function, Class... argType){ 57 | setFunction(init, function, argType); 58 | } 59 | 60 | public Function getConstructor(Class... argType){ 61 | FunctionType type = FunctionType.inst(argType); 62 | Function fun = select(init, type).getFunction(); 63 | type.recycle(); 64 | return fun; 65 | } 66 | 67 | /**在本池设置一个函数,无论父池是否具有同名同类型的函数存在,若本池中存在同名同类型函数,则旧函数将被覆盖。 68 | *

函数的名称,行为与参数类型和java方法保持一致,返回值由行为传入的匿名函数确定,例如: 69 | *

{@code
 70 |    * java定义的方法
 71 |    * int getTime(String location){
 72 |    *   return foo(location);
 73 |    * }
 74 |    * 等价于设置函数
 75 |    * set("getTime", (self, args) -> return foo(args.get(0)), String.class);
 76 |    * }
77 | * 78 | * @param name 函数名称 79 | * @param argsType 函数的参数类型列表 80 | * @param function 描述此函数行为的匿名函数*/ 81 | public void setFunction(String name, Function function, Class... argsType){ 82 | FunctionType type = FunctionType.inst(argsType); 83 | funcPool.computeIfAbsent(name, n -> new HashMap<>()) 84 | .put(type, new FunctionEntry<>(name, function, type)); 85 | } 86 | 87 | public void setFunction(String name, Function.SuperGetFunction func, Class[] argTypes){ 88 | FunctionType type = FunctionType.inst(argTypes); 89 | funcPool.computeIfAbsent(name, n -> new HashMap<>()) 90 | .put(type, new FunctionEntry<>(name, func, type, this)); 91 | } 92 | 93 | public void setFunction(IFunctionEntry functionEntry){ 94 | funcPool.computeIfAbsent(functionEntry.getName(), e -> new HashMap<>()).put(functionEntry.getType(), functionEntry); 95 | } 96 | 97 | /**从类层次结构中获取变量的对象 98 | * 99 | * @param name 变量名 100 | * @return 变量对象*/ 101 | public IVariable getVariable(String name){ 102 | IVariable var = varPool.get(name); 103 | if(var != null) return var; 104 | 105 | if(superPool != null){ 106 | return superPool.getVariable(name); 107 | } 108 | 109 | return null; 110 | } 111 | 112 | /**向池中添加一个变量对象,一般来说仅在标记java字段作为变量时会用到 113 | * 114 | * @param var 加入池的变量*/ 115 | public void setVariable(IVariable var){ 116 | varPool.putIfAbsent(var.name(), var); 117 | } 118 | 119 | /**将类层次结构中定义的函数输出为函数入口,会优先查找类型签名相同的函数,若未查找到相同的才会转入类型签名匹配的函数, 120 | * 因此调用函数在性能需求较高的情况下,建议对实参列表明确声明类型的签名,这可以有效提高重载决策的速度 121 | *

如果函数没有被定义则返回空 122 | * 123 | * @param name 函数的名称 124 | * @param type 函数的参数类型 125 | * @return 选中函数的函数入口*/ 126 | public IFunctionEntry select(String name, FunctionType type){ 127 | Map map; 128 | IFunctionEntry res; 129 | 130 | DataPool curr = this; 131 | while(curr != null){ 132 | map = curr.funcPool.get(name); 133 | if(map != null){ 134 | res = map.get(type); 135 | if(res != null) return res; 136 | } 137 | 138 | curr = curr.superPool; 139 | } 140 | 141 | curr = this; 142 | while(curr != null){ 143 | map = curr.funcPool.get(name); 144 | if(map != null){ 145 | for(Map.Entry entry: map.entrySet()){ 146 | if(entry.getKey().match(type.getTypes())){ 147 | return entry.getValue(); 148 | } 149 | } 150 | } 151 | 152 | curr = curr.superPool; 153 | } 154 | 155 | return null; 156 | } 157 | 158 | public IVariable[] getVariables(){ 159 | TMP_VAR.clear(); 160 | TMP_VAR.addAll(varPool.values()); 161 | return TMP_VAR.toArray(EMP_VARS); 162 | } 163 | 164 | public IFunctionEntry[] getFunctions(){ 165 | TMP_LIS.clear(); 166 | for(Map entry: funcPool.values()){ 167 | TMP_LIS.addAll(entry.values()); 168 | } 169 | 170 | return TMP_LIS.toArray(EMP_METS); 171 | } 172 | 173 | /**获得池的只读对象*/ 174 | public ReadOnlyPool getReader(DynamicObject owner){ 175 | return new ReadOnlyPool(this, owner, null); 176 | } 177 | 178 | public ReadOnlyPool getSuper(DynamicObject owner, ReadOnlyPool alternative){ 179 | return superPool == null? alternative: ReadOnlyPool.get(superPool, owner, alternative); 180 | } 181 | 182 | public static class ReadOnlyPool{ 183 | public static int MAX_CHANCES = 2048; 184 | private static final Stack POOLS = new Stack<>(); 185 | 186 | private DataPool pool; 187 | private DynamicObject owner; 188 | private ReadOnlyPool alternative; 189 | 190 | private final boolean hold; 191 | 192 | private ReadOnlyPool(){ 193 | hold = false; 194 | } 195 | 196 | private ReadOnlyPool(DataPool pool, DynamicObject owner, ReadOnlyPool alternative){ 197 | this.pool = pool; 198 | this.owner = owner; 199 | this.alternative = alternative; 200 | 201 | hold = true; 202 | } 203 | 204 | private static ReadOnlyPool get(DataPool source, DynamicObject owner, ReadOnlyPool alternative){ 205 | ReadOnlyPool res = POOLS.isEmpty()? new ReadOnlyPool(): POOLS.pop(); 206 | res.pool = source; 207 | res.owner = owner; 208 | res.alternative = alternative; 209 | 210 | return res; 211 | } 212 | 213 | public void recycle(){ 214 | if(hold) return; 215 | 216 | pool = null; 217 | owner = null; 218 | alternative = null; 219 | 220 | if(POOLS.size() < MAX_CHANCES) POOLS.push(this); 221 | } 222 | 223 | /**@see DynamicObject#getVar(String)*/ 224 | public T getVar(String name){ 225 | IVariable var = pool.getVariable(name); 226 | 227 | if(var == null) var = alternative == null? null: alternative.getVar(name); 228 | 229 | if(var == null) 230 | throw new IllegalHandleException("variable " + name + " was not definer"); 231 | 232 | return var.get(owner); 233 | } 234 | 235 | /**@see DynamicObject#getFunc(String, FunctionType)*/ 236 | public IFunctionEntry getFunc(String name, FunctionType type){ 237 | IFunctionEntry func = pool.select(name, type); 238 | if(func == null){ 239 | if(alternative == null) 240 | throw new IllegalHandleException("no such function: " + name + type); 241 | 242 | return alternative.getFunc(name, type); 243 | } 244 | 245 | return func; 246 | } 247 | 248 | /**@see DynamicObject#invokeFunc(String, Object...)*/ 249 | public R invokeFunc(String name, Object... args){ 250 | ArgumentList lis = ArgumentList.as(args); 251 | R r = invokeFunc(name, lis); 252 | lis.type().recycle(); 253 | lis.recycle(); 254 | return r; 255 | } 256 | 257 | public R invokeFunc(FunctionType type, String name, Object... args){ 258 | ArgumentList lis = ArgumentList.asWithType(type, args); 259 | R r = invokeFunc(name, lis); 260 | lis.recycle(); 261 | return r; 262 | } 263 | 264 | /**@see DynamicObject#invokeFunc(String, ArgumentList)*/ 265 | public R invokeFunc(String name, ArgumentList args){ 266 | FunctionType type = args.type(); 267 | 268 | return (R) getFunc(name, type).getFunction().invoke((DynamicObject) owner, args); 269 | } 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/Delegate.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | /**经过包装的委托调用函数接口,与{@linkplain Function 函数}不同,委托不接收this指针*/ 4 | @FunctionalInterface 5 | public interface Delegate{ 6 | R invoke(ArgumentList args); 7 | 8 | default R invoke(Object... args){ 9 | ArgumentList lis = ArgumentList.as(args); 10 | R r = invoke(lis); 11 | lis.type().recycle(); 12 | lis.recycle(); 13 | return r; 14 | } 15 | 16 | default R invoke(FunctionType type, Object... args){ 17 | ArgumentList lis = ArgumentList.asWithType(type, args); 18 | R r = invoke(lis); 19 | lis.recycle(); 20 | return r; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/Function.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | @FunctionalInterface 4 | public interface Function{ 5 | R invoke(DynamicObject self, ArgumentList args); 6 | 7 | default R invoke(DynamicObject self, Object... args){ 8 | ArgumentList lis = ArgumentList.as(args); 9 | R r = invoke(self, lis); 10 | lis.type().recycle(); 11 | lis.recycle(); 12 | return r; 13 | } 14 | 15 | default R invoke(DynamicObject self, FunctionType type, Object... args){ 16 | ArgumentList lis = ArgumentList.asWithType(type, args); 17 | R r = invoke(self, lis); 18 | lis.recycle(); 19 | return r; 20 | } 21 | 22 | interface NonRetFunction{ 23 | void invoke(DynamicObject self, ArgumentList args); 24 | } 25 | 26 | interface SuperGetFunction{ 27 | R invoke(DynamicObject self, DataPool.ReadOnlyPool superPointer, ArgumentList args); 28 | } 29 | 30 | interface NonRetSuperGetFunc{ 31 | void invoke(DynamicObject self, DataPool.ReadOnlyPool superPointer, ArgumentList args); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/FunctionEntry.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | 4 | public class FunctionEntry implements IFunctionEntry{ 5 | private final String name; 6 | private final Function func; 7 | private final FunctionType type; 8 | 9 | public FunctionEntry(String name, Function func, FunctionType type){ 10 | this.name = name; 11 | this.func = func; 12 | this.type = type; 13 | } 14 | 15 | public FunctionEntry(String name, Function.SuperGetFunction func, FunctionType type, DataPool owner){ 16 | this( 17 | name, 18 | (s, a) -> { 19 | DataPool.ReadOnlyPool p = owner.getSuper(s, s.baseSuperPointer()); 20 | R res = func.invoke(s, p, a); 21 | p.recycle(); 22 | return res; 23 | }, 24 | type 25 | ); 26 | } 27 | 28 | @Override 29 | public String getName(){ 30 | return name; 31 | } 32 | 33 | @Override 34 | @SuppressWarnings("unchecked") 35 | public Function getFunction(){ 36 | return func; 37 | } 38 | 39 | @Override 40 | public FunctionType getType(){ 41 | return type; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/FunctionType.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | import dynamilize.classmaker.ClassInfo; 4 | 5 | import java.lang.invoke.MethodType; 6 | import java.lang.reflect.Executable; 7 | import java.lang.reflect.Method; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.Stack; 11 | 12 | /**函数类型封装对象,记录函数的参数类型用于比对和搜索函数 13 | * 14 | * @author EBwilson */ 15 | public class FunctionType{ 16 | /**复用回收区容量,改数值通常不需要设置,但如果您可能需要大规模的递归或大量的并发调用,那么您可能需要将这个限制设置为一个更高的数值*/ 17 | public static int MAX_RECYCLE = 4096; 18 | 19 | private static final Class[] EMPTY = new Class[0]; 20 | private static final Stack RECYCLE_POOL = new Stack<>(); 21 | 22 | private Class[] paramType; 23 | private int hash; 24 | 25 | private FunctionType(Class... paramType){ 26 | this.paramType = paramType; 27 | hash = Arrays.hashCode(paramType); 28 | } 29 | 30 | //此类存在频繁的调用,数据量小,使用流处理数据会产生不必要的性能花销,使用for遍历取代流处理 31 | public static Class[] wrapper(Class... clazz){ 32 | for(int i = 0; i < clazz.length; i++){ 33 | clazz[i] = wrapper(clazz[i]); 34 | } 35 | return clazz; 36 | } 37 | 38 | public static Class[] unwrapped(Class... clazz){ 39 | for(int i = 0; i < clazz.length; i++){ 40 | clazz[i] = unwrapped(clazz[i]); 41 | } 42 | return clazz; 43 | } 44 | 45 | public static Class wrapper(Class clazz){ 46 | if(clazz == int.class) return Integer.class; 47 | if(clazz == float.class) return Float.class; 48 | if(clazz == long.class) return Long.class; 49 | if(clazz == double.class) return Double.class; 50 | if(clazz == byte.class) return Byte.class; 51 | if(clazz == short.class) return Short.class; 52 | if(clazz == boolean.class) return Boolean.class; 53 | if(clazz == char.class) return Character.class; 54 | return clazz; 55 | } 56 | 57 | public static Class unwrapped(Class clazz){ 58 | if(clazz == Integer.class) return int.class; 59 | if(clazz == Float.class) return float.class; 60 | if(clazz == Long.class) return long.class; 61 | if(clazz == Double.class) return double.class; 62 | if(clazz == Byte.class) return byte.class; 63 | if(clazz == Short.class) return short.class; 64 | if(clazz == Boolean.class) return boolean.class; 65 | if(clazz == Character.class) return char.class; 66 | return clazz; 67 | } 68 | 69 | public static FunctionType inst(List> paramType){ 70 | return inst(paramType.toArray(new Class[0])); 71 | } 72 | 73 | public static synchronized FunctionType inst(Class... paramType){ 74 | if(RECYCLE_POOL.isEmpty()) return new FunctionType(paramType); 75 | FunctionType res = RECYCLE_POOL.pop(); 76 | res.paramType = paramType; 77 | res.hash = Arrays.hashCode(paramType); 78 | return res; 79 | } 80 | 81 | public static FunctionType inst(Method method){ 82 | return inst(method.getParameterTypes()); 83 | } 84 | 85 | public static FunctionType inst(Object... param){ 86 | return inst(unwrapped(toTypes(param))); 87 | } 88 | 89 | public static Class[] toTypes(Object... objects){ 90 | Class[] types = new Class[objects.length]; 91 | 92 | for(int i = 0; i < types.length; i++){ 93 | types[i] = objects[i] == null? void.class: objects[i].getClass(); 94 | } 95 | 96 | return types; 97 | } 98 | 99 | public static Class[] toTypes(List objects){ 100 | Class[] types = new Class[objects.size()]; 101 | 102 | for(int i = 0; i < types.length; i++){ 103 | types[i] = objects.get(i) == null? void.class: objects.get(i).getClass(); 104 | } 105 | 106 | return types; 107 | } 108 | 109 | public static FunctionType inst(FunctionType type){ 110 | return inst(type.paramType); 111 | } 112 | 113 | public static FunctionType from(MethodType type){ 114 | return inst(type.parameterArray()); 115 | } 116 | 117 | public static FunctionType from(Executable method){ 118 | return inst(method.getParameterTypes()); 119 | } 120 | 121 | public static FunctionType generic(int argCount){ 122 | Class[] argTypes = new Class[argCount]; 123 | Arrays.fill(argTypes, void.class); 124 | return inst(argTypes); 125 | } 126 | 127 | public static String signature(Method method){ 128 | return method.getName() + FunctionType.from(method); 129 | } 130 | 131 | public static String signature(String name, Class... argTypes){ 132 | return name + FunctionType.inst(argTypes); 133 | } 134 | 135 | public static String signature(String name, FunctionType type){ 136 | return name + type; 137 | } 138 | 139 | public static int typeNameHash(Class[] types){ 140 | return Arrays.hashCode(Arrays.stream(types).map(Class::getName).toArray()); 141 | } 142 | 143 | public boolean match(Object... args){ 144 | return match(unwrapped(toTypes(args))); 145 | } 146 | 147 | public boolean match(Class... argsType){ 148 | if(argsType.length != paramType.length) return false; 149 | 150 | for(int i = 0; i < paramType.length; i++){ 151 | if(argsType[i] == void.class) continue; 152 | if(!paramType[i].isAssignableFrom(argsType[i])) return false; 153 | } 154 | 155 | return true; 156 | } 157 | 158 | public Class[] getTypes(){ 159 | return paramType; 160 | } 161 | 162 | public void recycle(){ 163 | if(RECYCLE_POOL.size() >= MAX_RECYCLE) return; 164 | 165 | paramType = EMPTY; 166 | hash = -1; 167 | RECYCLE_POOL.push(this); 168 | } 169 | 170 | @Override 171 | public boolean equals(Object o){ 172 | if(this == o) return true; 173 | if(!(o instanceof FunctionType that)) return false; 174 | return paramType.length == that.paramType.length && hashCode() == o.hashCode(); 175 | } 176 | 177 | @Override 178 | public int hashCode(){ 179 | return hash; 180 | } 181 | 182 | @Override 183 | public String toString(){ 184 | StringBuilder b = new StringBuilder("("); 185 | 186 | for(Class clazz: paramType){ 187 | b.append(ClassInfo.asType(clazz).realName()); 188 | } 189 | b.append(")"); 190 | 191 | return b.toString(); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/IFunctionEntry.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | public interface IFunctionEntry{ 4 | default String signature(){ 5 | return FunctionType.signature(getName(), getType()); 6 | } 7 | 8 | /**获取入口的名称*/ 9 | String getName(); 10 | 11 | /**获取此方法入口定义的引用匿名函数*/ 12 | Function getFunction(); 13 | 14 | /**获取此方法的形式参数表类型*/ 15 | FunctionType getType(); 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/IVariable.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | public interface IVariable{ 4 | String name(); 5 | 6 | void init(DynamicObject object); 7 | T get(DynamicObject obj); 8 | 9 | void set(DynamicObject obj, Object value); 10 | 11 | //primitive getters 12 | boolean get(DynamicObject obj, boolean def); 13 | byte get(DynamicObject obj, byte def); 14 | short get(DynamicObject obj, short def); 15 | int get(DynamicObject obj, int def); 16 | long get(DynamicObject obj, long def); 17 | float get(DynamicObject obj, float def); 18 | double get(DynamicObject obj, double def); 19 | 20 | //primitive setters 21 | void set(DynamicObject obj, boolean value); 22 | void set(DynamicObject obj, byte value); 23 | void set(DynamicObject obj, short value); 24 | void set(DynamicObject obj, int value); 25 | void set(DynamicObject obj, long value); 26 | void set(DynamicObject obj, float value); 27 | void set(DynamicObject obj, double value); 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/IllegalHandleException.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | public class IllegalHandleException extends RuntimeException{ 4 | public IllegalHandleException(){ 5 | } 6 | 7 | public IllegalHandleException(String info){ 8 | super(info); 9 | } 10 | 11 | public IllegalHandleException(Throwable e){ 12 | super(e); 13 | } 14 | 15 | public IllegalHandleException(String info, Throwable caused){ 16 | super(info, caused); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/Initializer.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | public class Initializer{ 4 | private final Producer init; 5 | 6 | public Initializer(Producer init){ 7 | this.init = init; 8 | } 9 | 10 | public Object getInit(){ 11 | return init.get(); 12 | } 13 | 14 | public interface Producer{ 15 | T get(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/JavaHandleHelper.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | import java.lang.reflect.Field; 4 | import java.lang.reflect.Method; 5 | 6 | /**出于jdk16+进一步升级的模块化管理以及跨平台和跨版本考虑,提供java基本行为的平台支持器,这包含了一些与版本/平台有关的行为接口。 7 | *

实施时应当按方法的功能说明针对运行平台进行实现。 8 | * 9 | * @author EBwilson */ 10 | public interface JavaHandleHelper{ 11 | void makeAccess(Object object); 12 | 13 | IVariable genJavaVariableRef(Field field); 14 | 15 | IFunctionEntry genJavaMethodRef(Method method); 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/PackageAccHandler.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | import dynamilize.classmaker.ClassInfo; 4 | import dynamilize.classmaker.CodeBlock; 5 | import dynamilize.classmaker.Parameter; 6 | import dynamilize.classmaker.code.IClass; 7 | import dynamilize.classmaker.code.ILocal; 8 | import dynamilize.classmaker.code.IMethod; 9 | 10 | import java.lang.reflect.Constructor; 11 | import java.lang.reflect.Method; 12 | import java.lang.reflect.Modifier; 13 | import java.util.Arrays; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | /**包私有方法访问提升使用的工具类,该类用于对一个目标类创建一串用于提升包私有方法的继承子树,以达到对包私有方法进行重写的目的 18 | *
具体来说,将目标基类传入{@link PackageAccHandler#handle(Class)}方法以后,会依次检查此类型的,每一个父类型中是否存在包私有的方法, 19 | * 若存在包私有方法,那么就会使用这个类的包名对上一层类型继承一次,通过{@link PackageAccHandler#loadClass(ClassInfo, Class)}将这个类加载到同一保护域, 20 | * 此时,此次继承类型即可访问目标类包私有方法,重写并将其访问修饰符提升为{@code protected},如此完成对基类所有超类的遍历后即可访问所有该类层次结构中的包私有方法 21 | * 22 | *

直观的说,这个类做的事情如下: 23 | *

{@code
 24 |  * 假设有下面几个类:
 25 |  *
 26 |  * package pac1;
 27 |  * public class A{
 28 |  *   void method1(){ //包私有方法
 29 |  *
 30 |  *   }
 31 |  *
 32 |  *   public void test(){
 33 |  *     method1();
 34 |  *   }
 35 |  * }
 36 |  *
 37 |  * package pac2;
 38 |  * import pac1.A;
 39 |  * public class B extends A{
 40 |  *   void method2(){ //包私有方法
 41 |  *
 42 |  *   }
 43 |  *
 44 |  *   @Override
 45 |  *   public void test(){
 46 |  *     super.test();
 47 |  *     method2();
 48 |  *   }
 49 |  * }
 50 |  *
 51 |  * 如果对handle方法传入B类,那么会创建如下两个类型:
 52 |  *
 53 |  * package pac2:
 54 |  * public class B$packageAccess$0 extends B{
 55 |  *   @Override
 56 |  *   protected void method2(){
 57 |  *     super.method2();
 58 |  *   }
 59 |  * }
 60 |  *
 61 |  * package pac1:
 62 |  * import pac2.B$packageAccess$0;
 63 |  * public class A$packageAccess$0 extends B$packageAccess$0{
 64 |  *   @Override
 65 |  *   protected void method1(){
 66 |  *     super.method2();
 67 |  *   }
 68 |  * }
 69 |  *
 70 |  * 最后handle方法会将类对象pac1.A$packageAccess$0返回,此时,所有包私有方法都已被提升为protected,其子类对两个包私有方法的重写,在调用test方法时都会生效
 71 |  * }
72 | * 注意:
    73 | *
  • 以上所示的跨包继承同包类的方法重写实际上在java编译器中是不合法的,但在JVM当中此逻辑有效,上述代码仅作逻辑示意
  • 74 | *
  • 由于java包命名空间限制的问题,无法正常从外部加载java开他包名的类型,所以开放包私有方法对包名以“java.”开头的类不生效
  • 75 | *
76 | * 77 | * @author EBwilson 78 | * @since 1.6*/ 79 | @SuppressWarnings("unchecked") 80 | public abstract class PackageAccHandler { 81 | public static final int PAC_PRI_FLAGS = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL; 82 | public static final ILocal[] A = new ILocal[0]; 83 | private final Map, Class> classMap = new HashMap<>(); 84 | 85 | public Class handle(Class baseClass) { 86 | return (Class) classMap.computeIfAbsent(baseClass, c -> { 87 | Class curr = c; 88 | Class opening = null; 89 | 90 | while (curr != null) { 91 | if (shouldOpen(curr)) { 92 | ClassInfo ci = makeClassInfo(opening == null ? ClassInfo.asType(curr) : ClassInfo.asType(opening), curr); 93 | opening = loadClass(ci, curr); 94 | } 95 | 96 | curr = curr.getSuperclass(); 97 | } 98 | 99 | return opening == null? c: opening; 100 | }); 101 | } 102 | 103 | protected boolean shouldOpen(Class checking){ 104 | if (checking.getPackage().getName().startsWith("java.")) return false; 105 | 106 | for (Method method : checking.getDeclaredMethods()) { 107 | if ((method.getModifiers() & PAC_PRI_FLAGS) == 0){ 108 | return true; 109 | } 110 | } 111 | return false; 112 | } 113 | 114 | @SuppressWarnings("rawtypes") 115 | protected ClassInfo makeClassInfo(ClassInfo superClass, Class base){ 116 | ClassInfo res = new ClassInfo<>( 117 | Modifier.PUBLIC, 118 | base.getName() + "$packageAccess$" + superClass.name().hashCode(), 119 | superClass 120 | ); 121 | 122 | ClassInfo baseC = ClassInfo.asType(base); 123 | 124 | for (Method method : base.getDeclaredMethods()) { 125 | if ((method.getModifiers() & PAC_PRI_FLAGS) == 0){ 126 | IMethod sup = baseC.getMethod( 127 | ClassInfo.asType(method.getReturnType()), 128 | method.getName(), 129 | Arrays.stream(method.getParameterTypes()).map(ClassInfo::asType).toArray(ClassInfo[]::new) 130 | ); 131 | 132 | CodeBlock code = res.declareMethod( 133 | Modifier.PROTECTED, 134 | method.getName(), 135 | ClassInfo.asType(method.getReturnType()), 136 | Parameter.asParameter(method.getParameters()) 137 | ); 138 | 139 | ILocal self = code.getThis(); 140 | if (method.getReturnType() == void.class){ 141 | code.invokeSuper( 142 | self, 143 | sup, 144 | null, 145 | code.getParamList().toArray(A) 146 | ); 147 | } 148 | else{ 149 | ILocal ret = code.local(ClassInfo.asType(method.getReturnType())); 150 | 151 | code.invokeSuper( 152 | self, 153 | sup, 154 | ret, 155 | code.getParamList().toArray(A) 156 | ); 157 | code.returnValue(ret); 158 | } 159 | } 160 | } 161 | 162 | for (Constructor constructor : superClass.getTypeClass().getDeclaredConstructors()) { 163 | if ((constructor.getModifiers() & PAC_PRI_FLAGS) == 0 && !(base.getPackage().equals(superClass.getTypeClass().getPackage()))) continue; 164 | if ((constructor.getModifiers() & (Modifier.PRIVATE | Modifier.FINAL)) != 0) continue; 165 | 166 | IMethod su = superClass.getConstructor(Arrays.stream(constructor.getParameterTypes()).map(ClassInfo::asType).toArray(IClass[]::new)); 167 | 168 | CodeBlock code = res.declareConstructor(Modifier.PUBLIC, Parameter.asParameter(constructor.getParameters())); 169 | code.invoke(code.getThis(), su, null, code.getParamList().toArray(A)); 170 | } 171 | 172 | return (ClassInfo) res; 173 | } 174 | 175 | /**将目标{@linkplain ClassInfo 类型信息}加载为与基类型同一保护域的对象,根据目标运行平台实现该方法*/ 176 | protected abstract Class loadClass(ClassInfo clazz, Class baseClass); 177 | } 178 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/ProxyMaker.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.Arrays; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.WeakHashMap; 8 | 9 | /**代理创建工具,用于生成类似{@linkplain java.lang.reflect.Proxy java代理工具}的面向切面代理实例,但不同的是这允许从类型进行委托,类似于cglib。 10 | *

通过此工具创建的代理实例会将所有可用(非static/final/private/package private)方法调用转入代理调用处理器,在此工具中被声明为了{@link ProxyMaker#invoke(DynamicObject, FuncMarker, FuncMarker, ArgumentList)}])}。 11 | *

使用此工具时需要给出一个{@link DynamicMaker}用于构建动态代理实例,尽管可能你需要的只是具备代理行为的{@linkplain DynamicClass 动态类型}。 12 | *

另外,动态类型中声明的函数也会被代理拦截,作用时机与动态类实例化的行为影响是一致的,请参阅{@link DynamicClass 动态类型函数变更的作用时机} 13 | *

你可以使用lambda表达式引用{@link ProxyMaker#getDefault(DynamicMaker, ProxyHandler)}获取默认的lambda实现, 14 | * 或者实现此类的抽象方法{@link ProxyMaker#invoke(DynamicObject, FuncMarker, FuncMarker, ArgumentList)}。 15 | *

{@code
 16 |  * 一个简单的用例:
 17 |  * ArrayList list = ProxyMaker.getDefault(DynamicMaker.getDefault(), (self, method, args) -> {
 18 |  *   System.out.println(method);
 19 |  *   return method.invoke(self, args);
 20 |  * }).newProxyInstance(ArrayList.class).self();
 21 |  *
 22 |  * 这个list的所有可用方法在执行时都将会打印方法本身的签名
 23 |  * }
24 | * 25 | * @since 1.2 26 | * @author EBwilson */ 27 | public abstract class ProxyMaker{ 28 | private static final Map, DynamicClass>> proxyMap = new WeakHashMap<>(); 29 | private static final Map, DynamicClass> nonSuperProxy = new HashMap<>(); 30 | public static final Class[] EMPTY_CLASSES = new Class[0]; 31 | public static final Object[] EMPTY_ARGS = new Object[0]; 32 | 33 | protected final DynamicMaker maker; 34 | 35 | protected ProxyMaker(DynamicMaker maker){ 36 | this.maker = maker; 37 | } 38 | 39 | /**获取代理生成器的默认实现实例,需要提供一个{@linkplain DynamicMaker 动态生成器}以构建实例 40 | * 41 | * @param maker 创建动态对象使用的生成器 42 | * @param proxyHandler 用于代理处理的{@link ProxyMaker#invoke(DynamicObject, FuncMarker, FuncMarker, ArgumentList)}方法拦截,所有有效方法调用都会被拦截并传入此方法 43 | * 44 | * @return 默认实例*/ 45 | public static ProxyMaker getDefault(DynamicMaker maker, ProxyHandler proxyHandler){ 46 | return new ProxyMaker(maker){ 47 | @Override 48 | public Object invoke(DynamicObject proxy, FuncMarker method, FuncMarker proxiedMethod, ArgumentList args){ 49 | return proxyHandler.invoke(proxy, method, proxiedMethod, args); 50 | } 51 | }; 52 | } 53 | 54 | public DynamicObject newProxyInstance(Class[] interfaces){ 55 | return newProxyInstance(interfaces, null); 56 | } 57 | 58 | public DynamicObject newProxyInstance(Class base, Object... args){ 59 | return newProxyInstance(base, EMPTY_CLASSES, null, null, args); 60 | } 61 | 62 | public DynamicObject newProxyInstance(Class base, DynamicClass dyClass, Object... args){ 63 | return newProxyInstance(base, EMPTY_CLASSES, null, dyClass, args); 64 | } 65 | 66 | public DynamicObject newProxyInstance(Class[] interfaces, Class[] aspects){ 67 | return newProxyInstance(Object.class, interfaces, aspects, EMPTY_ARGS); 68 | } 69 | 70 | public DynamicObject newProxyInstance(Class base, Class[] aspects, Object... args){ 71 | return newProxyInstance(base, EMPTY_CLASSES, aspects, null, args); 72 | } 73 | 74 | public DynamicObject newProxyInstance(Class base, Class[] aspects, DynamicClass superDyClass, Object... args){ 75 | return newProxyInstance(base, EMPTY_CLASSES, aspects, superDyClass, args); 76 | } 77 | 78 | public DynamicObject newProxyInstance(Class base, Class[] interfaces, Class[] aspects, Object... args){ 79 | return newProxyInstance(base, interfaces, aspects, null, args); 80 | } 81 | 82 | /** 83 | * 创建一个代理实例,它的所有有效方法都已被提供的代理处理器拦截 84 | * 85 | * @param base 代理委托的java基类,代理实例可以正确的分配给此类型 86 | * @param interfaces 代理实现的接口列表,所有方法均会实现以调用代理处理函数 87 | * @param dynamicClass 代理实例的动态类型,可以为空 88 | * @param args 代理委托的类型中可用的构造函数的参数 89 | * @return 一个代理实例 90 | */ 91 | public DynamicObject newProxyInstance(Class base, Class[] interfaces, Class[] aspects, DynamicClass dynamicClass, Object... args){ 92 | DynamicClass dyClass = getProxyDyClass(dynamicClass, base, interfaces, aspects); 93 | 94 | return maker.newInstance(base, interfaces, aspects, dyClass, args); 95 | } 96 | 97 | /**从类和接口实现获取声明为代理的动态类型,参数给出的动态类型会作为该类型的直接超类,可以为空 98 | * 99 | * @param dynamicClass 结果动态类型的直接超类,为空时表示类型超类不明确 100 | * @param base 委托基类 101 | * @param interfaces 代理实现的接口 102 | * 103 | * @return 声明为代理实现的动态类型*/ 104 | private DynamicClass getProxyDyClass(DynamicClass dynamicClass, Class base, Class[] interfaces, Class[] aspects){ 105 | ClassImplements impl = new ClassImplements<>(base, interfaces, aspects); 106 | DynamicClass dyc = dynamicClass == null? nonSuperProxy.get(impl): proxyMap.computeIfAbsent(dynamicClass, e -> new HashMap<>()).get(impl); 107 | 108 | if(dyc == null){ 109 | dyc = dynamicClass == null? DynamicClass.get("defProxy$" + impl): DynamicClass.declare(dynamicClass.getName() + "$proxy$" + impl, dynamicClass); 110 | 111 | Class dyBase = maker.getDynamicBase(base, interfaces, aspects); 112 | for(Method method: dyBase.getDeclaredMethods()){ 113 | String methodName = method.getName(); 114 | if(method.getAnnotation(DynamicMaker.DynamicMethod.class) != null){ 115 | FunctionType type = FunctionType.from(method); 116 | FuncMarker caller = new FuncMarker() { 117 | @Override 118 | public String getName() { return methodName; } 119 | @Override 120 | public FunctionType getType() { return type; } 121 | @Override 122 | public Object invoke(DynamicObject self, ArgumentList args) { return self.invokeFunc(type, methodName, args.args()); } 123 | }; 124 | 125 | dyc.setFunction( 126 | methodName, 127 | (s, su, a) -> { 128 | FunctionMarker superCaller = FunctionMarker.make(su.getFunc(methodName, type)); 129 | try{ 130 | Object res = invoke(s, caller, superCaller, a); 131 | superCaller.recycle(); 132 | return res; 133 | }catch(Throwable e){ 134 | superCaller.recycle(); 135 | throwException(e); 136 | return null; 137 | } 138 | }, 139 | method.getParameterTypes() 140 | ); 141 | } 142 | } 143 | 144 | (dynamicClass == null? nonSuperProxy: proxyMap.get(dynamicClass)).put(impl, dyc); 145 | } 146 | 147 | return dyc; 148 | } 149 | 150 | /**代理处理器,所有被代理的方法执行被拦截都会转入该方法,方法/函数都会以一个匿名函数的形式传递给这个方法 151 | *

默认实现调用会传入给出的匿名函数,否则子类应当按需要的代理处理方式实现此方法 152 | * 153 | * @param proxy 动态代理实例 154 | * @param method 被拦截的方法 155 | * @param args 实参列表 156 | * 157 | * @return 返回值*/ 158 | protected abstract Object invoke(DynamicObject proxy, FuncMarker method, FuncMarker proxiedMethod, ArgumentList args); 159 | 160 | /**异常处理器,当代理运行中发生任何异常都会转入此方法进行处理,默认直接封装为RuntimeException抛出 161 | * 162 | * @param thr 运行过程中捕获的异常*/ 163 | public void throwException(Throwable thr){ 164 | throw new RuntimeException(thr); 165 | } 166 | 167 | public static class FunctionMarker implements FuncMarker{ 168 | public static int maxPoolSize = 4096; 169 | 170 | private static FunctionMarker[] pool = new FunctionMarker[128]; 171 | private static int cursor = -1; 172 | 173 | private String name; 174 | private FunctionType type; 175 | private IFunctionEntry entry; 176 | private Function function; 177 | 178 | private static FunctionMarker make(IFunctionEntry functionEntry){ 179 | FunctionMarker res = cursor < 0? new FunctionMarker(): pool[cursor]; 180 | 181 | res.name = functionEntry.getName(); 182 | res.type = functionEntry.getType(); 183 | res.entry = functionEntry; 184 | res.function = functionEntry.getFunction(); 185 | 186 | return res; 187 | } 188 | 189 | @Override 190 | public String getName(){ 191 | return entry.getName(); 192 | } 193 | 194 | @Override 195 | public FunctionType getType(){ 196 | return entry.getType(); 197 | } 198 | 199 | @Override 200 | @SuppressWarnings({"unchecked", "rawtypes"}) 201 | public Object invoke(DynamicObject self, ArgumentList args){ 202 | return function.invoke((DynamicObject) self, args); 203 | } 204 | 205 | @Override 206 | public String toString(){ 207 | return "function: " + name + type; 208 | } 209 | 210 | private void recycle(){ 211 | name = null; 212 | type = null; 213 | function = null; 214 | entry = null; 215 | 216 | if (cursor >= maxPoolSize) return; 217 | 218 | cursor++; 219 | if (cursor >= pool.length){ 220 | pool = Arrays.copyOf(pool, pool.length*2); 221 | } 222 | 223 | pool[cursor] = this; 224 | } 225 | } 226 | 227 | /**调用封装器,提供了一个方法{@link FuncMarker#invoke(DynamicObject, ArgumentList)}方法来直接调用这个对象封装的方法或者函数*/ 228 | public interface FuncMarker { 229 | default String signature(){ 230 | return FunctionType.signature(getName(), getType()); 231 | } 232 | 233 | String getName(); 234 | 235 | FunctionType getType(); 236 | 237 | Object invoke(DynamicObject self, ArgumentList args); 238 | 239 | default Object invoke(DynamicObject self, Object... args){ 240 | ArgumentList lis = ArgumentList.as(args); 241 | Object r = invoke(self, lis); 242 | lis.type().recycle(); 243 | lis.recycle(); 244 | return r; 245 | } 246 | 247 | default Object invoke(DynamicObject self, FunctionType type, Object... args){ 248 | ArgumentList lis = ArgumentList.asWithType(type, args); 249 | Object r = invoke(self, lis); 250 | lis.recycle(); 251 | return r; 252 | } 253 | } 254 | 255 | public interface ProxyHandler{ 256 | Object invoke(DynamicObject proxy, FuncMarker func, FuncMarker superFunction, ArgumentList args); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/Variable.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | public class Variable implements IVariable{ 4 | private final String name; 5 | private final Initializer init; 6 | 7 | public Variable(String name){ 8 | this.name = name; 9 | this.init = null; 10 | } 11 | 12 | public Variable(String name, Initializer init){ 13 | this.name = name; 14 | this.init = init; 15 | } 16 | 17 | @Override 18 | public String name(){ 19 | return name; 20 | } 21 | 22 | @Override 23 | public void init(DynamicObject object) { 24 | Object value = init == null? null: init.getInit(); 25 | if (value == null) return; 26 | if (value.getClass().isPrimitive()){ 27 | if (value instanceof Byte b) set(object, b.byteValue()); 28 | else if (value instanceof Short b) set(object, b.shortValue()); 29 | else if (value instanceof Integer b) set(object, b.intValue()); 30 | else if (value instanceof Character b) set(object, b.charValue()); 31 | else if (value instanceof Long b) set(object, b.longValue()); 32 | else if (value instanceof Float b) set(object, b.floatValue()); 33 | else if (value instanceof Double b) set(object, b.doubleValue()); 34 | else if (value instanceof Boolean b) set(object, b.booleanValue()); 35 | else throw new IllegalHandleException("how do you cased this?"); 36 | } 37 | else set(object, value); 38 | } 39 | 40 | @Override 41 | public T get(DynamicObject obj){ 42 | return obj.varValueGet(name); 43 | } 44 | 45 | @Override 46 | public void set(DynamicObject obj, Object value){ 47 | obj.varValueSet(name, value); 48 | } 49 | 50 | @Override 51 | public boolean get(DynamicObject obj, boolean def) { 52 | Object b = get(obj); 53 | boolean res = def; 54 | if (b instanceof BooleanRef ref){ 55 | res = ref.value; 56 | } 57 | else if (b != null) 58 | throw new IllegalHandleException("variable " + name + " in object " + obj + " was not a boolean"); 59 | 60 | return res; 61 | } 62 | 63 | @Override 64 | public byte get(DynamicObject obj, byte def) { 65 | Object b = get(obj); 66 | byte res = def; 67 | if (b instanceof ByteRef ref){ 68 | res = ref.value; 69 | } 70 | else if (b != null) 71 | throw new IllegalHandleException("variable " + name + " in object " + obj + " was not a byte"); 72 | 73 | return res; 74 | } 75 | 76 | @Override 77 | public short get(DynamicObject obj, short def) { 78 | Object b = get(obj); 79 | short res = def; 80 | if (b instanceof ShortRef ref){ 81 | res = ref.value; 82 | } 83 | else if (b != null) 84 | throw new IllegalHandleException("variable " + name + " in object " + obj + " was not a short"); 85 | 86 | return res; 87 | } 88 | 89 | @Override 90 | public int get(DynamicObject obj, int def) { 91 | Object b = get(obj); 92 | int res = def; 93 | if (b instanceof IntRef ref){ 94 | res = ref.value; 95 | } 96 | else if (b != null) 97 | throw new IllegalHandleException("variable " + name + " in object " + obj + " was not a integer"); 98 | 99 | return res; 100 | } 101 | 102 | @Override 103 | public long get(DynamicObject obj, long def) { 104 | Object b = get(obj); 105 | long res = def; 106 | if (b instanceof LongRef ref){ 107 | res = ref.value; 108 | } 109 | else if (b != null) 110 | throw new IllegalHandleException("variable " + name + " in object " + obj + " was not a long"); 111 | 112 | return res; 113 | } 114 | 115 | @Override 116 | public float get(DynamicObject obj, float def) { 117 | Object b = get(obj); 118 | float res = def; 119 | if (b instanceof FloatRef ref){ 120 | res = ref.value; 121 | } 122 | else if (b != null) 123 | throw new IllegalHandleException("variable " + name + " in object " + obj + " was not a float"); 124 | 125 | return res; 126 | } 127 | 128 | @Override 129 | public double get(DynamicObject obj, double def) { 130 | Object b = get(obj); 131 | double res = def; 132 | if (b instanceof DoubleRef ref){ 133 | res = ref.value; 134 | } 135 | else if (b != null) 136 | throw new IllegalHandleException("variable " + name + " in object " + obj + " was not a double"); 137 | 138 | return res; 139 | } 140 | 141 | @Override 142 | public void set(DynamicObject obj, boolean value) { 143 | if (get(obj) instanceof BooleanRef ref){ 144 | ref.value = value; 145 | } 146 | else{ 147 | BooleanRef ref = new BooleanRef(); 148 | ref.value = value; 149 | set(obj, ref); 150 | } 151 | } 152 | 153 | @Override 154 | public void set(DynamicObject obj, byte value) { 155 | if (get(obj) instanceof ByteRef ref){ 156 | ref.value = value; 157 | } 158 | else{ 159 | ByteRef ref = new ByteRef(); 160 | ref.value = value; 161 | set(obj, ref); 162 | } 163 | } 164 | 165 | @Override 166 | public void set(DynamicObject obj, short value) { 167 | if (get(obj) instanceof ShortRef ref){ 168 | ref.value = value; 169 | } 170 | else{ 171 | ShortRef ref = new ShortRef(); 172 | ref.value = value; 173 | set(obj, ref); 174 | } 175 | } 176 | 177 | @Override 178 | public void set(DynamicObject obj, int value) { 179 | if (get(obj) instanceof IntRef ref){ 180 | ref.value = value; 181 | } 182 | else{ 183 | IntRef ref = new IntRef(); 184 | ref.value = value; 185 | set(obj, ref); 186 | } 187 | } 188 | 189 | @Override 190 | public void set(DynamicObject obj, long value) { 191 | if (get(obj) instanceof LongRef ref){ 192 | ref.value = value; 193 | } 194 | else{ 195 | LongRef ref = new LongRef(); 196 | ref.value = value; 197 | set(obj, ref); 198 | } 199 | } 200 | 201 | @Override 202 | public void set(DynamicObject obj, float value) { 203 | if (get(obj) instanceof FloatRef ref){ 204 | ref.value = value; 205 | } 206 | else{ 207 | FloatRef ref = new FloatRef(); 208 | ref.value = value; 209 | set(obj, ref); 210 | } 211 | } 212 | 213 | @Override 214 | public void set(DynamicObject obj, double value) { 215 | if (get(obj) instanceof DoubleRef ref){ 216 | ref.value = value; 217 | } 218 | else{ 219 | DoubleRef ref = new DoubleRef(); 220 | ref.value = value; 221 | set(obj, ref); 222 | } 223 | } 224 | 225 | private static class BooleanRef{ boolean value; } 226 | private static class ByteRef{ byte value; } 227 | private static class ShortRef{ Short value; } 228 | private static class IntRef{ int value; } 229 | private static class LongRef{ long value; } 230 | private static class FloatRef{ float value; } 231 | private static class DoubleRef{ double value; } 232 | } 233 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/WrappedObject.java: -------------------------------------------------------------------------------- 1 | package dynamilize; 2 | 3 | import java.lang.reflect.Field; 4 | import java.lang.reflect.Method; 5 | 6 | /**用于对对象进行动态访问的包装对象,仅具有受限的动态行为(可视为一个反射包装器)。 7 | *

8 | * 对于一个包装动态对象,仅能动态的访问其原有的属性和方法,以下行为将抛出异常: 9 | *

    10 | *
  • 尝试访问在被包装对象的层次结构中没有定义的成员(字段,方法)
  • 11 | *
  • 尝试修改包装动态对象的动态类型
  • 12 | *
  • 将包装动态对象尝试转换成被包装的对象类型
  • 13 | *
*/ 14 | public class WrappedObject implements DynamicObject{ 15 | private static final DynamicClass $wrappedDef$ = new DynamicClass("$wrappedDef$", null){ 16 | @Override public void setVariable(String name, Initializer.Producer prov) 17 | { throw new IllegalHandleException("wrapped object class is immutable"); } 18 | 19 | @Override 20 | public void setFunction(String name, Function func, Class... argTypes) 21 | { throw new IllegalHandleException("wrapped object class is immutable"); } 22 | 23 | @Override 24 | public void visitClass(Class template, JavaHandleHelper helper) 25 | { throw new IllegalHandleException("wrapped object class is immutable"); } 26 | 27 | @Override 28 | public void visitField(Field field) 29 | { throw new IllegalHandleException("wrapped object class is immutable"); } 30 | 31 | @Override 32 | public void visitMethod(Method method, JavaHandleHelper helper) 33 | { throw new IllegalHandleException("wrapped object class is immutable"); } 34 | }; 35 | 36 | private final DataPool pool; 37 | private final T obj; 38 | 39 | WrappedObject(T obj, DataPool pool) { 40 | this.pool = pool; 41 | this.obj = obj; 42 | } 43 | 44 | @SuppressWarnings("unchecked") 45 | @Override 46 | public T objSelf() { 47 | return obj; 48 | } 49 | 50 | @Override 51 | public DynamicClass getDyClass() { 52 | return $wrappedDef$; 53 | } 54 | 55 | @Override 56 | public IVariable getVariable(String name) { 57 | return pool.getVariable(name); 58 | } 59 | 60 | @Override 61 | public DataPool.ReadOnlyPool baseSuperPointer() { 62 | return null; 63 | } 64 | 65 | @Override 66 | public void setVariable(IVariable variable) { 67 | throw new IllegalHandleException("wrapped object cannot add new variable"); 68 | } 69 | 70 | @Override 71 | public T1 varValueGet(String name) { 72 | throw new IllegalHandleException("unsupported operation"); 73 | } 74 | 75 | @Override 76 | public void varValueSet(String name, T1 value) { 77 | throw new IllegalHandleException("unsupported operation"); 78 | } 79 | 80 | @Override 81 | public IFunctionEntry getFunc(String name, FunctionType type) { 82 | return pool.select(name, type); 83 | } 84 | 85 | @Override 86 | public void setFunc(String name, Function func, Class... argTypes) { 87 | throw new IllegalHandleException("wrapped object cannot add new function"); 88 | } 89 | 90 | @Override 91 | public void setFunc(String name, Function.SuperGetFunction func, Class... argTypes) { 92 | throw new IllegalHandleException("wrapped object cannot add new function"); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/AbstractClassGenerator.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker; 2 | 3 | import dynamilize.classmaker.code.*; 4 | 5 | import java.util.Map; 6 | 7 | public abstract class AbstractClassGenerator implements ElementVisitor{ 8 | protected IClass currGenerating; 9 | protected IField currField; 10 | protected IMethod currMethod; 11 | protected ICodeBlock currCodeBlock; 12 | 13 | protected Map> localMap; 14 | 15 | @Override 16 | public void visitClass(IClass clazz){ 17 | currGenerating = clazz; 18 | for(Element element: clazz.elements()){ 19 | element.accept(this); 20 | } 21 | } 22 | 23 | @Override 24 | public void visitCodeBlock(ICodeBlock block){ 25 | currCodeBlock = block; 26 | for(Element element: block.codes()){ 27 | element.accept(this); 28 | } 29 | } 30 | 31 | @Override 32 | public void visitField(IField field){ 33 | currField = field; 34 | } 35 | 36 | @Override 37 | public void visitMethod(IMethod method){ 38 | currMethod = method; 39 | if(method.block() != null) method.block().accept(this); 40 | } 41 | 42 | @Override 43 | public void visitLocal(ILocal local){ 44 | localMap.put(local.name(), local); 45 | } 46 | 47 | public abstract byte[] genByteCode(ClassInfo classInfo); 48 | 49 | protected abstract Class generateClass(ClassInfo classInfo) throws ClassNotFoundException; 50 | } 51 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/AnnotatedMember.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker; 2 | 3 | import dynamilize.IllegalHandleException; 4 | import dynamilize.classmaker.code.IClass; 5 | import dynamilize.classmaker.code.annotation.AnnotatedElement; 6 | import dynamilize.classmaker.code.annotation.IAnnotation; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.lang.annotation.ElementType; 10 | import java.lang.annotation.Repeatable; 11 | import java.lang.annotation.Target; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | public abstract class AnnotatedMember implements AnnotatedElement{ 16 | private final String name; 17 | private int modifiers; 18 | 19 | private List> annotationsList; 20 | 21 | public AnnotatedMember(String name){ 22 | this.name = name; 23 | } 24 | 25 | public String name(){ 26 | return name; 27 | } 28 | 29 | public int modifiers(){ 30 | return modifiers; 31 | } 32 | 33 | public void setModifiers(int modifiers){ 34 | this.modifiers = modifiers; 35 | } 36 | 37 | @Override 38 | public List> getAnnotations(){ 39 | return annotationsList == null? new ArrayList<>(): annotationsList; 40 | } 41 | 42 | @Override 43 | public boolean hasAnnotation(IClass annoType){ 44 | return getAnnotation(annoType) != null; 45 | } 46 | 47 | @Override 48 | @SuppressWarnings("unchecked") 49 | public IAnnotation getAnnotation(IClass annoType){ 50 | if(annotationsList == null) return null; 51 | 52 | for(IAnnotation annotation: annotationsList){ 53 | if(annoType.equals(annotation.annotationType().typeClass())){ 54 | return (IAnnotation) annotation; 55 | } 56 | } 57 | 58 | return null; 59 | } 60 | 61 | @Override 62 | public void addAnnotation(IAnnotation annotation){ 63 | if(annotationsList == null){ 64 | annotationsList = new ArrayList<>(); 65 | } 66 | 67 | checkType(annotation); 68 | 69 | for(IAnnotation anno: annotationsList){ 70 | if(anno.annotationType().equals(annotation.annotationType())){ 71 | if(!anno.annotationType().typeClass().hasAnnotation(ClassInfo.asType(Repeatable.class))) 72 | throw new IllegalHandleException("cannot add multiple annotations with same name that without repeatable meta annotation"); 73 | } 74 | } 75 | 76 | annotationsList.add(annotation); 77 | } 78 | 79 | protected void checkType(IAnnotation annotation){ 80 | IAnnotation tarMark = annotation.annotationType().typeClass().getAnnotation(ClassInfo.asType(Target.class)); 81 | if(tarMark == null) return; 82 | 83 | for(ElementType type: tarMark.asAnnotation().value()){ 84 | if(isType(type)) return; 85 | } 86 | 87 | throw new IllegalHandleException(annotation + " can not use on " + this); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/AnnotationDef.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker; 2 | 3 | import dynamilize.IllegalHandleException; 4 | import dynamilize.classmaker.code.IClass; 5 | import dynamilize.classmaker.code.annotation.AnnotatedElement; 6 | import dynamilize.classmaker.code.annotation.AnnotationType; 7 | import dynamilize.classmaker.code.annotation.IAnnotation; 8 | 9 | import java.lang.annotation.Annotation; 10 | import java.lang.reflect.Method; 11 | import java.lang.reflect.Modifier; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | public class AnnotationDef implements IAnnotation{ 16 | IClass typeClass; 17 | 18 | final AnnotationType annoType; 19 | Map pairs; 20 | 21 | A anno; 22 | 23 | AnnotatedElement element; 24 | 25 | @SuppressWarnings("unchecked") 26 | public AnnotationDef(A anno){ 27 | typeClass = (IClass) ClassInfo.asType(anno.annotationType()); 28 | annoType = typeClass.asAnnotation(null); 29 | 30 | this.anno = anno; 31 | 32 | try{ 33 | HashMap temp = new HashMap<>(annoType.defaultValues()); 34 | for(Method method: anno.annotationType().getMethods()){ 35 | if (Modifier.isStatic(method.getModifiers()) || method.getParameterCount() > 0) continue; 36 | temp.put(method.getName(), method.invoke(anno)); 37 | } 38 | pairs = new HashMap<>(temp); 39 | }catch(Throwable e){ 40 | throw new IllegalHandleException(e); 41 | } 42 | } 43 | 44 | public AnnotationDef(AnnotationType type, AnnotatedElement element, Map attributes){ 45 | annoType = type; 46 | typeClass = type.typeClass(); 47 | 48 | this.element = element; 49 | 50 | HashMap temp = new HashMap<>(annoType.defaultValues()); 51 | if(attributes != null) temp.putAll(attributes); 52 | pairs = new HashMap<>(temp); 53 | } 54 | 55 | @Override 56 | public AnnotationType annotationType(){ 57 | return annoType; 58 | } 59 | 60 | @Override 61 | public Map pairs(){ 62 | return pairs; 63 | } 64 | 65 | @Override 66 | public A asAnnotation(){ 67 | if(anno == null){ 68 | if(typeClass == null){ 69 | if(!annoType.typeClass().isExistedClass()) 70 | throw new IllegalHandleException("only get annotation object with existed type info"); 71 | 72 | typeClass = annoType.typeClass(); 73 | } 74 | 75 | anno = element.getAnnotation(typeClass.getTypeClass()); 76 | } 77 | 78 | return anno; 79 | } 80 | 81 | @Override 82 | @SuppressWarnings("unchecked") 83 | public T getAttr(String attr){ 84 | return (T) pairs.computeIfAbsent(attr, e -> {throw new IllegalHandleException("no such attribute in annotation" + this);}); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/BaseClassLoader.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker; 2 | 3 | import dynamilize.IllegalHandleException; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public class BaseClassLoader extends ClassLoader implements ByteClassLoader{ 9 | protected final Map bytecodes = new HashMap<>(); 10 | protected final Map> classMap = new HashMap<>(); 11 | 12 | public BaseClassLoader(ClassLoader parent){ 13 | super(parent); 14 | } 15 | 16 | @Override 17 | public void declareClass(String name, byte[] byteCode){ 18 | if(bytecodes.put(name, byteCode) != null) 19 | throw new IllegalHandleException("cannot declare class with same name twice"); 20 | } 21 | 22 | @Override 23 | public Class loadClass(String name, boolean resolve) throws ClassNotFoundException{ 24 | return super.loadClass(name, resolve); 25 | } 26 | 27 | @Override 28 | protected Class findClass(String name) throws ClassNotFoundException{ 29 | try{ 30 | return super.findClass(name); 31 | }catch(ClassNotFoundException e){ 32 | Class result = classMap.computeIfAbsent(name, n -> { 33 | byte[] byteCode = bytecodes.get(n); 34 | if(byteCode == null) return null; 35 | 36 | return defineClass(n, byteCode, 0, byteCode.length); 37 | }); 38 | 39 | if(result == null) 40 | throw new ClassNotFoundException("class " + name + " was not declared and not found a existed."); 41 | 42 | return result; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/ByteClassLoader.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker; 2 | 3 | public interface ByteClassLoader{ 4 | void declareClass(String name, byte[] byteCode); 5 | 6 | Class loadClass(String name, boolean resolve) throws ClassNotFoundException; 7 | } 8 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/DefaultReadVisitor.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker; 2 | 3 | import dynamilize.classmaker.code.*; 4 | 5 | public class DefaultReadVisitor implements ElementVisitor{ 6 | @Override 7 | public void visitClass(IClass clazz){ 8 | for(Element element: clazz.elements()){ 9 | element.accept(this); 10 | } 11 | } 12 | 13 | @Override 14 | public void visitMethod(IMethod method){ 15 | method.block().accept(this); 16 | } 17 | 18 | @Override 19 | public void visitField(IField field){} 20 | 21 | @Override 22 | public void visitLocal(ILocal local){} 23 | 24 | @Override 25 | public void visitInvoke(IInvoke invoke){} 26 | 27 | @Override 28 | public void visitGetField(IGetField getField){} 29 | 30 | @Override 31 | public void visitPutField(IPutField putField){} 32 | 33 | @Override 34 | public void visitLocalSet(ILocalAssign localSet){} 35 | 36 | @Override 37 | public void visitOperate(IOperate operate){} 38 | 39 | @Override 40 | public void visitCast(ICast cast){} 41 | 42 | @Override 43 | public void visitGoto(IGoto iGoto){} 44 | 45 | @Override 46 | public void visitLabel(IMarkLabel label){} 47 | 48 | @Override 49 | public void visitCompare(ICompare compare){} 50 | 51 | @Override 52 | public void visitCodeBlock(ICodeBlock codeBlock){ 53 | for(Element element: codeBlock.codes()){ 54 | element.accept(this); 55 | } 56 | } 57 | 58 | @Override 59 | public void visitReturn(IReturn iReturn){} 60 | 61 | @Override 62 | public void visitInstanceOf(IInstanceOf instanceOf){} 63 | 64 | @Override 65 | public void visitNewInstance(INewInstance newInstance){} 66 | 67 | @Override 68 | public void visitOddOperate(IOddOperate operate){} 69 | 70 | @Override 71 | public void visitConstant(ILoadConstant loadConstant){} 72 | 73 | @Override 74 | public void visitNewArray(INewArray newArray){} 75 | 76 | @Override 77 | public void visitCondition(ICondition condition){} 78 | 79 | @Override 80 | public void visitArrayGet(IArrayGet arrayGet){} 81 | 82 | @Override 83 | public void visitArrayPut(IArrayPut arrayPut){} 84 | 85 | @Override 86 | public void visitSwitch(ISwitch zwitch){} 87 | 88 | @Override 89 | public void visitThrow(IThrow thr){} 90 | } 91 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/ElementVisitor.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker; 2 | 3 | import dynamilize.classmaker.code.*; 4 | 5 | public interface ElementVisitor{ 6 | void visitClass(IClass clazz); 7 | 8 | void visitMethod(IMethod method); 9 | 10 | void visitField(IField field); 11 | 12 | void visitLocal(ILocal local); 13 | 14 | void visitInvoke(IInvoke invoke); 15 | 16 | void visitGetField(IGetField getField); 17 | 18 | void visitPutField(IPutField putField); 19 | 20 | void visitLocalSet(ILocalAssign localSet); 21 | 22 | void visitOperate(IOperate operate); 23 | 24 | void visitCast(ICast cast); 25 | 26 | void visitGoto(IGoto iGoto); 27 | 28 | void visitLabel(IMarkLabel label); 29 | 30 | void visitCompare(ICompare compare); 31 | 32 | void visitCodeBlock(ICodeBlock codeBlock); 33 | 34 | void visitReturn(IReturn iReturn); 35 | 36 | void visitInstanceOf(IInstanceOf instanceOf); 37 | 38 | void visitNewInstance(INewInstance newInstance); 39 | 40 | void visitOddOperate(IOddOperate operate); 41 | 42 | void visitConstant(ILoadConstant loadConstant); 43 | 44 | void visitNewArray(INewArray newArray); 45 | 46 | void visitCondition(ICondition condition); 47 | 48 | void visitArrayGet(IArrayGet arrayGet); 49 | 50 | void visitArrayPut(IArrayPut arrayPut); 51 | 52 | void visitSwitch(ISwitch zwitch); 53 | 54 | void visitThrow(IThrow thr); 55 | } 56 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/FieldInfo.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker; 2 | 3 | import dynamilize.IllegalHandleException; 4 | import dynamilize.classmaker.code.IClass; 5 | import dynamilize.classmaker.code.IField; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.lang.annotation.ElementType; 9 | import java.lang.reflect.Modifier; 10 | 11 | public class FieldInfo extends AnnotatedMember implements IField{ 12 | final IClass owner; 13 | final IClass type; 14 | final Object initial; 15 | boolean initialized; 16 | 17 | public FieldInfo(ClassInfo owner, int modifiers, String name, IClass type, Object initial){ 18 | super(name); 19 | setModifiers(modifiers); 20 | this.owner = owner; 21 | this.type = type; 22 | this.initial = initial; 23 | 24 | if(initial != null && !Modifier.isStatic(modifiers)) 25 | throw new IllegalArgumentException("cannot initial a constant to non-static field"); 26 | 27 | if(initial != null){ 28 | Class initType = initial.getClass(); 29 | 30 | if(!(initial instanceof Number) 31 | && !(initial instanceof Boolean) 32 | && !(initial instanceof String) 33 | && !(initial instanceof Character) 34 | && (!initType.isArray() || (!initType.getComponentType().isPrimitive() && !initType.getComponentType().equals(String.class))) 35 | && !(initial instanceof Enum)){ 36 | throw new IllegalArgumentException("initial must be a primitive, String or array, Enum, if array, it type should be primitive, enum or String"); 37 | } 38 | 39 | } 40 | } 41 | 42 | @Override 43 | public IClass owner(){ 44 | return owner; 45 | } 46 | 47 | @Override 48 | public IClass type(){ 49 | return type; 50 | } 51 | 52 | @Override 53 | public Object initial(){ 54 | return initial; 55 | } 56 | 57 | @Override 58 | public void initAnnotations(){ 59 | if(initialized) return; 60 | 61 | Class clazz = owner().getTypeClass(); 62 | if(clazz == null) 63 | throw new IllegalHandleException("only get annotation object in existed type info"); 64 | 65 | try{ 66 | for(Annotation annotation: clazz.getDeclaredField(name()).getAnnotations()){ 67 | addAnnotation(new AnnotationDef<>(annotation)); 68 | } 69 | }catch(NoSuchFieldException e){ 70 | throw new IllegalHandleException(e); 71 | } 72 | 73 | initialized = true; 74 | } 75 | 76 | @Override 77 | public boolean isType(ElementType type){ 78 | return type == ElementType.FIELD; 79 | } 80 | 81 | @Override 82 | public A getAnnotation(Class annoClass){ 83 | Class clazz = owner().getTypeClass(); 84 | if(clazz == null) 85 | throw new IllegalHandleException("only get annotation object in existed type info"); 86 | 87 | try{ 88 | return clazz.getDeclaredField(name()).getAnnotation(annoClass); 89 | }catch(NoSuchFieldException e){ 90 | throw new IllegalHandleException(e); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/MethodInfo.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker; 2 | 3 | import dynamilize.IllegalHandleException; 4 | import dynamilize.classmaker.code.IClass; 5 | import dynamilize.classmaker.code.IMethod; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.lang.annotation.ElementType; 9 | import java.lang.reflect.Constructor; 10 | import java.lang.reflect.Method; 11 | import java.lang.reflect.Modifier; 12 | import java.util.Arrays; 13 | import java.util.List; 14 | import java.util.Objects; 15 | 16 | public class MethodInfo extends AnnotatedMember implements IMethod{ 17 | private final CodeBlock block; 18 | 19 | IClass owner; 20 | 21 | IClass returnType; 22 | List> parameter; 23 | List> throwsList; 24 | 25 | boolean initialized; 26 | 27 | public MethodInfo(IClass owner, int modifiers, String name, IClass returnType, IClass[] throwsList, Parameter... params){ 28 | super(name); 29 | IClass thr = ClassInfo.asType(Throwable.class); 30 | for (IClass iClass : throwsList) { 31 | if (!thr.isAssignableFrom(iClass)) 32 | throw new IllegalHandleException("throws classes must be extend by java.lang.Throwable, but find " + iClass); 33 | } 34 | 35 | setModifiers(modifiers); 36 | this.block = (modifiers & Modifier.ABSTRACT) != 0? null: new CodeBlock<>(this); 37 | this.owner = owner; 38 | this.returnType = returnType; 39 | this.throwsList = Arrays.asList(throwsList); 40 | this.parameter = Arrays.asList(params); 41 | 42 | Arrays.stream(params).forEach(e -> e.setOwner(this)); 43 | 44 | if(block != null) block.initParams(owner, parameter); 45 | } 46 | 47 | @Override 48 | public List> parameters(){ 49 | return parameter; 50 | } 51 | 52 | @Override 53 | public List> throwTypes() { 54 | return throwsList; 55 | } 56 | 57 | @Override 58 | public IClass owner(){ 59 | return owner; 60 | } 61 | 62 | @Override 63 | public String typeDescription(){ 64 | StringBuilder builder = new StringBuilder(); 65 | builder.append("("); 66 | for(IClass arg: parameter.stream().map(Parameter::getType).toArray(IClass[]::new)){ 67 | builder.append(arg.realName()); 68 | } 69 | 70 | return builder + ")" + returnType.realName(); 71 | } 72 | 73 | @Override 74 | public IClass returnType(){ 75 | return returnType; 76 | } 77 | 78 | @Override 79 | public CodeBlock block(){ 80 | return block; 81 | } 82 | 83 | @Override 84 | public void initAnnotations(){ 85 | if(initialized || name().equals("")) return; 86 | 87 | Class clazz = owner().getTypeClass(); 88 | if(clazz == null) 89 | throw new IllegalHandleException("only get annotation object in existed type info"); 90 | 91 | try{ 92 | if(name().equals("")){ 93 | Constructor cstr = clazz.getDeclaredConstructor(parameters().stream().map(e -> e.getType().getTypeClass()).toArray(Class[]::new)); 94 | for(Annotation annotation: cstr.getAnnotations()){ 95 | addAnnotation(new AnnotationDef<>(annotation)); 96 | } 97 | } 98 | else{ 99 | Method met = clazz.getDeclaredMethod(name(), parameters().stream().map(e -> e.getType().getTypeClass()).toArray(Class[]::new)); 100 | for(Annotation annotation: met.getAnnotations()){ 101 | addAnnotation(new AnnotationDef<>(annotation)); 102 | } 103 | } 104 | }catch(NoSuchMethodException e){ 105 | throw new RuntimeException(e); 106 | } 107 | 108 | initialized = true; 109 | } 110 | 111 | @Override 112 | public boolean isType(ElementType type){ 113 | return type == (Objects.equals(name(), "") ? ElementType.CONSTRUCTOR: ElementType.METHOD); 114 | } 115 | 116 | @Override 117 | public A getAnnotation(Class annoClass){ 118 | Class clazz = owner().getTypeClass(); 119 | if(clazz == null) 120 | throw new IllegalHandleException("only get annotation object in existed type info"); 121 | 122 | try{ 123 | return clazz.getDeclaredMethod(name(), parameters().stream().map(e -> e.getType().getTypeClass()).toArray(Class[]::new)).getAnnotation(annoClass); 124 | }catch(NoSuchMethodException e){ 125 | throw new IllegalHandleException(e); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/Parameter.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker; 2 | 3 | import dynamilize.IllegalHandleException; 4 | import dynamilize.classmaker.code.IClass; 5 | import dynamilize.classmaker.code.IMethod; 6 | import dynamilize.classmaker.code.annotation.AnnotatedElement; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.lang.annotation.ElementType; 10 | import java.lang.reflect.Method; 11 | 12 | public class Parameter extends AnnotatedMember implements AnnotatedElement{ 13 | final IClass type; 14 | IMethod method; 15 | 16 | boolean initialized; 17 | 18 | public void setOwner(IMethod method){ 19 | this.method = method; 20 | } 21 | 22 | public IMethod owner(){ 23 | return method; 24 | } 25 | 26 | @SuppressWarnings({"unchecked", "rawtypes"}) 27 | public static Parameter[] as(Object... infos){ 28 | Parameter[] res = new Parameter[infos.length/3]; 29 | for(int i = 0; i < infos.length; i += 3){ 30 | res[i/3] = new Parameter<>( 31 | ((Number) infos[i]).intValue(), 32 | infos[i + 1] instanceof IClass? (IClass) infos[i + 1] : ClassInfo.asType((Class) infos[i + 1]), 33 | (String) infos[i + 2] 34 | ); 35 | } 36 | 37 | return res; 38 | } 39 | 40 | public static Parameter[] trans(IClass... types){ 41 | Parameter[] res = new Parameter[types.length]; 42 | for(int i = 0; i < res.length; i++){ 43 | res[i] = new Parameter<>(0, types[i], "$param$" + i); 44 | } 45 | 46 | return res; 47 | } 48 | 49 | public static Parameter[] asParameter(java.lang.reflect.Parameter... params){ 50 | Parameter[] res = new Parameter[params.length]; 51 | for(int i = 0; i < res.length; i++){ 52 | res[i] = new Parameter<>(params[i].getModifiers(), ClassInfo.asType(params[i].getType()), params[i].getName()); 53 | } 54 | 55 | return res; 56 | } 57 | 58 | public Parameter(int modifiers, IClass type, String name){ 59 | super(name); 60 | setModifiers(modifiers); 61 | this.type = type; 62 | } 63 | 64 | public IClass getType(){ 65 | return type; 66 | } 67 | 68 | @Override 69 | public void initAnnotations(){ 70 | if(initialized) return; 71 | 72 | Class clazz = method.owner().getTypeClass(); 73 | if(clazz == null) 74 | throw new IllegalHandleException("only get annotation object in existed type info"); 75 | 76 | try{ 77 | Method met = clazz.getDeclaredMethod(name(), method.parameters().stream().map(e -> e.getType().getTypeClass()).toArray(Class[]::new)); 78 | 79 | java.lang.reflect.Parameter[] p = met.getParameters(); 80 | for(int i = 0; i < p.length; i++){ 81 | for(Annotation annotation: p[i].getAnnotations()){ 82 | method.parameters().get(i).addAnnotation(new AnnotationDef<>(annotation)); 83 | } 84 | } 85 | }catch(NoSuchMethodException e){ 86 | throw new RuntimeException(e); 87 | } 88 | 89 | initialized = true; 90 | } 91 | 92 | @Override 93 | public boolean isType(ElementType type){ 94 | return type == ElementType.PARAMETER; 95 | } 96 | 97 | @Override 98 | public A getAnnotation(Class annoClass){ 99 | Class clazz = method.owner().getTypeClass(); 100 | if(clazz == null) 101 | throw new IllegalHandleException("only get annotation object in existed type info"); 102 | 103 | try{ 104 | for(java.lang.reflect.Parameter parameter: clazz.getDeclaredMethod(method.name(), method.parameters().stream().map(e -> e.getType().getTypeClass()).toArray(Class[]::new)).getParameters()){ 105 | if(parameter.getName().equals(name())) return parameter.getAnnotation(annoClass); 106 | } 107 | }catch(NoSuchMethodException e){ 108 | throw new IllegalHandleException(e); 109 | } 110 | 111 | return null; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/code/Element.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker.code; 2 | 3 | import dynamilize.classmaker.ElementVisitor; 4 | 5 | public interface Element{ 6 | void accept(ElementVisitor visitor); 7 | 8 | ElementKind kind(); 9 | } 10 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/code/ElementKind.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker.code; 2 | 3 | public enum ElementKind{ 4 | CLASS, 5 | FIELD, 6 | METHOD, 7 | BLOCK, 8 | LOCAL, 9 | INVOKE, 10 | GOTO, 11 | LOCALASSIGN, 12 | FIELDGET, 13 | FIELDSET, 14 | ARRAYGET, 15 | ARRAYPUT, 16 | OPERATE, 17 | ODDOPERATE, 18 | CONDITION, 19 | RETURN, 20 | NEWINSTANCE, 21 | NEWARRAY, 22 | LOADCONSTANT, 23 | CAST, 24 | INSTANCEOF, 25 | MARKLABEL, 26 | SWITCH, 27 | THROW, 28 | COMPARE 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/code/IArrayGet.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker.code; 2 | 3 | import dynamilize.classmaker.ElementVisitor; 4 | 5 | public interface IArrayGet extends Element{ 6 | @Override 7 | default void accept(ElementVisitor visitor){ 8 | visitor.visitArrayGet(this); 9 | } 10 | 11 | @Override 12 | default ElementKind kind(){ 13 | return ElementKind.ARRAYGET; 14 | } 15 | 16 | ILocal array(); 17 | 18 | ILocal index(); 19 | 20 | ILocal getTo(); 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/code/IArrayPut.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker.code; 2 | 3 | import dynamilize.classmaker.ElementVisitor; 4 | 5 | public interface IArrayPut extends Element{ 6 | @Override 7 | default void accept(ElementVisitor visitor){ 8 | visitor.visitArrayPut(this); 9 | } 10 | 11 | @Override 12 | default ElementKind kind(){ 13 | return ElementKind.ARRAYPUT; 14 | } 15 | 16 | ILocal array(); 17 | 18 | ILocal index(); 19 | 20 | ILocal value(); 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/code/ICast.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker.code; 2 | 3 | import dynamilize.classmaker.ElementVisitor; 4 | 5 | public interface ICast extends Element{ 6 | @Override 7 | default void accept(ElementVisitor visitor){ 8 | visitor.visitCast(this); 9 | } 10 | 11 | @Override 12 | default ElementKind kind(){ 13 | return ElementKind.CAST; 14 | } 15 | 16 | ILocal source(); 17 | 18 | ILocal target(); 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/code/IClass.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker.code; 2 | 3 | import dynamilize.classmaker.ClassInfo; 4 | import dynamilize.classmaker.ElementVisitor; 5 | import dynamilize.classmaker.code.annotation.AnnotatedElement; 6 | import dynamilize.classmaker.code.annotation.AnnotationType; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | public interface IClass extends Element, AnnotatedElement{ 13 | @Override 14 | default void accept(ElementVisitor visitor){ 15 | visitor.visitClass(this); 16 | } 17 | 18 | @Override 19 | default ElementKind kind(){ 20 | return ElementKind.CLASS; 21 | } 22 | 23 | /**返回此类型标识所标记的java类 24 | * 25 | * @return 此类型标记标记的类*/ 26 | Class getTypeClass(); 27 | 28 | /**此类型标识是否为已有类型标识 29 | * 30 | * @return 若标记的类已被JVM加载*/ 31 | boolean isExistedClass(); 32 | 33 | boolean isPrimitive(); 34 | 35 | IClass asArray(); 36 | 37 | AnnotationType asAnnotation(Map defaultAttributes); 38 | 39 | boolean isAnnotation(); 40 | 41 | boolean isArray(); 42 | 43 | IClass componentType(); 44 | 45 | String name(); 46 | 47 | /**获取此类在字节码中的真实名称,例如:java.lang.Object -> Ljava/lang/Object; 48 | *

特别的,对于基本数据类型: 49 | *

{@code
50 |    * int     -> I
51 |    * float   -> F
52 |    * byte    -> B
53 |    * short   -> S
54 |    * long    -> J
55 |    * double  -> D
56 |    * char    -> C
57 |    * boolean -> Z
58 |    * void    -> V
59 |    * }
60 | * 61 | * @return 类的实际名称*/ 62 | String realName(); 63 | 64 | /**获取此类型的字节码类型标识符,即真实名称省去首位的符号L,例如java.lang.Object -> java/lang/Object 65 | * 66 | * @return 类型的字节标识名称 67 | * @see ClassInfo#realName() */ 68 | String internalName(); 69 | 70 | int modifiers(); 71 | 72 | IClass superClass(); 73 | 74 | List> interfaces(); 75 | 76 | List elements(); 77 | 78 | IField getField(IClass type, String name); 79 | 80 | IMethod getMethod(IClass returnType, String name, IClass... args); 81 | 82 | IMethod getConstructor(IClass... args); 83 | 84 | boolean isAssignableFrom(IClass target); 85 | } 86 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/code/ICodeBlock.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker.code; 2 | 3 | import dynamilize.classmaker.ElementVisitor; 4 | 5 | import java.util.List; 6 | 7 | public interface ICodeBlock extends Element{ 8 | @Override 9 | default void accept(ElementVisitor visitor){ 10 | visitor.visitCodeBlock(this); 11 | } 12 | 13 | @Override 14 | default ElementKind kind(){ 15 | return ElementKind.BLOCK; 16 | } 17 | 18 | IMethod owner(); 19 | 20 | List codes(); 21 | 22 | List> getParamList(); 23 | 24 | List> getParamAll(); 25 | 26 | List
A getAnnotation(Class annoClass); 21 | 22 | void addAnnotation(IAnnotation annotation); 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/code/annotation/AnnotationType.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker.code.annotation; 2 | 3 | import dynamilize.classmaker.ClassInfo; 4 | import dynamilize.classmaker.code.IClass; 5 | 6 | import java.lang.annotation.Annotation; 7 | import java.util.Map; 8 | 9 | public interface AnnotationType{ 10 | static AnnotationType asAnnotationType(Class annoType){ 11 | IClass clazz = ClassInfo.asType(annoType); 12 | return clazz.asAnnotation(null); 13 | } 14 | 15 | IClass typeClass(); 16 | 17 | Map defaultValues(); 18 | 19 | IAnnotation annotateTo(AnnotatedElement element, Map attributes); 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/classmaker/code/annotation/IAnnotation.java: -------------------------------------------------------------------------------- 1 | package dynamilize.classmaker.code.annotation; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.util.Map; 5 | 6 | public interface IAnnotation{ 7 | AnnotationType annotationType(); 8 | 9 | Map pairs(); 10 | 11 | /**仅在已有类型标识上可用*/ 12 | A asAnnotation(); 13 | 14 | T getAttr(String attr); 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/runtimeannos/AspectInterface.java: -------------------------------------------------------------------------------- 1 | package dynamilize.runtimeannos; 2 | 3 | import dynamilize.DynamicClass; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /**此注解用于声明一个接口为切面接口类型,在{@link dynamilize.DynamicMaker}中构造动态实例时传入的切面接口需要携带此注解。 11 | *

当一个接口携带该注解时,它应当满足如下规范: 12 | *

    13 | *
  • 接口中不得有非抽象的默认方法
  • 14 | *
  • 接口所扩展的所有接口必须也是切面接口
  • 15 | *
  • 接口所扩展的切面接口中也不得存在非抽象方法
  • 16 | *
17 | * 18 | * 切面接口扩展的所有切面接口会迭代的将所有方法添加为切面行为,在{@link dynamilize.DynamicMaker#newInstance(Class, Class[], Class[], DynamicClass, Object...)}方法中亦可以传入若干个切面接口, 19 | * 所有的切面接口共同组合为这个动态实例上应用的切面。 20 | * 21 | *

切面中的行为,其作用在于创建动态实例的增强类型时,在对象的方法中定位将被委托的方法,即限定动态化过程会作用到的方法,从一个案例理解: 22 | *

{@code
23 |  * 一个切面接口:
24 |  * @AspectInterface
25 |  * public interface Sample{
26 |  *   void put(Object key, Object value);
27 |  *   Object get(Object key);
28 |  *   Object remove(Object key);
29 |  * }
30 |  *
31 |  * 现在,使用这个切面接口,创建一个动态化的HashMap:
32 |  * DynamicMaker maker = DynamicFactory.getDefault();
33 |  * DynamicObject> map = maker.newInstance(HashMap.class, new Class[]{Sample.class}, DynamicClass.get("Temp"));
34 |  *
35 |  * map.setFunction("put", (self, su, arg) -> {
36 |  *   System.out.println("putting:" + arg.get(0) + "-" + arg.get(1));
37 |  *   su.invokeFunc("put", arg);
38 |  * }, Object.class, Object.class);//put 方法在切面声明中存在,该行为可被监视
39 |  *
40 |  * map.setFunction("clear", (self, su, arg) -> {
41 |  *   System.out.println("clearing");
42 |  *   su.invokeFunc("clear", arg);
43 |  * });//clear 未在切面中声明,此处声明的行为只会向动态对象添加一个名为clear的函数,但不会对clear方法本身进行监视
44 |  * }
45 | * 46 | * 您也可以不提供切面接口,在创建动态实例时,若将切面接口表设置为null,那么此动态对象会是全委托的, 47 | *
即此动态实例会将所有的包括继承来的非private,非final,非static方法启用动态化。 48 | *
注意!全委托会对所有的被处理方法创建增强字节码,这个过程的成本可能十分昂贵, 49 | * 在您可以确定切面范围的情况下,尽可能采用切面限定委托范围,而不是广泛的使用全委托*/ 50 | @Target(ElementType.TYPE) 51 | @Retention(RetentionPolicy.RUNTIME) 52 | public @interface AspectInterface { 53 | } -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/runtimeannos/Exclude.java: -------------------------------------------------------------------------------- 1 | package dynamilize.runtimeannos; 2 | 3 | import dynamilize.JavaHandleHelper; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /**排除标记,用于{@link dynamilize.DynamicClass#visitClass(Class, JavaHandleHelper)} (Class)}访问行为样版时排除样版类中声明的成员,当方法/字段具有此注解,行为样版访问将直接忽略这个方法/字段 11 | * 12 | * @author EBwilson */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target({ElementType.METHOD, ElementType.FIELD}) 15 | public @interface Exclude{ 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/runtimeannos/FuzzyMatch.java: -------------------------------------------------------------------------------- 1 | package dynamilize.runtimeannos; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /**模糊化切面方法匹配器,该注解应当仅用在{@linkplain AspectInterface 且面接口}的方法上, 6 | * 令被注解的切面方法以模糊的搜索规则匹配与本方法同名的其他重载方法 7 | * 8 | *

此注解需要配合规定的对方法的参数注解来描述对一个方法名称的匹配规则,而参数注解请参阅: 9 | *

    10 | *
  • {@link AnyType}
  • 11 | *
  • {@link TypeAssignable}
  • 12 | *
  • {@link Exact}(默认值)
  • 13 | *
14 | * 15 | * 对于不携带此注解的切面方法会采用精确匹配,其匹配器等价于此注解使用默认参数,所有参数都不携带匹配注解,即: 16 | *
{@code
17 |  * void method(String arg1, Object arg2);
18 |  *
19 |  * 等价于:
20 |  *
21 |  * @FuzzyMatch
22 |  * void method(String arg1, Object arg2);
23 |  * }
24 | * 25 | * 如果在切面接口的扩展结构中多次声明了不同的模糊匹配器,那么实际匹配会以最先找到的匹配器为准 26 | * 27 | * 注意:无论您如何设置匹配器参数,所有在切面接口中的方法都永远不可能被排除, 28 | * 因为正常情况下切面接口中定义的方法在任何时候都会被动态对象实现,该模糊匹配器不能被用作自我过滤,仅能用于搜寻匹配的其他超类方法来添加委托。 29 | * 显然,通过模糊搜索匹配的额外切面方法并不能通过相应的且面接口类型进行访问*/ 30 | @Retention(RetentionPolicy.RUNTIME) 31 | @Target(ElementType.METHOD) 32 | public @interface FuzzyMatch { 33 | /**模糊搜索的范围是否包含动态委托进行的基类(包括动态实现的接口列表)*/ 34 | boolean inBaseClass() default true; 35 | /**模糊搜索的范围是否包含动态委托进行的基类扩展的超类(包括所有实现的接口)*/ 36 | boolean inSuperClass() default true; 37 | /**是否仅匹配抽象方法(来自动态实现的接口或者委托基类为抽象类)*/ 38 | boolean abstractOnly() default false; 39 | 40 | /**是否匹配所有范围内名称相同的方法,若此属性为真,则将忽略方法的参数表直接匹配所有同名的重载方法*/ 41 | boolean anySameName() default false; 42 | 43 | /**使该参数位置匹配任何类型的参数 44 | *
属性: {@link AnyType#value()} */ 45 | @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) @interface AnyType{ 46 | /**用于匹配参数名称的正则表达式,默认忽略参数名称仅比对参数类型*/ 47 | String value() default "^.*$"; 48 | } 49 | /**使该参数位置匹配可进行分配的类型,即被匹配的参数类型应当与匹配参数相同或者是其子类 50 | *
属性: {@link TypeAssignable#value()}*/ 51 | @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) @interface TypeAssignable{ 52 | /**用于匹配参数名称的正则表达式,默认忽略参数名称仅比对参数类型*/ 53 | String value() default "^.*$"; 54 | } 55 | /**使该参数位置匹配的参数类型与这个参数必须是完全一致的*/ 56 | @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) @interface Exact{ 57 | Exact INSTANCE = new Exact(){ 58 | @Override 59 | public Class annotationType() { 60 | return Exact.class; 61 | } 62 | }; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/runtimeannos/Super.java: -------------------------------------------------------------------------------- 1 | package dynamilize.runtimeannos; 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 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Super{ 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/dynamilize/runtimeannos/This.java: -------------------------------------------------------------------------------- 1 | package dynamilize.runtimeannos; 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 | /**this指针标记,用于{@link dynamilize.DynamicClass#visitClass(Class,dynamilize.JavaHandleHelper)}访问方法行为样版时,标记方法的第一个参数, 9 | * 被标记的参数在函数调用时,将作为this指针传递对象自身。此参素并不加入函数参数表匹配,请参见{@linkplain dynamilize.DynamicClass#visitClass(Class, dynamilize.JavaHandleHelper) 访问方法样版部分} 10 | *

尽管没有明确规定,但标记为this指针的参数应当是只读的(具备final修饰符) 11 | * 12 | * @author EBwilson */ 13 | @Target(ElementType.PARAMETER) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface This{ 16 | } 17 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EB-wilson/JavaDynamilizer/24cf7903779d42fa218a5e3ad5c27b52fbc38017/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.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MSYS* | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - openjdk17 3 | install: 4 | - chmod 777 gradlew 5 | - ./gradlew publishToMavenLocal -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'JavaDynamilizer' 2 | include 'core' 3 | include 'usage_sample' 4 | include 'baseimpl' 5 | 6 | include 'apt' 7 | include 'annotations' 8 | 9 | -------------------------------------------------------------------------------- /usage_sample/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | repositories { 6 | maven{ url 'https://www.jitpack.io' } 7 | mavenLocal() 8 | mavenCentral() 9 | } 10 | 11 | tasks.withType(JavaCompile){ 12 | sourceCompatibility = 17 13 | targetCompatibility = 17 14 | options.encoding = "UTF-8" 15 | } 16 | 17 | dependencies { 18 | implementation project(":core") 19 | implementation project(":baseimpl") 20 | implementation project(":annotations") 21 | 22 | annotationProcessor 'com.github.EB-wilson.UniverseCore:annotations:1.8.9' 23 | annotationProcessor project(":apt") 24 | } 25 | 26 | test { 27 | useJUnitPlatform() 28 | } -------------------------------------------------------------------------------- /usage_sample/processorLog/ClassDynamilizeProcessor.log: -------------------------------------------------------------------------------- 1 | processor: ClassDynamilizeProcessor (full class name: dynamilize.annoprocessor.ClassDynamilizeProcessor) 2 | time: Fri Aug 11 17:25:00 CST 2023 3 | 4 | ----------------------------------------- 5 | annotation: dynamilize.annotations.DynamilizeClass 6 | ----------------------------------------- 7 | 8 | > class: /home/local/IdeaProjects/JavaDynamilizer/usage_sample/src/main/java/com/github/ebwilson/sample_zh/HintAspect.java 9 | 1| package com.github.ebwilson.sample_zh; 10 | 2| 11 | 3| import dynamilize.JavaHandleHelper; 12 | 4| import dynamilize.annotations.DynamilizeClass; 13 | 5| 14 | 6| @DynamilizeClass 15 | 7| public abstract class HintAspect implements DynamilizeClass.DynamilizedClass { 16 | 8| 17 | 9| public HintAspect() { 18 | 10| super(); 19 | 11| } 20 | 12| @dynamilize.runtimeannos.Exclude 21 | 13| private static final JavaHandleHelper $helper$ = null; 22 | 14| public static Object lastDisplay; 23 | 15| @dynamilize.runtimeannos.Exclude 24 | 16| public static final java.lang.String NAME = "com.github.ebwilson.sample_zh.HintAspect"; 25 | 17| @dynamilize.runtimeannos.Exclude 26 | 18| public static final dynamilize.DynamicClass INSTANCE = dynamilize.DynamicClass.visit("com.github.ebwilson.sample_zh.HintAspect", com.github.ebwilson.sample_zh.HintAspect.class, null, $helper$); 27 | 19| 28 | 20| public static void display(@dynamilize.runtimeannos.This 29 | 21| dynamilize.DynamicObject self, @dynamilize.runtimeannos.Super 30 | 22| dynamilize.DataPool.ReadOnlyPool sup, Object hint) { 31 | 23| sup.invokeFunc("display", hint); 32 | 24| if (hint instanceof String sglHint) { 33 | 25| Object elem = self.getVar("group").hashCode(); 34 | 26| self.setVar("lastDisplay", elem); 35 | 27| } 36 | 28| } 37 | 29| 38 | 30| public static void hide(@dynamilize.runtimeannos.This 39 | 31| dynamilize.DynamicObject self, @dynamilize.runtimeannos.Super 40 | 32| dynamilize.DataPool.ReadOnlyPool sup) { 41 | 33| sup.invokeFunc("hide"); 42 | 34| if (self.getVar("lastDisplay") != null) { 43 | 35| } 44 | 36| } 45 | 37| } 46 | 47 | -------------------------------------------------------------------------------- /usage_sample/src/main/java/com/github/ebwilson/sample/BaseUse.java: -------------------------------------------------------------------------------- 1 | package com.github.ebwilson.sample; 2 | 3 | import dynamilize.*; 4 | import dynamilize.runtimeannos.AspectInterface; 5 | import dynamilize.runtimeannos.Super; 6 | import dynamilize.runtimeannos.This; 7 | 8 | import java.util.Date; 9 | import java.util.HashMap; 10 | 11 | /**Here is a basic usage example of Dynamic Object. Workflow with dynamic objects is given below to demonstrate the basic usage of dynamic objects and dynamic types 12 | *
Please read this usage first and get to know the basic usage of dynamic objects and dynamic types before reading the application usage*/ 13 | public class BaseUse { 14 | @SuppressWarnings("unchecked") 15 | public static void main(String[] args) { 16 | //Gets the default dynamic factory. 17 | //For custom dynamic factory implementation, please refer to Advanced Application 18 | DynamicMaker maker = DynamicFactory.getDefault(); 19 | 20 | //Create a dynamic type, which is necessary, regardless of whether the dynamic class has function. 21 | //The getter returns the dynamic type with the given name. A new dynamic type will be created if not existing 22 | DynamicClass Sample = DynamicClass.get("Sample"); 23 | 24 | /*==================================================================== 25 | * The following is the basic usage of Dynamic Object. 26 | * This paragraph shows the behavior of the most basic dynamic objects 27 | * ==================================================================*/ 28 | 29 | // Instantiate a dynamic object with a dynamic factory, and provide a argument of aspect interface to limit the scope of the dynamic delegation. 30 | //If the aspect is `null`, then the dynamic instance will delegate all visible methods 31 | HashMap map = maker.newInstance(HashMap.class, new Class[]{Example.class}, Sample).objSelf(); 32 | 33 | //Cast the map to dynamic object to invoke the methods. 34 | //In fact, the type returned by `newInstance` is a Dynamic Object. 35 | //The `objSelf()` method is just a cast of `this`. 36 | //For the generic, the type was not directly presented as A DynamicObject 37 | DynamicObject> dyMap = (DynamicObject>) map; 38 | //Sets the function and monitors the method `put` 39 | dyMap.setFunc("put", (self, su, arg) -> { 40 | su.invokeFunc("put", arg); 41 | //Call the super method, equivalent to super.put(key, value) 42 | 43 | System.out.println("putting " + arg.get(0) + " -> " + arg.get(1)); 44 | }, Object.class, Object.class); 45 | //Generic erasure. The upper bound of the type parameter needs to be used to limit the generic parameter. 46 | //The upper bound of this method is undetermined, that is, Object 47 | 48 | //test print 49 | map.put("a", "string 1"); 50 | map.put("b", "string 2"); 51 | map.put("c", "string 3"); 52 | 53 | System.out.println("\n===========sep line==========\n"); 54 | 55 | //For dynamic objects, hook functions can be used like scripting languages: 56 | Function, Object> hook = dyMap.getFunc("put", Object.class, Object.class); 57 | dyMap.setFunc("put", (self, arg) -> { 58 | hook.invoke(self, arg);//call hook 59 | System.out.println("putting now, current entries amount: " + self.objSelf().size()); 60 | }, Object.class, Object.class); 61 | 62 | //test print 63 | map.put("d", "string 4"); 64 | map.put("e", "string 5"); 65 | map.put("f", "string 6"); 66 | 67 | System.out.println("\n===========sep line==========\n"); 68 | 69 | //You can also invoke methods of dynamic objects with reflection statements: 70 | System.out.println("reflectional invoke:"); 71 | dyMap.invokeFunc("put", "g", "string 7"); 72 | 73 | //Of course, you can also deal with object's field in such a way: 74 | System.out.println("current entries amount: " + dyMap.getVar("size")); 75 | 76 | /*======================================================================= 77 | * The above are the basic operations on dynamic objects 78 | * Here you may be a little confused about the role of dynamic types 79 | * In this paragraph, you will learn the usage and meaning of dynamic type 80 | * =====================================================================*/ 81 | 82 | System.out.println("\n===========sep line==========\n"); 83 | //The dynamic type Sample has been defined above, so use it directly. First declares a function for it: 84 | Sample.setFunction("put", (self, su, arg) -> { 85 | System.out.println("Dynamic class instance handle:"); 86 | su.invokeFunc("put", arg); 87 | //The meaning here are the same as the direct definition of dynamic types 88 | }, Object.class, Object.class); 89 | 90 | //Test printing. Seeing the printing results, you will find that the lowest-level invocation has changed during the invoke 91 | //In fact, apart from some methods that refer to those in Java, the lowest-level function reference refers to those in the dynamic object, where the default function is to call the higher level or do nothing. 92 | //Therefore, when you change the function described in the dynamic type, all the instance of this dynamic type which is still invoke the higher level will function as it changed. 93 | map.put("i", "string 8"); 94 | map.put("j", "string 9"); 95 | map.put("k", "string 10"); 96 | 97 | System.out.println("\n===========sep line==========\n"); 98 | 99 | //Similarly, if a new function is created 100 | Sample.setFunction("putTime", (self, arg) -> { 101 | System.out.println("current time: " + new Date()); 102 | self.invokeFunc("put", arg.get(0), new Date().toString()); 103 | }, String.class); 104 | dyMap.invokeFunc("putTime", "currTime"); 105 | 106 | //Successfully get the current time from the map. So we find that the function we set to the dynamic type will come into its instances. 107 | System.out.println("get time entry, res: " + map.get("currTime")); 108 | 109 | System.out.println("\n===========sep line==========\n"); 110 | 111 | //Of course, it is a little cumbersome to set function with lambda., and there is a more convenient form of defining methods for dynamic types 112 | //In the following, we define a type `Template` as a Type Template. Here we make the template type takes effect 113 | Sample.visitClass(Template.class, maker.getHelper()); 114 | 115 | //Test print 116 | System.out.println("get: " + map.get("a")); 117 | System.out.println("get: " + map.get("b")); 118 | System.out.println("get: " + map.get("c")); 119 | 120 | System.out.println("\n===========sep line==========\n"); 121 | 122 | //In addition, dynamic types can also be extended, as shown below 123 | DynamicClass SampleChild = DynamicClass.declare("SampleChild", Sample); 124 | HashMap map1 = maker.newInstance(HashMap.class, new Class[]{Example.class}, SampleChild).objSelf(); 125 | 126 | SampleChild.setFunction("put", (self, su, arg) -> { 127 | su.invokeFunc("put", arg); 128 | System.out.println("current map: " + self); 129 | }, Object.class, Object.class); 130 | 131 | //Test printing and see the results, the object inherits the function `put` from its superclass. 132 | //And the function defined by this type is executed immediately. 133 | //It is easy to see that the inherit of dynamic type is very similar to the class inherit in Java. 134 | map1.put("abc", "extend sample 1"); 135 | map1.put("def", "extend sample 2"); 136 | map1.put("ghi", "extend sample 3"); 137 | 138 | //At this point, the basic usage instructions on dynamic types are over. Next, some specific applications will be shown in other files in the case. 139 | //Please learn the basic usage methods before reading the subsequent cases. 140 | } 141 | 142 | //The Type Template defined with Java, to make the dynamic object to directly get function from a Java class, which is more clear. 143 | static class Template{ 144 | //The method used to define the function must be `static`. The `this` pointer is provided in by adding the @This annotation to the first parameter, and `super` is the second parameter using the @Super annotation 145 | //The position cannot be swapped, but it can be omitted, such as the `self` here is actually unnecessary 146 | //The parameters after this and super are signed with the name of the method 147 | //The signature of this example is equivalent to public Object get(Object key), which will override the function of map 148 | 149 | //In addition, when using dynamic types, the type of this pointer is indeterminate, so its generic argument is not specified 150 | //But for dynamic classes that will only be used in a legal java class hierarchy, you can specify a exact type. 151 | public static Object get(@This DynamicObject self, @Super DataPool.ReadOnlyPool su, Object key){ 152 | System.out.println("getting entries, key: " + key); 153 | return su.invokeFunc("get", key); 154 | //The type is not exact here, so use reflection to call the method 155 | } 156 | } 157 | 158 | /**Define an aspect interface to limit the dynamic delegation*/ 159 | @AspectInterface 160 | public interface Example{ 161 | void put(Object k, Object v); 162 | Object get(Object k); 163 | } 164 | } -------------------------------------------------------------------------------- /usage_sample/src/main/java/com/github/ebwilson/sample/README.md: -------------------------------------------------------------------------------- 1 | ## Samples 2 | --- 3 | 简体中文请前往[sample_zh](https://github.com/EB-wilson/JavaDynamilizer/tree/master/usage_sample/src/main/java/com/github/ebwilson/sample_zh) 4 | 5 | Here are the usage reference of JavaDynamilizer, including basic features and usage samples, AOP, reflection framework, hot-fix and other examples, and providing the case guide for custom implementation, to understand how to use the framework, how it works and what it means. 6 | 7 | The codes for the general application of JavaDynamilizer are in each package in this directory,It is recommended to start read with [basic usage](https://github.com/EB-wilson/JavaDynamilizer/tree/master/usage_sample/src/main/java/com/github/ebwilson/sample/BaseUse.java), and then read the application usage. 8 | 9 | > Due to my weak ability to make demand assumptions, developers are encouraged to provide more high-quality usage samples 10 | -------------------------------------------------------------------------------- /usage_sample/src/main/java/com/github/ebwilson/sample/aop/AspectUsage.java: -------------------------------------------------------------------------------- 1 | package com.github.ebwilson.sample.aop; 2 | 3 | import dynamilize.*; 4 | import dynamilize.runtimeannos.AspectInterface; 5 | import dynamilize.runtimeannos.FuzzyMatch; 6 | 7 | /** This case shows a better usage for AOP. Please first read the usage and function of {@linkplain dynamilize.DynamicClass Dynamic Object}, so that you will have a better understand of the function in this case. 8 | * 9 | *
As the 3 classes provided in the beginning of the code. Usually, OOP (Object-Oriented Programming) teaches developers to extends several sub-classes. 10 | * But the extend of a class is single-directional. When dealing with the common behavior inherited, we may extend another sub-class over and over again. 11 | * And then we write the same overriding code in the each of the sub-classes, which increases workloads and coupling. 12 | * 13 | *

Classically, the way to solve the problem is to change the logic of the code to decrease the coupling, for example, moving the common behavior to the base-class. But it does not work many time. So AOP (Aspect-Oriented Programming) comes up. 14 | *

It teaches developers to pay attention to the common behavior of multiple types, which is even able not to be extended form the same class. Further, the framework refer to the type as variable

*/ 15 | public class AspectUsage { 16 | /**This is the base class where defined several methods, which will be the common behaviors of the sub-classes*/ 17 | static class ClassA{ 18 | public void update(){} 19 | public void draw(){} 20 | public void showInfo(){ 21 | System.out.println("Now here is `ClassA`"); 22 | } 23 | public void showInfo(String string){ 24 | System.out.println("Here is `ClassA`'s info: " + string); 25 | } 26 | } 27 | 28 | //The following are the sub-classes extended from the ClassA. What do you think we should do to add the following functions to the instance of ClassB and ClassC: 29 | //×-After `update()`, prints how many times it is invoked; 30 | //×-Before and after `draw()`, prints a line "--------------------"; 31 | //×-" Before `showInfo()`, prints a "> ". 32 | 33 | //And what if the sub-classes are more than ClassB and ClassC? 34 | 35 | public static class ClassB extends ClassA { 36 | public void update(){ 37 | System.out.println("updating"); 38 | } 39 | public void draw(){ 40 | System.out.println("- - - - -"); 41 | } 42 | public void showInfo(){ 43 | System.out.println("Now here is `ClassB`"); 44 | } 45 | public void showInfo(String string){ 46 | System.out.println("Here is `ClassB`'s info: " + string); 47 | } 48 | } 49 | 50 | public static class ClassC extends ClassA { 51 | public void reset(){ 52 | System.out.println("reset! (Actually did nothing)"); 53 | } 54 | public void draw(){ 55 | System.out.println("* * * * *"); 56 | } 57 | public void showInfo(){ 58 | System.out.println("Now here is `ClassC`"); 59 | } 60 | public void showInfo(String string){ 61 | System.out.println("Here is `ClassC`'s info: " + string); 62 | } 63 | } 64 | 65 | // Now let's get the default DynamicMaker 66 | static DynamicMaker maker = DynamicFactory.getDefault(); 67 | 68 | /**An aspect interface for the class above.*/ 69 | @AspectInterface 70 | public interface ClassAspect{ 71 | void update(); 72 | void draw(); 73 | /**The method is with {@linkplain FuzzyMatch Fuzzy Match}, where the {@linkplain FuzzyMatch#anySameName() anySameName} parameter means that the method can override any methods with the same name*/ 74 | @FuzzyMatch(anySameName = true) 75 | void showInfo(); 76 | } 77 | 78 | //Let's start 79 | public static void main(String[] args) { 80 | //Creates a dynamic type 81 | DynamicClass SampleAspect = DynamicClass.get("SampleAspect"); 82 | 83 | SampleAspect.setVariable("updateCounter", 0); 84 | //Next, we will describe the functions 85 | SampleAspect.setFunction("update", (s, su, arg) -> { 86 | su.invokeFunc("update", arg); 87 | System.out.println("update invoked counter: " + s.calculateVar("updateCounter", (int i) -> i + 1)); 88 | }); 89 | SampleAspect.setFunction("draw", (s, su, arg) -> { 90 | System.out.println("--------------------"); 91 | su.invokeFunc("draw", arg); 92 | System.out.println("--------------------"); 93 | }); 94 | //Defines a function for two methods with different signatures 95 | Function.NonRetSuperGetFunc fun = (s, su, arg) -> { 96 | System.out.print("> "); 97 | su.invokeFunc("showInfo", arg); 98 | }; 99 | SampleAspect.setFunction("showInfo", fun); 100 | // -> public void showInfo(){...} 101 | SampleAspect.setFunction("showInfo", fun, String.class); 102 | // -> public void showInfo(String string){...} 103 | 104 | //Then, creates two classes' aspect proxy object 105 | DynamicObject instB = maker.newInstance(ClassB.class, new Class[]{ClassAspect.class}, SampleAspect); 106 | DynamicObject instC = maker.newInstance(ClassC.class, new Class[]{ClassAspect.class}, SampleAspect); 107 | 108 | //Test printing 109 | System.out.println("testing instance B:"); 110 | ClassB b = instB.objSelf(); 111 | b.update(); 112 | b.draw(); 113 | b.showInfo(); 114 | b.showInfo("sample test"); 115 | 116 | System.out.println("testing instance C:"); 117 | ClassC c = instC.objSelf(); 118 | c.update(); 119 | c.draw(); 120 | c.showInfo(); 121 | c.showInfo("sample test"); 122 | 123 | c.reset(); 124 | 125 | //Obviously, this greatly reduces the coupling and workload of the code, and you can use less code to complete similar tasks, especially when there are far more sub-classes. 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /usage_sample/src/main/java/com/github/ebwilson/sample/aop/ProxyUsage.java: -------------------------------------------------------------------------------- 1 | package com.github.ebwilson.sample.aop; 2 | 3 | import dynamilize.DynamicClass; 4 | import dynamilize.DynamicFactory; 5 | import dynamilize.DynamicMaker; 6 | import dynamilize.ProxyMaker; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | 11 | /**This case shows usage of proxy-mode AOP, which is similar to `cglib`. If you do not know about it, you can also refer to @link java.lang.reflect.Proxy}. The usage of Proxy in JDer is similar to ones in Reflection, but this does not depend on the common interface*/ 12 | public class ProxyUsage { 13 | public static void main(String[] args) { 14 | //Gets the default DynamicMaker 15 | DynamicMaker maker = DynamicFactory.getDefault(); 16 | 17 | //Creates a ProxyMaker, which is used to create Proxy Instance. When any overridable methods in the proxy instance is invoked, the proxy function defined with the maker will be invoked instead. 18 | ProxyMaker proxyMaker = ProxyMaker.getDefault(maker, (proxy, func, superFunction, arg) -> { 19 | //Message will be printed whenever methods are invoked 20 | System.out.println("invoking " + superFunction + ", args: " + arg); 21 | 22 | //Invokes the `super` method. If not, the invocation will stop here instead of invoking the method od the object 23 | return superFunction.invoke(proxy, arg); 24 | }); 25 | 26 | //Creates a proxy instance 27 | ArrayList list = proxyMaker.newProxyInstance(ArrayList.class).objSelf(); 28 | HashMap map = proxyMaker.newProxyInstance(HashMap.class).objSelf(); 29 | 30 | //Test printing, and then you will find that the message is always be printed before any invocation 31 | list.add("first"); 32 | list.add("second"); 33 | list.add("third"); 34 | 35 | map.put("first", "a1"); 36 | map.put("second", "b2"); 37 | map.put("third", "c3"); 38 | 39 | list.add(map.get(list.get(0))); 40 | 41 | System.out.println("\n===========sep line==========\n"); 42 | 43 | //While the methods below is defined in the interface, but JDer does not require that. 44 | // When invoking the method `toString()`, which is from `Object`, the message is still be printed. 45 | System.out.println(list); 46 | 47 | System.out.println("\n===========sep line==========\n"); 48 | 49 | //In addition, you can provide a dynamic type as its super dynamic class 50 | 51 | //Creates a dynamic type 52 | DynamicClass Sample = DynamicClass.get("Sample"); 53 | Sample.setFunction("add", (self, su, arg) -> { 54 | System.out.println("this is call after than proxy monitor"); 55 | 56 | return su.invokeFunc("add", arg); 57 | }, Object.class); 58 | ArrayList builder = proxyMaker.newProxyInstance(ArrayList.class, Sample).objSelf(); 59 | 60 | //Test printing. The dynamic function is inserted after the proxy monitor. 61 | builder.add("hello "); 62 | builder.add("world"); 63 | System.out.println(builder); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /usage_sample/src/main/java/com/github/ebwilson/sample/dynamic/HotFixUsage.java: -------------------------------------------------------------------------------- 1 | package com.github.ebwilson.sample.dynamic; 2 | 3 | public class HotFixUsage { 4 | } 5 | -------------------------------------------------------------------------------- /usage_sample/src/main/java/com/github/ebwilson/sample/dynamic/ReflectionFrameUsage.java: -------------------------------------------------------------------------------- 1 | package com.github.ebwilson.sample.dynamic; 2 | 3 | public class ReflectionFrameUsage { 4 | } 5 | -------------------------------------------------------------------------------- /usage_sample/src/main/java/com/github/ebwilson/sample_zh/BaseUse.java: -------------------------------------------------------------------------------- 1 | package com.github.ebwilson.sample_zh; 2 | 3 | import dynamilize.*; 4 | import dynamilize.runtimeannos.AspectInterface; 5 | import dynamilize.runtimeannos.Super; 6 | import dynamilize.runtimeannos.This; 7 | 8 | import java.util.Date; 9 | import java.util.HashMap; 10 | 11 | /**这是动态对象的基本使用示例,在下面给出了一个使用动态对象的工作流以向您演示动态对象和动态类型的基本使用方法 12 | *
请首先阅读此案例并掌握动态类型及动态对象的基本使用方法后再阅读应用的案例*/ 13 | public class BaseUse { 14 | @SuppressWarnings("unchecked") 15 | public static void main(String[] args) { 16 | //获取默认动态工厂,关于自定义动态工厂实现请参阅高级应用 17 | DynamicMaker maker = DynamicFactory.getDefault(); 18 | 19 | //创建动态类型,这是必要的,无论动态类是否有行为,get方法会返回给定名称的动态类型,如果类型尚不存在则会创建一个新的动态类型 20 | DynamicClass Sample = DynamicClass.get("Sample"); 21 | 22 | /*=============================== 23 | * 如下所示是动态对象的基本使用方法 24 | * 该段落展示最基本的动态对象的行为操作 25 | * ===============================*/ 26 | 27 | //使用动态工厂来实例化动态对象,传递一个切面接口限定动态委托的范围,若限定切面为null,那么动态实例将对所有可见方法进行动态委托 28 | HashMap map = maker.newInstance(HashMap.class, new Class[]{Example.class}, Sample).objSelf(); 29 | 30 | //强转换为动态对象以调用行为,但实际上maker的newInstance方法创建返回的类型就是DynamicObject,objSelf()方法只是对对象this的一次强类型转换,此处因为泛型转换的问题没有直接使用返回的DynamicObject实例 31 | DynamicObject> dyMap = (DynamicObject>) map; 32 | //设置行为,对put方法进行监视: 33 | dyMap.setFunc("put", (self, su, arg) -> { 34 | su.invokeFunc("put", arg); 35 | //调用超方法,等价于super.put(key, value) 36 | 37 | System.out.println("putting " + arg.get(0) + " -> " + arg.get(1)); 38 | }, Object.class, Object.class); 39 | //泛型擦除,对泛型参数的限定需使用类型参数的上界,此方法上界未定,即Object 40 | 41 | //测试打印 42 | map.put("a", "string 1"); 43 | map.put("b", "string 2"); 44 | map.put("c", "string 3"); 45 | 46 | System.out.println("\n===========sep line==========\n"); 47 | 48 | //对于动态对象,可以像脚本语言一样使用钩子函数: 49 | Function, Object> hook = dyMap.getFunc("put", Object.class, Object.class); 50 | dyMap.setFunc("put", (self, arg) -> { 51 | hook.invoke(self, arg);//调用钩子 52 | System.out.println("putting now, current entries amount: " + self.objSelf().size()); 53 | }, Object.class, Object.class); 54 | 55 | //测试打印 56 | map.put("d", "string 4"); 57 | map.put("e", "string 5"); 58 | map.put("f", "string 6"); 59 | 60 | System.out.println("\n===========sep line==========\n"); 61 | 62 | //您也可以用反射式的语句调用动态对象的方法: 63 | System.out.println("reflectional invoke:"); 64 | dyMap.invokeFunc("put", "g", "string 7"); 65 | 66 | //当然,您也可以以类似的方式去操作对象的字段值: 67 | System.out.println("current entries amount: " + dyMap.getVar("size")); 68 | 69 | /*======================================================= 70 | * 以上是动态对象上的基本操作,到这里您可能对动态类型的作用稍有疑惑 71 | * 您将在这个段落了解到动态类型的用法及意义 72 | * =======================================================*/ 73 | 74 | System.out.println("\n===========sep line==========\n"); 75 | //上面已经定义过一个动态类型Sample,直接使用它,首先给动态类型声明一个函数: 76 | Sample.setFunction("put", (self, su, arg) -> { 77 | System.out.println("Dynamic class instance handle:"); 78 | su.invokeFunc("put", arg); 79 | //这里的语义和对动态类型直接定义是一致的 80 | }, Object.class, Object.class); 81 | 82 | //测试打印,观察打印结果,可以发现调用的过程中最低层次的调用发生了变化 83 | //其实,在动态对象中除部分方法对java方法的引用外,最低层的函数引用是指向动态类型的,只是这一层的默认行为是向上调用或者无行为 84 | //因此,当你改变动态类型中描述的行为时,所有没有中断掉向上的引用的这个动态类型的实例,此方法的行为都会随之改变 85 | map.put("i", "string 8"); 86 | map.put("j", "string 9"); 87 | map.put("k", "string 10"); 88 | 89 | System.out.println("\n===========sep line==========\n"); 90 | 91 | //同样的,如果新增函数 92 | Sample.setFunction("putTime", (self, arg) -> { 93 | System.out.println("current time: " + new Date()); 94 | self.invokeFunc("put", arg.get(0), new Date().toString()); 95 | }, String.class); 96 | dyMap.invokeFunc("putTime", "currTime"); 97 | 98 | //从map中获取currTime,可见给动态类型设置的行为被同步到了它的实例当中 99 | System.out.println("get time entry, res: " + map.get("currTime")); 100 | 101 | System.out.println("\n===========sep line==========\n"); 102 | 103 | //当然,使用lambda为对象设置行为有些繁琐,为动态类型定义方法还有更为方便的形式 104 | //在下方,我们定义了一个类型Template作为类型样板,在此使样板类型生效 105 | Sample.visitClass(Template.class, maker.getHelper()); 106 | 107 | //测试打印 108 | System.out.println("get: " + map.get("a")); 109 | System.out.println("get: " + map.get("b")); 110 | System.out.println("get: " + map.get("c")); 111 | 112 | System.out.println("\n===========sep line==========\n"); 113 | 114 | //另外,动态类型也是可以被继承的,就像如下所示 115 | DynamicClass SampleChild = DynamicClass.declare("SampleChild", Sample); 116 | HashMap map1 = maker.newInstance(HashMap.class, new Class[]{Example.class}, SampleChild).objSelf(); 117 | 118 | SampleChild.setFunction("put", (self, su, arg) -> { 119 | su.invokeFunc("put", arg); 120 | System.out.println("current map: " + self); 121 | }, Object.class, Object.class); 122 | 123 | //测试打印,观察结果,对象从其超类继承了put方法的行为,而此类型定义的行为紧随之后执行,容易看出动态类型继承的行为和java本身的类继承行为非常相似 124 | map1.put("abc", "extend sample 1"); 125 | map1.put("def", "extend sample 2"); 126 | map1.put("ghi", "extend sample 3"); 127 | 128 | //至此,关于动态类型的基本的使用方法说明就结束了,接下来会在案例中的其他文件中展示一些具体的应用,请首先将基本使用方法掌握后再阅读后续的案例 129 | } 130 | 131 | //使用java定义方法的模板,令对象直接从java类型中引用它的方法,使程序语义更清晰 132 | static class Template{ 133 | //用于定义行为的方法必须是静态的,this指针通过第一个参数上添加@This注解传入,super则是第二个参数使用@Super注解,位置不可交换,但可以省略,譬如此处的self其实就是不必要的 134 | //在this和super之后的参数配合方法的名称进行签名,这个例子的签名等价于 public Object get(Object key),会覆写map的行为 135 | 136 | //另外,使用动态类型时,this指针的类型就是不确定的了,所以通常来说不指定它的泛型限定类,但对于只会用于一个合法的java类层次结构的动态类,你可以为它分配一个确切的类型以更方便的使用, 137 | public static Object get(@This DynamicObject self, @Super DataPool.ReadOnlyPool su, Object key){ 138 | System.out.println("getting entries, key: " + key); 139 | return su.invokeFunc("get", key); 140 | //此处类型不确切,所以使用反射调用方法 141 | } 142 | } 143 | 144 | /**定义切面接口,用于限定动态委托的处理范围*/ 145 | @AspectInterface 146 | public interface Example{ 147 | void put(Object k, Object v); 148 | Object get(Object k); 149 | } 150 | } -------------------------------------------------------------------------------- /usage_sample/src/main/java/com/github/ebwilson/sample_zh/README.md: -------------------------------------------------------------------------------- 1 | ## 案例 2 | --- 3 | English sample is [here](https://github.com/EB-wilson/JavaDynamilizer/tree/master/usage_sample/src/main/java/com/github/ebwilson/sample) 4 | 5 | 这里列出了JavaDynamilizer的使用参考样例,包括基本的特性及用法样例,AOP,反射框架,热修复等实例,并提供了自定义实现的案例指南,您可以通过阅读这些样例参考以更快的了解本框架的使用方法及功能和意义。 6 | 7 | 在这个目录内存放的各个案例包内存放了关于JavaDynamilizer的一般应用时的代码,建议首先从[基本使用方法](https://github.com/EB-wilson/JavaDynamilizer/tree/master/usage_sample/src/main/java/com/github/ebwilson/sample_zh/BaseUse.java)开始阅读,在了解了框架的基本使用方法之后再阅读应用。 8 | 9 | > 由于我个人对需求假设的能力较弱,此页面鼓励各位开发者提供更加优质的使用样本 10 | -------------------------------------------------------------------------------- /usage_sample/src/main/java/com/github/ebwilson/sample_zh/aop/AspectUsage.java: -------------------------------------------------------------------------------- 1 | package com.github.ebwilson.sample_zh.aop; 2 | 3 | import dynamilize.*; 4 | import dynamilize.runtimeannos.AspectInterface; 5 | import dynamilize.runtimeannos.FuzzyMatch; 6 | 7 | /**本篇案例演示的是更好的面向切面用法,在您阅读本篇前请务必了解{@linkplain dynamilize.DynamicClass 动态类型}的使用及行为效果,以理解本篇中各行为的效果 8 | * 9 | *
正如本例子开头给出的三个类,多数情况下,面向对象的思想会指导开发者对一个类扩展多个子类进行编写, 10 | * 但是类进行扩展的过程是单向的,当我们需要对这些子类继承来的共有行为进行操作时,我们可能需要一个又一个的类分别扩展一个子类, 11 | * 然后又在子类里写下完全一样的方法重写,这就大大提高了工作量和代码的耦合度。 12 | * 13 | *

一般来说,解决这个问题通常会考虑改变代码的逻辑来减少耦合度,比如提升共同行为至基类,但通常这不是很好的办法,因此我们引入了面向切面编程,即AOP 14 | *

AOP思想指导开发者只关注多个类型的共有行为(这甚至可以不是从一个基类继承而来),而本框架更进一步的将其视为“可变的”,请继续往下阅读

*/ 15 | public class AspectUsage { 16 | /**这是一个基类,它描述了几个方法,关注这些方法,这会是子类当中的共有特征*/ 17 | public static class ClassA{ 18 | public void update(){} 19 | public void draw(){} 20 | public void showInfo(){ 21 | System.out.println("Now here is `ClassA`"); 22 | } 23 | public void showInfo(String string){ 24 | System.out.println("Here is `ClassA`'s info: " + string); 25 | } 26 | } 27 | 28 | //下面则是对ClassA的两个分别扩展的子类,思考一下,假设我们需要让ClassB和ClassC的实例具有如下效果: 29 | //×-在调用update()时,在执行后打印update方法被调用的次数 30 | //×-在调用draw()方法时,在draw的首行前增加一行和末尾追加一行,分别打印"--------------------" 31 | //×-在调用两个showInfo时,分别在之前添加一个"> " 32 | 33 | //另外,当子类远远不只有ClassB和ClassC的情况呢? 34 | 35 | public static class ClassB extends ClassA { 36 | public void update(){ 37 | System.out.println("updating"); 38 | } 39 | public void draw(){ 40 | System.out.println("- - - - -"); 41 | } 42 | public void showInfo(){ 43 | System.out.println("Now here is `ClassB`"); 44 | } 45 | public void showInfo(String string){ 46 | System.out.println("Here is `ClassB`'s info: " + string); 47 | } 48 | } 49 | 50 | public static class ClassC extends ClassA { 51 | public void reset(){ 52 | System.out.println("reset! (Actually did nothing)"); 53 | } 54 | public void draw(){ 55 | System.out.println("* * * * *"); 56 | } 57 | public void showInfo(){ 58 | System.out.println("Now here is `ClassC`"); 59 | } 60 | public void showInfo(String string){ 61 | System.out.println("Here is `ClassC`'s info: " + string); 62 | } 63 | } 64 | 65 | //请继续往下看,现在,创建一个DynamicMaker,此处使用默认工厂 66 | static DynamicMaker maker = DynamicFactory.getDefault(); 67 | 68 | /**现在,针对上述的基类创建一个切面接口*/ 69 | @AspectInterface 70 | public interface ClassAspect{ 71 | void update(); 72 | void draw(); 73 | /**本方法标记为{@linkplain FuzzyMatch 模糊匹配},属性{@linkplain FuzzyMatch#anySameName() anySameName}标记此方法匹配所有同名重载*/ 74 | @FuzzyMatch(anySameName = true) 75 | void showInfo(); 76 | } 77 | 78 | //开始 79 | public static void main(String[] args) { 80 | //创建一个动态类 81 | DynamicClass SampleAspect = DynamicClass.get("SampleAspect"); 82 | 83 | SampleAspect.setVariable("updateCounter", 0); 84 | //下面,我们描述这个切面动态类进行的所有操作 85 | SampleAspect.setFunction("update", (s, su, arg) -> { 86 | su.invokeFunc("update", arg); 87 | System.out.println("update invoked counter: " + s.calculateVar("updateCounter", (int i) -> i + 1)); 88 | }); 89 | SampleAspect.setFunction("draw", (s, su, arg) -> { 90 | System.out.println("--------------------"); 91 | su.invokeFunc("draw", arg); 92 | System.out.println("--------------------"); 93 | }); 94 | //共同行为,采取同一函数同时设置为两个签名的重载方法 95 | Function.NonRetSuperGetFunc fun = (s, su, arg) -> { 96 | System.out.print("> "); 97 | su.invokeFunc("showInfo", arg); 98 | }; 99 | SampleAspect.setFunction("showInfo", fun); 100 | // -> public void showInfo(){...} 101 | SampleAspect.setFunction("showInfo", fun, String.class); 102 | // -> public void showInfo(String string){...} 103 | 104 | //接下来,我们分别创建两个类的切面代理对象: 105 | DynamicObject instB = maker.newInstance(ClassB.class, new Class[]{ClassAspect.class}, SampleAspect); 106 | DynamicObject instC = maker.newInstance(ClassC.class, new Class[]{ClassAspect.class}, SampleAspect); 107 | 108 | //测试打印 109 | System.out.println("testing instance B:"); 110 | ClassB b = instB.objSelf(); 111 | b.update(); 112 | b.draw(); 113 | b.showInfo(); 114 | b.showInfo("sample test"); 115 | 116 | System.out.println("testing instance C:"); 117 | ClassC c = instC.objSelf(); 118 | c.update(); 119 | c.draw(); 120 | c.showInfo(); 121 | c.showInfo("sample test"); 122 | 123 | c.reset(); 124 | 125 | //显然,这大大的降低了代码的耦合度和工作量,你可以使用更少的代码来完成类似的工作,尤其在子类分支远远更多的情况下 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /usage_sample/src/main/java/com/github/ebwilson/sample_zh/aop/ProxyUsage.java: -------------------------------------------------------------------------------- 1 | package com.github.ebwilson.sample_zh.aop; 2 | 3 | import dynamilize.*; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | 9 | /**本篇向您演示该框架进行代理模式AOP的示例,这是一个类似cglib的行为,如果您不了解cglib,那么您可以参考{@link java.lang.reflect.Proxy},此用法与反射的Proxy类似,但是这并不依赖于切面对象的公共接口*/ 10 | public class ProxyUsage { 11 | @SuppressWarnings("unchecked") 12 | public static void main(String[] args) { 13 | //获取默认动态工厂 14 | DynamicMaker maker = DynamicFactory.getDefault(); 15 | 16 | //创建一个代理工厂,此类工厂将用于创建代理实例,当实例的任意可重写方法被调用时,其都会被捕获并转入声明该工厂时给定的代理函数,如下 17 | ProxyMaker proxyMaker = ProxyMaker.getDefault(maker, (proxy, func, superFunc, arg) -> { 18 | //调用任何方法时都将调用信息打印到输出流 19 | System.out.println("invoking " + superFunc + ", args: " + arg); 20 | 21 | //向上调用被拦截的方法,如果不调用的话此方法会被截断使此次调用无效 22 | return superFunc.invoke(proxy, arg); 23 | }); 24 | 25 | //利用proxyMaker创建代理实例,代理实例的行为都会被代理工厂代理委托到代理函数上 26 | ArrayList list = proxyMaker.newProxyInstance(ArrayList.class).objSelf(); 27 | HashMap map = proxyMaker.newProxyInstance(HashMap.class).objSelf(); 28 | 29 | //测试打印,您将会看到下面进行的每一次操作都会被截获并打印在输出流 30 | list.add("first"); 31 | list.add("second"); 32 | list.add("third"); 33 | 34 | map.put("first", "a1"); 35 | map.put("second", "b2"); 36 | map.put("third", "c3"); 37 | 38 | list.add(map.get(list.get(0))); 39 | 40 | System.out.println("\n===========sep line==========\n"); 41 | 42 | //尽管这上面调用的几个方法都被声明在了集合框架的接口之中,但是不同于反射proxy,JDER代理是不需要被监视行为来自接口的,如下: 43 | //这一条语句会调用list的toString()方法,这个方法来自类Object,同样的,它也被监视了 44 | System.out.println(list); 45 | 46 | System.out.println("\n===========sep line==========\n"); 47 | 48 | //另外,对于一个代理委托实例您依然可以在创建它的时候给它分配一个动态超类,动态类的行为则会被增加在此代理的行为之前 49 | 50 | //创建动态类 51 | DynamicClass Sample = DynamicClass.get("Sample"); 52 | Sample.setFunction("add", (self, su, arg) -> { 53 | System.out.println("this is call after than proxy monitor"); 54 | 55 | return su.invokeFunc("add", arg); 56 | }, Object.class); 57 | ArrayList builder = proxyMaker.newProxyInstance(ArrayList.class, Sample).objSelf(); 58 | 59 | //测试打印,观察打印结果,分配给动态对象的动态类行为被插入在监视器之前,代理处理器可以正确截断 60 | builder.add("hello "); 61 | builder.add("world"); 62 | System.out.println(builder); 63 | 64 | System.out.println("\n===========sep line==========\n"); 65 | 66 | //当然,就像代理的实际使用方法一样,我们可以使用一个代理对象来代为处理对被代理对象的行为调用,例如,我们用前文生成的builder作为代理对象: 67 | DynamicObject> dyBuilder = (DynamicObject>) builder; 68 | ProxyMaker proxy = ProxyMaker.getDefault(maker, (self, func, superFunc, arg) -> { 69 | System.out.println("invoke to proxy object"); 70 | return func.invoke(dyBuilder, arg); 71 | });//直接将所有方法调用重定向到dyBuilder 72 | List listProxied = proxy.newProxyInstance(new Class[]{List.class}).objSelf();//创建的被代理的对象不应该超出委托目标的范围,这里使用List接口 73 | 74 | //测试打印 75 | listProxied.add("hello world again"); 76 | System.out.println(builder); 77 | listProxied.clear(); 78 | System.out.println(builder); 79 | 80 | System.out.println("\n===========sep line==========\n"); 81 | 82 | //有时候,会需要对一些非动态化的对象作委托,这种时候需要将被代理的非动态对象进行动态包装,在DynamicMaker当中提供了一个包装方法: 83 | ArrayList delegate = new ArrayList<>(); 84 | DynamicObject> wrapped = maker.wrapInstance(delegate); 85 | 86 | //这个对象与来自newInstance的动态对象不同,这是一个受限制的动态对象,例如它只能用于动态调用被包装对象的已有方法,而不能创建新函数 87 | wrapped.invokeFunc("add", "hello world"); 88 | System.out.println(delegate); 89 | 90 | //并且尝试转换到被包装对象的类型会发生异常: 91 | //ArrayList test = (ArrayList)wrapped;//抛出异常 92 | //应使用: 93 | ArrayList test = wrapped.objSelf(); 94 | 95 | //该包装可被用在类型代理上,当调用需要的被代理对象是一个非动态对象,而又不希望对它动态化,则可将其进行包装: 96 | ProxyMaker wrapProxy = ProxyMaker.getDefault(maker, (self, func, superFunc, arg) -> { 97 | System.out.println("invoke to proxy object, delegate method: " + func.signature()); 98 | return func.invoke(wrapped, arg); 99 | }); 100 | List proxied = wrapProxy.newProxyInstance(new Class[]{List.class}).objSelf(); 101 | 102 | //测试打印 103 | proxied.add("i am a robot"); 104 | System.out.println(delegate); 105 | proxied.clear(); 106 | System.out.println(delegate); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /usage_sample/src/main/java/com/github/ebwilson/sample_zh/dynamic/HotFixUsage.java: -------------------------------------------------------------------------------- 1 | package com.github.ebwilson.sample_zh.dynamic; 2 | 3 | public class HotFixUsage { 4 | } 5 | -------------------------------------------------------------------------------- /usage_sample/src/main/java/com/github/ebwilson/sample_zh/dynamic/ReflectionFrameUsage.java: -------------------------------------------------------------------------------- 1 | package com.github.ebwilson.sample_zh.dynamic; 2 | 3 | public class ReflectionFrameUsage { 4 | } 5 | --------------------------------------------------------------------------------