├── .classpath ├── .gitignore ├── .project ├── .settings ├── org.eclipse.core.resources.prefs └── org.eclipse.jdt.core.prefs ├── .travis.yml ├── CONTRIBUTING.md ├── README.md ├── build └── junit-4.6.jar ├── lib └── asm-5.1.jar ├── license.txt ├── pom.xml ├── project.yaml ├── src └── com │ └── esotericsoftware │ └── reflectasm │ ├── AccessClassLoader.java │ ├── ConstructorAccess.java │ ├── FieldAccess.java │ ├── MethodAccess.java │ └── PublicConstructorAccess.java └── test └── com └── esotericsoftware └── reflectasm ├── ClassLoaderTest.java ├── ConstructorAccessTest.java ├── FieldAccessTest.java ├── MethodAccessTest.java └── benchmark ├── Benchmark.java ├── ConstructorAccessBenchmark.java ├── FieldAccessBenchmark.java └── MethodAccessBenchmark.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/=UTF-8 3 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 4 | org.eclipse.jdt.core.compiler.compliance=1.8 5 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 6 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 7 | org.eclipse.jdt.core.compiler.release=enabled 8 | org.eclipse.jdt.core.compiler.source=1.8 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - openjdk7 5 | - oraclejdk8 6 | 7 | cache: 8 | directories: 9 | - $HOME/.m2 10 | 11 | # use container 12 | sudo: false 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Project license(s): 3-Clause BSD License 2 | 3 | * You will only Submit Contributions where You have authored 100% of the content. 4 | 5 | * You will only Submit Contributions to which You have the necessary rights. This means that if You are employed You have received the necessary permissions from Your employer to make the Contributions. 6 | 7 | * Whatever content You Contribute will be provided under the Project License(s). 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://raw.github.com/wiki/EsotericSoftware/reflectasm/images/logo.png) 2 | 3 | [![Build Status](https://travis-ci.org/EsotericSoftware/reflectasm.png?branch=master)](https://travis-ci.org/EsotericSoftware/reflectasm) 4 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.esotericsoftware/reflectasm/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.esotericsoftware%22%20AND%20a%3Areflectasm) 5 | 6 | Please use the [ReflectASM discussion group](http://groups.google.com/group/reflectasm-users) for support. 7 | 8 | ## Overview 9 | 10 | 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. 11 | 12 | ## Performance 13 | 14 | ![](http://chart.apis.google.com/chart?chma=100&chtt=Field%20Set/Get&chs=700x62&chd=t:1402081,11339107&chds=0,11339107&chxl=0:|Java%20Reflection|FieldAccess&cht=bhg&chbh=10&chxt=y&chco=6600FF) 15 | 16 | ![](http://chart.apis.google.com/chart?chma=100&chtt=Method%20Call&chs=700x62&chd=t:97390,208750&chds=0,208750&chxl=0:|Java%20Reflection|MethodAccess&cht=bhg&chbh=10&chxt=y&chco=6600AA) 17 | 18 | ![](http://chart.apis.google.com/chart?chma=100&chtt=Constructor&chs=700x62&chd=t:2853063,5828993&chds=0,5828993&chxl=0:|Java%20Reflection|ConstructorAccess&cht=bhg&chbh=10&chxt=y&chco=660066) 19 | 20 | The source code for these benchmarks is included in the project. The above charts were generated on Oracle's Java 7u3, server VM. 21 | 22 | ## Installation 23 | 24 | To use reflectasm with maven, please use the following snippet in your pom.xml 25 | 26 | ```xml 27 | 28 | com.esotericsoftware 29 | reflectasm 30 | 1.11.9 31 | 32 | ``` 33 | 34 | ## Usage 35 | 36 | Method reflection with ReflectASM: 37 | 38 | ```java 39 | SomeClass someObject = ... 40 | MethodAccess access = MethodAccess.get(SomeClass.class); 41 | access.invoke(someObject, "setName", "Awesome McLovin"); 42 | String name = (String)access.invoke(someObject, "getName"); 43 | ``` 44 | 45 | Field reflection with ReflectASM: 46 | 47 | ```java 48 | SomeClass someObject = ... 49 | FieldAccess access = FieldAccess.get(SomeClass.class); 50 | access.set(someObject, "name", "Awesome McLovin"); 51 | String name = (String)access.get(someObject, "name"); 52 | ``` 53 | 54 | Constructor reflection with ReflectASM: 55 | 56 | ```java 57 | ConstructorAccess access = ConstructorAccess.get(SomeClass.class); 58 | SomeClass someObject = access.newInstance(); 59 | ``` 60 | 61 | ## Avoiding Name Lookup 62 | 63 | For maximum performance when methods or fields are accessed repeatedly, the method or field index should be used instead of the name: 64 | 65 | ```java 66 | SomeClass someObject = ... 67 | MethodAccess access = MethodAccess.get(SomeClass.class); 68 | int addNameIndex = access.getIndex("addName"); 69 | for (String name : names) 70 | access.invoke(someObject, addNameIndex, "Awesome McLovin"); 71 | ``` 72 | 73 | Iterate all fields: 74 | 75 | ```java 76 | FieldAccess access = FieldAccess.get(SomeClass.class); 77 | for(int i = 0, n = access.getFieldCount(); i < n; i++) { 78 | access.set(instanceObject, i, valueToPut); 79 | } 80 | ``` 81 | 82 | ## Visibility 83 | 84 | ReflectASM can always access public members. An attempt is made to define access classes in the same classloader (using setAccessible) and package as the accessed class. If the security manager allows setAccessible to succeed, then protected and default access (package private) members can be accessed. If setAccessible fails, no exception is thrown, but only public members can be accessed. Private members can never be accessed. 85 | 86 | ## Exceptions 87 | 88 | Stack traces when using ReflectASM are a bit cleaner. Here is Java's reflection calling a method that throws a RuntimeException: 89 | 90 | ``` 91 | Exception in thread "main" java.lang.reflect.InvocationTargetException 92 | at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 93 | at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 94 | at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 95 | at java.lang.reflect.Method.invoke(Method.java:597) 96 | at com.example.SomeCallingCode.doit(SomeCallingCode.java:22) 97 | Caused by: java.lang.RuntimeException 98 | at com.example.SomeClass.someMethod(SomeClass.java:48) 99 | ... 5 more 100 | ``` 101 | 102 | Here is the same but when ReflectASM is used: 103 | 104 | ``` 105 | Exception in thread "main" java.lang.RuntimeException 106 | at com.example.SomeClass.someMethod(SomeClass.java:48) 107 | at com.example.SomeClassMethodAccess.invoke(Unknown Source) 108 | at com.example.SomeCallingCode.doit(SomeCallingCode.java:22) 109 | ``` 110 | 111 | 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. 112 | -------------------------------------------------------------------------------- /build/junit-4.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EsotericSoftware/reflectasm/e34c5e8b816ec49b094e8e1d29843458ee77a90b/build/junit-4.6.jar -------------------------------------------------------------------------------- /lib/asm-5.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EsotericSoftware/reflectasm/e34c5e8b816ec49b094e8e1d29843458ee77a90b/lib/asm-5.1.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 | 2 | 3 | org.sonatype.oss 4 | oss-parent 5 | 7 6 | 7 | 4.0.0 8 | com.esotericsoftware 9 | reflectasm 10 | 1.11.10-SNAPSHOT 11 | bundle 12 | ReflectASM 13 | High performance Java reflection using code generation 14 | https://github.com/EsotericSoftware/reflectasm 15 | 16 | 17 | 18 | 3-Clause BSD License 19 | https://opensource.org/licenses/BSD-3-Clause 20 | repo 21 | 22 | 23 | 24 | 25 | https://github.com/EsotericSoftware/reflectasm 26 | scm:git:git@github.com:EsotericSoftware/reflectasm.git 27 | scm:git:git@github.com:EsotericSoftware/reflectasm.git 28 | 29 | 30 | 31 | 32 | nathan.sweet 33 | Nathan Sweet 34 | nathan.sweet@gmail.com 35 | 36 | 37 | 38 | 39 | UTF-8 40 | 41 | 42 | 43 | 44 | org.ow2.asm 45 | asm 46 | 5.1 47 | 48 | 49 | junit 50 | junit 51 | 4.8.2 52 | test 53 | 54 | 55 | 56 | 57 | 58 | src 59 | test 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-compiler-plugin 65 | 3.1 66 | 67 | 1.5 68 | 1.5 69 | 70 | 71 | 72 | 73 | maven-resources-plugin 74 | 2.5 75 | 76 | 77 | default-resources 78 | none 79 | 80 | 81 | default-testResources 82 | none 83 | 84 | 85 | 86 | 87 | org.apache.maven.plugins 88 | maven-jar-plugin 89 | 2.4 90 | 91 | 92 | 93 | com.esotericsoftware.reflectasm 94 | 95 | 96 | 97 | 98 | 99 | org.apache.maven.plugins 100 | maven-shade-plugin 101 | 2.3 102 | 103 | 104 | package 105 | 106 | shade 107 | 108 | 109 | 110 | true 111 | false 112 | 113 | 114 | org.objectweb.asm 115 | com.esotericsoftware.asm 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | org.apache.felix 124 | maven-bundle-plugin 125 | 2.5.0 126 | true 127 | 128 | 129 | com.esotericsoftware.reflectasm* 130 | !org.objectweb.asm.* 131 | 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /project.yaml: -------------------------------------------------------------------------------- 1 | version: 1.11.4 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 | if (name.equals(PublicConstructorAccess.class.getName())) return PublicConstructorAccess.class; 66 | // All other classes come from the classloader that loaded the type we are accessing. 67 | return super.loadClass(name, resolve); 68 | } 69 | 70 | Class defineClass (String name, byte[] bytes) throws ClassFormatError { 71 | try { 72 | // Attempt to load the access class in the same loader, which makes protected and default access members accessible. 73 | return (Class)getDefineClassMethod().invoke(getParent(), 74 | new Object[] {name, bytes, Integer.valueOf(0), Integer.valueOf(bytes.length), getClass().getProtectionDomain()}); 75 | } catch (Exception ignored) { 76 | // continue with the definition in the current loader (won't have access to protected and package-protected members) 77 | } 78 | return defineClass(name, bytes, 0, bytes.length, getClass().getProtectionDomain()); 79 | } 80 | 81 | // As per JLS, section 5.3, 82 | // "The runtime package of a class or interface is determined by the package name and defining class loader of the class or 83 | // interface." 84 | static boolean areInSameRuntimeClassLoader (Class type1, Class type2) { 85 | if (type1.getPackage() != type2.getPackage()) { 86 | return false; 87 | } 88 | ClassLoader loader1 = type1.getClassLoader(); 89 | ClassLoader loader2 = type2.getClassLoader(); 90 | ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); 91 | if (loader1 == null) { 92 | return (loader2 == null || loader2 == systemClassLoader); 93 | } 94 | if (loader2 == null) return loader1 == systemClassLoader; 95 | return loader1 == loader2; 96 | } 97 | 98 | static private ClassLoader getParentClassLoader (Class type) { 99 | ClassLoader parent = type.getClassLoader(); 100 | if (parent == null) parent = ClassLoader.getSystemClassLoader(); 101 | return parent; 102 | } 103 | 104 | static private Method getDefineClassMethod () throws Exception { 105 | if (defineClassMethod == null) { 106 | synchronized (accessClassLoaders) { 107 | if (defineClassMethod == null) { 108 | defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", 109 | new Class[] {String.class, byte[].class, int.class, int.class, ProtectionDomain.class}); 110 | try { 111 | defineClassMethod.setAccessible(true); 112 | } catch (Exception ignored) { 113 | } 114 | } 115 | } 116 | } 117 | return defineClassMethod; 118 | } 119 | 120 | static AccessClassLoader get (Class type) { 121 | ClassLoader parent = getParentClassLoader(type); 122 | // 1. fast-path: 123 | if (selfContextParentClassLoader.equals(parent)) { 124 | if (selfContextAccessClassLoader == null) { 125 | synchronized (accessClassLoaders) { // DCL with volatile semantics 126 | if (selfContextAccessClassLoader == null) 127 | selfContextAccessClassLoader = new AccessClassLoader(selfContextParentClassLoader); 128 | } 129 | } 130 | return selfContextAccessClassLoader; 131 | } 132 | // 2. normal search: 133 | synchronized (accessClassLoaders) { 134 | WeakReference ref = accessClassLoaders.get(parent); 135 | if (ref != null) { 136 | AccessClassLoader accessClassLoader = ref.get(); 137 | if (accessClassLoader != null) 138 | return accessClassLoader; 139 | else 140 | accessClassLoaders.remove(parent); // the value has been GC-reclaimed, but still not the key (defensive sanity) 141 | } 142 | AccessClassLoader accessClassLoader = new AccessClassLoader(parent); 143 | accessClassLoaders.put(parent, new WeakReference(accessClassLoader)); 144 | return accessClassLoader; 145 | } 146 | } 147 | 148 | static public void remove (ClassLoader parent) { 149 | // 1. fast-path: 150 | if (selfContextParentClassLoader.equals(parent)) { 151 | selfContextAccessClassLoader = null; 152 | } else { 153 | // 2. normal search: 154 | synchronized (accessClassLoaders) { 155 | accessClassLoaders.remove(parent); 156 | } 157 | } 158 | } 159 | 160 | static public int activeAccessClassLoaders () { 161 | int sz = accessClassLoaders.size(); 162 | if (selfContextAccessClassLoader != null) sz++; 163 | return sz; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/reflectasm/ConstructorAccess.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 static org.objectweb.asm.Opcodes.*; 18 | 19 | import java.lang.reflect.Constructor; 20 | import java.lang.reflect.Modifier; 21 | 22 | import org.objectweb.asm.ClassWriter; 23 | import org.objectweb.asm.MethodVisitor; 24 | 25 | abstract public class ConstructorAccess { 26 | boolean isNonStaticMemberClass; 27 | 28 | public boolean isNonStaticMemberClass () { 29 | return isNonStaticMemberClass; 30 | } 31 | 32 | /** Constructor for top-level classes and static nested classes. 33 | *

