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.object.bytecode;
006:
007: import com.tc.asm.ClassAdapter;
008: import com.tc.asm.ClassVisitor;
009: import com.tc.asm.Label;
010: import com.tc.asm.MethodAdapter;
011: import com.tc.asm.MethodVisitor;
012: import com.tc.asm.Opcodes;
013: import com.tc.util.runtime.VmVersion;
014:
015: public class StringBufferAdapter extends ClassAdapter implements
016: Opcodes {
017:
018: private final VmVersion version;
019:
020: public StringBufferAdapter(ClassVisitor cv, VmVersion version) {
021: super (cv);
022: this .version = version;
023: }
024:
025: public MethodVisitor visitMethod(int access, String name,
026: String desc, String signature, String[] exceptions) {
027: MethodVisitor mv = super .visitMethod(access, name, desc,
028: signature, exceptions);
029: if ("toString".equals(name)
030: && "()Ljava/lang/String;".equals(desc)
031: && version.isJDK14()) {
032: rewriteToString1_4(mv);
033: return null;
034: }
035:
036: return mv;
037: }
038:
039: private void rewriteToString1_4(MethodVisitor mv) {
040: // This code is from the String(StringBuffer) constructor. It's easier to do the autolocking here, instead of in
041: // String -- otherwise the code is exactly the same
042: mv.visitCode();
043: Label l0 = new Label();
044: mv.visitLabel(l0);
045: mv.visitLineNumber(12, l0);
046: mv.visitVarInsn(ALOAD, 0);
047: mv.visitInsn(DUP);
048: mv.visitVarInsn(ASTORE, 1);
049: mv.visitInsn(MONITORENTER);
050: Label l1 = new Label();
051: mv.visitLabel(l1);
052: mv.visitLineNumber(13, l1);
053: mv.visitVarInsn(ALOAD, 0);
054: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer",
055: "setShared", "()V");
056: Label l2 = new Label();
057: mv.visitLabel(l2);
058: mv.visitLineNumber(14, l2);
059: mv.visitTypeInsn(NEW, "java/lang/String");
060: mv.visitInsn(DUP);
061: mv.visitInsn(ICONST_0);
062: mv.visitVarInsn(ALOAD, 0);
063: mv.visitFieldInsn(GETFIELD, "java/lang/StringBuffer", "count",
064: "I");
065: mv.visitVarInsn(ALOAD, 0);
066: mv.visitFieldInsn(GETFIELD, "java/lang/StringBuffer", "value",
067: "[C");
068: mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>",
069: "(II[C)V");
070: mv.visitVarInsn(ALOAD, 1);
071: mv.visitInsn(MONITOREXIT);
072: Label l3 = new Label();
073: mv.visitLabel(l3);
074: mv.visitInsn(ARETURN);
075: Label l4 = new Label();
076: mv.visitLabel(l4);
077: mv.visitLineNumber(12, l4);
078: mv.visitVarInsn(ALOAD, 1);
079: mv.visitInsn(MONITOREXIT);
080: Label l5 = new Label();
081: mv.visitLabel(l5);
082: mv.visitInsn(ATHROW);
083: Label l6 = new Label();
084: mv.visitLabel(l6);
085: mv.visitTryCatchBlock(l1, l3, l4, null);
086: mv.visitTryCatchBlock(l4, l5, l4, null);
087: mv.visitMaxs(0, 0);
088: mv.visitEnd();
089: }
090:
091: public static class FixUp extends ClassAdapter {
092:
093: private static final String MANAGED_APPEND = DuplicateMethodAdapter.MANAGED_PREFIX
094: + "append";
095: private final VmVersion version;
096:
097: public FixUp(ClassVisitor cv, VmVersion version) {
098: super (cv);
099: this .version = version;
100: }
101:
102: public MethodVisitor visitMethod(int access, String name,
103: String desc, String signature, String[] exceptions) {
104: MethodVisitor visitor = super .visitMethod(access, name,
105: desc, signature, exceptions);
106:
107: if (MANAGED_APPEND.equals(name)
108: && "([CII)Ljava/lang/StringBuffer;".equals(desc)
109: && version.isJDK14()) {
110: // comment to make formatter sane
111: return new CloneCharArrayFixup(visitor);
112: }
113:
114: return visitor;
115: }
116:
117: // Because the char[] argument to StringBuffer.append([CII) is potentially a threadlocal (see Long.appendTo(long,
118: // StringBuffer) in JDK1.4), we need to make a defensive copy before calling anymore code that can modify it before
119: // it gets appended into this StringBuffer. Specifically, our code to get the DSO monitor was corrupting the char[]
120: // from the ThreadLocal
121: private static class CloneCharArrayFixup extends MethodAdapter {
122: public CloneCharArrayFixup(MethodVisitor mv) {
123: super (mv);
124: mv.visitVarInsn(ALOAD, 1);
125: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object",
126: "clone", "()Ljava/lang/Object;");
127: mv.visitTypeInsn(CHECKCAST, "[C");
128: mv.visitVarInsn(ASTORE, 1);
129: }
130: }
131: }
132:
133: }
|