0001: /*
0002: * Copyright 2005 JBoss Inc
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.drools.base;
0018:
0019: import java.lang.reflect.Method;
0020: import java.lang.reflect.Modifier;
0021: import java.security.AccessController;
0022: import java.security.PrivilegedAction;
0023: import java.security.ProtectionDomain;
0024: import java.util.ArrayList;
0025: import java.util.Collection;
0026: import java.util.HashMap;
0027: import java.util.Iterator;
0028: import java.util.List;
0029: import java.util.Map;
0030:
0031: import org.drools.RuntimeDroolsException;
0032: import org.drools.asm.ClassWriter;
0033: import org.drools.asm.FieldVisitor;
0034: import org.drools.asm.Label;
0035: import org.drools.asm.MethodVisitor;
0036: import org.drools.asm.Opcodes;
0037: import org.drools.asm.Type;
0038: import org.drools.util.ShadowProxyUtils;
0039:
0040: /**
0041: * A factory for ShadowProxy classes
0042: */
0043: public class ShadowProxyFactory {
0044: private static final String UPDATE_PROXY = "updateProxy";
0045: private static final String SET_SHADOWED_OBJECT = "setShadowedObject";
0046: private static final String GET_SHADOWED_OBJECT = "getShadowedObject";
0047:
0048: private static final String BASE_INTERFACE = Type
0049: .getInternalName(ShadowProxy.class);
0050:
0051: //private static final String FIELD_NAME_PREFIX = "__";
0052:
0053: public static final String FIELD_SET_FLAG = "IsSet";
0054:
0055: public static final String DELEGATE_FIELD_NAME = "delegate";
0056:
0057: public static final String HASHCACHE_FIELD_NAME = "__hashCache";
0058:
0059: private static final ProtectionDomain PROTECTION_DOMAIN;
0060:
0061: static {
0062: PROTECTION_DOMAIN = (ProtectionDomain) AccessController
0063: .doPrivileged(new PrivilegedAction() {
0064: public Object run() {
0065: return ShadowProxyFactory.class
0066: .getProtectionDomain();
0067: }
0068: });
0069: }
0070:
0071: public static Class getProxy(final Class clazz) {
0072: try {
0073: if (!isPossibleToGenerateTheProxyFor(clazz)) {
0074: return null;
0075: }
0076:
0077: final String className = getInternalProxyClassNameForClass(clazz);
0078: // generating byte array to create target class
0079: final byte[] bytes = dump(clazz, className);
0080: // use bytes to get a class
0081: final ByteArrayClassLoader classLoader = new ByteArrayClassLoader(
0082: Thread.currentThread().getContextClassLoader());
0083: final Class newClass = classLoader.defineClass(className
0084: .replace('/', '.'), bytes, PROTECTION_DOMAIN);
0085: return newClass;
0086: } catch (final Exception e) {
0087: throw new RuntimeDroolsException(e);
0088: }
0089: }
0090:
0091: public static byte[] getProxyBytes(final Class clazz) {
0092: try {
0093: if (!isPossibleToGenerateTheProxyFor(clazz)) {
0094: return null;
0095: }
0096:
0097: final String className = getInternalProxyClassNameForClass(clazz);
0098: // generating byte array to create target class
0099: final byte[] bytes = dump(clazz, className);
0100: return bytes;
0101: } catch (final Exception e) {
0102: throw new RuntimeDroolsException(e);
0103: }
0104: }
0105:
0106: protected static boolean isPossibleToGenerateTheProxyFor(
0107: final Class clazz) throws Exception {
0108: if ((clazz.getModifiers() & Modifier.FINAL) != 0) {
0109: return false;
0110: }
0111: try {
0112: Method equals = clazz.getMethod("equals",
0113: new Class[] { Object.class });
0114: if (Modifier.isFinal(equals.getModifiers())) {
0115: return false;
0116: }
0117: } catch (NoSuchMethodException e) {
0118: // that's fine
0119: }
0120: try {
0121: Method hashcode = clazz.getMethod("hashCode", new Class[0]);
0122: if (Modifier.isFinal(hashcode.getModifiers())) {
0123: return false;
0124: }
0125: } catch (NoSuchMethodException e) {
0126: // that's fine
0127: }
0128: return true;
0129: }
0130:
0131: /**
0132: * @param clazz
0133: * @return
0134: */
0135: public static String getInternalProxyClassNameForClass(
0136: final Class clazz) {
0137: String className = null;
0138: if (clazz.getPackage() != null
0139: && (clazz.getPackage().getName().startsWith("java.") || clazz
0140: .getPackage().getName().startsWith("javax."))) {
0141: className = "org/drools/shadow/"
0142: + Type.getInternalName(clazz) + "ShadowProxy";
0143: } else {
0144: className = Type.getInternalName(clazz) + "ShadowProxy";
0145: }
0146: return className;
0147: }
0148:
0149: public static String getProxyClassNameForClass(final Class clazz) {
0150: String className = null;
0151: Package pkg = clazz.getPackage();
0152: if (pkg != null
0153: && (pkg.getName().startsWith("java.") || pkg.getName()
0154: .startsWith("javax."))) {
0155: className = "org.drools.shadow." + clazz.getName()
0156: + "ShadowProxy";
0157: } else {
0158: className = clazz.getName() + "ShadowProxy";
0159: }
0160: return className;
0161: }
0162:
0163: protected static byte[] dump(final Class clazz,
0164: final String className) throws Exception {
0165:
0166: final ClassWriter cw = new ClassWriter(true);
0167:
0168: buildClassHeader(clazz, className, cw);
0169:
0170: buildConstructor(clazz, className, cw);
0171:
0172: buildField(ShadowProxyFactory.DELEGATE_FIELD_NAME, Type
0173: .getDescriptor(clazz), cw);
0174:
0175: final Method getShadowed = ShadowProxy.class.getDeclaredMethod(
0176: GET_SHADOWED_OBJECT, new Class[] {});
0177: final Method setShadowed = ShadowProxy.class.getDeclaredMethod(
0178: SET_SHADOWED_OBJECT, new Class[] { Object.class });
0179: buildSimpleGetMethod(ShadowProxyFactory.DELEGATE_FIELD_NAME,
0180: clazz, getShadowed, className, clazz, cw);
0181:
0182: buildSetShadowedObject(clazz, className, setShadowed, cw);
0183:
0184: if (Collection.class.isAssignableFrom(clazz)) {
0185: buildCollectionClass(clazz, className, cw);
0186: } else if (Map.class.isAssignableFrom(clazz)) {
0187: buildMapClass(clazz, className, cw);
0188: } else {
0189: buildRegularClass(clazz, className, cw);
0190: }
0191:
0192: return cw.toByteArray();
0193: }
0194:
0195: private static void buildCollectionClass(final Class clazz,
0196: final String className, final ClassWriter cw) {
0197:
0198: buildCollectionUpdateProxyMethod(clazz, className, cw);
0199:
0200: }
0201:
0202: private static void buildMapClass(final Class clazz,
0203: final String className, final ClassWriter cw) {
0204:
0205: buildMapUpdateProxyMethod(clazz, className, cw);
0206:
0207: }
0208:
0209: private static void buildRegularClass(final Class clazz,
0210: final String className, final ClassWriter cw) {
0211: final Map fieldTypes = new HashMap();
0212: final Method[] methods = getMethods(clazz);
0213: for (int i = 0; i < methods.length; i++) {
0214: if ((!Modifier.isFinal(methods[i].getModifiers()))
0215: && Modifier.isPublic(methods[i].getModifiers())
0216: && (!Modifier.isStatic(methods[i].getModifiers()))) {
0217: if ((!methods[i].getReturnType().equals(Void.TYPE))
0218: && (methods[i].getParameterTypes().length == 0)
0219: && (!methods[i].getName().equals("hashCode"))
0220: && (!methods[i].getName().equals("toString"))) {
0221:
0222: final String fieldName = methods[i].getName();
0223:
0224: buildField(fieldName, Type.getDescriptor(methods[i]
0225: .getReturnType()), cw);
0226: fieldTypes.put(fieldName, methods[i]);
0227:
0228: buildField(fieldName
0229: + ShadowProxyFactory.FIELD_SET_FLAG,
0230: Type.BOOLEAN_TYPE.getDescriptor(), cw);
0231: buildGetMethod(fieldName, methods[i]
0232: .getReturnType(), fieldName
0233: + ShadowProxyFactory.FIELD_SET_FLAG,
0234: methods[i], className, clazz, cw);
0235: } else if ((!methods[i].getName().equals("hashCode"))
0236: && (!methods[i].getName().equals("equals"))) {
0237: buildDelegateMethod(methods[i], clazz, className,
0238: cw);
0239: }
0240: }
0241: }
0242:
0243: buildUpdateProxyMethod(fieldTypes, className, cw);
0244:
0245: buildEquals(cw, className, clazz, fieldTypes);
0246:
0247: buildField(ShadowProxyFactory.HASHCACHE_FIELD_NAME, Type
0248: .getDescriptor(int.class), cw);
0249:
0250: buildHashCode(cw, className, clazz, fieldTypes);
0251: }
0252:
0253: /**
0254: * Filter out any method we are not interested in
0255: * @param clazz
0256: * @return
0257: */
0258: private static Method[] getMethods(final Class clazz) {
0259: // to help filtering process, we will create a map of maps:
0260: // Map< String methodName, Map< Class[] parameterTypes, Method method > >
0261: final Map map = new HashMap();
0262: final List helperList = new ArrayList();
0263: final Method[] methods = clazz.getMethods();
0264: for (int i = 0; i < methods.length; i++) {
0265: Method previous = null;
0266: Map signatures = (Map) map.get(methods[i].getName());
0267: final ParametersWrapper key = new ParametersWrapper(
0268: methods[i].getParameterTypes());
0269: if (signatures != null) {
0270: previous = (Method) signatures.get(key);
0271: }
0272: // if no previous method with the same name and parameter types is found
0273: // or if the previous method's return type is a super class of the
0274: // current method's return type, add current to the map
0275: // overriding previous if it exists
0276: if ((previous == null)
0277: || (previous.getReturnType()
0278: .isAssignableFrom(methods[i]
0279: .getReturnType()))) {
0280: if (signatures == null) {
0281: signatures = new HashMap();
0282: map.put(methods[i].getName(), signatures);
0283: }
0284: if (signatures.put(key, methods[i]) != null) {
0285: helperList.remove(previous);
0286: }
0287: helperList.add(methods[i]);
0288: }
0289: }
0290: return (Method[]) helperList.toArray(new Method[helperList
0291: .size()]);
0292: }
0293:
0294: private static class ParametersWrapper {
0295: private Class[] parameters;
0296:
0297: public ParametersWrapper(final Class[] parameters) {
0298: this .parameters = parameters;
0299: }
0300:
0301: public int hashCode() {
0302: return this .parameters.length;
0303: }
0304:
0305: public boolean equals(final Object o) {
0306: if (!(o instanceof ParametersWrapper)) {
0307: return false;
0308: }
0309: final ParametersWrapper other = (ParametersWrapper) o;
0310:
0311: if (this .parameters.length != other.parameters.length) {
0312: return false;
0313: }
0314:
0315: for (int i = 0; i < this .parameters.length; i++) {
0316: if (!this .parameters[i].equals(other.parameters[i])) {
0317: return false;
0318: }
0319: }
0320: return true;
0321: }
0322: }
0323:
0324: /**
0325: * Builds the shadow proxy class header
0326: *
0327: * @param clazz The class to build shadow proxy for
0328: * @param className The shadow proxy class name
0329: * @param cw
0330: */
0331: protected static void buildClassHeader(final Class clazz,
0332: final String className, final ClassWriter cw) {
0333: if (clazz.isInterface()) {
0334: cw.visit(Opcodes.V1_2, Opcodes.ACC_PUBLIC
0335: + Opcodes.ACC_SUPER, className, null, Type
0336: .getInternalName(Object.class), new String[] {
0337: ShadowProxyFactory.BASE_INTERFACE,
0338: Type.getInternalName(clazz) });
0339: } else {
0340: cw.visit(Opcodes.V1_2, Opcodes.ACC_PUBLIC
0341: + Opcodes.ACC_SUPER, className, null, Type
0342: .getInternalName(clazz),
0343: new String[] { ShadowProxyFactory.BASE_INTERFACE });
0344: }
0345:
0346: cw.visitSource(null, null);
0347: }
0348:
0349: /**
0350: * Creates the field defined by the given FieldDefinition
0351: *
0352: * @param cw
0353: * @param fieldDef
0354: */
0355: protected static void buildField(final String name,
0356: final String type, final ClassWriter cw) {
0357: FieldVisitor fv;
0358: fv = cw.visitField(Opcodes.ACC_PRIVATE, name, type, null, null);
0359: fv.visitEnd();
0360: }
0361:
0362: /**
0363: * Creates a constructor for the shadow proxy receiving
0364: * the actual delegate class as parameter
0365: *
0366: * @param originalClassName
0367: * @param className
0368: * @param cw
0369: */
0370: private static void buildConstructor(final Class clazz,
0371: final String className, final ClassWriter cw) {
0372: MethodVisitor mv;
0373: {
0374: mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", Type
0375: .getMethodDescriptor(Type.VOID_TYPE,
0376: new Type[] { Type.getType(clazz) }), null,
0377: null);
0378: mv.visitCode();
0379:
0380: // super();
0381: final Label l0 = new Label();
0382: mv.visitLabel(l0);
0383: mv.visitLineNumber(41, l0);
0384: mv.visitVarInsn(Opcodes.ALOAD, 0);
0385: if (clazz.isInterface()) {
0386: mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type
0387: .getInternalName(Object.class), "<init>", Type
0388: .getMethodDescriptor(Type.VOID_TYPE,
0389: new Type[] {}));
0390: } else {
0391: mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type
0392: .getInternalName(clazz), "<init>", Type
0393: .getMethodDescriptor(Type.VOID_TYPE,
0394: new Type[] {}));
0395: }
0396:
0397: // this.delegate = delegate
0398: final Label l1 = new Label();
0399: mv.visitLabel(l1);
0400: mv.visitLineNumber(42, l1);
0401: mv.visitVarInsn(Opcodes.ALOAD, 0);
0402: mv.visitVarInsn(Opcodes.ALOAD, 1);
0403: mv.visitFieldInsn(Opcodes.PUTFIELD, className,
0404: ShadowProxyFactory.DELEGATE_FIELD_NAME, Type
0405: .getDescriptor(clazz));
0406:
0407: // return
0408: final Label l2 = new Label();
0409: mv.visitLabel(l2);
0410: mv.visitLineNumber(43, l2);
0411: mv.visitInsn(Opcodes.RETURN);
0412:
0413: final Label l3 = new Label();
0414: mv.visitLabel(l3);
0415: mv.visitLocalVariable("this", "L" + className + ";", null,
0416: l0, l3, 0);
0417: mv.visitLocalVariable(
0418: ShadowProxyFactory.DELEGATE_FIELD_NAME, Type
0419: .getDescriptor(clazz), null, l0, l3, 1);
0420: mv.visitMaxs(0, 0);
0421: mv.visitEnd();
0422: }
0423: }
0424:
0425: /**
0426: * Creates the proxy reader method for the given method
0427: *
0428: * @param fieldName
0429: * @param fieldFlag
0430: * @param method
0431: * @param cw
0432: */
0433: protected static void buildGetMethod(final String fieldName,
0434: final Class fieldType, final String fieldFlag,
0435: final Method method, final String className,
0436: final Class clazz, final ClassWriter cw) {
0437: // method signature
0438: final Class[] exceptionTypes = method.getExceptionTypes();
0439: final String[] exceptions = getExceptionArrayAsString(exceptionTypes);
0440: final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC,
0441: method.getName(), Type.getMethodDescriptor(method),
0442: null, exceptions);
0443: mv.visitCode();
0444:
0445: // if ( ! _fieldIsSet ) {
0446: final Label l0 = new Label();
0447: mv.visitLabel(l0);
0448: mv.visitVarInsn(Opcodes.ALOAD, 0);
0449: mv.visitFieldInsn(Opcodes.GETFIELD, className, fieldFlag,
0450: Type.BOOLEAN_TYPE.getDescriptor());
0451: final Label l1 = new Label();
0452: mv.visitJumpInsn(Opcodes.IFNE, l1);
0453:
0454: // __fieldIsSet = true;
0455: final Label l3 = new Label();
0456: mv.visitLabel(l3);
0457: mv.visitVarInsn(Opcodes.ALOAD, 0);
0458: mv.visitInsn(Opcodes.ICONST_1);
0459: mv.visitFieldInsn(Opcodes.PUTFIELD, className, fieldFlag,
0460: Type.BOOLEAN_TYPE.getDescriptor());
0461:
0462: if (Map.class.isAssignableFrom(fieldType)
0463: || Collection.class.isAssignableFrom(fieldType)
0464: || fieldType.isArray()) {
0465:
0466: // FieldType aux = this.delegate.getField();
0467: Label l01 = new Label();
0468: mv.visitLabel(l01);
0469: mv.visitVarInsn(Opcodes.ALOAD, 0);
0470: mv.visitFieldInsn(Opcodes.GETFIELD, className,
0471: DELEGATE_FIELD_NAME, Type.getDescriptor(clazz));
0472: if (clazz.isInterface()) {
0473: mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type
0474: .getInternalName(clazz), method.getName(), Type
0475: .getMethodDescriptor(method));
0476: } else {
0477: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type
0478: .getInternalName(clazz), method.getName(), Type
0479: .getMethodDescriptor(method));
0480: }
0481: mv.visitVarInsn(Opcodes.ASTORE, 1);
0482:
0483: // this.field = (FieldType) ShadoProxyUtils.clone( aux );
0484: Label l11 = new Label();
0485: mv.visitLabel(l11);
0486: mv.visitVarInsn(Opcodes.ALOAD, 0);
0487: mv.visitVarInsn(Opcodes.ALOAD, 1);
0488: mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type
0489: .getInternalName(ShadowProxyUtils.class),
0490: "cloneObject",
0491: "(Ljava/lang/Object;)Ljava/lang/Object;");
0492: mv.visitTypeInsn(Opcodes.CHECKCAST, Type
0493: .getInternalName(fieldType));
0494: mv.visitFieldInsn(Opcodes.PUTFIELD, className, fieldName,
0495: Type.getDescriptor(fieldType));
0496:
0497: } else {
0498: // __field = this.delegate.method();
0499: final Label l2 = new Label();
0500: mv.visitLabel(l2);
0501: mv.visitVarInsn(Opcodes.ALOAD, 0);
0502: mv.visitVarInsn(Opcodes.ALOAD, 0);
0503: mv.visitFieldInsn(Opcodes.GETFIELD, className,
0504: ShadowProxyFactory.DELEGATE_FIELD_NAME, Type
0505: .getDescriptor(clazz));
0506: if (clazz.isInterface()) {
0507: mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type
0508: .getInternalName(clazz), method.getName(), Type
0509: .getMethodDescriptor(method));
0510: } else {
0511: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type
0512: .getInternalName(clazz), method.getName(), Type
0513: .getMethodDescriptor(method));
0514: }
0515: mv.visitFieldInsn(Opcodes.PUTFIELD, className, fieldName,
0516: Type.getDescriptor(fieldType));
0517:
0518: }
0519:
0520: // }
0521: // return __field;
0522: mv.visitLabel(l1);
0523: mv.visitVarInsn(Opcodes.ALOAD, 0);
0524: mv.visitFieldInsn(Opcodes.GETFIELD, className, fieldName, Type
0525: .getDescriptor(fieldType));
0526: mv
0527: .visitInsn(Type.getType(fieldType).getOpcode(
0528: Opcodes.IRETURN));
0529:
0530: // local variables table
0531: final Label l4 = new Label();
0532: mv.visitLabel(l4);
0533: mv.visitLocalVariable("this", "L" + className + ";", null, l0,
0534: l4, 0);
0535: mv.visitMaxs(0, 0);
0536: mv.visitEnd();
0537: }
0538:
0539: /**
0540: * Creates the proxy reader method for the given method
0541: *
0542: * @param fieldName
0543: * @param fieldFlag
0544: * @param method
0545: * @param cw
0546: */
0547: protected static void buildSimpleGetMethod(final String fieldName,
0548: final Class fieldType, final Method method,
0549: final String className, final Class clazz,
0550: final ClassWriter cw) {
0551:
0552: final Class[] exceptionTypes = method.getExceptionTypes();
0553: final String[] exceptions = getExceptionArrayAsString(exceptionTypes);
0554: // method signature
0555: final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC,
0556: method.getName(), Type.getMethodDescriptor(method),
0557: null, exceptions);
0558: mv.visitCode();
0559:
0560: // return __field;
0561: final Label l0 = new Label();
0562: mv.visitLabel(l0);
0563: mv.visitVarInsn(Opcodes.ALOAD, 0);
0564: mv.visitFieldInsn(Opcodes.GETFIELD, className, fieldName, Type
0565: .getDescriptor(fieldType));
0566: mv
0567: .visitInsn(Type.getType(fieldType).getOpcode(
0568: Opcodes.IRETURN));
0569:
0570: // local variables table
0571: final Label l4 = new Label();
0572: mv.visitLabel(l4);
0573: mv.visitLocalVariable("this", "L" + className + ";", null, l0,
0574: l4, 0);
0575: mv.visitMaxs(0, 0);
0576: mv.visitEnd();
0577: }
0578:
0579: protected static void buildUpdateProxyMethod(final Map fieldTypes,
0580: final String className, final ClassWriter cw) {
0581: final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC,
0582: ShadowProxyFactory.UPDATE_PROXY, Type
0583: .getMethodDescriptor(Type.VOID_TYPE,
0584: new Type[] {}), null, null);
0585: mv.visitCode();
0586: final Label l0 = new Label();
0587: mv.visitLabel(l0);
0588: for (final Iterator it = fieldTypes.entrySet().iterator(); it
0589: .hasNext();) {
0590: final Map.Entry entry = (Map.Entry) it.next();
0591: final String fieldName = (String) entry.getKey();
0592: final String fieldFlag = fieldName
0593: + ShadowProxyFactory.FIELD_SET_FLAG;
0594: final Class fieldType = ((Method) entry.getValue())
0595: .getReturnType();
0596: final Label l1 = new Label();
0597: mv.visitLabel(l1);
0598: mv.visitVarInsn(Opcodes.ALOAD, 0);
0599: if (fieldType.isPrimitive()) {
0600: if (fieldType.equals(Long.TYPE)) {
0601: mv.visitInsn(Opcodes.LCONST_0);
0602: } else if (fieldType.equals(Double.TYPE)) {
0603: mv.visitInsn(Opcodes.DCONST_0);
0604: } else if (fieldType.equals(Float.TYPE)) {
0605: mv.visitInsn(Opcodes.FCONST_0);
0606: } else {
0607: mv.visitInsn(Opcodes.ICONST_0);
0608: }
0609: } else {
0610: mv.visitInsn(Opcodes.ACONST_NULL);
0611: }
0612: mv.visitFieldInsn(Opcodes.PUTFIELD, className, fieldName,
0613: Type.getDescriptor(fieldType));
0614: final Label l2 = new Label();
0615: mv.visitLabel(l2);
0616: mv.visitVarInsn(Opcodes.ALOAD, 0);
0617: mv.visitInsn(Opcodes.ICONST_0);
0618: mv.visitFieldInsn(Opcodes.PUTFIELD, className, fieldFlag,
0619: Type.BOOLEAN_TYPE.getDescriptor());
0620: }
0621:
0622: // this.__hashCache = 0;
0623: mv.visitVarInsn(Opcodes.ALOAD, 0);
0624: mv.visitInsn(Opcodes.ICONST_0);
0625: mv.visitFieldInsn(Opcodes.PUTFIELD, className,
0626: ShadowProxyFactory.HASHCACHE_FIELD_NAME, Type
0627: .getDescriptor(int.class));
0628:
0629: final Label l4 = new Label();
0630: mv.visitLabel(l4);
0631: mv.visitInsn(Opcodes.RETURN);
0632: final Label l5 = new Label();
0633: mv.visitLabel(l5);
0634: mv.visitLocalVariable("this", "L" + className + ";", null, l0,
0635: l5, 0);
0636: mv.visitMaxs(0, 0);
0637: mv.visitEnd();
0638: }
0639:
0640: protected static void buildSetShadowedObject(final Class clazz,
0641: final String className, final Method setShadowed,
0642: final ClassWriter cw) {
0643: final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC,
0644: setShadowed.getName(), Type
0645: .getMethodDescriptor(setShadowed), null, null);
0646: mv.visitCode();
0647: Label l0 = new Label();
0648: mv.visitLabel(l0);
0649: // this.delegate = (<clazz>) object;
0650: mv.visitVarInsn(Opcodes.ALOAD, 0);
0651: mv.visitVarInsn(Opcodes.ALOAD, 1);
0652: mv
0653: .visitTypeInsn(Opcodes.CHECKCAST, Type
0654: .getInternalName(clazz));
0655: mv.visitFieldInsn(Opcodes.PUTFIELD, className,
0656: DELEGATE_FIELD_NAME, Type.getDescriptor(clazz));
0657: if (Collection.class.isAssignableFrom(clazz)
0658: || Map.class.isAssignableFrom(clazz)) {
0659: Label l1 = new Label();
0660: mv.visitLabel(l1);
0661: mv.visitVarInsn(Opcodes.ALOAD, 0);
0662: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className,
0663: UPDATE_PROXY, Type.getMethodDescriptor(
0664: Type.VOID_TYPE, new Type[0]));
0665: }
0666: Label l2 = new Label();
0667: mv.visitLabel(l2);
0668: mv.visitInsn(Opcodes.RETURN);
0669: Label l3 = new Label();
0670: mv.visitLabel(l3);
0671: mv.visitLocalVariable("this", "L" + className + ";", null, l0,
0672: l3, 0);
0673: mv.visitLocalVariable("object", Type
0674: .getDescriptor(Object.class), null, l0, l3, 1);
0675: mv.visitMaxs(0, 0);
0676: mv.visitEnd();
0677: }
0678:
0679: /**
0680: * Creates an update proxy method for Map classes
0681: *
0682: * public void updateProxy() {
0683: * this.clear();
0684: * this.addAll( this.delegate );
0685: * }
0686: *
0687: * @param clazz
0688: * @param className
0689: * @param cw
0690: */
0691: protected static void buildCollectionUpdateProxyMethod(
0692: final Class clazz, final String className,
0693: final ClassWriter cw) {
0694: final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC,
0695: ShadowProxyFactory.UPDATE_PROXY, Type
0696: .getMethodDescriptor(Type.VOID_TYPE,
0697: new Type[] {}), null, null);
0698: mv.visitCode();
0699: final Label l0 = new Label();
0700: mv.visitLabel(l0);
0701: // this.clear();
0702: mv.visitVarInsn(Opcodes.ALOAD, 0);
0703: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, "clear",
0704: Type.getMethodDescriptor(Type.VOID_TYPE, new Type[0]));
0705: Label l1 = new Label();
0706: mv.visitLabel(l1);
0707: // this.addAll( this.delegate );
0708: mv.visitVarInsn(Opcodes.ALOAD, 0);
0709: mv.visitVarInsn(Opcodes.ALOAD, 0);
0710: mv.visitFieldInsn(Opcodes.GETFIELD, className,
0711: DELEGATE_FIELD_NAME, Type.getDescriptor(clazz));
0712: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, "addAll",
0713: Type.getMethodDescriptor(Type.BOOLEAN_TYPE,
0714: new Type[] { Type.getType(Collection.class) }));
0715: mv.visitInsn(Opcodes.POP);
0716: Label l2 = new Label();
0717: mv.visitLabel(l2);
0718: mv.visitInsn(Opcodes.RETURN);
0719: Label l3 = new Label();
0720: mv.visitLabel(l3);
0721: mv.visitLocalVariable("this", "L" + className + ";", null, l0,
0722: l3, 0);
0723: mv.visitMaxs(0, 0);
0724: mv.visitEnd();
0725: }
0726:
0727: /**
0728: * Creates an update proxy method for Map classes
0729: *
0730: * public void updateProxy() {
0731: * this.clear();
0732: * this.putAll( this.delegate );
0733: * }
0734: *
0735: * @param clazz
0736: * @param className
0737: * @param cw
0738: */
0739: protected static void buildMapUpdateProxyMethod(final Class clazz,
0740: final String className, final ClassWriter cw) {
0741: final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC,
0742: ShadowProxyFactory.UPDATE_PROXY, Type
0743: .getMethodDescriptor(Type.VOID_TYPE,
0744: new Type[] {}), null, null);
0745: mv.visitCode();
0746: final Label l0 = new Label();
0747: mv.visitLabel(l0);
0748: // this.clear();
0749: mv.visitVarInsn(Opcodes.ALOAD, 0);
0750: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, "clear",
0751: Type.getMethodDescriptor(Type.VOID_TYPE, new Type[0]));
0752: Label l1 = new Label();
0753: mv.visitLabel(l1);
0754: // this.putAll( this.delegate );
0755: mv.visitVarInsn(Opcodes.ALOAD, 0);
0756: mv.visitVarInsn(Opcodes.ALOAD, 0);
0757: mv.visitFieldInsn(Opcodes.GETFIELD, className,
0758: DELEGATE_FIELD_NAME, Type.getDescriptor(clazz));
0759: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, "putAll",
0760: Type.getMethodDescriptor(Type.VOID_TYPE,
0761: new Type[] { Type.getType(Map.class) }));
0762: Label l2 = new Label();
0763: mv.visitLabel(l2);
0764: mv.visitInsn(Opcodes.RETURN);
0765: Label l3 = new Label();
0766: mv.visitLabel(l3);
0767: mv.visitLocalVariable("this", "L" + className + ";", null, l0,
0768: l3, 0);
0769: mv.visitMaxs(0, 0);
0770: mv.visitEnd();
0771: }
0772:
0773: protected static void buildDelegateMethod(final Method method,
0774: final Class clazz, final String className,
0775: final ClassWriter cw) {
0776:
0777: // creating method visitor
0778: final String[] exceptions = getExceptionArrayAsString(method
0779: .getExceptionTypes());
0780: final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC,
0781: method.getName(), Type.getMethodDescriptor(method),
0782: null, exceptions);
0783: mv.visitCode();
0784:
0785: // return this.delegate.method(...);
0786: final Label l0 = new Label();
0787: mv.visitLabel(l0);
0788: mv.visitVarInsn(Opcodes.ALOAD, 0);
0789: mv.visitFieldInsn(Opcodes.GETFIELD, className,
0790: ShadowProxyFactory.DELEGATE_FIELD_NAME, Type
0791: .getDescriptor(clazz));
0792:
0793: final Class[] parameters = method.getParameterTypes();
0794: for (int i = 0, offset = 1; i < parameters.length; i++) {
0795: Type type = Type.getType(parameters[i]);
0796: mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), offset);
0797: offset += type.getSize();
0798: }
0799: if (clazz.isInterface()) {
0800: mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type
0801: .getInternalName(clazz), method.getName(), Type
0802: .getMethodDescriptor(method));
0803: } else {
0804: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type
0805: .getInternalName(clazz), method.getName(), Type
0806: .getMethodDescriptor(method));
0807: }
0808: mv.visitInsn(Type.getType(method.getReturnType()).getOpcode(
0809: Opcodes.IRETURN));
0810: final Label l1 = new Label();
0811: mv.visitLabel(l1);
0812: mv.visitLocalVariable("this", "L" + className + ";", null, l0,
0813: l1, 0);
0814: for (int i = 0, offset = 0; i < parameters.length; i++) {
0815: mv
0816: .visitLocalVariable("arg" + i, Type
0817: .getDescriptor(parameters[i]), null, l0,
0818: l1, offset);
0819: offset += Type.getType(parameters[i]).getSize();
0820: }
0821: mv.visitMaxs(0, 0);
0822: mv.visitEnd();
0823: }
0824:
0825: protected static void buildEquals(final ClassWriter cw,
0826: final String className, final Class clazz,
0827: final Map fieldTypes) {
0828: MethodVisitor mv;
0829: // Building equals method
0830: {
0831: mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "equals", Type
0832: .getMethodDescriptor(Type.BOOLEAN_TYPE,
0833: new Type[] { Type.getType(Object.class) }),
0834: null, null);
0835: mv.visitCode();
0836: final Label l0 = new Label();
0837: mv.visitLabel(l0);
0838:
0839: // if ( this == object || this.delegate == object || this.delegate.equals( object ) ) {
0840: mv.visitVarInsn(Opcodes.ALOAD, 0);
0841: mv.visitVarInsn(Opcodes.ALOAD, 1);
0842: final Label l1 = new Label();
0843: mv.visitJumpInsn(Opcodes.IF_ACMPEQ, l1);
0844: mv.visitVarInsn(Opcodes.ALOAD, 0);
0845: mv.visitFieldInsn(Opcodes.GETFIELD, className,
0846: DELEGATE_FIELD_NAME, Type.getDescriptor(clazz));
0847: mv.visitVarInsn(Opcodes.ALOAD, 1);
0848: mv.visitJumpInsn(Opcodes.IF_ACMPEQ, l1);
0849:
0850: mv.visitVarInsn(Opcodes.ALOAD, 0);
0851: mv.visitFieldInsn(Opcodes.GETFIELD, className,
0852: DELEGATE_FIELD_NAME, Type.getDescriptor(clazz));
0853: mv.visitVarInsn(Opcodes.ALOAD, 1);
0854: if (clazz.isInterface()) {
0855: mv
0856: .visitMethodInsn(
0857: Opcodes.INVOKEINTERFACE,
0858: Type.getInternalName(clazz),
0859: "equals",
0860: Type
0861: .getMethodDescriptor(
0862: Type.BOOLEAN_TYPE,
0863: new Type[] { Type
0864: .getType(Object.class) }));
0865: } else {
0866: mv
0867: .visitMethodInsn(
0868: Opcodes.INVOKEVIRTUAL,
0869: Type.getInternalName(clazz),
0870: "equals",
0871: Type
0872: .getMethodDescriptor(
0873: Type.BOOLEAN_TYPE,
0874: new Type[] { Type
0875: .getType(Object.class) }));
0876: }
0877: Label l2 = new Label();
0878: mv.visitJumpInsn(Opcodes.IFEQ, l2);
0879:
0880: // return true;
0881: mv.visitLabel(l1);
0882: mv.visitInsn(Opcodes.ICONST_1);
0883: mv.visitInsn(Opcodes.IRETURN);
0884: mv.visitLabel(l2);
0885:
0886: // if (( object == null ) || ( ! ( object instanceof <class> ) ) )
0887: mv.visitVarInsn(Opcodes.ALOAD, 1);
0888: final Label l3 = new Label();
0889: mv.visitJumpInsn(Opcodes.IFNULL, l3);
0890: mv.visitVarInsn(Opcodes.ALOAD, 1);
0891: mv.visitTypeInsn(Opcodes.INSTANCEOF, Type
0892: .getInternalName(clazz));
0893: final Label l4 = new Label();
0894: mv.visitJumpInsn(Opcodes.IFNE, l4);
0895:
0896: // return false;
0897: mv.visitLabel(l3);
0898: mv.visitInsn(Opcodes.ICONST_0);
0899: mv.visitInsn(Opcodes.IRETURN);
0900: mv.visitLabel(l4);
0901:
0902: // if( object instanceof ShadowProxy &&
0903: // ( this.delegate == ((ShadowProxy)object).delegate ||
0904: // this.delegate.equals( ((ShadowProxy)object).delegate ) ) ) {
0905: Label c0 = new Label();
0906: mv.visitLabel(c0);
0907: mv.visitVarInsn(Opcodes.ALOAD, 1);
0908: mv.visitTypeInsn(Opcodes.INSTANCEOF, className);
0909: Label c1 = new Label();
0910: mv.visitJumpInsn(Opcodes.IFEQ, c1);
0911: mv.visitVarInsn(Opcodes.ALOAD, 0);
0912: mv.visitFieldInsn(Opcodes.GETFIELD, className,
0913: DELEGATE_FIELD_NAME, Type.getDescriptor(clazz));
0914: mv.visitVarInsn(Opcodes.ALOAD, 1);
0915: mv.visitTypeInsn(Opcodes.CHECKCAST, className);
0916: mv.visitFieldInsn(Opcodes.GETFIELD, className,
0917: DELEGATE_FIELD_NAME, Type.getDescriptor(clazz));
0918: Label c2 = new Label();
0919: mv.visitJumpInsn(Opcodes.IF_ACMPEQ, c2);
0920: mv.visitVarInsn(Opcodes.ALOAD, 0);
0921: mv.visitFieldInsn(Opcodes.GETFIELD, className,
0922: DELEGATE_FIELD_NAME, Type.getDescriptor(clazz));
0923: mv.visitVarInsn(Opcodes.ALOAD, 1);
0924: mv.visitTypeInsn(Opcodes.CHECKCAST, className);
0925: mv.visitFieldInsn(Opcodes.GETFIELD, className,
0926: DELEGATE_FIELD_NAME, Type.getDescriptor(clazz));
0927: if (clazz.isInterface()) {
0928: mv
0929: .visitMethodInsn(
0930: Opcodes.INVOKEINTERFACE,
0931: Type.getInternalName(clazz),
0932: "equals",
0933: Type
0934: .getMethodDescriptor(
0935: Type.BOOLEAN_TYPE,
0936: new Type[] { Type
0937: .getType(Object.class) }));
0938: } else {
0939: mv
0940: .visitMethodInsn(
0941: Opcodes.INVOKEVIRTUAL,
0942: Type.getInternalName(clazz),
0943: "equals",
0944: Type
0945: .getMethodDescriptor(
0946: Type.BOOLEAN_TYPE,
0947: new Type[] { Type
0948: .getType(Object.class) }));
0949: }
0950: mv.visitJumpInsn(Opcodes.IFEQ, c1);
0951: mv.visitLabel(c2);
0952: // return true;
0953: mv.visitInsn(Opcodes.ICONST_1);
0954: mv.visitInsn(Opcodes.IRETURN);
0955: // }
0956: mv.visitLabel(c1);
0957:
0958: // return false;
0959: mv.visitInsn(Opcodes.ICONST_0);
0960: mv.visitInsn(Opcodes.IRETURN);
0961: final Label lastLabel = new Label();
0962: mv.visitLabel(lastLabel);
0963:
0964: mv.visitLocalVariable("this", "L" + className + ";", null,
0965: l0, lastLabel, 0);
0966: mv.visitLocalVariable("object", Type
0967: .getDescriptor(Object.class), null, l0, lastLabel,
0968: 1);
0969:
0970: mv.visitMaxs(0, 0);
0971: mv.visitEnd();
0972: }
0973: }
0974:
0975: protected static void buildCollectionEquals(final ClassWriter cw,
0976: final String className, final Class clazz) {
0977:
0978: final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC,
0979: "equals", Type.getMethodDescriptor(Type.BOOLEAN_TYPE,
0980: new Type[] { Type.getType(Object.class) }),
0981: null, null);
0982: // if ( this == object ) {
0983: Label l0 = new Label();
0984: mv.visitLabel(l0);
0985: mv.visitVarInsn(Opcodes.ALOAD, 0);
0986: mv.visitVarInsn(Opcodes.ALOAD, 1);
0987: Label l1 = new Label();
0988: mv.visitJumpInsn(Opcodes.IF_ACMPNE, l1);
0989: // return true;
0990: Label l2 = new Label();
0991: mv.visitLabel(l2);
0992: mv.visitInsn(Opcodes.ICONST_1);
0993: mv.visitInsn(Opcodes.IRETURN);
0994: // }
0995: mv.visitLabel(l1);
0996: // return this.delegate.equals( object );
0997: mv.visitVarInsn(Opcodes.ALOAD, 0);
0998: mv.visitFieldInsn(Opcodes.GETFIELD, className,
0999: DELEGATE_FIELD_NAME, Type.getDescriptor(clazz));
1000: mv.visitVarInsn(Opcodes.ALOAD, 1);
1001: if (clazz.isInterface()) {
1002: mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type
1003: .getInternalName(clazz), "equals", Type
1004: .getMethodDescriptor(Type.BOOLEAN_TYPE,
1005: new Type[] { Type.getType(Object.class) }));
1006: } else {
1007: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type
1008: .getInternalName(clazz), "equals", Type
1009: .getMethodDescriptor(Type.BOOLEAN_TYPE,
1010: new Type[] { Type.getType(Object.class) }));
1011: }
1012: mv.visitInsn(Opcodes.IRETURN);
1013: Label l3 = new Label();
1014: mv.visitLabel(l3);
1015: mv.visitLocalVariable("this", "L" + className + ";", null, l0,
1016: l3, 0);
1017: mv.visitLocalVariable("object", Type
1018: .getDescriptor(Object.class), null, l0, l3, 1);
1019: mv.visitMaxs(0, 0);
1020: mv.visitEnd();
1021: }
1022:
1023: /**
1024: * Sample of generated code for all primitive + object types
1025: *
1026: * public int hashCode() {
1027: * if( ___hashCache == 0 ) {
1028: * __hashCache = this.delegate.hashCode();
1029: * }
1030: * return this.__hashCache;
1031: * }
1032: *
1033: * @param cw
1034: * @param className
1035: * @param clazz
1036: * @param fieldTypes
1037: */
1038: protected static void buildHashCode(final ClassWriter cw,
1039: final String className, final Class clazz,
1040: final Map fieldTypes) {
1041: MethodVisitor mv;
1042: // Building hashcode method
1043: {
1044: mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "hashCode", Type
1045: .getMethodDescriptor(Type.INT_TYPE, new Type[] {}),
1046: null, null);
1047: mv.visitCode();
1048:
1049: // if( this.__hashCache == 0 ) {
1050: Label l0 = new Label();
1051: mv.visitLabel(l0);
1052: mv.visitVarInsn(Opcodes.ALOAD, 0);
1053: mv
1054: .visitFieldInsn(Opcodes.GETFIELD, className,
1055: HASHCACHE_FIELD_NAME, Type.INT_TYPE
1056: .getDescriptor());
1057: Label l1 = new Label();
1058: mv.visitJumpInsn(Opcodes.IFNE, l1);
1059: Label l2 = new Label();
1060:
1061: // this.__hashCache = this.delegate.hashCode();
1062: mv.visitLabel(l2);
1063: mv.visitVarInsn(Opcodes.ALOAD, 0);
1064: mv.visitVarInsn(Opcodes.ALOAD, 0);
1065: mv.visitFieldInsn(Opcodes.GETFIELD, className,
1066: DELEGATE_FIELD_NAME, Type.getDescriptor(clazz));
1067: if (clazz.isInterface()) {
1068: mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type
1069: .getInternalName(clazz), "hashCode",
1070: Type.getMethodDescriptor(Type.INT_TYPE,
1071: new Type[0]));
1072: } else {
1073: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type
1074: .getInternalName(clazz), "hashCode",
1075: Type.getMethodDescriptor(Type.INT_TYPE,
1076: new Type[0]));
1077: }
1078: mv
1079: .visitFieldInsn(Opcodes.PUTFIELD, className,
1080: HASHCACHE_FIELD_NAME, Type.INT_TYPE
1081: .getDescriptor());
1082: // }
1083: mv.visitLabel(l1);
1084:
1085: // return this.__hashCache;
1086: mv.visitVarInsn(Opcodes.ALOAD, 0);
1087: mv
1088: .visitFieldInsn(Opcodes.GETFIELD, className,
1089: HASHCACHE_FIELD_NAME, Type.INT_TYPE
1090: .getDescriptor());
1091: mv.visitInsn(Opcodes.IRETURN);
1092: Label l3 = new Label();
1093: mv.visitLabel(l3);
1094: mv.visitLocalVariable("this", "L" + className + ";", null,
1095: l0, l3, 0);
1096:
1097: mv.visitMaxs(0, 0);
1098: mv.visitEnd();
1099: }
1100: }
1101:
1102: /**
1103: * @param exceptionTypes
1104: * @return
1105: */
1106: private static String[] getExceptionArrayAsString(
1107: final Class[] exceptionTypes) {
1108: final String[] exceptions = new String[exceptionTypes.length];
1109: for (int i = 0; i < exceptions.length; i++) {
1110: exceptions[i] = Type.getInternalName(exceptionTypes[i]);
1111: }
1112: return exceptions;
1113: }
1114:
1115: /**
1116: * Simple classloader
1117: * @author Michael Neale
1118: */
1119: static class ByteArrayClassLoader extends ClassLoader {
1120: public ByteArrayClassLoader(final ClassLoader parent) {
1121: super (parent);
1122: }
1123:
1124: public Class defineClass(final String name, final byte[] bytes,
1125: final ProtectionDomain PROTECTION_DOMAIN) {
1126: return defineClass(name, bytes, 0, bytes.length,
1127: PROTECTION_DOMAIN);
1128: }
1129: }
1130:
1131: }
|