34 | * If the underlying class is a inner (non-static nested) class, a new instance will be created using null as the 35 | * this$0 synthetic reference. The instantiated object will work as long as it actually don't use any member variable or method 36 | * fron the enclosing instance. */ 37 | abstract public T newInstance (); 38 | 39 | /** Constructor for inner classes (non-static nested classes). 40 | * @param enclosingInstance The instance of the enclosing type to which this inner instance is related to (assigned to its 41 | * synthetic this$0 field). */ 42 | abstract public T newInstance (Object enclosingInstance); 43 | 44 | static public ConstructorAccess get (Class type) { 45 | Class enclosingType = type.getEnclosingClass(); 46 | boolean isNonStaticMemberClass = enclosingType != null && type.isMemberClass() && !Modifier.isStatic(type.getModifiers()); 47 | 48 | String className = type.getName(); 49 | String accessClassName = className + "ConstructorAccess"; 50 | if (accessClassName.startsWith("java.")) accessClassName = "reflectasm." + accessClassName; 51 | 52 | Class accessClass; 53 | AccessClassLoader loader = AccessClassLoader.get(type); 54 | synchronized (loader) { 55 | accessClass = loader.loadAccessClass(accessClassName); 56 | if (accessClass == null) { 57 | String accessClassNameInternal = accessClassName.replace('.', '/'); 58 | String classNameInternal = className.replace('.', '/'); 59 | String enclosingClassNameInternal; 60 | Constructor constructor = null; 61 | int modifiers = 0; 62 | if (!isNonStaticMemberClass) { 63 | enclosingClassNameInternal = null; 64 | try { 65 | constructor = type.getDeclaredConstructor((Class[])null); 66 | modifiers = constructor.getModifiers(); 67 | } catch (Exception ex) { 68 | throw new RuntimeException("Class cannot be created (missing no-arg constructor): " + type.getName(), ex); 69 | } 70 | if (Modifier.isPrivate(modifiers)) { 71 | throw new RuntimeException("Class cannot be created (the no-arg constructor is private): " + type.getName()); 72 | } 73 | } else { 74 | enclosingClassNameInternal = enclosingType.getName().replace('.', '/'); 75 | try { 76 | constructor = type.getDeclaredConstructor(enclosingType); // Inner classes should have this. 77 | modifiers = constructor.getModifiers(); 78 | } catch (Exception ex) { 79 | throw new RuntimeException( 80 | "Non-static member class cannot be created (missing enclosing class constructor): " + type.getName(), ex); 81 | } 82 | if (Modifier.isPrivate(modifiers)) { 83 | throw new RuntimeException( 84 | "Non-static member class cannot be created (the enclosing class constructor is private): " + type.getName()); 85 | } 86 | } 87 | String superclassNameInternal = Modifier.isPublic(modifiers) 88 | ? "com/esotericsoftware/reflectasm/PublicConstructorAccess" 89 | : "com/esotericsoftware/reflectasm/ConstructorAccess"; 90 | 91 | ClassWriter cw = new ClassWriter(0); 92 | cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, accessClassNameInternal, null, superclassNameInternal, null); 93 | 94 | insertConstructor(cw, superclassNameInternal); 95 | insertNewInstance(cw, classNameInternal); 96 | insertNewInstanceInner(cw, classNameInternal, enclosingClassNameInternal); 97 | 98 | cw.visitEnd(); 99 | accessClass = loader.defineAccessClass(accessClassName, cw.toByteArray()); 100 | } 101 | } 102 | ConstructorAccess access; 103 | try { 104 | access = (ConstructorAccess)accessClass.newInstance(); 105 | } catch (Throwable t) { 106 | throw new RuntimeException("Exception constructing constructor access class: " + accessClassName, t); 107 | } 108 | if (!(access instanceof PublicConstructorAccess) && !AccessClassLoader.areInSameRuntimeClassLoader(type, accessClass)) { 109 | // Must test this after the try-catch block, whether the class has been loaded as if has been defined. 110 | // Throw a Runtime exception here instead of an IllegalAccessError when invoking newInstance() 111 | throw new RuntimeException((!isNonStaticMemberClass 112 | ? "Class cannot be created (the no-arg constructor is protected or package-protected, and its ConstructorAccess could not be defined in the same class loader): " 113 | : "Non-static member class cannot be created (the enclosing class constructor is protected or package-protected, and its ConstructorAccess could not be defined in the same class loader): ") 114 | + type.getName()); 115 | } 116 | access.isNonStaticMemberClass = isNonStaticMemberClass; 117 | return access; 118 | } 119 | 120 | static private void insertConstructor (ClassWriter cw, String superclassNameInternal) { 121 | MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); 122 | mv.visitCode(); 123 | mv.visitVarInsn(ALOAD, 0); 124 | mv.visitMethodInsn(INVOKESPECIAL, superclassNameInternal, "", "()V"); 125 | mv.visitInsn(RETURN); 126 | mv.visitMaxs(1, 1); 127 | mv.visitEnd(); 128 | } 129 | 130 | static void insertNewInstance (ClassWriter cw, String classNameInternal) { 131 | MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "()Ljava/lang/Object;", null, null); 132 | mv.visitCode(); 133 | mv.visitTypeInsn(NEW, classNameInternal); 134 | mv.visitInsn(DUP); 135 | mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "", "()V"); 136 | mv.visitInsn(ARETURN); 137 | mv.visitMaxs(2, 1); 138 | mv.visitEnd(); 139 | } 140 | 141 | static void insertNewInstanceInner (ClassWriter cw, String classNameInternal, String enclosingClassNameInternal) { 142 | MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null); 143 | mv.visitCode(); 144 | if (enclosingClassNameInternal != null) { 145 | mv.visitTypeInsn(NEW, classNameInternal); 146 | mv.visitInsn(DUP); 147 | mv.visitVarInsn(ALOAD, 1); 148 | mv.visitTypeInsn(CHECKCAST, enclosingClassNameInternal); 149 | mv.visitInsn(DUP); 150 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;"); 151 | mv.visitInsn(POP); 152 | mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "", "(L" + enclosingClassNameInternal + ";)V"); 153 | mv.visitInsn(ARETURN); 154 | mv.visitMaxs(4, 2); 155 | } else { 156 | mv.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException"); 157 | mv.visitInsn(DUP); 158 | mv.visitLdcInsn("Not an inner class."); 159 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/UnsupportedOperationException", "", "(Ljava/lang/String;)V"); 160 | mv.visitInsn(ATHROW); 161 | mv.visitMaxs(3, 2); 162 | } 163 | mv.visitEnd(); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/reflectasm/FieldAccess.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 static org.objectweb.asm.Opcodes.*; 18 | 19 | import java.lang.reflect.Field; 20 | import java.lang.reflect.Modifier; 21 | import java.util.ArrayList; 22 | 23 | import org.objectweb.asm.ClassWriter; 24 | import org.objectweb.asm.Label; 25 | import org.objectweb.asm.MethodVisitor; 26 | import org.objectweb.asm.Type; 27 | 28 | public abstract class FieldAccess { 29 | private String[] fieldNames; 30 | private Class[] fieldTypes; 31 | private Field[] fields; 32 | 33 | public int getIndex (String fieldName) { 34 | for (int i = 0, n = fieldNames.length; i < n; i++) 35 | if (fieldNames[i].equals(fieldName)) return i; 36 | throw new IllegalArgumentException("Unable to find non-private field: " + fieldName); 37 | } 38 | 39 | public int getIndex (Field field) { 40 | for (int i = 0, n = fields.length; i < n; i++) 41 | if (fields[i].equals(field)) return i; 42 | throw new IllegalArgumentException("Unable to find non-private field: " + field); 43 | } 44 | 45 | public void set (Object instance, String fieldName, Object value) { 46 | set(instance, getIndex(fieldName), value); 47 | } 48 | 49 | public Object get (Object instance, String fieldName) { 50 | return get(instance, getIndex(fieldName)); 51 | } 52 | 53 | public String[] getFieldNames () { 54 | return fieldNames; 55 | } 56 | 57 | public Class[] getFieldTypes () { 58 | return fieldTypes; 59 | } 60 | 61 | public int getFieldCount () { 62 | return fieldTypes.length; 63 | } 64 | 65 | public Field[] getFields () { 66 | return fields; 67 | } 68 | 69 | public void setFields (Field[] fields) { 70 | this.fields = fields; 71 | } 72 | 73 | abstract public void set (Object instance, int fieldIndex, Object value); 74 | 75 | abstract public void setBoolean (Object instance, int fieldIndex, boolean value); 76 | 77 | abstract public void setByte (Object instance, int fieldIndex, byte value); 78 | 79 | abstract public void setShort (Object instance, int fieldIndex, short value); 80 | 81 | abstract public void setInt (Object instance, int fieldIndex, int value); 82 | 83 | abstract public void setLong (Object instance, int fieldIndex, long value); 84 | 85 | abstract public void setDouble (Object instance, int fieldIndex, double value); 86 | 87 | abstract public void setFloat (Object instance, int fieldIndex, float value); 88 | 89 | abstract public void setChar (Object instance, int fieldIndex, char value); 90 | 91 | abstract public Object get (Object instance, int fieldIndex); 92 | 93 | abstract public String getString (Object instance, int fieldIndex); 94 | 95 | abstract public char getChar (Object instance, int fieldIndex); 96 | 97 | abstract public boolean getBoolean (Object instance, int fieldIndex); 98 | 99 | abstract public byte getByte (Object instance, int fieldIndex); 100 | 101 | abstract public short getShort (Object instance, int fieldIndex); 102 | 103 | abstract public int getInt (Object instance, int fieldIndex); 104 | 105 | abstract public long getLong (Object instance, int fieldIndex); 106 | 107 | abstract public double getDouble (Object instance, int fieldIndex); 108 | 109 | abstract public float getFloat (Object instance, int fieldIndex); 110 | 111 | /** @param type Must not be the Object class, an interface, a primitive type, or void. */ 112 | static public FieldAccess get (Class type) { 113 | if (type.getSuperclass() == null) 114 | throw new IllegalArgumentException("The type must not be the Object class, an interface, a primitive type, or void."); 115 | 116 | ArrayList fields = new ArrayList(); 117 | Class nextClass = type; 118 | while (nextClass != Object.class) { 119 | Field[] declaredFields = nextClass.getDeclaredFields(); 120 | for (int i = 0, n = declaredFields.length; i < n; i++) { 121 | Field field = declaredFields[i]; 122 | int modifiers = field.getModifiers(); 123 | if (Modifier.isStatic(modifiers)) continue; 124 | if (Modifier.isPrivate(modifiers)) continue; 125 | fields.add(field); 126 | } 127 | nextClass = nextClass.getSuperclass(); 128 | } 129 | 130 | String[] fieldNames = new String[fields.size()]; 131 | Class[] fieldTypes = new Class[fields.size()]; 132 | for (int i = 0, n = fieldNames.length; i < n; i++) { 133 | fieldNames[i] = fields.get(i).getName(); 134 | fieldTypes[i] = fields.get(i).getType(); 135 | } 136 | 137 | String className = type.getName(); 138 | String accessClassName = className + "FieldAccess"; 139 | if (accessClassName.startsWith("java.")) accessClassName = "reflectasm." + accessClassName; 140 | 141 | Class accessClass; 142 | AccessClassLoader loader = AccessClassLoader.get(type); 143 | synchronized (loader) { 144 | accessClass = loader.loadAccessClass(accessClassName); 145 | if (accessClass == null) { 146 | String accessClassNameInternal = accessClassName.replace('.', '/'); 147 | String classNameInternal = className.replace('.', '/'); 148 | 149 | ClassWriter cw = new ClassWriter(0); 150 | cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER + ACC_SYNTHETIC, accessClassNameInternal, null, "com/esotericsoftware/reflectasm/FieldAccess", 151 | null); 152 | insertConstructor(cw); 153 | insertGetObject(cw, classNameInternal, fields); 154 | insertSetObject(cw, classNameInternal, fields); 155 | insertGetPrimitive(cw, classNameInternal, fields, Type.BOOLEAN_TYPE); 156 | insertSetPrimitive(cw, classNameInternal, fields, Type.BOOLEAN_TYPE); 157 | insertGetPrimitive(cw, classNameInternal, fields, Type.BYTE_TYPE); 158 | insertSetPrimitive(cw, classNameInternal, fields, Type.BYTE_TYPE); 159 | insertGetPrimitive(cw, classNameInternal, fields, Type.SHORT_TYPE); 160 | insertSetPrimitive(cw, classNameInternal, fields, Type.SHORT_TYPE); 161 | insertGetPrimitive(cw, classNameInternal, fields, Type.INT_TYPE); 162 | insertSetPrimitive(cw, classNameInternal, fields, Type.INT_TYPE); 163 | insertGetPrimitive(cw, classNameInternal, fields, Type.LONG_TYPE); 164 | insertSetPrimitive(cw, classNameInternal, fields, Type.LONG_TYPE); 165 | insertGetPrimitive(cw, classNameInternal, fields, Type.DOUBLE_TYPE); 166 | insertSetPrimitive(cw, classNameInternal, fields, Type.DOUBLE_TYPE); 167 | insertGetPrimitive(cw, classNameInternal, fields, Type.FLOAT_TYPE); 168 | insertSetPrimitive(cw, classNameInternal, fields, Type.FLOAT_TYPE); 169 | insertGetPrimitive(cw, classNameInternal, fields, Type.CHAR_TYPE); 170 | insertSetPrimitive(cw, classNameInternal, fields, Type.CHAR_TYPE); 171 | insertGetString(cw, classNameInternal, fields); 172 | cw.visitEnd(); 173 | accessClass = loader.defineAccessClass(accessClassName, cw.toByteArray()); 174 | } 175 | } 176 | try { 177 | FieldAccess access = (FieldAccess)accessClass.newInstance(); 178 | access.fieldNames = fieldNames; 179 | access.fieldTypes = fieldTypes; 180 | access.fields = fields.toArray(new Field[fields.size()]); 181 | return access; 182 | } catch (Throwable t) { 183 | throw new RuntimeException("Error constructing field access class: " + accessClassName, t); 184 | } 185 | } 186 | 187 | static private void insertConstructor (ClassWriter cw) { 188 | MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); 189 | mv.visitCode(); 190 | mv.visitVarInsn(ALOAD, 0); 191 | mv.visitMethodInsn(INVOKESPECIAL, "com/esotericsoftware/reflectasm/FieldAccess", "", "()V"); 192 | mv.visitInsn(RETURN); 193 | mv.visitMaxs(1, 1); 194 | mv.visitEnd(); 195 | } 196 | 197 | static private void insertSetObject (ClassWriter cw, String classNameInternal, ArrayList fields) { 198 | int maxStack = 6; 199 | MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "set", "(Ljava/lang/Object;ILjava/lang/Object;)V", null, null); 200 | mv.visitCode(); 201 | mv.visitVarInsn(ILOAD, 2); 202 | 203 | if (!fields.isEmpty()) { 204 | maxStack--; 205 | Label[] labels = new Label[fields.size()]; 206 | for (int i = 0, n = labels.length; i < n; i++) 207 | labels[i] = new Label(); 208 | Label defaultLabel = new Label(); 209 | mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); 210 | 211 | for (int i = 0, n = labels.length; i < n; i++) { 212 | Field field = fields.get(i); 213 | Type fieldType = Type.getType(field.getType()); 214 | 215 | mv.visitLabel(labels[i]); 216 | mv.visitFrame(F_SAME, 0, null, 0, null); 217 | mv.visitVarInsn(ALOAD, 1); 218 | mv.visitTypeInsn(CHECKCAST, classNameInternal); 219 | mv.visitVarInsn(ALOAD, 3); 220 | 221 | switch (fieldType.getSort()) { 222 | case Type.BOOLEAN: 223 | mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean"); 224 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z"); 225 | break; 226 | case Type.BYTE: 227 | mv.visitTypeInsn(CHECKCAST, "java/lang/Byte"); 228 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B"); 229 | break; 230 | case Type.CHAR: 231 | mv.visitTypeInsn(CHECKCAST, "java/lang/Character"); 232 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C"); 233 | break; 234 | case Type.SHORT: 235 | mv.visitTypeInsn(CHECKCAST, "java/lang/Short"); 236 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S"); 237 | break; 238 | case Type.INT: 239 | mv.visitTypeInsn(CHECKCAST, "java/lang/Integer"); 240 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I"); 241 | break; 242 | case Type.FLOAT: 243 | mv.visitTypeInsn(CHECKCAST, "java/lang/Float"); 244 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F"); 245 | break; 246 | case Type.LONG: 247 | mv.visitTypeInsn(CHECKCAST, "java/lang/Long"); 248 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J"); 249 | break; 250 | case Type.DOUBLE: 251 | mv.visitTypeInsn(CHECKCAST, "java/lang/Double"); 252 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D"); 253 | break; 254 | case Type.ARRAY: 255 | mv.visitTypeInsn(CHECKCAST, fieldType.getDescriptor()); 256 | break; 257 | case Type.OBJECT: 258 | mv.visitTypeInsn(CHECKCAST, fieldType.getInternalName()); 259 | break; 260 | } 261 | 262 | mv.visitFieldInsn(PUTFIELD, field.getDeclaringClass().getName().replace('.', '/'), field.getName(), 263 | fieldType.getDescriptor()); 264 | mv.visitInsn(RETURN); 265 | } 266 | 267 | mv.visitLabel(defaultLabel); 268 | mv.visitFrame(F_SAME, 0, null, 0, null); 269 | } 270 | mv = insertThrowExceptionForFieldNotFound(mv); 271 | mv.visitMaxs(maxStack, 4); 272 | mv.visitEnd(); 273 | } 274 | 275 | static private void insertGetObject (ClassWriter cw, String classNameInternal, ArrayList fields) { 276 | int maxStack = 6; 277 | MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "get", "(Ljava/lang/Object;I)Ljava/lang/Object;", null, null); 278 | mv.visitCode(); 279 | mv.visitVarInsn(ILOAD, 2); 280 | 281 | if (!fields.isEmpty()) { 282 | maxStack--; 283 | Label[] labels = new Label[fields.size()]; 284 | for (int i = 0, n = labels.length; i < n; i++) 285 | labels[i] = new Label(); 286 | Label defaultLabel = new Label(); 287 | mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); 288 | 289 | for (int i = 0, n = labels.length; i < n; i++) { 290 | Field field = fields.get(i); 291 | 292 | mv.visitLabel(labels[i]); 293 | mv.visitFrame(F_SAME, 0, null, 0, null); 294 | mv.visitVarInsn(ALOAD, 1); 295 | mv.visitTypeInsn(CHECKCAST, classNameInternal); 296 | mv.visitFieldInsn(GETFIELD, field.getDeclaringClass().getName().replace('.', '/'), field.getName(), 297 | Type.getDescriptor(field.getType())); 298 | 299 | Type fieldType = Type.getType(field.getType()); 300 | switch (fieldType.getSort()) { 301 | case Type.BOOLEAN: 302 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;"); 303 | break; 304 | case Type.BYTE: 305 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;"); 306 | break; 307 | case Type.CHAR: 308 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;"); 309 | break; 310 | case Type.SHORT: 311 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;"); 312 | break; 313 | case Type.INT: 314 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;"); 315 | break; 316 | case Type.FLOAT: 317 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;"); 318 | break; 319 | case Type.LONG: 320 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;"); 321 | break; 322 | case Type.DOUBLE: 323 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;"); 324 | break; 325 | } 326 | 327 | mv.visitInsn(ARETURN); 328 | } 329 | 330 | mv.visitLabel(defaultLabel); 331 | mv.visitFrame(F_SAME, 0, null, 0, null); 332 | } 333 | insertThrowExceptionForFieldNotFound(mv); 334 | mv.visitMaxs(maxStack, 3); 335 | mv.visitEnd(); 336 | } 337 | 338 | static private void insertGetString (ClassWriter cw, String classNameInternal, ArrayList fields) { 339 | int maxStack = 6; 340 | MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "getString", "(Ljava/lang/Object;I)Ljava/lang/String;", null, null); 341 | mv.visitCode(); 342 | mv.visitVarInsn(ILOAD, 2); 343 | 344 | if (!fields.isEmpty()) { 345 | maxStack--; 346 | Label[] labels = new Label[fields.size()]; 347 | Label labelForInvalidTypes = new Label(); 348 | boolean hasAnyBadTypeLabel = false; 349 | for (int i = 0, n = labels.length; i < n; i++) { 350 | if (fields.get(i).getType().equals(String.class)) 351 | labels[i] = new Label(); 352 | else { 353 | labels[i] = labelForInvalidTypes; 354 | hasAnyBadTypeLabel = true; 355 | } 356 | } 357 | Label defaultLabel = new Label(); 358 | mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); 359 | 360 | for (int i = 0, n = labels.length; i < n; i++) { 361 | if (!labels[i].equals(labelForInvalidTypes)) { 362 | Field field = fields.get(i); 363 | mv.visitLabel(labels[i]); 364 | mv.visitFrame(F_SAME, 0, null, 0, null); 365 | mv.visitVarInsn(ALOAD, 1); 366 | mv.visitTypeInsn(CHECKCAST, classNameInternal); 367 | mv.visitFieldInsn(GETFIELD, field.getDeclaringClass().getName().replace('.', '/'), field.getName(), 368 | "Ljava/lang/String;"); 369 | mv.visitInsn(ARETURN); 370 | } 371 | } 372 | // Rest of fields: different type 373 | if (hasAnyBadTypeLabel) { 374 | mv.visitLabel(labelForInvalidTypes); 375 | mv.visitFrame(F_SAME, 0, null, 0, null); 376 | insertThrowExceptionForFieldType(mv, "String"); 377 | } 378 | // Default: field not found 379 | mv.visitLabel(defaultLabel); 380 | mv.visitFrame(F_SAME, 0, null, 0, null); 381 | } 382 | insertThrowExceptionForFieldNotFound(mv); 383 | mv.visitMaxs(maxStack, 3); 384 | mv.visitEnd(); 385 | } 386 | 387 | static private void insertSetPrimitive (ClassWriter cw, String classNameInternal, ArrayList fields, 388 | Type primitiveType) { 389 | int maxStack = 6; 390 | int maxLocals = 4; // See correction below for LLOAD and DLOAD 391 | final String setterMethodName; 392 | final String typeNameInternal = primitiveType.getDescriptor(); 393 | final int loadValueInstruction; 394 | switch (primitiveType.getSort()) { 395 | case Type.BOOLEAN: 396 | setterMethodName = "setBoolean"; 397 | loadValueInstruction = ILOAD; 398 | break; 399 | case Type.BYTE: 400 | setterMethodName = "setByte"; 401 | loadValueInstruction = ILOAD; 402 | break; 403 | case Type.CHAR: 404 | setterMethodName = "setChar"; 405 | loadValueInstruction = ILOAD; 406 | break; 407 | case Type.SHORT: 408 | setterMethodName = "setShort"; 409 | loadValueInstruction = ILOAD; 410 | break; 411 | case Type.INT: 412 | setterMethodName = "setInt"; 413 | loadValueInstruction = ILOAD; 414 | break; 415 | case Type.FLOAT: 416 | setterMethodName = "setFloat"; 417 | loadValueInstruction = FLOAD; 418 | break; 419 | case Type.LONG: 420 | setterMethodName = "setLong"; 421 | loadValueInstruction = LLOAD; 422 | maxLocals++; // (LLOAD and DLOAD actually load two slots) 423 | break; 424 | case Type.DOUBLE: 425 | setterMethodName = "setDouble"; 426 | loadValueInstruction = DLOAD; 427 | maxLocals++; // (LLOAD and DLOAD actually load two slots) 428 | break; 429 | default: 430 | setterMethodName = "set"; 431 | loadValueInstruction = ALOAD; 432 | break; 433 | } 434 | MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, setterMethodName, "(Ljava/lang/Object;I" + typeNameInternal + ")V", null, 435 | null); 436 | mv.visitCode(); 437 | mv.visitVarInsn(ILOAD, 2); 438 | 439 | if (!fields.isEmpty()) { 440 | maxStack--; 441 | Label[] labels = new Label[fields.size()]; 442 | Label labelForInvalidTypes = new Label(); 443 | boolean hasAnyBadTypeLabel = false; 444 | for (int i = 0, n = labels.length; i < n; i++) { 445 | if (Type.getType(fields.get(i).getType()).equals(primitiveType)) 446 | labels[i] = new Label(); 447 | else { 448 | labels[i] = labelForInvalidTypes; 449 | hasAnyBadTypeLabel = true; 450 | } 451 | } 452 | Label defaultLabel = new Label(); 453 | mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); 454 | 455 | for (int i = 0, n = labels.length; i < n; i++) { 456 | if (!labels[i].equals(labelForInvalidTypes)) { 457 | Field field = fields.get(i); 458 | mv.visitLabel(labels[i]); 459 | mv.visitFrame(F_SAME, 0, null, 0, null); 460 | mv.visitVarInsn(ALOAD, 1); 461 | mv.visitTypeInsn(CHECKCAST, classNameInternal); 462 | mv.visitVarInsn(loadValueInstruction, 3); 463 | mv.visitFieldInsn(PUTFIELD, field.getDeclaringClass().getName().replace('.', '/'), field.getName(), 464 | typeNameInternal); 465 | mv.visitInsn(RETURN); 466 | } 467 | } 468 | // Rest of fields: different type 469 | if (hasAnyBadTypeLabel) { 470 | mv.visitLabel(labelForInvalidTypes); 471 | mv.visitFrame(F_SAME, 0, null, 0, null); 472 | insertThrowExceptionForFieldType(mv, primitiveType.getClassName()); 473 | } 474 | // Default: field not found 475 | mv.visitLabel(defaultLabel); 476 | mv.visitFrame(F_SAME, 0, null, 0, null); 477 | } 478 | mv = insertThrowExceptionForFieldNotFound(mv); 479 | mv.visitMaxs(maxStack, maxLocals); 480 | mv.visitEnd(); 481 | } 482 | 483 | static private void insertGetPrimitive (ClassWriter cw, String classNameInternal, ArrayList fields, 484 | Type primitiveType) { 485 | int maxStack = 6; 486 | final String getterMethodName; 487 | final String typeNameInternal = primitiveType.getDescriptor(); 488 | final int returnValueInstruction; 489 | switch (primitiveType.getSort()) { 490 | case Type.BOOLEAN: 491 | getterMethodName = "getBoolean"; 492 | returnValueInstruction = IRETURN; 493 | break; 494 | case Type.BYTE: 495 | getterMethodName = "getByte"; 496 | returnValueInstruction = IRETURN; 497 | break; 498 | case Type.CHAR: 499 | getterMethodName = "getChar"; 500 | returnValueInstruction = IRETURN; 501 | break; 502 | case Type.SHORT: 503 | getterMethodName = "getShort"; 504 | returnValueInstruction = IRETURN; 505 | break; 506 | case Type.INT: 507 | getterMethodName = "getInt"; 508 | returnValueInstruction = IRETURN; 509 | break; 510 | case Type.FLOAT: 511 | getterMethodName = "getFloat"; 512 | returnValueInstruction = FRETURN; 513 | break; 514 | case Type.LONG: 515 | getterMethodName = "getLong"; 516 | returnValueInstruction = LRETURN; 517 | break; 518 | case Type.DOUBLE: 519 | getterMethodName = "getDouble"; 520 | returnValueInstruction = DRETURN; 521 | break; 522 | default: 523 | getterMethodName = "get"; 524 | returnValueInstruction = ARETURN; 525 | break; 526 | } 527 | MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, getterMethodName, "(Ljava/lang/Object;I)" + typeNameInternal, null, null); 528 | mv.visitCode(); 529 | mv.visitVarInsn(ILOAD, 2); 530 | 531 | if (!fields.isEmpty()) { 532 | maxStack--; 533 | Label[] labels = new Label[fields.size()]; 534 | Label labelForInvalidTypes = new Label(); 535 | boolean hasAnyBadTypeLabel = false; 536 | for (int i = 0, n = labels.length; i < n; i++) { 537 | if (Type.getType(fields.get(i).getType()).equals(primitiveType)) 538 | labels[i] = new Label(); 539 | else { 540 | labels[i] = labelForInvalidTypes; 541 | hasAnyBadTypeLabel = true; 542 | } 543 | } 544 | Label defaultLabel = new Label(); 545 | mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); 546 | 547 | for (int i = 0, n = labels.length; i < n; i++) { 548 | Field field = fields.get(i); 549 | if (!labels[i].equals(labelForInvalidTypes)) { 550 | mv.visitLabel(labels[i]); 551 | mv.visitFrame(F_SAME, 0, null, 0, null); 552 | mv.visitVarInsn(ALOAD, 1); 553 | mv.visitTypeInsn(CHECKCAST, classNameInternal); 554 | mv.visitFieldInsn(GETFIELD, field.getDeclaringClass().getName().replace('.', '/'), field.getName(), 555 | typeNameInternal); 556 | mv.visitInsn(returnValueInstruction); 557 | } 558 | } 559 | // Rest of fields: different type 560 | if (hasAnyBadTypeLabel) { 561 | mv.visitLabel(labelForInvalidTypes); 562 | mv.visitFrame(F_SAME, 0, null, 0, null); 563 | insertThrowExceptionForFieldType(mv, primitiveType.getClassName()); 564 | } 565 | // Default: field not found 566 | mv.visitLabel(defaultLabel); 567 | mv.visitFrame(F_SAME, 0, null, 0, null); 568 | } 569 | mv = insertThrowExceptionForFieldNotFound(mv); 570 | mv.visitMaxs(maxStack, 3); 571 | mv.visitEnd(); 572 | } 573 | 574 | static private MethodVisitor insertThrowExceptionForFieldNotFound (MethodVisitor mv) { 575 | mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException"); 576 | mv.visitInsn(DUP); 577 | mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); 578 | mv.visitInsn(DUP); 579 | mv.visitLdcInsn("Field not found: "); 580 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "(Ljava/lang/String;)V"); 581 | mv.visitVarInsn(ILOAD, 2); 582 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;"); 583 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"); 584 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "", "(Ljava/lang/String;)V"); 585 | mv.visitInsn(ATHROW); 586 | return mv; 587 | } 588 | 589 | static private MethodVisitor insertThrowExceptionForFieldType (MethodVisitor mv, String fieldType) { 590 | mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException"); 591 | mv.visitInsn(DUP); 592 | mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); 593 | mv.visitInsn(DUP); 594 | mv.visitLdcInsn("Field not declared as " + fieldType + ": "); 595 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "(Ljava/lang/String;)V"); 596 | mv.visitVarInsn(ILOAD, 2); 597 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;"); 598 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"); 599 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "", "(Ljava/lang/String;)V"); 600 | mv.visitInsn(ATHROW); 601 | return mv; 602 | } 603 | 604 | } 605 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/reflectasm/MethodAccess.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 static org.objectweb.asm.Opcodes.*; 18 | 19 | import java.lang.reflect.Method; 20 | import java.lang.reflect.Modifier; 21 | import java.util.ArrayList; 22 | import java.util.Arrays; 23 | 24 | import org.objectweb.asm.ClassWriter; 25 | import org.objectweb.asm.Label; 26 | import org.objectweb.asm.MethodVisitor; 27 | import org.objectweb.asm.Opcodes; 28 | import org.objectweb.asm.Type; 29 | 30 | public abstract class MethodAccess { 31 | private String[] methodNames; 32 | private Class[][] parameterTypes; 33 | private Class[] returnTypes; 34 | 35 | abstract public Object invoke (Object object, int methodIndex, Object... args); 36 | 37 | /** Invokes the method with the specified name and the specified param types. */ 38 | public Object invoke (Object object, String methodName, Class[] paramTypes, Object... args) { 39 | return invoke(object, getIndex(methodName, paramTypes), args); 40 | } 41 | 42 | /** Invokes the first method with the specified name and the specified number of arguments. */ 43 | public Object invoke (Object object, String methodName, Object... args) { 44 | return invoke(object, getIndex(methodName, args == null ? 0 : args.length), args); 45 | } 46 | 47 | /** Returns the index of the first method with the specified name. */ 48 | public int getIndex (String methodName) { 49 | for (int i = 0, n = methodNames.length; i < n; i++) 50 | if (methodNames[i].equals(methodName)) return i; 51 | throw new IllegalArgumentException("Unable to find non-private method: " + methodName); 52 | } 53 | 54 | /** Returns the index of the first method with the specified name and param types. */ 55 | public int getIndex (String methodName, Class... paramTypes) { 56 | for (int i = 0, n = methodNames.length; i < n; i++) 57 | if (methodNames[i].equals(methodName) && Arrays.equals(paramTypes, parameterTypes[i])) return i; 58 | throw new IllegalArgumentException("Unable to find non-private method: " + methodName + " " + Arrays.toString(paramTypes)); 59 | } 60 | 61 | /** Returns the index of the first method with the specified name and the specified number of arguments. */ 62 | public int getIndex (String methodName, int paramsCount) { 63 | for (int i = 0, n = methodNames.length; i < n; i++) 64 | if (methodNames[i].equals(methodName) && parameterTypes[i].length == paramsCount) return i; 65 | throw new IllegalArgumentException( 66 | "Unable to find non-private method: " + methodName + " with " + paramsCount + " params."); 67 | } 68 | 69 | public String[] getMethodNames () { 70 | return methodNames; 71 | } 72 | 73 | public Class[][] getParameterTypes () { 74 | return parameterTypes; 75 | } 76 | 77 | public Class[] getReturnTypes () { 78 | return returnTypes; 79 | } 80 | 81 | /** Creates a new MethodAccess for the specified type. 82 | * @param type Must not be a primitive type, or void. */ 83 | static public MethodAccess get (Class type) { 84 | boolean isInterface = type.isInterface(); 85 | if (!isInterface && type.getSuperclass() == null && type != Object.class) 86 | throw new IllegalArgumentException("The type must not be an interface, a primitive type, or void."); 87 | 88 | ArrayList methods = new ArrayList(); 89 | if (!isInterface) { 90 | Class nextClass = type; 91 | while (nextClass != Object.class) { 92 | addDeclaredMethodsToList(nextClass, methods); 93 | nextClass = nextClass.getSuperclass(); 94 | } 95 | } else 96 | recursiveAddInterfaceMethodsToList(type, methods); 97 | 98 | int n = methods.size(); 99 | String[] methodNames = new String[n]; 100 | Class[][] parameterTypes = new Class[n][]; 101 | Class[] returnTypes = new Class[n]; 102 | for (int i = 0; i < n; i++) { 103 | Method method = methods.get(i); 104 | methodNames[i] = method.getName(); 105 | parameterTypes[i] = method.getParameterTypes(); 106 | returnTypes[i] = method.getReturnType(); 107 | } 108 | 109 | String className = type.getName(); 110 | String accessClassName = className + "MethodAccess"; 111 | if (accessClassName.startsWith("java.")) accessClassName = "reflectasm." + accessClassName; 112 | 113 | Class accessClass; 114 | AccessClassLoader loader = AccessClassLoader.get(type); 115 | synchronized (loader) { 116 | accessClass = loader.loadAccessClass(accessClassName); 117 | if (accessClass == null) { 118 | String accessClassNameInternal = accessClassName.replace('.', '/'); 119 | String classNameInternal = className.replace('.', '/'); 120 | 121 | ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); 122 | MethodVisitor mv; 123 | cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, accessClassNameInternal, null, "com/esotericsoftware/reflectasm/MethodAccess", 124 | null); 125 | { 126 | mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); 127 | mv.visitCode(); 128 | mv.visitVarInsn(ALOAD, 0); 129 | mv.visitMethodInsn(INVOKESPECIAL, "com/esotericsoftware/reflectasm/MethodAccess", "", "()V"); 130 | mv.visitInsn(RETURN); 131 | mv.visitMaxs(0, 0); 132 | mv.visitEnd(); 133 | } 134 | { 135 | mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "invoke", 136 | "(Ljava/lang/Object;I[Ljava/lang/Object;)Ljava/lang/Object;", null, null); 137 | mv.visitCode(); 138 | 139 | if (!methods.isEmpty()) { 140 | mv.visitVarInsn(ALOAD, 1); 141 | mv.visitTypeInsn(CHECKCAST, classNameInternal); 142 | mv.visitVarInsn(ASTORE, 4); 143 | 144 | mv.visitVarInsn(ILOAD, 2); 145 | Label[] labels = new Label[n]; 146 | for (int i = 0; i < n; i++) 147 | labels[i] = new Label(); 148 | Label defaultLabel = new Label(); 149 | mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); 150 | 151 | StringBuilder buffer = new StringBuilder(128); 152 | for (int i = 0; i < n; i++) { 153 | mv.visitLabel(labels[i]); 154 | if (i == 0) 155 | mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] {classNameInternal}, 0, null); 156 | else 157 | mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 158 | mv.visitVarInsn(ALOAD, 4); 159 | 160 | buffer.setLength(0); 161 | buffer.append('('); 162 | 163 | Class[] paramTypes = parameterTypes[i]; 164 | Class returnType = returnTypes[i]; 165 | for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) { 166 | mv.visitVarInsn(ALOAD, 3); 167 | mv.visitIntInsn(BIPUSH, paramIndex); 168 | mv.visitInsn(AALOAD); 169 | Type paramType = Type.getType(paramTypes[paramIndex]); 170 | switch (paramType.getSort()) { 171 | case Type.BOOLEAN: 172 | mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean"); 173 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z"); 174 | break; 175 | case Type.BYTE: 176 | mv.visitTypeInsn(CHECKCAST, "java/lang/Byte"); 177 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B"); 178 | break; 179 | case Type.CHAR: 180 | mv.visitTypeInsn(CHECKCAST, "java/lang/Character"); 181 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C"); 182 | break; 183 | case Type.SHORT: 184 | mv.visitTypeInsn(CHECKCAST, "java/lang/Short"); 185 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S"); 186 | break; 187 | case Type.INT: 188 | mv.visitTypeInsn(CHECKCAST, "java/lang/Integer"); 189 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I"); 190 | break; 191 | case Type.FLOAT: 192 | mv.visitTypeInsn(CHECKCAST, "java/lang/Float"); 193 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F"); 194 | break; 195 | case Type.LONG: 196 | mv.visitTypeInsn(CHECKCAST, "java/lang/Long"); 197 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J"); 198 | break; 199 | case Type.DOUBLE: 200 | mv.visitTypeInsn(CHECKCAST, "java/lang/Double"); 201 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D"); 202 | break; 203 | case Type.ARRAY: 204 | mv.visitTypeInsn(CHECKCAST, paramType.getDescriptor()); 205 | break; 206 | case Type.OBJECT: 207 | mv.visitTypeInsn(CHECKCAST, paramType.getInternalName()); 208 | break; 209 | } 210 | buffer.append(paramType.getDescriptor()); 211 | } 212 | 213 | buffer.append(')'); 214 | buffer.append(Type.getDescriptor(returnType)); 215 | int invoke; 216 | if (isInterface) 217 | invoke = INVOKEINTERFACE; 218 | else if (Modifier.isStatic(methods.get(i).getModifiers())) 219 | invoke = INVOKESTATIC; 220 | else 221 | invoke = INVOKEVIRTUAL; 222 | mv.visitMethodInsn(invoke, classNameInternal, methodNames[i], buffer.toString()); 223 | 224 | switch (Type.getType(returnType).getSort()) { 225 | case Type.VOID: 226 | mv.visitInsn(ACONST_NULL); 227 | break; 228 | case Type.BOOLEAN: 229 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;"); 230 | break; 231 | case Type.BYTE: 232 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;"); 233 | break; 234 | case Type.CHAR: 235 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;"); 236 | break; 237 | case Type.SHORT: 238 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;"); 239 | break; 240 | case Type.INT: 241 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;"); 242 | break; 243 | case Type.FLOAT: 244 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;"); 245 | break; 246 | case Type.LONG: 247 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;"); 248 | break; 249 | case Type.DOUBLE: 250 | mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;"); 251 | break; 252 | } 253 | 254 | mv.visitInsn(ARETURN); 255 | } 256 | 257 | mv.visitLabel(defaultLabel); 258 | mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 259 | } 260 | mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException"); 261 | mv.visitInsn(DUP); 262 | mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); 263 | mv.visitInsn(DUP); 264 | mv.visitLdcInsn("Method not found: "); 265 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "(Ljava/lang/String;)V"); 266 | mv.visitVarInsn(ILOAD, 2); 267 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;"); 268 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"); 269 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "", "(Ljava/lang/String;)V"); 270 | mv.visitInsn(ATHROW); 271 | mv.visitMaxs(0, 0); 272 | mv.visitEnd(); 273 | } 274 | cw.visitEnd(); 275 | byte[] data = cw.toByteArray(); 276 | accessClass = loader.defineAccessClass(accessClassName, data); 277 | } 278 | } 279 | try { 280 | MethodAccess access = (MethodAccess)accessClass.newInstance(); 281 | access.methodNames = methodNames; 282 | access.parameterTypes = parameterTypes; 283 | access.returnTypes = returnTypes; 284 | return access; 285 | } catch (Throwable t) { 286 | throw new RuntimeException("Error constructing method access class: " + accessClassName, t); 287 | } 288 | } 289 | 290 | static private void addDeclaredMethodsToList (Class type, ArrayList methods) { 291 | Method[] declaredMethods = type.getDeclaredMethods(); 292 | for (int i = 0, n = declaredMethods.length; i < n; i++) { 293 | Method method = declaredMethods[i]; 294 | int modifiers = method.getModifiers(); 295 | // if (Modifier.isStatic(modifiers)) continue; 296 | if (Modifier.isPrivate(modifiers)) continue; 297 | methods.add(method); 298 | } 299 | } 300 | 301 | static private void recursiveAddInterfaceMethodsToList (Class interfaceType, ArrayList methods) { 302 | addDeclaredMethodsToList(interfaceType, methods); 303 | for (Class nextInterface : interfaceType.getInterfaces()) 304 | recursiveAddInterfaceMethodsToList(nextInterface, methods); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /src/com/esotericsoftware/reflectasm/PublicConstructorAccess.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 | public abstract class PublicConstructorAccess extends ConstructorAccess { 18 | 19 | } -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/ClassLoaderTest.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.io.ByteArrayOutputStream; 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | import static junit.framework.Assert.assertEquals; 21 | import static junit.framework.Assert.assertFalse; 22 | import static junit.framework.Assert.assertTrue; 23 | 24 | import junit.framework.TestCase; 25 | 26 | public class ClassLoaderTest extends TestCase { 27 | public void testDifferentClassloaders () throws Exception { 28 | // This classloader can see only the Test class and core Java classes. 29 | ClassLoader testClassLoader = new TestClassLoader1(); 30 | Class testClass = testClassLoader.loadClass("com.esotericsoftware.reflectasm.ClassLoaderTest$Test"); 31 | Object testObject = testClass.newInstance(); 32 | 33 | // Ensure AccessClassLoader can access both the Test class and FieldAccess. 34 | FieldAccess access = FieldAccess.get(testObject.getClass()); 35 | access.set(testObject, "name", "first"); 36 | assertEquals("first", testObject.toString()); 37 | assertEquals("first", access.get(testObject, "name")); 38 | } 39 | 40 | public void testAutoUnloadClassloaders () throws Exception { 41 | reclaimLoaders(); 42 | int initialCount = AccessClassLoader.activeAccessClassLoaders(); 43 | 44 | ClassLoader testClassLoader1 = new TestClassLoader1(); 45 | Class testClass1 = testClassLoader1.loadClass("com.esotericsoftware.reflectasm.ClassLoaderTest$Test"); 46 | Object testObject1 = testClass1.newInstance(); 47 | FieldAccess access1 = FieldAccess.get(testObject1.getClass()); 48 | access1.set(testObject1, "name", "first"); 49 | assertEquals("first", testObject1.toString()); 50 | assertEquals("first", access1.get(testObject1, "name")); 51 | 52 | ClassLoader testClassLoader2 = new TestClassLoader2(); 53 | Class testClass2 = testClassLoader2.loadClass("com.esotericsoftware.reflectasm.ClassLoaderTest$Test"); 54 | Object testObject2 = testClass2.newInstance(); 55 | FieldAccess access2 = FieldAccess.get(testObject2.getClass()); 56 | access2.set(testObject2, "name", "second"); 57 | assertEquals("second", testObject2.toString()); 58 | assertEquals("second", access2.get(testObject2, "name")); 59 | 60 | assertEquals(access1.getClass().toString(), access2.getClass().toString()); // Same class names 61 | assertFalse(access1.getClass().equals(access2.getClass())); // But different classes 62 | 63 | assertEquals(initialCount + 2, AccessClassLoader.activeAccessClassLoaders()); 64 | 65 | testClassLoader1 = null; 66 | testClass1 = null; 67 | testObject1 = null; 68 | access1 = null; 69 | testClassLoader2 = null; 70 | testClass2 = null; 71 | testObject2 = null; 72 | access2 = null; 73 | 74 | reclaimLoaders(); 75 | 76 | // Yeah, reclaimed! 77 | assertEquals(initialCount, AccessClassLoader.activeAccessClassLoaders()); 78 | } 79 | 80 | private void reclaimLoaders () throws Exception { 81 | // Force GC to reclaim unreachable (or only weak-reachable) objects 82 | System.gc(); 83 | try { 84 | Object[] array = new Object[(int)Runtime.getRuntime().maxMemory()]; 85 | System.out.println(array.length); 86 | } catch (Throwable e) { 87 | // Ignore OME 88 | } 89 | System.gc(); 90 | int times = 0; 91 | while (AccessClassLoader.activeAccessClassLoaders() > 1 && times < 50) { // max 5 seconds, should be instant 92 | Thread.sleep(100); // test again 93 | times++; 94 | } 95 | } 96 | 97 | public void testRemoveClassloaders () throws Exception { 98 | int initialCount = AccessClassLoader.activeAccessClassLoaders(); 99 | 100 | ClassLoader testClassLoader1 = new TestClassLoader1(); 101 | Class testClass1 = testClassLoader1.loadClass("com.esotericsoftware.reflectasm.ClassLoaderTest$Test"); 102 | Object testObject1 = testClass1.newInstance(); 103 | FieldAccess access1 = FieldAccess.get(testObject1.getClass()); 104 | access1.set(testObject1, "name", "first"); 105 | assertEquals("first", testObject1.toString()); 106 | assertEquals("first", access1.get(testObject1, "name")); 107 | 108 | ClassLoader testClassLoader2 = new TestClassLoader2(); 109 | Class testClass2 = testClassLoader2.loadClass("com.esotericsoftware.reflectasm.ClassLoaderTest$Test"); 110 | Object testObject2 = testClass2.newInstance(); 111 | FieldAccess access2 = FieldAccess.get(testObject2.getClass()); 112 | access2.set(testObject2, "name", "second"); 113 | assertEquals("second", testObject2.toString()); 114 | assertEquals("second", access2.get(testObject2, "name")); 115 | 116 | assertEquals(access1.getClass().toString(), access2.getClass().toString()); // Same class names 117 | assertFalse(access1.getClass().equals(access2.getClass())); // But different classes 118 | 119 | assertEquals(initialCount + 2, AccessClassLoader.activeAccessClassLoaders()); 120 | 121 | AccessClassLoader.remove(testObject1.getClass().getClassLoader()); 122 | assertEquals(initialCount + 1, AccessClassLoader.activeAccessClassLoaders()); 123 | AccessClassLoader.remove(testObject2.getClass().getClassLoader()); 124 | assertEquals(initialCount + 0, AccessClassLoader.activeAccessClassLoaders()); 125 | AccessClassLoader.remove(this.getClass().getClassLoader()); 126 | assertEquals(initialCount - 1, AccessClassLoader.activeAccessClassLoaders()); 127 | } 128 | 129 | static public class Test { 130 | public String name; 131 | 132 | public String toString () { 133 | return name; 134 | } 135 | } 136 | 137 | static public class TestClassLoader1 extends ClassLoader { 138 | protected synchronized Class loadClass (String name, boolean resolve) throws ClassNotFoundException { 139 | Class c = findLoadedClass(name); 140 | if (c != null) return c; 141 | if (name.startsWith("java.")) return super.loadClass(name, resolve); 142 | if (!name.equals("com.esotericsoftware.reflectasm.ClassLoaderTest$Test")) 143 | throw new ClassNotFoundException("Class not found on purpose: " + name); 144 | ByteArrayOutputStream output = new ByteArrayOutputStream(32 * 1024); 145 | InputStream input = ClassLoaderTest.class.getResourceAsStream("/" + name.replace('.', '/') + ".class"); 146 | if (input == null) return null; 147 | try { 148 | byte[] buffer = new byte[4096]; 149 | int total = 0; 150 | while (true) { 151 | int length = input.read(buffer, 0, buffer.length); 152 | if (length == -1) break; 153 | output.write(buffer, 0, length); 154 | } 155 | } catch (IOException ex) { 156 | throw new ClassNotFoundException("Error reading class file.", ex); 157 | } finally { 158 | try { 159 | input.close(); 160 | } catch (IOException ignored) { 161 | } 162 | } 163 | byte[] buffer = output.toByteArray(); 164 | return defineClass(name, buffer, 0, buffer.length); 165 | } 166 | } 167 | 168 | static public class TestClassLoader2 extends TestClassLoader1 { 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/ConstructorAccessTest.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.util.List; 18 | 19 | import junit.framework.TestCase; 20 | 21 | public class ConstructorAccessTest extends TestCase { 22 | static private boolean java17; 23 | static { 24 | try { 25 | Object version = Runtime.class.getDeclaredMethod("version").invoke(null); 26 | java17 = ((List)version.getClass().getDeclaredMethod("version").invoke(version)).get(0) >= 17; 27 | } catch (Exception ignored) { 28 | java17 = false; 29 | } 30 | } 31 | 32 | public void testNewInstance () { 33 | ConstructorAccess access = ConstructorAccess.get(SomeClass.class); 34 | SomeClass someObject = new SomeClass(); 35 | assertEquals(someObject, access.newInstance()); 36 | assertEquals(someObject, access.newInstance()); 37 | assertEquals(someObject, access.newInstance()); 38 | } 39 | 40 | public void testPackagePrivateNewInstance () { 41 | if (java17) return; 42 | ConstructorAccess access = ConstructorAccess.get(PackagePrivateClass.class); 43 | PackagePrivateClass someObject = new PackagePrivateClass(); 44 | assertEquals(someObject, access.newInstance()); 45 | assertEquals(someObject, access.newInstance()); 46 | assertEquals(someObject, access.newInstance()); 47 | } 48 | 49 | public void testHasArgumentConstructor () { 50 | try { 51 | ConstructorAccess.get(HasArgumentConstructor.class); 52 | assertTrue(false); 53 | } catch (RuntimeException re) { 54 | System.out.println("Expected exception happened: " + re); 55 | } catch (Throwable t) { 56 | t.printStackTrace(); 57 | assertTrue(false); 58 | } 59 | } 60 | 61 | public void testHasPrivateConstructor () { 62 | try { 63 | ConstructorAccess.get(HasPrivateConstructor.class); 64 | assertTrue(false); 65 | } catch (RuntimeException re) { 66 | System.out.println("Expected exception happened: " + re); 67 | } catch (Throwable t) { 68 | t.printStackTrace(); 69 | assertTrue(false); 70 | } 71 | } 72 | 73 | public void testHasProtectedConstructor () { 74 | if (java17) return; 75 | try { 76 | ConstructorAccess access = ConstructorAccess.get(HasProtectedConstructor.class); 77 | HasProtectedConstructor newInstance = access.newInstance(); 78 | assertEquals("cow", newInstance.getMoo()); 79 | } catch (Throwable t) { 80 | t.printStackTrace(); 81 | assertTrue(false); 82 | } 83 | } 84 | 85 | public void testHasPackagePrivateConstructor () { 86 | if (java17) return; 87 | try { 88 | ConstructorAccess access = ConstructorAccess.get(HasPackagePrivateConstructor.class); 89 | HasPackagePrivateConstructor newInstance = access.newInstance(); 90 | assertEquals("cow", newInstance.getMoo()); 91 | } catch (Throwable t) { 92 | t.printStackTrace(); 93 | assertTrue(false); 94 | } 95 | } 96 | 97 | public void testHasPublicConstructor () { 98 | try { 99 | ConstructorAccess access = ConstructorAccess.get(HasPublicConstructor.class); 100 | HasPublicConstructor newInstance = access.newInstance(); 101 | assertEquals("cow", newInstance.getMoo()); 102 | } catch (Throwable t) { 103 | t.printStackTrace(); 104 | assertTrue(false); 105 | } 106 | } 107 | 108 | static class PackagePrivateClass { 109 | public String name; 110 | public int intValue; 111 | protected float test1; 112 | Float test2; 113 | private String test3; 114 | 115 | public boolean equals (Object obj) { 116 | if (this == obj) return true; 117 | if (obj == null) return false; 118 | if (getClass() != obj.getClass()) return false; 119 | PackagePrivateClass other = (PackagePrivateClass)obj; 120 | if (intValue != other.intValue) return false; 121 | if (name == null) { 122 | if (other.name != null) return false; 123 | } else if (!name.equals(other.name)) return false; 124 | if (Float.floatToIntBits(test1) != Float.floatToIntBits(other.test1)) return false; 125 | if (test2 == null) { 126 | if (other.test2 != null) return false; 127 | } else if (!test2.equals(other.test2)) return false; 128 | if (test3 == null) { 129 | if (other.test3 != null) return false; 130 | } else if (!test3.equals(other.test3)) return false; 131 | return true; 132 | } 133 | } 134 | 135 | static public class SomeClass { 136 | public String name; 137 | public int intValue; 138 | protected float test1; 139 | Float test2; 140 | private String test3; 141 | 142 | public boolean equals (Object obj) { 143 | if (this == obj) return true; 144 | if (obj == null) return false; 145 | if (getClass() != obj.getClass()) return false; 146 | SomeClass other = (SomeClass)obj; 147 | if (intValue != other.intValue) return false; 148 | if (name == null) { 149 | if (other.name != null) return false; 150 | } else if (!name.equals(other.name)) return false; 151 | if (Float.floatToIntBits(test1) != Float.floatToIntBits(other.test1)) return false; 152 | if (test2 == null) { 153 | if (other.test2 != null) return false; 154 | } else if (!test2.equals(other.test2)) return false; 155 | if (test3 == null) { 156 | if (other.test3 != null) return false; 157 | } else if (!test3.equals(other.test3)) return false; 158 | return true; 159 | } 160 | } 161 | 162 | static public class HasArgumentConstructor { 163 | public String moo; 164 | 165 | public HasArgumentConstructor (String moo) { 166 | this.moo = moo; 167 | } 168 | 169 | public boolean equals (Object obj) { 170 | if (this == obj) return true; 171 | if (obj == null) return false; 172 | if (getClass() != obj.getClass()) return false; 173 | HasArgumentConstructor other = (HasArgumentConstructor)obj; 174 | if (moo == null) { 175 | if (other.moo != null) return false; 176 | } else if (!moo.equals(other.moo)) return false; 177 | return true; 178 | } 179 | 180 | public String getMoo () { 181 | return moo; 182 | } 183 | } 184 | 185 | static public class HasPrivateConstructor extends HasArgumentConstructor { 186 | private HasPrivateConstructor () { 187 | super("cow"); 188 | } 189 | } 190 | 191 | static public class HasProtectedConstructor extends HasPrivateConstructor { 192 | @SuppressWarnings("synthetic-access") 193 | protected HasProtectedConstructor () { 194 | super(); 195 | } 196 | } 197 | 198 | static public class HasPackagePrivateConstructor extends HasProtectedConstructor { 199 | HasPackagePrivateConstructor () { 200 | super(); 201 | } 202 | } 203 | 204 | static public class HasPublicConstructor extends HasPackagePrivateConstructor { 205 | public HasPublicConstructor () { 206 | super(); 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /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 | */ 14 | 15 | package com.esotericsoftware.reflectasm; 16 | 17 | import junit.framework.TestCase; 18 | 19 | public class FieldAccessTest extends TestCase { 20 | public void testNameSetAndGet () { 21 | FieldAccess access = FieldAccess.get(SomeClass.class); 22 | SomeClass test = new SomeClass(); 23 | 24 | assertEquals(null, test.name); 25 | access.set(test, "name", "first"); 26 | assertEquals("first", test.name); 27 | assertEquals("first", access.get(test, "name")); 28 | 29 | assertEquals(0, test.intValue); 30 | access.set(test, "intValue", 1234); 31 | assertEquals(1234, test.intValue); 32 | assertEquals(1234, access.get(test, "intValue")); 33 | } 34 | 35 | public void testIndexSetAndGet () { 36 | FieldAccess access = FieldAccess.get(SomeClass.class); 37 | SomeClass test = new SomeClass(); 38 | int index; 39 | 40 | assertEquals(null, test.name); 41 | index = access.getIndex("name"); 42 | access.set(test, index, "first"); 43 | assertEquals("first", test.name); 44 | assertEquals("first", access.get(test, index)); 45 | 46 | index = access.getIndex("intValue"); 47 | assertEquals(0, test.intValue); 48 | access.set(test, index, 1234); 49 | assertEquals(1234, test.intValue); 50 | assertEquals(1234, access.get(test, index)); 51 | 52 | assertEquals(false, access.getBoolean(test, access.getIndex("booleanField"))); 53 | access.setBoolean(test, access.getIndex("booleanField"), true); 54 | assertEquals(true, access.getBoolean(test, access.getIndex("booleanField"))); 55 | 56 | assertEquals(0, access.getByte(test, access.getIndex("byteField"))); 57 | access.setByte(test, access.getIndex("byteField"), (byte)23); 58 | assertEquals(23, access.getByte(test, access.getIndex("byteField"))); 59 | 60 | assertEquals(0, access.getChar(test, access.getIndex("charField"))); 61 | access.setChar(test, access.getIndex("charField"), (char)53); 62 | assertEquals(53, access.getChar(test, access.getIndex("charField"))); 63 | 64 | assertEquals(0, access.getShort(test, access.getIndex("shortField"))); 65 | access.setShort(test, access.getIndex("shortField"), (short)123); 66 | assertEquals(123, access.getShort(test, access.getIndex("shortField"))); 67 | 68 | assertEquals(0, access.getInt(test, access.getIndex("intField"))); 69 | access.setInt(test, access.getIndex("intField"), 123); 70 | assertEquals(123, access.getInt(test, access.getIndex("intField"))); 71 | 72 | assertEquals(0, access.getLong(test, access.getIndex("longField"))); 73 | access.setLong(test, access.getIndex("longField"), 123456789l); 74 | assertEquals(123456789l, access.getLong(test, access.getIndex("longField"))); 75 | 76 | assertEquals(0f, access.getFloat(test, access.getIndex("floatField"))); 77 | access.setFloat(test, access.getIndex("floatField"), 1.23f); 78 | assertEquals(1.23f, access.getFloat(test, access.getIndex("floatField"))); 79 | 80 | assertEquals(0d, access.getDouble(test, access.getIndex("doubleField"))); 81 | access.setDouble(test, access.getIndex("doubleField"), 123.456); 82 | assertEquals(123.456, access.getDouble(test, access.getIndex("doubleField"))); 83 | } 84 | 85 | public void testEmptyClass () { 86 | FieldAccess access = FieldAccess.get(EmptyClass.class); 87 | try { 88 | access.getIndex("name"); 89 | fail(); 90 | } catch (IllegalArgumentException expected) { 91 | // expected.printStackTrace(); 92 | } 93 | try { 94 | access.get(new EmptyClass(), "meow"); 95 | fail(); 96 | } catch (IllegalArgumentException expected) { 97 | // expected.printStackTrace(); 98 | } 99 | try { 100 | access.get(new EmptyClass(), 0); 101 | fail(); 102 | } catch (IllegalArgumentException expected) { 103 | // expected.printStackTrace(); 104 | } 105 | try { 106 | access.set(new EmptyClass(), "foo", "moo"); 107 | fail(); 108 | } catch (IllegalArgumentException expected) { 109 | // expected.printStackTrace(); 110 | } 111 | } 112 | 113 | static public class SomeClass { 114 | public String name; 115 | public int intValue; 116 | protected float test1; 117 | Float test2; 118 | private String test3; 119 | 120 | public boolean booleanField; 121 | public byte byteField; 122 | public char charField; 123 | public short shortField; 124 | public int intField; 125 | public long longField; 126 | public float floatField; 127 | public double doubleField; 128 | } 129 | 130 | static public class EmptyClass { 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/MethodAccessTest.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.util.concurrent.ConcurrentHashMap; 18 | import java.util.concurrent.ConcurrentMap; 19 | import static junit.framework.Assert.assertEquals; 20 | 21 | import junit.framework.TestCase; 22 | 23 | public class MethodAccessTest extends TestCase { 24 | public void testInvoke () { 25 | MethodAccess access = MethodAccess.get(SomeClass.class); 26 | SomeClass someObject = new SomeClass(); 27 | Object value; 28 | 29 | value = access.invoke(someObject, "getName"); 30 | assertEquals(null, value); 31 | value = access.invoke(someObject, "setName", "sweet"); 32 | assertEquals(null, value); 33 | value = access.invoke(someObject, "getName"); 34 | assertEquals("sweet", value); 35 | value = access.invoke(someObject, "setName", (Object)null); 36 | assertEquals(null, value); 37 | value = access.invoke(someObject, "getName"); 38 | assertEquals(null, value); 39 | 40 | value = access.invoke(someObject, "getIntValue"); 41 | assertEquals(0, value); 42 | value = access.invoke(someObject, "setIntValue", 1234); 43 | assertEquals(null, value); 44 | value = access.invoke(someObject, "getIntValue"); 45 | assertEquals(1234, value); 46 | 47 | value = access.invoke(someObject, "methodWithManyArguments", 1, 2f, 3, 4.2f, null, null, null); 48 | assertEquals("test", value); 49 | 50 | int index = access.getIndex("methodWithManyArguments", int.class, float.class, Integer.class, Float.class, SomeClass.class, 51 | SomeClass.class, SomeClass.class); 52 | assertEquals(access.getIndex("methodWithManyArguments"), index); 53 | 54 | value = access.invoke(null, "staticMethod", "moo", 1234); 55 | assertEquals("meow! moo, 1234", value); 56 | } 57 | 58 | public void testEmptyClass () { 59 | MethodAccess access = MethodAccess.get(EmptyClass.class); 60 | try { 61 | access.getIndex("name"); 62 | fail(); 63 | } catch (IllegalArgumentException expected) { 64 | // expected.printStackTrace(); 65 | } 66 | try { 67 | access.getIndex("name", String.class); 68 | fail(); 69 | } catch (IllegalArgumentException expected) { 70 | // expected.printStackTrace(); 71 | } 72 | try { 73 | access.invoke(new EmptyClass(), "meow", "moo"); 74 | fail(); 75 | } catch (IllegalArgumentException expected) { 76 | // expected.printStackTrace(); 77 | } 78 | try { 79 | access.invoke(new EmptyClass(), 0); 80 | fail(); 81 | } catch (IllegalArgumentException expected) { 82 | // expected.printStackTrace(); 83 | } 84 | try { 85 | access.invoke(new EmptyClass(), 0, "moo"); 86 | fail(); 87 | } catch (IllegalArgumentException expected) { 88 | // expected.printStackTrace(); 89 | } 90 | } 91 | 92 | public void testInvokeInterface () { 93 | MethodAccess access = MethodAccess.get(ConcurrentMap.class); 94 | access = MethodAccess.get(ConcurrentMap.class); 95 | ConcurrentHashMap someMap = new ConcurrentHashMap(); 96 | someMap.put("first", "one"); 97 | someMap.put("second", "two"); 98 | Object value; 99 | 100 | // invoke a method declared directly in the ConcurrentMap interface 101 | value = access.invoke(someMap, "replace", "first", "foo"); 102 | assertEquals("one", value); 103 | // invoke a method declared in the Map superinterface 104 | value = access.invoke(someMap, "size"); 105 | assertEquals(someMap.size(), value); 106 | } 107 | 108 | static public class EmptyClass { 109 | } 110 | 111 | static public class SomeClass { 112 | private String name; 113 | private int intValue; 114 | 115 | public String getName () { 116 | return name; 117 | } 118 | 119 | public void setName (String name) { 120 | this.name = name; 121 | } 122 | 123 | public int getIntValue () { 124 | return intValue; 125 | } 126 | 127 | public void setIntValue (int intValue) { 128 | this.intValue = intValue; 129 | } 130 | 131 | public String methodWithManyArguments (int i, float f, Integer I, Float F, SomeClass c, SomeClass c1, SomeClass c2) { 132 | return "test"; 133 | } 134 | 135 | static public String staticMethod (String a, int b) { 136 | return "meow! " + a + ", " + b; 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/benchmark/Benchmark.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.benchmark; 16 | 17 | import java.util.ArrayList; 18 | import java.util.Collections; 19 | import java.util.Comparator; 20 | import java.util.HashMap; 21 | import java.util.Map.Entry; 22 | 23 | public class Benchmark { 24 | public boolean warmup = true; 25 | public HashMap testTimes = new HashMap(); 26 | private long s; 27 | 28 | public void start () { 29 | s = System.nanoTime(); 30 | } 31 | 32 | public void end (String name) { 33 | if (warmup) return; 34 | long e = System.nanoTime(); 35 | long time = e - s; 36 | Long oldTime = testTimes.get(name); 37 | if (oldTime == null || time < oldTime) testTimes.put(name, time); 38 | System.out.println(name + ": " + time / 1000000f + " ms"); 39 | } 40 | 41 | public void chart (String title) { 42 | Comparator comparator = new Comparator() { 43 | public int compare (Entry o1, Entry o2) { 44 | // return ((String)o1.getKey()).compareTo((String)o2.getKey()); 45 | return (int)((Long)o1.getValue() - (Long)o2.getValue()); 46 | } 47 | }; 48 | ArrayList list = new ArrayList(testTimes.entrySet()); 49 | Collections.sort(list, comparator); 50 | 51 | StringBuilder names = new StringBuilder(512); 52 | StringBuilder times = new StringBuilder(512); 53 | long max = 0; 54 | int count = 0; 55 | for (Entry entry : list) { 56 | String name = entry.getKey(); 57 | names.insert(0, '|'); 58 | names.insert(0, name); 59 | long time = entry.getValue(); 60 | times.append(time); 61 | times.append(','); 62 | max = Math.max(max, time); 63 | count++; 64 | } 65 | times.setLength(times.length() - 1); 66 | names.setLength(names.length() - 1); 67 | int height = count * 18 + 21; 68 | int width = Math.min(700, 300000 / height); 69 | System.out.println("[img]http://chart.apis.google.com/chart?chtt=" + title + "&" + "chs=" + width + "x" + height 70 | + "&chd=t:" + times + "&chds=0," + max + "&chxl=0:|" + names + "&cht=bhg&chbh=10&chxt=y&" 71 | + "chco=660000|660033|660066|660099|6600CC|6600FF|663300|663333|" 72 | + "663366|663399|6633CC|6633FF|666600|666633|666666[/img]\n"); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/benchmark/ConstructorAccessBenchmark.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.benchmark; 16 | 17 | import com.esotericsoftware.reflectasm.ConstructorAccess; 18 | 19 | public class ConstructorAccessBenchmark extends Benchmark { 20 | public ConstructorAccessBenchmark () throws Exception { 21 | int count = 1000000; 22 | Object[] dontCompileMeAway = new Object[count]; 23 | 24 | Class type = SomeClass.class; 25 | ConstructorAccess access = ConstructorAccess.get(type); 26 | 27 | for (int i = 0; i < 100; i++) 28 | for (int ii = 0; ii < count; ii++) 29 | dontCompileMeAway[ii] = access.newInstance(); 30 | for (int i = 0; i < 100; i++) 31 | for (int ii = 0; ii < count; ii++) 32 | dontCompileMeAway[ii] = type.newInstance(); 33 | warmup = false; 34 | 35 | for (int i = 0; i < 100; i++) { 36 | start(); 37 | for (int ii = 0; ii < count; ii++) 38 | dontCompileMeAway[ii] = access.newInstance(); 39 | end("ConstructorAccess"); 40 | } 41 | for (int i = 0; i < 100; i++) { 42 | start(); 43 | for (int ii = 0; ii < count; ii++) 44 | dontCompileMeAway[ii] = type.newInstance(); 45 | end("Reflection"); 46 | } 47 | 48 | chart("Constructor"); 49 | } 50 | 51 | static public class SomeClass { 52 | public String name; 53 | } 54 | 55 | public static void main (String[] args) throws Exception { 56 | new ConstructorAccessBenchmark(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/benchmark/FieldAccessBenchmark.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.benchmark; 16 | 17 | import java.lang.reflect.Field; 18 | import java.util.HashMap; 19 | 20 | import com.esotericsoftware.reflectasm.FieldAccess; 21 | 22 | public class FieldAccessBenchmark extends Benchmark { 23 | public FieldAccessBenchmark () throws Exception { 24 | int count = 1000000; 25 | Object[] dontCompileMeAway = new Object[count]; 26 | 27 | FieldAccess access = FieldAccess.get(SomeClass.class); 28 | SomeClass someObject = new SomeClass(); 29 | int index = access.getIndex("name"); 30 | 31 | Field field = SomeClass.class.getField("name"); 32 | 33 | for (int i = 0; i < 100; i++) { 34 | for (int ii = 0; ii < count; ii++) { 35 | access.set(someObject, index, "first"); 36 | dontCompileMeAway[ii] = access.get(someObject, index); 37 | } 38 | for (int ii = 0; ii < count; ii++) { 39 | field.set(someObject, "first"); 40 | dontCompileMeAway[ii] = field.get(someObject); 41 | } 42 | } 43 | warmup = false; 44 | 45 | for (int i = 0; i < 100; i++) { 46 | start(); 47 | for (int ii = 0; ii < count; ii++) { 48 | access.set(someObject, index, "first"); 49 | dontCompileMeAway[ii] = access.get(someObject, index); 50 | } 51 | end("FieldAccess"); 52 | } 53 | for (int i = 0; i < 100; i++) { 54 | start(); 55 | for (int ii = 0; ii < count; ii++) { 56 | field.set(someObject, "first"); 57 | dontCompileMeAway[ii] = field.get(someObject); 58 | } 59 | end("Reflection"); 60 | } 61 | 62 | chart("Field Set/Get"); 63 | } 64 | 65 | static public class SomeClass { 66 | public String name; 67 | } 68 | 69 | public static void main (String[] args) throws Exception { 70 | new FieldAccessBenchmark(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/com/esotericsoftware/reflectasm/benchmark/MethodAccessBenchmark.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.benchmark; 16 | 17 | import com.esotericsoftware.reflectasm.MethodAccess; 18 | 19 | import java.lang.reflect.Method; 20 | 21 | public class MethodAccessBenchmark extends Benchmark { 22 | public MethodAccessBenchmark () throws Exception { 23 | int count = 100000; 24 | Object[] dontCompileMeAway = new Object[count]; 25 | Object[] args = new Object[0]; 26 | 27 | MethodAccess access = MethodAccess.get(SomeClass.class); 28 | SomeClass someObject = new SomeClass(); 29 | int index = access.getIndex("getName"); 30 | 31 | Method method = SomeClass.class.getMethod("getName"); 32 | // method.setAccessible(true); // Improves reflection a bit. 33 | 34 | for (int i = 0; i < 100; i++) { 35 | for (int ii = 0; ii < count; ii++) 36 | dontCompileMeAway[ii] = access.invoke(someObject, index, args); 37 | for (int ii = 0; ii < count; ii++) 38 | dontCompileMeAway[ii] = method.invoke(someObject, args); 39 | } 40 | warmup = false; 41 | 42 | for (int i = 0; i < 100; i++) { 43 | start(); 44 | for (int ii = 0; ii < count; ii++) 45 | dontCompileMeAway[ii] = access.invoke(someObject, index, args); 46 | end("MethodAccess"); 47 | } 48 | for (int i = 0; i < 100; i++) { 49 | start(); 50 | for (int ii = 0; ii < count; ii++) 51 | dontCompileMeAway[ii] = method.invoke(someObject, args); 52 | end("Reflection"); 53 | } 54 | 55 | chart("Method Call"); 56 | } 57 | 58 | static public class SomeClass { 59 | private String name = "something"; 60 | 61 | public String getName () { 62 | return name; 63 | } 64 | } 65 | 66 | public static void main (String[] args) throws Exception { 67 | new MethodAccessBenchmark(); 68 | } 69 | } 70 | --------------------------------------------------------------------------------