001/*
002 *  Copyright 2013 Christopher Pheby
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.jadira.reflection.access.asm;
017
018import static org.objectweb.asm.Opcodes.AALOAD;
019import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
020import static org.objectweb.asm.Opcodes.ACC_SUPER;
021import static org.objectweb.asm.Opcodes.ACC_VARARGS;
022import static org.objectweb.asm.Opcodes.ACONST_NULL;
023import static org.objectweb.asm.Opcodes.ALOAD;
024import static org.objectweb.asm.Opcodes.ARETURN;
025import static org.objectweb.asm.Opcodes.ASTORE;
026import static org.objectweb.asm.Opcodes.ATHROW;
027import static org.objectweb.asm.Opcodes.BIPUSH;
028import static org.objectweb.asm.Opcodes.CHECKCAST;
029import static org.objectweb.asm.Opcodes.DLOAD;
030import static org.objectweb.asm.Opcodes.DRETURN;
031import static org.objectweb.asm.Opcodes.DUP;
032import static org.objectweb.asm.Opcodes.FLOAD;
033import static org.objectweb.asm.Opcodes.FRETURN;
034import static org.objectweb.asm.Opcodes.F_APPEND;
035import static org.objectweb.asm.Opcodes.F_SAME;
036import static org.objectweb.asm.Opcodes.GETFIELD;
037import static org.objectweb.asm.Opcodes.ILOAD;
038import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
039import static org.objectweb.asm.Opcodes.INVOKESTATIC;
040import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
041import static org.objectweb.asm.Opcodes.IRETURN;
042import static org.objectweb.asm.Opcodes.ISTORE;
043import static org.objectweb.asm.Opcodes.LLOAD;
044import static org.objectweb.asm.Opcodes.LRETURN;
045import static org.objectweb.asm.Opcodes.NEW;
046import static org.objectweb.asm.Opcodes.POP;
047import static org.objectweb.asm.Opcodes.PUTFIELD;
048import static org.objectweb.asm.Opcodes.RETURN;
049import static org.objectweb.asm.Opcodes.V1_7;
050import static org.objectweb.asm.Type.BOOLEAN;
051import static org.objectweb.asm.Type.BYTE;
052import static org.objectweb.asm.Type.CHAR;
053import static org.objectweb.asm.Type.DOUBLE;
054import static org.objectweb.asm.Type.FLOAT;
055import static org.objectweb.asm.Type.INT;
056import static org.objectweb.asm.Type.LONG;
057import static org.objectweb.asm.Type.SHORT;
058
059import java.lang.reflect.Constructor;
060import java.lang.reflect.Field;
061import java.lang.reflect.Method;
062import java.lang.reflect.Modifier;
063import java.util.Arrays;
064import java.util.concurrent.ConcurrentHashMap;
065
066import org.jadira.reflection.access.AbstractClassAccess;
067import org.jadira.reflection.access.api.ClassAccess;
068import org.jadira.reflection.access.api.FieldAccess;
069import org.jadira.reflection.access.api.MethodAccess;
070import org.jadira.reflection.access.classloader.AccessClassLoader;
071import org.jadira.reflection.access.portable.PortableFieldAccess;
072import org.jadira.reflection.core.misc.ClassUtils;
073import org.objectweb.asm.ClassVisitor;
074import org.objectweb.asm.ClassWriter;
075import org.objectweb.asm.Label;
076import org.objectweb.asm.MethodVisitor;
077import org.objectweb.asm.Type;
078
079/**
080 * ClassAccess implementation which uses ASM to generate accessors
081 * @param <C> The Class to be accessed
082 */
083public abstract class AsmClassAccess<C> extends AbstractClassAccess<C> implements ClassAccess<C> {
084
085    private static final ConcurrentHashMap<Class<?>, AsmClassAccess<?>> CLASS_ACCESSES = new ConcurrentHashMap<Class<?>, AsmClassAccess<?>>();
086    
087        private static final String CLASS_ACCESS_NM = ClassAccess.class.getName().replace('.', '/');
088
089        private static final String ASM_CLASS_ACCESS_NM = AsmClassAccess.class.getName().replace('.', '/');
090    
091        private boolean isNonStaticMemberClass;
092                
093        /**
094         * Constructor, intended for use by generated subclasses
095         * @param clazz The Class to be accessed
096         */
097        protected AsmClassAccess(Class<C> clazz) {
098                super(clazz);
099        }
100
101        /**
102         * Indicates if the class being accessed is a non-static member class
103         * @return True if the class is a non-static member class
104         */
105        public boolean isNonStaticMemberClass() {
106                return isNonStaticMemberClass;
107        }
108        
109        /**
110         * Get a new instance that can access the given Class. If the ClassAccess for this class
111         * has not been obtained before, then the specific AsmClassAccess is created by generating
112         * a specialised subclass of this class and returning it. 
113         * @param clazz Class to be accessed
114         * @param <C> The type of class
115         * @return New AsmClassAccess instance
116         */
117        public static <C> AsmClassAccess<C> get(Class<C> clazz) {
118
119            @SuppressWarnings("unchecked")
120            AsmClassAccess<C> access = (AsmClassAccess<C>) CLASS_ACCESSES.get(clazz);
121        if (access != null) {
122            return access;
123        }
124                    
125                Class<?> enclosingType = clazz.getEnclosingClass();
126
127                final boolean isNonStaticMemberClass = determineNonStaticMemberClass(clazz, enclosingType);
128
129                String clazzName = clazz.getName();
130                
131                Field[] fields = ClassUtils.collectInstanceFields(clazz, false, false, true);
132                Method[] methods = ClassUtils.collectMethods(clazz);
133                
134                String accessClassName = constructAccessClassName(clazzName);
135
136                Class<?> accessClass = null;
137
138                AccessClassLoader loader = AccessClassLoader.get(clazz);
139                synchronized (loader) {
140                        try {
141                                accessClass = loader.loadClass(accessClassName);
142                        } catch (ClassNotFoundException ignored) {
143
144                                String accessClassNm = accessClassName.replace('.', '/');
145                                String clazzNm = clazzName.replace('.', '/');
146                                String enclosingClassNm = determineEnclosingClassNm(clazz, enclosingType, isNonStaticMemberClass);
147
148                                String signatureString = "L" + ASM_CLASS_ACCESS_NM + "<L" + clazzNm + ";>;L" + CLASS_ACCESS_NM + "<L" + clazzNm + ";>;";
149
150                                ClassWriter cw = new ClassWriter(0);
151
152//                              TraceClassVisitor tcv = new TraceClassVisitor(cv, new PrintWriter(System.err));
153//                              CheckClassAdapter cw = new CheckClassAdapter(tcv);
154
155                                cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, accessClassNm, signatureString, ASM_CLASS_ACCESS_NM, null);
156
157                                enhanceForConstructor(cw, accessClassNm, clazzNm);
158
159                                if (isNonStaticMemberClass) {
160                                        enhanceForNewInstanceInner(cw, clazzNm, enclosingClassNm);
161                                } else {
162                                        enhanceForNewInstance(cw, clazzNm);
163                                }
164
165                                enhanceForGetValueObject(cw, accessClassNm, clazzNm, fields);
166                                enhanceForPutValueObject(cw, accessClassNm, clazzNm, fields);
167                                enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.BOOLEAN_TYPE);
168                                enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.BOOLEAN_TYPE);
169                                enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.BYTE_TYPE);
170                                enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.BYTE_TYPE);
171                                enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.SHORT_TYPE);
172                                enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.SHORT_TYPE);
173                                enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.INT_TYPE);
174                                enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.INT_TYPE);
175                                enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.LONG_TYPE);
176                                enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.LONG_TYPE);
177                                enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.DOUBLE_TYPE);
178                                enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.DOUBLE_TYPE);
179                                enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.FLOAT_TYPE);
180                                enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.FLOAT_TYPE);
181                                enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.CHAR_TYPE);
182                                enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.CHAR_TYPE);
183                                enhanceForInvokeMethod(cw, accessClassNm, clazzNm, methods);
184
185                                cw.visitEnd();
186
187                                loader.registerClass(accessClassName, cw.toByteArray());
188
189                                try {
190                                        accessClass = loader.findClass(accessClassName);
191                                } catch (ClassNotFoundException e) {
192                                        throw new IllegalStateException("AccessClass unexpectedly could not be found", e);
193                                }
194                        }
195                }
196                
197                try {
198                        @SuppressWarnings("unchecked")
199                        Constructor<AsmClassAccess<C>> c = (Constructor<AsmClassAccess<C>>) accessClass.getConstructor(new Class[] { Class.class });
200                        
201                        access = c.newInstance(clazz);
202                        access.isNonStaticMemberClass = isNonStaticMemberClass;
203                                        
204                        CLASS_ACCESSES.putIfAbsent(clazz, access);
205                        
206                        return access;
207                } catch (Exception ex) {
208                    throw new RuntimeException("Error constructing constructor access class: " + accessClassName + "{ " + ex.getMessage() + " }", ex);
209                }
210        }
211
212        private static Label[] constructLabels(Field[] fields) {
213
214                Label[] labels = new Label[fields.length];
215                for (int i = 0, n = labels.length; i < n; i++) {
216                        labels[i] = new Label();
217                }
218                return labels;
219        }
220
221        private static Label[] constructLabels(Method[] methods) {
222
223                Label[] labels = new Label[methods.length];
224                for (int i = 0, n = labels.length; i < n; i++) {
225                        labels[i] = new Label();
226                }
227                return labels;
228        }
229        
230        private static <C> String determineEnclosingClassNm(Class<C> clazz, Class<?> enclosingType, final boolean isNonStaticMemberClass) {
231
232                final String enclosingClassNm;
233
234                if (!isNonStaticMemberClass) {
235
236                        enclosingClassNm = null;
237                } else {
238
239                        enclosingClassNm = enclosingType.getName().replace('.', '/');
240                }
241                return enclosingClassNm;
242        }
243
244        private static boolean determineNonStaticMemberClass(Class<?> clazz, Class<?> enclosingType) {
245                final boolean isNonStaticMemberClass;
246                if (enclosingType != null && clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers())) {
247                        isNonStaticMemberClass = true;
248                } else {
249                        isNonStaticMemberClass = false;
250                }
251                ;
252                return isNonStaticMemberClass;
253        }
254
255        private static String constructAccessClassName(String clazzName) {
256
257                String accessClassName = clazzName + AsmClassAccess.class.getSimpleName();
258                if (accessClassName.startsWith("java.")) {
259                        accessClassName = AsmClassAccess.class.getSimpleName().toLowerCase() + accessClassName;
260                }
261
262                return accessClassName;
263        }
264
265        private static void enhanceForConstructor(ClassVisitor cw, String accessClassNm, String clazzNm) {
266                MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Class;)V", "(L" + clazzNm + ";)V", null);
267                mv.visitCode();
268                mv.visitVarInsn(ALOAD, 0);
269                mv.visitVarInsn(ALOAD, 1);
270                mv.visitMethodInsn(INVOKESPECIAL, ASM_CLASS_ACCESS_NM, "<init>", "(Ljava/lang/Class;)V");
271                mv.visitInsn(RETURN);
272                mv.visitMaxs(2, 2);
273                mv.visitEnd();
274        }
275
276        private static void enhanceForNewInstance(ClassVisitor cw, String classNm) {
277                MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "()Ljava/lang/Object;", null, null);
278
279                mv.visitCode();
280                mv.visitTypeInsn(NEW, classNm);
281                mv.visitInsn(DUP);
282                mv.visitMethodInsn(INVOKESPECIAL, classNm, "<init>", "()V");
283                mv.visitInsn(ARETURN);
284                mv.visitMaxs(2, 1);
285                mv.visitEnd();
286        }
287
288        private static void enhanceForNewInstanceInner(ClassVisitor cw, String classNm, String enclosingClassNm) {
289                MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "()LLjava/lang/Object;", null, null);
290                mv.visitCode();
291                mv.visitTypeInsn(NEW, classNm);
292                mv.visitInsn(DUP);
293                mv.visitTypeInsn(NEW, enclosingClassNm);
294                mv.visitInsn(DUP);
295                mv.visitMethodInsn(INVOKESPECIAL, enclosingClassNm, "<init>", "()V");
296                mv.visitInsn(DUP);
297                mv.visitMethodInsn(INVOKEVIRTUAL, classNm, "getClass", "()Ljava/lang/Class;");
298                mv.visitInsn(POP);
299                mv.visitMethodInsn(INVOKESPECIAL, classNm, "<init>", "(L" + enclosingClassNm + ";)V");
300                mv.visitInsn(ARETURN);
301                mv.visitMaxs(4, 1);
302                mv.visitEnd();
303        }
304
305        private static void enhanceForGetValueObject(ClassVisitor cw, String accessClassNm, String clazzNm, Field[] fields) {
306
307                MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "getValue", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;", null, null);
308
309                mv.visitCode();
310                mv.visitVarInsn(ALOAD, 0);
311                mv.visitFieldInsn(GETFIELD, accessClassNm, "fieldNames", "[Ljava/lang/String;");
312                mv.visitVarInsn(ALOAD, 2);
313                mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "binarySearch", "([Ljava/lang/Object;Ljava/lang/Object;)I");
314                mv.visitVarInsn(ISTORE, 3);
315                mv.visitVarInsn(ILOAD, 3);
316
317                final int maxStack;
318
319                if (fields.length > 0) {
320                        maxStack = 5;
321                        Label[] labels = constructLabels(fields);
322
323                        Label defaultLabel = new Label();
324                        mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels);
325
326                        for (int i = 0, n = labels.length; i < n; i++) {
327                                Field field = fields[i];
328                                mv.visitLabel(labels[i]);
329                                mv.visitFrame(F_SAME, 0, null, 0, null);
330                                mv.visitVarInsn(ALOAD, 1);
331                                mv.visitTypeInsn(CHECKCAST, clazzNm);
332                                mv.visitFieldInsn(GETFIELD, clazzNm, field.getName(), Type.getDescriptor(field.getType()));
333
334                                Type fieldType = Type.getType(field.getType());
335                                switch (fieldType.getSort()) {
336                                case Type.BOOLEAN:
337                                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
338                                        break;
339                                case Type.BYTE:
340                                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
341                                        break;
342                                case Type.CHAR:
343                                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
344                                        break;
345                                case Type.SHORT:
346                                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
347                                        break;
348                                case Type.INT:
349                                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
350                                        break;
351                                case Type.FLOAT:
352                                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
353                                        break;
354                                case Type.LONG:
355                                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
356                                        break;
357                                case Type.DOUBLE:
358                                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
359                                        break;
360                                }
361
362                                mv.visitInsn(ARETURN);
363                        }
364
365                        mv.visitLabel(defaultLabel);
366                        mv.visitFrame(F_SAME, 0, null, 0, null);
367                } else {
368                        maxStack = 6;
369                }
370                enhanceForThrowingException(mv, IllegalArgumentException.class, "Field was not found", "Ljava/lang/Object;", ALOAD, 2);
371                mv.visitMaxs(maxStack, 4);
372                mv.visitEnd();
373        }
374
375        private static void enhanceForPutValueObject(ClassVisitor cw, String accessClassNm, String clazzNm, Field[] fields) {
376
377                int maxStack = 6;
378                MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "putValue", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V", null, null);
379
380                mv.visitCode();
381
382                mv.visitVarInsn(ALOAD, 0);
383                mv.visitFieldInsn(GETFIELD, accessClassNm, "fieldNames", "[Ljava/lang/String;");
384                mv.visitVarInsn(ALOAD, 2);
385                mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "binarySearch", "([Ljava/lang/Object;Ljava/lang/Object;)I");
386                mv.visitVarInsn(ISTORE, 4);
387                mv.visitVarInsn(ILOAD, 4);
388
389                if (fields.length > 0) {
390                        maxStack = 5;
391                        Label[] labels = constructLabels(fields);
392
393                        Label defaultLabel = new Label();
394                        mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels);
395
396                        for (int i = 0, n = labels.length; i < n; i++) {
397                                Field field = fields[i];
398
399                                mv.visitLabel(labels[i]);
400                                mv.visitFrame(F_SAME, 0, null, 0, null);
401                                mv.visitVarInsn(ALOAD, 1);
402                                mv.visitTypeInsn(CHECKCAST, clazzNm);
403
404                                mv.visitVarInsn(ALOAD, 3);
405
406                                Type fieldType = Type.getType(field.getType());
407                                switch (fieldType.getSort()) {
408                                case Type.BOOLEAN:
409                                        mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
410                                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
411                                        break;
412                                case Type.BYTE:
413                                        mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
414                                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B");
415                                        break;
416                                case Type.CHAR:
417                                        mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
418                                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
419                                        break;
420                                case Type.SHORT:
421                                        mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
422                                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
423                                        break;
424                                case Type.INT:
425                                        mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
426                                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
427                                        break;
428                                case Type.FLOAT:
429                                        mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
430                                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
431                                        break;
432                                case Type.LONG:
433                                        mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
434                                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
435                                        break;
436                                case Type.DOUBLE:
437                                        mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
438                                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
439                                        break;
440                                case Type.ARRAY:
441                                        mv.visitTypeInsn(CHECKCAST, fieldType.getDescriptor());
442                                        break;
443                                case Type.OBJECT:
444                                        mv.visitTypeInsn(CHECKCAST, fieldType.getInternalName());
445                                        break;
446                                }
447
448                                mv.visitFieldInsn(PUTFIELD, clazzNm, field.getName(), fieldType.getDescriptor());
449                                mv.visitInsn(RETURN);
450                        }
451
452                        mv.visitLabel(defaultLabel);
453                        mv.visitFrame(F_SAME, 0, null, 0, null);
454                } else {
455                        maxStack = 6;
456                }
457                enhanceForThrowingException(mv, IllegalArgumentException.class, "Field was not found", "Ljava/lang/Object;", ALOAD, 2);
458                mv.visitMaxs(maxStack, 5);
459                mv.visitEnd();
460        }
461
462        private static void enhanceForPutValuePrimitive(ClassVisitor cw, String accessClassNm, String clazzNm, Field[] fields, Type type) {
463
464                final String methodName;
465                final String typeNm = type.getDescriptor();
466                final int instruction;
467
468                switch (type.getSort()) {
469                case BOOLEAN:
470                        methodName = "putBooleanValue";
471                        instruction = ILOAD;
472                        break;
473                case BYTE:
474                        methodName = "putByteValue";
475                        instruction = ILOAD;
476                        break;
477                case CHAR:
478                        methodName = "putCharValue";
479                        instruction = ILOAD;
480                        break;
481                case SHORT:
482                        methodName = "putShortValue";
483                        instruction = ILOAD;
484                        break;
485                case INT:
486                        methodName = "putIntValue";
487                        instruction = ILOAD;
488                        break;
489                case FLOAT:
490                        methodName = "putFloatValue";
491                        instruction = FLOAD;
492                        break;
493                case LONG:
494                        methodName = "putLongValue";
495                        instruction = LLOAD;
496                        break;
497                case DOUBLE:
498                        methodName = "putDoubleValue";
499                        instruction = DLOAD;
500                        break;
501                default:
502                        methodName = "put" + type.getInternalName().lastIndexOf('/') + "Value";
503                        instruction = ALOAD;
504                        break;
505                }
506
507                int offset = (instruction == LLOAD || instruction == DLOAD) ? 1 : 0;
508
509                int maxStack = 6;
510                MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, methodName, "(Ljava/lang/Object;Ljava/lang/String;" + typeNm + ")V", null, null);
511
512                mv.visitCode();
513
514                mv.visitVarInsn(ALOAD, 0);
515                mv.visitFieldInsn(GETFIELD, accessClassNm, "fieldNames", "[Ljava/lang/String;");
516                mv.visitVarInsn(ALOAD, 2);
517                mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "binarySearch", "([Ljava/lang/Object;Ljava/lang/Object;)I");
518
519                mv.visitVarInsn(ISTORE, 4 + offset);
520                mv.visitVarInsn(ILOAD, 4 + offset);
521
522                if (fields.length > 0) {
523                        maxStack = 6;
524
525                        Label[] labels = new Label[fields.length];
526                        Label labelForInvalidTypes = new Label();
527                        boolean hasAnyBadTypeLabel = false;
528
529                        for (int i = 0, n = labels.length; i < n; i++) {
530                                if (Type.getType(fields[i].getType()).equals(type))
531                                        labels[i] = new Label();
532                                else {
533                                        labels[i] = labelForInvalidTypes;
534                                        hasAnyBadTypeLabel = true;
535                                }
536                        }
537                        Label defaultLabel = new Label();
538                        mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels);
539
540                        for (int i = 0, n = labels.length; i < n; i++) {
541                                if (!labels[i].equals(labelForInvalidTypes)) {
542                                        Field field = fields[i];
543
544                                        mv.visitLabel(labels[i]);
545                                        mv.visitFrame(F_SAME, 0, null, 0, null);
546                                        mv.visitVarInsn(ALOAD, 1);
547                                        mv.visitTypeInsn(CHECKCAST, clazzNm);
548
549                                        mv.visitVarInsn(instruction, 3);
550                                        mv.visitFieldInsn(PUTFIELD, clazzNm, field.getName(), typeNm);
551                                        mv.visitInsn(RETURN);
552                                }
553                        }
554
555                        if (hasAnyBadTypeLabel) {
556                                mv.visitLabel(labelForInvalidTypes);
557                                mv.visitFrame(F_SAME, 0, null, 0, null);
558                                enhanceForThrowingException(mv, IllegalArgumentException.class, type.getClassName(), typeNm, instruction, 3);
559                        }
560
561                        mv.visitLabel(defaultLabel);
562                        mv.visitFrame(F_SAME, 0, null, 0, null);
563                }
564
565                final int maxLocals = 5 + offset;
566
567                enhanceForThrowingException(mv, IllegalArgumentException.class, "Field was not found", typeNm, instruction, 3);
568                mv.visitMaxs(maxStack, maxLocals);
569                mv.visitEnd();
570        }
571
572        private static void enhanceForGetValuePrimitive(ClassVisitor cw, String accessClassNm, String clazzNm, Field[] fields, Type type) {
573
574                String methodName;
575                final String typeNm = type.getDescriptor();
576                final int instruction;
577
578                switch (type.getSort()) {
579                case Type.BOOLEAN:
580                        methodName = "getBooleanValue";
581                        instruction = IRETURN;
582                        break;
583                case Type.BYTE:
584                        methodName = "getByteValue";
585                        instruction = IRETURN;
586                        break;
587                case Type.CHAR:
588                        methodName = "getCharValue";
589                        instruction = IRETURN;
590                        break;
591                case Type.SHORT:
592                        methodName = "getShortValue";
593                        instruction = IRETURN;
594                        break;
595                case Type.INT:
596                        methodName = "getIntValue";
597                        instruction = IRETURN;
598                        break;
599                case Type.FLOAT:
600                        methodName = "getFloatValue";
601                        instruction = FRETURN;
602                        break;
603                case Type.LONG:
604                        methodName = "getLongValue";
605                        instruction = LRETURN;
606                        break;
607                case Type.DOUBLE:
608                        methodName = "getDoubleValue";
609                        instruction = DRETURN;
610                        break;
611                default:
612                        methodName = "getValue";
613                        instruction = ARETURN;
614                        break;
615                }
616
617                MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, methodName, "(Ljava/lang/Object;Ljava/lang/String;)" + typeNm, null, null);
618
619                mv.visitCode();
620                mv.visitVarInsn(ALOAD, 0);
621                mv.visitFieldInsn(GETFIELD, accessClassNm, "fieldNames", "[Ljava/lang/String;");
622                mv.visitVarInsn(ALOAD, 2);
623                mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "binarySearch", "([Ljava/lang/Object;Ljava/lang/Object;)I");
624                mv.visitVarInsn(ISTORE, 3);
625                mv.visitVarInsn(ILOAD, 3);
626
627                final int maxStack;
628
629                if (fields.length > 0) {
630                        maxStack = 5;
631                        Label[] labels = constructLabels(fields);
632
633                        Label defaultLabel = new Label();
634                        mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels);
635
636                        for (int i = 0, n = labels.length; i < n; i++) {
637                                Field field = fields[i];
638                                mv.visitLabel(labels[i]);
639                                mv.visitFrame(F_SAME, 0, null, 0, null);
640                                mv.visitVarInsn(ALOAD, 1);
641                                mv.visitTypeInsn(CHECKCAST, clazzNm);
642                                mv.visitFieldInsn(GETFIELD, clazzNm, field.getName(), typeNm);
643                                mv.visitInsn(instruction);
644                        }
645
646                        mv.visitLabel(defaultLabel);
647                        mv.visitFrame(F_SAME, 0, null, 0, null);
648                } else {
649                        maxStack = 6;
650                }
651                enhanceForThrowingException(mv, IllegalArgumentException.class, "Field was not found", "Ljava/lang/Object;", ALOAD, 2);
652                mv.visitMaxs(maxStack, 4);
653                mv.visitEnd();
654        }
655
656        private static void enhanceForThrowingException(MethodVisitor mv, Class<? extends Exception> exceptionClass, String msg, String argType, int instruction, int slot) {
657
658                String exceptionClassNm = Type.getInternalName(exceptionClass);
659                mv.visitTypeInsn(NEW, exceptionClassNm);
660                mv.visitInsn(DUP);
661                mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
662                mv.visitInsn(DUP);
663                mv.visitLdcInsn(msg);
664                mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
665                mv.visitVarInsn(instruction, slot);
666                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(" + argType + ")Ljava/lang/StringBuilder;");
667                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
668                mv.visitMethodInsn(INVOKESPECIAL, exceptionClassNm, "<init>", "(Ljava/lang/String;)V");
669                mv.visitInsn(ATHROW);
670        }
671
672        private static void enhanceForInvokeMethod(ClassVisitor cw, String accessClassNm, String clazzNm, Method[] methods) {
673
674                MethodVisitor mv = cw.visitMethod(ACC_PUBLIC  + ACC_VARARGS, "invokeMethod", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", null, null);              
675        mv.visitCode();
676
677        if (methods.length > 0) {
678                
679            mv.visitVarInsn(ALOAD, 1);
680            mv.visitTypeInsn(CHECKCAST, clazzNm);
681            
682            mv.visitVarInsn(ASTORE, 4);
683            mv.visitVarInsn(ALOAD, 0);
684            mv.visitVarInsn(ALOAD, 2);
685            mv.visitMethodInsn(INVOKESPECIAL, "org/jadira/reflection/access/asm/AsmClassAccess", "getMethodIndex", "(Ljava/lang/reflect/Method;)I");            
686            mv.visitVarInsn(ISTORE, 5);
687                mv.visitVarInsn(ILOAD, 5);
688
689            Label[] labels = constructLabels(methods);
690                        Label defaultLabel = new Label();
691
692                        mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels);
693
694                        for (int i = 0; i < labels.length; i++) {
695
696                                Method method = methods[i];
697                                Class<?>[] parameterTypes = method.getParameterTypes();
698
699                                mv.visitLabel(labels[i]);
700
701                                if (i == 0) {
702                                        mv.visitFrame(F_APPEND, 1, new Object[] { clazzNm }, 0, null);
703                                } else {
704                                        mv.visitFrame(F_SAME, 0, null, 0, null);
705                                }
706                                mv.visitVarInsn(ALOAD, 4);
707
708                                StringBuilder methodDescriptor = new StringBuilder("(");
709                for (int parameterIdx = 0; parameterIdx < parameterTypes.length; parameterIdx++) {
710                        
711                        Type type = Type.getType(parameterTypes[parameterIdx]);
712                        
713                        mv.visitVarInsn(ALOAD, 3);
714                    mv.visitIntInsn(BIPUSH, parameterIdx);
715                    mv.visitInsn(AALOAD);
716                                
717                                        switch (type.getSort()) {
718                                        case Type.BOOLEAN:
719                                                mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
720                                                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
721                                                break;
722                                        case Type.BYTE:
723                                                mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
724                                                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B");
725                                                break;
726                                        case Type.CHAR:
727                                                mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
728                                                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
729                                                break;
730                                        case Type.SHORT:
731                                                mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
732                                                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
733                                                break;
734                                        case Type.INT:
735                                                mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
736                                                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
737                                                break;
738                                        case Type.FLOAT:
739                                                mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
740                                                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
741                                                break;
742                                        case Type.LONG:
743                                                mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
744                                                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
745                                                break;
746                                        case Type.DOUBLE:
747                                                mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
748                                                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
749                                                break;
750                                        case Type.ARRAY:
751                                                mv.visitTypeInsn(CHECKCAST, type.getDescriptor());
752                                                break;
753                                        case Type.OBJECT:
754                                                mv.visitTypeInsn(CHECKCAST, type.getInternalName());
755                                                break;
756                                        }
757                    methodDescriptor.append(type.getDescriptor());
758                }
759
760                methodDescriptor.append(')');
761                methodDescriptor.append(Type.getDescriptor(method.getReturnType()));
762                
763                mv.visitMethodInsn(INVOKEVIRTUAL, clazzNm, method.getName(), methodDescriptor.toString());
764
765                switch (Type.getType(method.getReturnType()).getSort()) {
766                case Type.VOID:
767                        mv.visitInsn(ACONST_NULL);
768                        break;
769                case Type.BOOLEAN:
770                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
771                        break;
772                case Type.BYTE:
773                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
774                        break;
775                case Type.CHAR:
776                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
777                        break;
778                case Type.SHORT:
779                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
780                        break;
781                case Type.INT:
782                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
783                        break;
784                case Type.FLOAT:
785                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
786                        break;
787                case Type.LONG:
788                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
789                        break;
790                case Type.DOUBLE:
791                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
792                        break;
793                }
794
795                mv.visitInsn(ARETURN);
796            }
797
798            mv.visitLabel(defaultLabel);
799            mv.visitFrame(F_SAME, 0, null, 0, null);
800        }
801        
802        enhanceForThrowingException(mv, IllegalArgumentException.class, "Method not found", "Ljava/lang/Object;", ALOAD, 2);
803        
804        mv.visitMaxs(8, 6);
805        mv.visitEnd();          
806        }
807        
808        @Override
809        public abstract C newInstance();
810
811        /**
812         * Retrieve the value of the field for the given instance
813         * @param object The instance to access the field for
814         * @param fieldName Name of the field to access
815         * @return The field value as an object
816         */
817        public abstract Object getValue(C object, String fieldName);
818
819        /**
820         * Update the value of the field for the given instance
821         * @param object The instance to access the field for
822         * @param fieldName Name of the field to access
823         * @param value The new value
824         */
825        public abstract void putValue(C object, String fieldName, Object value);
826
827        /**
828         * Retrieve the value of the field for the given instance
829         * @param object The instance to access the field for
830         * @param fieldName Name of the field to access
831         * @return The field value as a boolean
832         */
833        public abstract boolean getBooleanValue(C object, String fieldName);
834
835        /**
836         * Update the value of the field for the given instance
837         * @param object The instance to access the field for
838         * @param fieldName Name of the field to access
839         * @param value The new boolean value
840         */
841        public abstract void putBooleanValue(C object, String fieldName, boolean value);
842
843        /**
844         * Retrieve the value of the field for the given instance
845         * @param object The instance to access the field for
846         * @param fieldName Name of the field to access
847         * @return The field value as a byte
848         */
849        public abstract byte getByteValue(C object, String fieldName);
850
851        /**
852         * Update the value of the field for the given instance
853         * @param object The instance to access the field for
854         * @param fieldName Name of the field to access
855         * @param value The new byte value
856         */
857        public abstract void putByteValue(C object, String fieldName, byte value);
858
859        /**
860         * Retrieve the value of the field for the given instance
861         * @param object The instance to access the field for
862         * @param fieldName Name of the field to access
863         * @return The field value as a char
864         */
865        public abstract char getCharValue(C object, String fieldName);
866
867        /**
868         * Update the value of the field for the given instance
869         * @param object The instance to access the field for
870         * @param fieldName Name of the field to access
871         * @param value The new char value
872         */
873        public abstract void putCharValue(C object, String fieldName, char value);
874
875        /**
876         * Retrieve the value of the field for the given instance
877         * @param object The instance to access the field for
878         * @param fieldName Name of the field to access
879         * @return The field value as a short
880         */
881        public abstract short getShortValue(C object, String fieldName);
882
883        /**
884         * Update the value of the field for the given instance
885         * @param object The instance to access the field for
886         * @param fieldName Name of the field to access
887         * @param value The new short value
888         */
889        public abstract void putShortValue(C object, String fieldName, short value);
890
891        /**
892         * Retrieve the value of the field for the given instance
893         * @param object The instance to access the field for
894         * @param fieldName Name of the field to access
895         * @return The field value as a int
896         */
897        public abstract int getIntValue(C object, String fieldName);
898
899        /**
900         * Update the value of the field for the given instance
901         * @param object The instance to access the field for
902         * @param fieldName Name of the field to access
903         * @param value The new int value
904         */
905        public abstract void putIntValue(C object, String fieldName, int value);
906
907        /**
908         * Retrieve the value of the field for the given instance
909         * @param object The instance to access the field for
910         * @param fieldName Name of the field to access
911         * @return The field value as a long
912         */
913        public abstract long getLongValue(C object, String fieldName);
914
915        /**
916         * Update the value of the field for the given instance
917         * @param object The instance to access the field for
918         * @param fieldName Name of the field to access
919         * @param value The new long value
920         */
921        public abstract void putLongValue(C object, String fieldName, long value);
922
923        /**
924         * Retrieve the value of the field for the given instance
925         * @param object The instance to access the field for
926         * @param fieldName Name of the field to access
927         * @return The field value as a float
928         */
929        public abstract float getFloatValue(C object, String fieldName);
930
931        /**
932         * Update the value of the field for the given instance
933         * @param object The instance to access the field for
934         * @param fieldName Name of the field to access
935         * @param value The new float value
936         */
937        public abstract void putFloatValue(C object, String fieldName, float value);
938
939        /**
940         * Retrieve the value of the field for the given instance
941         * @param object The instance to access the field for
942         * @param fieldName Name of the field to access
943         * @return The field value as a double
944         */
945        public abstract double getDoubleValue(C object, String fieldName);
946
947        /**
948         * Update the value of the field for the given instance
949         * @param object The instance to access the field for
950         * @param fieldName Name of the field to access
951         * @param value The new double value
952         */
953        public abstract void putDoubleValue(C object, String fieldName, double value);
954
955        /**
956         * Invoke the given method on the given instance
957         * @param target The target object
958         * @param method The method to invoke
959         * @param args The parameters to pass
960         * @return The result of the invocation (null for a void method)
961         */
962        public abstract Object invokeMethod(Object target, Method method, Object[] args);
963        
964        String foo = new String();
965        
966        public Object invokeMethod2(Object target, Method method, Object[] args) {
967                return getMethodIndex(method);
968        }
969        
970        private int getMethodIndex(Method m) {
971                int idx = Arrays.binarySearch(getDeclaredMethodNames(), m.getName());
972                if (getDeclaredMethodAccessors()[idx].method().equals(m)) {
973                        return idx;
974                }
975                int backTrack = idx;
976                while (true) {
977                        if (getDeclaredMethodAccessors()[backTrack].method().equals(m)) {
978                                return idx;
979                        }
980                        if (!(getDeclaredMethodAccessors()[backTrack].method().getName()
981                                        .equals(m.getName()))) {
982                                break;
983                        }
984                        backTrack = backTrack - 1;
985                }
986                while (true) {
987                        idx = idx + 1;
988                        if (getDeclaredMethodAccessors()[idx].method().equals(m)) {
989                                return idx;
990                        }
991                        if (!(getDeclaredMethodAccessors()[idx].method().getName().equals(m.getName()))) {
992                                break;
993                        }
994                }
995                return -1;
996        }
997        
998        @Override
999        protected MethodAccess<C> constructMethodAccess(Method method) {
1000                return AsmMethodAccess.get(this, method);
1001        }
1002        
1003        @Override
1004        protected FieldAccess<C> constructFieldAccess(Field field) {
1005                if ((field.getModifiers() & Modifier.PRIVATE) != 0) {
1006                        return PortableFieldAccess.get(field);
1007                } else {
1008                        return AsmFieldAccess.get(this, field);
1009                }
1010        }
1011        
1012        @Override
1013        protected <X> ClassAccess<X> constructClassAccess(Class<X> clazz) {
1014                return AsmClassAccess.get(clazz);
1015        }
1016}