001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.objectserver.managedobject.bytecode;
006:
007: import com.tc.asm.ClassWriter;
008: import com.tc.asm.FieldVisitor;
009: import com.tc.asm.Label;
010: import com.tc.asm.MethodVisitor;
011: import com.tc.asm.Opcodes;
012: import com.tc.exception.TCRuntimeException;
013: import com.tc.object.LiteralValues;
014: import com.tc.object.ObjectID;
015: import com.tc.util.AdaptedClassDumper;
016: import com.tc.util.Assert;
017:
018: import java.util.ArrayList;
019: import java.util.Collections;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024:
025: public class PhysicalStateClassLoader extends ClassLoader implements
026: Opcodes {
027:
028: public static class MethodDetail {
029:
030: private final String methodName;
031: private final String methodDesc;
032:
033: public MethodDetail(String methodName, String methodDesc) {
034: this .methodName = methodName;
035: this .methodDesc = methodDesc;
036: }
037:
038: public String getMethodDescriptor() {
039: return methodDesc;
040: }
041:
042: public String getMethodName() {
043: return methodName;
044: }
045:
046: }
047:
048: private static final String PARENT_ID_FIELD = "parentId";
049:
050: private static final Map OBJECT_OUTPUT_METHODS = Collections
051: .synchronizedMap(new HashMap());
052: private static final Map OBJECT_INPUT_METHODS = Collections
053: .synchronizedMap(new HashMap());
054:
055: static {
056: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.INTEGER,
057: "writeInt", "(I)V");
058: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.LONG,
059: "writeLong", "(J)V");
060: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.CHARACTER,
061: "writeChar", "(I)V");
062: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.BYTE,
063: "writeByte", "(I)V");
064: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.SHORT,
065: "writeShort", "(I)V");
066: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.FLOAT,
067: "writeFloat", "(F)V");
068: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.DOUBLE,
069: "writeDouble", "(D)V");
070: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.BOOLEAN,
071: "writeBoolean", "(Z)V");
072: // rest are written as Objects - Since we use TCObjectOutputStream, we optimize it there.
073: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.OBJECT,
074: "writeObject", "(Ljava/lang/Object;)V");
075: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.OBJECT_ID,
076: "writeObject", "(Ljava/lang/Object;)V");
077: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.ARRAY,
078: "writeObject", "(Ljava/lang/Object;)V");
079: addMapping(OBJECT_OUTPUT_METHODS,
080: LiteralValues.JAVA_LANG_CLASS, "writeObject",
081: "(Ljava/lang/Object;)V");
082: addMapping(OBJECT_OUTPUT_METHODS,
083: LiteralValues.JAVA_LANG_CLASS_HOLDER, "writeObject",
084: "(Ljava/lang/Object;)V");
085: addMapping(OBJECT_OUTPUT_METHODS,
086: LiteralValues.STACK_TRACE_ELEMENT, "writeObject",
087: "(Ljava/lang/Object;)V");
088: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.STRING,
089: "writeObject", "(Ljava/lang/Object;)V");
090: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.STRING_BYTES,
091: "writeObject", "(Ljava/lang/Object;)V");
092: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.BIG_INTEGER,
093: "writeObject", "(Ljava/lang/Object;)V");
094: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.BIG_DECIMAL,
095: "writeObject", "(Ljava/lang/Object;)V");
096:
097: addMapping(OBJECT_OUTPUT_METHODS,
098: LiteralValues.JAVA_LANG_CLASSLOADER, "writeObject",
099: "(Ljava/lang/Object;)V");
100: addMapping(OBJECT_OUTPUT_METHODS,
101: LiteralValues.JAVA_LANG_CLASSLOADER_HOLDER,
102: "writeObject", "(Ljava/lang/Object;)V");
103: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.ENUM,
104: "writeObject", "(Ljava/lang/Object;)V");
105: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.ENUM_HOLDER,
106: "writeObject", "(Ljava/lang/Object;)V");
107: addMapping(OBJECT_OUTPUT_METHODS, LiteralValues.CURRENCY,
108: "writeObject", "(Ljava/lang/Object;)V");
109:
110: addMapping(OBJECT_INPUT_METHODS, LiteralValues.INTEGER,
111: "readInt", "()I");
112: addMapping(OBJECT_INPUT_METHODS, LiteralValues.LONG,
113: "readLong", "()J");
114: addMapping(OBJECT_INPUT_METHODS, LiteralValues.CHARACTER,
115: "readChar", "()C");
116: addMapping(OBJECT_INPUT_METHODS, LiteralValues.BYTE,
117: "readByte", "()B");
118: addMapping(OBJECT_INPUT_METHODS, LiteralValues.SHORT,
119: "readShort", "()S");
120: addMapping(OBJECT_INPUT_METHODS, LiteralValues.FLOAT,
121: "readFloat", "()F");
122: addMapping(OBJECT_INPUT_METHODS, LiteralValues.DOUBLE,
123: "readDouble", "()D");
124: addMapping(OBJECT_INPUT_METHODS, LiteralValues.BOOLEAN,
125: "readBoolean", "()Z");
126: // rest are read as Objects - Since we use TCObjectInputStream, we optimize it there.
127: addMapping(OBJECT_INPUT_METHODS, LiteralValues.OBJECT,
128: "readObject", "()Ljava/lang/Object;");
129: addMapping(OBJECT_INPUT_METHODS, LiteralValues.OBJECT_ID,
130: "readObject", "()Ljava/lang/Object;");
131: addMapping(OBJECT_INPUT_METHODS, LiteralValues.ARRAY,
132: "readObject", "()Ljava/lang/Object;");
133: addMapping(OBJECT_INPUT_METHODS, LiteralValues.JAVA_LANG_CLASS,
134: "readObject", "()Ljava/lang/Object;");
135: addMapping(OBJECT_INPUT_METHODS,
136: LiteralValues.JAVA_LANG_CLASS_HOLDER, "readObject",
137: "()Ljava/lang/Object;");
138: addMapping(OBJECT_INPUT_METHODS,
139: LiteralValues.STACK_TRACE_ELEMENT, "readObject",
140: "()Ljava/lang/Object;");
141: addMapping(OBJECT_INPUT_METHODS, LiteralValues.STRING,
142: "readObject", "()Ljava/lang/Object;");
143: addMapping(OBJECT_INPUT_METHODS, LiteralValues.STRING_BYTES,
144: "readObject", "()Ljava/lang/Object;");
145: addMapping(OBJECT_INPUT_METHODS, LiteralValues.BIG_INTEGER,
146: "readObject", "()Ljava/lang/Object;");
147: addMapping(OBJECT_INPUT_METHODS, LiteralValues.BIG_DECIMAL,
148: "readObject", "()Ljava/lang/Object;");
149:
150: addMapping(OBJECT_INPUT_METHODS,
151: LiteralValues.JAVA_LANG_CLASSLOADER, "readObject",
152: "()Ljava/lang/Object;");
153: addMapping(OBJECT_INPUT_METHODS,
154: LiteralValues.JAVA_LANG_CLASSLOADER_HOLDER,
155: "readObject", "()Ljava/lang/Object;");
156: addMapping(OBJECT_INPUT_METHODS, LiteralValues.ENUM,
157: "readObject", "()Ljava/lang/Object;");
158: addMapping(OBJECT_INPUT_METHODS, LiteralValues.ENUM_HOLDER,
159: "readObject", "()Ljava/lang/Object;");
160: addMapping(OBJECT_INPUT_METHODS, LiteralValues.CURRENCY,
161: "readObject", "()Ljava/lang/Object;");
162: }
163:
164: public PhysicalStateClassLoader(ClassLoader parent) {
165: super (parent);
166: }
167:
168: private static void addMapping(Map map, int type,
169: String methodName, String methodDesc) {
170: map.put(new Integer(type), new MethodDetail(methodName,
171: methodDesc));
172: }
173:
174: private static MethodDetail get(Map map, int type) {
175: MethodDetail md = (MethodDetail) map.get(new Integer(type));
176: if (md == null) {
177: throw new TCRuntimeException("Unknown Type : " + type
178: + " Map = " + map);
179: }
180: return md;
181: }
182:
183: // Helper Method for tests
184: public static void verifyTypePresent(int type) {
185: MethodDetail md = get(OBJECT_INPUT_METHODS, type);
186: Assert.assertNotNull(md);
187: md = get(OBJECT_OUTPUT_METHODS, type);
188: Assert.assertNotNull(md);
189: }
190:
191: public PhysicalStateClassLoader() {
192: super ();
193: }
194:
195: public byte[] createClassBytes(ClassSpec cs, ObjectID parentID,
196: List fields) {
197: byte data[] = basicCreateClassBytes(cs, parentID, fields);
198: AdaptedClassDumper.INSTANCE.write(cs.getGeneratedClassName(),
199: data);
200: return data;
201: }
202:
203: public Class defineClassFromBytes(String className, int classId,
204: byte[] clazzBytes, int offset, int length) {
205: Class clazz = defineClass(className, clazzBytes, offset, length);
206: return clazz;
207: }
208:
209: private byte[] basicCreateClassBytes(ClassSpec cs,
210: ObjectID parentID, List fields) {
211: String classNameSlash = cs.getGeneratedClassName().replace('.',
212: '/');
213: String super ClassNameSlash = cs.getSuperClassName().replace(
214: '.', '/');
215: ClassWriter cw = new ClassWriter(0); // don't compute maxs
216:
217: cw.visit(V1_2, ACC_PUBLIC | ACC_SUPER, classNameSlash, null,
218: super ClassNameSlash, null);
219:
220: createConstructor(cw, super ClassNameSlash);
221: if (!parentID.isNull()) {
222: createParentIDField(cw);
223: createGetParentIDMethod(cw, classNameSlash);
224: createSetParentIDMethod(cw, classNameSlash);
225: }
226:
227: createFields(cw, fields);
228: createGetClassNameMethod(cw, classNameSlash, cs);
229: createGetLoaderDescriptionMethod(cw, classNameSlash, cs);
230: createGetObjectReferencesMethod(cw, classNameSlash, parentID,
231: cs, super ClassNameSlash, fields);
232: createBasicSetMethod(cw, classNameSlash, cs,
233: super ClassNameSlash, fields);
234: createBasicDehydrateMethod(cw, classNameSlash, cs,
235: super ClassNameSlash, fields);
236: createAddValuesMethod(cw, classNameSlash, cs,
237: super ClassNameSlash, fields);
238: createWriteObjectMethod(cw, classNameSlash, cs,
239: super ClassNameSlash, fields);
240: createReadObjectMethod(cw, classNameSlash, cs,
241: super ClassNameSlash, fields);
242:
243: createGetClassIdMethod(cw, classNameSlash, cs);
244:
245: cw.visitEnd();
246: return cw.toByteArray();
247: }
248:
249: // *************************************************************************************
250: // The Code generated by this method looks (kind of) this.
251: //
252: // public String getLoaderDescription() {
253: // return "System.ext";
254: // }
255: // *************************************************************************************
256: private void createGetLoaderDescriptionMethod(ClassWriter cw,
257: String classNameSlash, ClassSpec cs) {
258: if (!cs.isDirectSubClassOfPhysicalMOState()) {
259: // We dont have to regenerate this method as the super class would have it.
260: return;
261: }
262: MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,
263: "getLoaderDescription", "()Ljava/lang/String;", null,
264: null);
265: mv.visitCode();
266: mv.visitLdcInsn(cs.getLoaderDesc());
267: mv.visitInsn(ARETURN);
268: mv.visitMaxs(1, 1);
269: mv.visitEnd();
270: }
271:
272: // *************************************************************************************
273: // The Code generated by this method looks (kind of) this.
274: //
275: // public String getClassName() {
276: // return "com.tc.className";
277: // }
278: // *************************************************************************************
279: private void createGetClassNameMethod(ClassWriter cw,
280: String classNameSlash, ClassSpec cs) {
281: if (!cs.isDirectSubClassOfPhysicalMOState()) {
282: // We dont have to regenerate this method as the super class would have it.
283: return;
284: }
285: MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "getClassName",
286: "()Ljava/lang/String;", null, null);
287: mv.visitCode();
288: mv.visitLdcInsn(cs.getClassName());
289: mv.visitInsn(ARETURN);
290: mv.visitMaxs(1, 1);
291: mv.visitEnd();
292: }
293:
294: // *************************************************************************************
295: // The Code generated by this method looks (kind of) this.
296: //
297: // protected int getClassId() {
298: // return 8;
299: // }
300: // *************************************************************************************
301: private void createGetClassIdMethod(ClassWriter cw,
302: String classNameSlash, ClassSpec cs) {
303: MethodVisitor mv = cw.visitMethod(ACC_PROTECTED, "getClassId",
304: "()I", null, null);
305: mv.visitLdcInsn(new Integer(cs.getClassID()));
306: mv.visitInsn(IRETURN);
307: mv.visitMaxs(1, 1);
308: mv.visitEnd();
309: }
310:
311: // *************************************************************************************
312: // The Code generated by this method looks (kind of) this.
313: //
314: // long x;
315: // Object o;
316: // char c;
317: // public void writeObject(ObjectOutput out) throws IOException {
318: // out.writeLong(x);
319: // out.writeObject(o);
320: // out.writeChar(c);
321: // }
322: // *************************************************************************************
323: private void createWriteObjectMethod(ClassWriter cw,
324: String classNameSlash, ClassSpec cs,
325: String super ClassNameSlash, List fields) {
326: MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "writeObject",
327: "(Ljava/io/ObjectOutput;)V", null,
328: new String[] { "java/io/IOException" });
329: mv.visitCode();
330:
331: if (!cs.isDirectSubClassOfPhysicalMOState()) {
332: mv.visitVarInsn(ALOAD, 0);
333: mv.visitVarInsn(ALOAD, 1);
334: mv.visitMethodInsn(INVOKESPECIAL, super ClassNameSlash,
335: "writeObject", "(Ljava/io/ObjectOutput;)V");
336: }
337:
338: for (Iterator i = fields.iterator(); i.hasNext();) {
339: FieldType f = (FieldType) i.next();
340: mv.visitVarInsn(ALOAD, 1);
341: mv.visitVarInsn(ALOAD, 0);
342: mv
343: .visitFieldInsn(GETFIELD, classNameSlash, f
344: .getLocalFieldName(), getFieldTypeDesc(f
345: .getType()));
346: MethodDetail md = get(OBJECT_OUTPUT_METHODS, f.getType());
347: mv.visitMethodInsn(INVOKEINTERFACE, "java/io/ObjectOutput",
348: md.getMethodName(), md.getMethodDescriptor());
349: }
350: mv.visitInsn(RETURN);
351: mv.visitMaxs(3, 2);
352: mv.visitEnd();
353: }
354:
355: // *************************************************************************************
356: // The Code generated by this method looks (kind of) this.
357: //
358: // long x;
359: // Object o;
360: // char c;
361: //
362: // public void readObject(ObjectInput in) throws IOException, ClassNotFoundException {
363: // x = in.readLong();
364: // o = in.readObject();
365: // c = in.readChar();
366: // }
367: // *************************************************************************************
368: private void createReadObjectMethod(ClassWriter cw,
369: String classNameSlash, ClassSpec cs,
370: String super ClassNameSlash, List fields) {
371: MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "readObject",
372: "(Ljava/io/ObjectInput;)V", null, new String[] {
373: "java/io/IOException",
374: "java/lang/ClassNotFoundException" });
375: mv.visitCode();
376:
377: if (!cs.isDirectSubClassOfPhysicalMOState()) {
378: mv.visitVarInsn(ALOAD, 0);
379: mv.visitVarInsn(ALOAD, 1);
380: mv.visitMethodInsn(INVOKESPECIAL, super ClassNameSlash,
381: "readObject", "(Ljava/io/ObjectInput;)V");
382: }
383:
384: for (Iterator i = fields.iterator(); i.hasNext();) {
385: FieldType f = (FieldType) i.next();
386: mv.visitVarInsn(ALOAD, 0);
387: mv.visitVarInsn(ALOAD, 1);
388: MethodDetail md = get(OBJECT_INPUT_METHODS, f.getType());
389: mv.visitMethodInsn(INVOKEINTERFACE, "java/io/ObjectInput",
390: md.getMethodName(), md.getMethodDescriptor());
391: mv
392: .visitFieldInsn(PUTFIELD, classNameSlash, f
393: .getLocalFieldName(), getFieldTypeDesc(f
394: .getType()));
395: }
396: mv.visitInsn(RETURN);
397: mv.visitMaxs(3, 2);
398: mv.visitEnd();
399: }
400:
401: // *************************************************************************************
402: // The Code generated by this method looks (kind of) this.
403: //
404: // long x;
405: // Object o;
406: // public Map addValues(Map map) {
407: // map.put("x", new Long(x));
408: // map.put("o", o);
409: // return map;
410: // }
411: // *************************************************************************************
412: private void createAddValuesMethod(ClassWriter cw,
413: String classNameSlash, ClassSpec cs,
414: String super ClassNameSlash, List fields) {
415: MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "addValues",
416: "(Ljava/util/Map;)Ljava/util/Map;", null, null);
417: mv.visitCode();
418:
419: if (!cs.isDirectSubClassOfPhysicalMOState()) {
420: mv.visitVarInsn(ALOAD, 0);
421: mv.visitVarInsn(ALOAD, 1);
422: mv.visitMethodInsn(INVOKESPECIAL, super ClassNameSlash,
423: "addValues", "(Ljava/util/Map;)Ljava/util/Map;");
424: mv.visitInsn(POP);
425: }
426:
427: for (Iterator i = fields.iterator(); i.hasNext();) {
428: FieldType f = (FieldType) i.next();
429: mv.visitVarInsn(ALOAD, 1);
430: mv.visitLdcInsn(f.getQualifiedName());
431: getObjectFor(mv, classNameSlash, f);
432: mv
433: .visitMethodInsn(INVOKEINTERFACE, "java/util/Map",
434: "put",
435: "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
436: mv.visitInsn(POP);
437: }
438: mv.visitVarInsn(ALOAD, 1);
439: mv.visitInsn(ARETURN);
440:
441: mv.visitMaxs(6, 2);
442: mv.visitEnd();
443:
444: }
445:
446: // *************************************************************************************
447: // The Code generated by this method looks (kind of) this.
448: //
449: // long x;
450: // Object o;
451: // protected void basicDehydrate(DNAWriter writer) {
452: // writer.addPhysicalAction("x", new Long(x), false);
453: // writer.addPhysicalAction("o", o, true);
454: // }
455: // *************************************************************************************
456: private void createBasicDehydrateMethod(ClassWriter cw,
457: String classNameSlash, ClassSpec cs,
458: String super ClassNameSlash, List fields) {
459: MethodVisitor mv = cw.visitMethod(ACC_PROTECTED,
460: "basicDehydrate",
461: "(Lcom/tc/object/dna/api/DNAWriter;)V", null, null);
462: mv.visitCode();
463:
464: if (!cs.isDirectSubClassOfPhysicalMOState()) {
465: mv.visitVarInsn(ALOAD, 0);
466: mv.visitVarInsn(ALOAD, 1);
467: mv.visitMethodInsn(INVOKESPECIAL, super ClassNameSlash,
468: "basicDehydrate",
469: "(Lcom/tc/object/dna/api/DNAWriter;)V");
470: }
471:
472: for (Iterator i = fields.iterator(); i.hasNext();) {
473: FieldType f = (FieldType) i.next();
474: mv.visitVarInsn(ALOAD, 1);
475: mv.visitLdcInsn(f.getQualifiedName());
476: getObjectFor(mv, classNameSlash, f);
477: if (f.canBeReferenced()) {
478: mv.visitInsn(ICONST_1); // true
479: } else {
480: mv.visitInsn(ICONST_0); // false
481: }
482: // XXX:: We are calling DNAWriter methods from instrumented code !
483: mv.visitMethodInsn(INVOKEINTERFACE,
484: "com/tc/object/dna/api/DNAWriter",
485: "addPhysicalAction",
486: "(Ljava/lang/String;Ljava/lang/Object;Z)V");
487: }
488: mv.visitInsn(RETURN);
489:
490: mv.visitMaxs(6, 2);
491: mv.visitEnd();
492: }
493:
494: // *************************************************************************************
495: // The Code generated by this method looks (kind of) this.
496: //
497: // long x;
498: // Object o;
499: // protected Object basicSet(String f, Object value) {
500: // if ("x".equals(f)) {
501: // Object old = new Long(x);
502: // x = ((Long) value).longValue();
503: // return old;
504: // }
505: // if("o".equals(f)) {
506: // Object old = o;
507: // o = value;
508: // return old;
509: // }
510: // throw new ClassNotCompatableException("Not found ! field = " + f + " value = " + value);
511: // }
512: // *************************************************************************************
513: private void createBasicSetMethod(ClassWriter cw,
514: String classNameSlash, ClassSpec cs,
515: String super ClassNameSlash, List fields) {
516: MethodVisitor mv = cw
517: .visitMethod(
518: ACC_PROTECTED,
519: "basicSet",
520: "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;",
521: null, null);
522: mv.visitCode();
523:
524: for (Iterator i = fields.iterator(); i.hasNext();) {
525: FieldType f = (FieldType) i.next();
526: mv.visitLdcInsn(f.getQualifiedName());
527: mv.visitVarInsn(ALOAD, 1);
528: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String",
529: "equals", "(Ljava/lang/Object;)Z");
530: Label l1 = new Label();
531: mv.visitJumpInsn(IFEQ, l1);
532: getObjectFor(mv, classNameSlash, f);
533: mv.visitVarInsn(ASTORE, 3);
534: mv.visitVarInsn(ALOAD, 0);
535: mv.visitVarInsn(ALOAD, 2);
536: getValueFrom(mv, classNameSlash, f);
537: mv
538: .visitFieldInsn(PUTFIELD, classNameSlash, f
539: .getLocalFieldName(), getFieldTypeDesc(f
540: .getType()));
541: mv.visitVarInsn(ALOAD, 3);
542: mv.visitInsn(ARETURN);
543: mv.visitLabel(l1);
544: }
545:
546: if (cs.isDirectSubClassOfPhysicalMOState()) {
547: // throw Assertion Error
548: mv
549: .visitTypeInsn(NEW,
550: "com/tc/objectserver/managedobject/bytecode/ClassNotCompatableException");
551: mv.visitInsn(DUP);
552: mv.visitTypeInsn(NEW, "java/lang/StringBuffer");
553: mv.visitInsn(DUP);
554: mv.visitLdcInsn("Not found ! field = ");
555: mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer",
556: "<init>", "(Ljava/lang/String;)V");
557: mv.visitVarInsn(ALOAD, 1);
558: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer",
559: "append",
560: "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
561: mv.visitLdcInsn(" value = ");
562: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer",
563: "append",
564: "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
565: mv.visitVarInsn(ALOAD, 2);
566: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer",
567: "append",
568: "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
569: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer",
570: "toString", "()Ljava/lang/String;");
571: mv
572: .visitMethodInsn(
573: INVOKESPECIAL,
574: "com/tc/objectserver/managedobject/bytecode/ClassNotCompatableException",
575: "<init>", "(Ljava/lang/String;)V");
576: mv.visitInsn(ATHROW);
577: } else {
578: // Call super class's implementation
579: mv.visitVarInsn(ALOAD, 0);
580: mv.visitVarInsn(ALOAD, 1);
581: mv.visitVarInsn(ALOAD, 2);
582: mv
583: .visitMethodInsn(INVOKESPECIAL,
584: super ClassNameSlash, "basicSet",
585: "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;");
586: mv.visitInsn(ARETURN);
587: }
588:
589: mv.visitMaxs(5, 4);
590: mv.visitEnd();
591: }
592:
593: /**
594: * This method generates code to the object reference from the top of the stack and convert it into a primitive type
595: * (if needed) and leave that value in the top of the stack.
596: */
597: private void getValueFrom(MethodVisitor mv, String classNameSlash,
598: FieldType f) {
599: String classOnStack = getClassNameFor(f.getType());
600: if ("java/lang/Object".equals(classOnStack)) {
601: return;
602: }
603: mv.visitTypeInsn(CHECKCAST, classOnStack);
604: mv.visitMethodInsn(INVOKEVIRTUAL, classOnStack,
605: getMethodNameForPrimitives(f.getType()), "()"
606: + getFieldTypeDesc((f.getType())));
607:
608: }
609:
610: /**
611: * This method generates code so that the object equivalent of the field is left on the stack.
612: */
613: private void getObjectFor(MethodVisitor mv, String classNameSlash,
614: FieldType f) {
615: String classToReturn = getClassNameFor(f.getType());
616: if ("java/lang/Object".equals(classToReturn)) {
617: mv.visitVarInsn(ALOAD, 0);
618: mv
619: .visitFieldInsn(GETFIELD, classNameSlash, f
620: .getLocalFieldName(), getFieldTypeDesc(f
621: .getType()));
622: return;
623: }
624: String fieldTypeDesc = getFieldTypeDesc(f.getType());
625: String constructorDesc = "(" + fieldTypeDesc + ")V";
626: mv.visitTypeInsn(NEW, classToReturn);
627: mv.visitInsn(DUP);
628: mv.visitVarInsn(ALOAD, 0);
629: mv.visitFieldInsn(GETFIELD, classNameSlash, f
630: .getLocalFieldName(), fieldTypeDesc);
631: mv.visitMethodInsn(INVOKESPECIAL, classToReturn, "<init>",
632: constructorDesc);
633:
634: }
635:
636: // *************************************************************************************
637: // The Code generated by this method looks (kind of) this.
638: //
639: // private Object oid1;
640: // public Set getObjectReferences() {
641: // Set result = new THashSet(25);
642: // if (oid1 instanceof ObjectID && !((ObjectID)oid1).isNull() ) {
643: // result.add(oid1);
644: // }
645: // return result;
646: // }
647: // *************************************************************************************
648: private void createGetObjectReferencesMethod(ClassWriter cw,
649: String classNameSlash, ObjectID parentID, ClassSpec cs,
650: String super ClassNameSlash, List fields) {
651: List referenceFields = new ArrayList(fields.size());
652: for (Iterator i = fields.iterator(); i.hasNext();) {
653: FieldType f = (FieldType) i.next();
654: if (f.getType() == LiteralValues.OBJECT_ID) {
655: referenceFields.add(f);
656: }
657: }
658:
659: // There is no references in this object and it is not a direct subclass of Physical Managed Object State
660: if (referenceFields.size() == 0 && parentID.isNull()
661: && !cs.isDirectSubClassOfPhysicalMOState()) {
662: // The parent object has the necessary implementations
663: return;
664: }
665:
666: MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,
667: "getObjectReferences", "()Ljava/util/Set;", null, null);
668: mv.visitCode();
669:
670: // There is no references in this object
671: if (referenceFields.size() == 0 && parentID.isNull()) {
672: mv.visitFieldInsn(GETSTATIC, "java/util/Collections",
673: "EMPTY_SET", "Ljava/util/Set;");
674: mv.visitInsn(ARETURN);
675: mv.visitMaxs(1, 1);
676: mv.visitEnd();
677: return;
678: }
679:
680: int size = referenceFields.size();
681: if (!parentID.isNull()
682: && cs.isDirectSubClassOfPhysicalMOState()) {
683: size++;
684: }
685:
686: mv.visitTypeInsn(NEW, "gnu/trove/THashSet");
687: mv.visitInsn(DUP);
688: mv.visitIntInsn(BIPUSH, size);
689: mv.visitMethodInsn(INVOKESPECIAL, "gnu/trove/THashSet",
690: "<init>", "(I)V");
691: mv.visitVarInsn(ASTORE, 1);
692:
693: if (!cs.isDirectSubClassOfPhysicalMOState()) {
694: mv.visitVarInsn(ALOAD, 0);
695: mv.visitMethodInsn(INVOKESPECIAL, super ClassNameSlash,
696: "getObjectReferences", "()Ljava/util/Set;");
697: mv.visitVarInsn(ASTORE, 2);
698: mv.visitVarInsn(ALOAD, 1);
699: mv.visitVarInsn(ALOAD, 2);
700: mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set",
701: "addAll", "(Ljava/util/Collection;)Z");
702: mv.visitInsn(POP);
703: }
704:
705: for (Iterator i = referenceFields.iterator(); i.hasNext();) {
706: FieldType f = (FieldType) i.next();
707: mv.visitVarInsn(ALOAD, 0);
708: mv
709: .visitFieldInsn(GETFIELD, classNameSlash, f
710: .getLocalFieldName(), getFieldTypeDesc(f
711: .getType()));
712: mv.visitTypeInsn(INSTANCEOF, "com/tc/object/ObjectID");
713: Label l2 = new Label();
714: mv.visitJumpInsn(IFEQ, l2);
715: mv.visitVarInsn(ALOAD, 0);
716: mv
717: .visitFieldInsn(GETFIELD, classNameSlash, f
718: .getLocalFieldName(), getFieldTypeDesc(f
719: .getType()));
720: mv.visitTypeInsn(CHECKCAST, "com/tc/object/ObjectID");
721: mv.visitMethodInsn(INVOKEVIRTUAL, "com/tc/object/ObjectID",
722: "isNull", "()Z");
723: mv.visitJumpInsn(IFNE, l2);
724: mv.visitVarInsn(ALOAD, 1);
725: mv.visitVarInsn(ALOAD, 0);
726: mv
727: .visitFieldInsn(GETFIELD, classNameSlash, f
728: .getLocalFieldName(), getFieldTypeDesc(f
729: .getType()));
730: mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "add",
731: "(Ljava/lang/Object;)Z");
732: mv.visitInsn(POP);
733: mv.visitLabel(l2);
734: }
735: if (!parentID.isNull()
736: && cs.isDirectSubClassOfPhysicalMOState()) {
737: // add parentID too
738: mv.visitVarInsn(ALOAD, 1);
739: mv.visitVarInsn(ALOAD, 0);
740: mv.visitFieldInsn(GETFIELD, classNameSlash,
741: PARENT_ID_FIELD, "Lcom/tc/object/ObjectID;");
742: mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "add",
743: "(Ljava/lang/Object;)Z");
744: mv.visitInsn(POP);
745: }
746: mv.visitVarInsn(ALOAD, 1);
747: mv.visitInsn(ARETURN);
748: mv.visitMaxs(3, 3);
749: mv.visitEnd();
750: }
751:
752: private void createFields(ClassWriter cw, List fields) {
753: for (Iterator i = fields.iterator(); i.hasNext();) {
754: FieldType f = (FieldType) i.next();
755: FieldVisitor fv = cw.visitField(ACC_PRIVATE, f
756: .getLocalFieldName(),
757: getFieldTypeDesc(f.getType()), null, null);
758: fv.visitEnd();
759: }
760: }
761:
762: private String getFieldTypeDesc(int type) {
763: switch (type) {
764: case LiteralValues.INTEGER:
765: return "I";
766: case LiteralValues.LONG:
767: return "J";
768: case LiteralValues.CHARACTER:
769: return "C";
770: case LiteralValues.FLOAT:
771: return "F";
772: case LiteralValues.DOUBLE:
773: return "D";
774: case LiteralValues.BYTE:
775: return "B";
776: case LiteralValues.SHORT:
777: return "S";
778: case LiteralValues.BOOLEAN:
779: return "Z";
780: case LiteralValues.OBJECT_ID:
781: return "Ljava/lang/Object;"; // ObjectIDs are NOT stored as longs anymore :(
782: default:
783: return "Ljava/lang/Object;"; // Everything else is stored as Object reference
784: }
785: }
786:
787: // TODO:: Clean up to use MethodDetail class
788: private String getMethodNameForPrimitives(int type) {
789: switch (type) {
790: case LiteralValues.INTEGER:
791: return "intValue";
792: case LiteralValues.LONG:
793: return "longValue";
794: case LiteralValues.CHARACTER:
795: return "charValue";
796: case LiteralValues.FLOAT:
797: return "floatValue";
798: case LiteralValues.DOUBLE:
799: return "doubleValue";
800: case LiteralValues.BYTE:
801: return "byteValue";
802: case LiteralValues.SHORT:
803: return "shortValue";
804: case LiteralValues.BOOLEAN:
805: return "booleanValue";
806: // ObjectIDs are NOT stored as longs anymore :(
807: // case LiteralValues.OBJECT_ID:
808: // return "toLong";
809: default:
810: throw new AssertionError("This type is invalid : " + type);
811: }
812: }
813:
814: private String getClassNameFor(int type) {
815: switch (type) {
816: case LiteralValues.INTEGER:
817: return "java/lang/Integer";
818: case LiteralValues.LONG:
819: return "java/lang/Long";
820: case LiteralValues.CHARACTER:
821: return "java/lang/Character";
822: case LiteralValues.FLOAT:
823: return "java/lang/Float";
824: case LiteralValues.DOUBLE:
825: return "java/lang/Double";
826: case LiteralValues.BYTE:
827: return "java/lang/Byte";
828: case LiteralValues.SHORT:
829: return "java/lang/Short";
830: case LiteralValues.BOOLEAN:
831: return "java/lang/Boolean";
832: case LiteralValues.OBJECT_ID:
833: return "java/lang/Object"; // ObjectIDs are NOT stored as longs anymore :(
834: default:
835: return "java/lang/Object"; // Everything else is stored as Object reference
836: }
837: }
838:
839: private void createParentIDField(ClassWriter cw) {
840: FieldVisitor fv = cw.visitField(ACC_PRIVATE, PARENT_ID_FIELD,
841: "Lcom/tc/object/ObjectID;", null, null);
842: fv.visitEnd();
843: }
844:
845: // *************************************************************************************
846: // The Code generated by this method looks (kind of) this.
847: //
848: // ObjectID parentId;
849: // public ObjectID getParentID() {
850: // return parentId;
851: // }
852: // *************************************************************************************
853: private void createGetParentIDMethod(ClassWriter cw,
854: String classNameSlash) {
855: MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "getParentID",
856: "()Lcom/tc/object/ObjectID;", null, null);
857: mv.visitCode();
858: mv.visitVarInsn(ALOAD, 0);
859: mv.visitFieldInsn(GETFIELD, classNameSlash, PARENT_ID_FIELD,
860: "Lcom/tc/object/ObjectID;");
861: mv.visitInsn(ARETURN);
862: mv.visitMaxs(2, 1);
863: mv.visitEnd();
864: }
865:
866: // *************************************************************************************
867: // The Code generated by this method looks (kind of) this.
868: //
869: // ObjectID parentId;
870: // public void setParentID(ObjectID id) {
871: // parentId = id;
872: // }
873: // *************************************************************************************
874: private void createSetParentIDMethod(ClassWriter cw,
875: String classNameSlash) {
876: MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "setParentID",
877: "(Lcom/tc/object/ObjectID;)V", null, null);
878: mv.visitCode();
879: mv.visitVarInsn(ALOAD, 0);
880: mv.visitVarInsn(ALOAD, 1);
881: mv.visitFieldInsn(PUTFIELD, classNameSlash, PARENT_ID_FIELD,
882: "Lcom/tc/object/ObjectID;");
883: mv.visitInsn(RETURN);
884: mv.visitMaxs(3, 2);
885: mv.visitEnd();
886: }
887:
888: private void createConstructor(ClassWriter cw,
889: String super ClassNameSlash) {
890: MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V",
891: null, null);
892: mv.visitCode();
893: mv.visitVarInsn(ALOAD, 0);
894: mv.visitMethodInsn(INVOKESPECIAL, super ClassNameSlash,
895: "<init>", "()V");
896: mv.visitInsn(RETURN);
897: mv.visitMaxs(2, 2);
898: mv.visitEnd();
899: }
900:
901: }
|