001: /*
002: * All content copyright (c) 2003-2007 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc.object.bytecode;
005:
006: import com.tc.asm.Label;
007: import com.tc.asm.MethodVisitor;
008: import com.tc.asm.Opcodes;
009: import com.tc.asm.Type;
010: import com.tc.asm.commons.LocalVariablesSorter;
011:
012: class JavaUtilConcurrentHashMapLazyValuesMethodAdapter extends
013: LocalVariablesSorter implements Opcodes {
014: private boolean storeOnlyNonNull = false;
015:
016: public JavaUtilConcurrentHashMapLazyValuesMethodAdapter(
017: final int access, final String desc,
018: final MethodVisitor mv, final boolean storeOnlyNonNull) {
019: super (access, desc, mv);
020:
021: this .storeOnlyNonNull = storeOnlyNonNull;
022: }
023:
024: public void visitFieldInsn(int opcode, String owner, String name,
025: String desc) {
026: // Ensure that the hash entry values are looked up through the manager util
027: // when they're object IDs and not actual objects. This is synchronized through
028: // lock JVM locks on the hash entry instances to ensure that write access to the
029: // hash entry value doesn't happen in the middle, cause new values to be
030: // overwritten by old values that still had to be fetched and stored.
031: if (GETFIELD == opcode
032: && "java/util/concurrent/ConcurrentHashMap$HashEntry"
033: .equals(owner) && "value".equals(name)
034: && "Ljava/lang/Object;".equals(desc)) {
035:
036: Label labelDone = new Label();
037:
038: // store the hash entry in a local variable
039: int hashEntryLocal = newLocal(Type
040: .getObjectType("java/util/concurrent/ConcurrentHashMap$HashEntry"));
041: mv.visitVarInsn(ASTORE, hashEntryLocal);
042:
043: // setup try catch with finally to release the monitor on the hash entry that will be entered below
044: Label labelStartTryCatch = new Label();
045: Label labelEndTryCatch = new Label();
046: Label labelFinally = new Label();
047: mv.visitTryCatchBlock(labelStartTryCatch, labelEndTryCatch,
048: labelFinally, null);
049:
050: // enter a monitor on the hash entry
051: mv.visitVarInsn(ALOAD, hashEntryLocal);
052: mv.visitInsn(MONITORENTER);
053:
054: mv.visitLabel(labelStartTryCatch);
055:
056: // obtain the value of the entry
057: mv.visitVarInsn(ALOAD, hashEntryLocal);
058: super .visitFieldInsn(opcode, owner, name, desc);
059: int valueLocal = newLocal(Type
060: .getObjectType("java/lang/Object"));
061: mv.visitVarInsn(ASTORE, valueLocal);
062:
063: // check if the entry's value is a logical object ID
064: mv.visitVarInsn(ALOAD, valueLocal);
065: mv.visitTypeInsn(INSTANCEOF, "com/tc/object/ObjectID");
066: mv.visitJumpInsn(IFEQ, labelDone);
067:
068: // ensure that the type is verified by the VM
069: mv.visitVarInsn(ALOAD, valueLocal);
070: mv.visitTypeInsn(CHECKCAST, "com/tc/object/ObjectID");
071:
072: // lookup the real entry value from the object ID and store it back into the entry for caching
073: mv.visitMethodInsn(INVOKESTATIC,
074: "com/tc/object/bytecode/ManagerUtil",
075: "lookupObject",
076: "(Lcom/tc/object/ObjectID;)Ljava/lang/Object;");
077: mv.visitVarInsn(ASTORE, valueLocal);
078:
079: if (storeOnlyNonNull) {
080: // only store the value if it's not null, otherwise lock-less value retrieval could overwrite
081: // the object ID with a null value before the actual value is available
082: mv.visitVarInsn(ALOAD, valueLocal);
083: Label labelValueNull = new Label();
084: mv.visitJumpInsn(IFNULL, labelValueNull);
085: mv.visitVarInsn(ALOAD, hashEntryLocal);
086: mv.visitVarInsn(ALOAD, valueLocal);
087: mv
088: .visitMethodInsn(
089: INVOKEVIRTUAL,
090: "java/util/concurrent/ConcurrentHashMap$HashEntry",
091: TCMapEntry.TC_RAWSETVALUE_METHOD_NAME,
092: TCMapEntry.TC_RAWSETVALUE_METHOD_DESC);
093: mv.visitLabel(labelValueNull);
094: } else {
095: mv.visitVarInsn(ALOAD, hashEntryLocal);
096: mv.visitVarInsn(ALOAD, valueLocal);
097: mv
098: .visitMethodInsn(
099: INVOKEVIRTUAL,
100: "java/util/concurrent/ConcurrentHashMap$HashEntry",
101: TCMapEntry.TC_RAWSETVALUE_METHOD_NAME,
102: TCMapEntry.TC_RAWSETVALUE_METHOD_DESC);
103: }
104:
105: // load the entry's value from the local variable
106: mv.visitLabel(labelDone);
107: mv.visitVarInsn(ALOAD, valueLocal);
108:
109: // exit the monitor on the hash entry
110: mv.visitVarInsn(ALOAD, hashEntryLocal);
111: mv.visitInsn(MONITOREXIT);
112: mv.visitLabel(labelEndTryCatch);
113: Label labelAfterFinally = new Label();
114: mv.visitJumpInsn(GOTO, labelAfterFinally);
115: mv.visitLabel(labelFinally);
116: mv.visitVarInsn(ALOAD, hashEntryLocal);
117: mv.visitInsn(MONITOREXIT);
118: // re-throw exception
119: mv.visitInsn(ATHROW);
120:
121: mv.visitLabel(labelAfterFinally);
122: }
123: // Ensure that a modification to the value field of a hash entry is synchronized.
124: // Note that this is local JVM synchronization and that this is sufficient.
125: // We only want to ensure that a new value / object ID isn't written during
126: // the process of fetching and storing the real hash entry value since this could
127: // cause new values to be overwritten by old ones.
128: else if (PUTFIELD == opcode
129: && "java/util/concurrent/ConcurrentHashMap$HashEntry"
130: .equals(owner) && "value".equals(name)
131: && "Ljava/lang/Object;".equals(desc)) {
132:
133: // store the value in a local variable
134: int valueLocal = newLocal(Type
135: .getObjectType("java/lang/Object"));
136: mv.visitVarInsn(ASTORE, valueLocal);
137:
138: // store the hash entry in a local variable
139: int hashEntryLocal = newLocal(Type
140: .getObjectType("java/util/concurrent/ConcurrentHashMap$HashEntry"));
141: mv.visitVarInsn(ASTORE, hashEntryLocal);
142:
143: // setup try catch with finally to release the monitor on the hash entry that will be entered below
144: Label labelStartTryCatch = new Label();
145: Label labelEndTryCatch = new Label();
146: Label labelFinally = new Label();
147: mv.visitTryCatchBlock(labelStartTryCatch, labelEndTryCatch,
148: labelFinally, null);
149:
150: // enter a monitor on the hash entry
151: mv.visitVarInsn(ALOAD, hashEntryLocal);
152: mv.visitInsn(MONITORENTER);
153:
154: mv.visitLabel(labelStartTryCatch);
155:
156: // obtain the value of the entry
157: mv.visitVarInsn(ALOAD, hashEntryLocal);
158: mv.visitVarInsn(ALOAD, valueLocal);
159: super .visitFieldInsn(opcode, owner, name, desc);
160:
161: // exit the monitor on the hash entry
162: mv.visitVarInsn(ALOAD, hashEntryLocal);
163: mv.visitInsn(MONITOREXIT);
164: mv.visitLabel(labelEndTryCatch);
165: Label labelAfterFinally = new Label();
166: mv.visitJumpInsn(GOTO, labelAfterFinally);
167: mv.visitLabel(labelFinally);
168: mv.visitVarInsn(ALOAD, hashEntryLocal);
169: mv.visitInsn(MONITOREXIT);
170: // re-throw exception
171: mv.visitInsn(ATHROW);
172:
173: mv.visitLabel(labelAfterFinally);
174: } else {
175: super.visitFieldInsn(opcode, owner, name, desc);
176: }
177: }
178: }
|