/* This file is part of JOP, the Java Optimized Processor see <http://www.jopdesign.com/> Copyright (C) 2009, Peter Hilber (peter@hilber.name) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.jopdesign.build; import com.jopdesign.common.bcel.AnnotationAttribute; import com.jopdesign.sys.Const; import org.apache.bcel.Constants; import org.apache.bcel.classfile.Attribute; import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.classfile.Method; import org.apache.bcel.generic.ArrayType; import org.apache.bcel.generic.BranchInstruction; import org.apache.bcel.generic.ConstantPoolGen; import org.apache.bcel.generic.DSTORE; import org.apache.bcel.generic.IINC; import org.apache.bcel.generic.IndexedInstruction; import org.apache.bcel.generic.Instruction; import org.apache.bcel.generic.InstructionConstants; import org.apache.bcel.generic.InstructionFactory; import org.apache.bcel.generic.InstructionHandle; import org.apache.bcel.generic.InstructionList; import org.apache.bcel.generic.InstructionTargeter; import org.apache.bcel.generic.LSTORE; import org.apache.bcel.generic.MethodGen; import org.apache.bcel.generic.ObjectType; import org.apache.bcel.generic.PUSH; import org.apache.bcel.generic.StoreInstruction; import org.apache.bcel.generic.TargetLostException; import org.apache.bcel.generic.Type; import org.apache.bcel.util.InstructionFinder; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; /** * Known limitations: * - can't load string constants because they are already indexed in jz.load() * - return type double and long not supported * * TODO: check method signature * * @author Peter Hilber (peter@hilber.name) * */ public class ReplaceAtomicAnnotation extends JOPizerVisitor { public ReplaceAtomicAnnotation(OldAppInfo jz) { super(jz); } public void visitJavaClass(JavaClass clazz) { super.visitJavaClass(clazz); Method[] methods = clazz.getMethods(); for(int i = 0; i < methods.length; i++) { for (Attribute a: methods[i].getAttributes()) { if (a instanceof AnnotationAttribute) { if (((AnnotationAttribute)a).hasAtomicAnnotation()) { ConstantPoolGen cpoolgen = new ConstantPoolGen(clazz.getConstantPool()); Method nm = transform(methods[i], clazz, cpoolgen); OldMethodInfo mi = getCli().getMethodInfo(nm.getName()+nm.getSignature()); // set new method also in MethodInfo mi.setMethod(nm); methods[i] = nm; clazz.setConstantPool(cpoolgen.getFinalConstantPool()); System.out.println( "RTTM: transformed atomic method " + clazz.getClassName() + "." + nm.getName() + nm.getSignature()); } } } } } protected static int getArgsCount(MethodGen method) { int max = method.isStatic() ? 0 : 1; Type[] arg_types = method.getArgumentTypes(); if (arg_types != null) { for (int i = 0; i < arg_types.length; i++) { max += arg_types[i].getSize(); } } return max; } protected static SortedSet<Integer> getModifiedArguments(MethodGen method) { SortedSet<Integer> result = new TreeSet<Integer>(); int arguments = getArgsCount(method); /* * local variables are modified only by the bytecodes * astore, astore_<n>, dstore, dstore_<n>, fstore, fstore_<n>, iinc, * istore, istore_<n>, lstore, lstore_<n> */ for (Instruction in: method.getInstructionList().getInstructions()) { if (in instanceof IndexedInstruction) { IndexedInstruction i = (IndexedInstruction)in; if (i.getIndex() < arguments) { if (i instanceof DSTORE || i instanceof LSTORE) { result.add(i.getIndex()); result.add(i.getIndex()+1); } else if (i instanceof StoreInstruction || i instanceof IINC) { result.add(i.getIndex()); } } } } return result; } public static Method transform(Method m, JavaClass clazz, ConstantPoolGen _cp) { MethodGen method = new MethodGen(m, clazz.getClassName(), _cp); InstructionList oldIl = method.getInstructionList(); Type returnType = m.getReturnType(); if (returnType.equals(Type.LONG) || returnType.equals(Type.DOUBLE)) { throw new UnsupportedOperationException(); } final int transactionLocals = 2; /* * local variable indices: * isNotNestedTransaction is -2+2 * result is -2+3 * Throwable e is -2+3 */ final int maxLocals = method.getMaxLocals(); final int transactionLocalsBaseIndex = maxLocals; final int copyBaseIndex = transactionLocalsBaseIndex + transactionLocals; SortedSet<Integer> modifiedArguments = getModifiedArguments(method); // maps modified arguments indices to copies Map<Integer, Integer> modifiedArgumentsCopies = new TreeMap<Integer, Integer>(); { int copyIndex = copyBaseIndex; for (Integer i: modifiedArguments) { System.out.println("RTTM: method " + method.getClassName() + "." + method.getName() + method.getSignature() + ": saving argument " + i + " to variable " + copyIndex); modifiedArgumentsCopies.put(i, copyIndex++); } } InstructionList il = new InstructionList(); InstructionFactory _factory = new InstructionFactory(_cp); method.setInstructionList(il); { // InstructionHandle ih_0 = il.append(new PUSH(_cp, -559038737)); // il.append(_factory.createStore(Type.INT, transactionLocalsBaseIndex-2+1)); InstructionHandle ih_3 = il.append(_factory.createFieldAccess("rttm.internal.Utils", "inTransaction", new ArrayType(Type.BOOLEAN, 1), Constants.GETSTATIC)); il.append(new PUSH(_cp, -122)); il.append(_factory.createInvoke("com.jopdesign.sys.Native", "rd", Type.INT, new Type[] { Type.INT }, Constants.INVOKESTATIC)); il.append(InstructionConstants.BALOAD); BranchInstruction ifne_12 = InstructionFactory.createBranchInstruction(Constants.IFNE, null); il.append(ifne_12); il.append(new PUSH(_cp, 1)); BranchInstruction goto_16 = InstructionFactory.createBranchInstruction(Constants.GOTO, null); il.append(goto_16); InstructionHandle ih_19 = il.append(new PUSH(_cp, 0)); InstructionHandle ih_20 = il.append(InstructionFactory.createStore(Type.INT, transactionLocalsBaseIndex-2+2)); InstructionHandle ih_21 = il.append(InstructionFactory.createLoad(Type.INT, transactionLocalsBaseIndex-2+2)); BranchInstruction ifeq_22 = InstructionFactory.createBranchInstruction(Constants.IFEQ, null); il.append(ifeq_22); // InstructionHandle ih_25 = il.append(_factory.createLoad(Type.INT, transactionLocalsBaseIndex-2+0)); // il.append(_factory.createStore(Type.INT, transactionLocalsBaseIndex-2+1)); { // only save arguments which might be modified for (int i: modifiedArguments) { il.append(InstructionFactory.createLoad(Type.INT, i)); il.append(InstructionFactory.createStore(Type.INT, modifiedArgumentsCopies.get(i))); } } InstructionHandle ih_27 = il.append(new PUSH(_cp, 0)); il.append(new PUSH(_cp, -128)); il.append(_factory.createInvoke("com.jopdesign.sys.Native", "wr", Type.VOID, new Type[] { Type.INT, Type.INT }, Constants.INVOKESTATIC)); InstructionHandle ih_33 = il.append(_factory.createFieldAccess("rttm.internal.Utils", "inTransaction", new ArrayType(Type.BOOLEAN, 1), Constants.GETSTATIC)); il.append(new PUSH(_cp, -122)); il.append(_factory.createInvoke("com.jopdesign.sys.Native", "rd", Type.INT, new Type[] { Type.INT }, Constants.INVOKESTATIC)); il.append(new PUSH(_cp, 1)); il.append(InstructionConstants.BASTORE); // transaction loop InstructionHandle ih_43 = il.append(InstructionFactory.createLoad(Type.INT, transactionLocalsBaseIndex-2+2)); BranchInstruction ifeq_44 = InstructionFactory.createBranchInstruction(Constants.IFEQ, null); il.append(ifeq_44); InstructionHandle ih_47 = il.append(new PUSH(_cp, 1)); il.append(new PUSH(_cp, Const.MEM_TM_MAGIC)); il.append(_factory.createInvoke("com.jopdesign.sys.Native", "wrMem", Type.VOID, new Type[] { Type.INT, Type.INT }, Constants.INVOKESTATIC)); // InstructionHandle ih_53 = il.append(_factory.createLoad(Type.INT, transactionLocalsBaseIndex-2+0)); // il.append(_factory.createInvoke("rttm.swtest.Transaction", "atomicSection", Type.INT, new Type[] { Type.INT }, Constants.INVOKESTATIC)); // il.append(_factory.createStore(Type.INT, transactionLocalsBaseIndex-2+3)); InstructionHandle ih_53 = oldIl.getStart(); Collection<BranchInstruction> gotos_transactionCommit = new ArrayList<BranchInstruction>(); { // redirect returns InstructionFinder f = new InstructionFinder(oldIl); String returnInstructionsPattern = "ARETURN|IRETURN|FRETURN|RETURN"; for (Iterator i = f.search(returnInstructionsPattern); i.hasNext(); ) { InstructionHandle oldIh = ((InstructionHandle[])i.next())[0]; InstructionList nl = new InstructionList(); if (!method.getReturnType().equals(Type.VOID)) { nl.append(InstructionFactory.createStore( method.getReturnType(), transactionLocalsBaseIndex-2+3)); } BranchInstruction goto_transactionCommit = InstructionFactory.createBranchInstruction(Constants.GOTO, null); nl.append(goto_transactionCommit); gotos_transactionCommit.add(goto_transactionCommit); InstructionHandle newTarget = nl.getStart(); oldIl.append(oldIh, nl); try { oldIl.delete(oldIh); } catch(TargetLostException e) { InstructionHandle[] targets = e.getTargets(); for(int k=0; k < targets.length; k++) { InstructionTargeter[] targeters = targets[k].getTargeters(); for(int j=0; j < targeters.length; j++) { targeters[j].updateTarget(targets[k], newTarget); } } } } il.append(oldIl); } InstructionHandle ih_58 = il.append(InstructionFactory.createLoad(Type.INT, transactionLocalsBaseIndex-2+2)); BranchInstruction ifeq_59 = InstructionFactory.createBranchInstruction(Constants.IFEQ, null); il.append(ifeq_59); InstructionHandle ih_62 = il.append(new PUSH(_cp, 0)); il.append(new PUSH(_cp, Const.MEM_TM_MAGIC)); il.append(_factory.createInvoke("com.jopdesign.sys.Native", "wrMem", Type.VOID, new Type[] { Type.INT, Type.INT }, Constants.INVOKESTATIC)); InstructionHandle ih_68 = il.append(_factory.createFieldAccess("rttm.internal.Utils", "inTransaction", new ArrayType(Type.BOOLEAN, 1), Constants.GETSTATIC)); il.append(new PUSH(_cp, -122)); il.append(_factory.createInvoke("com.jopdesign.sys.Native", "rd", Type.INT, new Type[] { Type.INT }, Constants.INVOKESTATIC)); il.append(new PUSH(_cp, 0)); il.append(InstructionConstants.BASTORE); InstructionHandle ih_78 = il.append(new PUSH(_cp, 1)); il.append(new PUSH(_cp, -128)); il.append(_factory.createInvoke("com.jopdesign.sys.Native", "wr", Type.VOID, new Type[] { Type.INT, Type.INT }, Constants.INVOKESTATIC)); // InstructionHandle ih_84 = il.append(_factory.createLoad(Type.INT, transactionLocalsBaseIndex-2+3)); // il.append(_factory.createReturn(Type.INT)); InstructionHandle ih_84; { // return if (!method.getReturnType().equals(Type.VOID)) { ih_84 = il.append(InstructionFactory.createLoad(method.getReturnType(), transactionLocalsBaseIndex-2+3)); il.append(InstructionFactory.createReturn(method.getReturnType())); } else { ih_84 = il.append(InstructionFactory.createReturn(method.getReturnType())); } } // catch block // variable e has index 3 // } catch (Throwable e) { InstructionHandle nih_86 = il.append(InstructionFactory .createStore(Type.OBJECT, transactionLocalsBaseIndex-2+3)); // if (isNotNestedTransaction) { InstructionHandle nih_87 = il.append(InstructionFactory.createLoad(Type.INT, transactionLocalsBaseIndex-2+2)); BranchInstruction ifeq_88 = InstructionFactory.createBranchInstruction( Constants.IFEQ, null); il.append(ifeq_88); InstructionHandle nih_91 = il .append(InstructionFactory.createLoad(Type.OBJECT, transactionLocalsBaseIndex-2+3)); il.append(_factory.createFieldAccess("com.jopdesign.sys.RetryException", "instance", new ObjectType( "com.jopdesign.sys.RetryException"), Constants.GETSTATIC)); BranchInstruction if_acmpne_95 = InstructionFactory.createBranchInstruction( Constants.IF_ACMPNE, null); il.append(if_acmpne_95); // InstructionHandle nih_98 = il.append(_factory.createLoad(Type.INT, // 1)); // il.append(_factory.createStore(Type.INT, 0)); { for (int i: modifiedArguments) { il.append(InstructionFactory.createLoad( Type.INT, modifiedArgumentsCopies.get(i))); il.append(InstructionFactory.createStore(Type.INT, i)); } } BranchInstruction goto_110 = InstructionFactory.createBranchInstruction(Constants.GOTO, ih_43); InstructionHandle ih_110 = il.append(goto_110); // exception was manually aborted or a bug triggered InstructionHandle nih_103 = il.append(_factory.createFieldAccess( "rttm.internal.Utils", "inTransaction", new ArrayType( Type.BOOLEAN, 1), Constants.GETSTATIC)); il.append(new PUSH(_cp, -122)); il.append(_factory.createInvoke("com.jopdesign.sys.Native", "rd", Type.INT, new Type[] { Type.INT }, Constants.INVOKESTATIC)); il.append(new PUSH(_cp, 0)); il.append(InstructionConstants.BASTORE); InstructionHandle nih_113 = il.append(new PUSH(_cp, 1)); il.append(new PUSH(_cp, -128)); il.append(_factory.createInvoke("com.jopdesign.sys.Native", "wrMem", Type.VOID, new Type[] { Type.INT, Type.INT }, Constants.INVOKESTATIC)); InstructionHandle nih_119 = il.append(InstructionFactory .createLoad(Type.OBJECT, transactionLocalsBaseIndex-2+3)); // il.append(_factory.createCheckCast(new // ObjectType("java.lang.RuntimeException"))); il.append(InstructionConstants.ATHROW); // set branch targets ifne_12.setTarget(ih_19); goto_16.setTarget(ih_20); ifeq_22.setTarget(ih_43); ifeq_44.setTarget(ih_53); ifeq_59.setTarget(ih_84); ifeq_88.setTarget(nih_119); if_acmpne_95.setTarget(nih_103); { for (BranchInstruction b: gotos_transactionCommit) { b.setTarget(ih_58); } } // set exception handlers // TODO restrict exception handler method.addExceptionHandler(ih_53, ih_84, nih_86, null); method.setMaxStack(); method.setMaxLocals(); } m = method.getMethod(); oldIl.dispose(); il.dispose(); return m; } }