package com.sun.tools.javac.code; import java.util.Iterator; import com.sun.tools.javac.code.Substitute.AsMemberOf; import com.sun.tools.javac.code.Substitute.SubstRPLs; import com.sun.tools.javac.code.Symbol.EffectParameterSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.comp.AttrContext; import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.comp.Resolve; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Pair; /** A class to represent a DPJ effect */ public abstract class Effect implements SubstRPLs<Effect>, AsMemberOf<Effects> { /** Is this an atomic effect? */ protected boolean isAtomic; public boolean isAtomic() { return isAtomic && !isNonint; } /** Is this a nonint effect? */ protected boolean isNonint; public boolean isNonint() { return isNonint; } public RPLs rpls; protected Effect(RPLs rpls, boolean isAtomic, boolean isNonint) { this.rpls = rpls; this.isNonint = isNonint; this.isAtomic = isAtomic && !isNonint; } /** * Do all the RPL and effect parameter substitutions implied by the bindings of t */ public Effects substAllParams(Type t) { Effect e = Substitute.allRPLParams(this, t); return e.substEffectParams(t.tsym.type.getEffectArguments(), t.getEffectArguments()); } public Effect substRPLParams(Iterable<RPL> from, Iterable<RPL> to) { return this; } public Effect substTRParams(Iterable<Type> from, Iterable<Type> to) { return this; } public Effects substEffectParams(Iterable<Effects> from, Iterable<Effects> to) { return new Effects(this); } public Effect substRPLForVar(VarSymbol from, RPL to) { return this; } public Effect substVars(Iterable<VarSymbol> from, Iterable<VarSymbol> to) { return this; } public Effect substExpsForVars(Iterable<VarSymbol> from, Iterable<JCExpression> to) { return this; } public Effect substIndex(VarSymbol from, JCExpression to) { return substIndices(List.of(from), List.of(to)); } public Effect substIndices(Iterable<VarSymbol> from, Iterable<JCExpression> to) { return this; } public Effect inEnvironment(Resolve rs, Env<AttrContext> env, boolean pruneLocalEffects) { return this; } public Effects asMemberOf(Type t, Types types) { return new Effects(this); } /** * Subeffects -- See Section 1.4.2 of the DPJ Tech Report */ public abstract boolean isSubeffectOf(Effect e); public boolean isSubeffectOf(Effects effects) { // SE-UNION-1 if (effects.isEmpty()) return false; Effect e = effects.first(); if (this.isSubeffectOf(e)) return true; return this.isSubeffectOf(effects.without(e)); } /** * SE-ATOMIC-1, SE-ATOMIC 2 */ public static boolean isSubeffectAtomic(Effect e1, Effect e2) { return e1.isAtomic() || !e2.isAtomic(); } /** * SE-NONINT-1, SE-NONINT-2 */ public static boolean isSubeffectNonint(Effect e1, Effect e2) { return e2.isNonint() || !e1.isNonint(); } /** * NONDET-ATOMIC */ public static boolean isNondetAtomic(Effect e1, Effect e2) { return e1.isAtomic() && e2.isAtomic(); } /** * Noninterfering effects -- See section 3.3 of Tech Report */ public abstract boolean isNoninterferingWith(Effect e, Constraints constraints, boolean atomicOK); public boolean isNoninterferingWith(Effects effects, Constraints constraints, boolean atomicOK) { // NI-EMPTY if (effects.isEmpty()) return true; // NI-UNION Effect e = effects.first(); return this.isNoninterferingWith(e, constraints, atomicOK) && this.isNoninterferingWith(effects.without(e), constraints, atomicOK); } /** * Return this effect as it appears in an atomic statement */ public Effect inAtomic() { return this; } /** * Return this effect as it appears in a nonint statement */ public Effect inNonint() { return this; } /** * Capture the effect */ public Effect capture() { return new CapturedEffect(this); } /** A class for read effects */ public static class ReadEffect extends Effect { public RPL rpl; public ReadEffect(RPLs rpls, RPL rpl, boolean isAtomic, boolean isNonint) { super(rpls, isAtomic, isNonint); this.rpl = rpl; if (!rpl.isAtomic()) this.isAtomic = false; } public boolean isSubeffectOf(Effect e) { if (!isSubeffectAtomic(this, e)) return false; if (!isSubeffectNonint(this, e)) return false; if (e instanceof ReadEffect) { // SE-READS if (this.rpl.isIncludedIn(((ReadEffect) e).rpl)) return true; } if (e instanceof WriteEffect) { // SE-READS-WRITES if (this.rpl.isIncludedIn(((WriteEffect) e).rpl)) return true; } return false; } @Override public boolean isNoninterferingWith(Effect e, Constraints constraints, boolean atomicOK) { if (atomicOK && isNondetAtomic(this, e)) { return true; } if (e instanceof ReadEffect) { // NI-READ return true; } if (e instanceof WriteEffect) { // NI-WRITE if (rpls.areDisjoint(this.rpl, ((WriteEffect) e).rpl, constraints.disjointRPLs)) return true; } if (e instanceof InvocationEffect) { // NI-INVOKES-1 if (this.isNoninterferingWith(((InvocationEffect) e).withEffects, constraints, atomicOK)) return true; } return false; } @Override public Effect substRPLParams(Iterable<RPL> from, Iterable<RPL> to) { return new ReadEffect(rpls, rpl.substRPLParams(from, to), this.isAtomic(), this.isNonint()); } @Override public Effect substTRParams(Iterable<Type> from, Iterable<Type> to) { return new ReadEffect(rpls, rpl.substTRParams(from, to), this.isAtomic(), this.isNonint()); } @Override public Effect substVars(Iterable<VarSymbol> from, Iterable<VarSymbol> to) { return new ReadEffect(rpls, Substitute.iterable(RPLs.substVars, rpl, from, to), this.isAtomic(), this.isNonint()); } @Override public Effect substExpsForVars(Iterable<VarSymbol> from, Iterable<JCExpression> to) { return new ReadEffect(rpls, rpl.substExpsForVars(from, to), this.isAtomic(), this.isNonint()); } @Override public Effect substRPLForVar(VarSymbol from, RPL to) { return new ReadEffect(rpls, this.rpl.substRPLForVar(from, to), this.isAtomic(), this.isNonint()); } @Override public Effect substIndices(Iterable<VarSymbol> from, Iterable<JCExpression> to) { return new ReadEffect(rpls, rpl.substIndices(from, to), this.isAtomic(), this.isNonint()); } @Override public Effects asMemberOf(Type t, Types types) { RPL memberRPL = rpl.asMemberOf(t, types); return new Effects(memberRPL.equals(rpl) ? this : new ReadEffect(rpls, memberRPL, this.isAtomic(), this.isNonint())); } @Override public Effect inEnvironment(Resolve rs, Env<AttrContext> env, boolean pruneLocalEffects) { RPL newRPL = rpl.inEnvironment(rs, env, pruneLocalEffects); if (newRPL == null) return null; return newRPL.equals(rpl) ? this : new ReadEffect(rpls, newRPL, this.isAtomic(), this.isNonint()); } @Override public Effect inAtomic() { if (this.isAtomic() || this.isNonint()) return this; return new ReadEffect(this.rpls, this.rpl, true, false); } @Override public Effect inNonint() { if (this.isNonint()) return this; return new ReadEffect(this.rpls, this.rpl, false, true); } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("reads "); if (this.isAtomic()) sb.append("atomic "); sb.append(rpl); return sb.toString(); } @Override public int hashCode() { return 3 * this.rpl.hashCode(); } public boolean equals(Object o) { if (!(o instanceof ReadEffect)) return false; return this.rpl.equals(((ReadEffect) o).rpl); } } /** A class for write effects */ public static class WriteEffect extends Effect { public RPL rpl; public WriteEffect(RPLs rpls, RPL rpl, boolean isAtomic, boolean isNonint) { super(rpls, isAtomic, isNonint); this.rpl = rpl; if (!rpl.isAtomic()) this.isAtomic = false; } public boolean isSubeffectOf(Effect e) { if (!isSubeffectAtomic(this, e)) return false; if (!isSubeffectNonint(this, e)) return false; if (e instanceof WriteEffect) { // SE-WRITES if (this.rpl.isIncludedIn(((WriteEffect) e).rpl)) return true; } return false; } @Override public boolean isNoninterferingWith(Effect e, Constraints constraints, boolean atomicOK) { if (atomicOK && isNondetAtomic(this, e)) { return true; } if (e instanceof ReadEffect) { // NI-READ if (rpls.areDisjoint(this.rpl, ((ReadEffect) e).rpl, constraints.disjointRPLs)) return true; } if (e instanceof WriteEffect) { // NI-WRITE if (rpls.areDisjoint(this.rpl, ((WriteEffect) e).rpl, constraints.disjointRPLs)) return true; } if (e instanceof InvocationEffect) { // NI-INVOKES-1 if (this.isNoninterferingWith(((InvocationEffect) e).withEffects, constraints, atomicOK)) return true; } return false; } @Override public Effect substRPLParams(Iterable<RPL> from, Iterable<RPL> to) { return new WriteEffect(rpls, rpl.substRPLParams(from, to), this.isAtomic(), this.isNonint()); } @Override public Effect substTRParams(Iterable<Type> from, Iterable<Type> to) { return new WriteEffect(rpls, rpl.substTRParams(from, to), this.isAtomic(), this.isNonint()); } @Override public Effect substVars(Iterable<VarSymbol> from, Iterable<VarSymbol> to) { return new WriteEffect(rpls, Substitute.iterable(RPLs.substVars, this.rpl, from, to), this.isAtomic(), this.isNonint()); } @Override public Effect substExpsForVars(Iterable<VarSymbol> from, Iterable<JCExpression> to) { return new WriteEffect(rpls, rpl.substExpsForVars(from, to), this.isAtomic(), this.isNonint()); } @Override public Effect substRPLForVar(VarSymbol from, RPL to) { return new WriteEffect(rpls, this.rpl.substRPLForVar(from, to), this.isAtomic(), this.isNonint()); } @Override public Effect substIndices(Iterable<VarSymbol> from, Iterable<JCExpression> to) { Effect result = new WriteEffect(rpls, rpl.substIndices(from, to), this.isAtomic(), this.isNonint()); return result; } @Override public Effects asMemberOf(Type t, Types types) { RPL memberRPL = rpl.asMemberOf(t, types); return new Effects(memberRPL.equals(rpl) ? this : new WriteEffect(rpls, memberRPL, this.isAtomic(), this.isNonint())); } @Override public Effect inEnvironment(Resolve rs, Env<AttrContext> env, boolean pruneLocalEffects) { RPL newRPL = rpl.inEnvironment(rs, env, pruneLocalEffects); if (newRPL == null) return null; return newRPL.equals(rpl) ? this : new WriteEffect(rpls, newRPL, this.isAtomic(), this.isNonint()); } @Override public Effect inAtomic() { if (this.isAtomic() || this.isNonint()) return this; return new WriteEffect(this.rpls, this.rpl, true, false); } @Override public Effect inNonint() { if (this.isNonint()) return this; return new WriteEffect(this.rpls, this.rpl, false, true); } @Override public int hashCode() { return 7 * this.rpl.hashCode(); } public boolean equals(Object o) { if (!(o instanceof WriteEffect)) return false; return this.rpl.equals(((WriteEffect) o).rpl); } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("writes "); if (this.isAtomic()) sb.append("atomic "); sb.append(rpl); return sb.toString(); } } /** A class for invocation effects */ public static class InvocationEffect extends Effect { public MethodSymbol methSym; public Effects withEffects; public InvocationEffect (RPLs rpls, MethodSymbol methSym, Effects withEffects) { super(rpls, false, false); this.methSym = methSym; this.withEffects = withEffects; } /** * Invocation effects are never "automatically" atomic: they are governed * by their withEffects. */ public boolean isAtomic() { return false; } public boolean isSubeffectOf(Effect e) { if (!isSubeffectAtomic(this, e)) return false; if (!isSubeffectNonint(this, e)) return false; if (e instanceof InvocationEffect) { InvocationEffect ie = (InvocationEffect) e; // SE-INVOKES-1 if (this.methSym == ie.methSym && this.withEffects.areSubeffectsOf(ie.withEffects)) return true; } Effects effects = new Effects(); effects.add(e); if (this.withEffects.areSubeffectsOf(effects)) return true; return false; } @Override public boolean isNoninterferingWith(Effect e, Constraints constraints, boolean atomicOK) { if (e.isNoninterferingWith(withEffects, constraints, atomicOK)) return true; if (e instanceof InvocationEffect) { InvocationEffect ie = (InvocationEffect) e; if (Effects.noninterferingEffects(withEffects, ie.withEffects, constraints, atomicOK)) { return true; } if ((methSym == ie.methSym) && ((methSym.flags() & Flags.ISCOMMUTATIVE)) != 0) { return true; } } return false; } @Override public boolean isSubeffectOf(Effects set) { if (super.isSubeffectOf(set)) return true; // SE-INVOKES-2 return (this.withEffects.areSubeffectsOf(set)); } @Override public Effect substRPLParams(Iterable<RPL> from, Iterable<RPL> to) { return new InvocationEffect(rpls, methSym, withEffects.substRPLParams(from, to)); } @Override public Effect substTRParams(Iterable<Type> from, Iterable<Type> to) { return new InvocationEffect(rpls, methSym, withEffects.substTRParams(from, to)); } @Override public Effect substVars(Iterable<VarSymbol> from, Iterable<VarSymbol> to) { return new InvocationEffect(rpls, methSym, withEffects.substVars(from, to)); } @Override public Effect substExpsForVars(Iterable<VarSymbol> from, Iterable<JCExpression> to) { return new InvocationEffect(rpls, methSym, withEffects.substExpsForVars(from, to)); } @Override public Effect substRPLForVar(VarSymbol from, RPL to) { return new InvocationEffect(rpls, methSym, withEffects.substRPLForVar(from, to)); } @Override public Effect substIndices(Iterable<VarSymbol> from, Iterable<JCExpression> to) { return new InvocationEffect(rpls, methSym, withEffects.substIndices(from, to)); } @Override public Effects asMemberOf(Type t, Types types) { Effects memberEffects = withEffects.asMemberOf(t, types); return new Effects((memberEffects == withEffects) ? this : new InvocationEffect(rpls, methSym, memberEffects)); } @Override public Effect inEnvironment(Resolve rs, Env<AttrContext> env, boolean pruneLocalEffects) { Effects newEffects = withEffects.inEnvironment(rs, env, pruneLocalEffects); return (newEffects == withEffects) ? this : new InvocationEffect(rpls, methSym, newEffects); } @Override public Effects substEffectParams(Iterable<Effects> from, Iterable<Effects> to) { Effects newEffects = withEffects.substEffectParams(from, to); return new Effects(new InvocationEffect(rpls, methSym, newEffects)); } @Override public Effect inAtomic() { if (this.isAtomic() || this.isNonint()) return this; Effects inAtomicEffects = withEffects.inAtomic(); return (inAtomicEffects == withEffects) ? this : new InvocationEffect(rpls, methSym, inAtomicEffects); } @Override public int hashCode() { return 11 * this.methSym.hashCode() + this.withEffects.hashCode(); } public boolean equals(Object o) { if (!(o instanceof InvocationEffect)) return false; InvocationEffect ie = (InvocationEffect) o; return this.methSym == ie.methSym && this.withEffects.equals(ie.withEffects); } public String toString() { return "invokes " + methSym + " with " + withEffects; } } /** A class for variable effects */ public static class VariableEffect extends Effect { public EffectParameterSymbol sym; public VariableEffect(EffectParameterSymbol sym) { super(null, false, false); this.sym = sym; } @Override public boolean equals(Object o) { if (!(o instanceof VariableEffect)) return false; VariableEffect ve = (VariableEffect) o; return this.sym == ve.sym; } @Override public int hashCode() { return 13 * this.sym.hashCode(); } @Override public boolean isNoninterferingWith(Effect e, Constraints constraints, boolean atomicOK) { if (atomicOK && isNondetAtomic(this, e)) { return true; } for (Pair<Effects,Effects> constraint : constraints.noninterferingEffects) { if (this.isSubeffectOf(constraint.fst) && e.isSubeffectOf(constraint.snd)) return true; if (this.isSubeffectOf(constraint.snd) && e.isSubeffectOf(constraint.fst)) return true; } return false; } public final Effect WRITES_ROOT_STAR = new WriteEffect(rpls, new RPL(List.of(RPLElement.ROOT_ELEMENT, RPLElement.STAR)), false, true); @Override public boolean isSubeffectOf(Effect e) { if (!isSubeffectAtomic(this, e)) return false; if (!isSubeffectNonint(this, e)) return false; // TODO: We could also allow subeffect constraints if (WRITES_ROOT_STAR.isSubeffectOf(e)) return true; return this.equals(e); } @Override public Effects asMemberOf(Type t, Types types) { Symbol owner = this.sym.enclClass(); Type base = types.asOuterSuper(t, owner); return this.substEffectParams(base.tsym.type.getEffectArguments(), base.getEffectArguments()); } @Override public Effects substEffectParams(Iterable<Effects> from, Iterable<Effects> to) { Iterator<Effects> fromI = from.iterator(); Iterator<Effects> toI = to.iterator(); while (fromI.hasNext() && toI.hasNext()) { Effects fromElt = fromI.next(); Effects toElt = toI.next(); VariableEffect ve = fromElt.asVariableEffect(); assert(ve != null); if (ve.equals(this)) return toElt; } return new Effects(this); } @Override public Effect capture() { return this; } public String toString() { return "effect " + sym; } } /** A class for captured effects */ public static class CapturedEffect extends Effect { public Effect upperBound; protected CapturedEffect(Effect upperBound) { super(null, false, false); this.upperBound = upperBound; } @Override public boolean isNoninterferingWith(Effect e, Constraints constraints, boolean atomicOK) { return e.isNoninterferingWith(upperBound, constraints, atomicOK); } @Override public boolean isSubeffectOf(Effect e) { return upperBound.isSubeffectOf(e); } @Override public Effect capture() { return this; } public String toString() { return "capture of " + upperBound; } } }