// System.err.println("DEBUG: Transforming synchronized method: "+
// synchronizedMethod.getName());
final InstructionFactory fact = synchronizedMethod.getEnclosingClass().getFactory();
InstructionList body = synchronizedMethod.getBody();
InstructionList prepend = new InstructionList();
Type enclosingClassType = BcelWorld.makeBcelType(synchronizedMethod.getEnclosingClass().getType());
// STATIC METHOD TRANSFORMATION
if (synchronizedMethod.isStatic()) {
// What to do here depends on the level of the class file!
// LDC can handle class literals in Java5 and above *sigh*
if (synchronizedMethod.getEnclosingClass().isAtLeastJava5()) {
// MONITORENTER logic:
// 0: ldc #2; //class C
// 2: dup
// 3: astore_0
// 4: monitorenter
int slotForLockObject = synchronizedMethod.allocateLocal(enclosingClassType);
prepend.append(fact.createConstant(enclosingClassType));
prepend.append(InstructionFactory.createDup(1));
prepend.append(InstructionFactory.createStore(enclosingClassType, slotForLockObject));
prepend.append(InstructionFactory.MONITORENTER);
// MONITOREXIT logic:
// We basically need to wrap the code from the method in a
// finally block that
// will ensure monitorexit is called. Content on the finally
// block seems to
// be always:
//
// E1: ALOAD_1
// MONITOREXIT
// ATHROW
//
// so lets build that:
InstructionList finallyBlock = new InstructionList();
finallyBlock.append(InstructionFactory.createLoad(Type.getType(java.lang.Class.class), slotForLockObject));
finallyBlock.append(InstructionConstants.MONITOREXIT);
finallyBlock.append(InstructionConstants.ATHROW);
// finally -> E1
// | GETSTATIC java.lang.System.out Ljava/io/PrintStream; (line
// 21)
// | LDC "hello"
// | INVOKEVIRTUAL java.io.PrintStream.println
// (Ljava/lang/String;)V
// | ALOAD_1 (line 20)
// | MONITOREXIT
// finally -> E1
// GOTO L0
// finally -> E1
// | E1: ALOAD_1
// | MONITOREXIT
// finally -> E1
// ATHROW
// L0: RETURN (line 23)
// search for 'returns' and make them jump to the
// aload_<n>,monitorexit
InstructionHandle walker = body.getStart();
List<InstructionHandle> rets = new ArrayList<InstructionHandle>();
while (walker != null) {
if (walker.getInstruction().isReturnInstruction()) {
rets.add(walker);
}
walker = walker.getNext();
}
if (!rets.isEmpty()) {
// need to ensure targeters for 'return' now instead target
// the load instruction
// (so we never jump over the monitorexit logic)
for (Iterator<InstructionHandle> iter = rets.iterator(); iter.hasNext();) {
InstructionHandle element = iter.next();
InstructionList monitorExitBlock = new InstructionList();
monitorExitBlock.append(InstructionFactory.createLoad(enclosingClassType, slotForLockObject));
monitorExitBlock.append(InstructionConstants.MONITOREXIT);
// monitorExitBlock.append(Utility.copyInstruction(element
// .getInstruction()));
// element.setInstruction(InstructionFactory.createLoad(
// classType,slotForThis));
InstructionHandle monitorExitBlockStart = body.insert(element, monitorExitBlock);
// now move the targeters from the RET to the start of
// the monitorexit block
for (InstructionTargeter targeter : element.getTargetersCopy()) {
// what kinds are there?
if (targeter instanceof LocalVariableTag) {
// ignore
} else if (targeter instanceof LineNumberTag) {
// ignore
// } else if (targeter instanceof
// InstructionBranch &&
// ((InstructionBranch)targeter).isGoto()) {
// // move it...
// targeter.updateTarget(element,
// monitorExitBlockStart);
} else if (targeter instanceof InstructionBranch) {
// move it
targeter.updateTarget(element, monitorExitBlockStart);
} else {
throw new BCException("Unexpected targeter encountered during transform: " + targeter);
}
}
}
}
// now the magic, putting the finally block around the code
InstructionHandle finallyStart = finallyBlock.getStart();
InstructionHandle tryPosition = body.getStart();
InstructionHandle catchPosition = body.getEnd();
body.insert(body.getStart(), prepend); // now we can put the
// monitorenter stuff on
synchronizedMethod.getBody().append(finallyBlock);
synchronizedMethod.addExceptionHandler(tryPosition, catchPosition, finallyStart, null/* ==finally */, false);
synchronizedMethod.addExceptionHandler(finallyStart, finallyStart.getNext(), finallyStart, null, false);
} else {
// TRANSFORMING STATIC METHOD ON PRE JAVA5
// Hideous nightmare, class literal references prior to Java5
// YIKES! this is just the code for MONITORENTER !
// 0: getstatic #59; //Field class$1:Ljava/lang/Class;
// 3: dup
// 4: ifnonnull 32
// 7: pop
// try
// 8: ldc #61; //String java.lang.String
// 10: invokestatic #44; //Method
// java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
// 13: dup
// catch
// 14: putstatic #59; //Field class$1:Ljava/lang/Class;
// 17: goto 32
// 20: new #46; //class java/lang/NoClassDefFoundError
// 23: dup_x1
// 24: swap
// 25: invokevirtual #52; //Method
// java/lang/Throwable.getMessage:()Ljava/lang/String;
// 28: invokespecial #54; //Method
// java/lang/NoClassDefFoundError."<init>":(Ljava/lang/String;)V
// 31: athrow
// 32: dup <-- partTwo (branch target)
// 33: astore_0
// 34: monitorenter
//
// plus exceptiontable entry!
// 8 13 20 Class java/lang/ClassNotFoundException
Type classType = BcelWorld.makeBcelType(synchronizedMethod.getEnclosingClass().getType());
Type clazzType = Type.getType(Class.class);
InstructionList parttwo = new InstructionList();
parttwo.append(InstructionFactory.createDup(1));
int slotForThis = synchronizedMethod.allocateLocal(classType);
parttwo.append(InstructionFactory.createStore(clazzType, slotForThis)); // ? should be the real type ? String or
// something?
parttwo.append(InstructionFactory.MONITORENTER);
String fieldname = synchronizedMethod.getEnclosingClass().allocateField("class$");
FieldGen f = new FieldGen(Modifier.STATIC | Modifier.PRIVATE, Type.getType(Class.class), fieldname,
synchronizedMethod.getEnclosingClass().getConstantPool());
synchronizedMethod.getEnclosingClass().addField(f, null);
// 10: invokestatic #44; //Method
// java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
// 13: dup
// 14: putstatic #59; //Field class$1:Ljava/lang/Class;
// 17: goto 32
// 20: new #46; //class java/lang/NoClassDefFoundError
// 23: dup_x1
// 24: swap
// 25: invokevirtual #52; //Method
// java/lang/Throwable.getMessage:()Ljava/lang/String;
// 28: invokespecial #54; //Method
// java/lang/NoClassDefFoundError."<init>":(Ljava/lang/String;)V
// 31: athrow
String name = synchronizedMethod.getEnclosingClass().getName();
prepend.append(fact.createGetStatic(name, fieldname, Type.getType(Class.class)));
prepend.append(InstructionFactory.createDup(1));
prepend.append(InstructionFactory.createBranchInstruction(Constants.IFNONNULL, parttwo.getStart()));
prepend.append(InstructionFactory.POP);
prepend.append(fact.createConstant(name));
InstructionHandle tryInstruction = prepend.getEnd();
prepend.append(fact.createInvoke("java.lang.Class", "forName", clazzType,
new Type[] { Type.getType(String.class) }, Constants.INVOKESTATIC));
InstructionHandle catchInstruction = prepend.getEnd();
prepend.append(InstructionFactory.createDup(1));
prepend.append(fact.createPutStatic(synchronizedMethod.getEnclosingClass().getType().getName(), fieldname,
Type.getType(Class.class)));
prepend.append(InstructionFactory.createBranchInstruction(Constants.GOTO, parttwo.getStart()));
// start of catch block
InstructionList catchBlockForLiteralLoadingFail = new InstructionList();
catchBlockForLiteralLoadingFail.append(fact.createNew((ObjectType) Type.getType(NoClassDefFoundError.class)));
catchBlockForLiteralLoadingFail.append(InstructionFactory.createDup_1(1));
catchBlockForLiteralLoadingFail.append(InstructionFactory.SWAP);
catchBlockForLiteralLoadingFail.append(fact.createInvoke("java.lang.Throwable", "getMessage",
Type.getType(String.class), new Type[] {}, Constants.INVOKEVIRTUAL));
catchBlockForLiteralLoadingFail.append(fact.createInvoke("java.lang.NoClassDefFoundError", "<init>", Type.VOID,
new Type[] { Type.getType(String.class) }, Constants.INVOKESPECIAL));
catchBlockForLiteralLoadingFail.append(InstructionFactory.ATHROW);
InstructionHandle catchBlockStart = catchBlockForLiteralLoadingFail.getStart();
prepend.append(catchBlockForLiteralLoadingFail);
prepend.append(parttwo);
// MONITORENTER
// pseudocode: load up 'this' (var0), dup it, store it in a new
// local var (for use with monitorexit) and call
// monitorenter:
// ALOAD_0, DUP, ASTORE_<n>, MONITORENTER
// prepend.append(InstructionFactory.createLoad(classType,0));
// prepend.append(InstructionFactory.createDup(1));
// int slotForThis =
// synchronizedMethod.allocateLocal(classType);
// prepend.append(InstructionFactory.createStore(classType,
// slotForThis));
// prepend.append(InstructionFactory.MONITORENTER);
// MONITOREXIT
// here be dragons
// We basically need to wrap the code from the method in a
// finally block that
// will ensure monitorexit is called. Content on the finally
// block seems to
// be always:
//
// E1: ALOAD_1
// MONITOREXIT
// ATHROW
//
// so lets build that:
InstructionList finallyBlock = new InstructionList();
finallyBlock.append(InstructionFactory.createLoad(Type.getType(java.lang.Class.class), slotForThis));
finallyBlock.append(InstructionConstants.MONITOREXIT);
finallyBlock.append(InstructionConstants.ATHROW);
// finally -> E1
// | GETSTATIC java.lang.System.out Ljava/io/PrintStream; (line
// 21)
// | LDC "hello"
// | INVOKEVIRTUAL java.io.PrintStream.println
// (Ljava/lang/String;)V
// | ALOAD_1 (line 20)
// | MONITOREXIT
// finally -> E1
// GOTO L0
// finally -> E1
// | E1: ALOAD_1
// | MONITOREXIT
// finally -> E1
// ATHROW
// L0: RETURN (line 23)
// frameEnv.put(donorFramePos, thisSlot);
// search for 'returns' and make them to the
// aload_<n>,monitorexit
InstructionHandle walker = body.getStart();
List<InstructionHandle> rets = new ArrayList<InstructionHandle>();
while (walker != null) { // !walker.equals(body.getEnd())) {
if (walker.getInstruction().isReturnInstruction()) {
rets.add(walker);
}
walker = walker.getNext();
}
if (rets.size() > 0) {
// need to ensure targeters for 'return' now instead target
// the load instruction
// (so we never jump over the monitorexit logic)
for (InstructionHandle ret : rets) {
// System.err.println("Adding monitor exit block at "+
// element);
InstructionList monitorExitBlock = new InstructionList();
monitorExitBlock.append(InstructionFactory.createLoad(classType, slotForThis));
monitorExitBlock.append(InstructionConstants.MONITOREXIT);
// monitorExitBlock.append(Utility.copyInstruction(element
// .getInstruction()));
// element.setInstruction(InstructionFactory.createLoad(
// classType,slotForThis));
InstructionHandle monitorExitBlockStart = body.insert(ret, monitorExitBlock);
// now move the targeters from the RET to the start of
// the monitorexit block
for (InstructionTargeter targeter : ret.getTargetersCopy()) {
// what kinds are there?
if (targeter instanceof LocalVariableTag) {
// ignore
} else if (targeter instanceof LineNumberTag) {
// ignore
// } else if (targeter instanceof GOTO ||
// targeter instanceof GOTO_W) {
// // move it...
// targeter.updateTarget(element,
// monitorExitBlockStart);
} else if (targeter instanceof InstructionBranch) {
// move it
targeter.updateTarget(ret, monitorExitBlockStart);
} else {
throw new BCException("Unexpected targeter encountered during transform: " + targeter);
}
}
}
}
// body =
// rewriteWithMonitorExitCalls(body,fact,true,slotForThis,
// classType);
// synchronizedMethod.setBody(body);
// now the magic, putting the finally block around the code
InstructionHandle finallyStart = finallyBlock.getStart();
InstructionHandle tryPosition = body.getStart();
InstructionHandle catchPosition = body.getEnd();
body.insert(body.getStart(), prepend); // now we can put the
// monitorenter stuff on
synchronizedMethod.getBody().append(finallyBlock);
synchronizedMethod.addExceptionHandler(tryPosition, catchPosition, finallyStart, null/* ==finally */, false);
synchronizedMethod.addExceptionHandler(tryInstruction, catchInstruction, catchBlockStart,
(ObjectType) Type.getType(ClassNotFoundException.class), true);
synchronizedMethod.addExceptionHandler(finallyStart, finallyStart.getNext(), finallyStart, null, false);
}
} else {
// TRANSFORMING NON STATIC METHOD
Type classType = BcelWorld.makeBcelType(synchronizedMethod.getEnclosingClass().getType());
// MONITORENTER
// pseudocode: load up 'this' (var0), dup it, store it in a new
// local var (for use with monitorexit) and call
// monitorenter:
// ALOAD_0, DUP, ASTORE_<n>, MONITORENTER