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}