package com.sun.tools.javac.comp; import static com.sun.tools.javac.code.Kinds.*; import java.util.ArrayList; import javax.lang.model.element.ElementKind; import com.sun.tools.javac.code.Effects; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.RPL; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.TypeTags; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.OperatorSymbol; import com.sun.tools.javac.code.Symbol.RegionParameterSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.code.Type.ClassType; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.TreeCopier; import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.tree.TreeScanner; import com.sun.tools.javac.tree.TreeTranslator; import com.sun.tools.javac.tree.JCTree.DPJAtomic; import com.sun.tools.javac.tree.JCTree.DPJEffect; import com.sun.tools.javac.tree.JCTree.DPJForLoop; import com.sun.tools.javac.tree.JCTree.DPJNonint; import com.sun.tools.javac.tree.JCTree.DPJRegionPathList; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCArrayAccess; import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree; import com.sun.tools.javac.tree.JCTree.JCAssign; import com.sun.tools.javac.tree.JCTree.JCAssignOp; import com.sun.tools.javac.tree.JCTree.JCBinary; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCCatch; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; import com.sun.tools.javac.tree.JCTree.JCExpressionWithRPL; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCLiteral; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCNewClass; import com.sun.tools.javac.tree.JCTree.JCParens; import com.sun.tools.javac.tree.JCTree.JCReturn; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTry; import com.sun.tools.javac.tree.JCTree.JCUnary; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.JCTree.JCWhileLoop; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Options; import com.sun.tools.javac.util.Pair; import com.sun.tools.javac.util.Name.Table; public class InsertBarriers extends TreeTranslator { private static final String DPJRUNTIME = "DPJRuntime"; private static final String CONTEXT_DELEGATOR = "DPJContextDelegator"; private static final String NONINT_FLAG_CLASS = "NonintFlag"; private static final String BEFORE_READ_ACCESS = "beforeReadAccess"; private static final String ON_READ_ACCESS = "onReadAccess"; private static final String ON_WRITE_ACCESS = "onWriteAccess"; private static final String BEFORE_WRITE_ACCESS_LOG_ONLY = "beforeWriteAccessLogOnly"; private static final String RETURN_SECOND_VALUE = "returnSecondValue"; private static final String ON_ARRAY_READ_ACCESS = "onArrayReadAccess"; private static final String ON_ARRAY_WRITE_ACCESS = "onArrayWriteAccess"; private static final String ON_ARRAY_WRITE_ACCESS_LOG_ONLY = "onArrayWriteAccessLogOnly"; private static final String GET_FIELD_OFFSET = "getFieldOffset"; private static final String CLONE = "Clone"; private static final String OBJECT = "Object"; private static final String CONTEXT_CLASS = "Context"; private static final String NONINT_CONTEXT_CLASS = "NonintContext"; private static final String ORG = "org"; private static final String DEUCE = "deuce"; private static final String TRANSACTION = "transaction"; private static final String TRANSACTION_EXCEPTION = "TransactionException"; private static final String REFLECTION = "reflection"; private static final String ADDRESS_UTIL = "AddressUtil"; private static final String STATIC_FIELD_BASE = "staticFieldBase"; private static final String SUFFIX = "$DPJ_STM"; private static final String TEMP_VAR_PREFIX = "$DPJ_STM_temp"; private static final String CONTEXT_VAR = "$DPJ_STM_context"; private static final String NONINT_FLAG_VAR = "$DPJ_STM_nonint_flag"; private static final String FIELD_OFFSET_SUFFIX = "$DPJ_STM_offset"; private static final String CLASS_BASE = "$DPJ_STM_class_base"; private static final String TRANSACTIONAL_METHOD_SUFFIX = "_T$DPJ_STM"; // Mox number of times to retry a transaction private static final int max_retries = 10; private static int nextAtomicBlockID = 1; private Name dpjruntime; private Name contextDelegator; private Name nonintFlagClass; private Name beforeReadAccess; private Name onReadAccess; private Name onWriteAccess; private Name beforeReadAccessLogOnly; private Name returnSecondValue; private Name onArrayReadAccess; private Name onArrayWriteAccess; private Name onArrayWriteAccessLogOnly; private Name getFieldOffset; private Name clone; private Name contextClass; private Name nonintContextClass; private Name org; private Name deuce; private Name transaction; private Name transactionException; private Name reflection; private Name addressUtil; private Name staticFieldBase; private Name suffix; private Name tempVarPrefix; private Name contextVar; private Name nonintFlagVar; private Name fieldOffsetSuffix; private Name classBase; private Name object; private Name transactionalMethodSuffix; private void initNames() { dpjruntime = names.fromString(DPJRUNTIME); contextDelegator = names.fromString(CONTEXT_DELEGATOR); nonintFlagClass = names.fromString(NONINT_FLAG_CLASS); beforeReadAccess = names.fromString(BEFORE_READ_ACCESS); onReadAccess = names.fromString(ON_READ_ACCESS); onWriteAccess = names.fromString(ON_WRITE_ACCESS); beforeReadAccessLogOnly = names.fromString(BEFORE_WRITE_ACCESS_LOG_ONLY); returnSecondValue = names.fromString(RETURN_SECOND_VALUE); onArrayReadAccess = names.fromString(ON_ARRAY_READ_ACCESS); onArrayWriteAccess = names.fromString(ON_ARRAY_WRITE_ACCESS); onArrayWriteAccessLogOnly = names.fromString(ON_ARRAY_WRITE_ACCESS_LOG_ONLY); getFieldOffset = names.fromString(GET_FIELD_OFFSET); clone = names.fromString(CLONE); contextClass = names.fromString(CONTEXT_CLASS); nonintContextClass = names.fromString(NONINT_CONTEXT_CLASS); org = names.fromString(ORG); deuce = names.fromString(DEUCE); transaction = names.fromString(TRANSACTION); transactionException = names.fromString(TRANSACTION_EXCEPTION); reflection = names.fromString(REFLECTION); addressUtil = names.fromString(ADDRESS_UTIL); staticFieldBase = names.fromString(STATIC_FIELD_BASE); suffix = names.fromString(SUFFIX); tempVarPrefix = names.fromString(TEMP_VAR_PREFIX); contextVar = names.fromString(CONTEXT_VAR); nonintFlagVar = names.fromString(NONINT_FLAG_VAR); fieldOffsetSuffix = names.fromString(FIELD_OFFSET_SUFFIX); classBase = names.fromString(CLASS_BASE); object = names.fromString(OBJECT); transactionalMethodSuffix = names.fromString(TRANSACTIONAL_METHOD_SUFFIX); } // Types of expressions that can be assigned into private enum AssigneeType { FIELD_ID, // An identifier naming a field (in the current class) FIELD_ACCESS, // A field access expression ('e1.f') ARRAY_ACCESS, // An array access expression ('e1[e2]') DONT_TRANSLATE // Local variables, or anything else not for which we // don't need to translate the assignment into a barrier. } // Translation mode currently in effect private enum Mode { NORMAL, // No barriers or logging. Translate atomic blocks // and switch to transactional mode inside them. // Switch to noninterfering mode in nonint blocks. NONINTERFERING, // No barriers or logging. Ignore nonint and // atomic blocks. TRANSACTIONAL, // Insert barriers. Ignore atomic blocks. Switch // to logging-only mode in nonint blocks. LOGGING_ONLY // Insert logging for field writes, but no barriers. // Ignore nonint and atomic blocks. // Each mode also translates function calls to the // corresponding versions of functions. } private Mode mode = Mode.NORMAL; // Are we generating the code for the body of an atomic block // (even in a nested nonint block)? private boolean inAtomic = false; protected static final Context.Key<InsertBarriers> insertBarriersKey = new Context.Key<InsertBarriers>(); public static InsertBarriers instance(Context context) { InsertBarriers instance = context.get(insertBarriersKey); if (instance == null) instance = new InsertBarriers(context); return instance; } private TreeMaker make; private TreeCopier copy; private Log log; private Name.Table names; private Resolve rs; private Types types; private Symtab syms; private Options options; private CheckEffects checkEffects; // Environment for symbol lookup Env<AttrContext> attrEnv; // Enclosing method (we insert new variable decls at the top of this) private JCMethodDecl enclMethod = null; private JCClassDecl enclClass = null; // Have we already inserted a class_base field for in this class // (used for transactional access to static fields) private boolean madeClassBase = false; // Index for unique naming of new variables within methods private int nextVarIndex = 1; // New statements (variable decls) to insert at top of current function private List<JCStatement> newStats; // Should we translate the current method? private boolean translateMethod; // List of multiple results that should be substituted for the original one element. // If result == null, get results here instead (used to duplicate methods). List<JCTree> multipleResults = null; boolean translateWrites = true; protected InsertBarriers(Context context) { context.put(insertBarriersKey, this); make = TreeMaker.instance(context); copy = new TreeCopier(make); log = Log.instance(context); names = Name.Table.instance(context); rs = Resolve.instance(context); types = Types.instance(context); syms = Symtab.instance(context); options = Options.instance(context); checkEffects = CheckEffects.instance(context); initNames(); //TODO: Set attrEnv (or remove it) } @Override public <T extends JCTree> T translate(T tree) { T newTree = super.translate(tree); // Make translation preserve types (unless they're explicitly changed) if (newTree != null && newTree.type == null) newTree.type = tree.type; return newTree; } // Assignment operations private Pair<AssigneeType, JCExpression> translateAssignee(JCExpression tree) { if (tree instanceof JCIdent) { JCIdent ident = (JCIdent) tree; if (isTranslatableFieldSymbol(ident.sym) && translateWrites) { return Pair.of(AssigneeType.FIELD_ID, tree); } else { return Pair.of(AssigneeType.DONT_TRANSLATE, tree); } } if (tree instanceof JCFieldAccess) { JCFieldAccess fieldAccess = (JCFieldAccess) tree; fieldAccess.selected = translate(fieldAccess.selected); if (isTranslatableFieldAccess(fieldAccess) && translateWrites) { return Pair.of(AssigneeType.FIELD_ACCESS, tree); } else { return Pair.of(AssigneeType.DONT_TRANSLATE, tree); } } if (tree instanceof JCArrayAccess) { JCArrayAccess arrayAccess = (JCArrayAccess) tree; arrayAccess.indexed = translate(arrayAccess.indexed); arrayAccess.index = translate(arrayAccess.index); if (translateWrites) { return Pair.of(AssigneeType.ARRAY_ACCESS, tree); } else { return Pair.of(AssigneeType.DONT_TRANSLATE, tree); } } if (tree instanceof JCParens) { JCParens parens = (JCParens) tree; return translateAssignee(parens.expr); } log.error(tree.pos, "unexpected.form.of.assignment"); return Pair.of(AssigneeType.DONT_TRANSLATE, tree); } @Override public void visitAssign(JCAssign tree) { switch (mode) { case NORMAL: case NONINTERFERING: super.visitAssign(tree); break; case TRANSACTIONAL: case LOGGING_ONLY: visitAssign_transactional(tree); break; } } private void visitAssign_transactional(JCAssign tree) { Pair<AssigneeType, JCExpression> translated = translateAssignee(tree.lhs); switch (translated.fst) { case FIELD_ID: JCIdent ident = (JCIdent)translated.snd; JCExpression selected; if ((ident.sym.flags() & Flags.STATIC) != 0) { selected = make.Ident(ident.sym.owner); selected.type = ident.sym.owner.type; } else { selected = make.Ident(names._this); selected.type = enclClass.type; } result = makeWriteAccessTree(ident, null, selected, makeCastIfNeeded(ident.type, translate(tree.rhs)), ident.sym); break; case FIELD_ACCESS: JCFieldAccess fieldAccess = (JCFieldAccess)translated.snd; Name temp1 = insertLocalVariable(types.erasure(fieldAccess.selected.type)); result = makeWriteAccessTree(fieldAccess, temp1, fieldAccess.selected, makeCastIfNeeded(fieldAccess.type, translate(tree.rhs)), fieldAccess.sym); break; case ARRAY_ACCESS: JCArrayAccess arrayAccess = (JCArrayAccess)translated.snd; result = makeArrayWriteAccessTree(arrayAccess, null, arrayAccess.indexed, null, arrayAccess.index, translate(tree.rhs)); break; case DONT_TRANSLATE: tree.lhs = translated.snd; tree.rhs = translate(tree.rhs); result = tree; break; } } @Override public void visitAssignop(JCAssignOp tree) { switch (mode) { case NORMAL: case NONINTERFERING: super.visitAssignop(tree); break; case TRANSACTIONAL: case LOGGING_ONLY: visitAssignop_transactional(tree); break; } } private void visitAssignop_transactional(JCAssignOp tree) { Pair<AssigneeType, JCExpression> translated = translateAssignee(tree.lhs); int op = tree.getTag() - JCTree.ASGOffset; switch (translated.fst) { case FIELD_ID: JCIdent ident = (JCIdent)translated.snd; JCExpression selected; if ((ident.sym.flags() & Flags.STATIC) != 0) { selected = make.Ident(ident.sym.owner); selected.type = ident.sym.owner.type; } else { selected = make.Ident(names._this); selected.type = enclClass.type; } JCIdent identExp = (JCIdent)copy.copy(ident); identExp.rpl = ident.rpl; JCExpression binopExp = translate( make.Binary(op, identExp, make.Parens(tree.rhs))); result = makeWriteAccessTree(ident, null, selected, binopExp, ident.sym); break; case FIELD_ACCESS: JCFieldAccess fieldAccess = (JCFieldAccess)translated.snd; Name temp1 = insertLocalVariable(types.erasure(fieldAccess.selected.type)); JCExpression temp1id = make.Ident(temp1); temp1id.type = types.erasure(fieldAccess.selected.type); JCFieldAccess selectExp = (JCFieldAccess)make.Select(temp1id, fieldAccess.sym); selectExp.type = fieldAccess.sym.type; selectExp.rpl = fieldAccess.rpl; result = makeWriteAccessTree(fieldAccess, temp1, fieldAccess.selected, translate(make.Binary(op, selectExp, make.Parens(tree.rhs))), fieldAccess.sym); break; case ARRAY_ACCESS: JCArrayAccess arrayAccess = (JCArrayAccess)translated.snd; Name tempArr = insertLocalVariable(arrayAccess.indexed.type); Name tempIdx = insertLocalVariable(arrayAccess.index.type); JCExpression tempArrId = make.Ident(tempArr); tempArrId.type = arrayAccess.indexed.type; JCExpression tempIdxId = make.Ident(tempIdx); tempIdxId.type = arrayAccess.index.type; JCArrayAccess readExp = make.Indexed(tempArrId, tempIdxId); readExp.type = arrayAccess.type; readExp.rpl = arrayAccess.rpl; //onWriteAccess(tempArr = e1, tempIdx = e2, translate(tempArr[tempIdx] BINOP value), context) result = makeArrayWriteAccessTree(arrayAccess, tempArr, arrayAccess.indexed, tempIdx, arrayAccess.index, translate(make.Binary(op, readExp, make.Parens(tree.rhs)))); break; case DONT_TRANSLATE: tree.lhs = translated.snd; tree.rhs = translate(tree.rhs); result = tree; break; } } // Transform "++e" into "e += 1", and translate that private void visitPreIncDec(JCUnary tree, boolean inc) { int op = inc ? JCTree.PLUS_ASG : JCTree.MINUS_ASG; JCExpression literalOne = make.Literal(TypeTags.INT, 1); literalOne.type = new Type(TypeTags.INT, null); JCExpression assignop = make.Assignop(op, tree.arg, literalOne); JCExpression parens = make.Parens(assignop); assignop.type = parens.type = tree.arg.type; result = translate(parens); } private void visitPostIncDec(JCUnary tree, boolean inc) { Pair<AssigneeType, JCExpression> translated = translateAssignee(tree.arg); int op = inc ? JCTree.PLUS : JCTree.MINUS; Name temp1, temp2; switch (translated.fst) { case FIELD_ID: JCIdent ident = (JCIdent)translated.snd; temp1 = insertLocalVariable(tree.arg.type); JCExpression selected; if ((ident.sym.flags() & Flags.STATIC) != 0) { selected = make.Ident(ident.sym.owner); selected.type = ident.sym.owner.type; } else { selected = make.Ident(names._this); selected.type = enclClass.type; } result = makeWriteAccessTree(ident, null, selected, temp1, translate(ident), make.Binary(op, make.Ident(temp1), make.Literal(TypeTags.INT, 1)), ident.sym); break; case FIELD_ACCESS: JCFieldAccess fieldAccess = (JCFieldAccess)translated.snd; temp1 = insertLocalVariable(types.erasure(fieldAccess.selected.type)); temp2 = insertLocalVariable(tree.arg.type); JCExpression temp1id = make.Ident(temp1); temp1id.type = types.erasure(fieldAccess.selected.type); JCFieldAccess val = (JCFieldAccess)make.Select(temp1id, fieldAccess.sym); val.type = fieldAccess.sym.type; val.rpl = fieldAccess.rpl; result = makeWriteAccessTree(fieldAccess, temp1, fieldAccess.selected, temp2, translate(val), make.Binary(op, make.Ident(temp2), make.Literal(TypeTags.INT, 1)), fieldAccess.sym); break; case ARRAY_ACCESS: JCArrayAccess arrayAccess = (JCArrayAccess)translated.snd; Name tempArr = insertLocalVariable(arrayAccess.indexed.type); Name tempIdx = insertLocalVariable(arrayAccess.index.type); Name tempVal = insertLocalVariable(arrayAccess.type); JCExpression tempArrId = make.Ident(tempArr); tempArrId.type = arrayAccess.indexed.type; JCExpression tempIdxId = make.Ident(tempIdx); tempIdxId.type = arrayAccess.index.type; JCExpression readExp = make.Indexed(tempArrId, tempIdxId); readExp.type = arrayAccess.type; JCExpression tempValId = make.Ident(tempVal); tempValId.type = readExp.type; //onWriteAccess(tempArr = e1, tempIdx = e2, tempVal = tempArr[tempIdx], tempVal BINOP 1, context) result = makeArrayWriteAccessTree(arrayAccess, tempArr, arrayAccess.indexed, tempIdx, arrayAccess.index, tempVal, readExp, make.Binary(op, tempValId, make.Literal(TypeTags.INT, 1))); break; case DONT_TRANSLATE: tree.arg = translated.snd; result = tree; break; } } @Override public void visitUnary(JCUnary tree) { switch (mode) { case NORMAL: case NONINTERFERING: super.visitUnary(tree); break; case TRANSACTIONAL: case LOGGING_ONLY: visitUnary_transactional(tree); break; } } private void visitUnary_transactional(JCUnary tree) { int opcode = tree.getTag(); switch (opcode) { case JCTree.PREINC: case JCTree.PREDEC: visitPreIncDec(tree, opcode == JCTree.PREINC); break; case JCTree.POSTINC: case JCTree.POSTDEC: visitPostIncDec(tree, opcode == JCTree.POSTINC); break; default: tree.arg = translate(tree.arg); result = tree; break; } } // Potential field reference operations: may be used for reads or writes // Basic rule: If a field reference is _directly_ used as the left side // of an assignment, or if it's on the left side of an assignment // nested only in parentheses, then it's a write (& maybe also read) of // that field. If it's further nested in the AST for the left side of // an assignment, or if it's anywhere else, then it's a read. // // The write cases should be handled by the assignment operation visitors, // so the below visitors should only get called for the read cases. // (In the NORMAL and NONINTERFERING modes, these methods do get called // for the write cases, but that's OK because the translation is a no-op.) @Override public void visitIndexed(JCArrayAccess tree) { switch (mode) { case NORMAL: case NONINTERFERING: super.visitIndexed(tree); break; case LOGGING_ONLY: case TRANSACTIONAL: visitIndexed_transactional(tree); break; } } private void visitIndexed_transactional(JCArrayAccess tree) { if (isLogOnlyAccess(tree)) { super.visitIndexed(tree); return; } // An optimization would be not to track local arrays whose references don't escape. result = makeArrayReadAccessTree(translate(tree.indexed), translate(tree.index)); } @Override public void visitSelect(JCFieldAccess tree) { switch (mode) { case NORMAL: case NONINTERFERING: super.visitSelect(tree); break; case LOGGING_ONLY: case TRANSACTIONAL: visitSelect_transactional(tree); break; } } private void visitSelect_transactional(JCFieldAccess tree) { if (!isTranslatableFieldAccess(tree) || isLogOnlyAccess(tree)) { super.visitSelect(tree); return; } if (enclMethod == null) { super.visitSelect(tree); return; } // Generate simpler tree without temp var for fields of 'this' if (tree.selected instanceof JCIdent && ((JCIdent)tree.selected).name.equals(names._this)) { result = makeThisFieldReadTree(tree, tree.sym); return; } // This is definitely a read of a field if ((tree.sym.flags() & Flags.STATIC) != 0) { result = makeReadAccessTree( null, make.Select(make.Type(types.erasure(tree.selected.type)), classBase), make.Select(translate(tree.selected), tree.sym), makeOffsetofTree(tree.selected.type, tree.sym)); } else { Name tempVar = insertLocalVariable(tree.selected.type); result = makeReadAccessTree( tempVar, translate(tree.selected), make.Select(make.Ident(tempVar), tree.sym), makeOffsetofTree(tree.selected.type, tree.sym)); } } @Override public void visitIdent(JCIdent tree) { switch (mode) { case NORMAL: case NONINTERFERING: super.visitIdent(tree); break; case LOGGING_ONLY: case TRANSACTIONAL: visitIdent_transactional(tree); break; } } private void visitIdent_transactional(JCIdent tree) { // Figure out if this is a field access (of this) or not. if (isTranslatableFieldSymbol(tree.sym) && !isLogOnlyAccess(tree)) { result = makeThisFieldReadTree(tree, tree.sym); } else { result = tree; } } // Convert [this.]f to "DPJContextDelegator.onReadAccess(this, f, offsetof(f), context);" private JCTree makeThisFieldReadTree(JCExpression tree, Symbol sym) { JCExpression obj; if ((sym.flags() & Flags.STATIC) != 0) { obj = make.Select(make.Ident(sym.owner), classBase); } else { obj = make.Ident(names._this); } return makeReadAccessTree( null, obj, tree, makeOffsetofTree(enclClass.type, sym)); } /** Generate the JCTree for an onReadAccess(...) call (and preceding * beforeReadAccess() call), given trees for the arguments. * obj should already be translated (if necessary). */ private JCTree makeReadAccessTree(Name tempVarName, JCExpression obj, JCExpression val, JCExpression field) { JCExpression beforeReadExpr = makeBeforeReadAccessTree(tempVarName, obj, field); JCExpression meth = makeContextDelegatorMethod(ON_READ_ACCESS); // Build list of arguments to onReadAccess List<JCExpression> args = List.nil(); args = args.prepend(makeContextTree()); args = args.prepend(field); args = args.prepend(val); args = args.prepend(beforeReadExpr); return make.App(meth, args); } /** Generate the JCExpression for "tempVar = beforeReadAccess(obj, field, context)". * obj should already be translated (if necessary). */ private JCExpression makeBeforeReadAccessTree(Name tempVarName, JCExpression obj, JCExpression field) { JCExpression meth = makeContextDelegatorMethod(BEFORE_READ_ACCESS); List<JCExpression> args = List.nil(); args = args.prepend(makeContextTree()); args = args.prepend(field); args = args.prepend(obj); return makeAssignOpt(tempVarName, make.App(meth, args)); } /** Make an expression "varName = expr" (or just "expr" if varName is null) */ private JCExpression makeAssignOpt(Name varName, JCExpression expr) { if (varName != null) { return make.Assign(make.Ident(varName), expr); } else { return expr; } } /** Make a context delegator call with a default type (which is bogus, but * sufficient to make the pretty-printer generate appropriate code). */ // TODO Make this assign the right type (or change invocations of this to invocations with the correct type specified.) private JCExpression makeContextDelegatorMethod(String name) { return makeContextDelegatorMethod(name, new Type(TypeTags.METHOD, null)); } /** Make an expression for a context delegator method call, and give it the specified type */ private JCExpression makeContextDelegatorMethod(String name, Type type) { JCExpression meth = make.Select( make.Select(make.Select(make.Select(make.Ident( org), deuce), transaction), contextDelegator), names.fromString(name)); meth.setType(type); return meth; } /** Generate 4-argument form (no old value) of onWriteAccess call * Expressions passed in should already be translated (if necessary). */ private JCExpression makeWriteAccessTree(JCExpressionWithRPL tree, Name varName1, JCExpression obj, JCExpression value, Symbol field) { return makeWriteAccessTree(tree, varName1, obj, null, null, value, field); } /** Generate 4- or 5-argument form (with old value) of onWriteAccess call */ private JCExpression makeWriteAccessTree(JCExpressionWithRPL tree, Name varName1, JCExpression obj, Name varName2, JCExpression oldValue, JCExpression value, Symbol field) { if (isLogOnlyAccess(tree)) { return makeWriteAccessTreeLogOnly(varName1, obj, varName2, oldValue, value, field); } JCExpression meth = makeContextDelegatorMethod(ON_WRITE_ACCESS); if (varName1 == null && !(obj instanceof JCIdent)) { log.error("No object symbol for onWriteAccess call"); } Name objName = (varName1 != null) ? varName1 : ((JCIdent)obj).name; List<JCExpression> args = List.nil(); if ((field.flags() & Flags.STATIC) != 0) { // If this is a write to a static field, we put the argument for the classBase // object at the end. This results in calling a version of the onWriteAccess // methods where the first argument is a dummy. Note that we still need to // evaluate the 'obj' expression because it may have other effects, so // we can't just replace the first argument with this one. // But if the 'obj' expression is just a type specifier, we have to change it. JCExpression type = make.Type(types.erasure(field.owner.type)); args = args.prepend( make.Select(type, classBase)); if ((obj instanceof JCIdent && ((JCIdent)obj).sym instanceof TypeSymbol) || (obj instanceof JCFieldAccess && ((JCFieldAccess)obj).sym instanceof TypeSymbol)) { obj = make.Literal(TypeTags.BOT, null); } } args = args.prepend(makeContextTree()); args = args.prepend(makeOffsetofTree(make.Ident(objName), field)); args = args.prepend(value); if (oldValue != null) args = args.prepend(makeAssignOpt(varName2, oldValue)); args = args.prepend(makeAssignOpt(varName1, obj)); return make.App(meth, args); } private JCExpression makeWriteAccessTreeLogOnly(Name varName1, JCExpression obj, Name varName2, JCExpression oldValue, JCExpression newValue, Symbol field) { JCExpression meth = makeContextDelegatorMethod(BEFORE_WRITE_ACCESS_LOG_ONLY); JCExpression returnSecondValue = makeContextDelegatorMethod(RETURN_SECOND_VALUE); List<JCExpression> args = List.nil(); if (varName1 == null && !(obj instanceof JCIdent)) { log.error("No object symbol for onWriteAccess call"); } Name objName = (varName1 != null) ? varName1 : ((JCIdent)obj).name; // Case 1: Corresponds to onWriteAccess(obj, value, field, context). For non-static fields // Non - post-inc/dec case if (oldValue == null) { // Non-static field: case 1 // "(beforeWriteAccessLogOnly(v1 = obj, v1.f, fieldOffset, context).f = value)" if((field.flags() & Flags.STATIC) == 0) { if (varName1 == null) varName1 = insertLocalVariable(obj.type); args = args.append(makeAssignOpt(varName1, obj)); args = args.append(make.Select(make.Ident(varName1), field)); args = args.append(makeOffsetofTree(make.Ident(objName), field)); args = args.append(makeContextTree()); JCMethodInvocation callExp = make.App(meth, args); return make.Parens(make.Assign(make.Select(callExp, field), newValue)); } // Static field: case 2 // "Class.static_f = beforeWriteAccessLogOnly(obj, Class.static_f, newValue, fieldOffset, context, classBase)" else { // The class that contains the field JCExpression type = make.Type(types.erasure(field.owner.type)); if ((obj instanceof JCIdent && ((JCIdent)obj).sym instanceof TypeSymbol) || (obj instanceof JCFieldAccess && ((JCFieldAccess)obj).sym instanceof TypeSymbol)) { obj = make.Literal(TypeTags.BOT, null); } args = args.append(makeAssignOpt(varName1, obj)); args = args.append(make.Select(type, field)); args = args.append(newValue); args = args.append(makeOffsetofTree(make.Ident(objName), field)); args = args.append(makeContextTree()); args = args.append(make.Select(type, classBase)); JCMethodInvocation callExp = make.App(meth, args); return make.Parens(make.Assign(make.Select(type, field), callExp)); } } // Post-inc/dec case else { int op = ((JCBinary)newValue).getTag(); //JCTree.PLUS for post-int, JCTree.MINUS for post-dec // Non-static field: case 3 // "returnSecondValue(beforeWriteAccessLogOnly(obj, v2=oldValue, fieldOffset, context).f = v2 +/- 1, v2)" if((field.flags() & Flags.STATIC) == 0) { args = args.append(makeAssignOpt(varName1, obj)); args = args.append(makeAssignOpt(varName2, oldValue)); args = args.append(makeOffsetofTree(make.Ident(objName), field)); args = args.append(makeContextTree()); JCMethodInvocation callExp = make.App(meth, args); JCExpression assignExp = make.Assign( make.Select(callExp, field), make.Binary(op, make.Ident(varName2), make.Literal(TypeTags.INT, 1))); List<JCExpression> args2 = List.nil(); args2 = args2.append(assignExp); args2 = args2.append(make.Ident(varName2)); return make.App(returnSecondValue, args2); } // Static field: case 4 // "returnSecondValue(Class.static_f = beforeWriteAccessLogOnly(obj, v2=oldValue, v2 +/- 1, fieldOffset, context, classBase), v1)" else { // The class that contains the field JCExpression type = make.Type(types.erasure(field.owner.type)); if ((obj instanceof JCIdent && ((JCIdent)obj).sym instanceof TypeSymbol) || (obj instanceof JCFieldAccess && ((JCFieldAccess)obj).sym instanceof TypeSymbol)) { obj = make.Literal(TypeTags.BOT, null); } args = args.append(makeAssignOpt(varName1, obj)); args = args.append(makeAssignOpt(varName2, oldValue)); args = args.append(make.Binary(op, make.Ident(varName2), make.Literal(TypeTags.INT, 1))); args = args.append(makeOffsetofTree(make.Ident(objName), field)); args = args.append(makeContextTree()); args = args.append(make.Select(type, classBase)); JCMethodInvocation callExp = make.App(meth, args); JCExpression assignExp = make.Assign(make.Select(type, field), callExp); List<JCExpression> args2 = List.nil(); args2 = args2.append(assignExp); args2 = args2.append(make.Ident(varName2)); return make.App(returnSecondValue, args2); } } } /** Generate 4-argument form (no one value) of onArrayWriteAccess call * Expressions passed should already be translated (if necessary). */ private JCExpression makeArrayWriteAccessTree(JCExpressionWithRPL tree, Name arrayVarName, JCExpression arr, Name indexVarName, JCExpression index, JCExpression value) { return makeArrayWriteAccessTree(tree, arrayVarName, arr, indexVarName, index, null, null, value); } private JCExpression makeArrayWriteAccessTree(JCExpressionWithRPL tree, Name arrayVarName, JCExpression arr, Name indexVarName, JCExpression index, Name oldvalVarName, JCExpression oldValue, JCExpression value) { JCExpression meth; if (isLogOnlyAccess(tree)) { meth = makeContextDelegatorMethod(ON_ARRAY_WRITE_ACCESS_LOG_ONLY); } else { meth = makeContextDelegatorMethod(ON_ARRAY_WRITE_ACCESS); } List<JCExpression> args = List.nil(); args = args.prepend(makeContextTree()); args = args.prepend(value); if (oldValue != null) args = args.prepend(makeAssignOpt(oldvalVarName, oldValue)); args = args.prepend(makeAssignOpt(indexVarName, index)); args = args.prepend(makeAssignOpt(arrayVarName, arr)); return make.App(meth, args); } // Should be called with a tree representing a read or write operation. // Returns true if this is an access that should be done in log-only mode (i.e. an access to a nonint location) private boolean isLogOnlyAccess(JCExpressionWithRPL tree) { if (options.get("-disablenonintopt") != null) return false; // TODO Remove nonint block support? if (mode == Mode.LOGGING_ONLY) return true; // TODO Does this work right for accesses in constructors? if (tree.rpl.isAtomic()) { return false; } else { return true; } } private JCTree makeArrayReadAccessTree(JCExpression indexed, JCExpression index) { JCExpression meth = makeContextDelegatorMethod(ON_ARRAY_READ_ACCESS); List<JCExpression> args = List.nil(); args = args.prepend(makeContextTree()); args = args.prepend(index); args = args.prepend(indexed); return make.App(meth, args); } /** Generate the tree for an expression to access the current context */ private JCExpression makeContextTree() { return make.Ident(contextVar); } /** Generate an expression to give the offset of a field (which can * be used to access it with sum.misc.Unsafe methods) * @param e expression for the class name, or an object of that type */ private JCExpression makeOffsetofTree(JCExpression e, Symbol field) { return make.Select(e, field.name.append(fieldOffsetSuffix)); } private JCExpression makeOffsetofTree(Type t, Symbol field) { return makeOffsetofTree(make.Type(types.erasure(t)), field); } /** We need to cast some 'value' arguments to the type of the field being accessed in * order to ensure that the right polymorphic method is called. This isn't needed * for object types, since the context delegator methods for them don't use polymorphism. */ private JCExpression makeCastIfNeeded(Type type, JCExpression exp) { JCExpression retVal; if (!type.isPrimitive() || types.isSameType(type, exp.type)) retVal = exp; else { retVal = make.TypeCast(type, make.Parens(exp)); retVal.type = type; } return retVal; } /** Insert a new local variable of a given type in the current method, and return its name */ private Name insertLocalVariable(Type type) { if (enclMethod == null) log.error("Can't insert variable outside of method"); Name name = names.fromString(TEMP_VAR_PREFIX + nextVarIndex++); JCStatement declaration = make.VarDef(new VarSymbol(0, name, type, enclMethod.sym), null); newStats = newStats.prepend(declaration); return name; } /** Determine whether sym is a symbol referring to a field * for which we need to do the translation of reads/writes */ private boolean isTranslatableFieldSymbol(Symbol sym) { // TODO Handle accesses in initializers (outside methods) if (enclMethod == null) return false; if (sym == null) { return false; } // Don't translate final field accesses // TODO Is this always legitimate? if ((sym.flags() & Flags.FINAL) != 0) return false; return (sym instanceof VarSymbol && ((VarSymbol)sym).getKind() == ElementKind.FIELD && ((VarSymbol)sym).name != names._this && ((VarSymbol)sym).name != names._super && ((VarSymbol)sym).name != names._class); } /** Determine whether a 'select' expression needs to be translated * (i.e. it's actually a field access, not a method call or special pseudo-field access) */ private boolean isTranslatableFieldAccess(JCFieldAccess tree) { return isTranslatableFieldSymbol(tree.sym); } // Change a method invocation to the appropriate version // for our current mode. This requires inserting a context // parameter for the transactional and logging_only versions. // TODO Figure out how we want to distinguish the versions, // and refactor accordingly. @Override public void visitApply(JCMethodInvocation tree) { switch (mode) { case NORMAL: super.visitApply(tree); break; case NONINTERFERING: case TRANSACTIONAL: case LOGGING_ONLY: visitApply_addParameter(tree); break; } } public void visitNewClass(JCNewClass tree) { switch (mode) { case NORMAL: super.visitNewClass(tree); break; case NONINTERFERING: case TRANSACTIONAL: case LOGGING_ONLY: visitNewClass_addParameter(tree); break; } } /** Change method calls to call logging-only version & pass context */ private void visitApply_addParameter(JCMethodInvocation tree) { tree.meth = translate(tree.meth); JCExpression contextExpr; // Don't insert extra param in super() call in constructors for classes that directly // descend from Object (since Object doesn't have a constructor of that form). if (tree.meth instanceof JCIdent) { JCIdent ident = (JCIdent)tree.meth; if (ident.name.equals(names._super) && enclClass.extending == null) { result = tree; return; } } if (mode == Mode.TRANSACTIONAL) { contextExpr = make.Ident(contextVar); } else if (mode == Mode.LOGGING_ONLY) { // Make the context parameter a NonintContext, so that we get the // logging-only version of the constructor, if available. contextExpr = make.TypeCast( make.Select(make.Select(make.Select(make.Ident( org), deuce), transaction), nonintContextClass), make.Ident(contextVar)); } else if (mode == Mode.NONINTERFERING) { // Don't add NonintFlag parameter for calls to java.* and javax.* types. // This is conservative but safe with respect to subclassing of // standard library classes. // TODO actually determine which classes we are compiling, and use that String className = tree.getMethodSymbol().enclClass().toString(); if (className.startsWith("java.") || className.startsWith("javax.")) { result = tree; return; } contextExpr = make.TypeCast( make.Select(make.Ident(dpjruntime), nonintFlagClass), make.Literal(TypeTags.BOT, null)); } else { log.error("invalid.mode"); return; } tree.args = translate(tree.args).append(contextExpr); result = tree; } private void visitNewClass_addParameter(JCNewClass tree) { super.visitNewClass(tree); tree = (JCNewClass)result; JCExpression contextExpr; if (mode == Mode.TRANSACTIONAL) { contextExpr = make.Ident(contextVar); } else if (mode == Mode.LOGGING_ONLY) { // Make the context parameter a NonintContext, so that we get the // logging-only version of the constructor, if available. contextExpr = make.TypeCast( make.Select(make.Select(make.Select(make.Ident( org), deuce), transaction), nonintContextClass), make.Ident(contextVar)); } else if (mode == Mode.NONINTERFERING) { // Don't add NonintFlag parameter for calls to java.* and javax.* types. // This is conservative but safe with respect to subclassing of // standard library classes. // TODO actually determine which classes we are compiling, and use that String className = tree.constructor.enclClass().toString(); if (className.startsWith("java.") || className.startsWith("javax.")) { result = tree; return; } contextExpr = make.TypeCast( make.Select(make.Ident(dpjruntime), nonintFlagClass), make.Literal(TypeTags.BOT, null)); } else { log.error("invalid.mode"); return; } tree.args = tree.args.append(contextExpr); result = tree; } /** Decide whether to translate this method, based on its annotations. * Also reset numbering of temp variables */ // TODO Deal with field accesses in field and block initializers @Override public void visitMethodDef(JCMethodDecl tree) { JCMethodDecl prevEnclMethod = enclMethod; int prevVarIndex = nextVarIndex; List<JCStatement> prevStats = newStats; enclMethod = tree; nextVarIndex = 1; boolean prevInAtomic = inAtomic; inAtomic = false; // Examine annotations to decide if method needs translation if (hasCloneAnnotation(tree)) { // TODO Do appropriate translation for any or all of the four modes. mode = Mode.NORMAL; super.visitMethodDef((JCMethodDecl)copy.copy(tree)); JCTree nonstmTree = (JCMethodDecl)result; mode = Mode.TRANSACTIONAL; super.visitMethodDef((JCMethodDecl)copy.copy(tree)); JCMethodDecl stmTree = (JCMethodDecl)result; mode = Mode.LOGGING_ONLY; super.visitMethodDef((JCMethodDecl)copy.copy(tree)); JCMethodDecl loggingOnlyTree = (JCMethodDecl)result; mode = Mode.NONINTERFERING; super.visitMethodDef((JCMethodDecl)copy.copy(tree)); JCMethodDecl noninterferingTree = (JCMethodDecl)result; // Don't mangle names, for compatibility with Deuce's bytecode rewriter // TODO Can this create any problems? // Add context parameter to stm tree TypeSymbol tsym = new TypeSymbol(0, contextClass, Type.noType, new PackageSymbol(transaction, new PackageSymbol(deuce, new PackageSymbol(org, syms.unnamedPackage)))); Type contextType = new ClassType(Type.noType, List.<Type>nil(), List.<RPL>nil(), List.<Effects>nil(), tsym, null); JCVariableDecl contextParam = make.VarDef(new VarSymbol(Flags.FINAL, contextVar, contextType, enclMethod.sym), null); stmTree.params = stmTree.params.append(contextParam); // Add context parameter to logging-only tree tsym = new TypeSymbol(0, nonintContextClass, Type.noType, new PackageSymbol(transaction, new PackageSymbol(deuce, new PackageSymbol(org, syms.unnamedPackage)))); contextType = new ClassType(Type.noType, List.<Type>nil(), List.<RPL>nil(), List.<Effects>nil(), tsym, null); contextParam = make.VarDef(new VarSymbol(Flags.FINAL, contextVar, contextType, enclMethod.sym), null); loggingOnlyTree.params = loggingOnlyTree.params.append(contextParam); // Add nonint flag parameter to noninterfering tree tsym = new TypeSymbol(0, nonintFlagClass, Type.noType, new PackageSymbol(dpjruntime, syms.unnamedPackage)); contextType = new ClassType(Type.noType, List.<Type>nil(), List.<RPL>nil(), List.<Effects>nil(), tsym, null); contextParam = make.VarDef(new VarSymbol(Flags.FINAL, nonintFlagVar, contextType, enclMethod.sym), null); noninterferingTree.params = noninterferingTree.params.append(contextParam); multipleResults = List.of(nonstmTree, stmTree, loggingOnlyTree, noninterferingTree); result = null; } else { mode = Mode.NORMAL; super.visitMethodDef(tree); } nextVarIndex = prevVarIndex; enclMethod = prevEnclMethod; inAtomic = prevInAtomic; } // TODO Deal with parameters to control generation of 4 versions private boolean hasCloneAnnotation(JCMethodDecl tree) { List<JCAnnotation> l = tree.mods.annotations; for (; l.nonEmpty(); l = l.tail) { if (l.head.annotationType instanceof JCIdent) { JCIdent ident = (JCIdent)l.head.annotationType; if (ident.name.equals(clone)) return true; } } return false; } public void visitDPJForLoop(DPJForLoop tree) { super.visitDPJForLoop(tree); tree = (DPJForLoop)result; // Always include 'this' as a used var in foreach loops (in instance methods), // because barrier insertion can create references to 'this'. if (mode != Mode.NONINTERFERING && (enclMethod.mods.flags & Flags.STATIC) == 0) { for(VarSymbol var : tree.usedVars) { if (var.name.equals(names._this)) return; } tree.usedVars.add(new VarSymbol(Flags.FINAL, names._this, enclClass.type, enclMethod.sym)); } } /** Insert new temp variables at the top of the enclosing block */ @Override public void visitBlock(JCBlock tree) { List<JCStatement> prevStats = newStats; newStats = List.nil(); super.visitBlock(tree); // If we're in a constructor that starts with a "super()" call, // we need to keep that before the stuff we insert. JCStatement superFirst = null; if (tree.stats.head instanceof JCExpressionStatement) { JCExpression expr = ((JCExpressionStatement)tree.stats.head).expr; if (expr instanceof JCMethodInvocation) { JCExpression meth = ((JCMethodInvocation)expr).meth; if (meth instanceof JCIdent) { Name name = ((JCIdent)meth).name; if (name.equals(names._super)) { superFirst = tree.stats.head; tree.stats = tree.stats.tail; } } } } tree.stats = tree.stats.prependList(newStats.reverse()); if (superFirst != null) tree.stats = tree.stats.prepend(superFirst); newStats = prevStats; } @Override public void visitClassDef(JCClassDecl tree) { JCClassDecl prevEnclClass = enclClass; enclClass = tree; boolean prevMadeClassBase = madeClassBase; madeClassBase = false; boolean prevInAtomic = inAtomic; inAtomic = false; tree.mods = translate(tree.mods); tree.typarams = translateTypeParams(tree.typarams); tree.extending = translate(tree.extending); tree.implementing = translate(tree.implementing); tree.defs = translateDefsList(tree.defs); result = tree; enclClass = prevEnclClass; madeClassBase = prevMadeClassBase; inAtomic = prevInAtomic; } /** Translate a list of definitions inside a class. Allow one definition to * be replaced by two (or more) versions: if the result is null, look at * multipleResults for all the replacement definitions. */ private List<JCTree> translateDefsList(List<JCTree> trees) { if (trees == null) return null; List<JCTree> newList = List.nil(); for (List<JCTree> l = trees; l.nonEmpty(); l = l.tail) { JCTree result = translate(l.head); if (result != null) { newList = newList.prepend(result); } else { newList = newList.prependList(multipleResults.reverse()); multipleResults = null; } } return newList.reverse(); } @Override public void visitTypeArray(JCArrayTypeTree tree) { tree.elemtype = translate(tree.elemtype); tree.rpl = translate(tree.rpl); tree.indexParam = tree.indexParam; // Don't translate //TODO Is this right? result = tree; } // Delete any extra parens inserted around expression statements, // to conform to Java syntax rules. @Override public void visitExec(JCExpressionStatement tree) { // Convert post-inc/dec statements to pre-inc/dec for efficiency if (tree.expr instanceof JCUnary) { JCUnary unary = (JCUnary)tree.expr; int opcode = unary.getTag(); if (opcode == JCTree.POSTINC) unary.setTag(JCTree.PREINC); if (opcode == JCTree.POSTDEC) unary.setTag(JCTree.PREDEC); } super.visitExec(tree); while (tree.expr instanceof JCParens) tree.expr = ((JCParens)tree.expr).expr; } // Insert extra static fields for field offsets // Also, insert initializers for local vars. @Override public void visitVarDef(JCVariableDecl tree) { // Only do field transformation for fields of non-inner classes, // not on local variables. // TODO handle inner classes (which can't contain static fields) if (enclMethod != null || enclClass.sym.owner.kind != PCK /* check for inner class */) { super.visitVarDef(tree); return; } // TODO Exclude some kinds of fields (e.g. final)? Name offsetVarName = tree.name.append(fieldOffsetSuffix); VarSymbol offsetVarSymbol = new VarSymbol(Flags.PUBLIC|Flags.STATIC|Flags.FINAL, offsetVarName, new Type(TypeTags.LONG, null), enclClass.sym); List<JCExpression> args = List.of( make.Select(make.Ident(enclClass.name), names._class), make.Literal(tree.name.toString())); JCExpression initExpr = make.App(makeContextDelegatorMethod(GET_FIELD_OFFSET), args); JCTree offsetVarExpr = make.VarDef(offsetVarSymbol, initExpr); // TODO Set the mode here? tree.mods = translate(tree.mods); tree.vartype = translate(tree.vartype); // TODO Deal with (or disallow) calls in initializers //tree.init = translate(tree.init); result = null; multipleResults = List.of(tree, offsetVarExpr); // If this is the first static field we have encountered in this class, // use it to make the class_base field needed for transactional access to static fields if (!madeClassBase && (tree.mods.flags & Flags.STATIC) != 0) { JCExpression meth = make.Select(make.Select(make.Select(make.Select(make.Ident( org), deuce), reflection), addressUtil), staticFieldBase); meth.setType(new Type(TypeTags.METHOD, null)); args = List.of( make.Select(make.Ident(enclClass.name), names._class), make.Literal(tree.name.toString())); JCVariableDecl classBaseDecl = make.VarDef(make.Modifiers(Flags.PUBLIC | Flags.STATIC | Flags.FINAL), classBase, null, make.Ident(object), make.App(meth, args)); multipleResults = multipleResults.append(classBaseDecl); madeClassBase = true; } } // Translate nonint blocks based on mode //TODO Implement a switch to turn nonint optimization on/off @Override public void visitNonint(DPJNonint tree) { switch (mode) { case NORMAL: mode = Mode.NONINTERFERING; result = translate(tree.body); mode = Mode.NORMAL; break; case TRANSACTIONAL: mode = Mode.LOGGING_ONLY; result = translate(tree.body); mode = Mode.TRANSACTIONAL; break; case LOGGING_ONLY: case NONINTERFERING: result = translate(tree.body); break; } } // Translate atomic blocks based on mode @Override public void visitAtomic(DPJAtomic tree) { switch (mode) { case NORMAL: inAtomic = true; result = makeAtomicBlock(tree); inAtomic = false; break; case NONINTERFERING: case TRANSACTIONAL: case LOGGING_ONLY: // Don't generate code to start transaction in these modes result = translate(tree.body); break; } } // Generate the code to set up an atomic block private JCStatement makeAtomicBlock(DPJAtomic tree) { Name n_Throwable = names.fromString("Throwable"); Name n_ex = names.fromString("ex" + SUFFIX); String s_getInstance = "getInstance"; Name n_commitVar = names.fromString("commit" + SUFFIX); Name n_commitMethod = names.fromString("commit"); Name n_i = names.fromString("i" + SUFFIX); Name n_init = names.fromString("init"); Name n_rollback = names.fromString("rollback"); Name n_Error = names.fromString("Error"); Name n_RuntimeException = names.fromString("RuntimeException"); Name n_t = names.fromString("t" + SUFFIX); Name n_backup = names.fromString("_backup" + SUFFIX); Name n_returnVal = names.fromString("returnVal" + SUFFIX); Name n_returning = names.fromString("returning" + SUFFIX); Name n_tryBlock = names.fromString(SUFFIX + "_try_block"); JCExpression txExceptionClass = make.Select(make.Select(make.Select(make.Ident( org), deuce), transaction), transactionException); // List of new statements being generated List<JCStatement> stats = List.nil(); JCBlock whileBlock; JCBlock ifBlock; JCBlock ifExBlock; JCBlock elseBlock; JCTry tryCatch; Type methodType = new Type(TypeTags.METHOD, null); // List of types of throwables that we need to handle (as JCTrees). // We'll generate code to deal appropriately with catching and rethrowing // each of these types of throwables (any others will be wrapped in a // RuntimeException and rethrown in that form.) // TODO Calculate the list of checked exception types that we should // handle here, based on what can be thrown by the calls in the // atomic block. List<JCTree> throwableTypes = List.nil(); throwableTypes = throwableTypes.append(make.Ident(n_Error)); throwableTypes = throwableTypes.append(make.Ident(n_RuntimeException)); // Generate unique ID for each atomic block in the program int id = nextAtomicBlockID++; // Label for the code of this atomic block (so we can break out of it) Name n_label = names.fromString(SUFFIX + "_atomic_block_" + id); // "Throwable ex;" stats = stats.append( make.VarDef(make.Modifiers(0), n_ex, null, make.Ident(n_Throwable), null)); // "final Context context = ContextDelegator.getInstance();" stats = stats.append( make.VarDef(make.Modifiers(Flags.FINAL), contextVar, null, make.Select(make.Select(make.Select(make.Ident( org), deuce), transaction), contextClass), make.App(makeContextDelegatorMethod(s_getInstance)))); // "boolean commit;" stats = stats.append( make.VarDef(make.Modifiers(0), n_commitVar, null, make.Type(new Type(TypeTags.BOOLEAN, null)), null)); // Was: "int i = [max_retries];" // Commented out because it's not used in the while loop anymore /* stats = stats.append( make.VarDef(make.Modifiers(0), n_i, null, make.Type(new Type(TypeTags.INT, null)), make.Literal(TypeTags.INT, max_retries))); */ // Back up local vars written within the atomic block, so they can be restored on abort. // Don't include vars that were potentially uninitialized going into the atomic block // (see note in Flow:visitAtomic()). tree.definedVars.removeAll(tree.declaredVars); for (VarSymbol v: tree.definedVars) { Name backup_name = v.name.append(n_backup); // "[Type of v] v_backup = v;" stats = stats.append( make.VarDef(new VarSymbol(0, backup_name, v.type, enclClass.sym), make.Ident(v))); } // "while (true) { ... }" // Was: "while (i-- > 0) { ... }" stats = stats.append( make.WhileLoop( make.Literal(TypeTags.BOOLEAN, 1), /*make.Binary(JCTree.GT, make.Unary(JCTree.POSTDEC, make.Ident(n_i)), make.Literal(TypeTags.INT, 0)),*/ whileBlock = make.Block(0, List.<JCStatement>nil()))); // "commit = true;" whileBlock.stats = whileBlock.stats.append(make.Exec( make.Assign(make.Ident(n_commitVar), make.Literal(TypeTags.BOOLEAN, 1)))); // "ex = null;" whileBlock.stats = whileBlock.stats.append(make.Exec( make.Assign(make.Ident(n_ex), make.Literal(TypeTags.BOT, null)))); // "context.init(id);" whileBlock.stats = whileBlock.stats.append(make.Exec( make.App(make.Select(make.Ident(contextVar), n_init).setType(methodType), List.<JCExpression>of(make.Literal(TypeTags.INT, id))))); Type returnType = null; boolean haveReturnVal = false; if (enclMethod != null && enclMethod.restype != null) returnType = enclMethod.restype.type; if (returnType != null && returnType.tag != TypeTags.VOID) { // "[return type] returnVal = [dummy value];" whileBlock.stats = whileBlock.stats.append( make.VarDef(make.Modifiers(0), n_returnVal, null, make.Type(returnType), dummyVal(returnType))); haveReturnVal = true; } // "boolean returning = false;" whileBlock.stats = whileBlock.stats.append( make.VarDef(make.Modifiers(0), n_returning, null, make.Type(new Type(TypeTags.BOOLEAN, null)), make.Literal(TypeTags.BOOLEAN, 0))); // Translate body of atomic block in transactional mode mode = Mode.TRANSACTIONAL; JCStatement newBody = translate(tree.body); mode = Mode.NORMAL; // "try_block: try { [body] } ..." whileBlock.stats = whileBlock.stats.append(make.Labelled(n_tryBlock, tryCatch = make.Try(make.Block(0, List.of(newBody)), List.<JCCatch>nil(), null))); // "catch (TransactionException t) { commit = false; }" tryCatch.catchers = tryCatch.catchers.append( make.Catch(make.VarDef(make.Modifiers(0), n_t, null, (JCExpression)copy.copy(txExceptionClass), null), make.Block(0, List.<JCStatement>of(make.Exec( make.Assign(make.Ident(n_commitVar), make.Literal(TypeTags.BOOLEAN, 0))))))); // "catch (Throwable t) { ex = t; }" tryCatch.catchers = tryCatch.catchers.append( make.Catch(make.VarDef(make.Modifiers(0), n_t, null, make.Ident(n_Throwable), null), make.Block(0, List.<JCStatement>of(make.Exec( make.Assign(make.Ident(n_ex), make.Ident(n_t))))))); // "if (commit) { // if (context.commit()) {...} // } else {...}" whileBlock.stats = whileBlock.stats.append( make.If(make.Ident(n_commitVar), make.Block(0, List.<JCStatement>of( make.If(make.App(make.Select(make.Ident(contextVar), n_commitMethod). setType(methodType)), ifBlock = make.Block(0, List.<JCStatement>nil()), null))), elseBlock = make.Block(0, List.<JCStatement>nil()))); // if block: Successful commit case // "if (returning) return [returnVal];" ifBlock.stats = ifBlock.stats.append( make.If(make.Ident(n_returning), make.Return(haveReturnVal ? make.Ident(n_returnVal) : null), null)); // "if (ex != null) {...}" ifBlock.stats = ifBlock.stats.append( make.If(make.Binary(JCTree.NE, make.Ident(n_ex), make.Literal(TypeTags.BOT, null)), ifExBlock = make.Block(0, List.<JCStatement>nil()), null)); // Generate code to handle each throwable type that we need to deal with for (JCTree throwableType: throwableTypes) { // "if (ex instanceof throwableType) throw (throwableType)ex;" ifExBlock.stats = ifExBlock.stats.append( make.If(make.TypeTest(make.Ident(n_ex), copy.copy(throwableType)), make.Throw(make.TypeCast(copy.copy(throwableType), make.Ident(n_ex))), null)); } // "throw new RuntimeException(ex);" ifExBlock.stats = ifExBlock.stats.append( make.Throw(make.NewClass(null, List.<DPJRegionPathList>nil(), List.<JCExpression>nil(), List.<DPJEffect>nil(), make.Ident(n_RuntimeException), List.<JCExpression>of(make.Ident(n_ex)), null))); if (tree.aliveAtEnd) { // "break atomic_block_N;" ifBlock.stats = ifBlock.stats.append(make.Break(n_label)); } else { // If control never reaches the end of the atomic block, then insert a // dummy throw statement (which should never be reached) to make // javac's control flow analysis happy. ifBlock.stats = ifBlock.stats.append( make.Throw(make.NewClass(null, List.<DPJRegionPathList>nil(), List.<JCExpression>nil(), List.<DPJEffect>nil(), make.Ident(n_RuntimeException), List.<JCExpression>nil(), null))); } // else block: roll back and retry case // "commit.rollback();" elseBlock.stats = elseBlock.stats.append(make.Exec( make.App(make.Select(make.Ident(contextVar), n_rollback).setType(methodType)))); // Restore backed up local variables if transaction didn't commit successfully for (VarSymbol v: tree.definedVars) { Name backup_name = v.name.append(n_backup); // "v = v_backup;" whileBlock.stats = whileBlock.stats.append(make.Exec( make.Assign(make.Ident(v), make.Ident(backup_name)))); } // Was: "throw new TransactionException();" // Commented out because it's now unreachable when the while loop is "while (true) { ... }" /* stats = stats.append( make.Throw(make.NewClass(null, List.<DPJRegionPathList>nil(), List.<JCExpression>nil(), List.<DPJEffect>nil(), (JCExpression)copy.copy(txExceptionClass), List.<JCExpression>nil(), null))); */ return make.Labelled(n_label, make.Block(0, stats)); } //Translate return statements within atomic blocks public void visitReturn(JCReturn tree) { Name n_returnVal = names.fromString("returnVal" + SUFFIX); Name n_returning = names.fromString("returning" + SUFFIX); Name n_tryBlock = names.fromString(SUFFIX + "_try_block"); if (inAtomic) { JCBlock block = make.Block(0, List.<JCStatement>nil()); if (enclMethod != null && enclMethod.restype != null && enclMethod.restype.type.tag != TypeTags.VOID) { // "returnVal = ...;" block.stats = block.stats.append(make.Exec( make.Assign(make.Ident(n_returnVal), translate(tree.expr)))); } // "returning = true;" block.stats = block.stats.append(make.Exec( make.Assign(make.Ident(n_returning), make.Literal(TypeTags.BOOLEAN, 1)))); // "break tryBlock;" block.stats = block.stats.append( make.Break(n_tryBlock)); result = block; } else { super.visitReturn(tree); } } // This return an expression for some value that can be used to initialize a variable of the given type JCExpression dummyVal(Type type) { if (type.tag == TypeTags.BOOLEAN) { return make.Literal(TypeTags.BOOLEAN, 0); } else if (type.tag <= TypeTags.lastBaseTag) { return make.Literal(TypeTags.INT, 0); } else { return make.Literal(TypeTags.BOT, null); } } }