├── .classpath ├── .gitignore ├── .project ├── README.md ├── build ├── asm-5.2.jar ├── junit-4.6.jar ├── reflectasm_java7.jar └── reflectasm_java8.jar ├── license.txt ├── pom.xml ├── project.yaml ├── src └── com │ └── esotericsoftware │ └── reflectasm │ ├── AccessClassLoader.java │ ├── Accessor.java │ ├── ClassAccess.java │ ├── ClassInfo.java │ ├── ConstructorAccess.java │ ├── FieldAccess.java │ ├── LambdaAccess.java │ ├── MethodAccess.java │ └── util │ ├── NumberUtils.java │ └── Probe.java └── test ├── com └── esotericsoftware │ └── reflectasm │ ├── ClassAccessTest.java │ ├── ClassLoaderTest.java │ ├── ConstructorAccessTest.java │ ├── ConvertionTest.java │ ├── FieldAccessTest.java │ ├── MethodAccessTest.java │ ├── benchmark │ ├── Benchmark.java │ ├── ClassAccessBenchmark.java │ ├── ConstructorAccessBenchmark.java │ ├── FieldAccessBenchmark.java │ └── MethodAccessBenchmark.java │ └── test.java └── test ├── Many.java └── TestObject.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Temporary Files 2 | *~ 3 | .*.swp 4 | .DS_STORE 5 | 6 | bin/ 7 | target/ 8 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | reflectasm 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://raw.github.com/wiki/EsotericSoftware/reflectasm/images/logo.png) 2 | 3 | Please use the [ReflectASM discussion group](http://groups.google.com/group/reflectasm-users) for support. 4 | 5 | ## Note 6 | 7 | This is the revised version, some differences from `EsotericSoftware/reflectasm`: 8 | * Supports `Java 8` only in order to remove the dependency of `asm-xxx.jar` 9 | * New class `ClassAccess` as the base of `MethodAccess`/`FieldAccess`/`ConstructorAccess` 10 | * Provides the interface to dump the dynamic class for investigation purpose 11 | * Provides the caching options for performance purpose(default as enabled) 12 | * Optimize some logics for performance purpose 13 | * Auto data type conversion for invoking or construction 14 | * Accurately position the closest method for overloading 15 | * Supports methods/constructors with variable parameters 16 | * Removes the harcodes of the package name 17 | * Supports accessing non-public class/method/field 18 | * Uses of generic types to reduce unnecessary explicit/implicit casting 19 | * Reduces the generation of proxy classes 20 | * Supports [MethodHandle](https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/MethodHandle.html#asVarargsCollector-java.lang.Class-) 21 | 22 | To support Java 7, just change the imports in class `ClassAccess` to add back the dependency of `asm-xxx.jar` 23 | 24 | ## Overview 25 | 26 | ReflectASM is a very small Java library that provides high performance reflection by using code generation. An access class is generated to set/get fields, call methods, or create a new instance. The access class uses bytecode rather than Java's reflection, so it is much faster. It can also access primitive fields via bytecode to avoid boxing. 27 | 28 | #### Summary of the cost(Smaller value means better performance) 29 | The benchmark code can be found in the `benchmark` directory, test environment: 30 | * CPU: Intel I7-3820QM 31 | * VM : Java 8u112 x86 32 | * OS : Win10 x64, and as a laptop, the result is not very stable
33 | 34 | | VM | Item | Direct | DirectMethodHandle | ReflectASM | Reflection | 35 | | --- | --- | --------- | --------- | --------- | --------- | 36 | | Server VM | Field Set+Get | 1.17 ns | 7.83 ns | 6.88 ns | 12.51 ns | 37 | | Server VM | Method Call | 0.86 ns | 4.14 ns | 3.30 ns | 4.51 ns | 38 | | Server VM | Constructor | 5.10 ns | 7.51 ns | 6.86 ns | 8.62 ns | 39 | | Client VM | Field Set+Get | 2.49 ns | 16.30 ns | 13.75 ns | 209.21 ns | 40 | | Client VM | Method Call | 2.13 ns | 10.54 ns | 8.32 ns | 48.85 ns | 41 | | Client VM | Constructor | 71.00 ns | 192.87 ns | 74.80 ns | 154.43 ns | 42 | 43 | #### Server VM 44 | ![](http://chart.apis.google.com/chart?chtt=&Java 1.8.0_112 x86(Server VM)&chs=700x237&chd=t:104886099,704401675,619611373,1125590088,77170418,372776726,296855382,405673821,458851833,675729992,616966498,775650205&chds=0,1125590088&chxl=0:|Constructor - Reflection|Constructor - ReflectASM|Constructor - DirectMethodHandle|Constructor - Direct|Method Call - Reflection|Method Call - ReflectASM|Method Call - DirectMethodHandle|Method Call - Direct|Field Set+Get - Reflection|Field Set+Get - ReflectASM|Field Set+Get - DirectMethodHandle|Field Set+Get - Direct&cht=bhg&chbh=10&chxt=y&chco=660000|660033|660066|660099|6600CC|6600FF|663300|663333|663366|663399|6633CC|6633FF|666600|666633|666666) 45 | 46 | #### Client VM 47 | ![](http://chart.apis.google.com/chart?chtt=&Java 1.8.0_112 x86(Client VM)&chs=700x237&chd=t:74650984,489015485,412433873,6276273031,63781455,316114537,249572650,1465394076,2130048135,5786071571,2244086000,4632780627&chds=0,6276273031&chxl=0:|Constructor - Reflection|Constructor - ReflectASM|Constructor - DirectMethodHandle|Constructor - Direct|Method Call - Reflection|Method Call - ReflectASM|Method Call - DirectMethodHandle|Method Call - Direct|Field Set+Get - Reflection|Field Set+Get - ReflectASM|Field Set+Get - DirectMethodHandle|Field Set+Get - Direct&cht=bhg&chbh=10&chxt=y&chco=660000|660033|660066|660099|6600CC|6600FF|663300|663333|663366|663399|6633CC|6633FF|666600|666633|666666) 48 | 49 | ## Usage 50 | 51 | Considering this class: 52 | 53 | ```java 54 | public class TestObject { 55 | static String fs; 56 | public Double fd; 57 | public int fi; 58 | private long fl; 59 | public TestObject() {fs = "TestObject0";} 60 | public TestObject(int fi1, Double fd1, String fs1, long l) {} 61 | static String func1(String str) {fs = str;return str;} 62 | public String func2(int fi1, Double fd1, String fs1, long l) {return fs;} 63 | } 64 | 65 | ``` 66 | 67 | Reflection with ReflectASM: 68 | ```java 69 | ClassAccess access = ClassAccess.access(TestObject.class); 70 | TestObject obj; 71 | // Construction 72 | obj = access.newInstance(); 73 | obj = access.newInstance(1, 2, 3, 4); 74 | 75 | // Set+Get field 76 | access.set(null, "fs", 1); // static field 77 | System.out.println((String)access.get(null, "fs")); 78 | 79 | access.set(obj, "fd", 2); 80 | System.out.println((String)access.get(obj, "fd")); 81 | 82 | // Method invoke 83 | access.invoke(null,"func1","a"); //static call 84 | System.out.println((String)access.invoke(obj, "func2",1,2,3,4)); 85 | ``` 86 | 87 |
88 | If same field/method/constructor is referenced frequently, for performance purpose, consider following code: 89 | ```java 90 | ClassAccess access = ClassAccess.access(TestObject.class); 91 | TestObject obj; 92 | //Identify the indexes for further use 93 | int newIndex=access.indexOfConstructor(int.class, Double.class, String.class, long.class); 94 | int fieldIndex=access.indexOfField("fd"); 95 | int methodIndex=access.indexOfMethod("func1",String.class); 96 | //Now use the index to access object in loop or other part 97 | for(int i=0;i<100;i++) { 98 | obj=access.newInstanceWithIndex(newIndex,1,2,3,4); 99 | access.set(obj,fieldIndex,123); 100 | String result=access.invokeWithIndex(null,methodIndex,"x"); 101 | } 102 | ``` 103 | 104 |
105 | With above code, input arguments are auto-converted into the corresponding data types before the call. If auto-conversion is unnecessary in your code and all argument types are correct previously, for more performance purpose, replace as: 106 | ```java 107 | ClassAccess access = ClassAccess.access(TestObject.class); 108 | TestObject obj; 109 | //Identify the indexes for further use 110 | int newIndex=access.indexOfConstructor(int.class, Double.class, String.class, long.class); 111 | int fieldIndex=access.indexOfField("fd"); 112 | int methodIndex=access.indexOfMethod("func1",String.class); 113 | //Now use the index to access object in loop or other part 114 | for(int i=0;i<100;i++) { 115 | obj=access.accessor.newInstanceWithIndex(newIndex,1,Double.valueOf(2),"3",4L); 116 | access.accessor.set(obj,fieldIndex,Double.valueOf(123)); 117 | String result=access.accessor.invokeWithIndex(null,methodIndex,"x"); 118 | } 119 | ``` 120 | Class reflection with ReflectASM(F=FieldAccess,M=MethodAccess,C=ConstructorAccess, {}=Array): 121 | 122 | | ClassAccess.*method* | Equivalence | Description | 123 | | -------------------- | ---------- | ----------- | 124 | | *static* access(Class,[dump dir]) | (F/M/C).access | returns a wrapper object of the underlying class, in case of the 2nd parameter is specified, dumps the dynamic classes into the target folder | 125 | | indexesOf(name,type)| | returns an index array that matches the given name and type(`field`/`method`/``) | 126 | | indexesOf(Class,name,type)| | returns an index array that matches the given class,name and type(`field`/`method`/``) | 127 | | indexOf(name,type)| | returns the first element of `indexesOf`| 128 | | getHandleWithIndex(index,type) | | returns a MethodHandle regards to the index(from `indexOf`), type here can be `set/get/method/` | 129 | | getHandle(Class,name,type,{argtypes}) | | returns a MethodHandle regards to input parameter types | 130 | | getHandleWithArgs(Class,name,type,{args}) | | returns a MethodHandle regards to input parameter values | 131 | | invokeWithMethodHandle(instance,index,type,{args}|| if option `invokeWithMethodHandle` is `true`(default as `false`, then all below methods will use MethodHandle to process, instead of `accessor`| 132 | | indexOfField(name/Field) | F.getIndex | returns the field index that matches the given input| 133 | | indexOfField(Class,name) | | returns the field index that defined in the target class| 134 | | indexOfMethod(name/Method) | M.getIndex | returns the first method index that matches the given input| 135 | | indexOfMethod(Class,name,{argTypes}) | | returns the first method index that defined in the target class| 136 | | indexOfMethod(name,argCount/{argTypes}) | (M/C).getIndex | returns the first index that matches the given input| 137 | | indexOfConstructor(constructor) | C.getIndex | returns the index that matches the given constructor| 138 | | set(instance,Name/index,value) | F.set | Assign value to the specific field | 139 | | set*Primitive*(instance,index,value) | F.set*Primitive* | Assign primitive value to the specific field, *primitive* here can be `Integer/Long/Double/etc` | 140 | | get(instance,name/index) | F.get | Get field value | 141 | | get*Primitive*(instance,index) | F.get*Primitive* | Get field value and convert to target primitive type| 142 | | get(instance,name/index, Class) | F.get | Get field value and convert to target class type| 143 | | invoke(instance,name,{args}) | M.invoke | Executes method | 144 | | invokeWithIndex(instance,index,{args}) | M.invokeWithIndex | Executes method by specifying the exact index| 145 | | invokeWithWithTypes(instance,name/index,{argTypes},{args}) | M.invokeWithWithTypes | Invokes method by specifying parameter types in order to position the accurate method| 146 | | newInstance() | C.newInstance | Create underlying Object | 147 | | newInstance({args}) | C.newInstance | Create underlying Object | 148 | | newInstanceWithIndex(index,{args}) | C.newInstanceWithIndex | Create underlying Object with the specific constructor index| 149 | | newInstanceWithTypes({argTypes},{args}) | C.newInstanceWithTypes | Create underlying Object by specifying parameter types in order to position the accurate constructor| 150 | | getInfo || Get the underlying `ClassInfo`| 151 | | *accessor.*newInstanceWithIndex| | Directly initializes the instance without validating/auto-conversion the input arguments| 152 | | *accessor.*invokeWithIndex| | Directly invokes the method without validating/auto-conversion the input arguments| 153 | | *accessor.*set/get| | Directly set/get the field without validating/auto-conversion the input arguments| 154 | 155 | Other static fields: 156 | * `ClassAccess.ACCESS_CLASS_PREFIX`: the prefix of the dynamic class name(format is `.`), the default value is `asm.` 157 | * `ClassAccess.IS_CACHED`: The option to cache the method/constructor indexes and the dynamic classes, default is `true`. VM parameter `reflectasm.is_cache` can also control this option 158 | * `ClassAccess.IS_STRICT_CONVERT`: controls the auto-conversion of the input arguments for the invoke/set/constructor functions, i.e., auto-conversion `String` into `int` when a method only accepts the `int` parameter. Default is `false`(enabled conversion), and VM parameter `reflectasm.is_strict_convert` can also control this option 159 | 160 | 161 | 162 | ## Visibility 163 | 164 | ReflectASM can always access all members(public + non-public) that defined inside it + supper-classes + interfaces. 165 | 166 | To access a field/method defined in the class's super-class which is also samely defined in the class itself with the same parameters, then position the index firstly and invoke/get/set with it afterwards. For example: 167 | ```java 168 | int fieldIndex=access.indexOfField(,); 169 | access.get(instance,fieldIndex); 170 | access.set(instance,fieldIndex,value); 171 | 172 | int methodIndex=access.indexOfMethod(,,{}); 173 | access.invokeWithIndex(instance,methodIndex,{arguments}); 174 | ``` 175 | 176 | Refer to test case `com.hyee.reflectmeta.testOverload()/testOverloadWithLambda()` for more examples` 177 | 178 | 179 | ## Exceptions 180 | 181 | Stack traces when using ReflectASM are a bit cleaner. Here is Java's reflection calling a method that throws a RuntimeException: 182 | 183 | ``` 184 | Exception in thread "main" java.lang.reflect.InvocationTargetException 185 | at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 186 | at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 187 | at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 188 | at java.lang.reflect.Method.invoke(Method.java:597) 189 | at com.example.SomeCallingCode.doit(SomeCallingCode.java:22) 190 | Caused by: java.lang.RuntimeException 191 | at com.example.SomeClass.someMethod(SomeClass.java:48) 192 | ... 5 more 193 | ``` 194 | 195 | Here is the same but when ReflectASM is used: 196 | 197 | ``` 198 | Exception in thread "main" java.lang.RuntimeException 199 | at com.example.SomeClass.someMethod(SomeClass.java:48) 200 | at com.example.SomeClassMethodAccess.invoke(Unknown Source) 201 | at com.example.SomeCallingCode.doit(SomeCallingCode.java:22) 202 | ``` 203 | 204 | If ReflectASM is used to invoke code that throws a checked exception, the checked exception is thrown. Because it is a compilation error to use try/catch with a checked exception around code that doesn't declare that exception as being thrown, you must catch Exception if you care about catching a checked exception in code you invoke with ReflectASM. 205 | 206 | ## How it works 207 | 208 | Each time to call `ClassAccess.access()`, a wrapper class is created and instanced, or if the wrapper object can be found from the cache, then return from cache directly. This wrapper object is assigned as `ClassAccess.accessor`. 209 | 210 | So any access(get/set field,invoke method,construction) to the target class, the process flow is: 211 | ``` 212 | User Calls 213 | \/ 214 | Is auto-conversion enabled?---Yes---> ClassAccess.reArgs&Call(invoke/get/set/etc) 215 | \ \/ 216 | -----No---> ClassAccess.accessor.callIndex(invoke/get/set/etc) 217 | \/ 218 | TargetInstance.call(invoke/get/set/etc) 219 | \/ 220 | Return result 221 | ``` 222 | The outstanding cost by comparing to direct call is only redirect once in minimum so the performance differences can be ignored(< 10 ns), the most time-consuming part is the target method itself. 223 | 224 | To see how is the logic of the wrapper class, take below class `TestObject` as example: 225 | 226 | ```java 227 | package test; 228 | public class TestObject { 229 | static String fs; 230 | public Double fd; 231 | public int fi; 232 | private long fl; 233 | public TestObject() {fs = "TestObject0";} 234 | public TestObject(int fi1, Double fd1, String fs1, long l) {} 235 | static String func1(String str) {fs = str;return str;} 236 | public String func2(int fi1, Double fd1, String fs1, long l) {return fs;} 237 | } 238 | ``` 239 | 240 | And then the auto-generated wrapper class is shown below, you can also call `ClassAccess.access(TestObject.class,".")` to dump the wrapper class: 241 | ```java 242 | package asm.test; 243 | import com.hyee.reflectmeta.Accessor; 244 | import com.hyee.reflectmeta.ClassAccess; 245 | import com.hyee.reflectmeta.ClassInfo; 246 | import sun.reflect.MagicAccessorImpl; 247 | 248 | public class TestObject extends MagicAccessorImpl implements Accessor { 249 | static final ClassInfo classInfo = new ClassInfo(); 250 | 251 | public TestObject() {} 252 | 253 | static { 254 | classInfo.methodNames = new String[]{"func2", "func1"}; 255 | classInfo.methodParamTypes = new Class[][]{{Integer.TYPE, Double.class, String.class, Long.TYPE}, {String.class}}; 256 | classInfo.returnTypes = new Class[]{String.class, String.class}; 257 | classInfo.methodModifiers = new Integer[]{Integer.valueOf(1), Integer.valueOf(8)}; 258 | classInfo.methodDescs = new String[]{"(ILjava/lang/Double;Ljava/lang/String;J)Ljava/lang/String;", "(Ljava/lang/String;)Ljava/lang/String;"}; 259 | classInfo.fieldNames = new String[]{"fs", "fd", "fi", "fl"}; 260 | classInfo.fieldTypes = new Class[]{String.class, Double.class, Integer.TYPE, Long.TYPE}; 261 | classInfo.fieldModifiers = new Integer[]{Integer.valueOf(8), Integer.valueOf(1), Integer.valueOf(1), Integer.valueOf(2)}; 262 | classInfo.fieldDescs = new String[]{"Ljava/lang/String;", "Ljava/lang/Double;", "I", "J"}; 263 | classInfo.constructorParamTypes = new Class[][]{new Class[0], {Integer.TYPE, Double.class, String.class, Long.TYPE}}; 264 | classInfo.constructorModifiers = new Integer[]{Integer.valueOf(1), Integer.valueOf(1)}; 265 | classInfo.constructorDescs = new String[]{"()V", "(ILjava/lang/Double;Ljava/lang/String;J)V"}; 266 | classInfo.baseClass = test.TestObject.class; 267 | classInfo.isNonStaticMemberClass = false; 268 | classInfo.bucket = 13; 269 | ClassAccess.buildIndex(classInfo); 270 | } 271 | 272 | public ClassInfo getInfo() { 273 | return classInfo; 274 | } 275 | 276 | public T invokeWithIndex(test.TestObject var1, int var2, V... var3) { 277 | switch(var2) { 278 | case 0: 279 | return var1.func2(((Integer)var3[0]).intValue(), (Double)var3[1], (String)var3[2], ((Long)var3[3]).longValue()); 280 | case 1: 281 | return test.TestObject.func1((String)var3[0]); 282 | default: 283 | throw new IllegalArgumentException("Method not found: " + var2); 284 | } 285 | } 286 | 287 | public T get(test.TestObject var1, int var2) { 288 | switch(var2) { 289 | case 0: 290 | return test.TestObject.fs; 291 | case 1: 292 | return var1.fd; 293 | case 2: 294 | return Integer.valueOf(var1.fi); 295 | case 3: 296 | return Long.valueOf(var1.fl); 297 | default: 298 | throw new IllegalArgumentException("Field not found: " + var2); 299 | } 300 | } 301 | 302 | public void set(test.TestObject var1, int var2, V var3) { 303 | switch(var2) { 304 | case 0: 305 | test.TestObject.fs = (String)var3; 306 | return; 307 | case 1: 308 | var1.fd = (Double)var3; 309 | return; 310 | case 2: 311 | var1.fi = ((Integer)var3).intValue(); 312 | return; 313 | case 3: 314 | var1.fl = ((Long)var3).longValue(); 315 | return; 316 | default: 317 | throw new IllegalArgumentException("Field not found: " + var2); 318 | } 319 | } 320 | 321 | public test.TestObject newInstanceWithIndex(int var1, T... var2) { 322 | switch(var1) { 323 | case 0: 324 | return new test.TestObject(); 325 | case 1: 326 | return new test.TestObject(((Integer)var2[0]).intValue(), (Double)var2[1], (String)var2[2], ((Long)var2[3]).longValue()); 327 | default: 328 | throw new IllegalArgumentException("Constructor not found: " + var1); 329 | } 330 | } 331 | 332 | public test.TestObject newInstance() { 333 | return new test.TestObject(); 334 | } 335 | } 336 | ``` -------------------------------------------------------------------------------- /build/asm-5.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyee/ReflectASM/be599134785b5ba4528cf416a157a251ddcad9c0/build/asm-5.2.jar -------------------------------------------------------------------------------- /build/junit-4.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyee/ReflectASM/be599134785b5ba4528cf416a157a251ddcad9c0/build/junit-4.6.jar -------------------------------------------------------------------------------- /build/reflectasm_java7.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyee/ReflectASM/be599134785b5ba4528cf416a157a251ddcad9c0/build/reflectasm_java7.jar -------------------------------------------------------------------------------- /build/reflectasm_java8.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyee/ReflectASM/be599134785b5ba4528cf416a157a251ddcad9c0/build/reflectasm_java8.jar -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008, Nathan Sweet 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | * Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 4.0.0 6 | com.esotericsoftware.reflectasm 7 | reflectasm 8 | 1.09 9 | jar 10 | ReflectASM 11 | High performance Java reflection using code generation 12 | http://code.google.com/p/reflectasm/ 13 | 14 | 15 | 16 | New BSD License 17 | http://www.opensource.org/licenses/bsd-license.php 18 | repo 19 | 20 | 21 | 22 | 23 | http://reflectasm.googlecode.com/svn/ 24 | scm:svn:http://reflectasm.googlecode.com/svn/ 25 | 26 | 27 | 28 | 29 | nathan.sweet 30 | Nathan Sweet 31 | nathan.sweet@gmail.com 32 | 33 | 34 | 35 | 36 | UTF-8 37 | 38 | 39 | 40 | 41 | org.ow2.asm 42 | asm 43 | 4.0 44 | 45 | 46 | junit 47 | junit 48 | 4.8.2 49 | test 50 | 51 | 52 | 53 | 54 | 55 | src 56 | test 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-compiler-plugin 62 | 3.1 63 | 64 | 65 | -XDignore.symbol.file 66 | 1.5 67 | 1.5 68 | 69 | 70 | 71 | 72 | maven-resources-plugin 73 | 2.5 74 | 75 | 76 | default-resources 77 | none 78 | 79 | 80 | default-testResources 81 | none 82 | 83 | 84 | 85 | 86 | org.apache.maven.plugins 87 | maven-jar-plugin 88 | 2.4 89 | 90 | 91 | **/.svn/* 92 | 93 | 94 | 95 | 96 | org.apache.maven.plugins 97 | maven-shade-plugin 98 | 1.7 99 | 100 | 101 | package 102 | 103 | shade 104 | 105 | 106 | 107 | true 108 | true 109 | shaded 110 | 111 | 112 | org.objectweb.asm 113 | com.esotericsoftware.reflectasm.shaded.org.objectweb.asm 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /project.yaml: -------------------------------------------------------------------------------- 1 | version: 1.09 2 | --- 3 | Build.build(project); 4 | Build.oneJAR(project); -------------------------------------------------------------------------------- /src/com/esotericsoftware/reflectasm/AccessClassLoader.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2008, Nathan Sweet 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | * 3. Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | * 11 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | * 13 | */ 14 | 15 | package com.esotericsoftware.reflectasm; 16 | 17 | import java.lang.ref.WeakReference; 18 | import java.lang.reflect.Method; 19 | import java.security.ProtectionDomain; 20 | import java.util.HashSet; 21 | import java.util.WeakHashMap; 22 | 23 | class AccessClassLoader extends ClassLoader { 24 | // Weak-references to class loaders, to avoid perm gen memory leaks, for example in app servers/web containters if the 25 | // reflectasm library (including this class) is loaded outside the deployed applications (WAR/EAR) using ReflectASM/Kryo (exts, 26 | // user classpath, etc). 27 | // The key is the parent class loader and the value is the AccessClassLoader, both are weak-referenced in the hash table. 28 | static private final WeakHashMap> accessClassLoaders = new WeakHashMap(); 29 | 30 | // Fast-path for classes loaded in the same ClassLoader as this class. 31 | static private final ClassLoader selfContextParentClassLoader = getParentClassLoader(AccessClassLoader.class); 32 | static private volatile AccessClassLoader selfContextAccessClassLoader = new AccessClassLoader(selfContextParentClassLoader); 33 | 34 | static private volatile Method defineClassMethod; 35 | 36 | private final HashSet localClassNames = new HashSet(); 37 | 38 | private AccessClassLoader (ClassLoader parent) { 39 | super(parent); 40 | } 41 | 42 | /** Returns null if the access class has not yet been defined. */ 43 | Class loadAccessClass (String name) { 44 | // No need to check the parent class loader if the access class hasn't been defined yet. 45 | if (localClassNames.contains(name)) { 46 | try { 47 | return loadClass(name, false); 48 | } catch (ClassNotFoundException ex) { 49 | throw new RuntimeException(ex); // Should not happen, since we know the class has been defined. 50 | } 51 | } 52 | return null; 53 | } 54 | 55 | Class defineAccessClass (String name, byte[] bytes) throws ClassFormatError { 56 | localClassNames.add(name); 57 | return defineClass(name, bytes); 58 | } 59 | 60 | protected Class loadClass (String name, boolean resolve) throws ClassNotFoundException { 61 | // These classes come from the classloader that loaded AccessClassLoader. 62 | if (name.equals(FieldAccess.class.getName())) return FieldAccess.class; 63 | if (name.equals(MethodAccess.class.getName())) return MethodAccess.class; 64 | if (name.equals(ConstructorAccess.class.getName())) return ConstructorAccess.class; 65 | // All other classes come from the classloader that loaded the type we are accessing. 66 | return super.loadClass(name, resolve); 67 | } 68 | 69 | Class defineClass (String name, byte[] bytes) throws ClassFormatError { 70 | try { 71 | // Attempt to load the access class in the same loader, which makes protected and default access members accessible. 72 | return (Class)getDefineClassMethod().invoke(getParent(), 73 | new Object[] {name, bytes, Integer.valueOf(0), Integer.valueOf(bytes.length), getClass().getProtectionDomain()}); 74 | } catch (Exception ignored) { 75 | // continue with the definition in the current loader (won't have access to protected and package-protected members) 76 | } 77 | return defineClass(name, bytes, 0, bytes.length, getClass().getProtectionDomain()); 78 | } 79 | 80 | // As per JLS, section 5.3, 81 | // "The runtime package of a class or interface is determined by the package name and defining class loader of the class or 82 | // interface." 83 | static boolean areInSameRuntimeClassLoader (Class type1, Class type2) { 84 | if (type1.getPackage() != type2.getPackage()) { 85 | return false; 86 | } 87 | ClassLoader loader1 = type1.getClassLoader(); 88 | ClassLoader loader2 = type2.getClassLoader(); 89 | ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); 90 | if (loader1 == null) { 91 | return (loader2 == null || loader2 == systemClassLoader); 92 | } 93 | if (loader2 == null) return loader1 == systemClassLoader; 94 | return loader1 == loader2; 95 | } 96 | 97 | static private ClassLoader getParentClassLoader (Class type) { 98 | ClassLoader parent = type.getClassLoader(); 99 | if (parent == null) parent = ClassLoader.getSystemClassLoader(); 100 | return parent; 101 | } 102 | 103 | static private Method getDefineClassMethod () throws Exception { 104 | if (defineClassMethod == null) { 105 | synchronized (accessClassLoaders) { 106 | if (defineClassMethod == null) { 107 | defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", 108 | new Class[] {String.class, byte[].class, int.class, int.class, ProtectionDomain.class}); 109 | try { 110 | defineClassMethod.setAccessible(true); 111 | } catch (Exception ignored) { 112 | } 113 | } 114 | } 115 | } 116 | return defineClassMethod; 117 | } 118 | 119 | static AccessClassLoader get (Class type) { 120 | ClassLoader parent = getParentClassLoader(type); 121 | // 1. fast-path: 122 | if (selfContextParentClassLoader.equals(parent)) { 123 | if (selfContextAccessClassLoader == null) { 124 | synchronized (accessClassLoaders) { // DCL with volatile semantics 125 | if (selfContextAccessClassLoader == null) 126 | selfContextAccessClassLoader = new AccessClassLoader(selfContextParentClassLoader); 127 | } 128 | } 129 | return selfContextAccessClassLoader; 130 | } 131 | // 2. normal search: 132 | synchronized (accessClassLoaders) { 133 | WeakReference ref = accessClassLoaders.get(parent); 134 | if (ref != null) { 135 | AccessClassLoader accessClassLoader = ref.get(); 136 | if (accessClassLoader != null) 137 | return accessClassLoader; 138 | else 139 | accessClassLoaders.remove(parent); // the value has been GC-reclaimed, but still not the key (defensive sanity) 140 | } 141 | AccessClassLoader accessClassLoader = new AccessClassLoader(parent); 142 | accessClassLoaders.put(parent, new WeakReference(accessClassLoader)); 143 | return accessClassLoader; 144 | } 145 | } 146 | 147 | static public void remove (ClassLoader parent) { 148 | // 1. fast-path: 149 | if (selfContextParentClassLoader.equals(parent)) { 150 | selfContextAccessClassLoader = null; 151 | } else { 152 | // 2. normal search: 153 | synchronized (accessClassLoaders) { 154 | accessClassLoaders.remove(parent); 155 | } 156 | } 157 | } 158 | 159 | static public int activeAccessClassLoaders () { 160 | int sz = accessClassLoaders.size(); 161 | if (selfContextAccessClassLoader != null) sz++; 162 | return sz; 163 | } 164 | } -------------------------------------------------------------------------------- /src/com/esotericsoftware/reflectasm/Accessor.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm; 2 | 3 | import java.lang.invoke.MethodHandle; 4 | import java.lang.reflect.*; 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public interface Accessor { 11 | ClassInfo getInfo(); 12 | 13 | MethodHandle[][] getMethodHandles(); 14 | 15 | ANY newInstanceWithIndex(int constructorIndex, V... args); 16 | 17 | ANY newInstance(); 18 | 19 | T invokeWithIndex(ANY instance, int methodIndex, V... args); 20 | 21 | void set(ANY instance, int fieldIndex, V value); 22 | 23 | T get(ANY instance, int fieldIndex); 24 | 25 | /** 26 | * Method returns class implementing EntityInterface which was used in class 27 | * extending AbstractDAO 28 | * 29 | * @return Class 30 | */ 31 | default Class returnedClass() { 32 | return (Class) getTypeArguments(Accessor.class, getClass()).get(0); 33 | } 34 | 35 | /** 36 | * Get the underlying class for a type, or null if the type is a variable 37 | * type. 38 | * 39 | * @param type the type 40 | * @return the underlying class 41 | */ 42 | static Class getClass(Type type) { 43 | if (type instanceof Class) { 44 | return (Class) type; 45 | } else if (type instanceof ParameterizedType) { 46 | return getClass(((ParameterizedType) type).getRawType()); 47 | } else if (type instanceof GenericArrayType) { 48 | Type componentType = ((GenericArrayType) type).getGenericComponentType(); 49 | Class componentClass = getClass(componentType); 50 | if (componentClass != null) { 51 | return Array.newInstance(componentClass, 0).getClass(); 52 | } else { 53 | return null; 54 | } 55 | } else { 56 | return null; 57 | } 58 | } 59 | 60 | /** 61 | * Get the actual type arguments a child class has used to extend a generic 62 | * base class. 63 | * 64 | * @param baseClass the base class 65 | * @param childClass the child class 66 | * @return a list of the raw classes for the actual type arguments. 67 | */ 68 | static List> getTypeArguments( 69 | Class baseClass, Class childClass) { 70 | Map resolvedTypes = new HashMap(); 71 | Type type = childClass; 72 | // start walking up the inheritance hierarchy until we hit baseClass 73 | while (!getClass(type).equals(baseClass)) { 74 | if (type instanceof Class) { 75 | // there is no useful information for us in raw types, so just keep going. 76 | type = ((Class) type).getGenericSuperclass(); 77 | } else { 78 | ParameterizedType parameterizedType = (ParameterizedType) type; 79 | Class rawType = (Class) parameterizedType.getRawType(); 80 | 81 | Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); 82 | TypeVariable[] typeParameters = rawType.getTypeParameters(); 83 | for (int i = 0; i < actualTypeArguments.length; i++) { 84 | resolvedTypes.put(typeParameters[i], actualTypeArguments[i]); 85 | } 86 | 87 | if (!rawType.equals(baseClass)) { 88 | type = rawType.getGenericSuperclass(); 89 | } 90 | } 91 | } 92 | 93 | // finally, for each actual type argument provided to baseClass, determine (if possible) 94 | // the raw class for that type argument. 95 | Type[] actualTypeArguments; 96 | if (type instanceof Class) { 97 | actualTypeArguments = ((Class) type).getTypeParameters(); 98 | } else { 99 | actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments(); 100 | } 101 | List> typeArgumentsAsClasses = new ArrayList>(); 102 | // resolve types by chasing down type variables. 103 | for (Type baseType : actualTypeArguments) { 104 | while (resolvedTypes.containsKey(baseType)) { 105 | baseType = resolvedTypes.get(baseType); 106 | } 107 | typeArgumentsAsClasses.add(getClass(baseType)); 108 | } 109 | return typeArgumentsAsClasses; 110 | } 111 | } -------------------------------------------------------------------------------- /src/com/esotericsoftware/reflectasm/ClassAccess.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm; 2 | 3 | import com.esotericsoftware.reflectasm.util.NumberUtils; 4 | import jdk.internal.org.objectweb.asm.Type; 5 | import jdk.internal.org.objectweb.asm.*; 6 | import jdk.internal.org.objectweb.asm.util.CheckClassAdapter; 7 | import sun.misc.Unsafe; 8 | 9 | import java.io.File; 10 | import java.io.FileOutputStream; 11 | import java.io.PrintWriter; 12 | import java.lang.invoke.MethodHandle; 13 | import java.lang.invoke.MethodHandles; 14 | import java.lang.reflect.*; 15 | import java.util.*; 16 | import java.util.concurrent.locks.ReentrantReadWriteLock; 17 | 18 | import static com.esotericsoftware.reflectasm.util.NumberUtils.*; 19 | import static jdk.internal.org.objectweb.asm.Opcodes.*; 20 | 21 | /* For java8 22 | import static jdk.internal.org.objectweb.asm.Opcodes.*; 23 | import jdk.internal.org.objectweb.asm.*; 24 | import jdk.internal.org.objectweb.asm.Type; 25 | import jdk.internal.org.objectweb.asm.util.CheckClassAdapter; 26 | */ 27 | /* For java 7 28 | import org.objectweb.asm.*; 29 | import org.objectweb.asm.Type; 30 | import static org.objectweb.asm.Opcodes.*; 31 | import org.objectweb.asm.util.CheckClassAdapter; 32 | */ 33 | 34 | @SuppressWarnings({"UnusedDeclaration", "Convert2Diamond", "ConstantConditions", "Unsafe", "deprecation"}) 35 | public class ClassAccess implements Accessor { 36 | public final static int HASH_BUCKETS = Integer.valueOf(System.getProperty("reflectasm.hash_buckets", "16")); 37 | public final static int MODIFIER_VARARGS = 262144; 38 | public final static String NEW = "new"; 39 | public final static String FIELD = "field"; 40 | public final static String SETTER = "set"; 41 | public final static String GETTER = "get"; 42 | public final static String METHOD = "method"; 43 | public static String ACCESS_CLASS_PREFIX = "asm."; 44 | public static boolean IS_SINGLE_THREAD_MODE = false; 45 | public static boolean IS_CACHED = true; 46 | public static boolean IS_STRICT_CONVERT = false; 47 | public static boolean IS_DEBUG = false; 48 | public static boolean IS_INCLUDE_NON_PUBLIC = true; 49 | public static int totalAccesses = 0; 50 | public static int cacheHits = 0; 51 | public static int loaderHits = 0; 52 | static HashMap[] caches = new HashMap[HASH_BUCKETS]; 53 | static final String thisPath = Type.getInternalName(ClassAccess.class); 54 | static final String accessorPath = Type.getInternalName(Accessor.class); 55 | static final String classInfoPath = Type.getInternalName(ClassInfo.class); 56 | static ReentrantReadWriteLock[] locks = new ReentrantReadWriteLock[HASH_BUCKETS]; 57 | public static final MethodHandles.Lookup lookup = MethodHandles.lookup(); 58 | public MethodHandle[][] methodHandles; 59 | ThreadLocal isInvokeWithMethodHandle = new ThreadLocal(); 60 | public static Field methodWriterCodeField = null; 61 | public static Field byteVectorLengthField = null; 62 | 63 | static { 64 | try { 65 | Field field = lookup.getClass().getDeclaredField("allowedModes"); 66 | field.setAccessible(true); 67 | field.set(lookup, -1); // = MethodHandles.Lookup.TRUSTED 68 | } catch (Throwable e) { 69 | e.printStackTrace(); 70 | } 71 | if (System.getProperty("reflectasm.is_cache", "true").equalsIgnoreCase("false")) IS_CACHED = false; 72 | else for (int i = 0; i < HASH_BUCKETS; i++) caches[i] = new HashMap(HASH_BUCKETS); 73 | if (System.getProperty("reflectasm.is_debug", "false").equalsIgnoreCase("true")) IS_DEBUG = true; 74 | if (System.getProperty("reflectasm.is_strict_convert", "false").equalsIgnoreCase("true")) 75 | IS_STRICT_CONVERT = true; 76 | for (int i = 0; i < HASH_BUCKETS; i++) locks[i] = new ReentrantReadWriteLock(); 77 | } 78 | 79 | public final Accessor accessor; 80 | public final ClassInfo classInfo; 81 | 82 | void reset() { 83 | isInvokeWithMethodHandle.set(false); 84 | } 85 | 86 | protected ClassAccess(Accessor accessor) { 87 | this.classInfo = accessor.getInfo(); 88 | this.accessor = accessor; 89 | this.methodHandles = accessor.getMethodHandles(); 90 | } 91 | 92 | public static boolean isVarArgs(int modifier) { 93 | return (modifier & MODIFIER_VARARGS) != 0; 94 | } 95 | 96 | public final static int getBucket(Class clz) { 97 | return Math.abs(clz.getName().hashCode()) % HASH_BUCKETS; 98 | } 99 | 100 | public final static void lock(int bucket, String mode, boolean isEnable) { 101 | if (IS_SINGLE_THREAD_MODE) return; 102 | if (mode.equals("read")) { 103 | if (isEnable) locks[bucket].readLock().lock(); 104 | else locks[bucket].readLock().unlock(); 105 | } else { 106 | if (isEnable) locks[bucket].writeLock().lock(); 107 | else locks[bucket].writeLock().unlock(); 108 | } 109 | } 110 | 111 | public final static Object readCache(Class clz, String key) { 112 | int bucket = getBucket(clz); 113 | String k = clz.getName() + "." + key; 114 | lock(bucket, "read", true); 115 | try { 116 | return caches[bucket].get(k); 117 | } finally { 118 | lock(bucket, "read", false); 119 | } 120 | } 121 | 122 | public final static void writeCache(Class clz, String key, Object value) { 123 | int bucket = getBucket(clz); 124 | String k = clz.getName() + "." + key; 125 | lock(bucket, "write", true); 126 | try { 127 | caches[bucket].put(k, value); 128 | } finally { 129 | lock(bucket, "write", false); 130 | } 131 | } 132 | 133 | public static int activeAccessClassLoaders() { 134 | return AccessClassLoader.activeAccessClassLoaders(); 135 | } 136 | 137 | public static void buildIndex(ClassInfo info) { 138 | if (info == null || info.attrIndex != null) return; 139 | info.methodCount = info.methodNames.length; 140 | info.fieldCount = info.fieldNames.length; 141 | info.constructorCount = info.constructorModifiers.length; 142 | Class clz = info.baseClass; 143 | info.attrIndex = new HashMap<>(); 144 | String[] constructors = new String[info.constructorParamTypes.length]; 145 | Arrays.fill(constructors, NEW); 146 | String[][] attrs = new String[][]{info.fieldNames, info.methodNames, constructors}; 147 | HashMap> map = new HashMap<>(); 148 | for (int i = 0; i < attrs.length; i++) { 149 | for (int j = 0; j < attrs[i].length; j++) { 150 | String attr = (char) (i + 1) + attrs[i][j]; 151 | if (!map.containsKey(attr)) map.put(attr, new ArrayList()); 152 | map.get(attr).add(j); 153 | } 154 | } 155 | for (String key : map.keySet()) 156 | info.attrIndex.put(key, map.get(key).toArray(new Integer[]{})); 157 | } 158 | 159 | /** 160 | * @param type Target class for reflection 161 | * @param dumpFile Optional to specify the path/directory to dump the reflection class over the target class 162 | * @return A dynamic object that wraps the target class 163 | */ 164 | public static ClassAccess access(Class type, String... dumpFile) { 165 | if (type.isArray()) 166 | throw new IllegalArgumentException(String.format("Input class '%s' cannot be an array!", type.getCanonicalName())); 167 | String className = type.getName(); 168 | final String accessClassName = (ACCESS_CLASS_PREFIX + className).replace("$", ""); 169 | final String source = String.valueOf(type.getResource("")); 170 | Class accessClass = null; 171 | Accessor accessor; 172 | byte[] bytes = null; 173 | ClassInfo info = null; 174 | ClassAccess self; 175 | int bucket = getBucket(type); 176 | AccessClassLoader loader = null; 177 | ++totalAccesses; 178 | int lockFlag = 0; 179 | try { 180 | lock(bucket, "write", true); 181 | lockFlag |= 2; 182 | /*Cache: className={Class,classResourcePath,ClassAccess(),byte[]}*/ 183 | if (IS_CACHED) { 184 | Object cachedObject = caches[bucket].get(className); 185 | if (cachedObject != null) { 186 | Object[] cache = (Object[]) cachedObject; 187 | //Class equals then directly return from cache 188 | if (type == cache[0]) { 189 | ++cacheHits; 190 | self = (ClassAccess) cache[2]; 191 | self.isInvokeWithMethodHandle.set(false); 192 | return self; 193 | } 194 | //Else if resources are equal then load from pre-built bytes 195 | if (cache[3] != null) { 196 | if (cache[1] == null && source == null || cache[1].equals(source)) { 197 | bytes = (byte[]) cache[3]; 198 | ++loaderHits; 199 | } 200 | } 201 | } 202 | } else { 203 | loader = AccessClassLoader.get(type); 204 | try { 205 | accessClass = (Class) loader.loadClass(accessClassName); 206 | } catch (ClassNotFoundException ignore1) { 207 | synchronized (loader) { 208 | try { 209 | accessClass = (Class) loader.loadClass(accessClassName); 210 | } catch (ClassNotFoundException ignore2) { 211 | } 212 | } 213 | } 214 | if (accessClass != null) { 215 | ++loaderHits; 216 | return new ClassAccess((Accessor) accessClass.newInstance()); 217 | } 218 | } 219 | 220 | if (bytes == null) {//Otherwise rebuild the bytes 221 | ArrayList methods = new ArrayList(); 222 | ArrayList> constructors = new ArrayList>(); 223 | ArrayList fields = new ArrayList(); 224 | collectMembers(type, methods, fields, constructors); 225 | info = new ClassInfo(); 226 | info.bucket = bucket; 227 | int n = constructors.size(); 228 | info.constructorModifiers = new Integer[n]; 229 | info.constructorParamTypes = new Class[n][]; 230 | info.constructorDescs = new String[n]; 231 | info.constructorCount = n; 232 | for (int i = 0; i < n; i++) { 233 | Constructor c = constructors.get(i); 234 | info.constructorModifiers[i] = c.getModifiers(); 235 | if (c.isVarArgs()) info.constructorModifiers[i] |= MODIFIER_VARARGS; 236 | info.constructorParamTypes[i] = c.getParameterTypes(); 237 | info.constructorDescs[i] = Type.getConstructorDescriptor(c); 238 | } 239 | 240 | n = methods.size(); 241 | info.methodDescs = new String[n][2]; 242 | info.methodModifiers = new Integer[n]; 243 | info.methodParamTypes = new Class[n][]; 244 | info.returnTypes = new Class[n * 2]; 245 | info.methodNames = new String[n]; 246 | info.baseClass = type; 247 | info.methodCount = n; 248 | for (int i = 0; i < n; i++) { 249 | Method m = methods.get(i); 250 | info.methodModifiers[i] = m.getModifiers(); 251 | Class clz = m.getDeclaringClass(); 252 | if (m.isVarArgs()) info.methodModifiers[i] |= MODIFIER_VARARGS; 253 | info.methodModifiers[i] |= clz.isInterface() ? Modifier.INTERFACE : 0; 254 | info.methodParamTypes[i] = m.getParameterTypes(); 255 | info.returnTypes[i] = m.getReturnType(); 256 | info.returnTypes[n + i] = clz == type ? null : clz; 257 | info.methodNames[i] = m.getName(); 258 | info.methodDescs[i] = new String[]{m.getName(), Type.getMethodDescriptor(m)}; 259 | } 260 | 261 | n = fields.size(); 262 | info.fieldModifiers = new Integer[n]; 263 | info.fieldNames = new String[n]; 264 | info.fieldTypes = new Class[n * 2]; 265 | info.fieldDescs = new String[n][2]; 266 | info.fieldCount = n; 267 | for (int i = 0; i < n; i++) { 268 | Field f = fields.get(i); 269 | Class clz = f.getDeclaringClass(); 270 | info.fieldNames[i] = f.getName(); 271 | info.fieldTypes[i] = f.getType(); 272 | info.fieldTypes[n + i] = clz == type ? null : clz; 273 | info.fieldModifiers[i] = f.getModifiers(); 274 | info.fieldDescs[i] = new String[]{f.getName(), Type.getDescriptor(f.getType())}; 275 | info.fieldModifiers[i] |= clz.isInterface() ? Modifier.INTERFACE : 0; 276 | } 277 | 278 | String accessClassNameInternal = accessClassName.replace('.', '/'); 279 | String classNameInternal = className.replace('.', '/'); 280 | //Remove "type.getEnclosingClass()==null" due to may trigger error 281 | int position = className.lastIndexOf('$'); 282 | info.isNonStaticMemberClass = position > 0 && classNameInternal.substring(position).indexOf('/') == -1 && !Modifier.isStatic(type.getModifiers()); 283 | bytes = byteCode(info, accessClassNameInternal, classNameInternal); 284 | } 285 | if (dumpFile.length > 0) try { 286 | File f = new File(dumpFile[0]); 287 | if (!f.exists()) { 288 | if (!dumpFile[0].endsWith(".class")) f.createNewFile(); 289 | else f.mkdir(); 290 | } 291 | if (f.isDirectory()) f = new File(f.getCanonicalPath() + File.separator + accessClassName + ".class"); 292 | try (FileOutputStream writer = new FileOutputStream(f)) { 293 | writer.write(bytes); 294 | writer.flush(); 295 | System.out.println("Class saved to " + f.getCanonicalPath()); 296 | } 297 | } catch (Exception e) { 298 | e.printStackTrace(); 299 | } 300 | 301 | if (loader == null) loader = AccessClassLoader.get(type); 302 | if (IS_DEBUG) CheckClassAdapter.verify(new ClassReader(bytes), loader, false, new PrintWriter(System.out)); 303 | try { 304 | accessClass = (Class) UnsafeHolder.theUnsafe.defineClass(accessClassName, bytes, 0, bytes.length, loader, type.getProtectionDomain()); 305 | } catch (Throwable ignored1) { 306 | accessClass = (Class) loader.defineClass(accessClassName, bytes); 307 | } 308 | accessor = (Accessor) accessClass.newInstance(); 309 | self = new ClassAccess(accessor); 310 | if (IS_CACHED) caches[bucket].put(className, new Object[]{type, source, self, bytes}); 311 | self.isInvokeWithMethodHandle.set(false); 312 | return self; 313 | } catch (Exception ex) { 314 | ex.printStackTrace(); 315 | throw new RuntimeException("Error constructing method access class: " + accessClassName + ": " + ex.getMessage(), ex); 316 | } finally { 317 | if ((lockFlag & 2) > 0) lock(bucket, "write", false); 318 | if ((lockFlag & 1) > 0) lock(bucket, "read", false); 319 | } 320 | } 321 | 322 | private static void collectClasses(Class clz, ArrayList classes) { 323 | if (clz == Object.class && classes.size() > 0) return; 324 | classes.add(clz); 325 | Class superClass = clz.getSuperclass(); 326 | if (superClass != null && superClass != clz) collectClasses(superClass, classes); 327 | for (Class c : clz.getInterfaces()) collectClasses(c, classes); 328 | } 329 | 330 | private static int calcPriority(int modifier) { 331 | return (Modifier.isPublic(modifier) ? 8 : 0) + 332 | (Modifier.isStatic(modifier) ? 4 : 0) + 333 | (Modifier.isProtected(modifier) ? 2 : 0) + 334 | (Modifier.isPrivate(modifier) ? 0 : 1); 335 | } 336 | 337 | private static void collectMembers(Class type, List methods, List fields, List> constructors) { 338 | boolean search = true; 339 | 340 | for (Constructor constructor : type.getDeclaredConstructors()) { 341 | //if (!IS_INCLUDE_NON_PUBLIC && !Modifier.isPublic(constructor.getModifiers())) continue; 342 | constructors.add(constructor); 343 | } 344 | 345 | ArrayList classes = new ArrayList<>(); 346 | collectClasses(type, classes); 347 | LinkedHashMap map = new LinkedHashMap(); 348 | LinkedHashMap candidates = new LinkedHashMap(); 349 | HashMap names = new HashMap<>(); 350 | int typeModifier = type.getModifiers(); 351 | for (Class clz : classes) { 352 | for (Method m : clz.getDeclaredMethods()) { 353 | int md1 = m.getModifiers(); 354 | if (!IS_INCLUDE_NON_PUBLIC && !Modifier.isPublic(md1)) continue; 355 | String name = m.getName(); 356 | //if (Modifier.isAbstract(md1) && !type.isInterface() && !Modifier.isAbstract(typeModifier)) continue; 357 | String desc = name + Type.getMethodDescriptor(m); 358 | int modifier = 16 + calcPriority(md1); 359 | Method m0 = (Method) map.get(desc); 360 | int md0 = m0 == null ? 0 : m0.getModifiers(); 361 | if (m0 == null) map.put(desc, m); 362 | else if (((Modifier.isPublic(md1) && !Modifier.isPublic(md0))// 363 | || (Modifier.isStatic(md1) && !Modifier.isStatic(md0))// 364 | || (Modifier.isAbstract(md0) && !Modifier.isAbstract(md1))) && clz != type) { 365 | map.put(desc, m); 366 | candidates.put(desc, m0); 367 | } else candidates.put(desc, m); 368 | Integer org = names.get(name); 369 | if (org == null || org < modifier) names.put(name, modifier); 370 | } 371 | 372 | for (Field f : clz.getDeclaredFields()) { 373 | int md1 = f.getModifiers(); 374 | String desc = f.getName(); 375 | if (!IS_INCLUDE_NON_PUBLIC && !Modifier.isPublic(md1)) continue; 376 | Field f0 = (Field) map.get(desc); 377 | int modifier = calcPriority(md1); 378 | Integer org = names.get(desc); 379 | //deal with conflict between method and field 380 | if (org != null) { 381 | boolean isOverload = modifier <= (org ^ 16); 382 | if (org >= 16) { 383 | if (isOverload) continue; 384 | else names.put(desc, modifier); 385 | } else if (!isOverload) { 386 | names.put(desc, modifier); 387 | } 388 | } else names.put(desc, modifier); 389 | int md0 = f0 == null ? 0 : f0.getModifiers(); 390 | if (!map.containsKey(desc)) map.put(desc, f); 391 | else if (((Modifier.isPublic(md1) && !Modifier.isPublic(md0))// 392 | || (Modifier.isStatic(md1) && !Modifier.isStatic(md0))// 393 | || (Modifier.isAbstract(md0) && !Modifier.isAbstract(md1))) && clz != type) { 394 | map.put(desc, f); 395 | candidates.put(desc, f0); 396 | } else candidates.put(desc, f); 397 | } 398 | } 399 | 400 | for (String desc : map.keySet()) { 401 | int md = names.get(desc.indexOf('(') == -1 ? desc : desc.substring(0, desc.indexOf('('))); 402 | Object value = map.get(desc); 403 | if (value.getClass() == Method.class && md >= 16) methods.add((Method) value); 404 | else if (value.getClass() == Field.class) fields.add((Field) value); 405 | } 406 | 407 | for (String desc : candidates.keySet()) { 408 | int md = names.get(desc.indexOf('(') == -1 ? desc : desc.substring(0, desc.indexOf('('))); 409 | Object value = candidates.get(desc); 410 | if (value.getClass() == Method.class && md >= 16) methods.add((Method) value); 411 | else if (value.getClass() == Field.class) fields.add((Field) value); 412 | } 413 | } 414 | 415 | private static void iconst(MethodVisitor mv, final int cst) { 416 | if (cst >= -1 && cst <= 5) { 417 | mv.visitInsn(Opcodes.ICONST_0 + cst); 418 | } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) { 419 | mv.visitIntInsn(Opcodes.BIPUSH, cst); 420 | } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) { 421 | mv.visitIntInsn(Opcodes.SIPUSH, cst); 422 | } else { 423 | mv.visitLdcInsn(cst); 424 | } 425 | } 426 | 427 | private static void insertArray(ClassVisitor cw, MethodVisitor mv1, Object[] array, String attrName, String accessClassNameInternal) { 428 | MethodVisitor mv; 429 | if (attrName != null) { 430 | mv = cw.visitMethod(ACC_STATIC, attrName, "()V", null, null); 431 | mv.visitCode(); 432 | mv.visitFieldInsn(GETSTATIC, accessClassNameInternal, "classInfo", Type.getDescriptor(ClassInfo.class)); 433 | } else mv = mv1; 434 | int len = array.length; 435 | iconst(mv, len); 436 | mv.visitTypeInsn(ANEWARRAY, Type.getInternalName(array.getClass().getComponentType())); 437 | for (int i = 0; i < len; i++) { 438 | mv.visitInsn(DUP); 439 | iconst(mv, i); 440 | Object item = array[i]; 441 | if (item == null) mv.visitInsn(ACONST_NULL); 442 | else if (item.getClass().isArray()) insertArray(cw, mv, (Object[]) item, null, null); 443 | else if (item instanceof Class) { 444 | Class clz = (Class) Array.get(array, i); 445 | if (clz.isPrimitive()) 446 | mv.visitFieldInsn(GETSTATIC, Type.getInternalName(namePrimitiveMap.get(clz.getName())), "TYPE", "Ljava/lang/Class;"); 447 | else mv.visitLdcInsn(Type.getType(clz)); 448 | } else { 449 | if ((item instanceof Integer) || (item instanceof Long)) { 450 | iconst(mv, ((Number) item).intValue()); 451 | if (item instanceof Integer) 452 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;"); 453 | else mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;"); 454 | } else mv.visitLdcInsn(item); 455 | } 456 | mv.visitInsn(AASTORE); 457 | } 458 | if (attrName != null) { 459 | mv.visitFieldInsn(PUTFIELD, classInfoPath, attrName, Type.getInternalName(array.getClass())); 460 | mv.visitInsn(RETURN); 461 | mv.visitMaxs(0, 0); 462 | mv.visitEnd(); 463 | mv1.visitMethodInsn(INVOKESTATIC, accessClassNameInternal, attrName, "()V", false); 464 | } 465 | } 466 | 467 | public final static int getMethodSize(MethodVisitor mv) { 468 | try { 469 | if (methodWriterCodeField == null) { 470 | methodWriterCodeField = mv.getClass().getDeclaredField("code"); 471 | methodWriterCodeField.setAccessible(true); 472 | byteVectorLengthField = ByteVector.class.getDeclaredField("length"); 473 | byteVectorLengthField.setAccessible(true); 474 | } 475 | return (int) byteVectorLengthField.get(methodWriterCodeField.get(mv)); 476 | } catch (Throwable e) { 477 | e.printStackTrace(); 478 | return -1; 479 | } 480 | } 481 | 482 | public final static void setInline(MethodVisitor mv) { 483 | AnnotationVisitor av; 484 | for (String an : new String[]{"Ljava/lang/invoke/ForceInline;", "Ljava/lang/invoke/LambdaForm$Compiled;"}) { 485 | av = mv.visitAnnotation(an, true); 486 | av.visitEnd(); 487 | } 488 | mv.visitCode(); 489 | } 490 | 491 | /** 492 | * Build ClassInfo of the underlying class while contructing 493 | * 494 | * @param info ClassInfo of the underlying class 495 | * @param cw ClassWriter 496 | * @param accessClassNameInternal The class name of the wrapper class 497 | * @param classNameInternal The class name of the underlying class 498 | */ 499 | private static void insertClassInfo(ClassVisitor cw, ClassInfo info, String accessClassNameInternal, String classNameInternal) { 500 | final String baseName = "sun/reflect/MagicAccessorImpl"; 501 | //final String baseName = "com/esotericsoftware/reflectasm/MagicAccessorImpl"; 502 | //final String baseName = "java/lang/Object"; 503 | final String clzInfoDesc = Type.getDescriptor(ClassInfo.class); 504 | final String genericName = ";"; 505 | final String clzInfoGenericDesc = "L" + Type.getInternalName(ClassInfo.class) + genericName; 506 | cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC, accessClassNameInternal, "L" + baseName + ";L" + accessorPath + genericName, baseName, new String[]{accessorPath}); 507 | String className = classNameInternal; 508 | try { 509 | int position = className.lastIndexOf('$'); 510 | if (position >= 0 && classNameInternal.substring(position).indexOf('/') == -1) { 511 | String outerClass = classNameInternal.substring(0, position); 512 | cw.visitOuterClass(outerClass, null, null); 513 | cw.visitInnerClass(classNameInternal, outerClass, info.baseClass.getSimpleName(), info.baseClass.getModifiers()); 514 | } 515 | } catch (Throwable e) { 516 | } 517 | MethodVisitor mv; 518 | 519 | //Constructor 520 | { 521 | mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); 522 | mv.visitCode(); 523 | mv.visitVarInsn(ALOAD, 0); 524 | mv.visitMethodInsn(INVOKESPECIAL, baseName, "", "()V"); 525 | mv.visitInsn(RETURN); 526 | mv.visitMaxs(0, 0); 527 | mv.visitEnd(); 528 | } 529 | 530 | //Static block 531 | { 532 | FieldVisitor fv = cw.visitField(ACC_STATIC, "methodHandles", "[[Ljava/lang/invoke/MethodHandle;", null, null); 533 | fv.visitEnd(); 534 | mv = cw.visitMethod(ACC_PUBLIC, "getMethodHandles", "()[[Ljava/lang/invoke/MethodHandle;", null, null); 535 | mv.visitCode(); 536 | mv.visitFieldInsn(GETSTATIC, accessClassNameInternal, "methodHandles", "[[Ljava/lang/invoke/MethodHandle;"); 537 | mv.visitInsn(ARETURN); 538 | mv.visitMaxs(0, 0); 539 | mv.visitEnd(); 540 | 541 | fv = cw.visitField(ACC_FINAL + ACC_STATIC, "classInfo", clzInfoDesc, clzInfoDesc.substring(0, clzInfoDesc.length() - 1) + ";", null); 542 | fv.visitEnd(); 543 | mv = cw.visitMethod(ACC_PUBLIC, "getInfo", "()" + clzInfoDesc, "()" + clzInfoGenericDesc, null); 544 | mv.visitCode(); 545 | mv.visitFieldInsn(GETSTATIC, accessClassNameInternal, "classInfo", clzInfoDesc); 546 | mv.visitInsn(ARETURN); 547 | mv.visitMaxs(0, 0); 548 | mv.visitEnd(); 549 | } 550 | 551 | {//Static block 552 | mv = cw.visitMethod(ACC_STATIC, "", "()V", null, null); 553 | mv.visitCode(); 554 | 555 | mv.visitInsn(ICONST_3); 556 | iconst(mv, Math.max(Math.max(info.constructorCount, info.methodCount), info.fieldCount * 2)); 557 | mv.visitMultiANewArrayInsn("[[Ljava/lang/invoke/MethodHandle;", 2); 558 | mv.visitFieldInsn(PUTSTATIC, accessClassNameInternal, "methodHandles", "[[Ljava/lang/invoke/MethodHandle;"); 559 | 560 | mv.visitTypeInsn(Opcodes.NEW, classInfoPath); 561 | mv.visitInsn(DUP); 562 | mv.visitMethodInsn(INVOKESPECIAL, classInfoPath, "", "()V", false); 563 | mv.visitFieldInsn(PUTSTATIC, accessClassNameInternal, "classInfo", clzInfoDesc); 564 | 565 | mv.visitFieldInsn(GETSTATIC, accessClassNameInternal, "classInfo", clzInfoDesc); 566 | mv.visitLdcInsn(Type.getType(info.baseClass)); 567 | mv.visitFieldInsn(PUTFIELD, classInfoPath, "baseClass", "Ljava/lang/Class;"); 568 | 569 | mv.visitFieldInsn(GETSTATIC, accessClassNameInternal, "classInfo", clzInfoDesc); 570 | mv.visitInsn(info.isNonStaticMemberClass ? ICONST_1 : ICONST_0); 571 | mv.visitFieldInsn(PUTFIELD, classInfoPath, "isNonStaticMemberClass", "Z"); 572 | 573 | mv.visitFieldInsn(GETSTATIC, accessClassNameInternal, "classInfo", clzInfoDesc); 574 | iconst(mv, info.bucket); 575 | mv.visitFieldInsn(PUTFIELD, classInfoPath, "bucket", "I"); 576 | 577 | insertArray(cw, mv, info.methodNames, "methodNames", accessClassNameInternal); 578 | insertArray(cw, mv, info.methodParamTypes, "methodParamTypes", accessClassNameInternal); 579 | insertArray(cw, mv, info.returnTypes, "returnTypes", accessClassNameInternal); 580 | insertArray(cw, mv, info.methodModifiers, "methodModifiers", accessClassNameInternal); 581 | insertArray(cw, mv, info.methodDescs, "methodDescs", accessClassNameInternal); 582 | insertArray(cw, mv, info.fieldNames, "fieldNames", accessClassNameInternal); 583 | insertArray(cw, mv, info.fieldTypes, "fieldTypes", accessClassNameInternal); 584 | insertArray(cw, mv, info.fieldModifiers, "fieldModifiers", accessClassNameInternal); 585 | insertArray(cw, mv, info.fieldDescs, "fieldDescs", accessClassNameInternal); 586 | insertArray(cw, mv, info.constructorParamTypes, "constructorParamTypes", accessClassNameInternal); 587 | insertArray(cw, mv, info.constructorModifiers, "constructorModifiers", accessClassNameInternal); 588 | insertArray(cw, mv, info.constructorDescs, "constructorDescs", accessClassNameInternal); 589 | 590 | mv.visitFieldInsn(GETSTATIC, accessClassNameInternal, "classInfo", clzInfoDesc); 591 | mv.visitMethodInsn(INVOKESTATIC, thisPath, "buildIndex", "(" + clzInfoDesc + ")V", false); 592 | 593 | mv.visitInsn(RETURN); 594 | mv.visitMaxs(0, 0); 595 | mv.visitEnd(); 596 | } 597 | } 598 | 599 | private static byte[] byteCode(ClassInfo info, String accessClassNameInternal, String classNameInternal) { 600 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); 601 | insertClassInfo(cw, info, accessClassNameInternal, classNameInternal); 602 | 603 | //*********************************************************************************************** 604 | // method access 605 | insertInvoke(cw, classNameInternal, info, accessClassNameInternal); 606 | 607 | //*********************************************************************************************** 608 | // field access 609 | insertGetObject(cw, classNameInternal, info, accessClassNameInternal); 610 | insertSetObject(cw, classNameInternal, info, accessClassNameInternal); 611 | 612 | //*********************************************************************************************** 613 | // constructor access 614 | insertNewInstance(cw, classNameInternal, info, accessClassNameInternal); 615 | insertNewRawInstance(cw, classNameInternal, accessClassNameInternal); 616 | 617 | cw.visitEnd(); 618 | return cw.toByteArray(); 619 | } 620 | 621 | private static void insertNewRawInstance(ClassVisitor cw, String classNameInternal, String accessClassNameInternal) { 622 | MethodVisitor mv; 623 | { 624 | mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "newInstance", "()L" + classNameInternal + ";", null, null); 625 | setInline(mv); 626 | mv.visitTypeInsn(Opcodes.NEW, classNameInternal); 627 | mv.visitInsn(DUP); 628 | mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "", "()V"); 629 | mv.visitInsn(ARETURN); 630 | mv.visitMaxs(0, 0); 631 | mv.visitEnd(); 632 | } 633 | { 634 | mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC + ACC_FINAL, "newInstance", "()Ljava/lang/Object;", null, null); 635 | setInline(mv); 636 | mv.visitVarInsn(ALOAD, 0); 637 | mv.visitMethodInsn(INVOKESPECIAL, accessClassNameInternal, "newInstance", "()L" + classNameInternal + ";"); 638 | mv.visitInsn(ARETURN); 639 | mv.visitMaxs(1, 1); 640 | mv.visitEnd(); 641 | } 642 | } 643 | 644 | private static void insertNewInstance(ClassVisitor cw, String classNameInternal, ClassInfo info, String accessClassNameInternal) { 645 | MethodVisitor mv; 646 | mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS + ACC_FINAL, "newInstanceWithIndex", "(I[Ljava/lang/Object;)L" + classNameInternal + ";", "(I[TT;)L" + classNameInternal + ";", null); 647 | setInline(mv); 648 | 649 | int n = info.constructorCount; 650 | 651 | if (n != 0) { 652 | mv.visitVarInsn(ILOAD, 1); 653 | Label[] labels = new Label[n]; 654 | for (int i = 0; i < n; i++) 655 | labels[i] = new Label(); 656 | Label defaultLabel = new Label(); 657 | mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); 658 | 659 | for (int i = 0; i < n; i++) { 660 | mv.visitLabel(labels[i]); 661 | if (i == 0) mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{classNameInternal}, 0, null); 662 | else mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 663 | 664 | mv.visitTypeInsn(Opcodes.NEW, classNameInternal); 665 | mv.visitInsn(DUP); 666 | 667 | Class[] paramTypes = info.constructorParamTypes[i]; 668 | for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) { 669 | Type paramType = Type.getType(paramTypes[paramIndex]); 670 | mv.visitVarInsn(ALOAD, 2); 671 | iconst(mv, paramIndex); 672 | mv.visitInsn(AALOAD); 673 | unbox(mv, paramType); 674 | } 675 | mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "", info.constructorDescs[i]); 676 | mv.visitInsn(ARETURN); 677 | } 678 | mv.visitLabel(defaultLabel); 679 | mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 680 | } 681 | mv.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalArgumentException"); 682 | mv.visitInsn(DUP); 683 | mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); 684 | mv.visitInsn(DUP); 685 | mv.visitLdcInsn("Constructor not found: "); 686 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "(Ljava/lang/String;)V"); 687 | mv.visitVarInsn(ILOAD, 1); 688 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;"); 689 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"); 690 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "", "(Ljava/lang/String;)V"); 691 | mv.visitInsn(ATHROW); 692 | mv.visitMaxs(0, 0); 693 | mv.visitEnd(); 694 | 695 | mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC + ACC_FINAL, "newInstanceWithIndex", "(I[Ljava/lang/Object;)Ljava/lang/Object;", null, null); 696 | setInline(mv); 697 | mv.visitVarInsn(ALOAD, 0); 698 | mv.visitVarInsn(ILOAD, 1); 699 | mv.visitVarInsn(ALOAD, 2); 700 | mv.visitMethodInsn(INVOKESPECIAL, accessClassNameInternal, "newInstanceWithIndex", "(I[Ljava/lang/Object;)L" + classNameInternal + ";"); 701 | mv.visitInsn(ARETURN); 702 | mv.visitMaxs(3, 3); 703 | mv.visitEnd(); 704 | } 705 | 706 | private static void insertInvoke(ClassVisitor cw, String classNameInternal, ClassInfo info, String accessClassNameInternal) { 707 | MethodVisitor mv; 708 | mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS + ACC_FINAL, "invokeWithIndex", "(L" + classNameInternal + ";I[Ljava/lang/Object;)Ljava/lang/Object;", "(L" + classNameInternal + ";I[TV;)TT;", null); 709 | setInline(mv); 710 | 711 | int n = info.methodCount; 712 | 713 | if (n != 0) { 714 | mv.visitVarInsn(ILOAD, 2); 715 | Label[] labels = new Label[n]; 716 | for (int i = 0; i < n; i++) 717 | labels[i] = new Label(); 718 | Label defaultLabel = new Label(); 719 | mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); 720 | 721 | for (int i = 0; i < n; i++) { 722 | boolean isInterface = Modifier.isInterface(info.methodModifiers[i]); 723 | boolean isStatic = Modifier.isStatic(info.methodModifiers[i]); 724 | 725 | mv.visitLabel(labels[i]); 726 | if (i == 0) mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{classNameInternal}, 0, null); 727 | else mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 728 | 729 | if (!isStatic) { 730 | mv.visitVarInsn(ALOAD, 1); 731 | } 732 | 733 | String methodName = info.methodNames[i]; 734 | Class[] paramTypes = info.methodParamTypes[i]; 735 | Class returnType = info.returnTypes[i]; 736 | for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) { 737 | mv.visitVarInsn(ALOAD, 3); 738 | iconst(mv, paramIndex); 739 | mv.visitInsn(AALOAD); 740 | Type paramType = Type.getType(paramTypes[paramIndex]); 741 | unbox(mv, paramType); 742 | } 743 | //4096: SYNTHETIC 744 | //final int inv = (isInterface && (info.methodModifiers[i] & ACC_SYNTHETIC) == 0) ? INVOKEINTERFACE : (isStatic ? INVOKESTATIC : INVOKESPECIAL); 745 | final int inv = isStatic ? INVOKESTATIC:(isInterface ? INVOKEINTERFACE : INVOKESPECIAL); 746 | Class clz = info.returnTypes[i + info.methodCount]; 747 | mv.visitMethodInsn(inv, clz != null ? Type.getInternalName(clz) : classNameInternal, methodName, info.methodDescs[i][1]); 748 | final Type retType = Type.getType(returnType); 749 | box(mv, retType); 750 | mv.visitInsn(ARETURN); 751 | } 752 | 753 | mv.visitLabel(defaultLabel); 754 | mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 755 | } 756 | mv.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalArgumentException"); 757 | mv.visitInsn(DUP); 758 | mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); 759 | mv.visitInsn(DUP); 760 | mv.visitLdcInsn("Method not found: "); 761 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "(Ljava/lang/String;)V"); 762 | mv.visitVarInsn(ILOAD, 2); 763 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;"); 764 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"); 765 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "", "(Ljava/lang/String;)V"); 766 | mv.visitInsn(ATHROW); 767 | mv.visitMaxs(0, 0); 768 | mv.visitEnd(); 769 | 770 | mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC + ACC_FINAL, "invokeWithIndex", "(Ljava/lang/Object;I[Ljava/lang/Object;)Ljava/lang/Object;", null, null); 771 | setInline(mv); 772 | mv.visitVarInsn(ALOAD, 0); 773 | mv.visitVarInsn(ALOAD, 1); 774 | mv.visitTypeInsn(CHECKCAST, classNameInternal); 775 | mv.visitVarInsn(ILOAD, 2); 776 | mv.visitVarInsn(ALOAD, 3); 777 | mv.visitMethodInsn(INVOKESPECIAL, accessClassNameInternal, "invokeWithIndex", "(L" + classNameInternal + ";I[Ljava/lang/Object;)Ljava/lang/Object;"); 778 | mv.visitInsn(ARETURN); 779 | mv.visitMaxs(4, 4); 780 | mv.visitEnd(); 781 | } 782 | 783 | static private void insertSetObject(ClassVisitor cw, String classNameInternal, ClassInfo info, String accessClassNameInternal) { 784 | int maxStack = 6; 785 | MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "set", "(L" + classNameInternal + ";ILjava/lang/Object;)V", "(L" + classNameInternal + ";ITV;)V", null); 786 | setInline(mv); 787 | mv.visitVarInsn(ILOAD, 2); 788 | 789 | if (info.fieldCount > 0) { 790 | maxStack--; 791 | Label[] labels = new Label[info.fieldCount]; 792 | for (int i = 0, n = labels.length; i < n; i++) 793 | labels[i] = new Label(); 794 | Label defaultLabel = new Label(); 795 | mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); 796 | 797 | for (int i = 0, n = labels.length; i < n; i++) { 798 | Type fieldType = Type.getType(info.fieldTypes[i]); 799 | boolean st = Modifier.isStatic(info.fieldModifiers[i]); 800 | mv.visitLabel(labels[i]); 801 | mv.visitFrame(F_SAME, 0, null, 0, null); 802 | if (!st) mv.visitVarInsn(ALOAD, 1); 803 | mv.visitVarInsn(ALOAD, 3); 804 | unbox(mv, fieldType); 805 | Class clz = info.fieldTypes[i + info.fieldCount]; 806 | mv.visitFieldInsn(st ? PUTSTATIC : PUTFIELD, clz != null ? Type.getInternalName(clz) : classNameInternal, info.fieldNames[i], info.fieldDescs[i][1]); 807 | mv.visitInsn(RETURN); 808 | } 809 | mv.visitLabel(defaultLabel); 810 | mv.visitFrame(F_SAME, 0, null, 0, null); 811 | } 812 | mv = insertThrowExceptionForFieldNotFound(mv); 813 | mv.visitMaxs(maxStack, 4); 814 | mv.visitEnd(); 815 | 816 | mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC + ACC_FINAL, "set", "(Ljava/lang/Object;ILjava/lang/Object;)V", null, null); 817 | setInline(mv); 818 | mv.visitVarInsn(ALOAD, 0); 819 | mv.visitVarInsn(ALOAD, 1); 820 | mv.visitTypeInsn(CHECKCAST, classNameInternal); 821 | mv.visitVarInsn(ILOAD, 2); 822 | mv.visitVarInsn(ALOAD, 3); 823 | mv.visitMethodInsn(INVOKESPECIAL, accessClassNameInternal, "set", "(L" + classNameInternal + ";ILjava/lang/Object;)V"); 824 | mv.visitInsn(RETURN); 825 | mv.visitMaxs(4, 4); 826 | mv.visitEnd(); 827 | } 828 | 829 | static private void insertGetObject(ClassVisitor cw, String classNameInternal, ClassInfo info, String accessClassNameInternal) { 830 | int maxStack = 6; 831 | MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "get", "(L" + classNameInternal + ";I)Ljava/lang/Object;", "(L" + classNameInternal + ";I)TT;", null); 832 | setInline(mv); 833 | mv.visitVarInsn(ILOAD, 2); 834 | 835 | if (info.fieldCount > 0) { 836 | maxStack--; 837 | Label[] labels = new Label[info.fieldCount]; 838 | for (int i = 0, n = labels.length; i < n; i++) 839 | labels[i] = new Label(); 840 | Label defaultLabel = new Label(); 841 | mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); 842 | 843 | for (int i = 0, n = labels.length; i < n; i++) { 844 | mv.visitLabel(labels[i]); 845 | mv.visitFrame(F_SAME, 0, null, 0, null); 846 | Class clz = info.fieldTypes[i + info.fieldCount]; 847 | String clzName = clz != null ? Type.getInternalName(clz) : classNameInternal; 848 | if (Modifier.isStatic(info.fieldModifiers[i])) { 849 | mv.visitFieldInsn(GETSTATIC, clzName, info.fieldNames[i], info.fieldDescs[i][1]); 850 | } else { 851 | mv.visitVarInsn(ALOAD, 1); 852 | mv.visitFieldInsn(GETFIELD, clzName, info.fieldNames[i], info.fieldDescs[i][1]); 853 | } 854 | Type fieldType = Type.getType(info.fieldTypes[i]); 855 | box(mv, fieldType); 856 | mv.visitInsn(ARETURN); 857 | } 858 | mv.visitLabel(defaultLabel); 859 | mv.visitFrame(F_SAME, 0, null, 0, null); 860 | } 861 | insertThrowExceptionForFieldNotFound(mv); 862 | mv.visitInsn(ATHROW); 863 | mv.visitMaxs(0, 0); 864 | mv.visitEnd(); 865 | 866 | mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC + ACC_FINAL, "get", "(Ljava/lang/Object;I)Ljava/lang/Object;", null, null); 867 | setInline(mv); 868 | mv.visitVarInsn(ALOAD, 0); 869 | mv.visitVarInsn(ALOAD, 1); 870 | mv.visitTypeInsn(CHECKCAST, classNameInternal); 871 | mv.visitVarInsn(ILOAD, 2); 872 | mv.visitMethodInsn(INVOKESPECIAL, accessClassNameInternal, "get", "(L" + classNameInternal + ";I)Ljava/lang/Object;"); 873 | mv.visitInsn(ARETURN); 874 | mv.visitMaxs(3, 3); 875 | mv.visitEnd(); 876 | } 877 | 878 | static private MethodVisitor insertThrowExceptionForFieldNotFound(MethodVisitor mv) { 879 | mv.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalArgumentException"); 880 | mv.visitInsn(DUP); 881 | mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); 882 | mv.visitInsn(DUP); 883 | mv.visitLdcInsn("Field not found: "); 884 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "(Ljava/lang/String;)V"); 885 | mv.visitVarInsn(ILOAD, 2); 886 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;"); 887 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"); 888 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "", "(Ljava/lang/String;)V"); 889 | mv.visitInsn(ATHROW); 890 | return mv; 891 | } 892 | 893 | static private MethodVisitor insertThrowExceptionForFieldType(MethodVisitor mv, String fieldType) { 894 | mv.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalArgumentException"); 895 | mv.visitInsn(DUP); 896 | mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); 897 | mv.visitInsn(DUP); 898 | mv.visitLdcInsn("Field not declared as " + fieldType + ": "); 899 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "(Ljava/lang/String;)V"); 900 | mv.visitVarInsn(ILOAD, 2); 901 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;"); 902 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"); 903 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "", "(Ljava/lang/String;)V"); 904 | mv.visitInsn(ATHROW); 905 | return mv; 906 | } 907 | 908 | private static void box(MethodVisitor mv, Type type) { 909 | switch (type.getSort()) { 910 | case Type.VOID: 911 | mv.visitInsn(ACONST_NULL); 912 | return; 913 | case Type.BOOLEAN: 914 | case Type.CHAR: 915 | case Type.BYTE: 916 | case Type.SHORT: 917 | case Type.INT: 918 | case Type.FLOAT: 919 | case Type.LONG: 920 | case Type.DOUBLE: 921 | Type clz = Type.getType(namePrimitiveMap.get(type.getClassName())); 922 | mv.visitMethodInsn(INVOKESTATIC, clz.getInternalName(), "valueOf", "(" + type.getDescriptor() + ")" + clz.getDescriptor()); 923 | break; 924 | } 925 | } 926 | 927 | private static void unbox(MethodVisitor mv, Type type) { 928 | switch (type.getSort()) { 929 | case Type.BOOLEAN: 930 | case Type.CHAR: 931 | case Type.BYTE: 932 | case Type.SHORT: 933 | case Type.INT: 934 | case Type.FLOAT: 935 | case Type.LONG: 936 | case Type.DOUBLE: 937 | String name = Type.getInternalName(namePrimitiveMap.get(type.getClassName())); 938 | mv.visitTypeInsn(CHECKCAST, name); 939 | mv.visitMethodInsn(INVOKESPECIAL, name, type.getClassName() + "Value", "()" + type.getDescriptor()); 940 | break; 941 | case Type.ARRAY: 942 | mv.visitTypeInsn(CHECKCAST, type.getDescriptor()); 943 | break; 944 | case Type.OBJECT: 945 | mv.visitTypeInsn(CHECKCAST, type.getInternalName()); 946 | break; 947 | } 948 | } 949 | 950 | @Override 951 | public String toString() { 952 | return accessor.toString(); 953 | } 954 | 955 | public boolean isNonStaticMemberClass() { 956 | return classInfo.isNonStaticMemberClass; 957 | } 958 | 959 | public String getNameType(String name) { 960 | String item = null; 961 | if (classInfo.attrIndex.containsKey(name)) { 962 | char c = name.charAt(0); 963 | return c == 1 ? FIELD : c == 2 ? METHOD : NEW; 964 | } 965 | for (int i = 1; i <= 3; i++) { 966 | if (classInfo.attrIndex.containsKey((char) i + name)) 967 | item = i == 1 ? FIELD : i == 2 ? METHOD : NEW; 968 | } 969 | return item; 970 | } 971 | 972 | public Integer[] indexesOf(Class clz, String name, String type) { 973 | char index; 974 | if (name.equals(NEW)) index = 3; 975 | else switch (type) { 976 | case FIELD: 977 | index = 1; 978 | break; 979 | case METHOD: 980 | index = 2; 981 | break; 982 | case NEW: 983 | case "constructor": 984 | index = 3; 985 | break; 986 | default: 987 | throw new IllegalArgumentException("No such type " + type); 988 | } 989 | final String attr = index + name; 990 | Integer[] list = (Integer[]) classInfo.attrIndex.get(attr); 991 | if (list != null) { 992 | if (clz == null || index == 3) return list; 993 | ArrayList ary = new ArrayList(); 994 | Class[] classes = index == 1 ? classInfo.fieldTypes : classInfo.returnTypes; 995 | final int offset = index == 1 ? classInfo.fieldCount : classInfo.methodCount; 996 | String className = Type.getInternalName(clz); 997 | for (Integer e : list) 998 | if (clz == classes[offset + e] || (clz == classInfo.baseClass && classes[offset + e] == null)) 999 | ary.add(e); 1000 | list = ary.toArray(new Integer[]{}); 1001 | if (list.length > 0) return list; 1002 | } 1003 | throw new IllegalArgumentException("Unable to find " + type + ": " + name); 1004 | } 1005 | 1006 | public static Class[] args2Types(final T... args) { 1007 | Class[] classes = new Class[args.length]; 1008 | for (int i = 0, n = args.length; i < n; i++) 1009 | classes[i] = args[i] == null ? null : (Class) args[i].getClass(); 1010 | return classes; 1011 | } 1012 | 1013 | /** 1014 | * Get MethodHandle of Field/Method/Constructor 1015 | * 1016 | * @param index The index that can be retrieved from indexOfField/indexOfMethod/indexOfConstructor 1017 | * @param type Can be: ClassAccess.GETTER/ClassAccess.SETTER/ClassAccess.METHOD/ClassAccess.NEW 1018 | * @return The direct MethodHandle 1019 | */ 1020 | public MethodHandle getHandleWithIndex(int index, String type) { 1021 | MethodHandle handle; 1022 | int d1 = (type.equals(SETTER) || type.equals(GETTER)) ? 2 : type.equals(METHOD) ? 1 : 0; 1023 | int d2 = type.equals(SETTER) ? classInfo.fieldCount + index : index; 1024 | handle = methodHandles[d1][d2]; 1025 | Class clz; 1026 | if (handle == null) try { 1027 | switch (type) { 1028 | case NEW: 1029 | Constructor c = classInfo.baseClass.getDeclaredConstructor(classInfo.constructorParamTypes[index]); 1030 | handle = lookup.unreflectConstructor(c); 1031 | break; 1032 | case METHOD: 1033 | clz = classInfo.returnTypes[classInfo.methodCount + index]; 1034 | Method m = (clz == null ? classInfo.baseClass : clz).getDeclaredMethod(classInfo.methodNames[index], classInfo.methodParamTypes[index]); 1035 | handle = lookup.unreflect(m); 1036 | break; 1037 | default: 1038 | clz = classInfo.fieldTypes[classInfo.fieldCount + index]; 1039 | Field f = (clz == null ? classInfo.baseClass : clz).getDeclaredField(classInfo.fieldNames[index]); 1040 | if (type.equals(GETTER)) handle = lookup.unreflectGetter(f); 1041 | else handle = lookup.unreflectSetter(f); 1042 | break; 1043 | } 1044 | //handle=new ConstantCallSite(handle).dynamicInvoker(); 1045 | methodHandles[d1][d2] = handle; 1046 | } catch (Throwable throwable) { 1047 | throwable.printStackTrace(); 1048 | throw new IllegalArgumentException(throwable.getMessage()); 1049 | } 1050 | 1051 | return handle; 1052 | } 1053 | 1054 | /** 1055 | * Get MethodHandle of Field/Method/Constructor 1056 | * 1057 | * @param clz Target class, null means auto, can be super-class or interface 1058 | * @param name Field name or method name, for constructor, specify as null 1059 | * @param type Can be: ClassAccess.GETTER/ClassAccess.SETTER/ClassAccess.METHOD/ClassAccess.NEW 1060 | * @param paramTypes The parameter types of target method or constructor 1061 | * @return The direct MethodHandle 1062 | */ 1063 | public MethodHandle getHandle(Class clz, String name, String type, Class... paramTypes) { 1064 | int index; 1065 | if (type.equals(SETTER) || type.equals(GETTER)) index = indexesOf(clz, name, FIELD)[0]; 1066 | index = indexOfMethod(clz, NEW.equals(type) ? NEW : name, paramTypes); 1067 | return getHandleWithIndex(index, type); 1068 | } 1069 | 1070 | /** 1071 | * Get MethodHandle of Field/Method/Constructor 1072 | * 1073 | * @param clz Target class, null means auto, can be super-class or interface 1074 | * @param name Field name or method name, for constructor, specify as null 1075 | * @param type Can be: ClassAccess.GETTER/ClassAccess.SETTER/ClassAccess.METHOD/ClassAccess.NEW 1076 | * @param args Parameters for further invoke 1077 | * @return The direct MethodHandle 1078 | */ 1079 | public MethodHandle getHandleWithArgs(Class clz, String name, String type, T... args) { 1080 | return getHandle(clz, name, type, args2Types(args)); 1081 | } 1082 | 1083 | public Integer[] indexesOf(String name, String type) { 1084 | return indexesOf(null, name, type); 1085 | } 1086 | 1087 | public Integer indexOf(String name, String type) { 1088 | Integer[] indexes = indexesOf(name, type); 1089 | return indexes.length == 1 ? indexes[0] : null; 1090 | } 1091 | 1092 | public int indexOfField(Class clz, String fieldName) { 1093 | return indexesOf(clz, fieldName, FIELD)[0]; 1094 | } 1095 | 1096 | public int indexOfField(String fieldName) { 1097 | return indexesOf(null, fieldName, FIELD)[0]; 1098 | } 1099 | 1100 | public int indexOfField(Field field) { 1101 | return indexOfField(field.getDeclaringClass(), field.getName()); 1102 | } 1103 | 1104 | public int indexOfConstructor(Constructor constructor) { 1105 | return indexOfMethod(null, NEW, constructor.getParameterTypes()); 1106 | } 1107 | 1108 | public int indexOfConstructor(Class... parameterTypes) { 1109 | return indexOfMethod(null, NEW, parameterTypes); 1110 | } 1111 | 1112 | public int indexOfMethod(Method method) { 1113 | return indexOfMethod(method.getDeclaringClass(), method.getName(), method.getParameterTypes()); 1114 | } 1115 | 1116 | /** 1117 | * Returns the index of the first method with the specified name. 1118 | */ 1119 | public int indexOfMethod(String methodName) { 1120 | return indexesOf(null, methodName, METHOD)[0]; 1121 | } 1122 | 1123 | public Long getSignature(Class clz, String methodName, Class... paramTypes) { 1124 | long signature = classInfo.baseClass.hashCode() * 65599 + methodName.hashCode(); 1125 | if (clz != null) signature += clz.getClass().getName().hashCode() * 65599 * 65599; 1126 | if (paramTypes != null) for (int i = 0; i < paramTypes.length; i++) { 1127 | signature = signature * 65599 + (paramTypes[i] == null ? 0 : paramTypes[i].hashCode()); 1128 | } 1129 | return signature; 1130 | } 1131 | 1132 | @SafeVarargs 1133 | private final String typesToString(String methodName, Object... argTypes) { 1134 | StringBuilder sb = new StringBuilder(classInfo.baseClass.getName()); 1135 | sb.append(".").append(methodName).append("("); 1136 | for (int i = 0; i < argTypes.length; i++) { 1137 | Class clz; 1138 | if (argTypes[i] == null) clz = null; 1139 | else if (argTypes[i] instanceof Class) clz = (Class) argTypes[i]; 1140 | else clz = argTypes[i].getClass(); 1141 | if (clz == null) sb.append("null"); 1142 | else { 1143 | String clzName = clz.getCanonicalName(); 1144 | if (clzName == null) clzName = clz.getName(); 1145 | sb.append(clz == null ? "null" : clzName.startsWith("java.lang.") ? clz.getSimpleName() : clzName); 1146 | } 1147 | sb.append(i == argTypes.length - 1 ? "" : ","); 1148 | } 1149 | sb.append(")"); 1150 | return sb.toString(); 1151 | } 1152 | 1153 | 1154 | @SafeVarargs 1155 | public final int indexOfMethod(final Class clz, final String methodName, final Integer[] candidates, final Class... argTypes) { 1156 | //if(IS_STRICT_CONVERT) return candidates[0]; 1157 | int result = -1; 1158 | Class[][] paramTypes; 1159 | Integer[] modifiers; 1160 | Long signature = -1L; 1161 | int distance = 0; 1162 | int minDistance = 10; 1163 | final int stepSize = 100; 1164 | if (methodName.equals(NEW)) { 1165 | for (int i = 0, n = candidates.length; i < n; i++) candidates[i] = i; 1166 | paramTypes = classInfo.constructorParamTypes; 1167 | modifiers = classInfo.constructorModifiers; 1168 | } else { 1169 | paramTypes = classInfo.methodParamTypes; 1170 | modifiers = classInfo.methodModifiers; 1171 | } 1172 | final int bucket = classInfo.bucket; 1173 | int lockFlag = 0; 1174 | try { 1175 | if (IS_CACHED) { 1176 | signature = getSignature(clz, methodName, argTypes); 1177 | lock(bucket, "read", true); 1178 | lockFlag |= 1; 1179 | Integer targetIndex = (Integer) caches[bucket].get(signature); 1180 | lock(bucket, "read", false); 1181 | lockFlag ^= 1; 1182 | if (targetIndex != null) { 1183 | minDistance = targetIndex / 10000; 1184 | targetIndex = targetIndex % 10000; 1185 | if (10000 - targetIndex == 1) result = -2; 1186 | else for (int index : candidates) 1187 | if (index == targetIndex) result = index; 1188 | } 1189 | } 1190 | final int argCount = argTypes.length; 1191 | int[] distances = new int[0]; 1192 | if (result == -1) for (int index : candidates) { 1193 | int min = 10; 1194 | int[] val = new int[argCount + 1]; 1195 | if (Arrays.equals(argTypes, paramTypes[index])) { 1196 | if (IS_CACHED) { 1197 | lock(bucket, "write", true); 1198 | lockFlag |= 2; 1199 | caches[bucket].put(signature, Integer.valueOf(index + 50000)); 1200 | } 1201 | return index; 1202 | } 1203 | int thisDistance = 0; 1204 | final int paramCount = paramTypes[index].length; 1205 | final int last = paramCount - 1; 1206 | final Class lastClass = last < 0 ? null : paramTypes[index][last]; 1207 | final boolean isVarArgs = isVarArgs(modifiers[index]) || (lastClass != null && lastClass.isArray() && argCount > last); 1208 | for (int i = 0, n = Math.min(argCount, paramCount); i < n; i++) { 1209 | if (i == last && isVarArgs) break; 1210 | val[i] = IS_STRICT_CONVERT ? 10 : NumberUtils.getDistance(argTypes[i], paramTypes[index][i]); 1211 | min = Math.min(val[i], min); 1212 | thisDistance += stepSize + val[i]; 1213 | //System.out.println((argTypes[i]==null?"null":argTypes[i].getCanonicalName())+" <-> "+paramTypes[index][i].getCanonicalName()+": "+dis); 1214 | } 1215 | if (argCount > last && isVarArgs) { 1216 | if (!IS_STRICT_CONVERT) { 1217 | final Class arrayType = paramTypes[index][last].getComponentType(); 1218 | int sum = 0; 1219 | for (int i = last; i < argCount; i++) { 1220 | thisDistance += stepSize; 1221 | val[i] = Math.max(getDistance(argTypes[i], arrayType), getDistance(argTypes[i], paramTypes[index][last])); 1222 | min = Math.min(min, val[i]); 1223 | if (val[i] <= 0) sum = -stepSize; 1224 | else sum += val[i]; 1225 | } 1226 | thisDistance += sum; 1227 | } 1228 | } else if (paramCount != argCount) { 1229 | thisDistance -= (Math.abs(paramCount - argCount) - (isVarArgs(modifiers[index]) ? 1 : 0)) * stepSize / (argCount > paramCount ? 2 : 1); 1230 | } 1231 | if (thisDistance > distance) { 1232 | distance = thisDistance; 1233 | distances = val; 1234 | result = index; 1235 | minDistance = min; 1236 | } 1237 | } 1238 | if (result < -1) result = -1; 1239 | if (IS_CACHED) { 1240 | lock(bucket, "write", true); 1241 | lockFlag |= 2; 1242 | caches[bucket].put(signature, Integer.valueOf(minDistance * 10000 + result)); 1243 | } 1244 | if (result >= 0 && argCount == 0 && paramTypes[result].length == 0) return result; 1245 | if (result < 0 || minDistance == 0 // 1246 | || (argCount < paramTypes[result].length && !isVarArgs(modifiers[result])) // 1247 | || (isVarArgs(modifiers[result]) && argCount < paramTypes[result].length - 1)) { 1248 | String str = "Unable to apply " + (methodName.equals(NEW) ? "constructor" : METHOD) + ":\n " + typesToString(methodName, argTypes) // 1249 | + (result == -1 ? "" : "\n => " + typesToString(methodName, paramTypes[result])); 1250 | if (IS_DEBUG && result >= 0) { 1251 | System.out.println(String.format("Method=%s, Index=%d, isVarArgs=%s, MinDistance=%d%s", methodName, result, isVarArgs(modifiers[result]) + "(" + modifiers[result] + ")", minDistance, Arrays.toString(distances))); 1252 | for (int i = 0; i < Math.max(argCount, paramTypes[result].length); i++) { 1253 | int flag = i >= argCount ? 1 : i >= paramTypes[result].length ? 2 : 0; 1254 | System.out.println(String.format("Parameter#%2d: %20s -> %-20s : %2d",// 1255 | i, flag == 1 ? "N/A" : argTypes[i] == null ? "null" : argTypes[i].getSimpleName(),// 1256 | flag == 2 ? "N/A" : paramTypes[result][i] == null ? "null" : paramTypes[result][i].getSimpleName(),// 1257 | flag > 0 ? -1 : distances[i])); 1258 | } 1259 | } 1260 | throw new IllegalArgumentException(str); 1261 | } 1262 | return result; 1263 | } finally { 1264 | if ((lockFlag & 2) > 0) lock(bucket, "write", false); 1265 | if ((lockFlag & 1) > 0) lock(bucket, "read", false); 1266 | } 1267 | } 1268 | 1269 | /** 1270 | * Returns the index of the first method with the specified name and param types. 1271 | * 1272 | * @param methodName Method name or '' for constructing 1273 | * @param argTypes Arguments class types 1274 | * @return 1275 | */ 1276 | public final int indexOfMethod(final Class clz, final String methodName, Class... argTypes) { 1277 | Integer[] candidates = indexesOf(clz, methodName, METHOD); 1278 | return indexOfMethod(clz, methodName, indexesOf(clz, methodName, METHOD), argTypes); 1279 | } 1280 | 1281 | public final int indexOfMethod(String methodName, Class... argTypes) { 1282 | return indexOfMethod(null, methodName, argTypes); 1283 | } 1284 | 1285 | /** 1286 | * Returns the index of the first method with the specified name and the specified number of arguments. 1287 | */ 1288 | public int indexOfMethod(String methodName, int paramsCount) { 1289 | for (int index : indexesOf(null, methodName, METHOD)) { 1290 | final int modifier = (methodName == NEW) ? classInfo.constructorModifiers[index] : classInfo.methodModifiers[index]; 1291 | final int len = (methodName == NEW) ? classInfo.constructorParamTypes[index].length : classInfo.methodParamTypes[index].length; 1292 | if (len == paramsCount || isVarArgs(modifier) && paramsCount >= len - 1) return index; 1293 | } 1294 | throw new IllegalArgumentException("Unable to find method: " + methodName + " with " + paramsCount + " params."); 1295 | } 1296 | 1297 | private String getMethodNameByParamTypes(Class[] paramTypes) { 1298 | String methodName = NEW; 1299 | for (int i = 0; i < classInfo.methodParamTypes.length; i++) { 1300 | if (classInfo.methodParamTypes[i] == paramTypes) { 1301 | methodName = classInfo.methodNames[i]; 1302 | } 1303 | } 1304 | return methodName; 1305 | } 1306 | 1307 | private T[] reArgs(final String method, final int index, V[] args) { 1308 | final boolean isNewInstance = (method instanceof String) && (method.equals(NEW)); 1309 | if (index >= (isNewInstance ? classInfo.constructorCount : classInfo.methodCount)) 1310 | throw new IllegalArgumentException("No index: " + index); 1311 | final Class[] paramTypes = isNewInstance ? classInfo.constructorParamTypes[index] : classInfo.methodParamTypes[index]; 1312 | final int paramCount = paramTypes.length; 1313 | if (paramCount == 0) return null; 1314 | final int modifier = isNewInstance ? classInfo.constructorModifiers[index] : classInfo.methodModifiers[index]; 1315 | final int argCount = args.length; 1316 | final int last = paramCount - 1; 1317 | final Class lastClass = last < 0 ? null : paramTypes[last]; 1318 | final boolean isVarArgs = isVarArgs(modifier) || (argCount >= paramCount && (lastClass != null && lastClass.isArray())); 1319 | 1320 | if (argCount < (isVarArgs ? last : paramCount)) { 1321 | String methodName = getMethodNameByParamTypes(paramTypes); 1322 | throw new IllegalArgumentException("Unable to " + (isNewInstance ? "construct instance" : "invoke method") + " with index " + index + ": "// 1323 | + "\n " + typesToString(methodName, args) // 1324 | + "\n =>" + typesToString(methodName, paramTypes)); 1325 | } 1326 | if (!isVarArgs && IS_STRICT_CONVERT) return (T[]) args; 1327 | try { 1328 | Object[] arg = new Object[paramCount]; 1329 | for (int i = 0; i < (isVarArgs ? last : paramCount); i++) { 1330 | if (args[i] == null && paramTypes[i].isPrimitive()) 1331 | throw new IllegalArgumentException("Cannot assign null to element#" + (i + 1) + ": " + paramTypes[i].getCanonicalName()); 1332 | arg[i] = IS_STRICT_CONVERT ? args[i] : convert(args[i], paramTypes[i]); 1333 | } 1334 | if (isVarArgs) { 1335 | final Class varArgsType = paramTypes[last]; 1336 | final Class subType = varArgsType.getComponentType(); 1337 | Object var = null; 1338 | if (argCount > paramCount) { 1339 | var = Arrays.copyOfRange(args, last, argCount); 1340 | } else if (argCount == paramCount) { 1341 | var = args[last]; 1342 | if (var == null) { 1343 | if (subType.isPrimitive()) 1344 | throw new IllegalArgumentException("Cannot assign null to element#" + paramCount + ": " + varArgsType.getCanonicalName()); 1345 | var = Array.newInstance(subType, 1); 1346 | } else if (getDistance(var.getClass(), varArgsType) <= getDistance(var.getClass(), subType)) 1347 | var = Arrays.copyOfRange(args, last, argCount); 1348 | } else { 1349 | var = Array.newInstance(subType, 0); 1350 | } 1351 | arg[last] = IS_STRICT_CONVERT ? var : convert(var, varArgsType); 1352 | } 1353 | return (T[]) arg; 1354 | } catch (Exception e) { 1355 | if (isNonStaticMemberClass() && (args[0] == null || args[0].getClass() != classInfo.baseClass.getEnclosingClass())) 1356 | throw new IllegalArgumentException("Cannot initialize a non-static inner class " + classInfo.baseClass.getCanonicalName() + " without specifying the enclosing instance!"); 1357 | String methodName = getMethodNameByParamTypes(paramTypes); 1358 | if (IS_DEBUG) e.printStackTrace(); 1359 | throw new IllegalArgumentException("Data conversion error when invoking method: " + e.getMessage()// 1360 | + "\n " + typesToString(methodName, args) // 1361 | + "\n =>" + typesToString(methodName, paramTypes)); 1362 | } 1363 | } 1364 | 1365 | public void printFieldStack(Class clz, int level, String prefix) { 1366 | for (Field f : clz.getDeclaredFields()) { 1367 | System.out.println(String.format("%" + (level * 4) + "s%s => %s", "", prefix + f.getName(), f.getType())); 1368 | if (f.getType() instanceof Class) printFieldStack(f.getType(), level + 1, prefix + f.getName() + "."); 1369 | } 1370 | } 1371 | 1372 | public T invokeWithMethodHandle(ANY instance, final int index, String type, V... args) { 1373 | try { 1374 | MethodHandle handle = getHandleWithIndex(index, type); 1375 | if (!type.equals(NEW)) handle = handle.bindTo(instance); 1376 | return (T) handle.invokeWithArguments(args); 1377 | } catch (Throwable e) { 1378 | e.printStackTrace(); 1379 | throw new IllegalArgumentException(e.getMessage()); 1380 | } 1381 | } 1382 | 1383 | final public T invokeWithIndex(ANY instance, final int methodIndex, V... args) { 1384 | Object[] arg = args; 1385 | if (classInfo.methodCount <= methodIndex) 1386 | throw new IllegalArgumentException("No such method index: " + methodIndex); 1387 | if (!IS_STRICT_CONVERT) arg = reArgs(METHOD, methodIndex, args); 1388 | instance = Modifier.isStatic(classInfo.methodModifiers[methodIndex]) ? null : instance; 1389 | if (isInvokeWithMethodHandle.get()) return invokeWithMethodHandle(instance, methodIndex, METHOD, arg); 1390 | return accessor.invokeWithIndex(instance, methodIndex, arg); 1391 | } 1392 | 1393 | final public T invoke(ANY instance, String methodName, V... args) { 1394 | final int index = indexOfMethod(null, methodName, args2Types(args)); 1395 | return invokeWithIndex(instance, index, args); 1396 | } 1397 | 1398 | final public T invokeWithTypes(ANY instance, String methodName, Class[] paramTypes, V... args) { 1399 | final int index = indexOfMethod(null, methodName, paramTypes); 1400 | return invokeWithIndex(instance, index, args); 1401 | } 1402 | 1403 | @Override 1404 | public ClassInfo getInfo() { 1405 | return classInfo; 1406 | } 1407 | 1408 | @Override 1409 | public MethodHandle[][] getMethodHandles() { 1410 | return new MethodHandle[0][]; 1411 | } 1412 | 1413 | final public ANY newInstance() { 1414 | if (isNonStaticMemberClass()) 1415 | throw new IllegalArgumentException("Cannot initialize a non-static inner class " + classInfo.baseClass.getCanonicalName() + " without specifing the enclosing instance!"); 1416 | return accessor.newInstance(); 1417 | } 1418 | 1419 | final public ANY newInstanceWithIndex(int constructorIndex, V... args) { 1420 | V[] arg = args; 1421 | if (!IS_STRICT_CONVERT) args = reArgs(NEW, constructorIndex, args); 1422 | if (isInvokeWithMethodHandle.get()) return invokeWithMethodHandle(null, constructorIndex, NEW, arg); 1423 | return accessor.newInstanceWithIndex(constructorIndex, args); 1424 | } 1425 | 1426 | final public ANY newInstanceWithTypes(Class[] paramTypes, T... args) { 1427 | return newInstanceWithIndex(indexOfMethod(null, NEW, paramTypes), args); 1428 | } 1429 | 1430 | final public ANY newInstance(Object... args) { 1431 | Integer index = indexOf(NEW, METHOD); 1432 | if (index == null) { 1433 | Class[] paramTypes = new Class[args.length]; 1434 | for (int i = 0; i < args.length; i++) paramTypes[i] = args[i] == null ? null : args[i].getClass(); 1435 | index = indexOfMethod(null, NEW, paramTypes); 1436 | } 1437 | return newInstanceWithIndex(index, args); 1438 | } 1439 | 1440 | final public void set(ANY instance, int fieldIndex, V value) { 1441 | instance = Modifier.isStatic(classInfo.fieldModifiers[fieldIndex]) ? null : instance; 1442 | if (!IS_STRICT_CONVERT) try { 1443 | Class clz = classInfo.fieldTypes[fieldIndex]; 1444 | if (isInvokeWithMethodHandle.get()) 1445 | invokeWithMethodHandle(instance, fieldIndex, SETTER, convert(value, clz)); 1446 | else accessor.set(instance, fieldIndex, convert(value, clz)); 1447 | return; 1448 | } catch (Exception e) { 1449 | throw new IllegalArgumentException(String.format("Unable to set field '%s.%s' as '%s': %s ", // 1450 | classInfo.baseClass.getName(), classInfo.fieldNames[fieldIndex], value == null ? "null" : value.getClass().getCanonicalName(), e.getMessage())); 1451 | } 1452 | if (isInvokeWithMethodHandle.get()) invokeWithMethodHandle(instance, fieldIndex, SETTER, value); 1453 | else accessor.set(instance, fieldIndex, value); 1454 | } 1455 | 1456 | public void set(ANY instance, String fieldName, T value) { 1457 | set(instance, indexOfField(fieldName), value); 1458 | } 1459 | 1460 | public void setBoolean(ANY instance, int fieldIndex, boolean value) { 1461 | set(instance, fieldIndex, value); 1462 | } 1463 | 1464 | public void setByte(ANY instance, int fieldIndex, byte value) { 1465 | set(instance, fieldIndex, value); 1466 | } 1467 | 1468 | public void setShort(ANY instance, int fieldIndex, short value) { 1469 | set(instance, fieldIndex, value); 1470 | } 1471 | 1472 | public void setInt(ANY instance, int fieldIndex, int value) { 1473 | set(instance, fieldIndex, value); 1474 | } 1475 | 1476 | public void setLong(ANY instance, int fieldIndex, long value) { 1477 | set(instance, fieldIndex, value); 1478 | } 1479 | 1480 | public void setDouble(ANY instance, int fieldIndex, double value) { 1481 | set(instance, fieldIndex, value); 1482 | } 1483 | 1484 | public void setFloat(ANY instance, int fieldIndex, float value) { 1485 | set(instance, fieldIndex, value); 1486 | } 1487 | 1488 | public void setChar(ANY instance, int fieldIndex, char value) { 1489 | set(instance, fieldIndex, value); 1490 | } 1491 | 1492 | public T get(ANY instance, int fieldIndex) { 1493 | if (classInfo.fieldCount <= fieldIndex) 1494 | throw new IllegalArgumentException("No such field index: " + fieldIndex); 1495 | if (isInvokeWithMethodHandle.get()) return invokeWithMethodHandle(instance, fieldIndex, GETTER); 1496 | return accessor.get(Modifier.isStatic(classInfo.fieldModifiers[fieldIndex]) ? null : instance, fieldIndex); 1497 | } 1498 | 1499 | public T get(ANY instance, String fieldName) { 1500 | return get(instance, indexOfField(fieldName)); 1501 | } 1502 | 1503 | public T get(ANY instance, int fieldIndex, Class clz) { 1504 | try { 1505 | return convert(get(instance, fieldIndex), clz); 1506 | } catch (Exception e) { 1507 | throw new IllegalArgumentException(String.format("Unable to set field '%s': %s", classInfo.fieldNames[fieldIndex], e.getMessage())); 1508 | } 1509 | } 1510 | 1511 | public T get(ANY instance, String fieldName, Class clz) { 1512 | return get(instance, indexOfField(fieldName), clz); 1513 | } 1514 | 1515 | public char getChar(ANY instance, int fieldIndex) { 1516 | return get(instance, fieldIndex, char.class); 1517 | } 1518 | 1519 | public boolean getBoolean(ANY instance, int fieldIndex) { 1520 | return get(instance, fieldIndex, boolean.class); 1521 | } 1522 | 1523 | public byte getByte(ANY instance, int fieldIndex) { 1524 | return get(instance, fieldIndex, byte.class); 1525 | } 1526 | 1527 | public short getShort(ANY instance, int fieldIndex) { 1528 | return get(instance, fieldIndex, short.class); 1529 | } 1530 | 1531 | public int getInt(ANY instance, int fieldIndex) { 1532 | return get(instance, fieldIndex, int.class); 1533 | } 1534 | 1535 | public long getLong(ANY instance, int fieldIndex) { 1536 | return get(instance, fieldIndex, long.class); 1537 | } 1538 | 1539 | public double getDouble(ANY instance, int fieldIndex) { 1540 | return get(instance, fieldIndex, double.class); 1541 | } 1542 | 1543 | public float getFloat(ANY instance, int fieldIndex) { 1544 | return get(instance, fieldIndex, float.class); 1545 | } 1546 | 1547 | public static class UnsafeHolder { 1548 | public static Unsafe theUnsafe = null; 1549 | 1550 | static { 1551 | try { 1552 | Field uf = Unsafe.class.getDeclaredField("theUnsafe"); 1553 | uf.setAccessible(true); 1554 | theUnsafe = (Unsafe) uf.get(null); 1555 | } catch (Exception e) { 1556 | //throw new AssertionError(e); 1557 | } 1558 | } 1559 | } 1560 | } -------------------------------------------------------------------------------- /src/com/esotericsoftware/reflectasm/ClassInfo.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm; 2 | 3 | /** 4 | * Created by Will on 2017/2/2. 5 | */ 6 | 7 | import java.util.Map; 8 | 9 | public class ClassInfo { 10 | public int bucket; 11 | public String[] fieldNames; 12 | public Class[] fieldTypes; 13 | public Integer[] fieldModifiers; 14 | public String[] methodNames; 15 | public Class[][] methodParamTypes; 16 | public Class[] returnTypes; 17 | public Integer[] methodModifiers; 18 | public Integer[] constructorModifiers; 19 | public Class[][] constructorParamTypes; 20 | public boolean isNonStaticMemberClass; 21 | public Class baseClass; 22 | public int methodCount; 23 | public int fieldCount; 24 | public int constructorCount; 25 | public Map attrIndex; 26 | public String[][] methodDescs; 27 | public String[][] fieldDescs; 28 | public String[] constructorDescs; 29 | } 30 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/reflectasm/ConstructorAccess.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm; 2 | 3 | import java.lang.reflect.Constructor; 4 | 5 | @SuppressWarnings({"UnusedDeclaration", "Convert2Diamond"}) 6 | public class ConstructorAccess { 7 | public final ClassAccess console; 8 | public final ClassInfo classInfo; 9 | 10 | protected ConstructorAccess(ClassAccess console) { 11 | this.console = console; 12 | this.classInfo = console.getInfo(); 13 | } 14 | 15 | static public ConstructorAccess access(Class type, String... dumpFile) { 16 | return new ConstructorAccess(ClassAccess.access(type, dumpFile)); 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return console.toString(); 22 | } 23 | 24 | public boolean isNonStaticMemberClass() { 25 | return console.isNonStaticMemberClass(); 26 | } 27 | 28 | public int getIndex(Class... paramTypes) { 29 | return console.indexOfMethod(null, ClassAccess.NEW, paramTypes); 30 | } 31 | 32 | public int getIndex(int paramCount) { 33 | return console.indexOfMethod(ClassAccess.NEW, paramCount); 34 | } 35 | 36 | public int getIndex(Constructor constructor) { 37 | return console.indexOfConstructor(constructor); 38 | } 39 | 40 | /** 41 | * Constructor for top-level classes and static nested classes. 42 | *

43 | * If the underlying class is a inner (non-static nested) class, a new instance will be created using null as the 44 | * this$0 synthetic reference. The instantiated object will work as long as it actually don't use any member variable or method 45 | * fron the enclosing instance. 46 | */ 47 | @SuppressWarnings("unchecked") 48 | public ANY newInstance() { 49 | return console.newInstance(); 50 | } 51 | 52 | public ANY newInstanceWithIndex(int constructorIndex, V... args) { 53 | return console.newInstanceWithIndex(constructorIndex, args); 54 | } 55 | 56 | public ANY newInstanceWithTypes(Class[] paramTypes, V... args) { 57 | return console.newInstanceWithTypes(paramTypes, args); 58 | } 59 | 60 | public ANY newInstance(V... args) { 61 | return console.newInstance(args); 62 | } 63 | 64 | public Class[][] getParameterTypes() { 65 | return classInfo.constructorParamTypes; 66 | } 67 | 68 | public Integer[] getModifiers() { 69 | return classInfo.constructorModifiers; 70 | } 71 | 72 | public int getConstructorCount() { 73 | return classInfo.constructorCount; 74 | } 75 | } -------------------------------------------------------------------------------- /src/com/esotericsoftware/reflectasm/FieldAccess.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm; 2 | 3 | @SuppressWarnings("UnusedDeclaration") 4 | public class FieldAccess { 5 | public final ClassAccess console; 6 | public final ClassInfo classInfo; 7 | 8 | protected FieldAccess(ClassAccess console) { 9 | this.console = console; 10 | this.classInfo = console.getInfo(); 11 | } 12 | 13 | static public FieldAccess access(Class type, String... dumpFile) { 14 | return new FieldAccess(ClassAccess.access(type, dumpFile)); 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | return console.toString(); 20 | } 21 | 22 | public int getIndex(String fieldName) { 23 | return console.indexOfField(fieldName); 24 | } 25 | 26 | public void set(ANY instance, String fieldName, V value) { 27 | console.set(instance, fieldName, value); 28 | } 29 | 30 | public T get(ANY instance, String fieldName) { 31 | return console.get(instance, fieldName); 32 | } 33 | 34 | public void set(ANY instance, int fieldIndex, V value) { 35 | console.set(instance, fieldIndex, value); 36 | } 37 | 38 | public void setBoolean(ANY instance, int fieldIndex, boolean value) { 39 | console.setBoolean(instance, fieldIndex, value); 40 | } 41 | 42 | public void setByte(ANY instance, int fieldIndex, byte value) { 43 | console.setByte(instance, fieldIndex, value); 44 | } 45 | 46 | public void setShort(ANY instance, int fieldIndex, short value) { 47 | console.setShort(instance, fieldIndex, value); 48 | } 49 | 50 | public void setInt(ANY instance, int fieldIndex, int value) { 51 | console.setInt(instance, fieldIndex, value); 52 | } 53 | 54 | public void setLong(ANY instance, int fieldIndex, long value) { 55 | console.setLong(instance, fieldIndex, value); 56 | } 57 | 58 | public void setDouble(ANY instance, int fieldIndex, double value) { 59 | console.setDouble(instance, fieldIndex, value); 60 | } 61 | 62 | public void setFloat(ANY instance, int fieldIndex, float value) { 63 | console.setFloat(instance, fieldIndex, value); 64 | } 65 | 66 | public void setChar(ANY instance, int fieldIndex, char value) { 67 | console.setChar(instance, fieldIndex, value); 68 | } 69 | 70 | public T get(ANY instance, int fieldIndex) { 71 | return console.get(instance, fieldIndex); 72 | } 73 | 74 | public T get(ANY instance, int fieldIndex, Class clz) { 75 | return console.get(instance, fieldIndex, clz); 76 | } 77 | 78 | public T get(ANY instance, String fieldName, Class clz) { 79 | return console.get(instance, fieldName, clz); 80 | } 81 | 82 | public char getChar(ANY instance, int fieldIndex) { 83 | return console.getChar(instance, fieldIndex); 84 | } 85 | 86 | public boolean getBoolean(ANY instance, int fieldIndex) { 87 | return console.getBoolean(instance, fieldIndex); 88 | } 89 | 90 | public byte getByte(ANY instance, int fieldIndex) { 91 | return console.getByte(instance, fieldIndex); 92 | } 93 | 94 | public short getShort(ANY instance, int fieldIndex) { 95 | return console.getShort(instance, fieldIndex); 96 | } 97 | 98 | public int getInt(ANY instance, int fieldIndex) { 99 | return console.getInt(instance, fieldIndex); 100 | } 101 | 102 | public long getLong(ANY instance, int fieldIndex) { 103 | return console.getLong(instance, fieldIndex); 104 | } 105 | 106 | public double getDouble(ANY instance, int fieldIndex) { 107 | return console.getDouble(instance, fieldIndex); 108 | } 109 | 110 | public float getFloat(ANY instance, int fieldIndex) { 111 | return console.getFloat(instance, fieldIndex); 112 | } 113 | 114 | public String getString(ANY instance, int fieldIndex) { 115 | return get(instance, fieldIndex); 116 | } 117 | 118 | public String[] getFieldNames() { 119 | return classInfo.fieldNames; 120 | } 121 | 122 | public Class[] getFieldTypes() { 123 | return classInfo.fieldTypes; 124 | } 125 | 126 | public int getFieldCount() { 127 | return classInfo.fieldCount; 128 | } 129 | } -------------------------------------------------------------------------------- /src/com/esotericsoftware/reflectasm/LambdaAccess.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm; 2 | 3 | import java.lang.invoke.CallSite; 4 | import java.lang.invoke.MethodHandle; 5 | 6 | /** 7 | * Created by Will on 2017/2/12. 8 | */ 9 | public class LambdaAccess extends ClassAccess { 10 | protected LambdaAccess(Accessor accessor) { 11 | super(accessor); 12 | } 13 | 14 | public CallSite getCallsite(int index, String type) { 15 | MethodHandle handle = getHandleWithIndex(index, type); 16 | return null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/reflectasm/MethodAccess.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | @SuppressWarnings("UnusedDeclaration") 6 | public class MethodAccess { 7 | public final ClassAccess console; 8 | public final ClassInfo classInfo; 9 | 10 | protected MethodAccess(ClassAccess console) { 11 | this.console = console; 12 | this.classInfo = console.getInfo(); 13 | } 14 | 15 | static public MethodAccess access(Class type, String... dumpFile) { 16 | return new MethodAccess(ClassAccess.access(type, dumpFile)); 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return console.toString(); 22 | } 23 | 24 | public T invokeWithIndex(ANY object, int methodIndex, V... args) { 25 | return console.invokeWithIndex(object, methodIndex, args); 26 | } 27 | 28 | /** 29 | * Invokes the method with the specified name and the specified param types. 30 | */ 31 | public T invokeWithTypes(ANY object, String methodName, Class[] paramTypes, V... args) { 32 | return console.invokeWithTypes(object, methodName, paramTypes, args); 33 | } 34 | 35 | /** 36 | * Invokes the first method with the specified name and the specified number of arguments. 37 | */ 38 | public T invoke(ANY object, String methodName, V... args) { 39 | return console.invoke(object, methodName, args); 40 | } 41 | 42 | /** 43 | * Returns the index of the first method with the specified name. 44 | */ 45 | public int getIndex(String methodName) { 46 | return console.indexOfMethod(methodName); 47 | } 48 | 49 | public int getIndex(Method method) { 50 | return console.indexOfMethod(method); 51 | } 52 | 53 | /** 54 | * Returns the index of the first method with the specified name and param types. 55 | */ 56 | public int getIndex(String methodName, Class... paramTypes) { 57 | return console.indexOfMethod(null, methodName, paramTypes); 58 | } 59 | 60 | /** 61 | * Returns the index of the first method with the specified name and the specified number of arguments. 62 | */ 63 | public int getIndex(String methodName, int paramsCount) { 64 | return console.indexOfMethod(methodName, paramsCount); 65 | } 66 | 67 | public String[] getMethodNames() { 68 | return classInfo.methodNames; 69 | } 70 | 71 | public Class[][] getParameterTypes() { 72 | return classInfo.methodParamTypes; 73 | } 74 | 75 | public Class[] getReturnTypes() { 76 | return classInfo.returnTypes; 77 | } 78 | 79 | public Integer[] getModifiers() { 80 | return classInfo.constructorModifiers; 81 | } 82 | 83 | public int getMethodCount() { 84 | return classInfo.methodCount; 85 | } 86 | } -------------------------------------------------------------------------------- /src/com/esotericsoftware/reflectasm/util/NumberUtils.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm.util; 2 | 3 | /* 4 | * Copyright 2002-2016 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 | * http://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 | import java.lang.reflect.Array; 20 | import java.math.BigDecimal; 21 | import java.math.BigInteger; 22 | import java.util.*; 23 | 24 | /** 25 | * Miscellaneous utility methods for number conversion and parsing. 26 | *

Mainly for internal use within the framework; consider Apache's 27 | * Commons Lang for a more comprehensive suite of number utilities. 28 | * 29 | * @author Juergen Hoeller 30 | * @author Rob Harrop 31 | * @since 1.1.2 32 | */ 33 | public abstract class NumberUtils { 34 | /** 35 | * Standard number types (all immutable): 36 | * Byte, Short, Integer, Long, BigInteger, Float, Double, BigDecimal. 37 | */ 38 | public static final Set> STANDARD_NUMBER_TYPES; 39 | public static final Map> namePrimitiveMap = new HashMap<>(); 40 | private static final BigInteger LONG_MIN = BigInteger.valueOf(Long.MIN_VALUE); 41 | private static final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE); 42 | 43 | static { 44 | namePrimitiveMap.put("boolean", Boolean.class); 45 | namePrimitiveMap.put("byte", Byte.class); 46 | namePrimitiveMap.put("char", Character.class); 47 | namePrimitiveMap.put("short", Short.class); 48 | namePrimitiveMap.put("int", Integer.class); 49 | namePrimitiveMap.put("long", Long.class); 50 | namePrimitiveMap.put("double", Double.class); 51 | namePrimitiveMap.put("float", Float.class); 52 | namePrimitiveMap.put("void", Void.class); 53 | 54 | Set> numberTypes = new HashSet<>(8); 55 | numberTypes.add(Byte.class); 56 | numberTypes.add(Byte.TYPE); 57 | numberTypes.add(Short.class); 58 | numberTypes.add(Short.TYPE); 59 | numberTypes.add(Integer.class); 60 | numberTypes.add(Integer.TYPE); 61 | numberTypes.add(Long.class); 62 | numberTypes.add(Long.TYPE); 63 | numberTypes.add(BigInteger.class); 64 | numberTypes.add(Float.class); 65 | numberTypes.add(Float.TYPE); 66 | numberTypes.add(Double.class); 67 | numberTypes.add(Double.TYPE); 68 | numberTypes.add(BigDecimal.class); 69 | STANDARD_NUMBER_TYPES = Collections.unmodifiableSet(numberTypes); 70 | } 71 | 72 | private static T convertOrGetDistance(V from, Class toClass, final boolean isGetDistance) { 73 | //return (T) (isGetDistance ? Integer.valueOf(5) : from); 74 | if (toClass == null) return (T) (isGetDistance ? 5 : null); 75 | if (from == null) return (T) (isGetDistance ? (toClass.isPrimitive() ? -1 : 5) : null); 76 | Class clz; 77 | boolean isClass = false; 78 | 79 | if (!(from instanceof Class)) clz = from.getClass(); 80 | else { 81 | clz = (Class) from; 82 | isClass = true; 83 | } 84 | 85 | if (clz == String.class && toClass == byte[].class) 86 | return (T) (isGetDistance ? 5 : isClass ? toClass : ((String) from).getBytes()); 87 | 88 | if (clz == byte[].class && toClass == String.class) 89 | return (T) (isGetDistance ? 5 : isClass ? toClass : new String((byte[]) from)); 90 | 91 | if (clz == toClass || toClass.isAssignableFrom(clz)) return (T) (isGetDistance ? 5 : from); 92 | if (toClass.isArray() && clz.isArray()) { 93 | int distance = 5; 94 | toClass = toClass.getComponentType(); 95 | if (isClass) return convertOrGetDistance(clz.getComponentType(), toClass, isGetDistance); 96 | Object objects = isGetDistance ? null : Array.newInstance(toClass, Array.getLength(from)); 97 | for (int i = 0; i < Array.getLength(from); i++) { 98 | if (isGetDistance) 99 | distance = Math.min(distance, (int) convertOrGetDistance(Array.get(from, i), toClass, true)); 100 | else Array.set(objects, i, convertOrGetDistance(Array.get(from, i), toClass, false)); 101 | } 102 | return (T) (isGetDistance ? distance : objects); 103 | } 104 | if (toClass == String.class) return (T) (isGetDistance ? 2 : isClass ? toClass : String.valueOf(from)); 105 | if (STANDARD_NUMBER_TYPES.contains(toClass)) { 106 | Class to = (Class) toClass; 107 | if (STANDARD_NUMBER_TYPES.contains(clz)) { 108 | return (T) (isGetDistance ? 4 : isClass ? toClass : convertNumberToTargetClass((Number) from, to)); 109 | } 110 | if (clz == String.class) return (T) (isGetDistance ? 1 : isClass ? clz : parseNumber((String) from, to)); 111 | if (clz == Character.class || clz == char.class) 112 | return (T) (isGetDistance ? 3 : isClass ? toClass : convertNumberToTargetClass((int) ((Character) from).charValue(), to)); 113 | } 114 | if ((toClass == Character.class || toClass == char.class)) { 115 | if (STANDARD_NUMBER_TYPES.contains(clz)) 116 | return (T) (isGetDistance ? 3 : isClass ? toClass : Character.valueOf((char) ((Number) from).intValue())); 117 | if (clz == String.class) { 118 | if (isClass) return (T) (isGetDistance ? 3 : isClass ? clz : toClass); 119 | if (((String) from).length() == 1) 120 | return (T) (isGetDistance ? 3 : isClass ? toClass : ((String) from).charAt(0)); 121 | } 122 | } 123 | if (namePrimitiveMap.containsKey(toClass.getName()) && namePrimitiveMap.get(toClass.getName()) == clz) { 124 | return (T) (isGetDistance ? 5 : isClass ? toClass : from); 125 | } 126 | if (namePrimitiveMap.containsKey(clz.getName()) && namePrimitiveMap.get(clz.getName()) == toClass) { 127 | return (T) (isGetDistance ? 5 : isClass ? toClass : from); 128 | } 129 | return (T) (isGetDistance ? 0 : isClass ? clz : toClass.cast(from)); 130 | } 131 | 132 | public static T convert(V from, Class toClass) { 133 | return convertOrGetDistance(from, toClass, false); 134 | } 135 | 136 | public static int getDistance(V from, Class toClass) { 137 | return (Integer) convertOrGetDistance(from, toClass, true); 138 | } 139 | 140 | /** 141 | * Convert the given number into an instance of the given target class. 142 | * 143 | * @param number the number to convert 144 | * @param targetClass the target class to convert to 145 | * @return the converted number 146 | * @throws IllegalArgumentException if the target class is not supported 147 | * (i.e. not a standard Number subclass as included in the JDK) 148 | * @see Byte 149 | * @see Short 150 | * @see Integer 151 | * @see Long 152 | * @see BigInteger 153 | * @see Float 154 | * @see Double 155 | * @see BigDecimal 156 | */ 157 | @SuppressWarnings("unchecked") 158 | public static T convertNumberToTargetClass(Number number, Class targetClass) throws IllegalArgumentException { 159 | if (number == null) return null; 160 | Number to = null; 161 | if (targetClass.isInstance(number)) { 162 | return (T) number; 163 | } 164 | Class clz = targetClass; 165 | if (clz.isPrimitive() && STANDARD_NUMBER_TYPES.contains(clz)) clz = namePrimitiveMap.get(clz.getName()); 166 | if (Byte.class == clz) { 167 | long value = checkedLongValue(number, clz); 168 | if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) { 169 | raiseOverflowException(number, clz); 170 | } 171 | to = Byte.valueOf(number.byteValue()); 172 | } else if (Short.class == clz) { 173 | long value = checkedLongValue(number, clz); 174 | if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { 175 | raiseOverflowException(number, clz); 176 | } 177 | to = Short.valueOf(number.shortValue()); 178 | } else if (Integer.class == clz) { 179 | long value = checkedLongValue(number, clz); 180 | if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) { 181 | raiseOverflowException(number, clz); 182 | } 183 | to = Integer.valueOf(number.intValue()); 184 | } else if (Long.class == clz) { 185 | long value = checkedLongValue(number, clz); 186 | to = Long.valueOf(value); 187 | } else if (BigInteger.class == clz) { 188 | if (number instanceof BigDecimal) { 189 | // do not lose precision - use BigDecimal's own conversion 190 | to = ((BigDecimal) number).toBigInteger(); 191 | } else { 192 | // original value is not a Big* number - use standard long conversion 193 | to = BigInteger.valueOf(number.longValue()); 194 | } 195 | } else if (Float.class == clz) { 196 | to = Float.valueOf(number.floatValue()); 197 | } else if (Double.class == clz) { 198 | to = Double.valueOf(number.doubleValue()); 199 | } else if (BigDecimal.class == clz) { 200 | // always use BigDecimal(String) here to avoid unpredictability of BigDecimal(double) 201 | // (see BigDecimal javadoc for details) 202 | to = new BigDecimal(number.toString()); 203 | } 204 | if (to == null) { 205 | throw new IllegalArgumentException("Could not convert number [" + number + "] of type [" + number.getClass().getName() + "] to unsupported target class [" + clz.getName() + "]"); 206 | } 207 | return (T) to; 208 | } 209 | 210 | /** 211 | * Check for a {@code BigInteger}/{@code BigDecimal} long overflow 212 | * before returning the given number as a long value. 213 | * 214 | * @param number the number to convert 215 | * @param targetClass the target class to convert to 216 | * @return the long value, if convertible without overflow 217 | * @throws IllegalArgumentException if there is an overflow 218 | * @see #raiseOverflowException 219 | */ 220 | private static long checkedLongValue(Number number, Class targetClass) { 221 | BigInteger bigInt = null; 222 | if (number instanceof BigInteger) { 223 | bigInt = (BigInteger) number; 224 | } else if (number instanceof BigDecimal) { 225 | bigInt = ((BigDecimal) number).toBigInteger(); 226 | } 227 | // Effectively analogous to JDK 8's BigInteger.longValueExact() 228 | if (bigInt != null && (bigInt.compareTo(LONG_MIN) < 0 || bigInt.compareTo(LONG_MAX) > 0)) { 229 | raiseOverflowException(number, targetClass); 230 | } 231 | return number.longValue(); 232 | } 233 | 234 | /** 235 | * Raise an overflow exception for the given number and target class. 236 | * 237 | * @param number the number we tried to convert 238 | * @param targetClass the target class we tried to convert to 239 | * @throws IllegalArgumentException if there is an overflow 240 | */ 241 | private static void raiseOverflowException(Number number, Class targetClass) { 242 | throw new IllegalArgumentException("Could not convert number [" + number + "] of type [" + number.getClass().getName() + "] to target class [" + targetClass.getName() + "]: overflow"); 243 | } 244 | 245 | /** 246 | * Parse the given {@code text} into a {@link Number} instance of the given 247 | * target class, using the corresponding {@code decode} / {@code valueOf} method. 248 | *

Trims the input {@code String} before attempting to parse the number. 249 | *

Supports numbers in hex format (with leading "0x", "0X", or "#") as well. 250 | * 251 | * @param text the text to convert 252 | * @param targetClass the target class to parse into 253 | * @return the parsed number 254 | * @throws IllegalArgumentException if the target class is not supported 255 | * (i.e. not a standard Number subclass as included in the JDK) 256 | * @see Byte#decode 257 | * @see Short#decode 258 | * @see Integer#decode 259 | * @see Long#decode 260 | * @see #decodeBigInteger(String) 261 | * @see Float#valueOf 262 | * @see Double#valueOf 263 | * @see BigDecimal#BigDecimal(String) 264 | */ 265 | @SuppressWarnings("unchecked") 266 | public static T parseNumber(String text, Class targetClass) { 267 | if (text == null) return null; 268 | String trimmed = text.replace(" ", ""); 269 | if (trimmed.equals("")) return null; 270 | Class clz = targetClass; 271 | if (clz.isPrimitive()) clz = namePrimitiveMap.get(clz.getName()); 272 | if (Byte.class == clz) { 273 | return (T) (isHexNumber(trimmed) ? Byte.decode(trimmed) : Byte.valueOf(trimmed)); 274 | } else if (Short.class == clz) { 275 | return (T) (isHexNumber(trimmed) ? Short.decode(trimmed) : Short.valueOf(trimmed)); 276 | } else if (Integer.class == clz) { 277 | return (T) (isHexNumber(trimmed) ? Integer.decode(trimmed) : Integer.valueOf(trimmed)); 278 | } else if (Long.class == clz) { 279 | return (T) (isHexNumber(trimmed) ? Long.decode(trimmed) : Long.valueOf(trimmed)); 280 | } else if (BigInteger.class == clz) { 281 | return (T) (isHexNumber(trimmed) ? decodeBigInteger(trimmed) : new BigInteger(trimmed)); 282 | } else if (Float.class == clz) { 283 | return (T) Float.valueOf(trimmed); 284 | } else if (Double.class == clz) { 285 | return (T) Double.valueOf(trimmed); 286 | } else if (BigDecimal.class == clz || Number.class == clz) { 287 | return (T) new BigDecimal(trimmed); 288 | } else { 289 | throw new IllegalArgumentException("Cannot convert String [" + text + "] to target class [" + clz.getName() + "]"); 290 | } 291 | } 292 | 293 | /** 294 | * Determine whether the given {@code value} String indicates a hex number, 295 | * i.e. needs to be passed into {@code Integer.decode} instead of 296 | * {@code Integer.valueOf}, etc. 297 | */ 298 | private static boolean isHexNumber(String value) { 299 | int index = (value.startsWith("-") ? 1 : 0); 300 | return (value.startsWith("0x", index) || value.startsWith("0X", index) || value.startsWith("#", index)); 301 | } 302 | 303 | /** 304 | * Decode a {@link BigInteger} from the supplied {@link String} value. 305 | *

Supports decimal, hex, and octal notation. 306 | * 307 | * @see BigInteger#BigInteger(String, int) 308 | */ 309 | private static BigInteger decodeBigInteger(String value) { 310 | int radix = 10; 311 | int index = 0; 312 | boolean negative = false; 313 | 314 | // Handle minus sign, if present. 315 | if (value.startsWith("-")) { 316 | negative = true; 317 | index++; 318 | } 319 | 320 | // Handle radix specifier, if present. 321 | if (value.startsWith("0x", index) || value.startsWith("0X", index)) { 322 | index += 2; 323 | radix = 16; 324 | } else if (value.startsWith("#", index)) { 325 | index++; 326 | radix = 16; 327 | } else if (value.startsWith("0", index) && value.length() > 1 + index) { 328 | index++; 329 | radix = 8; 330 | } 331 | 332 | BigInteger result = new BigInteger(value.substring(index), radix); 333 | return (negative ? result.negate() : result); 334 | } 335 | } -------------------------------------------------------------------------------- /src/com/esotericsoftware/reflectasm/util/Probe.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm.util; 2 | 3 | import java.util.*; 4 | import java.util.concurrent.locks.ReentrantLock; 5 | 6 | public class Probe { 7 | public static Probe p = new Probe(); 8 | static Comparator comparator = new Comparator() { 9 | public int compare(Object[] o1, Object[] o2) { 10 | return ((Long) ((Long) o2[1] - (Long) o1[1])).intValue(); 11 | } 12 | }; 13 | public boolean warmup = true; 14 | public Map times = new HashMap(10); 15 | private long s; 16 | private volatile long e; 17 | private String prevTag; 18 | private volatile boolean isRunning = false; 19 | private Thread thread = null; 20 | private long duration; 21 | private ArrayDeque queue = new ArrayDeque(); 22 | private ReentrantLock lock = new ReentrantLock(); 23 | 24 | public static void mark_(String tag) {p.mark(tag);} 25 | 26 | public static void clear_() {p.clear();} 27 | 28 | public static void end_() {p.end();} 29 | 30 | public static void print_() {p.print();} 31 | 32 | public static void reset_() {p.reset();} 33 | 34 | public void mark(String tag) { 35 | if (warmup) return; 36 | if (prevTag != null) { 37 | Long time = e - s; 38 | String name = prevTag + " -> " + (tag == null ? "" : tag); 39 | //lock.lock(); 40 | Long[] oldTime = times.get(name); 41 | if (oldTime == null) times.put(name, new Long[]{time, 1L, Integer.valueOf(times.size()).longValue()}); 42 | else { 43 | oldTime[0] += time; 44 | ++oldTime[1]; 45 | } 46 | //lock.unlock(); 47 | } 48 | prevTag = tag == null ? "" : tag; 49 | s = e; 50 | } 51 | 52 | public void reset() { 53 | mark(null); 54 | if (thread == null && !warmup) { 55 | duration = System.nanoTime(); 56 | e = 0L; 57 | isRunning = true; 58 | thread = new Thread(new Runnable() { 59 | @Override 60 | public void run() { 61 | while (isRunning) { 62 | try { 63 | Thread.sleep(0, 10); 64 | } catch (InterruptedException e1) { 65 | e1.printStackTrace(); 66 | } 67 | e = System.nanoTime(); 68 | } 69 | } 70 | }); 71 | thread.setDaemon(true); 72 | thread.start(); 73 | 74 | try { 75 | while (e == 0L) Thread.sleep(1L); 76 | } catch (InterruptedException e) { 77 | thread = null; 78 | } 79 | s = e; 80 | } 81 | } 82 | 83 | public void end() { 84 | if (isRunning) { 85 | isRunning = false; 86 | if (thread != null && thread.isAlive()) try { 87 | thread.join(); 88 | } catch (InterruptedException e) {} 89 | thread = null; 90 | duration = e - duration; 91 | } 92 | } 93 | 94 | public void clear() { 95 | queue.clear(); 96 | end(); 97 | if (warmup) return; 98 | times.clear(); 99 | prevTag = null; 100 | } 101 | 102 | public void print() { 103 | end(); 104 | String[] keys = times.keySet().toArray(new String[]{}); 105 | Object[][] o = new Object[keys.length][]; 106 | Long totalTime = 0L; 107 | int maxsize = 0; 108 | for (int i = 0, n = o.length; i < n; i++) { 109 | Long[] item = times.get(keys[i]); 110 | o[i] = new Object[]{keys[i], item[0], item[1], item[2]}; 111 | totalTime += item[0]; 112 | maxsize = Math.max(maxsize, keys[i].length()); 113 | } 114 | Arrays.sort(o, comparator); 115 | StringBuilder sb = new StringBuilder(1024); 116 | for (Object[] i : o) { 117 | sb.append(String.format("|%4d:%-" + (maxsize + 1) + "s | %10.3f ms | %2.2f%% | %10d times |\n",// 118 | i[3], i[0], ((Long) i[1]) / (Double) 1e6, 100 * ((Long) i[1]) / totalTime.doubleValue(), ((Long) i[2]).intValue())); 119 | } 120 | System.out.println(sb.toString()); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/ClassAccessTest.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm; 2 | 3 | import junit.framework.TestCase; 4 | import test.Many; 5 | import test.TestObject; 6 | 7 | import java.util.concurrent.CountDownLatch; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Executors; 10 | 11 | /** 12 | * Created by Will on 2017/2/6. 13 | */ 14 | public class ClassAccessTest extends TestCase { 15 | public void testBigClass() throws ClassNotFoundException { 16 | Class clz = Class.forName("oracle.jdbc.driver.T4CConnection"); 17 | ClassAccess.access(clz, "."); 18 | } 19 | 20 | public void testCase1() { 21 | //Generic style 22 | ClassAccess.access(TestObject.class, "."); 23 | ClassAccess access0 = ClassAccess.access(Many.class); 24 | Many many = access0.newInstance(); 25 | access0.set(many, "x295", 123); 26 | int value = access0.get(many, "x295"); 27 | assertEquals(123, value); 28 | 29 | //Non-generic style 30 | ClassAccess access5 = ClassAccess.access(Many.class); 31 | Object many1 = access5.newInstance(); 32 | access5.set(many1, "x295", 456); 33 | value = (int) access5.get(many1, "x295"); 34 | assertEquals(456, value); 35 | 36 | //Cast back to generic style 37 | ClassAccess access6 = access5; 38 | Many many2 = (Many) many1; 39 | access6.set(many2, "x295", 789); 40 | value = access6.get(many2, "x295"); 41 | assertEquals(789, value); 42 | 43 | ClassAccess access = ClassAccess.access(BaseClass.class, ".", ""); 44 | ClassAccess access1 = ClassAccess.access(BaseClass.Inner.class); 45 | try { 46 | BaseClass instance = access.newInstance(); 47 | fail(); 48 | } catch (IllegalArgumentException e) {} 49 | BaseClass instance = access.newInstance(this); 50 | instance.test0(); 51 | BaseClass.Inner inner = (BaseClass.Inner) access.invoke(instance, "newInner"); 52 | access1.newInstance(instance); 53 | access = ClassAccess.access(BaseClass.StaticInner.class); 54 | access.newInstance(instance); 55 | access = ClassAccess.access(BaseClass.Inner.DeeperInner.class); 56 | access.newInstance(inner); 57 | } 58 | 59 | public void testOverload0(ClassAccess... accesses) { 60 | ClassAccess access1 = ClassAccess.access(BaseClass.class); 61 | ClassAccess access2; 62 | if (accesses.length == 0) access2 = ClassAccess.access(ChildClass.class, "."); 63 | else access2 = accesses[0]; 64 | 65 | ChildClass child = (ChildClass) access2.newInstance(this); 66 | assertEquals("test10", access2.invoke(child, "test0")); 67 | int index = access2.indexOfMethod("test1"); 68 | assertEquals("test11", access2.invoke(child, "test1")); 69 | assertEquals("test02", access2.invoke(child, "test2")); 70 | assertEquals("test01", access1.invoke(child, "test1")); 71 | assertEquals(1, (int) access1.get(child, "x")); 72 | assertEquals(3, access2.get(child, "x")); 73 | assertEquals(4, access2.get(child, "y")); 74 | assertEquals(5, access2.get(child, "z")); 75 | //assertEquals(6, access2.get(child, "o")); 76 | //Invoke the overloaded parts of BaseClass 77 | int fieldIndex = access2.indexOfField(BaseClass.class, "x"); 78 | int methodIndex = access2.indexOfMethod(BaseClass.class, "test1"); 79 | assertEquals("test01", access2.invokeWithIndex(child, methodIndex)); 80 | assertEquals(1, access2.get(child, fieldIndex)); 81 | if ((Boolean) access2.isInvokeWithMethodHandle.get() == false) { 82 | access2.set(child, fieldIndex, 9); 83 | assertEquals(9, access2.get(child, fieldIndex)); 84 | assertEquals(3, access2.get(child, "x")); 85 | assertEquals(6, access2.get(child, "o")); 86 | } 87 | } 88 | 89 | public void testOverload() { 90 | testOverload0(); 91 | } 92 | 93 | public void testOverloadWithLambda() throws Throwable { 94 | ClassAccess access2 = ClassAccess.access(ChildClass.class, "."); 95 | access2.isInvokeWithMethodHandle.set(true); 96 | testOverload0(access2); 97 | } 98 | 99 | public void testCase2() throws InterruptedException, IllegalAccessException, InstantiationException, ClassNotFoundException { 100 | final int count = 100; 101 | final int rounds = 300; 102 | ExecutorService pool = Executors.newFixedThreadPool(count); 103 | final CountDownLatch latch = new CountDownLatch(count); 104 | Runnable R = new Runnable() { 105 | @Override 106 | public void run() { 107 | for (int i = 0; i < rounds; i++) ClassAccess.access(Many.class).newInstance(); 108 | latch.countDown(); 109 | } 110 | }; 111 | Long s = System.nanoTime(); 112 | for (int i = 0; i < count; i++) { 113 | pool.submit(R); 114 | } 115 | 116 | latch.await(); 117 | assertEquals(1, ClassAccess.activeAccessClassLoaders()); 118 | System.out.println(ClassAccess.totalAccesses + " invokes from ClassAccess.access() and " + ClassAccess.cacheHits + " hits from cache"); 119 | System.out.println("Creating " + (count * rounds) + " same proxies with parallel 100 takes " + String.format("%.3f", (System.nanoTime() - s) / 1e6) + " ms."); 120 | 121 | ClassAccess.totalAccesses = 0; 122 | ClassAccess.cacheHits = 0; 123 | s = System.nanoTime(); 124 | for (int i = 0; i < rounds * count; i++) ClassAccess.access(Many.class).newInstance(); 125 | System.out.println(ClassAccess.totalAccesses + " invokes from ClassAccess.access() and " + ClassAccess.cacheHits + " hits from cache"); 126 | System.out.println("Creating " + (count * rounds) + " same proxies#1 in serial mode takes " + String.format("%.3f", (System.nanoTime() - s) / 1e6) + " ms."); 127 | 128 | ClassAccess.totalAccesses = 0; 129 | ClassAccess.cacheHits = 0; 130 | ClassAccess.IS_CACHED = false; 131 | s = System.nanoTime(); 132 | for (int i = 0; i < rounds * count; i++) ClassAccess.access(Many.class).newInstance(); 133 | System.out.println(ClassAccess.totalAccesses + " invokes from ClassAccess.access() and " + ClassAccess.loaderHits + " hits from loader"); 134 | System.out.println("Creating " + (count * rounds) + " same proxies#1 in serial mode takes " + String.format("%.3f", (System.nanoTime() - s) / 1e6) + " ms."); 135 | 136 | ClassAccess.totalAccesses = 0; 137 | ClassAccess.cacheHits = 0; 138 | ClassAccess.IS_CACHED = true; 139 | s = System.nanoTime(); 140 | for (int i = 0; i < rounds * count; i++) { 141 | ClassLoader testClassLoader = new ClassLoaderTest.TestClassLoader1(); 142 | Class testClass = testClassLoader.loadClass(Many.class.getName()); 143 | ClassAccess access = ClassAccess.access(Many.class); 144 | Many many = access.newInstance(); 145 | access.set(many, "x1", i); 146 | } 147 | System.out.println(ClassAccess.totalAccesses + " invokes from ClassAccess.access() and " + ClassAccess.cacheHits + " hits from cache"); 148 | System.out.println("Creating " + (count * rounds) + " same proxies#2 in serial mode takes " + String.format("%.3f", (System.nanoTime() - s) / 1e6) + " ms."); 149 | } 150 | 151 | public void testCase3() throws Exception { 152 | { 153 | ClassAccess access = ClassAccess.access(TestObject.class); 154 | TestObject obj; 155 | // Construction 156 | obj = access.newInstance(); 157 | obj = access.newInstance(1, 2, 3, 4); 158 | 159 | // Set+Get field 160 | access.set(null, "fs", 1); // static field 161 | System.out.println((String) access.get(null, "fs")); 162 | 163 | access.set(obj, "fd", 2); 164 | System.out.println(access.get(obj, "fd").toString()); 165 | 166 | // Method invoke 167 | access.invoke(null, "func1", "a"); //static call 168 | System.out.println((String) access.invoke(obj, "func2", 1, 2, 3, 4)); 169 | } 170 | 171 | { 172 | ClassAccess access = ClassAccess.access(TestObject.class); 173 | TestObject obj; 174 | //Identify the indexes for further use 175 | int newIndex = access.indexOfConstructor(int.class, Double.class, String.class, long.class); 176 | int fieldIndex = access.indexOfField("fd"); 177 | int methodIndex = access.indexOfMethod("func1", String.class); 178 | //Now use the index to access object in loop or other part 179 | for (int i = 0; i < 100; i++) { 180 | obj = access.newInstanceWithIndex(newIndex, 1, 2, 3, 4); 181 | access.set(obj, fieldIndex, 123); 182 | String result = access.invokeWithIndex(null, methodIndex, "x"); 183 | } 184 | } 185 | 186 | { 187 | ClassAccess access = ClassAccess.access(TestObject.class); 188 | TestObject obj; 189 | //Identify the indexes for further use 190 | int newIndex = access.indexOfConstructor(int.class, Double.class, String.class, long.class); 191 | int fieldIndex = access.indexOfField("fd"); 192 | int methodIndex = access.indexOfMethod("func1", String.class); 193 | //Now use the index to access object in loop or other part 194 | for (int i = 0; i < 100; i++) { 195 | obj = access.accessor.newInstanceWithIndex(newIndex, 1, Double.valueOf(2), "3", 4L); 196 | access.accessor.set(obj, fieldIndex, Double.valueOf(123)); 197 | String result = access.accessor.invokeWithIndex(null, methodIndex, "x"); 198 | } 199 | } 200 | } 201 | 202 | interface BaseInterface { 203 | int o = 6; 204 | 205 | String test0(); 206 | } 207 | 208 | class BaseClass implements BaseInterface { 209 | public int x = 1; 210 | int y = 2; 211 | int z = 5; 212 | 213 | public String test0() {return "test00";} 214 | 215 | private String test1() {return "test01";} 216 | 217 | private String test2() {return "test02";} 218 | 219 | Inner newInner() { 220 | return new Inner(); 221 | } 222 | 223 | class Inner { 224 | class DeeperInner {} 225 | } 226 | 227 | class StaticInner {} 228 | } 229 | 230 | class ChildClass extends BaseClass { 231 | public int x = 3; 232 | int y = 4; 233 | 234 | public String test0() {return "test10";} 235 | 236 | private String test1() { 237 | return "test11"; 238 | } 239 | } 240 | } 241 | 242 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/ClassLoaderTest.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm; 2 | 3 | import junit.framework.TestCase; 4 | 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | 9 | public class ClassLoaderTest extends TestCase { 10 | public void testDifferentClassloaders() throws Exception { 11 | ClassLoader testClassLoader = new TestClassLoader1(); 12 | Class testClass = testClassLoader.loadClass("com.esotericsoftware.reflectasm.ClassLoaderTest$Test"); 13 | Object testObject = testClass.newInstance(); 14 | 15 | // Ensure AccessClassLoader can access both the Test class and FieldAccess. 16 | FieldAccess access = FieldAccess.access(testObject.getClass()); 17 | access.set(testObject, "name", "first"); 18 | assertEquals("first", testObject.toString()); 19 | assertEquals("first", access.get(testObject, "name")); 20 | } 21 | 22 | public void testAutoUnloadClassloaders() throws Exception { 23 | int initialCount = AccessClassLoader.activeAccessClassLoaders(); 24 | ClassLoader testClassLoader1 = new TestClassLoader1(); 25 | Class testClass1 = testClassLoader1.loadClass("com.esotericsoftware.reflectasm.ClassLoaderTest$Test"); 26 | Object testObject1 = testClass1.newInstance(); 27 | FieldAccess access1 = FieldAccess.access(testObject1.getClass()); 28 | access1.set(testObject1, "name", "first"); 29 | assertEquals("first", testObject1.toString()); 30 | assertEquals("first", access1.get(testObject1, "name")); 31 | 32 | ClassLoader testClassLoader2 = new TestClassLoader2(); 33 | Class testClass2 = testClassLoader1.loadClass("com.esotericsoftware.reflectasm.ClassLoaderTest$Test"); 34 | Object testObject2 = testClass2.newInstance(); 35 | FieldAccess access2 = FieldAccess.access(testObject2.getClass()); 36 | access2.set(testObject2, "name", "second"); 37 | assertEquals("second", testObject2.toString()); 38 | assertEquals("second", access2.get(testObject2, "name")); 39 | 40 | assertEquals(access1.console.getClass().toString(), access2.console.getClass().toString()); // Same class names 41 | assertTrue(access1.console.accessor.getClass().equals(access2.console.accessor.getClass())); // But different classes 42 | 43 | assertEquals(true, (initialCount + 2 >= AccessClassLoader.activeAccessClassLoaders())); 44 | 45 | testClassLoader1 = null; 46 | testClass1 = null; 47 | testObject1 = null; 48 | access1 = null; 49 | testClassLoader2 = null; 50 | testClass2 = null; 51 | testObject2 = null; 52 | access2 = null; 53 | 54 | // Force GC to reclaim unreachable (or only weak-reachable) objects 55 | System.gc(); 56 | try { 57 | Object[] array = new Object[(int) Runtime.getRuntime().maxMemory()]; 58 | System.out.println(array.length); 59 | } catch (Throwable e) { 60 | // Ignore OME 61 | } 62 | System.gc(); 63 | int times = 0; 64 | while (AccessClassLoader.activeAccessClassLoaders() > 1 && times < 50) { // max 5 seconds, should be instant 65 | Thread.sleep(100); // test again 66 | times++; 67 | } 68 | // Yeah, both reclaimed! 69 | assertEquals(1, AccessClassLoader.activeAccessClassLoaders()); 70 | } 71 | 72 | public void testRemoveClassloaders() throws Exception { 73 | int initialCount = AccessClassLoader.activeAccessClassLoaders(); 74 | 75 | ClassLoader testClassLoader1 = new TestClassLoader1(); 76 | Class testClass1 = testClassLoader1.loadClass("com.esotericsoftware.reflectasm.ClassLoaderTest$Test"); 77 | Object testObject1 = testClass1.newInstance(); 78 | FieldAccess access1 = FieldAccess.access(testObject1.getClass()); 79 | access1.set(testObject1, "name", "first"); 80 | assertEquals("first", testObject1.toString()); 81 | assertEquals("first", access1.get(testObject1, "name")); 82 | 83 | ClassLoader testClassLoader2 = new TestClassLoader2(); 84 | Class testClass2 = testClassLoader2.loadClass("com.esotericsoftware.reflectasm.ClassLoaderTest$Test"); 85 | Object testObject2 = testClass2.newInstance(); 86 | FieldAccess access2 = FieldAccess.access(testObject2.getClass()); 87 | access2.set(testObject2, "name", "second"); 88 | assertEquals("second", testObject2.toString()); 89 | assertEquals("second", access2.get(testObject2, "name")); 90 | 91 | assertEquals(access1.console.accessor.getClass().toString(), access2.console.accessor.getClass().toString()); // Same class names 92 | assertFalse(access1.console.accessor.getClass().equals(access2.console.accessor.getClass())); // But different classes 93 | 94 | assertEquals(initialCount + 2, AccessClassLoader.activeAccessClassLoaders()); 95 | 96 | AccessClassLoader.remove(testObject1.getClass().getClassLoader()); 97 | assertEquals(initialCount + 1, AccessClassLoader.activeAccessClassLoaders()); 98 | AccessClassLoader.remove(testObject2.getClass().getClassLoader()); 99 | assertEquals(initialCount + 0, AccessClassLoader.activeAccessClassLoaders()); 100 | AccessClassLoader.remove(this.getClass().getClassLoader()); 101 | assertEquals(initialCount - 1, AccessClassLoader.activeAccessClassLoaders()); 102 | } 103 | 104 | static public class Test { 105 | public String name; 106 | 107 | public String toString() { 108 | return name; 109 | } 110 | } 111 | 112 | static public class TestClassLoader1 extends ClassLoader { 113 | protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { 114 | Class c = findLoadedClass(name); 115 | if (c != null) return c; 116 | if (!name.startsWith("com.esotericsoftware.reflectasm.ClassLoaderTest")) 117 | return super.loadClass(name, resolve); 118 | ByteArrayOutputStream output = new ByteArrayOutputStream(32 * 1024); 119 | InputStream input = ClassLoaderTest.class.getResourceAsStream("/" + name.replace('.', '/') + ".class"); 120 | if (input == null) return null; 121 | try { 122 | byte[] buffer = new byte[4096]; 123 | int total = 0; 124 | while (true) { 125 | int length = input.read(buffer, 0, buffer.length); 126 | if (length == -1) break; 127 | output.write(buffer, 0, length); 128 | } 129 | } catch (IOException ex) { 130 | throw new ClassNotFoundException("Error reading class file.", ex); 131 | } finally { 132 | try { 133 | input.close(); 134 | } catch (IOException ignored) { 135 | } 136 | } 137 | byte[] buffer = output.toByteArray(); 138 | return defineClass(name, buffer, 0, buffer.length); 139 | } 140 | } 141 | 142 | static public class TestClassLoader2 extends TestClassLoader1 {} 143 | } -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/ConstructorAccessTest.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm; 2 | 3 | import junit.framework.TestCase; 4 | 5 | public class ConstructorAccessTest extends TestCase { 6 | public void testNewInstance() { 7 | ConstructorAccess access = ConstructorAccess.access(SomeClass.class, "."); 8 | SomeClass someObject = new SomeClass(); 9 | assertEquals(someObject, access.newInstance()); 10 | assertEquals(someObject, access.newInstance()); 11 | assertEquals(someObject, access.newInstance()); 12 | } 13 | 14 | public void testPackagePrivateNewInstance() { 15 | ConstructorAccess access = ConstructorAccess.access(PackagePrivateClass.class); 16 | PackagePrivateClass someObject = new PackagePrivateClass(); 17 | assertEquals(someObject, access.newInstance()); 18 | assertEquals(someObject, access.newInstance()); 19 | assertEquals(someObject, access.newInstance()); 20 | } 21 | 22 | public void testHasArgumentConstructor() { 23 | HasArgumentConstructor someObject = new HasArgumentConstructor("bla"); 24 | ConstructorAccess access = ConstructorAccess.access(HasArgumentConstructor.class, "."); 25 | assertEquals(someObject, access.newInstance("bla")); 26 | int index = access.getIndex(String.class); 27 | assertEquals(someObject, access.newInstanceWithIndex(index, "bla")); 28 | assertEquals(someObject, access.newInstanceWithTypes(new Class[]{String.class}, "bla")); 29 | assertEquals((int) (access.console.get(access.newInstance(1), "y")), 1); 30 | } 31 | 32 | public void testHasPrivateConstructor() { 33 | ConstructorAccess access = ConstructorAccess.access(HasPrivateConstructor.class); 34 | HasPrivateConstructor someObject = new HasPrivateConstructor(); 35 | assertEquals(someObject, access.newInstance()); 36 | assertEquals(someObject, access.newInstance()); 37 | assertEquals(someObject, access.newInstance()); 38 | } 39 | 40 | public void testHasProtectedConstructor() { 41 | try { 42 | ConstructorAccess access = ConstructorAccess.access(HasProtectedConstructor.class); 43 | HasProtectedConstructor newInstance = access.newInstance(); 44 | assertEquals("cow", newInstance.getMoo()); 45 | } catch (Throwable t) { 46 | System.out.println("Unexpected exception happened: " + t); 47 | assertTrue(false); 48 | } 49 | } 50 | 51 | public void testHasPackageProtectedConstructor() { 52 | try { 53 | ConstructorAccess access = ConstructorAccess.access(HasPackageProtectedConstructor.class); 54 | HasPackageProtectedConstructor newInstance = access.newInstance(); 55 | assertEquals("cow", newInstance.getMoo()); 56 | } catch (Throwable t) { 57 | System.out.println("Unexpected exception happened: " + t); 58 | assertTrue(false); 59 | } 60 | } 61 | 62 | public void testHasPublicConstructor() { 63 | try { 64 | ConstructorAccess access = ConstructorAccess.access(HasPublicConstructor.class); 65 | HasPublicConstructor newInstance = access.newInstance(); 66 | assertEquals("cow", newInstance.getMoo()); 67 | } catch (Throwable t) { 68 | System.out.println("Unexpected exception happened: " + t); 69 | assertTrue(false); 70 | } 71 | } 72 | 73 | static class PackagePrivateClass { 74 | public String name; 75 | public int intValue; 76 | protected float test1; 77 | Float test2; 78 | private String test3; 79 | 80 | public boolean equals(Object obj) { 81 | if (this == obj) return true; 82 | if (obj == null) return false; 83 | if (getClass() != obj.getClass()) return false; 84 | PackagePrivateClass other = (PackagePrivateClass) obj; 85 | if (intValue != other.intValue) return false; 86 | if (name == null) { 87 | if (other.name != null) return false; 88 | } else if (!name.equals(other.name)) return false; 89 | if (Float.floatToIntBits(test1) != Float.floatToIntBits(other.test1)) return false; 90 | if (test2 == null) { 91 | if (other.test2 != null) return false; 92 | } else if (!test2.equals(other.test2)) return false; 93 | if (test3 == null) { 94 | if (other.test3 != null) return false; 95 | } else if (!test3.equals(other.test3)) return false; 96 | return true; 97 | } 98 | } 99 | 100 | static public class SomeClass { 101 | public String name; 102 | public int intValue; 103 | protected float test1; 104 | Float test2; 105 | private String test3; 106 | 107 | public boolean equals(Object obj) { 108 | if (this == obj) return true; 109 | if (obj == null) return false; 110 | if (getClass() != obj.getClass()) return false; 111 | SomeClass other = (SomeClass) obj; 112 | if (intValue != other.intValue) return false; 113 | if (name == null) { 114 | if (other.name != null) return false; 115 | } else if (!name.equals(other.name)) return false; 116 | if (Float.floatToIntBits(test1) != Float.floatToIntBits(other.test1)) return false; 117 | if (test2 == null) { 118 | if (other.test2 != null) return false; 119 | } else if (!test2.equals(other.test2)) return false; 120 | if (test3 == null) { 121 | if (other.test3 != null) return false; 122 | } else if (!test3.equals(other.test3)) return false; 123 | return true; 124 | } 125 | } 126 | 127 | static public class HasArgumentConstructor { 128 | public String moo; 129 | public int y; 130 | 131 | public HasArgumentConstructor(String moo) { 132 | this.moo = moo; 133 | } 134 | 135 | public HasArgumentConstructor(int x) { 136 | this.y = x; 137 | } 138 | 139 | public boolean equals(Object obj) { 140 | if (this == obj) return true; 141 | if (obj == null) return false; 142 | if (getClass() != obj.getClass()) return false; 143 | HasArgumentConstructor other = (HasArgumentConstructor) obj; 144 | if (moo == null) { 145 | if (other.moo != null) return false; 146 | } else if (!moo.equals(other.moo)) return false; 147 | return true; 148 | } 149 | 150 | public String getMoo() { 151 | return moo; 152 | } 153 | } 154 | 155 | static public class HasPrivateConstructor extends HasArgumentConstructor { 156 | private HasPrivateConstructor() { 157 | super("cow"); 158 | } 159 | } 160 | 161 | static public class HasProtectedConstructor extends HasPrivateConstructor { 162 | @SuppressWarnings("synthetic-access") 163 | protected HasProtectedConstructor() { 164 | super(); 165 | } 166 | } 167 | 168 | static public class HasPackageProtectedConstructor extends HasProtectedConstructor { 169 | HasPackageProtectedConstructor() { 170 | super(); 171 | } 172 | } 173 | 174 | static public class HasPublicConstructor extends HasPackageProtectedConstructor { 175 | HasPublicConstructor() { 176 | super(); 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/ConvertionTest.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm; 2 | 3 | import junit.framework.TestCase; 4 | 5 | import java.util.Arrays; 6 | 7 | import static com.esotericsoftware.reflectasm.util.NumberUtils.convert; 8 | 9 | /** 10 | * Created by Will on 2017/2/10. 11 | */ 12 | public class ConvertionTest extends TestCase { 13 | 14 | public void testConvert() { 15 | assertTrue(Integer.class == convert(1L, int.class).getClass()); 16 | assertEquals(Integer[].class, convert(new String[]{"1", "2", "3"}, Integer[].class).getClass()); 17 | assertEquals("[1, 2]", Arrays.toString(convert(new Object[]{Double.valueOf(1.6), Float.valueOf(2.3F)}, Integer[].class))); 18 | assertEquals(Character.class, convert(123, char.class).getClass()); 19 | int i = 0; 20 | } 21 | 22 | public abstract class Abstract> { 23 | private final Class subClass; 24 | 25 | protected Abstract(Class subClass) { 26 | this.subClass = subClass; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/FieldAccessTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2008, Nathan Sweet 3 | * All rights reserved. 4 | *

5 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | *

7 | * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | * 3. Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | *

11 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | */ 13 | package com.esotericsoftware.reflectasm; 14 | 15 | import junit.framework.TestCase; 16 | 17 | public class FieldAccessTest extends TestCase { 18 | public void testNameSetAndGet() { 19 | FieldAccess access = FieldAccess.access(SomeClass.class, "."); 20 | SomeClass test = new SomeClass(); 21 | 22 | assertEquals(null, test.name); 23 | access.set(test, "name", "first"); 24 | assertEquals("first", test.name); 25 | assertEquals("first", access.get(test, "name")); 26 | 27 | assertEquals(0, test.intValue); 28 | access.set(test, "intValue", 1234); 29 | assertEquals(1234, test.intValue); 30 | assertEquals(1234, (int) access.get(test, "intValue")); 31 | } 32 | 33 | public void testIndexSetAndGet() { 34 | FieldAccess access = FieldAccess.access(SomeClass.class, "."); 35 | SomeClass test = new SomeClass(); 36 | int index; 37 | 38 | assertEquals(null, test.name); 39 | index = access.getIndex("name"); 40 | access.set(test, index, "first"); 41 | assertEquals("first", test.name); 42 | assertEquals("first", access.get(test, index)); 43 | 44 | index = access.getIndex("intValue"); 45 | assertEquals(0, test.intValue); 46 | access.set(test, index, 1234); 47 | assertEquals(1234, test.intValue); 48 | int value = access.get(test, index); 49 | assertEquals(1234, value); 50 | 51 | assertEquals(false, access.getBoolean(test, access.getIndex("booleanField"))); 52 | access.setBoolean(test, access.getIndex("booleanField"), true); 53 | assertEquals(true, access.getBoolean(test, access.getIndex("booleanField"))); 54 | 55 | assertEquals(0, access.getByte(test, access.getIndex("byteField"))); 56 | access.setByte(test, access.getIndex("byteField"), (byte) 23); 57 | assertEquals(23, access.getByte(test, access.getIndex("byteField"))); 58 | 59 | assertEquals(0, access.getChar(test, access.getIndex("charField"))); 60 | access.setChar(test, access.getIndex("charField"), (char) 53); 61 | assertEquals(53, access.getChar(test, access.getIndex("charField"))); 62 | 63 | assertEquals(0, access.getShort(test, access.getIndex("shortField"))); 64 | access.setShort(test, access.getIndex("shortField"), (short) 123); 65 | assertEquals(123, access.getShort(test, access.getIndex("shortField"))); 66 | 67 | assertEquals(0, access.getInt(test, access.getIndex("intField"))); 68 | access.setInt(test, access.getIndex("intField"), 123); 69 | assertEquals(123, access.getInt(test, access.getIndex("intField"))); 70 | 71 | assertEquals(0, access.getLong(test, access.getIndex("longField"))); 72 | access.setLong(test, access.getIndex("longField"), 123456789l); 73 | assertEquals(123456789l, access.getLong(test, access.getIndex("longField"))); 74 | 75 | assertEquals(0f, access.getFloat(test, access.getIndex("floatField"))); 76 | access.setFloat(test, access.getIndex("floatField"), 1.23f); 77 | assertEquals(1.23f, access.getFloat(test, access.getIndex("floatField"))); 78 | 79 | assertEquals(0d, access.getDouble(test, access.getIndex("doubleField"))); 80 | access.setDouble(test, access.getIndex("doubleField"), 123.456); 81 | assertEquals(123.456, access.getDouble(test, access.getIndex("doubleField"))); 82 | } 83 | 84 | public void testEmptyClass() { 85 | FieldAccess access = FieldAccess.access(EmptyClass.class); 86 | try { 87 | access.getIndex("name"); 88 | fail(); 89 | } catch (IllegalArgumentException expected) { 90 | // expected.printStackTrace(); 91 | } 92 | try { 93 | access.get(new EmptyClass(), "meow"); 94 | fail(); 95 | } catch (IllegalArgumentException expected) { 96 | // expected.printStackTrace(); 97 | } 98 | try { 99 | access.get(new EmptyClass(), 0); 100 | fail(); 101 | } catch (IllegalArgumentException expected) { 102 | // expected.printStackTrace(); 103 | } 104 | try { 105 | access.set(new EmptyClass(), "foo", "moo"); 106 | fail(); 107 | } catch (IllegalArgumentException expected) { 108 | // expected.printStackTrace(); 109 | } 110 | } 111 | 112 | static public class SomeClass { 113 | public String name; 114 | public int intValue; 115 | public boolean booleanField; 116 | public byte byteField; 117 | public char charField; 118 | public short shortField; 119 | public int intField; 120 | public long longField; 121 | public float floatField; 122 | public double doubleField; 123 | protected float test1; 124 | Float test2; 125 | private String test3; 126 | } 127 | 128 | static public class EmptyClass {} 129 | } 130 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/MethodAccessTest.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm; 2 | 3 | import junit.framework.TestCase; 4 | 5 | import java.math.BigDecimal; 6 | import java.util.Arrays; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | import java.util.concurrent.ConcurrentMap; 9 | 10 | public class MethodAccessTest extends TestCase { 11 | 12 | public void testInvoke() { 13 | 14 | MethodAccess access = MethodAccess.access(SomeClass.class, "."); 15 | SomeClass someObject = access.console.newInstance(); 16 | Object value; 17 | value = access.invoke(someObject, "test"); 18 | value = access.invoke(someObject, "getName"); 19 | assertEquals(null, value); 20 | value = access.invoke(someObject, "setName", "sweet"); 21 | assertEquals(null, value); 22 | value = access.invoke(someObject, "getName"); 23 | assertEquals("sweet", value); 24 | value = access.invoke(someObject, "setName", (Object) null); 25 | assertEquals(null, value); 26 | value = access.invoke(someObject, "getName"); 27 | assertEquals(null, value); 28 | 29 | value = access.invoke(someObject, "getIntValue"); 30 | assertEquals(0, value); 31 | value = access.invoke(someObject, "setValue", 1234, false); 32 | assertEquals(null, value); 33 | value = access.invoke(someObject, "getIntValue"); 34 | assertEquals(1234, value); 35 | value = access.invoke(someObject, "methodWithManyArguments", 6, 2f, null, null); 36 | assertEquals("test0", value); 37 | value = access.invoke(someObject, "methodWithManyArguments", "1", 2f, new int[]{3, 4}, 4.2f, null, true); 38 | assertEquals("0", value); 39 | value = access.invoke(someObject, "methodWithManyArguments", "1", 2f, new int[]{3, 4}, 4.2f, null, true, new int[]{1, 2, 3}); 40 | value = access.invoke(someObject, "methodWithManyArguments", "1", 2f, new int[]{3, 4}, 4.2f, null, true, 1, 2, 3); 41 | assertEquals(Arrays.toString(new int[]{1, 2, 3}), value); 42 | value = access.invoke(null, "staticMethod", "moo", 1234); 43 | assertEquals("meow! moo, 1234", value); 44 | //int methodWithVarArgs(char,Double,Long,Integer[]) 45 | try { 46 | value = access.invoke(someObject, "methodWithVarArgs", null, 2, 3); 47 | fail(); 48 | } catch (IllegalArgumentException e) {} 49 | value = access.invoke(someObject, "methodWithVarArgs", 1, 2, 3); 50 | assertEquals(0, value); 51 | value = access.invoke(someObject, "methodWithVarArgs", 1, 2, 3, null); 52 | assertEquals(1, value); 53 | value = access.invoke(someObject, "methodWithVarArgs", 1, 2, 3, 4, 5, 6, 7); 54 | assertEquals(4, value); 55 | value = access.invoke(someObject, "methodWithVarArgs", 'x', 2, 3, 4); 56 | assertEquals(1, value); 57 | ClassAccess.IS_STRICT_CONVERT = false; 58 | value = access.invoke(someObject, "methodWithVarArgs", "B", null, 3, 4, null); 59 | assertEquals(2, value); 60 | value = access.invoke(someObject, "methodWithVarArgs", new BigDecimal(1), 2, new BigDecimal(20), new Integer[2]); 61 | assertEquals(2, value); 62 | } 63 | 64 | public void testEmptyClass() { 65 | MethodAccess access = MethodAccess.access(EmptyClass.class, "."); 66 | try { 67 | access.getIndex("name"); 68 | fail(); 69 | } catch (IllegalArgumentException expected) { 70 | // expected.printStackTrace(); 71 | } 72 | try { 73 | access.getIndex("name", String.class); 74 | fail(); 75 | } catch (IllegalArgumentException expected) { 76 | // expected.printStackTrace(); 77 | } 78 | try { 79 | access.invoke(new EmptyClass(), "meow", "moo"); 80 | fail(); 81 | } catch (IllegalArgumentException expected) { 82 | // expected.printStackTrace(); 83 | } 84 | try { 85 | access.invokeWithIndex(new EmptyClass(), 0); 86 | fail(); 87 | } catch (IllegalArgumentException expected) { 88 | // expected.printStackTrace(); 89 | } 90 | try { 91 | access.invokeWithIndex(new EmptyClass(), 0, "moo"); 92 | fail(); 93 | } catch (IllegalArgumentException expected) { 94 | // expected.printStackTrace(); 95 | } 96 | } 97 | 98 | public void testInvokeInterface() { 99 | MethodAccess access = MethodAccess.access(ConcurrentMap.class, "."); 100 | ConcurrentHashMap someMap = new ConcurrentHashMap(); 101 | someMap.put("first", "one"); 102 | someMap.put("second", "two"); 103 | Object value; 104 | 105 | // invokeWithIndex a method declared directly in the ConcurrentMap interface 106 | value = access.invoke(someMap, "replace", "first", "foo"); 107 | assertEquals("one", value); 108 | // invokeWithIndex a method declared in the Map superinterface 109 | value = access.invoke(someMap, "size"); 110 | assertEquals(someMap.size(), value); 111 | } 112 | 113 | static public class EmptyClass {} 114 | 115 | static public class baseClass extends EmptyClass { 116 | public void test() {} 117 | } 118 | 119 | static public class SomeClass extends baseClass { 120 | public static boolean x; 121 | static boolean bu; 122 | String name; 123 | private int intValue; 124 | 125 | public SomeClass() { 126 | 127 | } 128 | 129 | public SomeClass(int x, int y) { 130 | 131 | } 132 | 133 | public SomeClass(String x) { 134 | 135 | } 136 | 137 | static public String staticMethod(String a, int b) { 138 | return "meow! " + a + ", " + b; 139 | } 140 | 141 | public String getName() { 142 | return name; 143 | } 144 | 145 | public void setName(String name) { 146 | this.name = name; 147 | } 148 | 149 | public int getIntValue() { 150 | return intValue; 151 | } 152 | 153 | public void setValue(int intValue, Boolean bu) { 154 | this.intValue = intValue; 155 | this.bu = bu; 156 | } 157 | 158 | public String methodWithManyArguments(int i, float f, Integer[] I, int c1) { 159 | return "test0"; 160 | } 161 | 162 | public String methodWithManyArguments(int i, float f, Integer[] I, SomeClass[] c1) { 163 | return "test0"; 164 | } 165 | 166 | public String methodWithManyArguments(int i, float f, Integer[] I, Float F, SomeClass[] c1, Boolean x, int... y) { 167 | if (y.length == 0) return "0"; 168 | return Arrays.toString(y); 169 | } 170 | 171 | public int methodWithVarArgs(char a, Double b, Long c, Integer... d) { 172 | return d == null ? 0 : d.length; 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/benchmark/Benchmark.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm.benchmark; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | import java.util.Map; 6 | import java.util.Map.Entry; 7 | import java.util.TreeMap; 8 | 9 | public class Benchmark { 10 | static int testRounds = 300000; 11 | static int testCount = (System.getProperty("java.vm.name").indexOf("Server VM") > -1) ? 300 : 100; 12 | public boolean warmup = true; 13 | public Map testTimes = new TreeMap<>(); 14 | private long s; 15 | 16 | public void start() { 17 | if (warmup) return; 18 | s = System.nanoTime(); 19 | } 20 | 21 | public void end(String name) { 22 | if (warmup) return; 23 | long e = System.nanoTime(); 24 | long time = e - s; 25 | /* 26 | if(System.getProperty("java.vm.name").indexOf("Server VM")>-1) name="ServerVM["+name+"]"; 27 | else name="ClientVM["+name+"]"; 28 | */ 29 | Long oldTime = testTimes.get(name); 30 | if (oldTime == null || time < oldTime) testTimes.put(name, time); 31 | System.out.println(name + ": " + time / 1000000f + " ms"); 32 | } 33 | 34 | public String[] chart(String title) { 35 | Comparator comparator = new Comparator() { 36 | public int compare(Entry o1, Entry o2) { 37 | // return ((String)o1.getKey()).compareTo((String)o2.getKey()); 38 | return (int) ((Long) o1.getValue() - (Long) o2.getValue()); 39 | } 40 | }; 41 | ArrayList list = new ArrayList(testTimes.entrySet()); 42 | //Collections.sort(list, comparator); 43 | 44 | StringBuilder names = new StringBuilder(512); 45 | StringBuilder times = new StringBuilder(512); 46 | long max = 0; 47 | int count = 0; 48 | 49 | for (Entry entry : list) { 50 | String name = entry.getKey(); 51 | names.insert(0, '|'); 52 | names.insert(0, name); 53 | long time = entry.getValue(); 54 | times.append(time); 55 | times.append(','); 56 | max = Math.max(max, time); 57 | count++; 58 | } 59 | times.setLength(times.length() - 1); 60 | names.setLength(names.length() - 1); 61 | int height = count * 18 + 21; 62 | int width = Math.min(700, 300000 / height); 63 | System.out.println("![](http://chart.apis.google.com/chart?chtt=" + title + "&" + "chs=" + width + "x" + height + "&chd=t:" + times + "&chds=0," + max + "&chxl=0:|" + names + "&cht=bhg&chbh=10&chxt=y&" + "chco=660000|660033|660066|660099|6600CC|6600FF|663300|663333|" + "663366|663399|6633CC|6633FF|666600|666633|666666)\n"); 64 | return new String[]{times.toString(), String.valueOf(max), names.toString()}; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/benchmark/ClassAccessBenchmark.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm.benchmark; 2 | 3 | import com.esotericsoftware.reflectasm.Accessor; 4 | import com.esotericsoftware.reflectasm.ClassAccess; 5 | import test.TestObject; 6 | 7 | import java.lang.reflect.Constructor; 8 | import java.lang.reflect.Field; 9 | import java.lang.reflect.Method; 10 | import java.util.Arrays; 11 | import java.util.HashMap; 12 | 13 | /** 14 | * Created by hyee on 2017/2/4. 15 | */ 16 | public class ClassAccessBenchmark { 17 | public static String[] doBenchmark() throws Exception { 18 | final int count = Benchmark.testRounds; 19 | final int rounds = Benchmark.testCount; 20 | final String fieldName = "fi"; 21 | final String methodName = "func2"; 22 | final Benchmark benchmark = new Benchmark(); 23 | 24 | TestObject obj = null; 25 | 26 | final Class[] args = new Class[]{int.class, Double.class, String.class, long.class}; 27 | 28 | //access info from ClassAccessor; 29 | final ClassAccess console = ClassAccess.access(TestObject.class, "."); 30 | final Accessor asm = console.accessor; 31 | final int fieldIndex = console.indexOfField(fieldName); 32 | final int methodIndex = console.indexOfMethod(methodName, args); 33 | final int constructorIndex = console.indexOfConstructor(args); 34 | 35 | final Field field = TestObject.class.getField(fieldName); 36 | final Method method = TestObject.class.getDeclaredMethod(methodName, args); 37 | final Constructor constructor = TestObject.class.getConstructor(args); 38 | 39 | for (int c1 = 1; c1 <= 9; c1++) { 40 | for (int c = 0; c <= 1; c++) { 41 | benchmark.warmup = c == 0 ? true : false; 42 | String tag = (c1 <= 3 ? "Construction - " : c1 <= 6 ? "Field Set+Get - " : "Method Call - ") + (c1 % 3 == 1 ? "ReflectASM" : c1 % 3 == 2 ? "Reflection" : "Direct"); 43 | benchmark.start(); 44 | for (int i = 0; i < rounds; i++) { 45 | for (int ii = 0; ii < count; ii++) { 46 | int val = i + ii; 47 | switch (c1) { 48 | //Compare Constructions 49 | //===================== 50 | case 1: 51 | obj = asm.newInstanceWithIndex(constructorIndex, val, ((Number) val).doubleValue(), String.valueOf(val), ((Number) val).longValue()); 52 | break; 53 | case 2: 54 | obj = (TestObject) constructor.newInstance(val, ((Number) val).doubleValue(), String.valueOf(val), ((Number) val).longValue()); 55 | break; 56 | case 3: 57 | obj = new TestObject(val, ((Number) val).doubleValue(), String.valueOf(val), ((Number) val).longValue()); 58 | break; 59 | //Compare Field Set+Get 60 | //===================== 61 | case 4: 62 | asm.set(obj, fieldIndex, val); 63 | val = asm.get(obj, fieldIndex); 64 | break; 65 | case 5: 66 | field.set(obj, val); 67 | val = (int) field.get(obj); 68 | break; 69 | case 6: 70 | obj.fi = val; 71 | val = obj.fi; 72 | break; 73 | //Compare Method Call 74 | //===================== 75 | case 7: 76 | asm.invokeWithIndex(obj, methodIndex, val, ((Number) val).doubleValue(), String.valueOf(val), ((Number) val).longValue()); 77 | break; 78 | case 8: 79 | method.invoke(obj, val, ((Number) val).doubleValue(), String.valueOf(val), ((Number) val).longValue()); 80 | break; 81 | case 9: 82 | obj.func2(val, ((Number) val).doubleValue(), String.valueOf(val), ((Number) val).longValue()); 83 | break; 84 | } 85 | } 86 | } 87 | benchmark.end(tag); 88 | } 89 | } 90 | 91 | return benchmark.chart("Benchmark Summary"); 92 | } 93 | 94 | public static String join(String sep, String[] s0) { 95 | String r = ""; 96 | for (int i = 0; i < s0.length; i++) r += s0[i] + (i == s0.length - 1 ? "" : sep); 97 | return r; 98 | } 99 | 100 | public static void print(String[][] results) { 101 | long max = 0; 102 | String[] times = new String[results.length]; 103 | String[] names = new String[results.length]; 104 | int index = -1; 105 | String title = ((System.getProperty("java.vm.name").indexOf("Server VM") > -1) ? "Server" : "Client") + " VM"; 106 | HashMap map = new HashMap(); 107 | double count = Benchmark.testRounds * Benchmark.testCount; 108 | System.out.println(count); 109 | for (String[] result : results) { 110 | max = Math.max(max, Long.valueOf(result[1])); 111 | times[++index] = result[0]; 112 | names[results.length - 1 - index] = result[2]; 113 | String[] times0 = result[0].split(","); 114 | String[] names0 = result[2].split("\\|"); 115 | String[] names1 = new String[names0.length]; 116 | String[] times1 = new String[names0.length]; 117 | String tag = null; 118 | int tagIdx = 0; 119 | for (int i = 0; i < names0.length; i++) { 120 | if (tag == null) { 121 | tagIdx = names0[i].indexOf('-'); 122 | tag = names0[i].substring(0, tagIdx - 1).trim(); 123 | } 124 | String name = names0[i].substring(tagIdx + 1).trim(); 125 | names1[names0.length - 1 - i] = name; 126 | if (!map.containsKey(name)) { 127 | map.put(name, i); 128 | } 129 | times1[map.get(name)] = String.format("%.2f ns", Long.valueOf(times0[i]) / count); 130 | } 131 | 132 | if (index == 0) { 133 | System.out.println("| VM | Item | " + join(" | ", names1) + " |"); 134 | Arrays.fill(names1, " --------- "); 135 | System.out.println("| --- | --- | " + join(" | ", names1) + " |"); 136 | } 137 | System.out.println("| " + title + " | " + tag + " | " + join(" | ", times1) + " |"); 138 | } 139 | int height = 12 * 18 + 21; 140 | int width = Math.min(700, 300000 / height); 141 | System.out.println("Active ClassLoaders: " + ClassAccess.activeAccessClassLoaders()); 142 | title = "Java " + System.getProperty("java.version") + " " + System.getProperty("os.arch") + "(" + title + ")"; 143 | System.out.println("![](http://chart.apis.google.com/chart?chtt=&" + title + "&chs=" + width + "x" + height + "&chd=t:" + join(",", times) + "&chds=0," + max + "&chxl=0:|" + join("|", names) + "&cht=bhg&chbh=10&chxt=y&" + "chco=660000|660033|660066|660099|6600CC|6600FF|663300|663333|" + "663366|663399|6633CC|6633FF|666600|666633|666666)\n"); 144 | } 145 | 146 | public static void main(String[] args) throws Throwable { 147 | //ClassAccess.IS_CACHED = true; 148 | ClassAccess.IS_STRICT_CONVERT = true; 149 | new FieldAccessBenchmark(); 150 | new MethodAccessBenchmark(); 151 | new ConstructorAccessBenchmark(); 152 | String[][] results = new String[][]{FieldAccessBenchmark.result, MethodAccessBenchmark.result, ConstructorAccessBenchmark.result}; 153 | print(results); 154 | doBenchmark(); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/benchmark/ConstructorAccessBenchmark.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm.benchmark; 2 | 3 | import com.esotericsoftware.reflectasm.ClassAccess; 4 | import com.esotericsoftware.reflectasm.ConstructorAccess; 5 | 6 | import java.lang.invoke.MethodHandle; 7 | 8 | public class ConstructorAccessBenchmark extends Benchmark { 9 | public static String[] result; 10 | 11 | public ConstructorAccessBenchmark() throws Throwable { 12 | final int count = Benchmark.testRounds; 13 | final int rounds = Benchmark.testCount; 14 | Object[] dontCompileMeAway = new Object[count]; 15 | 16 | Class type = SomeClass.class; 17 | ConstructorAccess access = ConstructorAccess.access(type); 18 | MethodHandle handle = access.console.getHandle(null, null, ClassAccess.NEW); 19 | 20 | for (int i = 0; i < rounds / 3; i++) 21 | for (int ii = 0; ii < count; ii++) { 22 | dontCompileMeAway[ii] = access.console.accessor.newInstance(); 23 | dontCompileMeAway[ii] = type.newInstance(); 24 | dontCompileMeAway[ii] = (SomeClass) handle.invokeExact(); 25 | } 26 | 27 | for (int i = 0; i < rounds; i++) 28 | for (int ii = 0; ii < count; ii++) 29 | 30 | warmup = false; 31 | start(); 32 | for (int i = 0; i < rounds; i++) { 33 | for (int ii = 0; ii < count; ii++) 34 | dontCompileMeAway[ii] = access.newInstance(); 35 | } 36 | end("Constructor - ReflectASM"); 37 | start(); 38 | for (int i = 0; i < rounds; i++) { 39 | for (int ii = 0; ii < count; ii++) 40 | dontCompileMeAway[ii] = (SomeClass) handle.invokeExact(); 41 | } 42 | end("Constructor - DirectMethodHandle"); 43 | start(); 44 | for (int i = 0; i < rounds; i++) { 45 | for (int ii = 0; ii < count; ii++) 46 | dontCompileMeAway[ii] = type.newInstance(); 47 | } 48 | end("Constructor - Reflection"); 49 | start(); 50 | for (int i = 0; i < rounds; i++) { 51 | for (int ii = 0; ii < count; ii++) 52 | dontCompileMeAway[ii] = new SomeClass(); 53 | } 54 | end("Constructor - Direct"); 55 | result = chart("Constructor"); 56 | } 57 | 58 | public static void main(String[] args) throws Throwable { 59 | new ConstructorAccessBenchmark(); 60 | } 61 | 62 | static public class SomeClass { 63 | public String name; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/benchmark/FieldAccessBenchmark.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm.benchmark; 2 | 3 | import com.esotericsoftware.reflectasm.ClassAccess; 4 | import com.esotericsoftware.reflectasm.FieldAccess; 5 | 6 | import java.lang.invoke.MethodHandle; 7 | import java.lang.reflect.Field; 8 | 9 | public class FieldAccessBenchmark extends Benchmark { 10 | public static String[] result; 11 | 12 | public FieldAccessBenchmark() throws Throwable { 13 | final int count = Benchmark.testRounds; 14 | final int rounds = Benchmark.testCount; 15 | String[] dontCompileMeAway = new String[count]; 16 | 17 | FieldAccess access = FieldAccess.access(SomeClass.class); 18 | SomeClass someObject = new SomeClass(); 19 | int index = access.getIndex("name"); 20 | MethodHandle getter = access.console.getHandleWithIndex(index, ClassAccess.GETTER); 21 | MethodHandle setter = access.console.getHandleWithIndex(index, ClassAccess.SETTER); 22 | 23 | Field field = SomeClass.class.getField("name"); 24 | 25 | for (int i = 0; i < rounds / 3; i++) { 26 | for (int ii = 0; ii < count; ii++) { 27 | access.console.accessor.set(someObject, index, "first"); 28 | dontCompileMeAway[ii] = access.console.accessor.get(someObject, index); 29 | field.set(someObject, "first"); 30 | dontCompileMeAway[ii] = (String) field.get(someObject); 31 | setter.invokeExact(someObject, "first"); 32 | dontCompileMeAway[ii] = (String) getter.invokeExact(someObject); 33 | } 34 | } 35 | warmup = false; 36 | start(); 37 | for (int i = 0; i < rounds; i++) { 38 | for (int ii = 0; ii < count; ii++) { 39 | access.console.accessor.set(someObject, index, "first"); 40 | dontCompileMeAway[ii] = access.console.accessor.get(someObject, index); 41 | } 42 | } 43 | end("Field Set+Get - ReflectASM"); 44 | start(); 45 | for (int i = 0; i < rounds; i++) { 46 | for (int ii = 0; ii < count; ii++) { 47 | setter.invokeExact(someObject, "first"); 48 | dontCompileMeAway[ii] = (String) getter.invokeExact(someObject); 49 | } 50 | } 51 | end("Field Set+Get - DirectMethodHandle"); 52 | start(); 53 | for (int i = 0; i < rounds; i++) { 54 | for (int ii = 0; ii < count; ii++) { 55 | field.set(someObject, "first"); 56 | dontCompileMeAway[ii] = (String) field.get(someObject); 57 | } 58 | } 59 | end("Field Set+Get - Reflection"); 60 | start(); 61 | for (int i = 0; i < rounds; i++) { 62 | for (int ii = 0; ii < count; ii++) { 63 | someObject.name = "first"; 64 | dontCompileMeAway[ii] = someObject.name; 65 | } 66 | } 67 | end("Field Set+Get - Direct"); 68 | result = chart("Field Set/Get"); 69 | } 70 | 71 | public static void main(String[] args) throws Throwable { 72 | new FieldAccessBenchmark(); 73 | } 74 | 75 | static public class SomeClass { 76 | public String name; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/benchmark/MethodAccessBenchmark.java: -------------------------------------------------------------------------------- 1 | package com.esotericsoftware.reflectasm.benchmark; 2 | 3 | import com.esotericsoftware.reflectasm.ClassAccess; 4 | 5 | import java.lang.invoke.MethodHandle; 6 | import java.lang.reflect.Method; 7 | 8 | public class MethodAccessBenchmark extends Benchmark { 9 | public static String[] result; 10 | 11 | public MethodAccessBenchmark() throws Throwable { 12 | //ClassAccess.IS_STRICT_CONVERT=true; 13 | final int count = Benchmark.testRounds; 14 | final int rounds = Benchmark.testCount; 15 | Object[] dontCompileMeAway = new Object[count]; 16 | Object[] args = new Object[0]; 17 | 18 | ClassAccess access = ClassAccess.access(SomeClass.class); 19 | SomeClass someObject = new SomeClass(); 20 | int index = access.indexOfMethod("getName"); 21 | MethodHandle handle = access.getHandleWithIndex(index, ClassAccess.METHOD); 22 | 23 | Method method = SomeClass.class.getMethod("getName"); 24 | // method.setAccessible(true); // Improves reflection a bit. 25 | 26 | for (int i = 0; i < rounds / 3; i++) { 27 | for (int ii = 0; ii < count; ii++) { 28 | dontCompileMeAway[ii] = access.accessor.invokeWithIndex(someObject, index, args); 29 | dontCompileMeAway[ii] = method.invoke(someObject, args); 30 | dontCompileMeAway[ii] = (String) handle.invokeExact(someObject); 31 | } 32 | } 33 | warmup = false; 34 | start(); 35 | for (int i = 0; i < rounds; i++) { 36 | for (int ii = 0; ii < count; ii++) 37 | dontCompileMeAway[ii] = access.accessor.invokeWithIndex(someObject, index, args); 38 | } 39 | end("Method Call - ReflectASM"); 40 | start(); 41 | for (int i = 0; i < rounds; i++) { 42 | for (int ii = 0; ii < count; ii++) 43 | dontCompileMeAway[ii] = (String) handle.invokeExact(someObject); 44 | } 45 | end("Method Call - DirectMethodHandle"); 46 | start(); 47 | for (int i = 0; i < rounds; i++) { 48 | for (int ii = 0; ii < count; ii++) 49 | dontCompileMeAway[ii] = method.invoke(someObject, args); 50 | } 51 | end("Method Call - Reflection"); 52 | start(); 53 | for (int i = 0; i < rounds; i++) { 54 | for (int ii = 0; ii < count; ii++) 55 | dontCompileMeAway[ii] = someObject.getName(); 56 | } 57 | end("Method Call - Direct"); 58 | result = chart("Method Call"); 59 | } 60 | 61 | public static void main(String[] args) throws Throwable { 62 | new MethodAccessBenchmark(); 63 | } 64 | 65 | static public class SomeClass { 66 | private String name = "something"; 67 | 68 | public String getName() { 69 | return name; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/test.java: -------------------------------------------------------------------------------- 1 | // 2 | // Source code recreated from a .class file by IntelliJ IDEA 3 | // (powered by Fernflower decompiler) 4 | // 5 | 6 | package com.esotericsoftware.reflectasm; 7 | 8 | import com.esotericsoftware.reflectasm.MethodAccessTest.SomeClass; 9 | import com.esotericsoftware.reflectasm.MethodAccessTest.baseClass; 10 | 11 | import java.lang.invoke.MethodHandle; 12 | 13 | public final class test implements Accessor { 14 | static MethodHandle[][] methodHandles = new MethodHandle[3][10]; 15 | static final ClassInfo classInfo = new ClassInfo(); 16 | 17 | public test() { 18 | } 19 | 20 | public MethodHandle[][] getMethodHandles() { 21 | return methodHandles; 22 | } 23 | 24 | public ClassInfo getInfo() { 25 | return classInfo; 26 | } 27 | 28 | static { 29 | classInfo.baseClass = SomeClass.class; 30 | classInfo.isNonStaticMemberClass = false; 31 | classInfo.bucket = 7; 32 | methodNames(); 33 | methodParamTypes(); 34 | returnTypes(); 35 | methodModifiers(); 36 | methodDescs(); 37 | fieldNames(); 38 | fieldTypes(); 39 | fieldModifiers(); 40 | fieldDescs(); 41 | constructorParamTypes(); 42 | constructorModifiers(); 43 | constructorDescs(); 44 | ClassAccess.buildIndex(classInfo); 45 | } 46 | 47 | static void methodNames() { 48 | classInfo.methodNames = new String[]{"getName", "setName", "setValue", "methodWithManyArguments", "methodWithManyArguments", "methodWithManyArguments", "methodWithVarArgs", "getIntValue", "staticMethod", "test"}; 49 | } 50 | 51 | static void methodParamTypes() { 52 | classInfo.methodParamTypes = new Class[][]{new Class[0], {String.class}, {Integer.TYPE, Boolean.class}, {Integer.TYPE, Float.TYPE, Integer[].class, Integer.TYPE}, {Integer.TYPE, Float.TYPE, Integer[].class, SomeClass[].class}, {Integer.TYPE, Float.TYPE, Integer[].class, Float.class, SomeClass[].class, Boolean.class, int[].class}, {Character.TYPE, Double.class, Long.class, Integer[].class}, new Class[0], {String.class, Integer.TYPE}, new Class[0]}; 53 | } 54 | 55 | static void returnTypes() { 56 | classInfo.returnTypes = new Class[]{String.class, Void.TYPE, Void.TYPE, String.class, String.class, String.class, Integer.TYPE, Integer.TYPE, String.class, Void.TYPE, null, null, null, null, null, null, null, null, null, baseClass.class}; 57 | } 58 | 59 | static void methodModifiers() { 60 | classInfo.methodModifiers = new Integer[]{Integer.valueOf(1), Integer.valueOf(1), Integer.valueOf(1), Integer.valueOf(1), Integer.valueOf(1), Integer.valueOf(262273), Integer.valueOf(262273), Integer.valueOf(1), Integer.valueOf(9), Integer.valueOf(1)}; 61 | } 62 | 63 | static void methodDescs() { 64 | classInfo.methodDescs = new String[][]{{"getName", "()Ljava/lang/String;"}, {"setName", "(Ljava/lang/String;)V"}, {"setValue", "(ILjava/lang/Boolean;)V"}, {"methodWithManyArguments", "(IF[Ljava/lang/Integer;I)Ljava/lang/String;"}, {"methodWithManyArguments", "(IF[Ljava/lang/Integer;[Lcom/esotericsoftware/reflectasm/MethodAccessTest$SomeClass;)Ljava/lang/String;"}, {"methodWithManyArguments", "(IF[Ljava/lang/Integer;Ljava/lang/Float;[Lcom/esotericsoftware/reflectasm/MethodAccessTest$SomeClass;Ljava/lang/Boolean;[I)Ljava/lang/String;"}, {"methodWithVarArgs", "(CLjava/lang/Double;Ljava/lang/Long;[Ljava/lang/Integer;)I"}, {"getIntValue", "()I"}, {"staticMethod", "(Ljava/lang/String;I)Ljava/lang/String;"}, {"test", "()V"}}; 65 | } 66 | 67 | static void fieldNames() { 68 | classInfo.fieldNames = new String[]{"x", "bu", "name", "intValue"}; 69 | } 70 | 71 | static void fieldTypes() { 72 | classInfo.fieldTypes = new Class[]{Boolean.TYPE, Boolean.TYPE, String.class, Integer.TYPE, null, null, null, null}; 73 | } 74 | 75 | static void fieldModifiers() { 76 | classInfo.fieldModifiers = new Integer[]{Integer.valueOf(9), Integer.valueOf(8), Integer.valueOf(0), Integer.valueOf(2)}; 77 | } 78 | 79 | static void fieldDescs() { 80 | classInfo.fieldDescs = new String[][]{{"x", "Z"}, {"bu", "Z"}, {"name", "Ljava/lang/String;"}, {"intValue", "I"}}; 81 | } 82 | 83 | static void constructorParamTypes() { 84 | classInfo.constructorParamTypes = new Class[][]{{String.class}, {Integer.TYPE, Integer.TYPE}, new Class[0]}; 85 | } 86 | 87 | static void constructorModifiers() { 88 | classInfo.constructorModifiers = new Integer[]{Integer.valueOf(1), Integer.valueOf(1), Integer.valueOf(1)}; 89 | } 90 | 91 | static void constructorDescs() { 92 | classInfo.constructorDescs = new String[]{"(Ljava/lang/String;)V", "(II)V", "()V"}; 93 | } 94 | 95 | public final T invokeWithIndex(SomeClass var1, int var2, V... var3) { 96 | switch (var2) { 97 | case 0: 98 | return (T) var1.getName(); 99 | case 1: 100 | var1.setName((String) var3[0]); 101 | return null; 102 | case 2: 103 | var1.setValue(((Integer) var3[0]).intValue(), (Boolean) var3[1]); 104 | return null; 105 | case 3: 106 | return (T) var1.methodWithManyArguments(((Integer) var3[0]).intValue(), ((Float) var3[1]).floatValue(), (Integer[]) var3[2], ((Integer) var3[3]).intValue()); 107 | case 4: 108 | return (T) var1.methodWithManyArguments(((Integer) var3[0]).intValue(), ((Float) var3[1]).floatValue(), (Integer[]) var3[2], (SomeClass[]) var3[3]); 109 | case 5: 110 | return (T) var1.methodWithManyArguments(((Integer) var3[0]).intValue(), ((Float) var3[1]).floatValue(), (Integer[]) var3[2], (Float) var3[3], (SomeClass[]) var3[4], (Boolean) var3[5], (int[]) var3[6]); 111 | case 6: 112 | return (T) Integer.valueOf(var1.methodWithVarArgs(((Character) var3[0]).charValue(), (Double) var3[1], (Long) var3[2], (Integer[]) var3[3])); 113 | case 7: 114 | return (T) Integer.valueOf(var1.getIntValue()); 115 | case 8: 116 | return (T) SomeClass.staticMethod((String) var3[0], ((Integer) var3[1]).intValue()); 117 | case 9: 118 | var1.test(); 119 | return null; 120 | default: 121 | throw new IllegalArgumentException("Method not found: " + var2); 122 | } 123 | } 124 | 125 | public final T get(SomeClass var1, int var2) { 126 | switch (var2) { 127 | case 0: 128 | return (T) Boolean.valueOf(SomeClass.x); 129 | case 1: 130 | return (T) Boolean.valueOf(SomeClass.bu); 131 | case 2: 132 | return (T) var1.name; 133 | default: 134 | throw new IllegalArgumentException("Field not found: " + var2); 135 | } 136 | } 137 | 138 | public final void set(SomeClass var1, int var2, V var3) { 139 | switch (var2) { 140 | case 0: 141 | SomeClass.x = ((Boolean) var3).booleanValue(); 142 | return; 143 | case 1: 144 | SomeClass.bu = ((Boolean) var3).booleanValue(); 145 | return; 146 | case 2: 147 | var1.name = (String) var3; 148 | return; 149 | default: 150 | throw new IllegalArgumentException("Field not found: " + var2); 151 | } 152 | } 153 | 154 | public final SomeClass newInstanceWithIndex(int var1, T... var2) { 155 | switch (var1) { 156 | case 0: 157 | return new SomeClass((String) var2[0]); 158 | case 1: 159 | return new SomeClass(((Integer) var2[0]).intValue(), ((Integer) var2[1]).intValue()); 160 | case 2: 161 | return new SomeClass(); 162 | default: 163 | throw new IllegalArgumentException("Constructor not found: " + var1); 164 | } 165 | } 166 | 167 | public final SomeClass newInstance() { 168 | return new SomeClass(); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /test/test/Many.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | /** 4 | * Created by Will on 2017/2/7. 5 | */ 6 | public class Many { 7 | int x0; 8 | int x1; 9 | int x2; 10 | int x3; 11 | int x4; 12 | int x5; 13 | int x6; 14 | int x7; 15 | int x8; 16 | int x9; 17 | int x10; 18 | int x11; 19 | int x12; 20 | int x13; 21 | int x14; 22 | int x15; 23 | int x16; 24 | int x17; 25 | int x18; 26 | int x19; 27 | int x20; 28 | int x21; 29 | int x22; 30 | int x23; 31 | int x24; 32 | int x25; 33 | int x26; 34 | int x27; 35 | int x28; 36 | int x29; 37 | int x30; 38 | int x31; 39 | int x32; 40 | int x33; 41 | int x34; 42 | int x35; 43 | int x36; 44 | int x37; 45 | int x38; 46 | int x39; 47 | int x40; 48 | int x41; 49 | int x42; 50 | int x43; 51 | int x44; 52 | int x45; 53 | int x46; 54 | int x47; 55 | int x48; 56 | int x49; 57 | int x50; 58 | int x51; 59 | int x52; 60 | int x53; 61 | int x54; 62 | int x55; 63 | int x56; 64 | int x57; 65 | int x58; 66 | int x59; 67 | int x60; 68 | int x61; 69 | int x62; 70 | int x63; 71 | int x64; 72 | int x65; 73 | int x66; 74 | int x67; 75 | int x68; 76 | int x69; 77 | int x70; 78 | int x71; 79 | int x72; 80 | int x73; 81 | int x74; 82 | int x75; 83 | int x76; 84 | int x77; 85 | int x78; 86 | int x79; 87 | int x80; 88 | int x81; 89 | int x82; 90 | int x83; 91 | int x84; 92 | int x85; 93 | int x86; 94 | int x87; 95 | int x88; 96 | int x89; 97 | int x90; 98 | int x91; 99 | int x92; 100 | int x93; 101 | int x94; 102 | int x95; 103 | int x96; 104 | int x97; 105 | int x98; 106 | int x99; 107 | int x100; 108 | int x101; 109 | int x102; 110 | int x103; 111 | int x104; 112 | int x105; 113 | int x106; 114 | int x107; 115 | int x108; 116 | int x109; 117 | int x110; 118 | int x111; 119 | int x112; 120 | int x113; 121 | int x114; 122 | int x115; 123 | int x116; 124 | int x117; 125 | int x118; 126 | int x119; 127 | int x120; 128 | int x121; 129 | int x122; 130 | int x123; 131 | int x124; 132 | int x125; 133 | int x126; 134 | int x127; 135 | int x128; 136 | int x129; 137 | int x130; 138 | int x131; 139 | int x132; 140 | int x133; 141 | int x134; 142 | int x135; 143 | int x136; 144 | int x137; 145 | int x138; 146 | int x139; 147 | int x140; 148 | int x141; 149 | int x142; 150 | int x143; 151 | int x144; 152 | int x145; 153 | int x146; 154 | int x147; 155 | int x148; 156 | int x149; 157 | int x150; 158 | int x151; 159 | int x152; 160 | int x153; 161 | int x154; 162 | int x155; 163 | int x156; 164 | int x157; 165 | int x158; 166 | int x159; 167 | int x160; 168 | int x161; 169 | int x162; 170 | int x163; 171 | int x164; 172 | int x165; 173 | int x166; 174 | int x167; 175 | int x168; 176 | int x169; 177 | int x170; 178 | int x171; 179 | int x172; 180 | int x173; 181 | int x174; 182 | int x175; 183 | int x176; 184 | int x177; 185 | int x178; 186 | int x179; 187 | int x180; 188 | int x181; 189 | int x182; 190 | int x183; 191 | int x184; 192 | int x185; 193 | int x186; 194 | int x187; 195 | int x188; 196 | int x189; 197 | int x190; 198 | int x191; 199 | int x192; 200 | int x193; 201 | int x194; 202 | int x195; 203 | int x196; 204 | int x197; 205 | int x198; 206 | int x199; 207 | int x200; 208 | int x201; 209 | int x202; 210 | int x203; 211 | int x204; 212 | int x205; 213 | int x206; 214 | int x207; 215 | int x208; 216 | int x209; 217 | int x210; 218 | int x211; 219 | int x212; 220 | int x213; 221 | int x214; 222 | int x215; 223 | int x216; 224 | int x217; 225 | int x218; 226 | int x219; 227 | int x220; 228 | int x221; 229 | int x222; 230 | int x223; 231 | int x224; 232 | int x225; 233 | int x226; 234 | int x227; 235 | int x228; 236 | int x229; 237 | int x230; 238 | int x231; 239 | int x232; 240 | int x233; 241 | int x234; 242 | int x235; 243 | int x236; 244 | int x237; 245 | int x238; 246 | int x239; 247 | int x240; 248 | int x241; 249 | int x242; 250 | int x243; 251 | int x244; 252 | int x245; 253 | int x246; 254 | int x247; 255 | int x248; 256 | int x249; 257 | int x250; 258 | int x251; 259 | int x252; 260 | int x253; 261 | int x254; 262 | int x255; 263 | int x256; 264 | int x257; 265 | int x258; 266 | int x259; 267 | int x260; 268 | int x261; 269 | int x262; 270 | int x263; 271 | int x264; 272 | int x265; 273 | int x266; 274 | int x267; 275 | int x268; 276 | int x269; 277 | int x270; 278 | int x271; 279 | int x272; 280 | int x273; 281 | int x274; 282 | int x275; 283 | int x276; 284 | int x277; 285 | int x278; 286 | int x279; 287 | int x280; 288 | int x281; 289 | int x282; 290 | int x283; 291 | int x284; 292 | int x285; 293 | int x286; 294 | int x287; 295 | int x288; 296 | int x289; 297 | int x290; 298 | int x291; 299 | int x292; 300 | int x293; 301 | int x294; 302 | int x295; 303 | int x296; 304 | int x297; 305 | int x298; 306 | int x299; 307 | 308 | public static void main(String[] args) { 309 | for (int i = 0; i < 300; i++) { 310 | System.out.println("int x" + i + ";"); 311 | } 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /test/test/TestObject.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | /** 4 | * Created by Will on 2017/2/7. 5 | */ 6 | public class TestObject { 7 | static String fs; 8 | public Double fd; 9 | public int fi; 10 | private long fl; 11 | 12 | public TestObject() { 13 | fs = "TestObject0"; 14 | } 15 | 16 | public TestObject(int fi1, Double fd1, String fs1, long l) {} 17 | 18 | static String func1(String str) { 19 | fs = str; 20 | return str; 21 | } 22 | 23 | public String func2(int fi1, Double fd1, String fs1, long l) { 24 | return fs; 25 | } 26 | } 27 | --------------------------------------------------------------------------------