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.FieldVisitor;
010: import com.tc.asm.MethodAdapter;
011: import com.tc.asm.MethodVisitor;
012: import com.tc.asm.Opcodes;
013: import com.tc.asm.commons.AdviceAdapter;
014: import com.tc.asm.tree.ClassNode;
015: import com.tc.asm.tree.FieldNode;
016: import com.tc.asm.tree.InnerClassNode;
017: import com.tc.asm.tree.MethodNode;
018:
019: import java.lang.reflect.Modifier;
020: import java.util.ArrayList;
021: import java.util.HashSet;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Map;
025: import java.util.Set;
026:
027: public class MergeTCToJavaClassAdapter extends
028: ChangeClassNameHierarchyAdapter implements Opcodes {
029: private static final String TC_INIT = ByteCodeUtil.TC_METHOD_PREFIX
030: + "$$_init_$$";
031:
032: private final List jInnerClassNames = new ArrayList();
033: private final ClassNode tcClassNode;
034: private final String jFullClassSlashes;
035: private final String tcFullClassSlashes;
036: private final Map instrumentedContext;
037: private final Set visitedMethods;
038: private final String methodPrefix;
039: private final boolean insertTCinit;
040: private String super Name;
041: private TransparencyClassAdapter dsoAdapter;
042:
043: public MergeTCToJavaClassAdapter(ClassVisitor cv,
044: TransparencyClassAdapter dsoAdapter, String jFullClassDots,
045: String tcFullClassDots, ClassNode tcClassNode,
046: Map instrumentedContext, String methodPrefix,
047: boolean insertTCinit) {
048: super (cv);
049: this .insertTCinit = insertTCinit;
050:
051: if (insertTCinit) {
052: createTCInit(tcClassNode);
053: }
054:
055: List jInnerClasses = tcClassNode.innerClasses;
056: for (Iterator i = jInnerClasses.iterator(); i.hasNext();) {
057: InnerClassNode jInnerClass = (InnerClassNode) i.next();
058: jInnerClassNames.add(jInnerClass.name);
059: }
060:
061: this .tcClassNode = tcClassNode;
062:
063: this .jFullClassSlashes = jFullClassDots.replace(DOT_DELIMITER,
064: SLASH_DELIMITER);
065: this .tcFullClassSlashes = tcFullClassDots.replace(
066: DOT_DELIMITER, SLASH_DELIMITER);
067:
068: addNewContextIfNotExist(tcFullClassDots, jFullClassDots,
069: instrumentedContext);
070: this .instrumentedContext = instrumentedContext;
071: this .visitedMethods = new HashSet();
072: this .dsoAdapter = dsoAdapter;
073: this .methodPrefix = methodPrefix;
074: }
075:
076: private static void createTCInit(ClassNode tcClassNode) {
077: // For now, we only allow the "TC" class to contain 1 constructor at most.
078: // This constructor body will be woven into all of the constructors present
079: // in the original class
080:
081: List cstrs = new ArrayList();
082:
083: for (Iterator i = tcClassNode.methods.iterator(); i.hasNext();) {
084: MethodNode mn = (MethodNode) i.next();
085:
086: if (isInitMethod(mn.name)) {
087: cstrs.add(mn);
088: }
089: }
090:
091: if (cstrs.size() > 1) {
092: //
093: throw new IllegalArgumentException(tcClassNode.name
094: + " contains " + cstrs.size()
095: + " constructors, but only 1 is allowed");
096: }
097:
098: MethodNode cstr = (MethodNode) cstrs.get(0);
099:
100: if (!cstr.exceptions.isEmpty()) {
101: //
102: throw new IllegalArgumentException(
103: "constructor in TC class not allowed to throw checked exceptions: "
104: + cstr.exceptions);
105: }
106:
107: MethodNode processed = new MethodNode();
108: cstr.accept(new TransformConstructorAdapter(processed,
109: cstr.access, cstr.name, cstr.desc));
110:
111: cstr.instructions = processed.instructions;
112: cstr.access = ACC_PRIVATE | ACC_SYNTHETIC;
113: cstr.name = TC_INIT;
114: }
115:
116: public MergeTCToJavaClassAdapter(ClassVisitor cv,
117: TransparencyClassAdapter dsoAdapter, String jFullClassDots,
118: String tcFullClassDots, ClassNode tcClassNode,
119: Map instrumentedContext) {
120: this (cv, dsoAdapter, jFullClassDots, tcFullClassDots,
121: tcClassNode, instrumentedContext,
122: ByteCodeUtil.TC_METHOD_PREFIX, true);
123: }
124:
125: public void visit(int version, int access, String name,
126: String signature, String super Name, String[] interfaces) {
127: this .super Name = super Name;
128:
129: List tcInterfaces = tcClassNode.interfaces;
130: List jInterfaces = new ArrayList();
131: for (int i = 0; i < interfaces.length; i++) {
132: if (!tcInterfaces.contains(interfaces[i])) {
133: jInterfaces.add(interfaces[i]);
134: }
135: }
136: for (Iterator i = tcInterfaces.iterator(); i.hasNext();) {
137: String intf = (String) i.next();
138: jInterfaces.add(intf);
139: }
140: interfaces = new String[jInterfaces.size()];
141: jInterfaces.toArray(interfaces);
142: // super.visit(version, access, name, signature, superName, interfaces);
143: dsoAdapter.visit(version, access, name, signature, super Name,
144: interfaces);
145: }
146:
147: private String getNewName(String methodName) {
148: if (isInitMethod(methodName)) {
149: return methodName;
150: }
151: return this .methodPrefix + methodName;
152: }
153:
154: public MethodVisitor visitMethod(int access, String name,
155: String desc, String signature, String[] exceptions) {
156: String methodDesc = name + desc;
157: this .visitedMethods.add(methodDesc);
158:
159: if (!isInitMethod(name)) {
160: List tcMethods = tcClassNode.methods;
161: for (Iterator i = tcMethods.iterator(); i.hasNext();) {
162: MethodNode mNode = (MethodNode) i.next();
163: if (0 == (mNode.access & ACC_ABSTRACT)
164: && mNode.name.equals(name)
165: && mNode.desc.equals(desc)) {
166: mNode.signature = signature;
167: return super .visitMethod(ACC_PRIVATE,
168: getNewName(name), desc, signature,
169: exceptions);
170: }
171: }
172: }
173:
174: MethodVisitor mv = super .visitMethod(access, name, desc,
175: signature, exceptions);
176: if (LogicalClassSerializationAdapter.WRITE_OBJECT_SIGNATURE
177: .equals(methodDesc)
178: || LogicalClassSerializationAdapter.READ_OBJECT_SIGNATURE
179: .equals(methodDesc)) { //
180: return new LogicalClassSerializationAdapter.LogicalClassSerializationMethodAdapter(
181: mv, jFullClassSlashes);
182: }
183:
184: if (insertTCinit && isInitMethod(name)) {
185: mv = new AddTCInitCallAdapter(jFullClassSlashes, mv,
186: access, name, desc);
187: }
188:
189: return mv;
190: }
191:
192: public FieldVisitor visitField(int access, String name,
193: String desc, String signature, Object value) {
194: List tcFields = tcClassNode.fields;
195: for (Iterator i = tcFields.iterator(); i.hasNext();) {
196: FieldNode fieldNode = (FieldNode) i.next();
197: if (name.equals(fieldNode.name)
198: && desc.equals(fieldNode.desc)) {
199: i.remove();
200: break;
201: }
202: }
203:
204: // hack for now
205: if (("java/util/LinkedHashMap".equals(jFullClassSlashes) && "accessOrder"
206: .equals(name))
207: || ("java/util/concurrent/locks/ReentrantReadWriteLock"
208: .equals(jFullClassSlashes) && ("sync"
209: .equals(name)
210: || "readerLock".equals(name) || "writerLock"
211: .equals(name)))) {
212: access = ~Modifier.FINAL & access;
213: }
214:
215: return super .visitField(access, name, desc, signature, value);
216: }
217:
218: public void visitEnd() {
219: addTCFields();
220: addTCMethods();
221: addTCInnerClasses();
222: super .visitEnd();
223: }
224:
225: private void addTCMethods() {
226: List tcMethods = tcClassNode.methods;
227: for (Iterator i = tcMethods.iterator(); i.hasNext();) {
228: MethodNode mNode = (MethodNode) i.next();
229: if (((mNode.access & ACC_ABSTRACT) != 0)
230: || (isInitMethod(mNode.name) && (visitedMethods
231: .contains(mNode.name + mNode.desc)))) {
232: continue;
233: }
234: mNode.accept(new TCSuperClassAdapter(cv));
235: }
236: LogicalClassSerializationAdapter
237: .addCheckSerializationOverrideMethod(cv, false);
238: }
239:
240: private static boolean isInitMethod(String methodName) {
241: return "<init>".equals(methodName);
242: }
243:
244: private void addTCFields() {
245: List tcFields = tcClassNode.fields;
246: for (Iterator i = tcFields.iterator(); i.hasNext();) {
247: FieldNode fNode = (FieldNode) i.next();
248: fNode.accept(cv);
249: }
250: }
251:
252: private void addTCInnerClasses() {
253: List tcInnerClasses = tcClassNode.innerClasses;
254: for (Iterator i = tcInnerClasses.iterator(); i.hasNext();) {
255: InnerClassNode innerClass = (InnerClassNode) i.next();
256: if (!tcInnerClassExistInJavaClass(innerClass)) {
257: innerClass.accept(new TCSuperClassAdapter(cv));
258: }
259: }
260: }
261:
262: private boolean tcInnerClassExistInJavaClass(
263: InnerClassNode tcInnerClass) {
264: return jInnerClassNames.contains(tcInnerClass.name);
265: }
266:
267: private class TCSuperClassAdapter extends ClassAdapter implements
268: Opcodes {
269: public TCSuperClassAdapter(ClassVisitor cv) {
270: super (cv);
271: }
272:
273: public void visit(int version, int access, String name,
274: String signature, String super Name, String[] interfaces) {
275: name = replaceClassName(name);
276: super Name = replaceClassName(super Name);
277: super .visit(version, access & ~ACC_ABSTRACT, name,
278: signature, super Name, interfaces);
279: }
280:
281: public MethodVisitor visitMethod(int access, String name,
282: String desc, String signature, String[] exceptions) {
283: desc = replaceClassName(desc);
284: signature = replaceClassName(signature);
285: // return new TCSuperMethodAdapter(super.visitMethod(access, name, desc, signature, exceptions));
286: return new TCSuperMethodAdapter(dsoAdapter
287: .basicVisitMethod(access, name, desc, signature,
288: exceptions));
289: }
290:
291: private String replaceClassName(String classNameDots) {
292: return replaceClassName(classNameDots, tcFullClassSlashes,
293: jFullClassSlashes);
294: }
295:
296: private String replaceClassName(String classNameDots,
297: String srcClassNameDots, String targetClassNameDots) {
298: if (classNameDots == null || classNameDots.length() == 0) {
299: return classNameDots;
300: }
301:
302: classNameDots = classNameDots.replace(DOT_DELIMITER,
303: SLASH_DELIMITER);
304: srcClassNameDots = srcClassNameDots.replace(DOT_DELIMITER,
305: SLASH_DELIMITER);
306: targetClassNameDots = targetClassNameDots.replace(
307: DOT_DELIMITER, SLASH_DELIMITER);
308:
309: int index = classNameDots.indexOf(srcClassNameDots);
310: if (index == -1) {
311: return classNameDots;
312: }
313:
314: StringBuffer newClassName = new StringBuffer();
315: while (index != -1) {
316: if (index > 0) {
317: newClassName.append(classNameDots.substring(0,
318: index));
319: }
320: newClassName.append(targetClassNameDots);
321: classNameDots = classNameDots.substring(index
322: + srcClassNameDots.length());
323: index = classNameDots.indexOf(srcClassNameDots);
324: }
325: newClassName.append(classNameDots);
326: return newClassName.toString();
327: }
328:
329: private class TCSuperMethodAdapter extends MethodAdapter
330: implements Opcodes {
331: public TCSuperMethodAdapter(MethodVisitor mv) {
332: super (mv);
333: }
334:
335: public void visitMethodInsn(int opcode, String owner,
336: String name, String desc) {
337: int index = owner.indexOf(tcFullClassSlashes);
338: if (opcode == INVOKESPECIAL && index == -1) {
339: if (!visitedMethods.contains(name + desc)
340: && !isInitMethod(name)) {
341: owner = super Name;
342: } else {
343: ChangeContext context = (ChangeContext) instrumentedContext
344: .get(owner);
345: if (context != null) {
346: owner = context.convertedClassNameSlashes;
347: }
348: name = getNewName(name);
349: }
350: super .visitMethodInsn(opcode, owner, name, desc);
351: } else {
352: owner = replaceClassName(owner);
353: desc = replaceClassName(desc);
354: super .visitMethodInsn(opcode, owner, name, desc);
355: }
356: }
357:
358: public void visitFieldInsn(int opcode, String owner,
359: String name, String desc) {
360: owner = replaceClassName(owner);
361: desc = replaceClassName(desc);
362: super .visitFieldInsn(opcode, owner, name, desc);
363: }
364:
365: public void visitTypeInsn(int opcode, String desc) {
366: ChangeContext context = (ChangeContext) instrumentedContext
367: .get(desc);
368: if (context != null) {
369: desc = context.convertedClassNameSlashes;
370: } else {
371: desc = replaceClassName(desc);
372: }
373: super .visitTypeInsn(opcode, desc);
374: }
375:
376: }
377: }
378:
379: private static class TransformConstructorAdapter extends
380: AdviceAdapter {
381: private final MethodNode target;
382:
383: public TransformConstructorAdapter(MethodNode mv, int access,
384: String name, String desc) {
385: super (mv, access, name, desc);
386: this .target = mv;
387: }
388:
389: protected void onMethodEnter() {
390: target.instructions.clear();
391: }
392:
393: protected void onMethodExit(int opcode) {
394: //
395: }
396: }
397:
398: private static class AddTCInitCallAdapter extends AdviceAdapter
399: implements Opcodes {
400:
401: private final String owner;
402:
403: public AddTCInitCallAdapter(String owner, MethodVisitor mv,
404: int access, String name, String desc) {
405: super (mv, access, name, desc);
406: this .owner = owner;
407: }
408:
409: protected void onMethodEnter() {
410: //
411: }
412:
413: protected void onMethodExit(int opcode) {
414: if (RETURN == opcode) {
415: super .visitVarInsn(ALOAD, 0);
416: super .visitMethodInsn(INVOKESPECIAL, owner, TC_INIT,
417: "()V");
418: }
419: }
420:
421: }
422:
423: }
|